STM32从零到量产开发:四路继电器工业控制模块开发— 继电器模块bsp_led.h与 bsp_led.c文件 bsp_led.h实现 /** ****************************************************************************** * @file bsp_led.h * @brief 四路LED指示灯底层驱动对外接口 * @note RUN/COMM/FAULT/RS232四路独立指示灯,非阻塞毫秒时基状态机 ****************************************************************************** */ # ifndef __BSP_LED_H # define __BSP_LED_H # ifdef __cplusplus extern "C" { # endif # include "main.h" /** * @brief LED模块初始化 * @note 初始化全部计时静态变量,上电熄灭所有LED */ void BSP_LED_Init ( void ) ; /** * @brief LED状态机刷新函数,主循环周期调用(5~10ms) * @note 处理RUN慢闪、FAULT快闪、COMM/RS232脉冲超时熄灭、自检计时 */ void BSP_LED_Refresh ( void ) ; /** * @brief Modbus RS485总线通信脉冲触发,收发数据点亮50ms */ void BSP_CommPulse ( void ) ; /** * @brief RS232上位机交互脉冲触发,收到串口指令点亮50ms */ void BSP_RS232Pulse ( void ) ; /** * @brief 设置故障告警灯开关 * @param active 1=开启FAULT快闪 0=关闭故障灯 */ void BSP_LED_SetFault ( uint8_t active) ; /** * @brief 四路LED整机自检,全部点亮1.5s后自动恢复正常灯效 */ void BSP_LED_SelfTest ( void ) ; # ifdef __cplusplus } # endif # endif bsp_led.c实现 /** ****************************************************************************** * @file bsp_led.c * @brief 4路独立LED指示灯驱动实现(非阻塞状态机 + 通信瞬时脉冲)(#LED redesign) * @details 依托HAL_GetTick毫秒时基实现无阻塞计时,区分常驻闪烁、瞬时脉冲、全局自检三类灯效逻辑 * 硬件统一低电平有效:GPIO_PIN_RESET=点亮,GPIO_PIN_SET=熄灭 ****************************************************************************** */ # include "bsp_led.h" # include "main.h" /* ===================== 1. 顶层时序配置宏(统一管理灯效时长参数) ===================== */ /* 闪烁周期单位ms,匹配产品规格书时序要求 * RUN运行灯 1Hz 慢闪;FAULT故障灯 10Hz 快闪 */ # define LED_SLOW_PERIOD 1000u /* RUN完整闪烁周期 1000ms */ # define LED_SLOW_ON_TIME 500u /* RUN单次点亮持续时长 */ # define LED_FAST_PERIOD 100u /* FAULT完整闪烁周期 100ms */ # define LED_FAST_ON_TIME 50u /* FAULT单次点亮持续时长 */ # define LED_PULSE_MS 50u /* RS485/RS232通信脉冲点亮宽度 */ # define LED_TEST_MS 1500u /* 四路LED硬件自检总持续时长 */ /* ===================== 2. 模块静态私有状态变量(仅本文件内部读写) ===================== */ static uint32_t s_run_last= 0u ; /* RUN运行灯慢闪计时基准时间戳 * 读写位置:仅BSP_LED_Refresh内部读写 */ static uint8_t s_fault_active= 0u ; /* 故障告警总开关 1=开启FAULT快闪,0=关闭告警 * 赋值入口:BSP_LED_SetFault();读取判断:BSP_LED_Refresh故障分支 */ static uint32_t s_fault_last= 0u ; /* FAULT故障灯快闪计时基准时间戳 * 赋值入口:BSP_LED_SetFault();读取判断:BSP_LED_Refresh故障分支 */ static uint32_t s_comm_off_at= 0u ; /* RS485通信脉冲熄灭截止时间戳,0代表无有效脉冲 * 赋值入口:BSP_LED_CommPulse(),赋值为当前滴答+LED_PULSE_MS;读取清零:BSP_LED_Refresh脉冲分支 */ static uint32_t s_rs232_off_at= 0u ; /* RS232串口脉冲熄灭截止时间戳,0代表无有效脉冲 * 赋值入口:BSP_RS232Pulse(),赋值为当前滴答+LED_PULSE_MS;读取清零:BSP_LED_Refresh脉冲分支 */ static uint32_t s_test_off_at= 0u ; /* LED全局自检模式截止时间戳,0代表无自检任务 * 赋值入口:BSP_LED_SelfTest(),赋值为当前滴答+LED_TEST_MS;读取清零:BSP_LED_Refresh自检分支 */ /* ===================== 3. 底层私有硬件IO适配函数(全文件统一调用,硬件逻辑单点维护) ===================== */ /** * @brief LED底层引脚输出统一适配接口 * @param port: LED对应GPIO端口 * @param pin: LED对应引脚编号 * @param on: 逻辑状态 1=点亮,0=熄灭 * @note 硬件低电平有效,on=1输出GPIO_PIN_RESET,on=0输出GPIO_PIN_SET */ static void LED_Write ( GPIO_TypeDef* port, uint16_t pin, uint8_t on) { HAL_GPIO_WritePin ( port, pin, on? GPIO_PIN_RESET: GPIO_PIN_SET) ; } /* ===================== 4. 对外公共业务接口(按程序运行时序:初始化→核心刷新→状态控制→自检) ===================== */ /** * @brief LED模块初始化,复位全部计时、状态缓存,四路LED上电统一熄灭 */ void BSP_LED_Init ( void ) { s_run_last= HAL_GetTick ( ) ; s_fault_last= HAL_GetTick ( ) ; s_fault_active= 0u ; s_comm_off_at= 0u ; s_rs232_off_at= 0u ; s_test_off_at= 0u ; /* 四路LED初始全部熄灭 */ LED_Write ( STATUS_LED_GPIO_Port, STATUS_LED_Pin, 0u ) ; LED_Write ( COMM_LED_GPIO_Port, COMM_LED_Pin, 0u ) ; LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 0u ) ; LED_Write ( RS232_LED_GPIO_Port, RS232_LED_Pin, 0u ) ; } /** * @brief LED全局状态机定时刷新主函数,主循环轮询调用 * @note 推荐调用周期 5~10ms;依赖HAL_GetTick()系统毫秒时基 * 执行优先级:自检逻辑 > RUN常驻闪烁 > FAULT故障闪烁 > 通信脉冲超时熄灭 */ void BSP_LED_Refresh ( void ) { uint32_t now= HAL_GetTick ( ) ; uint32_t elapsed; /* 【双向关联指引】 * 变量 s_test_off_at:定义在文件顶部【模块静态私有状态变量】区块 * 变量赋值位置:文件末尾函数 BSP_LED_SelfTest(),调用时赋值为 HAL_GetTick() + LED_TEST_MS(1500ms) * 变量非0代表自检任务正在执行;自检优先级最高,直接跳过所有常规灯效逻辑 */ if ( s_test_off_at!= 0u ) { /* 有符号差值判断,兼容uwTick 49.7天溢出归零场景 */ if ( ( ( int32_t ) ( now- s_test_off_at) ) >= 0 ) { s_test_off_at= 0u ; s_run_last= now; /* 复位RUN计时基准,避免自检结束闪烁相位偏移 */ /* 自检到期熄灭全部通信、故障灯 */ LED_Write ( COMM_LED_GPIO_Port, COMM_LED_Pin, 0u ) ; LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 0u ) ; LED_Write ( RS232_LED_GPIO_Port, RS232_LED_Pin, 0u ) ; } else { return ; } } /* RUN运行灯1Hz常驻慢闪逻辑,不受故障、通信状态干扰 * 计时变量 s_run_last:定义在顶部静态变量区,仅本分支读写 */ elapsed= now- s_run_last; if ( elapsed>= LED_SLOW_ON_TIME&& elapsed< LED_SLOW_PERIOD) { LED_Write ( STATUS_LED_GPIO_Port, STATUS_LED_Pin, 0u ) ; } else if ( elapsed>= LED_SLOW_PERIOD) { LED_Write ( STATUS_LED_GPIO_Port, STATUS_LED_Pin, 1u ) ; s_run_last= now; } else { LED_Write ( STATUS_LED_GPIO_Port, STATUS_LED_Pin, 1u ) ; } /* FAULT故障灯10Hz快闪逻辑,仅故障激活时执行 * 关联变量:s_fault_active(总开关)、s_fault_last(计时戳) * 变量定义:文件顶部静态变量;赋值入口:BSP_LED_SetFault() */ if ( s_fault_active) { elapsed= now- s_fault_last; if ( elapsed>= LED_FAST_ON_TIME&& elapsed< LED_FAST_PERIOD) { LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 0u ) ; } else if ( elapsed>= LED_FAST_PERIOD) { LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 1u ) ; s_fault_last= now; } else { LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 1u ) ; } } /* RS485通信脉冲计时标志 s_comm_off_at:定义在文件顶部【模块静态私有状态变量】区块 * 赋值入口:BSP_LED_CommPulse(),赋值为 HAL_GetTick() + LED_PULSE_MS(50ms) * 变量非0代表存在未结束通信脉冲,时间戳超时后熄灭LED并清零标记 */ if ( s_comm_off_at!= 0u && ( ( int32_t ) ( now- s_comm_off_at) ) >= 0 ) { LED_Write ( COMM_LED_GPIO_Port, COMM_LED_Pin, 0u ) ; s_comm_off_at= 0u ; } /* RS232串口通信脉冲计时标志 s_rs232_off_at:定义在文件顶部【模块静态私有状态变量】区块 * 赋值入口:BSP_RS232Pulse(),赋值为 HAL_GetTick() + LED_PULSE_MS(50ms) * 变量非0代表存在未结束串口脉冲,时间戳超时后熄灭LED并清零标记 */ if ( s_rs232_off_at!= 0u && ( ( int32_t ) ( now- s_rs232_off_at) ) >= 0 ) { LED_Write ( RS232_LED_GPIO_Port, RS232_LED_Pin, 0u ) ; s_rs232_off_at= 0u ; } } /** * @brief 故障告警状态开关控制 * @param active: 1=触发故障,FAULT灯10Hz快闪;0=清除故障,FAULT灯直接熄灭 * @note 【关联指引】本函数为 s_fault_active / s_fault_last 唯一赋值入口 * 两个变量在BSP_LED_Refresh()故障闪烁分支做读取判断,控制FAULT灯周期翻转 */ void BSP_LED_SetFault ( uint8_t active) { //上层要开启故障,且当前原本无故障 if ( active&& ! s_fault_active) { s_fault_active= 1u ; // 打开故障总开关 s_fault_last= HAL_GetTick ( ) ; // 记录故障触发时刻,作为闪烁计时起点 LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 1u ) ; } //上层要清除故障,且当前正处于故障状态 else if ( ! active&& s_fault_active) { s_fault_active= 0u ; // 关闭故障总开关 LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 0u ) ; } } /** * @brief Modbus RS485总线通信脉冲触发 * @note 【关联指引】本函数为 s_comm_off_at 唯一赋值入口 * s_comm_off_at = HAL_GetTick() + LED_PULSE_MS,赋值后在BSP_LED_Refresh()脉冲分支做超时判断熄灭 * 在Modbus有效帧收发位置调用,单次触发50ms点亮脉冲;频繁收发会持续刷新超时,保持常亮 */ void BSP_CommPulse ( void ) { s_comm_off_at= HAL_GetTick ( ) + LED_PULSE_MS; //记录LED_PULSE_MS后要熄灭的截止时间戳 LED_Write ( COMM_LED_GPIO_Port, COMM_LED_Pin, 1u ) ; //立刻点亮通信指示灯,BSP_LED_Refresh()没有亮灯的动作,要在这里亮灯 } /** * @brief RS232串口通信活动脉冲触发 * @note 【关联指引】本函数为 s_rs232_off_at 唯一赋值入口 * s_rs232_off_at = HAL_GetTick() + LED_PULSE_MS,赋值后在BSP_LED_Refresh()脉冲分支做超时判断熄灭 * 在RS232命令解析、数据收发位置调用,单次触发50ms点亮脉冲 */ void BSP_RS232Pulse ( void ) { s_rs232_off_at= HAL_GetTick ( ) + LED_PULSE_MS; LED_Write ( RS232_LED_GPIO_Port, RS232_LED_Pin, 1u ) ; } /** * @brief 启动四路LED硬件自检,全部同步点亮1.5s * @note 【关联指引】本函数为 s_test_off_at 唯一赋值入口 * s_test_off_at = HAL_GetTick() + LED_TEST_MS,赋值后在BSP_LED_Refresh()头部做自检优先级判断 * 自检优先级最高,运行期间屏蔽所有闪烁、脉冲逻辑;非阻塞,1.5s后自动恢复常规灯效,用于产线硬件接线校验 */ void BSP_LED_SelfTest ( void ) { s_test_off_at= HAL_GetTick ( ) + LED_TEST_MS; LED_Write ( STATUS_LED_GPIO_Port, STATUS_LED_Pin, 1u ) ; LED_Write ( COMM_LED_GPIO_Port, COMM_LED_Pin, 1u ) ; LED_Write ( FAULT_LED_GPIO_Port, FAULT_LED_Pin, 1u ) ; LED_Write ( RS232_LED_GPIO_Port, RS232_LED_Pin, 1u ) ; }