该代码已经在项目上推广应用,有需要自取。不多说,直接贴代码,代码中已注释比较清晰,有疑问或不对的地方,欢迎评论区留言。




1、呼吸灯参数配置相关
//呼吸灯感应时模式枚举值
typedef enum {
LED_PWM_ACT_OPEN, //感应时射灯正常点亮
LED_PWM_ACT_CLOSE //感应时呼吸灯关闭
} Led_Pwm_ACT_Status;
//设置为感应时呼吸灯关闭
#define LED_PWM_ACT_STATUS_SET LED_PWM_ACT_CLOSE
// 呼吸灯参数, ;理论一个呼吸循环周期4.5秒
#define MIN_DUTY 100 // 最小占空比计数(25%)
#define MAX_DUTY 1000 // 最大占空比计数(100%)
#define DUTY_STEP 3 // 占空比变化步长
#define UPDATE_INTERVAL 1 // 占空比更新间隔(定时器周期数)
#define BREATH_PERIOD_US 1000 // 周期改为10ms(10000us)
#define LED_PWM_GPIO_PIN GPIO_PIN_5 //指示灯绿,PWM输出, 呼吸灯效
#define LED_PWM_GPIO_Port CW_GPIOA
//呼吸灯工作状态枚举值
typedef enum {
LED_PWM_IDLE, //已初始化,但未开启
LED_PWM_ON_FIXED, //固定占空比,常亮无呼吸效果
LED_PWM_ON_BREATH, //呼吸灯效果
LED_PWM_DISABLE //禁用呼吸指示灯,硬件失能,适用于无指示灯
} PwmState;
2、硬件初始化
//呼吸灯PB5初始化
void LED_PEM_Conf_Init(void)
{
PB05_DIGTAL_ENABLE();
PB05_DIR_OUTPUT();
PB05_PUSHPULL_ENABLE();
PB05_AFx_GTIM1CH2(); // 配置PB05复用为GTIM1 CH2
//GTIM定时器初始化
GTIM_InitTypeDef GTIM_InitStruct = {0};
GTIM_OCModeCfgTypeDef GTIM_OCModeCfgStruct = {DISABLE,DISABLE,0};
/* 定时器时基配置(保持1MHz计数频率) */
GTIM_InitStruct.AlignMode = GTIM_ALIGN_MODE_EDGE; // 边沿对齐模式
GTIM_InitStruct.ARRBuffState = GTIM_ARR_BUFF_EN; // 使能自动重装载缓冲
GTIM_InitStruct.Direction = GTIM_DIRECTION_UP; // 向上计数
GTIM_InitStruct.EventOption = GTIM_EVENT_NORMAL; // 正常事件模式
GTIM_InitStruct.Prescaler = 8 - 1; // 关键修改:8分频(8MHz/8=1MHz)
GTIM_InitStruct.PulseMode = GTIM_PULSE_MODE_DIS; // 禁用单次脉冲模式
GTIM_InitStruct.ReloadValue = BREATH_PERIOD_US - 1; // 周期值=1000-1=999(1ms周期)
GTIM_InitStruct.UpdateOption = GTIM_UPDATE_DIS; // 使能更新事件
GTIM_TimeBaseInit(CW_GTIM1, >IM_InitStruct); // 初始化定时器
/* 配置CH2通道为PWM模式 */
GTIM_OCModeCfgStruct.FastMode = DISABLE; // 禁用快速模式
GTIM_OCModeCfgStruct.OCMode = GTIM_OC_MODE_PWM1; // PWM模式1
GTIM_OCModeCfgStruct.OCPolarity = GTIM_OC_POLAR_NONINVERT; // 输出极性非反相(高电平有效)
GTIM_OCModeCfgStruct.PreloadState = DISABLE; // 使能预装载
GTIM_OC2ModeCfg(CW_GTIM1, >IM_OCModeCfgStruct); // 配置CH2通道
/* 设置初始占空比并使能输出 */
GTIM_SetCompare2(CW_GTIM1, PosWidth - 1); // 初始CH2占空比100%
GTIM_OC2Cmd(CW_GTIM1, ENABLE); // 使能CH2输出
//GTIM_ITConfig(CW_GTIM1, GTIM_IT_UPDATA, ENABLE); // 使能更新中断
//NVIC_SetPriority(GTIM1_IRQn, 0); // 高优先级确保响应及时
//NVIC_EnableIRQ(GTIM1_IRQn); //只产生PWM不触发中断;
GTIM_Cmd(CW_GTIM1, ENABLE); // 启动定时器
}
3、呼吸灯定时刷新PWM占空比,实现呼吸效果
//呼吸灯更新占空比
void LED_PWM_CTR_SUB(void){
/* 静态计数器控制更新频率 */
/* 根据方向调整占空比 */
if(Dir) // 方向1:占空比增大(从25%到100%)
{
PosWidth += DUTY_STEP;
if(PosWidth >= MAX_DUTY)
{
PosWidth = MAX_DUTY;
Dir = 0; // 到达最大值,切换方向
}
}else{ // 方向0:占空比减小(从100%到25%)
PosWidth -= DUTY_STEP;
if(PosWidth <= MIN_DUTY)
{
PosWidth = MIN_DUTY;
Dir = 1; // 到达最小值,切换方向
}
}
/* 更新PWM占空比 */
GTIM_SetCompare2(CW_GTIM1, PosWidth - 1); //CH2 占空比更新
}
// 新增:开启呼吸灯
void BreathLED_Start(void)
{
// 恢复PWM输出
GTIM_OC1Cmd(CW_GTIM1, ENABLE);
// 恢复初始占空比
PosWidth = MAX_DUTY;
Dir = 0;
GTIM_SetCompare1(CW_GTIM1, PosWidth - 1);
}
// 新增:关闭呼吸灯
void BreathLED_Stop(void)
{
// 关闭PWM输出,可选择保持熄灭或常亮
GTIM_OC2Cmd(CW_GTIM1, DISABLE);
// 确保LED熄灭
PB05_SETLOW(); // 根据硬件电路可能需要改为PB05_SETHIGH()
}
4、使用中断的方式定时更新占空比,这里使用的RTC闹钟
//配置时钟
void RCC_Conf_Init(void)
{
IWDT_InitTypeDef IWDT_InitStruct = {0};
/* HSI和 LSI使能 */
SYSCTRL_HSI_Enable(SYSCTRL_HSIOSC_DIV6); //48/6=8; HSIOSC = 8MHz; HSI = 8MHz;
SYSCTRL_LSI_Enable();
/* 1. 设置HCLK和PCLK的分频系数 */
SYSCTRL_HCLKPRS_Config(SYSCTRL_HCLK_DIV1);
SYSCTRL_PCLKPRS_Config(SYSCTRL_PCLK_DIV1);
SYSCTRL_SystemCoreClockUpdate(8000000); //更新系统核心时钟
/* 2. 配置外设时钟 */
__SYSCTRL_IWDT_CLK_ENABLE(); //IWDT时钟使能
__SYSCTRL_GPIOA_CLK_ENABLE(); //GPIOA时钟使能
__SYSCTRL_GPIOB_CLK_ENABLE(); //GPIOB时钟使能
__SYSCTRL_GTIM1_CLK_ENABLE(); // GTIM1时钟使能
//__SYSCTRL_I2C_CLK_ENABLE(); //IIC时钟使能
__SYSCTRL_RTC_CLK_ENABLE(); // RTC时钟使能
//__SYSCTRL_UART1_CLK_ENABLE(); //UART1时钟使能
//初始化IWDT
IWDT_InitStruct.IWDT_ITState = ENABLE;
IWDT_InitStruct.IWDT_OverFlowAction = IWDT_OVERFLOW_ACTION_RESET; //超时复位, IWDT_OVERFLOW_ACTION_INT只触发中断,须手动复位;
IWDT_InitStruct.IWDT_Pause = IWDT_SLEEP_CONTINUE; //休眠状态保持持续计数
IWDT_InitStruct.IWDT_Prescaler = IWDT_Prescaler_DIV512; // IWDT使用LSI为计时时钟,分频后计时间隔为15.6ms
IWDT_InitStruct.IWDT_ReloadValue = IWDT_2_SECS; // 2s,超时未喂狗将溢出
IWDT_InitStruct.IWDT_WindowValue = 0xFFF; //设置窗口值,0xFFF表示关闭窗口功能,任何时候都可以喂狗;其他值,计数器值小于窗口值时才能喂狗;
IWDT_Init(&IWDT_InitStruct);
}
5、interrupts_cw32l010.c 中中断处理函数实现
/**
* @brief This funcation handles RTC
*/
void RTC_IRQHandler(void)
{
/* USER CODE BEGIN */
USER_RTC_IRQHandler(); //用户程序,实现10MS定时
/* USER CODE END */
}
6、处理中断,然后在while中根据cnt_wakeup标志位,定时调用LED_PWM_CTR_SUB()函数:
//RTC中断服务函数(每10ms触发一次,翻转指示灯)
void USER_RTC_IRQHandler(void)
{
// 检查自动唤醒中断标志
if(RTC_GetITState(RTC_IT_AWTIMER) != RESET)
{
// 清除中断标志
//
RTC_ClearITPendingBit(RTC_IT_AWTIMER);
cnt_wakeup = 1; //累计唤醒次数
// 开启全局中断
}
}
7、低功耗实现,在循环结束后,喂狗休眠,休眠前为了实现更低的功耗,可能需要按需关闭不必要的外设,特别是时钟,并确保相关引脚的电平状态正常。唤醒后需重新配置相关外设和时钟。
int32_t main(void)
{
//初始化代码,执行一次的代码在下方:
//循环执行
while(1){
if(cnt_wakeup){
cnt_wakeup = 0;
//10MS RTC定时时间到了>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
tick_10ms++;
if(tick_10ms % 5 == 0){
//每50毫秒执行一次的任务****************
}
//每10ms 执行一次的任务写下方***************
}
//定时喂狗:每次唤醒都喂狗一次
IWDT_Refresh(); //100MS喂狗一次;使用大于做判断比取模更合理,避免中断导致的累加
//休眠判断
if(cnt_wakeup == 0 && cnt_gpio_int == 0) __WFI(); // 无待处理中断,进入低功耗,等待中断唤醒
}
}

1419

被折叠的 条评论
为什么被折叠?



