在嵌入式系统开发中,LED控制是一个常见的需求,不同项目对LED的效果和功能要求各不相同。传统的LED控制方式需要针对不同的需求编写不同的代码,增加了开发的工作量和复杂度。为了解决这一问题,我开发了一种扩展性较强的LED框架,该框架脱离底层细节,只关注应用层,提供一种通用的LED控制方案,使得开发人员可以轻松实现各种LED效果,极大地提高了开发效率和灵活性。
背景介绍
在嵌入式系统中,LED作为一种简单直观的状态指示器,被广泛应用于各种设备中,例如单片机开发板、智能家居设备、工业控制系统等。在实际应用中,LED的效果和功能需求千差万别,有时需要实现常亮、闪烁、呼吸等效果,有时需要实现流水灯、跑马灯等动态效果。然而,传统的LED控制方式往往需要针对不同的需求编写不同的代码,导致代码冗余、维护困难,严重影响了开发效率和代码质量。
问题分析
在实际开发中,每次修改LED的功能或效果时,都需要重新编写代码,这样会导致代码重复、维护困难等问题。而且,随着项目的不断迭代和功能的不断增加,LED控制的需求也会不断变化,传统的硬编码方式很难满足快速、灵活地进行修改和定制的需求。
框架设计思路
基于以上问题,我设计了一种基于事件驱动的LED控制框架,该框架将LED控制从底层解耦,只关注应用层,提供了一种通用的LED控制方案,使得开发人员可以轻松实现各种LED效果,极大地提高了开发效率和灵活性。该框架主要包括以下几个关键点:
- LED事件表管理:通过定义LED事件表,将不同的LED控制功能映射到相应的事件上,实现LED控制的灵活配置和管理。
- LED状态控制:提供统一的LED状态控制接口,使得开发人员可以方便地调用各种LED效果,并且可以根据实际需求进行定制和扩展。
核心代码解析
下面是LED框架的核心代码,包括LED事件表初始化、事件表管理、LED状态控制等功能的实现原理和关键代码片段:
// LED事件表初始化
void LED_EventTableInit(LED_EventTableItem_t ledEvent[])
{
if (ledEvent == NULL)
{
ledPrintf(LOG_ERROR, "ledEvent is NULL\r\n");
return;
}
memset(ledPriorityTable, 0, sizeof(ledPriorityTable));
memset(ledEventTable, 0, sizeof(ledEventTable));
memcpy(ledEventTable, ledEvent, sizeof(ledEventTable));
}
// 将LED事件加入事件表
void LED_EventAdd(LED_Event_t ledEvent) {
for (size_t i = 0; i < EVENT_MAX; i++) {
if (ledEventTable[i].event == ledEvent) {
if (ledPriorityTable[ledEventTable[i].priority].event != ledEvent) {
// 如果当前优先级中存在不同的LED事件,则执行以下操作
ledEventTable[i].config.currentTime = TIME_Get(); // 记录开始执行时的时间戳
if (ledPriorityTable[ledEventTable[i].priority].event != 0) {
if (LED_SetState == NULL) {
ledPrintf(LOG_ERROR, "LED_SetState is NULL!!!\r\n");
return;
}
// 关闭其他LED事件与本LED事件不同的LED灯
LED_SetState((ledPriorityTable[ledEventTable[i].priority].config.ledMask)^(ledEventTable[i].config.ledMask), LED_OFF);
}
}
// 更新LED事件表中对应优先级的LED事件
ledPriorityTable[ledEventTable[i].priority] = ledEventTable[i];
return;
}
}
}
// 将LED事件从事件表删除
void LED_EventDelete(LED_Event_t ledEvent) {
for (size_t i = 0; i < PRIORITY_MAX; i++) {
if (ledPriorityTable[i].event == ledEvent) {
// 找到与传入的LED事件相同的事件,将其清零并关闭LED
memset(&ledPriorityTable[i], 0, sizeof(LED_EventTableItem_t));
if (LED_SetState == NULL) {
ledPrintf(LOG_ERROR, "LED_SetState is NULL!!!\r\n");
return;
}
LED_SetState(ledEventTable[ledEvent].config.ledMask, LED_OFF);
return;
}
}
}
// LED事件处理函数
void LED_EventHandle(void) {
uint8_t ret;
int i;
for (i = (PRIORITY_MAX - 1); i >= 0; i--) {
// 从高优先级开始往下查询
if (ledPriorityTable[i].event != 0) {
// 如果当前优先级中存在LED事件,则执行对应的LED事件处理函数
ledPrintf(LOG_DEBUG, "led_priority_table[%d].event %d\r\n", i, ledPriorityTable[i].event);
ret = ledPriorityTable[i].ledEventHandler(&ledPriorityTable[i].config);
// 如果正在执行事件功能返回1,执行完事件功能返回0
// 例:每30秒闪烁1次,闪烁的1s里返回1,闪烁完成返回0
if (ret) {
return;
}
}
}
}
uint8_t LED_Lighting_Update(LED_Config_t *led);
uint8_t LED_AlternatingBlinking_Update(LED_Config_t *led);
uint8_t LED_Blinking_Update(LED_Config_t *led);
uint8_t LED_Breathing_Update(LED_Config_t *led);
uint8_t LED_Running_Update(LED_Config_t *led);
uint8_t LED_MARQUEE_Update(LED_Config_t *led);
// LED更新函数
uint8_t LED_Update(LED_Config_t *led) {
uint8_t ret;
switch (led->state) {
case LED_LIGHT:
ret = LED_Lighting_Update(led);
break;
#if (BLINK_ENABLE)
case LED_ALTERNATING_BLINK:
ret = LED_AlternatingBlinking_Update(led);
break;
#endif
#if (ALTERNATING_BLINK_ENABLE)
case LED_BLINKING:
ret = LED_Blinking_Update(led);
break;
#endif
#if (BREATH_ENABLE)
case LED_BREATHING:
ret = LED_Breathing_Update(led);
break;
#endif
#if (RUNNING_ENABLE)
case LED_RUNNING:
ret = LED_Running_Update(led);
break;
#endif
#if (MARQUEE_ENABLE)
case LED_MARQUEE:
ret = LED_MARQUEE_Update(led);
break;
#endif
default:
break;
}
return ret;
}
程序解析
以下是对LED框架核心代码的详细解析:
LED事件表初始化(LED_EventTableInit)
LED_EventTableInit
函数用于初始化LED事件表。- 首先检查传入的
ledEvent
是否为空指针,如果为空,则打印错误日志并返回。 - 然后,将
ledPriorityTable
和ledEventTable
数组清零。 - 最后,通过
memcpy
将传入的ledEvent
复制到ledEventTable
数组中。
将LED事件加入事件表(LED_EventAdd)
LED_EventAdd
函数用于将LED事件添加到事件表中。- 通过循环遍历
ledEventTable
数组,查找是否存在与传入的ledEvent
相同的事件。 - 如果找到相同的事件,则检查当前优先级中是否已经存在相同的LED事件,如果不存在,则执行以下操作:
- 记录当前时间戳。
- 如果当前优先级中存在其他LED事件,则关闭该LED事件与本LED事件不同的LED灯。
- 更新LED事件表中对应优先级的LED事件为传入的LED事件。
将LED事件从事件表删除(LED_EventDelete)
LED_EventDelete
函数用于从事件表中删除LED事件。- 通过循环遍历
ledPriorityTable
数组,查找是否存在与传入的ledEvent
相同的事件。 - 如果找到相同的事件,则将该事件对应的项清零,并关闭LED。
LED事件处理函数(LED_EventHandle)
LED_EventHandle
函数用于处理LED事件。- 通过循环遍历
ledPriorityTable
数组,从高优先级开始往下查询是否存在LED事件。 - 如果当前优先级中存在LED事件,则执行对应的LED事件处理函数,并根据返回值判断是否继续执行下一个事件。
LED更新函数(LED_Update)
LED_Update
函数根据传入的LED状态选择对应的更新函数,并返回更新结果。
以上是LED框架核心代码的解析,通过对每个函数的功能和实现原理进行分析,可以更好地理解LED框架的设计和使用方式。
LED框架的功能模块
该LED框架主要包括以下几个功能模块:
- 常亮状态控制(LED_LIGHT): 使LED保持常亮状态,适用于需要持续指示某种状态的场景。
- 闪烁状态控制(LED_BLINKING): 控制LED周期性地闪烁,可用于提醒、警告等场景。
- 交替闪烁状态控制(LED_ALTERNATING_BLINK): 控制LED两组灯交替闪烁,更加生动有趣。
- 呼吸状态控制(LED_BREATHING): 控制LED呼吸效果,实现渐变的亮度变化。
- 流水灯状态控制(LED_RUNNING): 控制LED像流水一样流动,增加动态感。
- 跑马灯状态控制(LED_MARQUEE): 控制LED像跑马灯一样循环移动,适用于展示性的场景。
用户可根据宏打开或关闭某个模块,节省空间开支。
使用示例
下面是LED框架的使用示例,演示了如何初始化LED事件表、添加LED事件、处理LED事件以及更新LED状态的过程:
// 输出LED状态
void set_led(GPIO_TypeDef *gpio_port, uint32_t pin, uint8_t state)
{
if (state)
{
LL_GPIO_SetOutputPin(gpio_port, pin);
}
else
{
LL_GPIO_ResetOutputPin(gpio_port, pin);
}
}
// 对LED进行状态设置
void set_led_state(uint16_t led_site, uint8_t state)
{
switch (led_site)
{
case USER_LED1:
set_led(USER_LED1_GPIO_PORT, USER_LED1_PIN, state);
break;
case USER_LED2:
set_led(USER_LED2_GPIO_PORT, USER_LED2_PIN, state);
break;
case USER_LED3:
set_led(USER_LED3_GPIO_PORT, USER_LED3_PIN, state);
break;
case USER_LED4:
set_led(USER_LED4_GPIO_PORT, USER_LED4_PIN, state);
break;
case USER_LED5:
set_led(USER_LED5_GPIO_PORT, USER_LED5_PIN, state);
break;
default:
break;
}
}
// LED状态设置回调函数
void set_led_multi(uint16_t led, uint8_t state)
{
for (size_t led_site = USER_LED1; led_site < USER_LED_MAX; led_site++)
{
if (led & LED_CHANNEL(led_site))
{
set_led_state(led_site, state);
}
}
}
// 不充电事件LED事件
uint8_t not_charging_led_handle(LED_Config_t *led_config)
{
uint16_t batteryLevel;
static uint32_t flow_interval = 0;
uint16_t led_channels[] = {USER_LED2, USER_LED3, USER_LED4, USER_LED5};
uint16_t charge_levels[] = {charg_level_gear1,charg_level_gear2,charg_level_gear3,charg_level_gear4};
size_t num_levels = sizeof(charge_levels) / sizeof(charge_levels[0]);
led_config->ledMask = 0;
// 获取电池电量百分比
batteryLevel = BAT_DischargeVol();
// 迭代充电等级,根据电量设置LED通道
for (size_t i = 0; i < num_levels; ++i)
{
if (batteryLevel >= charge_levels[i])
{
// 添加对应的LED通道
led_config->ledMask |= LED_CHANNEL(led_channels[i]);
}
else
{
// 熄灭电量不足的LED灯
set_led_multi(LED_CHANNEL(led_channels[i]), LED_OFF);
}
}
if (LED_Update(led_config))
{
return 1;
}
// 返回0会继续执行下个优先级任务,返回1则不运行其他低优先级任务
return 0;
}
// 充电事件LED事件
uint8_t charging_led_handle(LED_Config_t *led_config)
{
if (LED_Update(led_config))
{
return 1;
}
// 返回0会继续执行下个优先级任务,返回1则不运行其他低优先级任务
return 0;
}
// 缺电LED事件
uint8_t bat_low_led_handle(LED_Config_t *led_config)
{
if (!LED_Update(led_config))
{
LED_EventDelete(EVENT_LOW_BATTERY); // 删除缺电事件
state_machine.board_state = BOARD_STOP; // 进入休眠状态
}
// 返回0会继续执行下个优先级任务,返回1则不运行其他低优先级任务
return 0;
}
// led初始化
void user_led_init(void)
{
// 初始化时钟
USER_LED1_GPIO_CLK_ENABLE();
USER_LED2_GPIO_CLK_ENABLE();
USER_LED3_GPIO_CLK_ENABLE();
USER_LED4_GPIO_CLK_ENABLE();
USER_LED5_GPIO_CLK_ENABLE();
// 初始化GPIO
LL_GPIO_SetPinMode(USER_LED1_GPIO_PORT, USER_LED1_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(USER_LED2_GPIO_PORT, USER_LED2_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(USER_LED3_GPIO_PORT, USER_LED3_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(USER_LED4_GPIO_PORT, USER_LED4_PIN, LL_GPIO_MODE_OUTPUT);
LL_GPIO_SetPinMode(USER_LED5_GPIO_PORT, USER_LED5_PIN, LL_GPIO_MODE_OUTPUT);
LED_SetStateInit(set_led_multi); // 设置LED灯状态的回调函数
SET_TimeGetInit(LL_GetTick); // 获取时间戳的回调函数
// LED事件初始化
LED_EventTableItem_t led_event_category[EVENT_MAX] = {
[EVENT_NOT_CHARGING] = {
// 不充电时LED显示电量。
.event = EVENT_NOT_CHARGING,
.priority = PRIORITY_4, // 优先级为4
// 参与本次事件的LED
.config.ledMask = LED_CHANNEL(USER_LED2) | LED_CHANNEL(USER_LED3) | LED_CHANNEL(USER_LED4) | LED_CHANNEL(USER_LED5),
// 常亮1s为一个周期,没有次数,没有循环,即一直常亮下去
.config.state = LED_LIGHT,
.config.runNum = 0, // 次数为0次数无限大,一直执行
.ledEventHandler = not_charging_led_handle,
},
[EVENT_CHARGING] = {
// 充电时LED跑马灯工作。
.event = EVENT_CHARGING,
.priority = PRIORITY_4,// 优先级为4
// 参与本次事件的LED
.config.ledMask = LED_CHANNEL(USER_LED2) | LED_CHANNEL(USER_LED3) | LED_CHANNEL(USER_LED4) | LED_CHANNEL(USER_LED5),
// 每个LED亮起的时间为250ms,跑完一轮间隔500ms。
.config.state = LED_MARQUEE,
.config.marqueeParams.cycleTime = 250,
.config.marqueeParams.intervalTime = 500,
.config.runNum = 0, // 次数为0次数无限大,一直执行
.ledEventHandler = charging_led_handle,
},
[EVENT_LOW_BATTERY] = {
/// 缺电时(电压低于 8.4V±0.1V)故障LED闪烁3次
.event = EVENT_LOW_BATTERY,
.priority = PRIORITY_4,// 优先级为4
// 参与本次事件的LED
.config.ledMask = LED_CHANNEL(USER_LED1),
// 灭500ms,亮500ms,即1s为一个周期,次数3次
.config.state = LED_BLINKING,
.config.blinkParams.offTime = 500,
.config.blinkParams.onTime = 500,
.config.runNum = 3, // 次数为0次数无限大,一直执行
.ledEventHandler = bat_low_led_handle,
},
};
LED_EventTableInit(led_event_category);
}
int main(void)
{
user_led_init(); // 初始化LED事件组
LED_EventAdd(EVENT_NOT_CHARGING); // 添加不充电的LED事件
while(1)
{
LED_EventHandle(); // LED事件处理函数
}
}
程序解析
以下是对新增的LED事件处理函数、LED初始化函数以及主函数的详细解析:
不充电事件LED事件处理函数(not_charging_led_handle)
not_charging_led_handle
函数用于处理不充电事件下LED的状态。- 首先声明了变量
batteryLevel
和flow_interval
以及LED通道数组和电量等级数组。 - 获取电池电量百分比。
- 迭代充电等级,根据电量设置LED通道。
- 如果LED更新成功,则返回1,否则返回0。
充电事件LED事件处理函数(charging_led_handle)
charging_led_handle
函数用于处理充电事件下LED的状态。- 如果LED更新成功,则返回1,否则返回0。
缺电LED事件处理函数(bat_low_led_handle)
bat_low_led_handle
函数用于处理缺电LED事件。- 如果LED更新成功,则删除缺电事件并进入休眠状态。
LED初始化函数(user_led_init)
user_led_init
函数用于初始化LED。- 首先初始化LED的时钟和GPIO。
- 然后设置LED灯状态的回调函数和获取时间戳的回调函数。
- 最后初始化LED事件,包括不充电事件、充电事件和缺电事件。
主函数(main)
- 主函数中首先调用
user_led_init
函数,初始化LED事件组。 - 然后调用
LED_EventAdd(EVENT_NOT_CHARGING)
,添加不充电的LED事件。 - 最后进入一个无限循环,在循环中不断调用
LED_EventHandle
函数,用于处理LED事件。
整体框架程序
/**
* @file led.c
* @author cyWu (1917507415@qq.com)
* @brief LED应用层框架
* @version 0.1
* @date 2024-02-22
*
* @copyright Copyright (c) 2024
* */
#include "led.h"
static LED_EventTableItem_t ledPriorityTable[PRIORITY_MAX];
static LED_EventTableItem_t ledEventTable[EVENT_MAX];
static LED_SetState_t LED_SetState;
static TIME_Get_t TIME_Get;
// 设置led状态回调函数
void LED_SetStateInit(LED_SetState_t setState)
{
LED_SetState = setState;
}
// 设置获取时间戳回调函数
void SET_TimeGetInit(TIME_Get_t getTime)
{
TIME_Get = getTime;
}
// LED事件表初始化
void LED_EventTableInit(LED_EventTableItem_t ledEvent[])
{
if (ledEvent == NULL)
{
ledPrintf(LOG_ERROR, "ledEvent is NULL\r\n");
return;
}
memset(ledPriorityTable, 0, sizeof(ledPriorityTable));
memset(ledEventTable, 0, sizeof(ledEventTable));
memcpy(ledEventTable, ledEvent, sizeof(ledEventTable));
}
// 将LED事件加入事件表
void LED_EventAdd(LED_Event_t ledEvent) {
for (size_t i = 0; i < EVENT_MAX; i++) {
if (ledEventTable[i].event == ledEvent) {
if (ledPriorityTable[ledEventTable[i].priority].event != ledEvent) {
ledEventTable[i].config.currentTime = TIME_Get(); // 记录开始执行时的时间戳
if (ledPriorityTable[ledEventTable[i].priority].event != 0) {
if (LED_SetState == NULL) {
ledPrintf(LOG_ERROR, "LED_SetState is NULL!!!\r\n");
return;
}
LED_SetState((ledPriorityTable[ledEventTable[i].priority].config.ledMask)^(ledEventTable[i].config.ledMask), LED_OFF);
}
}
ledPriorityTable[ledEventTable[i].priority] = ledEventTable[i]; // 相同优先级的会覆盖
return;
}
}
}
// 将LED事件从事件表删除
void LED_EventDelete(LED_Event_t ledEvent) {
for (size_t i = 0; i < PRIORITY_MAX; i++) {
if (ledPriorityTable[i].event == ledEvent) {
memset(&ledPriorityTable[i], 0, sizeof(LED_EventTableItem_t));
if (LED_SetState == NULL) {
ledPrintf(LOG_ERROR, "LED_SetState is NULL!!!\r\n");
return;
}
LED_SetState(ledEventTable[ledEvent].config.ledMask, LED_OFF);
return;
}
}
}
// LED事件处理函数
void LED_EventHandle(void) {
uint8_t ret;
int i;
for (i = (PRIORITY_MAX - 1); i >= 0; i--) { // 从高优先级开始往下查询
if (ledPriorityTable[i].event != 0) {
ledPrintf(LOG_DEBUG, "led_priority_table[%d].event %d\r\n", i, ledPriorityTable[i].event);
ret = ledPriorityTable[i].ledEventHandler(&ledPriorityTable[i].config); // 执行对应LED事件处理函数
// 正在执行事件功能返回1,执行完事件功能返回0,例:每30秒闪烁1次,闪烁的1s里返回1,闪烁完成返回0,目的是使间隔的30秒内可以运行别的LED事件
if (ret) {
return;
}
}
}
}
// 计算时间差
uint32_t GetTickDiff(uint32_t meiosis)
{
uint32_t temp = TIME_Get();
if (temp >= meiosis)
{
temp = temp - meiosis;
}
else
{
temp = 0xFFFFFFFFU - meiosis + temp;
}
return temp;
}
uint8_t LED_Lighting_Update(LED_Config_t *led);
uint8_t LED_AlternatingBlinking_Update(LED_Config_t *led);
uint8_t LED_Blinking_Update(LED_Config_t *led);
uint8_t LED_Breathing_Update(LED_Config_t *led);
uint8_t LED_Running_Update(LED_Config_t *led);
uint8_t LED_MARQUEE_Update(LED_Config_t *led);
// LED更新函数
uint8_t LED_Update(LED_Config_t *led) {
uint8_t ret;
switch (led->state) {
case LED_LIGHT:
ret = LED_Lighting_Update(led);
break;
#if (BLINK_ENABLE)
case LED_ALTERNATING_BLINK:
ret = LED_AlternatingBlinking_Update(led);
break;
#endif
#if (ALTERNATING_BLINK_ENABLE)
case LED_BLINKING:
ret = LED_Blinking_Update(led);
break;
#endif
#if (BREATH_ENABLE)
case LED_BREATHING:
ret = LED_Breathing_Update(led);
break;
#endif
#if (RUNNING_ENABLE)
case LED_RUNNING:
ret = LED_Running_Update(led);
break;
#endif
#if (MARQUEE_ENABLE)
case LED_MARQUEE:
ret = LED_MARQUEE_Update(led);
break;
#endif
default:
break;
}
return ret;
}
// LED常亮状态更新函数
static uint8_t LED_Lighting_Update(LED_Config_t *led)
{
// 以1s为周期计算已经进行了多少次常亮
uint16_t blinkCount = GetTickDiff(led->currentTime) / 1000;
// 判断是否大于运行次数
if (led->runNum > 0 && blinkCount >= led->runNum) {
LED_SetState(led->ledMask, LED_OFF); // 关闭LED
// 达到指定次数后停止闪烁
return 0;
}
LED_SetState(led->ledMask, LED_ON); // 常亮
return 1;
}
// LED闪烁状态更新函数
#if (BLINK_ENABLE)
static uint8_t LED_Blinking_Update(LED_Config_t *led)
{
// 周期时间
uint32_t run_cycle = led->blinkParams.onTime + led->blinkParams.offTime;
// 计算已经进行了多少次闪烁
uint16_t blinkCount = GetTickDiff(led->currentTime) / run_cycle;
// 计算当前时间在周期中的位置
uint32_t sec_left = GetTickDiff(led->currentTime) % run_cycle;
// 判断是否大于运行次数
if (led->runNum > 0 && blinkCount >= led->runNum) {
LED_SetState(led->ledMask, LED_OFF); // 关闭LED
// 达到指定次数后停止闪烁
return 0;
}
// 判断当前时间是否在一个周期内的亮灭时间段内
if (sec_left <= led->blinkParams.onTime) {
LED_SetState(led->ledMask, led->blinkParams.orDer ? LED_OFF : LED_ON); // 先灭后亮
} else if (sec_left <= run_cycle) {
LED_SetState(led->ledMask, led->blinkParams.orDer ? LED_ON : LED_OFF); // 先亮后灭
}
return 1;
}
#endif
// LED交替闪烁状态更新函数
#if (ALTERNATING_BLINK_ENABLE)
static uint8_t LED_AlternatingBlinking_Update(LED_Config_t *led)
{
// 计算已经进行了多少次交替
uint16_t blinkCount = GetTickDiff(led->currentTime) / led->alternatingBlinkParams.Cycle;
// 计算当前时间在周期中的位置
uint32_t sec_left = GetTickDiff(led->currentTime) % led->alternatingBlinkParams.Cycle;
// 判断是否大于运行次数
if (led->runNum > 0 && blinkCount >= led->runNum) {
LED_SetState(led->ledMask, LED_OFF); // 关闭LED
// 达到指定次数后停止闪烁
return 0;
}
// 计算每一组的时间片
uint32_t timeSlice = led->alternatingBlinkParams.Cycle / ALTERNAT_GROUP;
// 判断当前时间是否在哪一组的亮时间段内
for (size_t i = 0; i < ALTERNAT_GROUP; i++)
{
if (sec_left > (timeSlice*i) && sec_left <= (timeSlice*(i+1)))
{
LED_SetState(led->alternatingBlinkParams.Group[i],LED_ON); // 亮
}else
{
LED_SetState(led->alternatingBlinkParams.Group[i],LED_OFF); // 灭
}
}
return 1;
}
#endif
// LED呼吸状态更新函数
#if (BREATH_ENABLE)
static uint8_t LED_Breathing_Update(LED_Config_t *led) {
//记录占空比
uint8_t result = 0;
// 计算增减步数
uint8_t stepNum = (led->breathParams.maxCycle - led->breathParams.minCycle) / led->breathParams.step;
// 计算呼吸周期总时间
uint32_t breathCycleTime = led->breathParams.onTime + led->breathParams.offTime;
// 计算已经进行了多少次呼吸
uint16_t breathCount = GetTickDiff(led->currentTime) / breathCycleTime;
// 计算当前时间在周期中的位置
uint32_t timeInCycle = GetTickDiff(led->currentTime) % breathCycleTime;
// 静态变量,用于记录上次更新时间
static uint32_t lastUpdateTime;
// 判断是否大于运行次数
if (led->runNum > 0 && breathCount >= led->runNum) {
led->breathParams.pwmCallback(led->breathParams.minCycle); // 关闭LED
// 达到指定次数后停止呼吸
return 0;
}
// 根据当前呼吸周期阶段进行占空比调整
if (led->breathParams.currentCycle >= (led->breathParams.maxCycle * 2)) {
led->breathParams.currentCycle = led->breathParams.minCycle;
} else {
// 计算当前阶段每个梯度的时间
uint32_t stepTime = (led->breathParams.currentCycle >= led->breathParams.maxCycle) ?
(led->breathParams.offTime / stepNum) : (led->breathParams.onTime / stepNum);
// 检查距离上次更新时间是否超过了应该增加的时间
if (GetTickDiff(lastUpdateTime) >= stepTime) {
// 增加占空比梯度
led->breathParams.currentCycle += led->breathParams.step;
// 更新上次更新时间
lastUpdateTime = TIME_Get();
}
}
// 返回当前占空比
if (led->breathParams.currentCycle <= led->breathParams.maxCycle) {
result = led->breathParams.currentCycle;
} else {
result = (led->breathParams.maxCycle * 2) - led->breathParams.currentCycle;
}
led->breathParams.pwmCallback(result);
return 1;
}
#endif
// LED流水灯状态更新函数
#if (RUNNING_ENABLE)
static uint8_t LED_Running_Update(LED_Config_t *led) {
// 记录 LED 灯的数量
uint8_t count = 0;
// 记录 LED 灯的映射
uint8_t mask = led->ledMask;
// 获取最低位的 LED 灯
uint16_t Running_low = (led->ledMask & (~led->ledMask+1));
// 计算 LED 灯的数量
while (mask) {
mask &= (mask - 1);
count++;
}
// 获取当前已经进行了多少次流水
uint16_t flowCount = GetTickDiff(led->currentTime) / led->runningParams.cycleTime;
// 判断是否超过运行次数
if (led->runNum > 0 && flowCount >= led->runNum) {
LED_SetState(led->ledMask, LED_OFF); // 关闭LED
// 达到指定次数后停止流水
return 0;
}
// 判断是否到达下次流水的间隔时间
if (GetTickDiff(led->runningParams.lastCycleTime) >= led->runningParams.intervalTime) {
// 判断是否到达 LED 点亮的间隔时间
if (GetTickDiff(led->runningParams.lastLEDTime) >= led->runningParams.cycleTime) {
// 判断是否已经点亮所有 LED
if (led->runningParams.numLEDs >= count) {
// 如果已经点亮所有 LED,则将 LED 灭掉并更新时间
LED_SetState(led->runningParams.currentSite, LED_OFF);
led->runningParams.lastCycleTime = TIME_Get();
led->runningParams.numLEDs = 0;
led->runningParams.currentSite = 0;
}else
{
// 更新 LED 灯的数量和位置
led->runningParams.numLEDs++;
led->runningParams.currentSite <<= 1;
led->runningParams.currentSite |= Running_low;
// 点亮 LED 并更新时间
LED_SetState(led->runningParams.currentSite, LED_ON);
led->runningParams.lastLEDTime = TIME_Get();
}
}
}
return 1;
}
#endif
// LED跑马灯状态更新函数
#if (MARQUEE_ENABLE)
static uint8_t LED_MARQUEE_Update(LED_Config_t *led) {
// 记录 LED 灯的数量
uint8_t count = 0;
// 记录 LED 灯的映射
uint8_t mask = led->ledMask;
// 获取最低位的 LED 灯
uint16_t Running_low = (led->ledMask & (~led->ledMask+1));
// 计算 LED 灯的数量
while (mask) {
mask &= (mask - 1);
count++;
}
// 获取当前已经进行了多少次跑马
uint16_t flowCount = GetTickDiff(led->currentTime) / led->marqueeParams.cycleTime;
// 判断是否超过运行次数
if (led->runNum > 0 && flowCount >= led->runNum) {
LED_SetState(led->ledMask, LED_OFF); // 关闭LED
// 达到指定次数后停止跑马
return 0;
}
// 判断是否到达下次跑马的间隔时间
if (GetTickDiff(led->marqueeParams.lastCycleTime) >= led->marqueeParams.intervalTime) {
// 判断是否到达 LED 点亮的间隔时间
if (GetTickDiff(led->marqueeParams.lastLEDTime) >= led->marqueeParams.cycleTime) {
// 判断是否已经点亮所有 LED
if (led->marqueeParams.numLEDs >= count) {
// 如果已经点亮所有 LED,则将 LED 灭掉并更新时间
LED_SetState(led->marqueeParams.currentSite, LED_OFF);
led->marqueeParams.lastCycleTime = TIME_Get();
led->marqueeParams.numLEDs = 0;
led->marqueeParams.currentSite = 0;
}else
{
LED_SetState(led->marqueeParams.currentSite, LED_OFF);
// 更新 LED 灯的数量和位置
led->marqueeParams.numLEDs++;
if (led->marqueeParams.currentSite == 0)
{
led->marqueeParams.currentSite = Running_low;
}else
{
led->marqueeParams.currentSite <<= 1;
}
// 点亮 LED 并更新时间
LED_SetState(led->marqueeParams.currentSite, LED_ON);
led->marqueeParams.lastLEDTime = TIME_Get();
}
}
}
return 1;
}
#endif
/**
* @file led.h
* @author cyWu (1917507415@qq.com)
* @brief LED应用层框架
* @version 0.1
* @date 2024-02-22
*
* @copyright Copyright (c) 2024
*
*/
#ifndef LED_H
#define LED_H
#include <main.h>
#include <stdint.h>
#define BLINK_ENABLE 1
#define ALTERNATING_BLINK_ENABLE 1
#define BREATH_ENABLE 0
#define RUNNING_ENABLE 0
#define MARQUEE_ENABLE 1
// 定义LED开关状态
#define LED_ON 1
#define LED_OFF 0
// 定义LED通道宏
#define LED_CHANNEL(user_led) (1 << (user_led))
// LED状态枚举
typedef enum
{
LED_LIGHT, // 常亮状态
LED_BLINKING, // 闪烁状态
LED_ALTERNATING_BLINK, // 交替闪烁模式
LED_BREATHING, // 呼吸状态
LED_RUNNING, // 流水灯状态
LED_MARQUEE // 跑马灯状态
} LED_State_t;
// LED事件枚举
typedef enum
{
EVENT_NULL, // 无事件
EVENT_LOCK, // 锁定事件
EVENT_LOCK_WARN, // 锁定通知事件
EVENT_UNLOCK, // 解锁事件
EVENT_POWER_TEST, // 上电事件
EVENT_NOT_CHARGING, // 不充电事件
EVENT_CHARGING, // 充电事件
EVENT_FULL_BATTERY, // 充满电事件
EVENT_LOW_BATTERY, // 缺电事件
EVENT_CURRENT_HIGH, // 电机电流过大事件
EVENT_MAX // 事件数量
} LED_Event_t;
// LED事件优先级枚举
typedef enum
{
PRIORITY_1,
PRIORITY_2,
PRIORITY_3,
PRIORITY_4,
PRIORITY_5,
PRIORITY_MAX // 优先级枚举的最大值,用于数组大小
} LED_EventPriority_t;
#if (BLINK_ENABLE)
// LED 闪烁参数结构体
__packed typedef struct
{
uint8_t orDer; // 用于判断先开还是先关,1:先灭后亮,0:先亮后灭
uint32_t onTime; // 亮时间(毫秒)
uint32_t offTime; // 灭时间(毫秒)
} LED_BlinkParams_t;
#endif
#if (ALTERNATING_BLINK_ENABLE)
#define ALTERNAT_GROUP 2
// LED交替闪烁参数结构体
__packed typedef struct
{
uint16_t Group[ALTERNAT_GROUP]; // 交替组(2组)
uint32_t Cycle; // 交替周期时间(毫秒)
} LED_AlternatingBlinkParams_t;
#endif
#if (BREATH_ENABLE)
// LED 呼吸参数结构体
typedef struct
{
uint8_t maxCycle; // 最大占空比(0~100%)
uint8_t minCycle; // 最小占空比(0~100%)
uint8_t currentCycle; // 当前占空比(0~100%)
uint8_t step; // 增减梯度
uint32_t onTime; // 呼吸亮时间(毫秒)
uint32_t offTime; // 呼吸灭时间(毫秒)
void (*pwmCallback)(uint8_t Duty); // PWM回调函数
} LED_BreathParams_t;
#endif
#if (RUNNING_ENABLE)
// LED 流水灯参数结构体
__packed typedef struct
{
uint32_t cycleTime; // 流水灯周期时间(毫秒)
uint32_t intervalTime; // 下次运行的间隔时间(毫秒)
uint32_t lastCycleTime; // 上次周期运行完的记录时间戳
uint32_t lastLEDTime; // 上个 LED 点亮的记录时间戳
uint32_t currentSite; // 流水灯的当前位置
uint32_t numLEDs; // 流水灯的 LED 数量
} LED_RunningParams_t;
#endif
#if (MARQUEE_ENABLE)
// LED 跑马灯参数结构体
__packed typedef struct
{
uint32_t cycleTime; // 跑马灯周期时间(毫秒)
uint32_t intervalTime; // 下次运行的间隔时间(毫秒)
uint32_t lastCycleTime; // 上次周期运行完的记录时间戳
uint32_t lastLEDTime; // 上个 LED 点亮的记录时间戳
uint32_t currentSite; // 跑马灯的当前位置
uint32_t numLEDs; // 跑马灯的 LED 数量
} LED_MarqueeParams_t;
#endif
// LED 配置结构体
__packed typedef struct
{
LED_State_t state; // LED状态
uint16_t ledMask; // LED掩码
uint16_t runNum; // 运行次数(0表示无限次)
uint32_t currentTime; // 运行时的时间戳
#if (BLINK_ENABLE)
LED_BlinkParams_t blinkParams; // 闪烁参数
#endif
#if (BREATH_ENABLE)
LED_BreathParams_t breathParams; // 呼吸参数
#endif
#if (RUNNING_ENABLE)
LED_RunningParams_t runningParams; // 流水灯参数
#endif
#if (ALTERNATING_BLINK_ENABLE)
LED_AlternatingBlinkParams_t alternatingBlinkParams; // 交替闪烁参数
#endif
#if (MARQUEE_ENABLE)
LED_MarqueeParams_t marqueeParams; // 跑马灯参数
#endif
} LED_Config_t;
// LED事件表项结构体
__packed typedef struct
{
LED_Event_t event; // 事件
LED_EventPriority_t priority; // 优先级
LED_Config_t config; // LED配置
uint8_t (*ledEventHandler)(LED_Config_t *config); // LED事件处理回调函数
} LED_EventTableItem_t;
// LED状态设置函数
typedef void (*LED_SetState_t)(uint16_t led, uint8_t state);
// 获取时间戳函数
typedef uint32_t (*TIME_Get_t)(void);
// 设置led状态回调函数
void LED_SetStateInit(LED_SetState_t setState);
// 设置获取时间戳回调函数
void SET_TimeGetInit(TIME_Get_t getTime);
// LED初始化函数
void LED_EventTableInit(LED_EventTableItem_t ledEvent[]);
// 将LED事件加入事件表
void LED_EventAdd(LED_Event_t ledEvent);
// 将LED事件从事件表删除
void LED_EventDelete(LED_Event_t ledEvent);
// LED事件处理函数
void LED_EventHandle(void);
// LED更新函数
uint8_t LED_Update(LED_Config_t *led);
#endif // LED_H
结语
通过以上设计和实现,我开发了一种基于事件驱动的LED控制框架,该框架将LED控制从底层解耦,只关注应用层,提供了一种通用的LED控制方案,使得开发人员可以轻松实现各种LED效果,极大地提高了开发效率和灵活性。该框架不仅简化了LED控制的代码编写,还提供了丰富的功能模块,可以满足不同项目的需求,具有较高的通用性和可扩展性。
以上是LED框架的设计思路、核心代码和使用示例,希望能够对嵌入式系统开发中的LED控制提供一种新的思路和解决方案。