2、自动/手动互相切换都闪 2 下灯;APP 发停止后再发自动模式可正常启动,手动残留停止不再挡住自动 3、自动模式 1~6 单次最长跑 10 分钟后回待机;关机时长按 12 秒进 OTA,OTA 期间红绿交替闪
329 lines
8.3 KiB
C
329 lines
8.3 KiB
C
/******************************************************************************
|
|
* @file led.c
|
|
* @brief LED 事件框架实现:只改 ledMask 对应通道,红绿互不抢
|
|
* @author cyWu <1917507415@qq.com>
|
|
* @date 2026.08.25
|
|
* @version V1.0.0
|
|
* @history
|
|
* - V1.0.0, 2026.08.25, cyWu, 首次发布,实现按通道掩码独立开关的 LED 事件框架
|
|
******************************************************************************/
|
|
|
|
#include "led.h"
|
|
#include <stddef.h>
|
|
|
|
static LED_EventTableItem_t s_led_priority_table[PRIORITY_MAX];
|
|
static LED_EventTableItem_t s_led_event_table[LED_EVENT_MAX];
|
|
static LED_SetState_t s_led_set_state;
|
|
static TIME_Get_t s_time_get;
|
|
|
|
static uint32_t get_tick_diff(uint32_t meiosis);
|
|
static uint8_t led_lighting_update(LED_Config_t *led);
|
|
#if (BLINK_ENABLE)
|
|
static uint8_t led_blinking_update(LED_Config_t *led);
|
|
#endif
|
|
#if (ALTERNATING_BLINK_ENABLE)
|
|
static uint8_t led_alternating_update(LED_Config_t *led);
|
|
#endif
|
|
|
|
/**
|
|
* @brief 注册写灯回调,只对掩码里的通道动手
|
|
* @param setState 回调,led=通道掩码,state=LED_ON/OFF
|
|
* @return 无
|
|
*/
|
|
void LED_SetStateInit(LED_SetState_t setState)
|
|
{
|
|
s_led_set_state = setState;
|
|
}
|
|
|
|
/**
|
|
* @brief 注册毫秒时间戳回调
|
|
* @param getTime 回调
|
|
* @return 无
|
|
*/
|
|
void SET_TimeGetInit(TIME_Get_t getTime)
|
|
{
|
|
s_time_get = getTime;
|
|
}
|
|
|
|
/**
|
|
* @brief 拷贝产品事件表
|
|
* @param ledEvent 长度必须为 LED_EVENT_MAX
|
|
* @return 无
|
|
*/
|
|
void LED_EventTableInit(LED_EventTableItem_t ledEvent[])
|
|
{
|
|
if (ledEvent == NULL) {
|
|
return;
|
|
}
|
|
memset(s_led_priority_table, 0, sizeof(s_led_priority_table));
|
|
memset(s_led_event_table, 0, sizeof(s_led_event_table));
|
|
memcpy(s_led_event_table, ledEvent, sizeof(s_led_event_table));
|
|
}
|
|
|
|
/**
|
|
* @brief 把事件挂到对应优先级槽;同优先级会被覆盖
|
|
* @param ledEvent 事件号
|
|
* @return 无
|
|
*/
|
|
void LED_EventAdd(LED_Event_t ledEvent)
|
|
{
|
|
size_t i;
|
|
LED_EventTableItem_t *item;
|
|
LED_EventPriority_t prio;
|
|
|
|
if ((s_time_get == NULL) || (s_led_set_state == NULL)) {
|
|
return;
|
|
}
|
|
if (ledEvent >= LED_EVENT_MAX) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < LED_EVENT_MAX; i++) {
|
|
item = &s_led_event_table[i];
|
|
if (item->event != ledEvent) {
|
|
continue;
|
|
}
|
|
prio = item->priority;
|
|
if (prio >= PRIORITY_MAX) {
|
|
return;
|
|
}
|
|
if (s_led_priority_table[prio].event != ledEvent) {
|
|
item->config.currentTime = s_time_get();
|
|
if (s_led_priority_table[prio].event != LED_EVENT_NONE) {
|
|
/* 同优先级换事件:只关掉旧灯里新事件用不到的通道 */
|
|
s_led_set_state((uint16_t)(s_led_priority_table[prio].config.ledMask
|
|
^ item->config.ledMask),
|
|
LED_OFF);
|
|
}
|
|
}
|
|
s_led_priority_table[prio] = *item;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 清空优先级表(不改 GPIO,由调用方灭灯)
|
|
* @return 无
|
|
*/
|
|
void LED_EventAllDelete(void)
|
|
{
|
|
memset(s_led_priority_table, 0, sizeof(s_led_priority_table));
|
|
}
|
|
|
|
/**
|
|
* @brief 删除指定事件并关掉它占用的通道
|
|
* @param ledEvent 事件号
|
|
* @return 无
|
|
*/
|
|
void LED_EventDelete(LED_Event_t ledEvent)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < PRIORITY_MAX; i++) {
|
|
if (s_led_priority_table[i].event != ledEvent) {
|
|
continue;
|
|
}
|
|
memset(&s_led_priority_table[i], 0, sizeof(LED_EventTableItem_t));
|
|
if ((s_led_set_state != NULL) && (ledEvent < LED_EVENT_MAX)) {
|
|
s_led_set_state(s_led_event_table[ledEvent].config.ledMask, LED_OFF);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 从高优先级往下跑事件
|
|
* @note handler 返回 1 会拦住更低优先级。红绿要共存时 handler 必须返回 0
|
|
* @return 无
|
|
*/
|
|
void LED_EventHandle(void)
|
|
{
|
|
int i;
|
|
uint8_t ret;
|
|
|
|
for (i = (int)PRIORITY_MAX - 1; i >= 0; i--) {
|
|
if (s_led_priority_table[i].event == LED_EVENT_NONE) {
|
|
continue;
|
|
}
|
|
if (s_led_priority_table[i].ledEventHandler == NULL) {
|
|
continue;
|
|
}
|
|
ret = s_led_priority_table[i].ledEventHandler(&s_led_priority_table[i].config);
|
|
if (ret) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 是否有事件在跑
|
|
* @return 1=有,0=无
|
|
*/
|
|
uint8_t LED_IsEventActive(void)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < PRIORITY_MAX; i++) {
|
|
if (s_led_priority_table[i].event != LED_EVENT_NONE) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 指定事件是否挂在优先级表里
|
|
* @param ledEvent 事件号
|
|
* @return 1=在,0=不在
|
|
*/
|
|
uint8_t LED_IsEventExist(LED_Event_t ledEvent)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < PRIORITY_MAX; i++) {
|
|
if (s_led_priority_table[i].event == ledEvent) {
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief 按配置刷新一次灯效
|
|
* @param led 配置,不可为空
|
|
* @return 1=还在跑,0=次数用完
|
|
*/
|
|
uint8_t LED_Update(LED_Config_t *led)
|
|
{
|
|
if (led == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
switch (led->state) {
|
|
case LED_LIGHT:
|
|
return led_lighting_update(led);
|
|
#if (BLINK_ENABLE)
|
|
case LED_BLINKING:
|
|
return led_blinking_update(led);
|
|
#endif
|
|
#if (ALTERNATING_BLINK_ENABLE)
|
|
case LED_ALTERNATING_BLINK:
|
|
return led_alternating_update(led);
|
|
#endif
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief 毫秒时间差,兼容溢出
|
|
* @param meiosis 起点时间戳
|
|
* @return 差值 ms
|
|
*/
|
|
static uint32_t get_tick_diff(uint32_t meiosis)
|
|
{
|
|
uint32_t now;
|
|
|
|
if (s_time_get == NULL) {
|
|
return 0;
|
|
}
|
|
now = s_time_get();
|
|
if (now >= meiosis) {
|
|
return now - meiosis;
|
|
}
|
|
return (0xFFFFFFFFu - meiosis) + now;
|
|
}
|
|
|
|
/**
|
|
* @brief 常亮;runNum 按秒计,0=一直亮
|
|
* @param led 配置
|
|
* @return 1=还在亮,0=到点灭
|
|
*/
|
|
static uint8_t led_lighting_update(LED_Config_t *led)
|
|
{
|
|
uint16_t sec_count;
|
|
|
|
if (s_led_set_state == NULL) {
|
|
return 0;
|
|
}
|
|
sec_count = (uint16_t)(get_tick_diff(led->currentTime) / 1000u);
|
|
if ((led->runNum > 0) && (sec_count >= led->runNum)) {
|
|
s_led_set_state(led->ledMask, LED_OFF);
|
|
return 0;
|
|
}
|
|
s_led_set_state(led->ledMask, LED_ON);
|
|
return 1;
|
|
}
|
|
|
|
#if (BLINK_ENABLE)
|
|
/**
|
|
* @brief 闪烁;runNum 按完整周期计,0=一直闪
|
|
* @param led 配置
|
|
* @return 1=还在闪,0=到点灭
|
|
*/
|
|
static uint8_t led_blinking_update(LED_Config_t *led)
|
|
{
|
|
uint32_t run_cycle;
|
|
uint16_t blink_count;
|
|
uint32_t sec_left;
|
|
|
|
if (s_led_set_state == NULL) {
|
|
return 0;
|
|
}
|
|
run_cycle = led->blinkParams.onTime + led->blinkParams.offTime;
|
|
if (run_cycle == 0) {
|
|
return 0;
|
|
}
|
|
blink_count = (uint16_t)(get_tick_diff(led->currentTime) / run_cycle);
|
|
sec_left = get_tick_diff(led->currentTime) % run_cycle;
|
|
if ((led->runNum > 0) && (blink_count >= led->runNum)) {
|
|
s_led_set_state(led->ledMask, LED_OFF);
|
|
return 0;
|
|
}
|
|
if (sec_left <= led->blinkParams.onTime) {
|
|
s_led_set_state(led->ledMask, led->blinkParams.orDer ? LED_OFF : LED_ON);
|
|
} else {
|
|
s_led_set_state(led->ledMask, led->blinkParams.orDer ? LED_ON : LED_OFF);
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#if (ALTERNATING_BLINK_ENABLE)
|
|
/**
|
|
* @brief 两组灯按时间片轮流亮
|
|
* @param led 配置
|
|
* @return 1=还在跑,0=到点灭
|
|
*/
|
|
static uint8_t led_alternating_update(LED_Config_t *led)
|
|
{
|
|
uint16_t blink_count;
|
|
uint32_t sec_left;
|
|
uint32_t time_slice;
|
|
size_t i;
|
|
|
|
if (s_led_set_state == NULL) {
|
|
return 0;
|
|
}
|
|
if (led->alternatingBlinkParams.Cycle == 0) {
|
|
return 0;
|
|
}
|
|
blink_count = (uint16_t)(get_tick_diff(led->currentTime)
|
|
/ led->alternatingBlinkParams.Cycle);
|
|
sec_left = get_tick_diff(led->currentTime) % led->alternatingBlinkParams.Cycle;
|
|
if ((led->runNum > 0) && (blink_count >= led->runNum)) {
|
|
s_led_set_state(led->ledMask, LED_OFF);
|
|
return 0;
|
|
}
|
|
time_slice = led->alternatingBlinkParams.Cycle / ALTERNAT_GROUP;
|
|
s_led_set_state(led->ledMask, LED_OFF);
|
|
for (i = 0; i < ALTERNAT_GROUP; i++) {
|
|
if ((sec_left > (time_slice * i)) && (sec_left <= (time_slice * (i + 1)))) {
|
|
s_led_set_state(led->alternatingBlinkParams.Group[i], LED_ON);
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|