请问我的按键操作的代码有什么问题吗,如何修改?
Buttom_Scan函数要分别实现单击,双击,长按按键
单击时,灯的亮灭周期为1秒
双击时,灯的亮灭周期为2秒
长按时,灯的亮灭周期为长按的时间
当灯有现象时,单击按键使其熄灭
#出现的问题:
问题1:
单击时,得到的total_time
问题2:
当有现象时,单击按键不能返回原来状态,即不能让灯灭
问题3:
有的时候双击也没有现象,在调试时发现,有时候双击完后,count不知为何会一直增加,有时count不增加但就是没现象
以下为GPIO,定时器TIM3的初始化函数和中断回调函数
uint8_t count = 0; //定时器溢出次数
TIM_HandleTypeDef htim3;
/* TIM3初始化 */
void TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim3.Instance = TIM3;
//定时器设置为向上计数,时间为1秒,(7199+1)*(9999+1)/72000000
htim3.Init.Prescaler = 7199;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 9999;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM3)
{
__HAL_RCC_TIM3_CLK_ENABLE(); //初始化TIM3时钟
//设置中断
HAL_NVIC_SetPriority(TIM3_IRQn, 1, 2);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}
}
//中断回调函数,定时器每溢出一次,count+1
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
if(htim->Instance == TIM3){
count++;
__HAL_TIM_CLEAR_FLAG(&htim3, TIM_FLAG_UPDATE); //清除中断标志
}
}
/*初始化GPIO*/
void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); //让PB5最开始为高电平,即让灯灭
//PE4,按键
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
//PB5,灯
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
以下是main.c
#define LED_OFF 0 //灯灭
#define ONE_PUSH_FLAG 1 //单击标记
#define DOUBLE_PUSH_FLAG 2 //双击标记
#define LONG_PUSH_FLAG 3 //长按标记
extern TIM_HandleTypeDef htim3;
extern uint16_t count; //定时器溢出次数
uint16_t end_count; //最后获取的定时器的值
uint16_t total_time; //总时间,单位毫秒
static uint8_t Buttom_Flag = 0;
/*按键扫描函数*/
/*按键按下,引脚呈低电平*/
uint8_t Buttom_Scan(void){
if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == 0){
HAL_Delay(20);
if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == 0){
HAL_TIM_Base_Start_IT(&htim3); //使能中断和计数器
__HAL_TIM_SET_COUNTER(&htim3, 0); //将定时器设置为0
while(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == 0);
end_count = __HAL_TIM_GET_COUNTER(&htim3); //获取定时器的值
HAL_TIM_Base_Stop_IT(&htim3); //失能中断和计数器
total_time = 1000 * count + end_count / 10;
if(total_time > 2000){ //大于2秒,长按
Buttom_Flag = LONG_PUSH_FLAG;
}
else if(total_time >= 0 && total_time <= 200){ //多于0秒,小于0.2秒,单击
if(Buttom_Flag != LED_OFF){
Buttom_Flag = LED_OFF; //当灯亮着时,单击按键使灯熄灭
}
else{
Buttom_Flag = ONE_PUSH_FLAG;
}
}
}
else if(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == 1){
HAL_TIM_Base_Start_IT(&htim3); //使能中断和计数器
__HAL_TIM_SET_COUNTER(&htim3, 0); //将定时器设置为0
while(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == 1);
end_count = __HAL_TIM_GET_COUNTER(&htim3); //获取定时器的值
HAL_TIM_Base_Stop_IT(&htim3); //失能中断和计数器
while(HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == 0);
total_time = 1000 * count + end_count / 10;
if(total_time > 200 && total_time <=1000){ //在1秒内按两次,双击
Buttom_Flag = DOUBLE_PUSH_FLAG;
}
}
}
count = 0; //清零
return Buttom_Flag;
}
int main(void)
{
uint8_t buttom;
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
while (1)
{
buttom = Buttom_Scan();
if(buttom == LED_OFF){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
}
else if(buttom == ONE_PUSH_FLAG){
HAL_Delay(500);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
else if(buttom == DOUBLE_PUSH_FLAG){
HAL_Delay(1000);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
else if(buttom == LONG_PUSH_FLAG){
HAL_Delay(total_time / 2);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
}
}
【以下回答由 GPT 生成】
首先,根据提供的代码和问题描述,有几个问题需要解决:
针对这些问题,我给出以下解决方案:
问题的关键是如何准确计算单击的时间。在原有的代码中,并没有给出计算total_time的部分。我们可以通过在按键按下和松开时分别获取系统时间,然后计算两个时间之差来获取按键持续的时间。具体的修改如下:
uint32_t press_time; // 记录按键按下的时间
uint32_t release_time; // 记录按键松开的时间
uint32_t total_time; // 记录按键总共持续的时间
// 在按键按下时调用
void Button_Pressed(void)
{
press_time = HAL_GetTick(); // 获取按下时的系统时间
}
// 在按键松开时调用
void Button_Released(void)
{
release_time = HAL_GetTick(); // 获取松开时的系统时间
total_time = release_time - press_time; // 计算按键持续的时间
}
然后在 Buttom_Scan
函数中调用 Button_Pressed
函数和 Button_Released
函数即可。
根据提供的代码,没有在按键单击时关闭灯的部分。我们可以添加一个函数来控制灯的亮灭,并在单击时调用该函数关闭灯。具体的修改如下:
// 控制灯的亮灭
void Toggle_Led(void)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // 假设灯的控制引脚是PB0
}
// 在单击时调用
void Button_Clicked(void)
{
Toggle_Led(); // 关闭灯
}
然后在 Buttom_Scan
函数中调用 Button_Clicked
函数。
这个问题可能是由于双击事件的判断条件不准确引起的。我们可以增加一个计时器,在第一次点击后,如果一定时间内没有第二次点击,则认为双击事件结束。具体的修改如下:
uint8_t click_count = 0; // 记录点击的次数
uint32_t prev_click_time; // 记录上一次点击的时间
uint32_t double_click_time = 300; // 设定两次点击之间的最大时间间隔为300毫秒
// 在单击时调用
void Button_Clicked(void)
{
uint32_t current_time = HAL_GetTick(); // 获取当前的系统时间
if (click_count == 0)
{
prev_click_time = current_time; // 记录第一次点击的时间
click_count++;
}
else if (click_count == 1)
{
if (current_time - prev_click_time <= double_click_time)
{
// 双击事件发生
Toggle_Led(); // 关闭灯
click_count = 0; // 重置点击次数
}
else
{
// 单击事件发生
prev_click_time = current_time; // 更新点击时间
click_count++;
}
}
}
然后在 Buttom_Scan
函数中调用 Button_Clicked
函数即可。
最后,根据以上的修改方案,更新 Asnwer.md
文件。