Timer挂掉的处理方法

本文探讨了Java中Timer组件的不稳定性问题,并提供了一种解决方案:使用ScheduledExecutorService替代原有的Timer来提高任务调度的稳定性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Timer运行几个小时后挂掉,虽然我在里面捕捉了所有异常,仍然挂掉了。
查找资料,发现Timer确实有这样的不稳定性。


解决:
java.util.concurrent.ScheduledExecutorService globalTimer = java.util.concurrent.Executors.newSingleThreadScheduledExecutor();

改用这个替代Timer
SomeTask 继承 TimerTask

globalTimer.scheduleAtFixedRate(new SomeTask(), COMMON.CHECK_REDIS_IDLE, COMMON.CHECK_REDIS_IDLE, TimeUnit.MILLISECONDS);
/** ****************************************************************************** * @file main.c * @author MCU Application Team * @brief Main program body ****************************************************************************** * @attention * * <h2><center>© Copyright (c) Puya Semiconductor Co. * All rights reserved.</center></h2> * * <h2><center>© Copyright (c) 2016 STMicroelectronics. * All rights reserved.</center></h2> * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "string.h" #include "py32f002b_it.h" #include "py32f002b_hal_tim.h" #include <stdio.h> #include <stdlib.h> // for abs() EXTI_HandleTypeDef exti_handle; UART_HandleTypeDef UartHandle; TIM_HandleTypeDef TimHandle; TIM_HandleTypeDef TimHandle14; TIM_OC_InitTypeDef sConfigOC = {0}; // 定义并初始化sConfigOC // 红外协议参数 #define NEC_HEADER_HIGH 9000 // 9ms引导码高电平 #define NEC_HEADER_LOW 4500 // 4.5ms引导码低电平 #define NEC_BIT_HIGH 3900 // 调整为实际测量的约3900us #define NEC_BIT_ONE_LOW 3900 // 调整为实际测量的约3900us #define NEC_BIT_ZERO_LOW 3900 // 调整为实际测量的约3900us /* 容差范围 */ #define NEC_HEADER_TOLERANCE 1000 // 引导码容差1ms #define NEC_BIT_TOLERANCE 500 // 数据位容差500us #define IR_MAX_PULSE_CNT 100 /* Private define ------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ volatile uint32_t timer1_overflow_count = 0; // TIM1溢出计数 /* Private user code ---------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ // 系统时钟频率(根据配置为24MHz) #define SYSTEM_CLOCK_FREQ 24000000 // 定义红外遥控按键对应的功能 #define KEY_POWER 0x01 #define KEY_ON 0xc0 #define KEY_OFF 0x40 #define KEY_RED 0x20 #define KEY_GREEN 0xA0 #define KEY_BLUE 0x60 #define KEY_WHITE 0xE0 #define KEY_FLASH 0xD0 #define KEY_SMOOTH 0xC8 #define KEY_YELLOW 0x06 #define KEY_CYAN 0x07 #define KEY_MAGENTA 0x08 #define KEY_BRIGHT_UP 0x00 #define KEY_BRIGHT_DN 0x80 #define KEY_MODE_NEXT 0x0B #define KEY_TIMER 0x28 #define KEY_SPEED_UP 0xA8 #define KEY_SPEED_DOWN 0x68 #define KEY_WARM 0x30 #define PWM_MAX 1000 // 满占空比 #define FADE_STEP 5 // 渐变步进 #define FADE_DELAY 10 // 每步延时(ms) #define BLUE_CHANNEL TIM_CHANNEL_1 #define GREEN_CHANNEL TIM_CHANNEL_2 #define RED_CHANNEL TIM_CHANNEL_3 #define WHITE_CHANNEL TIM_CHANNEL_4 // LED引脚定义 #define LED_RED_PIN GPIO_PIN_5 #define LED_GREEN_PIN GPIO_PIN_3 #define LED_BLUE_PIN GPIO_PIN_4 #define LED_WHITE_PIN GPIO_PIN_1 #define LED_ALL_PINS (LED_RED_PIN | LED_GREEN_PIN | LED_BLUE_PIN) // 白光呼吸灯参数 #define WARM_BRIGHTNESS_LEVELS 3 // 三种亮度等级 #define WARM_MODE 7 // 白光呼吸模式标识 uint8_t warm_brightness_level = 0; // 亮度等级(0-2对应3种亮度) uint16_t breath_speed = 20; // 呼吸速度(ms/步,值越小越快) // 三种亮度等级的最大亮度(0-100) const uint8_t warm_max_brightness[WARM_BRIGHTNESS_LEVELS] = {70, 40, 0}; // 全局变量 uint8_t ir_data[4] = {0}; // 红外数据缓冲区 uint8_t ir_data_len = 0; // 数据长度 uint8_t ir_repeat_flag = 0; // 重复码标志 uint8_t ir_last_cmd = 0; // 上一次命令 uint32_t last_int_time = 0; // 上一次中断时间戳 uint8_t decode_state = 0; // 解码状态机 uint8_t current_mode = 0; // 0-静态颜色, 1-呼吸模式, 2-颜色渐变 uint8_t current_brightness = 100; // 亮度0-100 (百分比) uint8_t color_r = 0; // 当前红色分量(0-100%) uint8_t color_g = 0; // 当前绿色分量(0-100%) uint8_t color_b = 0; // 当前蓝色分量(0-100%) uint8_t color_w = 0; //当前白色分量 uint8_t keep_color = 0; //保存当前灯的状态 // 定时器相关变量 // 定时器相关变量 typedef enum { TIMER_IDLE = 0, TIMER_SETTING, TIMER_GRADIENT, TIMER_RUNNING, TIMER_EXPIRED } TimerState_t; TimerState_t timer_state = TIMER_IDLE; uint32_t timer_end_time = 0; uint8_t timer_duration = 0; // 定时时长(小时) uint32_t gradient_start_time = 0; uint8_t gradient_steps = 0; // 保存定时前的状态 uint8_t pre_timer_mode = 0; uint8_t pre_timer_r = 0, pre_timer_g = 0, pre_timer_b = 0, pre_timer_w = 0; uint8_t saved_r = 0, saved_g = 0, saved_b = 0, saved_w = 0; // 全局呼吸参数 uint8_t breath_direction = 1; // 1=渐亮,0=渐暗 uint16_t breath_period = 4000; // 完整呼吸周期(ms) uint32_t last_breath_time = 0; // 上次呼吸更新时间 uint8_t breath_step_interval = 20;// 步进间隔(ms) // 函数声明 void led_extinct(void); void APP_GpioConfig(void); void APP_SystemClockConfig(void); void IR_ProcessCommand(void); void IR_ProcessRepeat(void); void set_led_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w); void set_led_brightness(uint8_t brightness); void next_mode(void); void update_leds(void); void led_off(void); void IR_Decode(uint32_t duration, uint8_t level); void TIM14_Init(void); uint32_t IR_GetMicroseconds(void); // 4组七彩跳变组合(每组7种颜色,包含R/G/B/W分量) const uint16_t color_groups[4][7][4] = { // 第1组:基础彩虹色 { {1000, 0, 0, 0}, // 红 {1000, 647, 0, 0}, // 橙 {1000, 1000, 0, 0}, // 黄 {0, 1000, 0, 0}, // 绿 {0, 1000, 1000, 0}, // 青 {0, 0, 1000, 0}, // 蓝 {1000, 0, 1000, 0} // 紫 }, // 第2组:高饱和混合色 { {1000, 300, 300, 0}, // 粉 {300, 1000, 300, 0}, // 绿蓝 {300, 300, 1000, 0}, // 靛蓝 {1000, 1000, 300, 0}, // 金黄 {1000, 300, 1000, 0}, // 洋红 {300, 1000, 1000, 0}, // 青绿 {500, 500, 500, 0} // 灰 }, // 第3组:低饱和柔和色 { {500, 200, 200, 300}, // 淡红 {500, 400, 200, 300}, // 淡橙 {500, 500, 200, 300}, // 淡黄 {200, 500, 200, 300}, // 淡绿 {200, 500, 500, 300}, // 淡青 {200, 200, 500, 300}, // 淡蓝 {500, 200, 500, 300} // 淡紫 }, // 第4组:含白色混合色 { {1000, 0, 0, 300}, // 红+白 {1000, 600, 0, 300}, // 橙+白 {1000, 1000, 0, 300}, // 黄+白 {0, 1000, 0, 300}, // 绿+白 {0, 1000, 1000, 300}, // 青+白 {0, 0, 1000, 300}, // 蓝+白 {800, 800, 800, 500} // 全白 } }; // 4组七彩渐变颜色组合(每组7种颜色,用于渐变过渡) const uint8_t gradient_groups[4][7][4] = { // 第1组:标准彩虹渐变 { {100, 0, 0, 0}, // 红 {100, 50, 0, 0}, // 橙 {100, 100, 0, 0}, // 黄 {0, 100, 0, 0}, // 绿 {0, 100, 100, 0}, // 青 {0, 0, 100, 0}, // 蓝 {100, 0, 100, 0} // 紫 }, // 第2组:高饱和渐变 { {100, 30, 30, 0}, // 粉 {30, 100, 30, 0}, // 绿蓝 {30, 30, 100, 0}, // 靛蓝 {100, 100, 30, 0}, // 金黄 {100, 30, 100, 0}, // 洋红 {30, 100, 100, 0}, // 青绿 {50, 50, 50, 0} // 灰 }, // 第3组:柔和渐变(含白色) { {80, 20, 20, 20}, // 淡红 {80, 60, 20, 20}, // 淡橙 {80, 80, 20, 20}, // 淡黄 {20, 80, 20, 20}, // 淡绿 {20, 80, 80, 20}, // 淡青 {20, 20, 80, 20}, // 淡蓝 {80, 20, 80, 20} // 淡紫 }, // 第4组:暖色渐变 { {100, 20, 0, 10}, // 暖红 {100, 60, 0, 10}, // 暖橙 {100, 100, 20, 10}, // 暖黄 {60, 100, 0, 10}, // 暖绿 {20, 80, 40, 10}, // 暖青 {0, 40, 80, 10}, // 暖蓝 {60, 20, 80, 10} // 暖紫 } }; // 渐变模式全局变量 uint8_t current_gradient_group = 0; // 当前渐变组索引(0-3) uint8_t gradient_step = 0; // 组内渐变步骤(0-100) uint8_t gradient_from_idx = 0; // 起始颜色索引 uint8_t gradient_to_idx = 1; // 目标颜色索引 // 新增全局变量跟踪当前组和组内颜色 uint8_t current_color_group = 0; // 当前组索引(0-3) static uint8_t group_step_idx = 0; // 当前组内颜色索引(0-6) // 6组三色跳变组合 (每组3种颜色,每种颜色包含R/G/B/W分量) const uint8_t jump_color_groups[6][3][4] = { // 第1组:红→绿→蓝 {{100,0,0,0}, {0,100,0,0}, {0,0,100,0}}, // 第2组:黄→青→紫 {{100,100,0,0}, {0,100,100,0}, {100,0,100,0}}, // 第3组:橙→绿蓝→粉 {{100,50,0,0}, {0,50,100,0}, {100,30,70,0}}, // 第4组:暖白→冷白→中性白 {{80,40,10,30}, {10,40,80,50}, {40,40,40,80}}, // 第5组:深红→深绿→深蓝 {{50,0,0,0}, {0,50,0,0}, {0,0,50,0}}, // 第6组:红+白→绿+白→蓝+白 {{100,0,0,50}, {0,100,0,50}, {0,0,100,50}} }; uint8_t jump_group_index = 0; // 当前组合索引(0-5) uint8_t current_jump_color = 0; // 当前组内颜色索引(0-2) uint32_t jump_timer = 0; // 跳变计时器 uint16_t jump_interval = 800; // 每组颜色停留时间(ms) /** * @brief TIM1初始化,配置为1MHz计数(1us计数一次) * @retval None */ /*int fputc(int ch, FILE *f) { HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; }*/ typedef enum { RAINBOW_STOP = 0, RAINBOW_RUN } RainbowState_t; // 全局变量 static uint16_t cur[4] = {0}; // 当前占空比 static uint32_t last_tick = 0; void delay_us(uint32_t us) { uint32_t tickstart = IR_GetMicroseconds(); while ((IR_GetMicroseconds() - tickstart) < us); } int fputc(int ch, FILE *f) { HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, HAL_MAX_DELAY); return ch; } //配置计数器 void TIM14_Init(void) { __HAL_RCC_TIM14_CLK_ENABLE(); // 对于24MHz的系统时钟,分频到1MHz TimHandle14.Instance = TIM14; TimHandle14.Init.Prescaler = 24 - 1; // 24分频得到1MHz TimHandle14.Init.CounterMode = TIM_COUNTERMODE_UP; TimHandle14.Init.Period = 0xffff; // 16位计数器 TimHandle14.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; TimHandle14.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 1, 0); HAL_NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); if (HAL_TIM_Base_Init(&TimHandle14) != HAL_OK) { APP_ErrorHandler(); } // 清空计数器 __HAL_TIM_SET_COUNTER(&TimHandle14, 0); // 启动定时器 HAL_TIM_Base_Start(&TimHandle14); HAL_NVIC_SetPriority(TIM14_IRQn, 1, 0); HAL_NVIC_EnableIRQ(TIM14_IRQn); } //配置pwm发生器 void APP_TimPwmConfig(void) { __HAL_RCC_TIM1_CLK_ENABLE(); // 对于24MHz的系统时钟,分频到1MHz TimHandle.Instance = TIM1; TimHandle.Init.Prescaler = 24 - 1; // 24分频得到1MHz TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP; TimHandle.Init.Period = 1000-1; // 重载值 TimHandle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; TimHandle.Init.RepetitionCounter = 0; TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&TimHandle) != HAL_OK) { APP_ErrorHandler(); } // 清空计数器 __HAL_TIM_SET_COUNTER(&TimHandle, 0); // 启动定时器 HAL_TIM_Base_Start(&TimHandle); sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; /* OCN channel output high level effective */ sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; /* Idle state OC1N output low level */ sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; /* Idle state OC1 output low level*/ sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; sConfigOC.Pulse = 0; /* Channel 1 configuration */ if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { APP_ErrorHandler(); } sConfigOC.Pulse = 0; /* Channel 2 configuration */ if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { APP_ErrorHandler(); } sConfigOC.Pulse = 0; /* Channel 3 configuration */ if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { APP_ErrorHandler(); } sConfigOC.Pulse = 0; /* Channel 4 configuration */ if (HAL_TIM_PWM_ConfigChannel(&TimHandle, &sConfigOC, TIM_CHANNEL_4) != HAL_OK) { APP_ErrorHandler(); } HAL_TIM_PWM_Start(&TimHandle, RED_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, GREEN_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, BLUE_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, WHITE_CHANNEL); __HAL_TIM_SET_COMPARE(&TimHandle, TIM_CHANNEL_1,1000); // 设置红色通道占空比 __HAL_TIM_SET_COMPARE(&TimHandle, TIM_CHANNEL_2,1000); // 设置绿色通道占空比 __HAL_TIM_SET_COMPARE(&TimHandle, TIM_CHANNEL_3, 1000); // 设置蓝色通道占空比 __HAL_TIM_SET_COMPARE(&TimHandle, TIM_CHANNEL_4, 1000); // 设置白色通道占空比 __HAL_TIM_MOE_ENABLE(&TimHandle); } // 设置RGB颜色 void set_rgb_color(uint16_t r, uint16_t g, uint16_t b) { __HAL_TIM_SET_COMPARE(&TimHandle, RED_CHANNEL, r); __HAL_TIM_SET_COMPARE(&TimHandle, GREEN_CHANNEL, g); __HAL_TIM_SET_COMPARE(&TimHandle, BLUE_CHANNEL, b); } /** * @brief TIM14更新中断处理函数 * @retval None */ void TIM14_IRQHandler(void) { // 只处理更新中断 if (__HAL_TIM_GET_FLAG(&TimHandle14, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(&TimHandle14, TIM_FLAG_UPDATE); timer1_overflow_count++; } } uint32_t millis(void) { return timer1_overflow_count; } /** * @brief 获取当前微秒数(使用TIM1实现) * @retval 当前时间(us) */ uint32_t IR_GetMicroseconds(void) { uint32_t time; do { time = __HAL_TIM_GET_COUNTER(&TimHandle14); } while(time != __HAL_TIM_GET_COUNTER(&TimHandle14)); return time; } // 计算两个时间点之间的差值 uint32_t Calculate_Duration(uint32_t current, uint32_t last) { if(current >= last) { return current - last; } else { // 处理计数器溢出 return (0xFFFF - last) + current + 1; } } void LED_Init(void) { // 1. 使能LED所在GPIO端口的时钟(根据实际端口修改) __HAL_RCC_GPIOA_CLK_ENABLE(); // 若LED接GPIOA,则使能GPIOA时钟 // 2. 定义GPIO初始化结构体 GPIO_InitTypeDef GPIO_InitStruct; // 3. 配置LED引脚参数 GPIO_InitStruct.Pin = GPIO_PIN_1; // 选择LED引脚 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出(适合驱动LED) GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉(根据硬件电路决定) GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速模式(LED无需高速切换) // 4. 初始化GPIO HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 5. 初始状态:关闭LED(根据硬件电路决定高低电平) // 假设LED为高电平点亮:初始输出低电平=关闭 HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1, GPIO_PIN_SET); } /** * @brief EXTI中断服务函数(双边沿触发) * @retval None */ void EXTI0_1_IRQHandler(void) { static uint32_t last_edge_time = 0; static uint8_t last_pin_state = 1; static uint8_t is_first_edge = 1; // 使用uint8_t代替bool if (__HAL_GPIO_EXTI_GET_FLAG(GPIO_PIN_0) != RESET) { __HAL_GPIO_EXTI_CLEAR_FLAG(GPIO_PIN_0); uint32_t current_time = IR_GetMicroseconds(); uint8_t current_pin_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0); if (is_first_edge) { is_first_edge = 0; // 使用0代替false last_edge_time = current_time; last_pin_state = current_pin_state; printf("First edge: level=%d\n", current_pin_state); return; } if (current_pin_state != last_pin_state) // 确认电平真的变化了 { uint32_t duration = Calculate_Duration(current_time, last_edge_time); // 过滤掉可能的噪声脉冲 if (duration >= 100) // 最小100us { IR_Decode(duration, current_pin_state); } last_edge_time = current_time; last_pin_state = current_pin_state; } } } void Test_Timer_Accuracy(void) { uint32_t start, end, elapsed; printf("Timer accuracy test:\r\n"); start = IR_GetMicroseconds(); HAL_Delay(1000); // 延时1秒 end = IR_GetMicroseconds(); elapsed = end - start; printf("1000ms delay = %lu us\r\n", elapsed); } /** * @brief 红外解码状态机 * @param duration: 脉冲持续时间(us) * @param level_type: 电平类型 (1=高电平结束, 0=低电平结束) * @retval None */ #define NEC_TOLERANCE 300 void IR_Decode(uint32_t duration, uint8_t level_type) { static uint8_t state = 0; static uint8_t bit_index = 0; static uint8_t data[4] = {0}; switch (state) { case 0: // 等待引导码高电平 if (level_type == 1 && abs(duration - 9000) < NEC_TOLERANCE) { state = 1; } break; case 1: // 等待引导码低电平 if (level_type == 0 && abs(duration - 4500) < NEC_TOLERANCE) { memset(data, 0, 4); bit_index = 0; state = 2; } else if (level_type == 0 && abs(duration - 2250) < NEC_TOLERANCE) { // 重复码 IR_ProcessRepeat(); state = 0; } else { state = 0; } break; case 2: // 等待位高电平(560us) if (level_type == 1 && abs(duration - 560) < NEC_TOLERANCE) { state = 3; } else { state = 0; } break; case 3: // 等待位低电平(判断逻辑0或1) if (level_type == 0) { if (abs(duration - 560) < NEC_TOLERANCE) { // 逻辑0,不设置位 } else if (abs(duration - 1690) < NEC_TOLERANCE) { // 逻辑1,设置位 data[bit_index / 8] |= (1 << (7 - (bit_index % 8))); } else { state = 0; return; } bit_index++; if (bit_index >= 32) { memcpy(ir_data, data, 4); IR_ProcessCommand(); state = 0; } else { state = 2; } } break; default: state = 0; break; } } //串口 void MX_USART1_UART_Init(uint32_t bound) { UartHandle.Instance = USART1; UartHandle.Init.BaudRate = bound; UartHandle.Init.WordLength = UART_WORDLENGTH_8B; UartHandle.Init.StopBits = UART_STOPBITS_1; UartHandle.Init.Parity = UART_PARITY_NONE; UartHandle.Init.Mode = UART_MODE_TX_RX; UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE; UartHandle.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&UartHandle) != HAL_OK) { APP_ErrorHandler(); } // 使能接收中断 __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_RXNE); } void HAL_UART_spInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct = {0}; if(huart->Instance==USART1) { __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_USART1_CLK_ENABLE(); // -> USART_TX GPIO_InitStruct.Pin = GPIO_PIN_4; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = DEBUG_USART_TX_AF; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // -> USART_RX GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Alternate = DEBUG_USART_RX_AF; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 配置USART1中断 HAL_NVIC_SetPriority(USART1_IRQn, 0, 1); HAL_NVIC_EnableIRQ(USART1_IRQn); } } // 白光呼吸任务函数 void warm_breath_task(void) { if (keep_color != WARM_MODE) return; static uint8_t direction = 1; // 1=渐暗, 0=渐亮 static uint8_t brightness = 0; // 0=最亮, 100=最暗 static uint32_t last_update = 0; static uint8_t initialized = 0; // 用于初始化亮度 // 检查是否到达更新时间 if (HAL_GetTick() - last_update < breath_speed) return; last_update = HAL_GetTick(); // 获取当前亮度级别的最大值 const uint8_t max_bright = warm_max_brightness[warm_brightness_level]; // 初始化亮度为当前级别的最大值 if (!initialized) { brightness = max_bright; initialized = 1; } if (direction) { // 渐暗 (亮度值增加) if (brightness < 100) { // 最大亮度为100 brightness++; } else { direction = 0; // 切换为渐亮 } } else { // 渐亮 (亮度值减小) if (brightness > max_bright) { brightness--; } else { direction = 1; // 切换为渐暗 } } // 设置白灯亮度(RGB关闭) set_led_color(100, 100, 100, brightness); printf("速率:%d ms, 亮度:%d\n", breath_speed, brightness); } static void rgbw_fade_task(void) { if (keep_color != 1) { return; } // 根据当前组索引获取目标颜色 const uint16_t *tar = color_groups[current_color_group][group_step_idx]; uint8_t done = 1; for (uint8_t ch = 0; ch < 4; ch++) { if (cur[ch] < tar[ch]) { cur[ch] += 5; // 步进递增 if (cur[ch] > tar[ch]) cur[ch] = tar[ch]; done = 0; } else if (cur[ch] > tar[ch]) { cur[ch] -= 5; // 步进递减 if (cur[ch] < tar[ch]) cur[ch] = tar[ch]; done = 0; } } // 设置当前颜色(转换为0-100范围) set_led_color( cur[0] / 10, cur[1] / 10, cur[2] / 10, cur[3] / 10 ); // 颜色切换条件:当前颜色已到达目标,且停留时间超过1秒 if (done && (HAL_GetTick() - last_tick > 1000)) { // 切换到组内下一种颜色(0-6循环) group_step_idx = (group_step_idx + 1) % 7; last_tick = HAL_GetTick(); } } // 多组七彩渐变函数 void multi_group_gradient(uint8_t speed) { // 从当前组获取起始色和目标色 const uint8_t *from = gradient_groups[current_gradient_group][gradient_from_idx]; const uint8_t *to = gradient_groups[current_gradient_group][gradient_to_idx]; // 计算当前步骤的RGBW值(线性插值) uint8_t current_r = from[0] + (to[0] - from[0]) * gradient_step / 100; uint8_t current_g = from[1] + (to[1] - from[1]) * gradient_step / 100; uint8_t current_b = from[2] + (to[2] - from[2]) * gradient_step / 100; uint8_t current_w = from[3] + (to[3] - from[3]) * gradient_step / 100; // 设置当前颜色 set_led_color(current_r, current_g, current_b, current_w); // 步骤递增 gradient_step++; if (gradient_step > 100) { gradient_step = 0; // 切换到组内下一对颜色(循环7种颜色) gradient_from_idx = gradient_to_idx; gradient_to_idx = (gradient_to_idx + 1) % 7; } // 控制渐变速度 HAL_Delay(speed); } // 更新渐变任务调用 void gradient_task(void) { multi_group_gradient(30); // 调用多组渐变函数,速度30ms } // 三色跳变任务函数(在main循环中调用) void color_jump_task(void) { // 仅在跳变模式(keep_color=6)下执行 if (keep_color != 6) return; // 检查是否到达跳变时间 if (HAL_GetTick() - jump_timer >= jump_interval) { // 切换到组内下一种颜色 current_jump_color = (current_jump_color + 1) % 3; // 设置当前颜色 set_led_color( jump_color_groups[jump_group_index][current_jump_color][0], jump_color_groups[jump_group_index][current_jump_color][1], jump_color_groups[jump_group_index][current_jump_color][2], jump_color_groups[jump_group_index][current_jump_color][3] ); // 重置计时器 jump_timer = HAL_GetTick(); } } void reset_timer(void) { timer_state = TIMER_IDLE; timer_duration = 0; timer_end_time = 0; printf("定时器已重置\n"); } // 添加定时器状态处理函数 void timer_task(void) { switch (timer_state) { case TIMER_GRADIENT: // 显示红绿蓝渐变效果 if (HAL_GetTick() - gradient_start_time > 500) { // 每100ms更新一次 gradient_start_time = HAL_GetTick(); switch (gradient_steps) { case 0: // 红色 set_led_color(0, 100, 100, 100); break; case 1: // 绿色 set_led_color(100, 0, 100, 100); break; case 2: // 蓝色 set_led_color(100, 100, 0, 100); break; case 3: // 恢复原状态 // 启动定时器 timer_state = TIMER_RUNNING; timer_end_time = HAL_GetTick() + (timer_duration * 3600000); // 转换为毫秒 printf("定时器启动,将在 %d 小时后关灯\n", timer_duration); // 恢复原状态 keep_color = pre_timer_mode; set_led_color(pre_timer_r, pre_timer_g, pre_timer_b, pre_timer_w); return; } gradient_steps++; } break; case TIMER_RUNNING: // 检查是否到达定时结束时间 if (HAL_GetTick() >= timer_end_time) { timer_state = TIMER_EXPIRED; printf("定时结束,关闭灯光\n"); // 关闭所有灯光 led_off(); } break; case TIMER_EXPIRED: // 5秒后自动重置定时器 if (HAL_GetTick() - timer_end_time > 5000) { timer_state = TIMER_IDLE; printf("定时器已重置,可重新设置\n"); } break; default: break; } } /** * @brief Main program. * @retval int */ int main(void) { HAL_Init(); APP_SystemClockConfig(); __HAL_RCC_USART1_CLK_ENABLE(); // 确保USART1时钟先使能 MX_USART1_UART_Init(115200); HAL_UART_spInit(&UartHandle); // HAL_Delay(100); //串口初始化 // HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1); // 初始化TIM1用于微秒级计时 TIM14_Init(); // 初始化GPIO和EXTI APP_GpioConfig(); APP_TimPwmConfig(); // 测试定时器精度 // Test_Timer_Accuracy(); // 初始化LED LED_Init(); current_gradient_group = 0; gradient_from_idx = 0; gradient_to_idx = 1; gradient_step = 0; // 初始化为第1组第一种颜色 current_color_group = 0; group_step_idx = 0; // 初始化当前占空比数组 memcpy(cur, color_groups[0][0], sizeof(cur)); // 初始关闭LED set_led_color(0, 0, 0, 0); printf("holle world\r\n"); //light_seven(); //set_led_color(0, 0, 0, 0); // 初始关闭所有LED while (1) { // 处理定时器任务 timer_task(); // 如果定时器处于渐变状态,跳过其他任务 if (timer_state != TIMER_GRADIENT && timer_state != TIMER_EXPIRED) { // 原有模式处理... if (keep_color == 0) { gradient_task(); // 渐变 } else if (keep_color == 1) { rgbw_fade_task(); // 跳变 } else if (keep_color == 6) { color_jump_task(); // 三色跳变 } else if (keep_color == WARM_MODE) { warm_breath_task(); // 白光呼吸 } } } } void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&UartHandle); } static void APP_SystemClockConfig(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_24MHz; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSISYS; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0); } static void APP_GpioConfig(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // 配置红,绿,蓝引脚 GPIO_InitStruct.Pin = LED_ALL_PINS; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF2_TIM1; //复用为TIM1 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); //配置白引脚 GPIO_InitStruct.Pin = LED_WHITE_PIN; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF3_TIM1; //复用为TIM1 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 配置红外接收引脚(PA0)为双边沿触发 GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置外部中断 HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_1_IRQn); } /** * @brief 设置LED颜色 * @param r: 红色分量 (0-100) * @param g: 绿色分量 (0-100) * @param b: 蓝色分量 (0-100) * @param w: 白色分量 (0-100) * @retval None */ void set_led_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { color_r = r; color_g = g; color_b = b; color_w = w; uint16_t rr = (uint16_t)r * PWM_MAX / 100 ; uint16_t gg = (uint16_t)g * PWM_MAX / 100 ; uint16_t bb = (uint16_t)b * PWM_MAX / 100 ; uint16_t ww = (uint16_t)w * PWM_MAX / 100 ; // uint16_t ww = PWM_MAX - (uint16_t)w * PWM_MAX / 100 ; // 反转计算 __HAL_TIM_SET_COMPARE(&TimHandle, RED_CHANNEL, rr); __HAL_TIM_SET_COMPARE(&TimHandle, GREEN_CHANNEL, gg); __HAL_TIM_SET_COMPARE(&TimHandle, BLUE_CHANNEL, bb); __HAL_TIM_SET_COMPARE(&TimHandle, WHITE_CHANNEL, ww); } // 12种DIY颜色(每组4种,共3组) static const uint8_t diy_colors[4][4][4] = { // R组(KEY_RED) { {100, 0, 0, 0}, // 纯红 {100, 30, 0, 0}, // 红橙 {100, 0, 30, 0}, // 红紫 {100, 50, 0, 0} // 深红 }, // G组(KEY_GREEN) { {0, 100, 0, 0}, // 纯绿 {30, 100, 0, 0}, // 黄绿 {0, 100, 30, 0}, // 青绿 {50, 100, 0, 0} // 草绿 }, // B组(KEY_BLUE) { {0, 0, 100, 0}, // 纯蓝 {30, 0, 100, 0}, // 紫蓝 {0, 30, 100, 0}, // 天蓝 {0, 0, 100, 50} // 冰蓝 }, { {100, 100, 100, 0}, // 纯蓝 {71, 84, 100, 45}, // 暖白 - 偏黄红色调 {44,68, 100, 88}, // 正白 - 均衡色调 } }; static uint8_t color_index[4] = {0}; // 每组当前颜色索引 /** * @brief 关闭所有LED * @retval None */ void led_off(void) { HAL_TIM_PWM_Stop(&TimHandle, RED_CHANNEL); HAL_TIM_PWM_Stop(&TimHandle, GREEN_CHANNEL); HAL_TIM_PWM_Stop(&TimHandle, BLUE_CHANNEL); HAL_TIM_PWM_Stop(&TimHandle, WHITE_CHANNEL); //HAL_TIM_Base_Start(&TimHandle); } void led_extinct(void) { set_led_color(100,100,100,100); } void three_color (void) { led_off(); HAL_TIM_PWM_Start(&TimHandle, RED_CHANNEL); led_off(); HAL_TIM_PWM_Start(&TimHandle, GREEN_CHANNEL); } /** * @brief 处理解码后的指令 * @retval None */ void IR_ProcessCommand(void) { // 如果定时器处于渐变状态,忽略所有按键 if (timer_state == TIMER_GRADIENT) { printf("渐变中,忽略按键\n"); return; } // 如果定时器已到期,只响应KEY_ON if (timer_state == TIMER_EXPIRED && ir_data[2] != KEY_ON) { printf("灯光已关闭,请按ON键开启\n"); return; // 忽略其他按键 } ir_last_cmd = ir_data[2]; // 保存命令 // 打印完整的NEC协议数据帧 printf("\r\n=== IR Code Received ===\r\n"); printf("Address: 0x%02X%02X\r\n", ir_data[0], ir_data[1]); printf("Command: 0x%02X\r\n", ir_data[2]); printf("Cmd Inverse: 0x%02X\r\n", ir_data[3]); printf("=====================\r\n"); HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_1); // 验证命令码和其反码是否匹配 if(ir_data[2] != (uint8_t)(~ir_data[3])) { printf("Warning: Command verification failed!\r\n"); return; } switch (ir_last_cmd) { case KEY_OFF: // 保存当前颜色 saved_r = color_r; saved_g = color_g; saved_b = color_b; saved_w = color_w ; led_off(); led_extinct(); break; case KEY_ON: // 如果定时器已到期,恢复定时前的状态 if (timer_state == TIMER_EXPIRED) { timer_state = TIMER_IDLE; // 恢复PWM输出 HAL_TIM_PWM_Start(&TimHandle, RED_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, GREEN_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, BLUE_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, WHITE_CHANNEL); // 恢复之前的状态 keep_color = pre_timer_mode; set_led_color(pre_timer_r, pre_timer_g, pre_timer_b, pre_timer_w); printf("恢复定时前状态\n"); } else { // 正常处理KEY_ON HAL_TIM_PWM_Start(&TimHandle, RED_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, GREEN_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, BLUE_CHANNEL); HAL_TIM_PWM_Start(&TimHandle, WHITE_CHANNEL); set_led_color(saved_r, saved_g, saved_b, saved_w); } break; case KEY_RED: keep_color =2; //led_extinct(); set_led_color(0, 100, 100, 100); break; case KEY_FLASH: // 切换到下一组,第5次按回归第1组(0-3循环) current_color_group = (current_color_group + 1) % 4; // 重置组内颜色索引,从第一种颜色开始 group_step_idx = 0; // 立即切换到新组的第一种颜色 set_led_color( color_groups[current_color_group][group_step_idx][0] / 10, // 转换为0-100范围 color_groups[current_color_group][group_step_idx][1] / 10, color_groups[current_color_group][group_step_idx][2] / 10, color_groups[current_color_group][group_step_idx][3] / 10 ); // 重置跳变计时器 last_tick = HAL_GetTick(); // 打印当前组信息 printf("切换到七彩跳变组合 %d/4\r\n", current_color_group + 1); // 激活七彩跳变模式 keep_color = 1; break; case KEY_GREEN: keep_color =3; led_extinct(); //HAL_TIM_PWM_Start(&TimHandle, GREEN_CHANNEL); set_led_color(100, 0, 100, 100); break; case KEY_BLUE: keep_color =4; led_extinct(); //HAL_TIM_PWM_Start(&TimHandle, BLUE_CHANNEL); set_led_color(100, 100, 0, 100); break; case KEY_WHITE: keep_color =5; led_extinct(); //HAL_TIM_PWM_Start(&TimHandle, WHITE_CHANNEL); set_led_color(100, 100, 100, 0); break; case KEY_BRIGHT_UP: switch(keep_color) { case 2: // 当前为红色 color_r = (color_r < 5) ? 0 : color_r - 5; break; case 3: // 当前为绿色 color_g = (color_g < 5) ? 0 : color_g - 5; break; case 4: // 当前为蓝色 color_b = (color_b < 5) ? 0 : color_b - 5; break; case 5: // 当前为白色 color_w = (color_w < 5) ? 0 : color_w - 5; break; } set_led_color(color_r, color_g, color_b, color_w); // 刷新显示 break; case KEY_BRIGHT_DN: switch(keep_color) { case 2: // 当前为红色 color_r = (color_r + 15 > 90) ? 90 : color_r + 15; break; case 3: // 当前为绿色 color_g = (color_g + 15 > 90) ? 90 : color_g + 15; break; case 4: // 当前为蓝色 color_b = (color_b + 15 > 90) ? 90 : color_b + 15; break; case 5: // 当前为白色 color_w = (color_w + 15 > 90) ? 90: color_w + 15; break; } set_led_color(color_r, color_g, color_b, color_w); // 刷新显示 break; case KEY_WARM: // 切换到白光呼吸模式 keep_color = 7; // 循环切换三种亮度等级 warm_brightness_level = (warm_brightness_level + 1) % WARM_BRIGHTNESS_LEVELS; printf("白光呼吸模式: 亮度等级 %d/%d (最大亮度 %d%%)\r\n", warm_brightness_level + 1, WARM_BRIGHTNESS_LEVELS, warm_max_brightness[warm_brightness_level]); break; case KEY_TIMER: // 允许在空闲、渐变结束或定时结束状态下设置定时 if (timer_state == TIMER_IDLE || timer_state == TIMER_EXPIRED) { printf("定时按键被按下\n"); // 重置定时器状态 timer_state = TIMER_SETTING; timer_duration = 0; // 重置定时时长 // 增加定时时长(2小时步进,循环0-12小时) timer_duration = (timer_duration + 2) % 14; if (timer_duration == 0) timer_duration = 2; // 跳过0小时 // 保存当前灯光状态(仅在未过期时) if (timer_state != TIMER_EXPIRED) { pre_timer_mode = keep_color; pre_timer_r = color_r; pre_timer_g = color_g; pre_timer_b = color_b; pre_timer_w = color_w; } // 进入渐变显示状态 timer_state = TIMER_GRADIENT; gradient_start_time = HAL_GetTick(); gradient_steps = 0; // 重置渐变步骤计数器 printf("定时设置: %d小时后关灯\n", timer_duration); } break; case KEY_SPEED_UP: breath_speed = (breath_speed > 5) ? breath_speed - 5 : 5; printf("呼吸加快: %d ms\n", breath_speed); break; case KEY_SPEED_DOWN: printf("11111111111\r\n"); breath_speed = (breath_speed < 100) ? breath_speed + 10 : 100; printf("呼吸加快: %d ms\n", breath_speed); break; case KEY_SMOOTH: // 切换到下一组渐变,第5次按回归第1组(0-3循环) current_gradient_group = (current_gradient_group + 1) % 4; // 重置渐变参数,从新组第1种颜色开始 gradient_from_idx = 0; gradient_to_idx = 1; gradient_step = 0; // 立即显示新组的起始颜色 set_led_color( gradient_groups[current_gradient_group][0][0], gradient_groups[current_gradient_group][0][1], gradient_groups[current_gradient_group][0][2], gradient_groups[current_gradient_group][0][3] ); // 打印当前组信息 printf("切换到七彩渐变组合 %d/4\r\n", current_gradient_group + 1); // 激活渐变模式 keep_color = 0; break; case KEY_MAGENTA: // 切换到下一组组合,第7次按回归第1组 jump_group_index = (jump_group_index + 1) % 6; // 重置当前组内颜色索引,从第一种颜色开始 current_jump_color = 0; // 立即切换到新组的第一种颜色 set_led_color( jump_color_groups[jump_group_index][current_jump_color][0], jump_color_groups[jump_group_index][current_jump_color][1], jump_color_groups[jump_group_index][current_jump_color][2], jump_color_groups[jump_group_index][current_jump_color][3] ); // 重置计时器 jump_timer = HAL_GetTick(); // 打印当前组合信息 printf("切换到三色跳变组合 %d/6\r\n", jump_group_index + 1); // 激活跳变模式 keep_color = 6; break; default: // 未知命令 break; } } /** * @brief 处理重复码 * @retval None */ void IR_ProcessRepeat(void) { ir_last_cmd = ir_data[2]; // 保存命令 printf("Command: 0x%02X\r\n", ir_data[2]); static uint32_t last_repeat_time = 0; if (HAL_GetTick() - last_repeat_time < 500) return; last_repeat_time = HAL_GetTick(); switch (ir_last_cmd) { case KEY_RED: color_index[0] = (color_index[0] + 1) % 4; set_led_color( diy_colors[0][color_index[0]][0], diy_colors[0][color_index[0]][1], diy_colors[0][color_index[0]][2], diy_colors[0][color_index[0]][3] ); break; case KEY_GREEN: color_index[1] = (color_index[1] + 1) % 4; set_led_color( diy_colors[1][color_index[1]][0], diy_colors[1][color_index[1]][1], diy_colors[1][color_index[1]][2], diy_colors[1][color_index[1]][3] ); break; case KEY_BLUE: color_index[2] = (color_index[2] + 1) % 4; set_led_color( diy_colors[2][color_index[2]][0], diy_colors[2][color_index[2]][1], diy_colors[2][color_index[2]][2], diy_colors[2][color_index[2]][3] ); break; case KEY_WHITE: color_index[3] = (color_index[3] + 1) % 4; set_led_color( diy_colors[3][color_index[3]][0], diy_colors[3][color_index[3]][1], diy_colors[3][color_index[3]][2], diy_colors[3][color_index[3]][3] ); break; default: break; } } /** * @brief Error executing function. * @param None * @retval None */ void APP_ErrorHandler(void) { while (1) { } } 为什么我使用定时按键只能定时一次
最新发布
08-09
<think>根据用户的问题描述和提供的引用内容,用户在使用STM32的定时按键功能时遇到了只能触发一次的问题。结合引用[2]中的按键处理代码和引用[4]中关于按键检测的说明,我们可以分析可能的原因和解决方法。 ### 问题分析 1. **中断标志未清除**:在按键中断服务程序中,如果没有正确清除中断标志,可能导致后续中断无法触发。 2. **状态变量未重置**:按键状态变量(如`keyval`)在松开后未正确重置,导致后续按键无法被识别。 3. **定时器未正确配置或使能**:在按键按下时启动定时器,但在按键松开后未禁用定时器,或者定时器中断处理不当。 4. **全局变量被意外修改**:用于记录按键状态的全局变量可能在别处被修改,导致状态机紊乱。 5. **硬件问题**:按键硬件电路问题(如接触不良)或GPIO配置错误(如上拉/下拉电阻配置不正确)。 ### 解决步骤 #### 1. 检查中断标志清除 在中断服务程序(如`EXTI0_IRQHandler`)中,必须清除对应的中断标志。参考引用[2]的代码: ```c void EXTI0_IRQHandler(void) { // ... 处理逻辑 EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志 } ``` 确保每次中断后都执行了清除操作。 #### 2. 检查按键状态机 参考引用[2]的代码,按键状态变量`keyval`用于记录按键按下/松开状态。确保在按键松开后重置状态: ```c if(keyval && !KEY1_STATE()) { keyval = 0; // 重置按键状态 // ... 其他处理 } ``` #### 3. 定时器管理 - **启动定时器**:按键按下时启动定时器(如`TIM_Cmd(TIM2, ENABLE);`)。 - **停止定时器**:在按键松开并处理完成后,应停止定时器,避免持续计数。引用[2]的代码中未停止定时器,可能导致后续按键时定时器仍在运行,计数值不准确。 建议在按键松开处理完后停止定时器: ```c TIM_Cmd(TIM2, DISABLE); // 停止定时器 ``` #### 4. 检查全局变量 确保用于记录按键状态和计数的全局变量(如`g_key1.tim2_cnt`)只在中断服务程序或受保护的环境(如关闭中断)下修改,避免多线程冲突。 #### 5. 硬件检查 - 确认按键GPIO配置正确(输入模式、上拉/下拉)。 - 使用万用表检查按键按下时是否正常接通。 ### 改进代码示例 结合引用[2]和引用[4]的思路,改进按键中断服务程序: ```c // 全局结构体(定义在合适的位置) typedef struct { uint8_t valcnt; // 按键计数器 uint16_t tim2_cnt; // 定时器溢出计数 } Key_TypeDef; Key_TypeDef g_key1 = {0}; void EXTI0_IRQHandler(void) { static uint8_t keyval = 0; // 静态按键状态:0-松开,1-按下 if (EXTI_GetITStatus(EXTI_Line0) != RESET) { if (!keyval && (KEY1_STATE() == 0)) { // 假设按键按下为低电平 keyval = 1; g_key1.tim2_cnt = 0; // 重置定时器计数 TIM_SetCounter(TIM2, 0); // 可选:重置定时器计数值 TIM_Cmd(TIM2, ENABLE); // 启动定时器 } else if (keyval && (KEY1_STATE() != 0)) { // 按键松开 keyval = 0; TIM_Cmd(TIM2, DISABLE); // 停止定时器 // 判断按键时长(假设定时器溢出周期为10ms) if (g_key1.tim2_cnt >= 3 && g_key1.tim2_cnt < 200) { // 30ms~2000ms g_key1.valcnt++; // 短按计数 printf("Short press, duration: %d * 10ms\n", g_key1.tim2_cnt); } g_key1.tim2_cnt = 0; // 清除计数 } EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志 } } ``` ### 补充说明 - **定时器中断配置**:定时器TIM2应配置为每隔一定时间(如10ms)产生一次更新中断,在中断服务程序中递增`g_key1.tim2_cnt`。 - **防抖处理**:上述代码通过定时器计数实现了软件防抖,避免在主循环中延时。 ### 相关问题 1. 如何配置STM32的外部中断(EXTI)? 2. STM32定时器如何实现精确的时间测量? 3. 如何优化按键检测以支持长按、短按和连按功能? 4. 在多任务环境下如何安全地共享按键状态变量? 5. STM32的GPIO输入模式有哪几种?如何选择? [^1]: STM32 输入捕获功能 [^2]: STM32按键的长按和连按(二) [^3]: stm32 工业按键检测_stm32按键识别 [^4]: stm32 工业按键检测_STM32f103按键检测程序实现长按短按</think>### STM32定时按键只能触发一次的可能原因和解决方法 #### 1. **中断标志未清除**(最常见原因) - **问题**:按键中断触发后未清除中断挂起标志,导致后续中断被阻塞。 - **解决**:在中断服务函数末尾必须调用清除标志函数: ```c EXTI_ClearITPendingBit(EXTI_LineX); // X为对应中断线 ``` 参考引用[2]中的代码末尾操作[^2]。 #### 2. **状态变量未重置** - **问题**:记录按键状态的静态变量(如`keyval`)在松开后未正确归零。 - **解决**:在按键松开逻辑中确保重置状态: ```c if(keyval && !KEY_STATE()) { keyval = 0; // 重置状态 // ...其他操作 } ``` 参考引用[2]的状态机实现[^2]。 #### 3. **定时器未重新使能** - **问题**:定时器在首次触发后未重新启动(如未调用`TIM_Cmd(TIMx, ENABLE)`)。 - **解决**:在按键按下时显式启动定时器: ```c if(!keyval && KEY_STATE()) { TIM_Cmd(TIM2, ENABLE); // 每次按下都重启定时器 } ``` 参考引用[2]的定时器控制逻辑[^2]。 #### 4. **全局变量冲突** - **问题**:用于计时的全局变量(如`g_key1.tim2_cnt`)被其他代码修改。 - **解决**: - 使用`volatile`声明全局变量:`volatile uint16_t tim2_cnt;` - 确保仅在中断服务函数中修改该变量[^4]。 #### 5. **硬件配置问题** - **可能原因**: - GPIO模式错误(未配置为输入+上拉/下拉) - 外部中断线配置错误 - **检查点**: ```c GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; // 双边沿触发 ``` #### 6. **中断优先级冲突** - **问题**:高优先级中断阻塞按键中断。 - **解决**:调整中断优先级(NVIC配置): ```c NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 适当降低优先级 ``` ### 完整调试流程 1. **检查中断标志清除** → 2. **验证状态机重置** → 3. **用逻辑分析仪抓取GPIO波形** → 4. **检查变量内存地址是否冲突**。 > 示例修正代码(基于引用[2]优化): > ```c > void EXTI0_IRQHandler(void) { > static uint8_t keyval = 0; > if(EXTI_GetITStatus(EXTI_Line0)) { > if(!keyval && !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) { // 按下 > keyval = 1; > g_key.tim_cnt = 0; > TIM_Cmd(TIM2, ENABLE); // 关键:每次按下重启定时器 > } > else if(keyval && GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) { // 松开 > keyval = 0; // 状态重置 > TIM_Cmd(TIM2, DISABLE); > if(g_key.tim_cnt > 30) handle_key(); // 处理按键 > } > EXTI_ClearITPendingBit(EXTI_Line0); // 必须清除标志! > } > } > ``` ### 相关问题 1. 如何实现STM32按键的长按、短按和连按检测? 2. STM32外部中断(EXTI)配置有哪些注意事项? 3. 如何用定时器实现精准的按键消抖? 4. 多按键检测时如何避免资源冲突? 5. STM32中断嵌套的优先级规则是什么? [^1]: STM32 输入捕获功能 [^2]: STM32按键的长按和连按(二) [^3]: stm32 工业按键检测_stm32按键识别 [^4]: stm32 工业按键检测_STM32f103按键检测程序实现长按短按
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值