1. 解决PWM 更新导致周期混乱继而亮度抖动的问题

2. 增加无论何种状态都能强制复位的机制
3. 添加设备注册检查机制,如果设备已经处于配网但是还是注册的状态,强制进行一次复位
4. 添加串口控制协议
This commit is contained in:
2025-10-26 17:18:59 +08:00
parent a16c05db7b
commit 21c6d05bad
18 changed files with 2378 additions and 81 deletions

View File

@@ -64,6 +64,8 @@ bool g_reset_factory_flag = false;
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);
@@ -112,6 +114,7 @@ static struct fade_ctx_t {
uint16_t fade_time; //s
uint32_t smooth_time_us; // 渐变总时长(us)
uint32_t update_interval; // 更新间隔(us)
volatile bool timer_active; // 渐变定时器是否允许自重启(竞态保护)
// 打印限制器
print_limiter_t print_limiter;
};
@@ -208,7 +211,7 @@ static void *fade_task(const char *arg)
fade_ctx.current_cct, fade_ctx.target_cct, fade_ctx.step_cct, fade_ctx.is_closing_fade);
}
// 检查是否达到目标值(恢复原始逻辑)
// 检查是否达到目标值
bool brightness_reached = (fade_ctx.step_brightness > 0) ?
(fade_ctx.current_brightness >= fade_ctx.target_brightness) :
(fade_ctx.current_brightness <= fade_ctx.target_brightness);
@@ -216,14 +219,14 @@ static void *fade_task(const char *arg)
bool cct_reached = (fade_ctx.step_cct > 0) ?
(fade_ctx.current_cct >= fade_ctx.target_cct) :
(fade_ctx.current_cct <= fade_ctx.target_cct);
// 更新当前值(恢复原始逻辑)
// 更新当前值
if (!brightness_reached) {
fade_ctx.current_brightness += fade_ctx.step_brightness;
}
if (!cct_reached) {
fade_ctx.current_cct += fade_ctx.step_cct;
}
// 如果达到目标,停止渐变(恢复原始逻辑)
// 达到目标,停止渐变
if (brightness_reached && cct_reached) {
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);
@@ -231,6 +234,7 @@ static void *fade_task(const char *arg)
fade_ctx.fade_completed = true;
fade_ctx.current_brightness = fade_ctx.target_brightness;
fade_ctx.current_cct = fade_ctx.target_cct;
fade_ctx.timer_active = false; // 防止回调自重启
uapi_timer_stop(fade_ctx.timer_handle);
}
@@ -335,6 +339,10 @@ static void *breath_task(const char *arg)
// 渐变定时器回调函数
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);
}
@@ -364,6 +372,27 @@ static void init_fade_ctx(void)
// 初始化打印限制器
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",
@@ -380,8 +409,7 @@ static void init_fade_ctx(void)
// 设置默认更新间隔
fade_ctx.update_interval = FADE_INTERVAL_MIN;
// fade_ctx.current_brightness = g_device_control.brightness_local;
// fade_ctx.current_cct = g_device_control.cct_local;
fade_ctx.timer_active = false; // 默认禁用,只有开始渐变时启用
}
// 初始化呼吸灯控制
@@ -491,8 +519,12 @@ void calculate_pwm_duty(device_control_t* pdevice_control)
// 更新PWM输出
void update_pwm_output(bool on_state, uint16_t duty_cw_val, uint16_t duty_ww_val)
{
pwm_config_t cfg_repeat = {0};
cfg_repeat.repeat = true;
// 说明:为了避免每步“重相位 + 动态拼接”导致的低亮抖动,
// 这里改为固定相位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;
@@ -512,29 +544,53 @@ void update_pwm_output(bool on_state, uint16_t duty_cw_val, uint16_t duty_ww_val
if (!on_state) {
high_cnt_cw = 0;
low_cnt_cw = pwm_period_cnt; // Ensure low_cnt is full period if off
low_cnt_cw = pwm_period_cnt; // 关灯时保持低电平整周期
high_cnt_ww = 0;
low_cnt_ww = pwm_period_cnt; // Ensure low_cnt is full period if off
low_cnt_ww = pwm_period_cnt;
}
// uapi_pwm_stop_group(CONFIG_PWM_GROUP_ID);
cfg_repeat.high_time = high_cnt_cw;
cfg_repeat.low_time = low_cnt_cw;
uapi_pwm_open(channel_id_cw, &cfg_repeat);
// uapi_pwm_update_duty_ratio(channel_id_cw, cfg_repeat.low_time, cfg_repeat.high_time);
#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_repeat.high_time = high_cnt_ww;
cfg_repeat.low_time = low_cnt_ww;
cfg_repeat.offset_time = high_cnt_cw; // WW PWM starts after CW PWM high time
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);
uapi_pwm_open(channel_id_ww, &cfg_repeat);
// uapi_pwm_update_duty_ratio(channel_id_ww, cfg_repeat.low_time, cfg_repeat.high_time);
uapi_pwm_start_group(CONFIG_PWM_GROUP_ID);
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:high:%u, low:%u, duty:%u/%u, ww:high:%u, low:%u, duty:%u/%u, offset_time:%u\r\n",
on_state, high_cnt_cw, low_cnt_cw, current_duty_cw, PWM_DUTY_RATIO_MAX, high_cnt_ww, low_cnt_ww, current_duty_ww, PWM_DUTY_RATIO_MAX, cfg_repeat.offset_time);
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);
}
}
@@ -608,6 +664,8 @@ lab_exit:
static void cancel_current_light_fade(void)
{
// 竞态保护:先拉低标志再停计时器,避免回调自重启
fade_ctx.timer_active = false;
uapi_timer_stop(fade_ctx.timer_handle);
// 如果我打断了渐变,则将当前值更新到目标值不然下次计算会异常
// g_device_control.cct_local = fade_ctx.current_cct;
@@ -887,10 +945,17 @@ int set_light(light_ctrl_source_e source,
// 如果打开灯,则需要手动将前置状态设置为关闭的样子,这样子后面计算渐变才能正常计算
if (APP_OPEN_LIGHT == source || DEV_POWER_ON == source) {
g_device_control.on = true;
// fade_ctx.current_brightness = 0;
if (DEV_POWER_ON == source) {
// 🔧 开机渐变明确设置从0开始
fade_ctx.current_brightness = 0; // 从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;
// 色温不进行变化,只改变亮度
fade_ctx.current_cct = g_device_control.cct_local;
cct_local_target = g_device_control.cct_local;
if (g_device_control.colourMode != COLOUR_MODE_DUAL) {
@@ -930,6 +995,7 @@ int set_light(light_ctrl_source_e source,
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();
@@ -953,6 +1019,7 @@ int set_light(light_ctrl_source_e source,
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();
@@ -972,6 +1039,7 @@ int set_light(light_ctrl_source_e source,
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();
@@ -1025,27 +1093,34 @@ static void pwm_init(pin_t pin, pin_t pin1)
uapi_pin_set_mode(pin, PIN_MODE_1);
uapi_pin_set_mode(pin1, PIN_MODE_1);
uapi_pwm_init();
uint32_t frequency = uapi_pwm_get_frequency(pin%8);
// uint32_t pwm_base_period_ns = 1000 * 1000 * 1000 / frequency;
// uint32_t pwm_base_period_ns = 1000 * 1000 * 1000 / (80 * 1000 * 1000);// 80MHZ
// uint32_t pwm_target_period_ns = 1000 * 1000 * 1000 / PWM_FREQUENCY;
pwm_period_cnt = ((80 * 1000 * 1000) / PWM_FREQUENCY);
// 基准时钟固定 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;
// 设置PWM组
channel_id_cw = pin%8;
channel_id_ww = pin1%8;
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};
// uapi_pwm_open(channel_id_cw, &cfg_repeat);
// uapi_pwm_open(channel_id_ww, &cfg_repeat);
uapi_pwm_set_group(CONFIG_PWM_GROUP_ID, channel_ids, sizeof(channel_ids));
// uapi_pwm_start_group(CONFIG_PWM_GROUP_ID);
e_printf("PWM基础时钟频率: %dHz, 目标时钟频率: %dHz, 周期计数: %d\r\n", frequency, PWM_FREQUENCY, pwm_period_cnt);
(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_device:%d\r\n", g_device_control.power_on_cnt, !g_device_control.is_net_configured);
g_device_control.power_on_cnt = 0;//
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: 这里简单恢复之前等待配网的灯效即可
@@ -1075,23 +1150,16 @@ static int handle_network_status(void)
// 检查是否需要进入配网状态或恢复出厂设置
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;
if (g_device_control.is_net_configured) {
// 已出厂设备上下电6次后恢复出厂设置并重启
e_printf("已到达出厂设备上电次,执行恢复出厂设置\r\n");
g_reset_factory_flag = true;
extern int HILINK_RestoreFactorySettings(void);
while (!hf_hilink_main_is_runing()) {
msleep(10);
}
HILINK_RestoreFactorySettings();
return start_net_cfg;
} else {
// 未出厂设备:进入配网模式
e_printf("未出厂设备上电6次进入配网模式\r\n");
start_net_config();
start_net_cfg = 1;
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();
@@ -1274,18 +1342,17 @@ void handle_device_offline(void)
// 处理设备解绑
void handle_device_unbind(void)
{
e_printf("设备被解绑,重置配网状态\r\n");
e_printf("设备被解绑,重置配网状态:%d\r\n", g_device_control.is_bound);
if (g_device_control.is_bound) {
g_device_control.is_bound = false;
g_device_control.power_on_cnt = 0; // 重置上电计数
// 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();
g_device_control = tmp;
}
save_device_data();
}
uint64_t startup_time = 0;
@@ -1312,10 +1379,27 @@ 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);
}
@@ -1402,6 +1486,10 @@ int spotlight_main(void) {
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);
@@ -1476,12 +1564,23 @@ static void set_light2net_cfg_done(void)
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;
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);
}
// 启动呼吸灯
@@ -1502,4 +1601,3 @@ lightMode_e convert_mode_for_report(lightMode_e current_mode)
// 否则上报基础模式
return LIGHT_MODE_GET_BASE(current_mode);
}