LED程序
配置问题
在选择写法前,请回答以下问题(一次性全部回答):
- 需要几个 LED?(如:状态灯1个 + 错误灯1个 共2个)
- 有效电平?(高电平点亮 / 低电平点亮)
- 是否需要闪烁功能?(是 / 否)
根据回答自动推荐写法(不再让用户手动选):
- 1个LED,无闪烁 → 直接写裸操作(无需任何封装)
- 多个LED,无复杂闪烁 → 面向对象写法
- 需要闪烁 / 多种闪烁模式 → Scriptable LED写法
🔴面向对象写法
// ── drv_led.h ────────────────────────────────────────────────────────────
typedef struct {
uint16_t remain_blink_ms;
uint16_t toggle_tick;
uint16_t blink_interval_ms;
void (*hw_on)(void);
void (*hw_off)(void);
void (*hw_toggle)(void); // hw 是 Hardware 的意思
} LED_Obj_t;
// 暴露给外部的 API,第一个参数永远是"对象句柄"
void LED_Init(LED_Obj_t *led, void (*on)(void), void (*off)(void), void (*toggle)(void));
void LED_On(LED_Obj_t *led);
void LED_Off(LED_Obj_t *led);
void LED_StartBlink(LED_Obj_t *led, uint16_t total_ms, uint16_t interval_ms);
void LED_Process(LED_Obj_t *led); // 放在 1ms 定时中断里调用
// ── drv_led.c ────────────────────────────────────────────────────────────
// LED_Init:把函数地址赋给结构体里的指针,初始化时默认关灯
void LED_Init(LED_Obj_t *led, void (*on)(void), void (*off)(void), void (*toggle)(void)) {
led->remain_blink_ms = 0;
led->toggle_tick = 0;
led->hw_on = on;
led->hw_off = off;
led->hw_toggle = toggle;
if(led->hw_off) led->hw_off();
}
// LED_On / LED_Off:只需传对象指针,不需要再传底层硬件函数
void LED_On(LED_Obj_t *led) {
if (led == NULL) return;
led->remain_blink_ms = 0; // 清除闪烁状态,强制常亮
if (led->hw_on) led->hw_on();
}
void LED_Off(LED_Obj_t *led) {
if (led == NULL) return;
led->remain_blink_ms = 0;
if (led->hw_off) led->hw_off();
}
// LED_StartBlink:启动闪烁,例:LED_StartBlink(&led, 3000, 50) 闪3秒,50ms反转一次
void LED_StartBlink(LED_Obj_t *led, uint16_t total_ms, uint16_t interval_ms) {
led->remain_blink_ms = total_ms;
led->blink_interval_ms = interval_ms;
led->toggle_tick = 0;
}
// LED_Process:放在 1ms 定时中断里调用,驱动闪烁计时
void LED_Process(LED_Obj_t *led) {
if (led->remain_blink_ms > 0) {
led->remain_blink_ms--;
led->toggle_tick++;
if (led->toggle_tick >= led->blink_interval_ms) {
led->toggle_tick = 0;
if(led->hw_toggle) led->hw_toggle(); // 防野指针
}
if (led->remain_blink_ms == 0) {
if(led->hw_off) led->hw_off(); // 结束时强制关灯
led->toggle_tick = 0;
}
}
}
// ── main.c 使用示例 ───────────────────────────────────────────────────────
// LED_Obj_t running_led;
// LED_Init(&running_led, stm32_led_on, stm32_led_off, stm32_led_toggle);🔴Scriptable LED,可传递信息
// ── drv_led_scriptable.h ─────────────────────────────────────────────────
// 定义一帧动作
typedef struct {
uint8_t action; // 1=亮, 0=灭, 0xFF=循环重头开始
uint16_t time_ms; // 维持这个状态多长时间
} LED_Frame_t;
// LED 对象里存当前播放的指针,而不是 remain_blink_ms
typedef struct {
const LED_Frame_t *current_pattern; // 当前正在播放的模式数组
uint8_t frame_index; // 播放到第几帧了
uint16_t frame_tick; // 当前这一帧坚持了多久了
// ... 硬件函数指针同面向对象写法 ...
} LED_Player_t;
void LED_Play(LED_Player_t *led, const LED_Frame_t *pattern);
// ── 闪烁模式定义(const 存 Flash,不占 RAM)────────────────────────────
const LED_Frame_t PATTERN_ERROR[] = {
{1, 100}, {0, 100}, {1, 100}, {0, 100}, {1, 100}, {0, 1000}, // 闪三下,停1秒
{0xFF, 0} // 循环指令
};
const LED_Frame_t PATTERN_WIFI_CONNECTING[] = {
{1, 50}, {0, 500}, // 滴--- 滴---
{0xFF, 0}
};
// ── drv_led_scriptable.c ─────────────────────────────────────────────────
void LED_Play(LED_Player_t *led, const LED_Frame_t *pattern) {
led->current_pattern = pattern;
led->frame_index = 0;
led->frame_tick = 0;
// 根据 pattern[0].action 立刻执行第一次亮灭
}🔴C++写法
// 模板参数:引脚所在的端口,引脚号,有效电平
// 编译期绑定引脚,编译出来不占任何额外 RAM
template<typename PortType, PortType* Port, uint16_t Pin, bool ActiveLevel = true>
class Led {
public:
static void init() {
// 配置 GPIO 寄存器的底层逻辑
}
static void on() {
if constexpr (ActiveLevel) {
Port->BSRR = Pin;
} else {
Port->BRR = Pin;
}
}
static void off() { /* ... */ }
static void toggle() { /* ... */ }
};
// 使用示例
using StatusLed = Led<GPIO_TypeDef, GPIOA, GPIO_PIN_5, true>; // 高电平点亮
using ErrorLed = Led<GPIO_TypeDef, GPIOB, GPIO_PIN_12, false>; // 低电平点亮
int main() {
StatusLed::init();
StatusLed::on(); // 编译后直接等价于:GPIOA->BSRR = 0x0020;
}