Files
LPT26x-HSF-4MB-Hilink_14.2.…/application/ws63/user_main/spotlight/spotlight_main.c
ekko.bao dcef9664ae fix(spotlight): 优化PWM调光算法解决低亮度抖动
1. 修改PWM频率为4000Hz,与80MHz基准时钟形成整数倍关系(20000 ticks),消除计算取整导致的物理层抖动。
2. 实现基于时间轴的渐变算法替代原有的步进累加逻辑,消除累积误差,确保渐变时长精确且不受系统负载影响。
3. 调整渐变刷新间隔为8ms,提升视觉平滑度。
2025-11-22 13:43:45 +08:00

1675 lines
63 KiB
C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "hsf.h"
#include "hfconfig.h"
#if defined(CONFIG_PWM_SUPPORT_LPM)
#include "pm_veto.h"
#endif
#include "pinctrl.h"
#include "gpio.h"
#include "pinctrl.h"
#include "pwm.h"
#include "timer.h"
#include "soc_osal.h"
#include "systick.h"
// #include "tcxo.h"
#include "spotlight.h"
#include "factory_test.h"
// 函数前向声明
static void init_timer_system(void);
static void init_fade_ctx(void);
static void init_breath_ctx(void);
static void stop_fade_task(void);
static void stop_breath_task(void);
static void stop_breath_timer(void);
static void stop_spotlight_main_task(void);
static void check_net_cfg_timeout(void);
static void check_net_cfg_power_on_keep_time(void);
static void set_light2breathtimeout(void);
static void set_light2net_cfg_done(void);
static void start_breath_timer(void);
static void report_switch_status(void);
void calculate_pwm_duty(device_control_t* pdevice_control);
void close_light(void);
// 定义上报函数类型
typedef void (*report_func_t)(void);
static void start_report_task(uint32_t report_mask);
static void report_fade_complete_status(void);
static uint8_t calculate_checksum(const device_data_t* data);
static bool write_device_data_to_addr(uint32_t addr, const device_data_t* data);;
static void report_device_online_status(void);
// 工具:是否处于配网呼吸模式
// 使用宏避免某些编译模式下 inline 函数被误判为未返回
#define is_in_breath_mode() (g_net_breath_state == NET_CFG_BREATHING)
// 状态上报任务相关变量
#define REPORT_TASK_PRIO 80
#define REPORT_TASK_STACK_SIZE 0x1000
static osal_task *report_task_handle = NULL;
static bool report_task_running = false; // 添加任务运行状态标志
// 新增:全局上报掩码变量和条件变量
static uint32_t g_report_mask = 0;
static osal_semaphore report_sem; // 条件变量用于唤醒report task
bool g_reset_factory_flag = false;
// 灯渐变任务相关定义
#define FADE_TASK_PRIO 80
#define FADE_TASK_STACK_SIZE 0x1000
// 呼吸灯任务相关定义
#define BREATH_TASK_PRIO 80
#define BREATH_TASK_STACK_SIZE 0x1000
static uint32_t pwm_period_cnt = 0; // PWM周期 40us
static uint8_t channel_id_cw = 0; // 冷白LED通道ID
static uint8_t channel_id_ww = 0; // 暖白LED通道ID
// PWM参数更新调试标志引脚是否已初始化
static bool g_pwm_update_flag_inited = false;
void save_device_data(void);
void read_device_data(void);
void req_save_device_data(void);
static void stop_breath_timer(void);
device_control_t g_device_control = DEFAULT_DEVICE_DATA;
// 配网状态定义
typedef enum {
NET_CFG_IDLE = 0, // 空闲状态
NET_CFG_BREATHING, // 呼吸灯状态,配网中
NET_CFG_TIMEOUT, // 超时状态
} net_cfg_state_e;
// 配网状态相关变量
static net_cfg_state_e g_net_breath_state = NET_CFG_IDLE; // 配网状态
static uint64_t g_net_cfg_start_time = 0; // 配网开始时间
// 保存任务相关变量
static osal_task *save_task_handle = NULL;
static bool save_task_running = false;
static osal_semaphore save_data_sem;
// 打印频率限制相关定义
#define PRINT_INTERVAL_MS 1000 // 最小打印间隔(ms)
typedef struct {
uint64_t last_print_time; // 上次打印时间
const char *prefix; // 打印前缀
} print_limiter_t;
#define FADE_CTRL_DATA_SIZE(fade_ctx) ((fade_ctx).member_barrier - (uint8_t*)&(fade_ctx))
// 渐变控制相关变量
static struct fade_ctx_t {
struct{
// 渐变控制参数
int32_t start_brightness; // 起始亮度
int32_t start_cct; // 起始色温
int32_t target_brightness; // 目标亮度
int32_t target_cct; // 目标色温
int32_t current_brightness; // 当前亮度
int32_t current_cct; // 当前色温
bool is_fading;
bool fade_completed;
bool is_closing_fade; // 标记是否为关灯渐变
uint16_t fade_time; // s
uint32_t duration_ms; // 渐变总时长(ms)
uint32_t start_time_ms; // 渐变开始时间(ms)
uint32_t update_interval; // 更新间隔(us)
volatile bool timer_active; // 渐变定时器是否允许自重启(竞态保护)
// 打印限制器
print_limiter_t print_limiter;
};
uint8_t member_barrier[0]; // 用于计算前面成员变量大小
// 任务相关
osal_task *task_handle;
bool task_running;
osal_semaphore sem;
timer_handle_t timer_handle;
} fade_ctx = {0};
// 呼吸灯控制相关变量
static struct {
// 呼吸灯控制参数
int32_t duty;
int32_t step;
uint32_t update_interval; // 更新间隔(us)
bool is_initial_breath; // 是否是初始3秒呼吸
// 任务相关
osal_task *task_handle;
bool task_running;
osal_semaphore sem;
timer_handle_t timer_handle;
uint32_t start_time_ms;
uint32_t breath_cnt;
// 打印限制器
print_limiter_t print_limiter;
} breath_ctx = {0};
print_limiter_t pwm_limiter = {
.last_print_time = 0,
.prefix = "[pwm]",
};
// 定时器初始化标志
static bool timer_initialized = false;
// 初始化定时器系统
static void init_timer_system(void)
{
if (!timer_initialized) {
uapi_timer_init();
int ret = uapi_timer_adapter(TIMER_INDEX_1, TIMER_1_IRQN, 1);
if (ret != 0) {
e_printf("定时器适配器初始化失败\r\n");
return;
}
timer_initialized = true;
}
}
// 初始化打印限制器
static void init_print_limiter(print_limiter_t *limiter, const char *prefix)
{
limiter->last_print_time = 0;
limiter->prefix = prefix;
}
// 带频率限制的打印函数
static bool should_print(print_limiter_t *limiter)
{
uint64_t current_time = uapi_systick_get_ms();
if (current_time - limiter->last_print_time >= PRINT_INTERVAL_MS) {
limiter->last_print_time = current_time;
return true;
}
return false;
}
// 渐变灯任务函数
static void *fade_task(const char *arg)
{
unused(arg);
e_printf("[fade_task] Task started\r\n");
device_control_t device_control = {};
while (fade_ctx.task_running) {
// 等待渐变信号
if (osal_sem_down_timeout(&fade_ctx.sem, 100*1000) != OSAL_SUCCESS) {
continue;
}
if (!fade_ctx.is_fading) {
continue;
}
// 1. 获取当前时间
uint32_t now = uapi_systick_get_ms();
// 2. 计算流逝时间
uint32_t elapsed = now - fade_ctx.start_time_ms;
// 使用打印限制器控制打印频率
if (should_print(&fade_ctx.print_limiter)) {
e_printf("%s Brightness: %d->%d, CCT: %d->%d, Progress: %d/%d ms\r\n",
fade_ctx.print_limiter.prefix,
fade_ctx.start_brightness, fade_ctx.target_brightness,
fade_ctx.start_cct, fade_ctx.target_cct,
elapsed, fade_ctx.duration_ms);
}
bool is_finished = false;
// 3. 判断是否结束
if (elapsed >= fade_ctx.duration_ms) {
// 强制设为目标值,确保最终结果精确
fade_ctx.current_brightness = fade_ctx.target_brightness;
fade_ctx.current_cct = fade_ctx.target_cct;
is_finished = true;
} else {
// 4. 核心算法:计算当前应有的值 (线性插值)
// 公式Val = Start + (Diff * Elapsed) / Duration
int32_t diff_bri = fade_ctx.target_brightness - fade_ctx.start_brightness;
int32_t diff_cct = fade_ctx.target_cct - fade_ctx.start_cct;
// 注意:运算顺序很重要,先乘后除以保留精度,但要防止溢出
// 使用 int64_t 避免乘法溢出
fade_ctx.current_brightness = fade_ctx.start_brightness +
(int32_t)((int64_t)diff_bri * elapsed / fade_ctx.duration_ms);
fade_ctx.current_cct = fade_ctx.start_cct +
(int32_t)((int64_t)diff_cct * elapsed / fade_ctx.duration_ms);
}
// 达到目标,停止渐变
if (is_finished) {
e_printf("[fade_task] Fade completed, Final brightness: %d, CCT: %d, closing_fade: %d\r\n",
fade_ctx.target_brightness, fade_ctx.target_cct, fade_ctx.is_closing_fade);
fade_ctx.is_fading = false;
fade_ctx.fade_completed = true;
fade_ctx.timer_active = false; // 防止回调自重启
uapi_timer_stop(fade_ctx.timer_handle);
}
BRIGHTNESS_LITME_RANGE(fade_ctx.current_brightness);
CCT_LITME_RANGE(fade_ctx.current_cct);
// 更新PWM输出
device_control.brightness_local = fade_ctx.current_brightness;
device_control.cct_local = fade_ctx.current_cct;
calculate_pwm_duty(&device_control);
// 根据is_closing_fade标志判断PWM开关状态
bool effective_on_state = fade_ctx.is_closing_fade ? true : g_device_control.on;
update_pwm_output(effective_on_state, device_control.duty_cw, device_control.duty_ww);
if (fade_ctx.fade_completed) {
fade_ctx.fade_completed = false;
if (fade_ctx.is_closing_fade) {
// 若在收尾时外部已请求开灯且不处于配网呼吸模式则不执行PWM关断避免黑场
if (g_device_control.on && !is_in_breath_mode()) {
e_printf("[fade_task] Close fade preempted by OPEN, skip turning off\r\n");
fade_ctx.is_closing_fade = false;
} else {
// 关灯渐变完成不更新g_device_control.brightness_local保持原有目标亮度
fade_ctx.is_closing_fade = false;
update_pwm_output(false, 0, 0);
g_device_control.duty_cw = 0;
g_device_control.duty_ww = 0;
// 注意:这里不更新 g_device_control.brightness_local保持之前的亮度值
e_printf("[fade_task] Close light fade completed, PWM turned off, brightness_local preserved: %d\r\n",
g_device_control.brightness_local);
}
} else {
// 正常渐变完成:正常更新所有值
g_device_control.duty_ww = device_control.duty_ww;
g_device_control.duty_cw = device_control.duty_cw;
g_device_control.brightness_local = fade_ctx.current_brightness;
if (g_device_control.colourMode == COLOUR_MODE_DUAL) {
g_device_control.cct_local = fade_ctx.current_cct;
}
}
e_printf("[fade_task] Status report: local brightness=%d, CCT=%d\r\n",
g_device_control.brightness_local,
g_device_control.cct_local);
// 启动状态上报任务,使用渐变完成状态上报函数
start_report_task(REPORT_BRIGHTNESS | REPORT_CCT);
req_save_device_data();
}
}
e_printf("[fade_task] Task exited\r\n");
return NULL;
}
// 呼吸灯任务函数
static void *breath_task(const char *arg)
{
unused(arg);
e_printf("[breath_task] Task started\r\n");
while (breath_ctx.task_running) {
// 等待呼吸信号
if (osal_sem_down_timeout(&breath_ctx.sem, 100*1000) != OSAL_SUCCESS) {
continue;
}
if (g_net_breath_state != NET_CFG_BREATHING) {
continue;
}
// 检查是否超过呼吸灯持续时间
uint32_t current_time = uapi_systick_get_ms();
uint32_t elapsed_time = current_time - g_net_cfg_start_time;
if (elapsed_time >= NET_CFG_BREATH_DURATION) {
set_light2breathtimeout();
stop_breath_timer();
continue;
}
// 检查是否需要改变方向
if (breath_ctx.duty >= BRIGHTNESS_LOCAL_MAX) {
breath_ctx.duty = BRIGHTNESS_LOCAL_MAX;
breath_ctx.step = -1 * breath_ctx.step;
e_printf("%s breathing %d, down %dms\n", breath_ctx.print_limiter.prefix, breath_ctx.breath_cnt, uapi_systick_get_ms() - breath_ctx.start_time_ms);
} else if (breath_ctx.duty <= 0) {
breath_ctx.duty = 0;
breath_ctx.step = -1 * breath_ctx.step;
e_printf("%s breathing %d, up %dms\n", breath_ctx.print_limiter.prefix, breath_ctx.breath_cnt, uapi_systick_get_ms() - breath_ctx.start_time_ms);
}
// 更新亮度
breath_ctx.duty += breath_ctx.step;
int32_t brightness_pwm = breath_ctx.duty;
// 设置黄白灯交替呼吸
g_device_control.duty_ww = brightness_pwm;
g_device_control.duty_cw = PWM_DUTY_RATIO_MAX - brightness_pwm;
update_pwm_output(g_device_control.on, g_device_control.duty_cw, g_device_control.duty_ww);
}
e_printf("[breath_task] Task exited\r\n");
return NULL;
}
// 渐变定时器回调函数
static void fade_timer_callback(uintptr_t data)
{
// 若已被取消或未处于渐变态,禁止回调重入自重启,避免双定时器抖动
if (!fade_ctx.timer_active || !fade_ctx.is_fading) {
return;
}
osal_sem_up(&fade_ctx.sem); // 唤醒渐变任务 fade_task
uapi_timer_start(fade_ctx.timer_handle, fade_ctx.update_interval, fade_timer_callback, 0);
}
// 呼吸灯定时器回调函数
static void breath_timer_callback(uintptr_t data)
{
osal_sem_up(&breath_ctx.sem);
uapi_timer_start(breath_ctx.timer_handle, breath_ctx.update_interval, breath_timer_callback, 0);
}
// 初始化渐变控制
static void init_fade_ctx(void)
{
init_timer_system();
int ret = uapi_timer_create(TIMER_INDEX_1, &fade_ctx.timer_handle);
if (ret != 0) {
e_printf("渐变定时器创建失败\r\n");
}
// 初始化信号量
if (osal_sem_init(&fade_ctx.sem, 0) != OSAL_SUCCESS) {
e_printf("[init_fade_ctx] Failed to init semaphore\n");
return;
}
// 初始化打印限制器
init_print_limiter(&fade_ctx.print_limiter, "[fade_task]");
// 🔧 智能初始化:根据设备状态决定初始值
if (g_device_control.read_done) {
// 数据已读取完成(正常启动)
if (g_device_control.on) {
// 开机渐变从0开始渐变到目标亮度
fade_ctx.current_brightness = 0;
fade_ctx.current_cct = g_device_control.cct_local; // 色温保持目标值
e_printf("[init_fade_ctx] 开机渐变模式: brightness=0->%d, cct=%d\n",
g_device_control.brightness_local, fade_ctx.current_cct);
} else {
// 设备关闭状态
fade_ctx.current_brightness = 0;
fade_ctx.current_cct = g_device_control.cct_local;
}
} else {
// 数据尚未读取(初始化阶段)- 保持原有逻辑
fade_ctx.current_brightness = 0;
fade_ctx.current_cct = 0;
e_printf("[init_fade_ctx] 初始化阶段: brightness=0, cct=0\n");
}
// 创建渐变任务
osal_kthread_lock();
fade_ctx.task_handle = osal_kthread_create((osal_kthread_handler)fade_task, NULL, "FadeTask",
FADE_TASK_STACK_SIZE);
if (fade_ctx.task_handle != NULL) {
fade_ctx.task_running = true;
osal_kthread_set_priority(fade_ctx.task_handle, FADE_TASK_PRIO);
e_printf("[init_fade_ctx] Fade task created successfully\n");
} else {
e_printf("[init_fade_ctx] Failed to create fade task\n");
osal_sem_destroy(&fade_ctx.sem);
}
osal_kthread_unlock();
// 设置默认更新间隔
fade_ctx.update_interval = FADE_INTERVAL_MIN;
fade_ctx.timer_active = false; // 默认禁用,只有开始渐变时启用
}
// 初始化呼吸灯控制
static void init_breath_ctx(void)
{
init_timer_system();
int ret = uapi_timer_create(TIMER_INDEX_1, &breath_ctx.timer_handle);
if (ret != 0) {
e_printf("呼吸灯定时器创建失败\r\n");
}
// 初始化信号量
if (osal_sem_init(&breath_ctx.sem, 0) != OSAL_SUCCESS) {
e_printf("[init_breath_ctx] Failed to init semaphore\n");
return;
}
// 初始化打印限制器
init_print_limiter(&breath_ctx.print_limiter, "[breath_task]");
breath_ctx.duty = g_device_control.brightness_local;
breath_ctx.step = BREARTH_STEP == 0 ? 1 : BREARTH_STEP;
breath_ctx.update_interval = BREARTH_INTERVAL;
breath_ctx.is_initial_breath = true;
breath_ctx.start_time_ms = uapi_systick_get_ms();
e_printf("Breath task: brightness=%d, step=%d, update_interval=%d, is_initial_breath=%d\n",
breath_ctx.duty, breath_ctx.step, breath_ctx.update_interval, breath_ctx.is_initial_breath);
// 创建呼吸灯任务
osal_kthread_lock();
breath_ctx.task_handle = osal_kthread_create((osal_kthread_handler)breath_task, NULL, "BreathTask",
BREATH_TASK_STACK_SIZE);
if (breath_ctx.task_handle != NULL) {
breath_ctx.task_running = true;
osal_kthread_set_priority(breath_ctx.task_handle, BREATH_TASK_PRIO);
e_printf("[init_breath_ctx] Breath task created successfully\n");
} else {
e_printf("[init_breath_ctx] Failed to create breath task\n");
osal_sem_destroy(&breath_ctx.sem);
}
osal_kthread_unlock();
}
// 停止渐变任务
static void stop_fade_task(void)
{
if (fade_ctx.task_handle == NULL) {
return;
}
fade_ctx.task_running = false;
osal_sem_up(&fade_ctx.sem); // 唤醒任务以使其退出
osal_kthread_lock();
osal_kthread_destroy(fade_ctx.task_handle, 0);
fade_ctx.task_handle = NULL;
osal_kthread_unlock();
osal_sem_destroy(&fade_ctx.sem);
e_printf("[stop_fade_task] Fade task stopped\n");
}
// 停止呼吸灯任务
void stop_breath_task(void)
{
if (breath_ctx.task_handle == NULL) {
return;
}
breath_ctx.task_running = false;
osal_sem_up(&breath_ctx.sem); // 唤醒任务以使其退出
osal_kthread_lock();
osal_kthread_destroy(breath_ctx.task_handle, 0);
breath_ctx.task_handle = NULL;
osal_kthread_unlock();
// osal_sem_destroy(&breath_ctx.sem);
e_printf("[stop_breath_task] Breath task stopped\n");
}
// 修改stop_breath_timer函数
static void stop_breath_timer(void)
{
if (breath_ctx.timer_handle) {
uapi_timer_stop(breath_ctx.timer_handle);
uapi_timer_delete(breath_ctx.timer_handle);
}
breath_ctx.timer_handle = NULL;
stop_breath_task();
}
// 计算PWM占空比
void calculate_pwm_duty(device_control_t* pdevice_control)
{ // 如果开关关闭则占空比为0
uint32_t total_brightness_pwm = BRINGHTNING2PWMDUTY(pdevice_control->brightness_local);
// 计算色温比例 (0-10000)
float cct_ratio = ((pdevice_control->cct_local - CCT_LOCAL_MIN) * 1.0) / CCT_RANGE;
// 根据色温比例计算CW和WW的占空比
// 总亮度保持不变只调整CW和WW的比例
pdevice_control->duty_cw = total_brightness_pwm * cct_ratio + 0.5;
pdevice_control->duty_ww = total_brightness_pwm - pdevice_control->duty_cw;
}
// 更新PWM输出
void update_pwm_output(bool on_state, uint16_t duty_cw_val, uint16_t duty_ww_val)
{
// 说明:为了避免每步“重相位 + 动态拼接”导致的低亮抖动,
// 这里改为固定相位offset_time=0并避免每次重复start_group。
// 若平台支持 uapi_pwm_update_duty_ratio可切换为更新接口否则退回 open 配置。
pwm_config_t cfg = {0};
cfg.repeat = true;
uint16_t current_duty_cw = duty_cw_val;
uint16_t current_duty_ww = duty_ww_val;
if (current_duty_cw > PWM_DUTY_RATIO_MAX) {
current_duty_cw = PWM_DUTY_RATIO_MAX;
}
if (current_duty_ww > PWM_DUTY_RATIO_MAX) {
current_duty_ww = PWM_DUTY_RATIO_MAX;
}
uint32_t high_cnt_cw = (pwm_period_cnt * current_duty_cw) / PWM_DUTY_RATIO_MAX;
uint32_t low_cnt_cw = pwm_period_cnt - high_cnt_cw;
uint32_t high_cnt_ww = (pwm_period_cnt * current_duty_ww) / PWM_DUTY_RATIO_MAX;
uint32_t low_cnt_ww = pwm_period_cnt - high_cnt_ww;
if (!on_state) {
high_cnt_cw = 0;
low_cnt_cw = pwm_period_cnt; // 关灯时保持低电平整周期
high_cnt_ww = 0;
low_cnt_ww = pwm_period_cnt;
}
#if defined(CONFIG_PWM_USING_V150)
// V150: 先更新占空,再通过 start_group 触发加载
(void)uapi_pwm_update_duty_ratio(channel_id_cw, low_cnt_cw, high_cnt_cw);
(void)uapi_pwm_update_duty_ratio(channel_id_ww, low_cnt_ww, high_cnt_ww);
// 需要显式触发加载
(void)uapi_pwm_start_group(CONFIG_PWM_GROUP_ID);
#else
// V151 场景
cfg.offset_time = 0; // 固定相位,避免相位不断重置造成抖动
#if defined(CONFIG_PWM_PRELOAD)
// 使用预加载:配置写入影子寄存器,等当前周期结束后自动无缝切换。
cfg.high_time = high_cnt_cw;
cfg.low_time = low_cnt_cw;
(void)uapi_pwm_config_preload(CONFIG_PWM_GROUP_ID, channel_id_cw, &cfg);
cfg.high_time = high_cnt_ww;
cfg.low_time = low_cnt_ww;
(void)uapi_pwm_config_preload(CONFIG_PWM_GROUP_ID, channel_id_ww, &cfg);
// 预加载模式下不重复调用 start_group避免打乱当前计数器与周期
#else
// 无预加载能力时的回退:直接 open + start_group可能导致轻微周期扰动
cfg.high_time = high_cnt_cw;
cfg.low_time = low_cnt_cw;
(void)uapi_pwm_open(channel_id_cw, &cfg);
cfg.high_time = high_cnt_ww;
cfg.low_time = low_cnt_ww;
(void)uapi_pwm_open(channel_id_ww, &cfg);
(void)uapi_pwm_start_group(CONFIG_PWM_GROUP_ID);
#endif
#endif
if (should_print(&pwm_limiter)) {
e_printf("on:%d, cw(H/L):%u/%u, duty:%u, ww(H/L):%u/%u, duty:%u\r\n",
on_state, high_cnt_cw, low_cnt_cw, current_duty_cw,
high_cnt_ww, low_cnt_ww, current_duty_ww);
}
// 调试每次更新PWM参数后翻转一次 GPIO10PWM_UPDATE_FLAG_PIN
// 便于使用逻辑分析仪观察控制发生的时间点
if (g_pwm_update_flag_inited) {
(void)uapi_gpio_toggle(PWM_UPDATE_FLAG_PIN);
}
}
#define ABS(x) ((x) < 0 ? -(x) : (x))
// 计算渐变步长
// 计算渐变参数(时间轴模式初始化)
static void calculate_fade_steps(struct fade_ctx_t *pfade_ctx)
{
// 1. 记录锚点数据
pfade_ctx->start_time_ms = uapi_systick_get_ms();
pfade_ctx->duration_ms = pfade_ctx->fade_time * 1000; // s -> ms
pfade_ctx->start_brightness = pfade_ctx->current_brightness;
pfade_ctx->start_cct = pfade_ctx->current_cct;
e_printf("fade init: brightness %d->%d, cct %d->%d, duration %dms\r\n",
pfade_ctx->start_brightness, pfade_ctx->target_brightness,
pfade_ctx->start_cct, pfade_ctx->target_cct,
pfade_ctx->duration_ms);
// 2. 特殊情况处理:无渐变时间或无变化
int32_t brightness_diff = pfade_ctx->target_brightness - pfade_ctx->current_brightness;
int32_t cct_diff = pfade_ctx->target_cct - pfade_ctx->current_cct;
if ((brightness_diff == 0 && cct_diff == 0) || pfade_ctx->duration_ms == 0) {
pfade_ctx->current_brightness = pfade_ctx->target_brightness;
pfade_ctx->current_cct = pfade_ctx->target_cct;
// 标记为瞬间完成,不启动定时器,但需要触发一次更新
// 这里通过设置极短时间来让task立即完成
pfade_ctx->duration_ms = 0;
}
// 3. 设置定时器刷新频率
// 固定使用较快的刷新率以保证平滑度,例如 10ms (100Hz)
// 如果系统负载高,可以适当降低,例如 20ms
pfade_ctx->update_interval = 8 * 1000; // 8ms in us
}
static void cancel_current_light_fade(void)
{
// 竞态保护:先拉低标志再停计时器,避免回调自重启
fade_ctx.timer_active = false;
uapi_timer_stop(fade_ctx.timer_handle);
// 清理标志,防止过期回调触发收尾导致关断等副作用
fade_ctx.is_fading = false;
fade_ctx.is_closing_fade = false;
fade_ctx.fade_completed = false;
}
// 计算校验和
static uint8_t calculate_checksum(const device_data_t* data)
{
uint8_t sum = 0;
const uint8_t* ptr = (const uint8_t*)(((char*)data) + sizeof(data->checksum));
size_t len = sizeof(device_data_t) - sizeof(data->checksum); // 减去checksum字段的大小
for (size_t i = 0; i < len; i++) {
sum ^= ptr[i];
}
return sum;
}
// 验证数据有效性
static bool verify_device_data(const device_data_t* data)
{
if (data->magic != DEVICE_DATA_MAGIC) {
e_printf("magic error:%x\n", data->magic);
return false;
}
if (data->version > DEVICE_DATA_VERSION) {
e_printf("versoin missmatch:%x\n", data->version);
return false;
}
uint8_t checksum = calculate_checksum(data);
if (checksum != data->checksum) {
e_printf("check sum error:%x\n", data->version);
return false;
}
return true;
}
// 从指定地址读取设备数据
static bool read_device_data_from_addr(uint32_t addr, device_data_t* data)
{
int ret = hfuflash_read(addr, (char*)data, sizeof(device_data_t));
if (ret != sizeof(device_data_t)) {
e_printf("从地址0x%x读取设备数据失败实际读取长度%d\r\n", addr, ret);
return false;
}
if (!verify_device_data(data)) {
e_printf("地址0x%x的数据无效\r\n", addr);
return false;
}
return true;
}
// 写入数据到指定地址
static bool write_device_data_to_addr(uint32_t addr, const device_data_t* data)
{
int ret = hfuflash_erase_page(addr, 1);
if (ret != HF_SUCCESS) {
e_printf("擦除地址0x%x的Flash页失败错误码%d\r\n", addr, ret);
return false;
}
ret = hfuflash_write(addr, (char*)data, sizeof(device_data_t));
if (ret != sizeof(device_data_t)) {
e_printf("写入数据到地址0x%x失败实际写入长度%d\r\n", addr, ret);
return false;
}
return true;
}
void read_device_data(void)
{
#if SUPPORT_SAVE_TO_FLASH
device_data_t data;
bool main_valid = false;
bool backup_valid = false;
// 尝试读取主数据区
main_valid = read_device_data_from_addr(DEVICE_DATA_FLASH_ADDR, &data);
// 如果主数据区无效,尝试读取备份区
if (!main_valid) {
backup_valid = read_device_data_from_addr(DEVICE_DATA_BACKUP_ADDR, &data);
}
if (!main_valid && backup_valid) { // 如果主数据区无效但备份区有效,从备份区恢复
e_printf("从备份区恢复数据\r\n");
write_device_data_to_addr(DEVICE_DATA_FLASH_ADDR, &data);
} else if (!main_valid && !backup_valid) { // 如果两个区域都无效,使用默认值
e_printf("主数据和备份数据都无效,使用默认值\r\n");
// 只打印g_device_control的当前值不打印data.control
e_printf("上电次数: %d\r\n", g_device_control.power_on_cnt);
e_printf("配网状态: %d\r\n", g_device_control.is_bound);
e_printf("是否是新设备: %d\r\n", !g_device_control.is_net_configured);
e_printf("开关状态: %d\r\n", g_device_control.on);
e_printf("灯光模式: %d\r\n", g_device_control.elightMode);
e_printf("亮度: %d\r\n", g_device_control.brightness_local);
e_printf("色温: %d\r\n", g_device_control.cct_local);
e_printf("冷白LED: %d\r\n", g_device_control.duty_cw);
e_printf("暖白LED: %d\r\n", g_device_control.duty_ww);
e_printf("渐变时间: %d\r\n", g_device_control.fade_time);
goto lab_exit;
}
// 更新设备控制状态
e_printf("设备状态恢复:\r\n");
e_printf("上电次数: %d => %d\r\n", g_device_control.power_on_cnt, data.control.power_on_cnt);
e_printf("配网状态: %d => %d\r\n", g_device_control.is_bound, data.control.is_bound);
e_printf("是否是新设备: %d => %d\r\n", !g_device_control.is_net_configured, !data.control.is_net_configured);
e_printf("开关状态: %d => %d\r\n", g_device_control.on, data.control.on);
e_printf("灯光模式: %d => %d\r\n", g_device_control.elightMode, data.control.elightMode);
e_printf("亮度: %d => %d\r\n", g_device_control.brightness_local, data.control.brightness_local);
e_printf("色温: %d => %d\r\n", g_device_control.cct_local, data.control.cct_local);
e_printf("色温模式: %d => %d\r\n", g_device_control.colourMode, data.control.colourMode);
e_printf("冷白LED: %d => %d\r\n", g_device_control.duty_cw, data.control.duty_cw);
e_printf("暖白LED: %d => %d\r\n", g_device_control.duty_ww, data.control.duty_ww);
e_printf("渐变时间: %d => %d\r\n", g_device_control.fade_time, data.control.fade_time);
data.control.read_done = false;
g_device_control = data.control;
// fastboot的时候就已经亮起来了所以当前测温直接变为目标值
// fade_ctx.current_brightness = g_device_control.brightness_local;
// fade_ctx.current_cct = g_device_control.cct_local;
lab_exit:
#endif
g_device_control.read_done = true;
e_printf("读取设备数据成功\r\n");
}
void save_device_data(void)
{
e_printf("Starting to save device data\n");
#if SUPPORT_SAVE_TO_FLASH
// 使用静态变量保存上一次的数据状态
static device_control_t last_control = {0};
static bool is_first_save = true;
// 需要比较的字段(根据注释标记的持久化维持部分)
bool need_save = is_first_save ||
last_control.on != g_device_control.on ||
last_control.elightMode != g_device_control.elightMode ||
last_control.brightness_local != g_device_control.brightness_local ||
last_control.fade_time != g_device_control.fade_time ||
last_control.cct_local != g_device_control.cct_local ||
last_control.colourMode != g_device_control.colourMode ||
last_control.power_on_cnt != g_device_control.power_on_cnt ||
last_control.is_bound != g_device_control.is_bound ||
last_control.is_net_configured != g_device_control.is_net_configured;
if (!need_save) {
e_printf("No changes detected, skip saving\n");
return;
}
device_data_t data;
// 准备要保存的数据
data.magic = DEVICE_DATA_MAGIC;
data.version = DEVICE_DATA_VERSION;
data.control = g_device_control;
data.checksum = calculate_checksum(&data);
// 先写入备份区
if (!write_device_data_to_addr(DEVICE_DATA_BACKUP_ADDR, &data)) {
e_printf("Failed to write backup data\n");
return;
}
// 再写入主数据区
if (!write_device_data_to_addr(DEVICE_DATA_FLASH_ADDR, &data)) {
e_printf("Failed to write main data, but backup data saved\n");
}
// 更新上一次的数据状态
last_control = g_device_control;
is_first_save = false;
e_printf("Device data saved successfully\n");
#endif
e_printf("Save device data done\n");
}
void req_save_device_data(void)
{
osal_sem_up(&save_data_sem);
}
// 保存任务函数
static void *save_data_task(const char *arg)
{
e_printf("[save_data_task] Save task started\n");
while (save_task_running) {
// 等待保存信号
if (osal_sem_down_timeout(&save_data_sem, 100*1000) != OSAL_SUCCESS) {
continue;
}
while(OSAL_SUCCESS == osal_sem_trydown(&save_data_sem));//消耗所有的信号一次性刷入
save_device_data();
}
e_printf("[save_data_task] Save task exited\n");
return NULL;
}
// 初始化保存任务
static void init_save_task(void)
{
if (save_task_handle != NULL) {
return;
}
// 初始化信号量
if (osal_sem_init(&save_data_sem, 0) != OSAL_SUCCESS) {
e_printf("[init_save_task] Failed to init semaphore\n");
return;
}
// 创建保存任务
osal_kthread_lock();
save_task_handle = osal_kthread_create((osal_kthread_handler)save_data_task, NULL, "SaveDataTask",
SAVE_TASK_STACK_SIZE);
if (save_task_handle != NULL) {
save_task_running = true;
osal_kthread_set_priority(save_task_handle, SAVE_TASK_PRIO);
e_printf("[init_save_task] Save task created successfully\n");
} else {
e_printf("[init_save_task] Failed to create save task\n");
osal_sem_destroy(&save_data_sem);
}
osal_kthread_unlock();
}
// 停止保存任务
static void stop_save_task(void)
{
if (save_task_handle == NULL) {
return;
}
save_task_running = false;
osal_sem_up(&save_data_sem); // 唤醒任务以使其退出
osal_kthread_lock();
osal_kthread_destroy(save_task_handle, 0);
save_task_handle = NULL;
osal_kthread_unlock();
osal_sem_destroy(&save_data_sem);
e_printf("[stop_save_task] Save task stopped\n");
}
// 设置灯光状态(统一控制函数)
// 参数:
// source: 控制源,用于区分不同的控制场景
// current_brightness_local: 当前亮度值
// current_cct_local: 当前色温值
// brightness_local_target: 亮度目标值
// cct_local_target: 色温目标值
// 返回值:
// 0: 成功
// -111: 异步上报
// -1: 失败
int set_light(light_ctrl_source_e source,
int32_t brightness_local_target, int32_t cct_local_target)
{
bool was_on = g_device_control.on;
bool breath_active = is_in_breath_mode();
bool was_closing_fade = (fade_ctx.is_fading && fade_ctx.is_closing_fade);
struct fade_ctx_t tmp_fade_ctx = {0};
if (fade_ctx.is_fading) {
bool closing_fade_active = fade_ctx.is_closing_fade;
bool suppress_cancel = false;
// 在关灯渐变过程中,属性/模式变更不应打断关灯过程
if (closing_fade_active) {
if (source == APP_CHANGE_LIGHT_BRIGHTNESS_CCT || source == APP_CHANGE_LIGHT_MODE || source == APP_CLOSE_LIGHT) {
suppress_cancel = true; // 允许继续关灯渐变;再次关灯请求可复用或稍后重启
}
}
// 配网呼吸期间,不允许普通渐变取消/抢占,避免干扰呼吸效果
if (!suppress_cancel && !breath_active) {
cancel_current_light_fade();
}
}
// 如果打开灯,则需要手动将前置状态设置为关闭的样子,这样子后面计算渐变才能正常计算
if (APP_OPEN_LIGHT == source || DEV_POWER_ON == source) {
g_device_control.on = true;
if (!breath_active) {
if (was_closing_fade && APP_OPEN_LIGHT == source && !was_on) {
// 非配网场景:开灯打断关灯渐变,沿用即时亮度作为起点(无缝反向)
e_printf("[set_light] 开灯打断关灯渐变: curr=%d -> target=%d\n",
fade_ctx.current_brightness, g_device_control.brightness_local);
fade_ctx.is_closing_fade = false;
// 起点保持为当前渐变即时亮度
fade_ctx.current_cct = g_device_control.cct_local;
} else if (DEV_POWER_ON == source || (APP_OPEN_LIGHT == source && !was_on)) {
// 真正关→开从0开始渐变
fade_ctx.current_brightness = 0;
fade_ctx.current_cct = g_device_control.cct_local; // 色温保持目标值
e_printf("[set_light] 开灯渐变设置: brightness=0->%d, cct=%d\n",
g_device_control.brightness_local, fade_ctx.current_cct);
}
}
brightness_local_target = g_device_control.brightness_local;
// 色温不进行变化,只改变亮度
cct_local_target = g_device_control.cct_local;
if (g_device_control.colourMode != COLOUR_MODE_DUAL) {
fade_ctx.current_cct = SINGLE_COLOUR_CCT_LOCAL;
cct_local_target = SINGLE_COLOUR_CCT_LOCAL;
}
}
memcpy(&tmp_fade_ctx, &fade_ctx, FADE_CTRL_DATA_SIZE(fade_ctx));
// 默认目标应为“当前设备状态”避免在未指定字段时误用上一次渐变的目标可能为0
tmp_fade_ctx.target_brightness = g_device_control.brightness_local;
tmp_fade_ctx.target_cct = g_device_control.cct_local;
if (brightness_local_target >= 0) {
BRIGHTNESS_LITME_RANGE(brightness_local_target);
tmp_fade_ctx.target_brightness = brightness_local_target;
} else {
e_printf("[set_light] keep brightness:%d (source:%d)\r\n",
tmp_fade_ctx.target_brightness, source);
}
if (cct_local_target >= 0) {
CCT_LITME_RANGE(cct_local_target);
tmp_fade_ctx.target_cct = cct_local_target;
} else {
e_printf("[set_light] keep cct:%d (source:%d)\r\n",
tmp_fade_ctx.target_cct, source);
}
e_printf("source:%d, lightMode:%d, local brightness: base:%d, target:%d, local cct: base:%d, target:%d\r\n",
source, g_device_control.elightMode, tmp_fade_ctx.current_brightness,
tmp_fade_ctx.target_brightness, tmp_fade_ctx.current_cct, tmp_fade_ctx.target_cct);
tmp_fade_ctx.fade_time = g_device_control.fade_time;
tmp_fade_ctx.is_closing_fade = false;
switch (source) {
case APP_CLOSE_LIGHT:
// 使用渐变方式关灯设置目标亮度为0使用is_closing_fade标志
g_device_control.on = false; // 设置开关状态为关闭(用于状态上报)
tmp_fade_ctx.is_closing_fade = true; // 标记为关灯渐变
tmp_fade_ctx.target_brightness = 0; // 目标亮度设为0
tmp_fade_ctx.target_cct = tmp_fade_ctx.current_cct; // 色温保持不变
tmp_fade_ctx.fade_time = NORMAL_FADE_TIME; // 使用默认渐变时长
tmp_fade_ctx.is_fading = true;
tmp_fade_ctx.fade_completed = false;
if (tmp_fade_ctx.is_fading && !breath_active) {
cancel_current_light_fade();
}
calculate_fade_steps(&tmp_fade_ctx); // 复用原有计算流程
memcpy(&fade_ctx, &tmp_fade_ctx, FADE_CTRL_DATA_SIZE(tmp_fade_ctx));
e_printf("start close light fade\r\n");
fade_ctx.timer_active = true;
uapi_timer_start(fade_ctx.timer_handle, fade_ctx.update_interval, fade_timer_callback, 0);
start_report_task(REPORT_SWITCH | REPORT_LIGHT_MODE);
req_save_device_data();
return -111; // 异步上报
break;
case DEV_POWER_ON:
case APP_OPEN_LIGHT:
tmp_fade_ctx.fade_time = NORMAL_FADE_TIME; // 上电时,直接设置为默认渐变时长
case APP_CHANGE_LIGHT_MODE:
// 开灯场景或上电:执行渐变;关灯状态下仅更新目标不执行渐变
if (breath_active) {
// 配网期:只更新目标/上报,不启动普通渐变
if (g_device_control.colourMode == COLOUR_MODE_DUAL) {
g_device_control.cct_local = tmp_fade_ctx.target_cct;
}
g_device_control.brightness_local = tmp_fade_ctx.target_brightness;
start_report_task(REPORT_SWITCH | REPORT_BRIGHTNESS | REPORT_CCT | REPORT_LIGHT_MODE | REPORT_COLOUR_MODE);
req_save_device_data();
return -111;
} else if (was_on || source != APP_CHANGE_LIGHT_MODE) {
if (g_device_control.colourMode == COLOUR_MODE_DUAL) {
g_device_control.cct_local = tmp_fade_ctx.target_cct;
}
g_device_control.brightness_local = tmp_fade_ctx.target_brightness;
tmp_fade_ctx.is_fading = true;
tmp_fade_ctx.fade_completed = false;
if (tmp_fade_ctx.is_fading) {
cancel_current_light_fade();
}
calculate_fade_steps(&tmp_fade_ctx);
memcpy(&fade_ctx, &tmp_fade_ctx, FADE_CTRL_DATA_SIZE(tmp_fade_ctx));
e_printf("start fade\r\n");
fade_ctx.timer_active = true;
uapi_timer_start(fade_ctx.timer_handle, fade_ctx.update_interval, fade_timer_callback, 0);
start_report_task(REPORT_SWITCH | REPORT_BRIGHTNESS | REPORT_CCT | REPORT_LIGHT_MODE | REPORT_COLOUR_MODE);
req_save_device_data();
return 0; // 异步上报
} else {
// 灯处于关闭仅更新目标值保持当前亮度为0不触发渐变
if (g_device_control.colourMode == COLOUR_MODE_DUAL) {
g_device_control.cct_local = tmp_fade_ctx.target_cct;
// 关灯渐变进行中不改动 fade_ctx避免打断渐变过程的内部状态
if (!fade_ctx.is_closing_fade) {
fade_ctx.current_cct = tmp_fade_ctx.target_cct; // 预设色温,开灯后按新色温渐变
}
}
g_device_control.brightness_local = tmp_fade_ctx.target_brightness;
e_printf("[set_light] off-state mode change, preserve current_brightness:%d, target_brightness:%d\r\n",
fade_ctx.current_brightness, g_device_control.brightness_local);
start_report_task(REPORT_BRIGHTNESS | REPORT_CCT | REPORT_LIGHT_MODE | REPORT_COLOUR_MODE);
req_save_device_data();
return -111;
}
break;
case APP_CHANGE_LIGHT_BRIGHTNESS_CCT:
if (breath_active) {
// 配网期:只更新目标/上报
if (g_device_control.colourMode == COLOUR_MODE_DUAL && cct_local_target >= 0) {
g_device_control.cct_local = tmp_fade_ctx.target_cct;
}
if (brightness_local_target >= 0) {
g_device_control.brightness_local = tmp_fade_ctx.target_brightness;
}
start_report_task(REPORT_BRIGHTNESS | REPORT_CCT | REPORT_LIGHT_MODE | REPORT_COLOUR_MODE);
req_save_device_data();
} else if (!was_on) {
// 关灯状态:仅更新目标,不执行渐变,保持 current_brightness=0避免开灯时跳变
if (g_device_control.colourMode == COLOUR_MODE_DUAL && cct_local_target >= 0) {
g_device_control.cct_local = tmp_fade_ctx.target_cct;
if (!fade_ctx.is_closing_fade) {
fade_ctx.current_cct = tmp_fade_ctx.target_cct; // 预设色温
}
}
if (brightness_local_target >= 0) {
g_device_control.brightness_local = tmp_fade_ctx.target_brightness;
}
e_printf("[set_light] off-state attr change, keep current_brightness:%d, target_brightness:%d\r\n",
fade_ctx.current_brightness, g_device_control.brightness_local);
start_report_task(REPORT_BRIGHTNESS | REPORT_CCT | REPORT_LIGHT_MODE | REPORT_COLOUR_MODE);
req_save_device_data();
} else {
// 开灯状态:执行正常渐变
if (g_device_control.colourMode == COLOUR_MODE_DUAL && cct_local_target >= 0) { // 双色温且明确修改时才更新色温
g_device_control.cct_local = tmp_fade_ctx.target_cct;
}
if (brightness_local_target >= 0) { // 明确修改亮度时才更新亮度
g_device_control.brightness_local = tmp_fade_ctx.target_brightness;
}
tmp_fade_ctx.is_fading = true;
tmp_fade_ctx.fade_completed = false;
if (tmp_fade_ctx.is_fading) {
cancel_current_light_fade();
}
calculate_fade_steps(&tmp_fade_ctx);
memcpy(&fade_ctx, &tmp_fade_ctx, FADE_CTRL_DATA_SIZE(tmp_fade_ctx));
e_printf("start fade\r\n");
fade_ctx.timer_active = true;
uapi_timer_start(fade_ctx.timer_handle, fade_ctx.update_interval, fade_timer_callback, 0);
start_report_task(REPORT_BRIGHTNESS | REPORT_CCT | REPORT_LIGHT_MODE | REPORT_COLOUR_MODE);
req_save_device_data();
}
#if 0
等待渐变完成
while (!fade_ctx.fade_completed) {
msleep(10);
}
return 0; // 同步上报
#else
return -111; // 异步上报
#endif
break;
}
e_printf("set_light error, source:%d, current_brightness_local:%d, current_cct_local:%d, brightness_local_target:%d, cct_local_target:%d\r\n",
source, tmp_fade_ctx.current_brightness, tmp_fade_ctx.current_cct, brightness_local_target, cct_local_target);
return -1;
}
void close_light(void)
{
g_device_control.on = false;
if (fade_ctx.is_fading) {
cancel_current_light_fade();
}
fade_ctx.current_brightness = 0;
fade_ctx.current_cct = 0;
update_pwm_output(false, 0, 0);
req_save_device_data();
}
static void gpio_init(pin_t pin)
{
/* PINCTRL init. */
uapi_pin_init();
uapi_pin_set_mode(pin, HAL_PIO_FUNC_GPIO);
uapi_gpio_set_dir(pin, GPIO_DIRECTION_OUTPUT);
uapi_pin_set_ds(pin, PIN_DS_3);
uapi_pin_set_pull(pin, PIN_PULL_TYPE_DOWN);
uapi_gpio_set_val(pin, CLOSE_LIGHT);
}
static void pwm_sys_init(void) {
uapi_pwm_deinit();
}
static void pwm_init(pin_t pin, pin_t pin1)
{
pwm_config_t cfg_repeat = {0};
cfg_repeat.repeat = true;
uapi_pin_set_mode(pin, PIN_MODE_1);
uapi_pin_set_mode(pin1, PIN_MODE_1);
uapi_pwm_init();
// 基准时钟固定 80MHz客户指定。不要使用 uapi_pwm_get_frequency 返回值。
channel_id_cw = pin % 8;
channel_id_ww = pin1 % 8;
const uint32_t base_clk = 80 * 1000 * 1000; // 80MHz 固定
pwm_period_cnt = (base_clk / PWM_FREQUENCY);
// 初始为关灯高电平0低电平整周期固定相位
cfg_repeat.high_time = 0;
cfg_repeat.low_time = pwm_period_cnt;
cfg_repeat.offset_time = 0;
// 先打开两个通道
(void)uapi_pwm_open(channel_id_cw, &cfg_repeat);
(void)uapi_pwm_open(channel_id_ww, &cfg_repeat);
// 设置分组并启动(分组只需启动一次)
uint8_t channel_ids[2] = {channel_id_cw, channel_id_ww};
(void)uapi_pwm_set_group(CONFIG_PWM_GROUP_ID, channel_ids, sizeof(channel_ids));
(void)uapi_pwm_start_group(CONFIG_PWM_GROUP_ID);
e_printf("PWM基础时钟频率(固定): %uHz, 目标时钟频率: %dHz, 周期计数: %u\r\n", base_clk, PWM_FREQUENCY, pwm_period_cnt);
}
extern int start_hilink_ble_net_config(int32_t net_cfg_time_s);
void start_net_config(void) {
e_printf("进入配网状态,上电次数:%d, is_new设备:%d\r\n", g_device_control.power_on_cnt, !g_device_control.is_net_configured);
// 按新规则上电计数仅能在上电超过5秒后自然清零
// 配网流程不再主动清除 power_on_cnt。
int ret = start_hilink_ble_net_config(NET_CFG_TOTAL_TIMEOUT/1000);
if (ret) {
// FIXME: 这里简单恢复之前等待配网的灯效即可
e_printf("start net_cfg fail:%n\r\n", ret);
req_save_device_data();
// extern int HILINK_RestoreFactorySettings(void);
// HILINK_RestoreFactorySettings();
return;
}
// 初始化呼吸灯控制
init_breath_ctx();
start_breath_timer();
}
void stop_net_config(void) {
stop_breath_timer();
start_hilink_ble_net_config(0);
}
// 配网状态管理相关函数
static int handle_network_status(void)
{
uint8_t start_net_cfg = 0;
// 增加上电计数
g_device_control.power_on_cnt++;
e_printf("现在是第 %d 次上电\r\n", g_device_control.power_on_cnt);
// 检查是否需要进入配网状态或恢复出厂设置
if (g_device_control.power_on_cnt >= NET_CFG_ENTRY_CNT) {
// 任何状态下达到阈值都强制解绑并恢复出厂设置
e_printf("快速上电达到 %d 次,强制恢复出厂设置(解绑)\r\n", NET_CFG_ENTRY_CNT);
g_reset_factory_flag = true;
g_device_control.power_on_cnt = 0;
extern int HILINK_RestoreFactorySettings(void);
while (!hf_hilink_main_is_runing()) {
msleep(10);
}
HILINK_RestoreFactorySettings();
return start_net_cfg;
} else if (!g_device_control.is_net_configured) {
// 未出厂设备直接进入配网
start_net_config();
start_net_cfg = 1;
}
// 保存设备状态
req_save_device_data();
// 如果已经配网,直接返回
if (g_device_control.is_bound) {
// 如果已经配网则设置发出ble广播确保如果没法连接路由器可以转为本地蓝牙控制
start_hilink_ble_net_config(0xFFFFFFFF);
return start_net_cfg;
}
return start_net_cfg;
}
// 设备上线时的状态上报
static void report_device_online_status(void)
{
e_printf("Starting to report all service status\r\n");
fast_report(SVC_ID_SWITCH);
fast_report(SVC_ID_BRIGHTNESS);
fast_report(SVC_ID_CCT);
fast_report(SVC_ID_LIGHT_MODE);
fast_report(SVC_ID_FADE_TIME);
fast_report(SVC_ID_COLOUR_MODE);
// fast_report(SVC_ID_NET_INFO);
e_printf("Status report completed\r\n");
}
// 渐变结束时的状态上报
static void report_fade_complete_status(void)
{
req_save_device_data();
e_printf("Reporting fade complete status\r\n");
fast_report(SVC_ID_LIGHT_MODE);
fast_report(SVC_ID_BRIGHTNESS);
fast_report(SVC_ID_CCT);
e_printf("Fade status report completed\r\n");
}
// 渐变结束时的状态上报
static void report_switch_status(void)
{
req_save_device_data();
fast_report(SVC_ID_SWITCH);
}
static const struct {
const char* svc_id;
} report_mask_map[] = {
[REPORT_SWITCH] = {SVC_ID_SWITCH},
[REPORT_BRIGHTNESS] = {SVC_ID_BRIGHTNESS},
[REPORT_CCT] = {SVC_ID_CCT},
[REPORT_LIGHT_MODE] = {SVC_ID_LIGHT_MODE},
[REPORT_FADE_TIME] = {SVC_ID_FADE_TIME},
[REPORT_COLOUR_MODE] = {SVC_ID_COLOUR_MODE},
};
// 状态上报任务函数
static void *report_task(const char *arg)
{
unused(arg);
e_printf("[report_task] Task started\r\n");
// 等待hilink和数据ready
while (!(g_device_control.read_done && hf_hilink_main_is_runing())) {
e_printf("read_done%d, hilink:%d\r\n", g_device_control.read_done, hf_hilink_main_is_runing());
osal_msleep(50);
}
while (report_task_running) {
// 等待信号量唤醒
if (g_report_mask == 0 && osal_sem_down_timeout(&report_sem, 100*1000) != OSAL_SUCCESS) {
continue;
}
e_printf("[report_task] Reported mask: 0x%x\r\n", g_report_mask);
// 根据掩码进行上报
if (g_report_mask & REPORT_SWITCH) {
g_report_mask &= ~REPORT_SWITCH;
fast_report(SVC_ID_SWITCH);
}
if (g_report_mask & REPORT_BRIGHTNESS) {
g_report_mask &= ~REPORT_BRIGHTNESS;
fast_report(SVC_ID_BRIGHTNESS);
}
if (g_report_mask & REPORT_CCT) {
g_report_mask &= ~REPORT_CCT;
fast_report(SVC_ID_CCT);
}
if (g_report_mask & REPORT_LIGHT_MODE) {
g_report_mask &= ~REPORT_LIGHT_MODE;
fast_report(SVC_ID_LIGHT_MODE);
}
if (g_report_mask & REPORT_FADE_TIME) {
g_report_mask &= ~REPORT_FADE_TIME;
fast_report(SVC_ID_FADE_TIME);
}
if (g_report_mask & REPORT_COLOUR_MODE) {
g_report_mask &= ~REPORT_COLOUR_MODE;
fast_report(SVC_ID_COLOUR_MODE);
}
}
e_printf("[report_task] Task exited\r\n");
return NULL;
}
// 启动状态上报任务
static void start_report_task(uint32_t report_mask)
{
// 设置全局掩码
g_report_mask |= report_mask;
osal_sem_up(&report_sem);
// 只在第一次创建任务
if (!report_task_running) {
osal_kthread_lock();
if (report_task_running) {
osal_kthread_unlock();
return;
}
report_task_running = true;
e_printf("[start_report_task] Creating report task\r\n");
report_task_handle = osal_kthread_create((osal_kthread_handler)report_task, NULL, "ReportStaTask",
REPORT_TASK_STACK_SIZE);
if (report_task_handle != NULL) {
osal_kthread_set_priority(report_task_handle, REPORT_TASK_PRIO);
e_printf("[start_report_task] Report task created successfully\r\n");
} else {
e_printf("[start_report_task] Failed to create report task\r\n");
report_task_running = false;
}
osal_kthread_unlock();
} else {
e_printf("[start_report_task] Report task already running, mask: 0x%x\r\n", report_mask);
}
}
// 设备注册事件:仅处理配网相关(绑定标志、退出配网)
void handle_device_register(void)
{
e_printf("device registered!\r\n");
g_device_control.is_net_configured = true; // 视为完成一次配网流程
if (!g_device_control.is_bound) {
e_printf("设备首次注册,记录配网状态\r\n");
g_device_control.is_bound = true;
stop_breath_timer();
set_light2net_cfg_done();
req_save_device_data();
}
g_net_breath_state = NET_CFG_IDLE;
g_net_cfg_start_time = 0;
}
// 设备注销事件仅清理配网行为不涉及online/offline状态
void handle_device_unregister(void)
{
e_printf("device unregistered!\r\n");
stop_breath_timer();
g_net_breath_state = NET_CFG_IDLE;
g_net_cfg_start_time = 0;
}
// 处理设备解绑
void handle_device_unbind(void)
{
e_printf("设备被解绑,重置配网状态:%d\r\n", g_device_control.is_bound);
if (g_device_control.is_bound) {
g_device_control.is_bound = false;
// stop_spotlight_main_task();
device_control_t tmp = DEFAULT_DEVICE_DATA;//恢复默认
// if (!g_reset_factory_flag) {
// tmp.is_net_configured = g_device_control.is_net_configured;
// }
g_device_control = tmp;
}
save_device_data();
}
uint64_t startup_time = 0;
static void check_net_cfg_power_on_keep_time(void)
{
if (g_device_control.power_on_cnt <= 0) {
return;
}
if (uapi_systick_get_ms() - startup_time > INIT_NET_CFG_PWOER_ON_KEEP_TIME) {
e_printf("power_on keep time is %llums must < %ds, we will reset cnt\r\n", uapi_systick_get_ms(), INIT_NET_CFG_PWOER_ON_KEEP_TIME/1000);
g_device_control.power_on_cnt = 0;
req_save_device_data();
}
}
// spotlight主任务相关变量
#define SPOTLIGHT_MAIN_TASK_PRIO 23
#define SPOTLIGHT_MAIN_TASK_STACK_SIZE 0x1000
static osal_task *spotlight_main_task_handle = NULL;
static bool spotlight_main_task_running = false;
// spotlight主任务函数
static void *spotlight_main_task(const char *arg)
{
unused(arg);
e_printf("[spotlight_main_task] Task started\r\n");
uint32_t current_time = 0; //uapi_systick_get_ms();
// 自愈在HiLink Main运行后做一次绑定状态一致性检查
static bool s_bind_consistency_checked = false;
while (spotlight_main_task_running) {
// 检查配网超时
check_net_cfg_timeout();
check_net_cfg_power_on_keep_time();
if (!s_bind_consistency_checked && hf_hilink_main_is_runing()) {
// 仅执行一次检查应用层与HiLink SDK的绑定状态是否一致
extern int HILINK_IsRegister(void);
extern int HILINK_RestoreFactorySettings(void);
int reg = HILINK_IsRegister();
bool sdk_bound = (reg != 0);
bool app_bound = g_device_control.is_bound;
if (sdk_bound != app_bound) {
e_printf("[consistency] app_bound=%d, sdk_bound=%d, do factory reset\r\n", app_bound, sdk_bound);
// 触发完整流程由HiLink框架内部清理持久化并按注册的重启回调执行
HILINK_RestoreFactorySettings();
}
s_bind_consistency_checked = true;
}
osal_msleep(20);
}
// 停止保存任务
stop_save_task();
e_printf("[spotlight_main_task] Task exited\r\n");
return NULL;
}
// 启动spotlight主任务
static void start_spotlight_main_task(void)
{
if (spotlight_main_task_handle != NULL) {
e_printf("[start spotlight task] Task already running\r\n");
return;
}
e_printf("[start spotlight task] Starting task\r\n");
osal_kthread_lock();
spotlight_main_task_handle = osal_kthread_create((osal_kthread_handler)spotlight_main_task, NULL, "SpotlightMain",
SPOTLIGHT_MAIN_TASK_STACK_SIZE);
if (spotlight_main_task_handle != NULL) {
spotlight_main_task_running = true;
osal_kthread_set_priority(spotlight_main_task_handle, SPOTLIGHT_MAIN_TASK_PRIO);
e_printf("[start spotlight task] Task created successfully\r\n");
} else {
e_printf("[start spotlight task] Failed to create task\r\n");
}
osal_kthread_unlock();
}
// 停止spotlight主任务
static void stop_spotlight_main_task(void)
{
if (spotlight_main_task_handle == NULL) {
return;
}
spotlight_main_task_running = false;
osal_kthread_lock();
osal_kthread_destroy(spotlight_main_task_handle, 0);
spotlight_main_task_handle = NULL;
osal_kthread_unlock();
// 停止report task
if (report_task_running) {
report_task_running = false;
osal_sem_up(&report_sem); // 唤醒任务以使其退出
osal_kthread_lock();
if (report_task_handle) {
osal_kthread_destroy(report_task_handle, 0);
report_task_handle = NULL;
}
osal_kthread_unlock();
osal_sem_destroy(&report_sem);
e_printf("[stop_spotlight_main_task] Report task stopped\r\n");
}
e_printf("[stop_spotlight_main_task] Task stopped\r\n");
}
// 检查配网超时
static void check_net_cfg_timeout(void)
{
if (g_net_breath_state != NET_CFG_BREATHING) {
return;
}
uint64_t current_time = uapi_systick_get_ms();
uint64_t elapsed_time = current_time - g_net_cfg_start_time;
if (elapsed_time >= NET_CFG_TOTAL_TIMEOUT) {
e_printf("[Net Cfg] 配网超过10分钟退出配网模式\r\n");
g_net_breath_state = NET_CFG_TIMEOUT;
}
}
// 修改spotlight_main函数
int spotlight_main(void) {
uapi_systick_init();
startup_time = uapi_systick_get_ms();
e_printf("uapi_timer_get_max_us:%uus, start_time:%uus\r\n", uapi_timer_get_max_us(), uapi_systick_get_ms());
read_device_data();
// 初始化GPIO并将灯关闭
gpio_init(SWITCH_PIN);
// 初始化 PWM 更新标志引脚GPIO10为普通GPIO输出默认拉低
gpio_init(PWM_UPDATE_FLAG_PIN);
g_pwm_update_flag_inited = true;
// 初始化PWM系统
pwm_sys_init();
pwm_init(BRIGHTNESS_PIN, CCT_PIN);
// 初始化渐变控制
init_fade_ctx();
// 初始化保存任务
init_save_task();
// 初始化report task信号量
if (osal_sem_init(&report_sem, 0) != OSAL_SUCCESS) {
e_printf("[spotlight_main] Failed to init report semaphore\r\n");
return -1;
}
// 检查配网状态,确定是否需要进入配网状态
handle_network_status();
//上电亮灯也是需要缓慢亮起来, 如果是在配网的话就不用了。交给配网的代码进行控制
if (g_net_breath_state == NET_CFG_IDLE && g_device_control.on == true){
if (g_device_control.colourMode == COLOUR_MODE_SINGLE) {
set_light(DEV_POWER_ON, g_device_control.brightness_local, SINGLE_COLOUR_CCT_LOCAL);
} else {
set_light(DEV_POWER_ON, g_device_control.brightness_local, g_device_control.cct_local);
}
// set_light(DEV_POWER_ON, g_device_control.brightness_local, g_device_control.cct_local);
}
if (!g_device_control.is_bound && !g_device_control.is_net_configured) {
try_detect_factory_test();
}
start_spotlight_main_task();
return 0;
}
// 设置渐变时长
int set_smooth_time(uint32_t smooth_time)
{
e_printf("[set_smooth_time] Setting smooth time: %d s\r\n", smooth_time);
if (smooth_time < SMOOTH_TIME_MIN) {
e_printf("[set_smooth_time] Smooth time below minimum, adjusted to: %d s\r\n", SMOOTH_TIME_MIN);
smooth_time = SMOOTH_TIME_MIN;
}
if (smooth_time > SMOOTH_TIME_MAX) {
e_printf("[set_smooth_time] Smooth time above maximum, adjusted to: %d s\r\n", SMOOTH_TIME_MAX);
smooth_time = SMOOTH_TIME_MAX;
}
g_device_control.fade_time = smooth_time;
req_save_device_data();
e_printf("[set_smooth_time] Smooth time setting completed\r\n");
return 0;
}
// 设置灯光到配网超时状态
static void set_light2breathtimeout(void)
{
e_printf("[Net cfg] 配网超过1分钟退出呼吸灯状态\r\n");
// 恢复到自定义模式
g_device_control.brightness_local = BRIGHTNESS_REMOTE2LOCAL(NET_CFG_TIMEOUT_BRIGHTNESS);
g_device_control.cct_local = CCT_REMOTE2LOCAL(NET_CFG_TIMEOUT_CCT);
fade_ctx.current_brightness = g_device_control.brightness_local;
fade_ctx.current_cct = g_device_control.cct_local;
calculate_pwm_duty(&g_device_control);
update_pwm_output(g_device_control.on, g_device_control.duty_cw, g_device_control.duty_ww);
}
// 设置灯光到配网完成状态
static void set_light2net_cfg_done(void)
{
g_device_control.elightMode = NET_CFG_DEFAULT_LIGHTMODE;
g_device_control.brightness_local = BRIGHTNESS_REMOTE2LOCAL(NET_CFG_DEFAULT_BRIGHTNESS);
g_device_control.cct_local = CCT_REMOTE2LOCAL(NET_CFG_DEFAULT_CCT);
g_device_control.fade_time = NET_CFG_DEFAULT_FADE_TIME;
// 🔧 关键修复配网完成后直接使用目标色温不从PWM反推
// PWM反推逻辑是错误的因为呼吸灯PWM状态不代表用户期望的色温
fade_ctx.current_brightness = g_device_control.brightness_local;
fade_ctx.current_cct = g_device_control.cct_local; // 直接使用目标色温
e_printf("[set_light2net_cfg_done] 配网完成,同步色温: brightness=%d, cct=%d\n",
fade_ctx.current_brightness, fade_ctx.current_cct);
// 设置目标值(与当前值一致,避免不必要的渐变)
fade_ctx.target_brightness = g_device_control.brightness_local;
fade_ctx.target_cct = g_device_control.cct_local;
calculate_pwm_duty(&g_device_control);
update_pwm_output(g_device_control.on, g_device_control.duty_cw, g_device_control.duty_ww);
e_printf("[set_light2net_cfg_done] 配网完成状态同步: current_cct=%d, target_cct=%d\n",
fade_ctx.current_cct, fade_ctx.target_cct);
}
// 启动呼吸灯
static void start_breath_timer(void)
{
g_net_breath_state = NET_CFG_BREATHING;
g_net_cfg_start_time = uapi_systick_get_ms();
uapi_timer_start(breath_ctx.timer_handle, breath_ctx.update_interval, breath_timer_callback, 0);
}
// 模式转换函数将bitmask转换为上报用的枚举值
lightMode_e convert_mode_for_report(lightMode_e current_mode)
{
// 如果处于离家模式,上报离家模式
if (LIGHT_MODE_IS_LEAVE(current_mode)) {
return LIGHT_MODE_LEAVE;
}
// 否则上报基础模式
return LIGHT_MODE_GET_BASE(current_mode);
}