udpate button and leds

This commit is contained in:
Steven Dan
2026-05-13 18:19:22 +08:00
parent 7875f6122d
commit db328853d1
2 changed files with 244 additions and 93 deletions

View File

@@ -83,7 +83,7 @@ on tile[0]: port p_sda = PORT_I2C_SDA;
on tile[0]: in port p_button = XS1_PORT_8D; //bit 4: mode button, bit 5: volume down button, bit 6: mic mute button, bit 7: volume up button
on tile[1]: out port p_mode_led_red = XS1_PORT_1F;
on tile[1]: out port p_leds = XS1_PORT_4D; //bit 0: mode led green, bit 1: mode led blue, bit 2: mic mute led red, bit 3: volume up led green
on tile[1]: out port p_leds = XS1_PORT_4D; //bit 0: mode led green, bit 1: mode led blue, bit 2: mic mute led red, bit 3: mic mute led green
on tile[1]: out port p_mic_mute_led_blue = XS1_PORT_1G;
timer tm;
@@ -111,13 +111,17 @@ timer tm;
// 改动原因C1的TIMER_PERIOD为20ms按DS1同类短按/长按模型换算阈值mode/mic 仍用 LONG 区分短按窗口;音量键已取消长按连调故不再使用 REPEAT 间隔宏。
#define C1_KEY_SHORT_TICKS 1
#define C1_KEY_LONG_TICKS 50
// 改动原因:规格要求 mic 键长按 1.5s 触发 AI 通话降噪开关20ms*75=1.5s,与 mode 键的 C1_KEY_LONG_TICKS(1s) 解耦,避免误触。
#define C1_KEY_MIC_AI_NR_TICKS 75
// 改动原因:双击第二下需在首击释放后约 500ms 内按下否则判为单击静音20ms*25=500ms。
#define C1_MIC_DOUBLE_DEFER_TICKS 25
// 改动原因新增C1模式持久化文件路径使用LittleFS保存mode按键状态保证断电重启后可恢复。
#define C1_MODE_VALUE_PATH "c1_mode_value"
// 改动原因:新增mode默认值与有效范围常量集中定义便于维护并确保Flash异常时能回退到安全默认值
#define C1_MODE_VALUE_DEFAULT 0
#define C1_MODE_VALUE_MIN 0
#define C1_MODE_VALUE_MAX 1
// 改动原因:c1_mode 与 tile1 mode 灯索引一致1=灭 2=蓝 3=绿 4=橙 5=紫;合法范围 1~5interface 原样传 c1_mode
#define C1_MODE_VALUE_DEFAULT 1
#define C1_MODE_VALUE_MIN 1
#define C1_MODE_VALUE_MAX 5
// 改动原因DAC 音量单独持久化路径,与 mode 的 c1_mode 文件分离,避免互相覆盖且便于维护。
#define C1_DAC_VOL_INFO_PATH "c1_dac_vol"
@@ -132,6 +136,8 @@ timer tm;
// 改动原因TIMER_PERIOD≈20ms连续 3 次采样一致再认定电平稳定,避免检测脚抖动误触发。
#define C1_JACK_DEBOUNCE_SAMPLES 3u
extern void device_reboot(void);
// 改动原因:判断 Flash 读出字节是否为合法 NAU88C22 DAC 音量码0x00 表示按键规则下的 mute0x4B~0xCF 为正常衰减范围。
static unsigned c1_saved_dac_vol_is_valid(unsigned char v)
{
@@ -142,11 +148,72 @@ static unsigned c1_saved_dac_vol_is_valid(unsigned char v)
return 0;
}
// 改动原因:mode值与面板LED颜色索引不是同一编码封装转换避免业务代码散落魔数映射关系
static inline unsigned c1_mode_to_led_color_idx(unsigned mode_value)
// 改动原因:灯索引与 c1_mode 同值;仅钳位非法 Flash 读数,合法值原样下发 tile1避免误映射
static inline unsigned c1_mode_to_tile_mode_led_code(unsigned mode_value)
{
// 改动原因需求定义0=蓝灯、1=绿灯现有LED接口约定2=蓝灯、1=绿灯,因此做显式转换。
return (mode_value == 0) ? 2 : 1;
if (mode_value > C1_MODE_VALUE_MAX || mode_value < C1_MODE_VALUE_MIN)
return C1_MODE_VALUE_DEFAULT;
return mode_value;
}
void switch_mode_by_c1_mode(unsigned c1_mode, unsigned force_reboot)
{
unsigned reboot_need = 0;
#if UAC1_MODE == 0
switch (c1_mode)
{
case 1:
SetRoleSwitchFlag(MODE_FPS_UAC2);
reboot_need = 0;
break;
case 2:
SetRoleSwitchFlag(MODE_BR_UAC2);
reboot_need = 1;
break;
case 3:
SetRoleSwitchFlag(MODE_V71_UAC2);
reboot_need = 1;
break;
case 4:
SetRoleSwitchFlag(MODE_FPS_UAC2);
reboot_need = 1;
break;
case 5:
SetRoleSwitchFlag(MODE_FPS_UAC2);
reboot_need = 0;
break;
}
#else
switch (c1_mode)
{
case 1:
SetRoleSwitchFlag(MODE_FPS_UAC1);
reboot_need = 0;
break;
case 2:
SetRoleSwitchFlag(MODE_V71_UAC1);
reboot_need = 1;
break;
case 3:
SetRoleSwitchFlag(MODE_V71_UAC1);
reboot_need = 0;
break;
case 4:
SetRoleSwitchFlag(MODE_FPS_UAC1);
reboot_need = 1;
break;
case 5:
SetRoleSwitchFlag(MODE_FPS_UAC1);
reboot_need = 0;
break;
}
#endif
if (reboot_need || force_reboot)
{
delay_milliseconds(20);
device_reboot();
while (1);
}
}
#define NAU88L21_PGA_GAIN_REG_MIN_USED_VALUE 0x0 // 0x1=1, 0dB (0x0, -1dB which is not used in this design)
@@ -436,6 +503,13 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
unsigned vol_down_press_ticks = 0;
unsigned mic_mute_press_ticks = 0;
unsigned vol_up_press_ticks = 0;
// 改动原因mic 键双击需延迟首轮短按的静音动作,用 stage1=等超时单击 / stage2=已收到第二下按下等释放;与长按 AI 互斥靠 mic_ai_long_fired。
unsigned mic_dbl_stage = 0;
unsigned mic_defer_left = 0;
unsigned mic_ai_long_fired = 0;
// 改动原因:变声/美声仅按键+灯tile0 保存状态供下发橙色 mode 区AI 降噪无灯仅本地翻转供后续扩展。
unsigned c1_mic_voice_fx = 0;
unsigned c1_mic_ai_nr = 0;
// 改动原因mic mute红灯通过interface下发到tile1仅在状态变化时发送减少无意义跨tile调用。
unsigned last_mute_switch_for_led = 0xFFFFFFFF;
unsigned host_os = 0;
@@ -453,14 +527,13 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
firmware_upgrade_init();
#endif
if (c1_mode > 4)
if (c1_mode > C1_MODE_VALUE_MAX || c1_mode < C1_MODE_VALUE_MIN)
{
c1_mode = 0;
c1_mode = C1_MODE_VALUE_DEFAULT;
save_value(C1_MODE_INFO_PATH, c1_mode);
}
#if defined(WIN_OS_DETECTION)
for(int i = 0; i < 500; i++)
{
@@ -474,35 +547,11 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
if (host_os == OS_WIN) {
debug_printf("Detected Windows OS (OS_WIN) c1_mode: %d\n", c1_mode);
unsigned flag = 1;
switch (c1_mode)
{
case 1:
flag = MODE_FPS_UAC2;
break;
case 3:
flag = MODE_BR_UAC2;
break;
case 4:
flag = MODE_V71_UAC2;
break;
}
SetRoleSwitchFlag(flag);
#ifndef DISABLE_REBOOT
device_reboot();
while (1);
#endif
switch_mode_by_c1_mode(c1_mode, 1);
}
else
{
if (c1_mode == 5)
{
SetRoleSwitchFlag(MODE_V71_UAC1);
#ifndef DISABLE_REBOOT
device_reboot();
while (1);
#endif
}
switch_mode_by_c1_mode(c1_mode, 0);
}
#endif
@@ -522,8 +571,14 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
dac_vol_persist_deadline = 0;
}
// 改动原因:开机后立即按Flash恢复的mode值设置面板灯色避免首次按键前灯态与保存状态不一致
i_c1_led_ctrl.set_mode_led_color(c1_mode_to_led_color_idx(c1_mode));
// 改动原因:tile1 上电已硬件全灭;先下发当前 g_mute_switch 再下发 Flash 中的 c1_mode使首帧 apply 时 mic 已正确,避免仅 set_mode 时用占位 mic 闪错 mute 灯
unsigned init_mute_for_led;
GET_SHARED_GLOBAL(init_mute_for_led, g_mute_switch);
i_c1_led_ctrl.set_mic_mute_state(init_mute_for_led);
i_c1_led_ctrl.set_mode_led_color(c1_mode_to_tile_mode_led_code(c1_mode));
// 改动原因:上电默认关闭变声灯效覆盖,避免未初始化随机亮橙。
i_c1_led_ctrl.set_mic_voice_fx(0);
last_mute_switch_for_led = init_mute_for_led;
while(1)
{
@@ -763,8 +818,46 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
vol_up_pressed = ((button_state & C1_KEY_VOL_UP_MASK) == 0);
pressed_count = mode_pressed + vol_down_pressed + mic_mute_pressed + vol_up_pressed;
// 改动原因mic 首击短按后延迟 500ms 再静音,以便识别双击;仅 mic 松开时递减 mic_defer_left到 0 执行单击静音。
if (mic_dbl_stage == 1 && mic_defer_left > 0)
{
unsigned mic_released_now = ((button_state & C1_KEY_MIC_MUTE_MASK) != 0);
if (mic_released_now)
{
mic_defer_left--;
if (mic_defer_left == 0)
{
unsigned current_mute_switch;
unsigned effective_adc_vol;
GET_SHARED_GLOBAL(effective_adc_vol, g_adc_vol);
GET_SHARED_GLOBAL(current_mute_switch, g_mute_switch);
current_mute_switch = (current_mute_switch == 0) ? 1 : 0;
if (current_mute_switch == 0)
{
mic_volume(0, i2c);
}
else
{
mic_volume(effective_adc_vol, i2c);
}
SET_SHARED_GLOBAL(g_mute_switch, current_mute_switch);
i_c1_led_ctrl.set_mic_mute_state(current_mute_switch);
last_mute_switch_for_led = current_mute_switch;
debug_printf("C1 key mic mute toggle (deferred single): %d\n", current_mute_switch);
mic_dbl_stage = 0;
}
}
}
if (pressed_count == 1)
{
// 改动原因mic 双击等待期间若用户去按 mode/音量键,应取消延迟单击静音,避免误切 g_mute_switch。
if (!mic_mute_pressed)
{
mic_dbl_stage = 0;
mic_defer_left = 0;
}
if (mode_pressed)
{
mode_press_ticks++;
@@ -783,6 +876,12 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
}
else if (mic_mute_pressed)
{
// 改动原因:双击窗口内第二下按下,取消延迟单击静音,改为等待释放后判双击变声。
if (mic_dbl_stage == 1)
{
mic_defer_left = 0;
mic_dbl_stage = 2;
}
mic_mute_press_ticks++;
// 改动原因同一时刻只允许mic mute键生效清除其它键状态避免释放时误执行音量或mode短按。
mode_press_ticks = 0;
@@ -802,18 +901,18 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
{
if (pressed_count == 0)
{
// 改动原因mode短按改为0/1切换并持久化到Flash满足0=蓝灯、1=绿灯以及重启恢复需求
// 改动原因mode 短按在 c1_mode 1~5 间循环,灯索引与 mode 同值;持久化后切角色并 set_mode_led_color(c1_mode) 下发 tile1无重启路径必须调用否则灯不更新
if ((mode_press_ticks >= C1_KEY_SHORT_TICKS) && (mode_press_ticks < C1_KEY_LONG_TICKS))
{
unsigned flag;
c1_mode = (c1_mode == 0) ? 1 : 0;
c1_mode ++;
if (c1_mode > C1_MODE_VALUE_MAX)
{
c1_mode = C1_MODE_VALUE_MIN;
}
save_value(C1_MODE_INFO_PATH, c1_mode);
flag = c1_mode == 1 ? USB_IN_FLAG : COAX_IN_FLAG;
SetRoleSwitchFlag(flag);
delay_milliseconds(1);
device_reboot();
while(1);
switch_mode_by_c1_mode(c1_mode, 0);
i_c1_led_ctrl.set_mode_led_color(c1_mode_to_tile_mode_led_code(c1_mode));
}
// 改动原因:音量键仅短按——释放时若已满足去抖时长则步进一档,与按住时长无关(已取消长按连调)。
@@ -840,28 +939,34 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
}
}
// 改动原因mic mute短按直接切换g_mute_switch复用现有HID变化上报和effective_adc_vol静音路径
if ((mic_mute_press_ticks >= C1_KEY_SHORT_TICKS) && (mic_mute_press_ticks < C1_KEY_LONG_TICKS))
// 改动原因mic短按静音改为延迟单击防双击双击短按切变声灯橙R+G按住满1.5s仅切 AI 状态变量无灯;长按与短/双互斥
{
unsigned current_mute_switch;
unsigned effective_adc_vol;
GET_SHARED_GLOBAL(effective_adc_vol, g_adc_vol);
GET_SHARED_GLOBAL(current_mute_switch, g_mute_switch);
current_mute_switch = (current_mute_switch == 0) ? 1 : 0;
if (current_mute_switch == 0)
unsigned mic_snap = mic_mute_press_ticks;
if (mic_snap > 0)
{
mic_volume(0, i2c);
if ((mic_snap >= C1_KEY_MIC_AI_NR_TICKS) || mic_ai_long_fired)
{
if (mic_dbl_stage == 2)
mic_dbl_stage = 0;
}
else if ((mic_snap >= C1_KEY_SHORT_TICKS) && (mic_snap < C1_KEY_MIC_AI_NR_TICKS))
{
if (mic_dbl_stage == 2)
{
c1_mic_voice_fx = (c1_mic_voice_fx ? 0 : 1);
i_c1_led_ctrl.set_mic_voice_fx(c1_mic_voice_fx);
mic_dbl_stage = 0;
mic_defer_left = 0;
debug_printf("C1 mic voice/beautifier toggle: %d\n", c1_mic_voice_fx);
}
else
{
mic_dbl_stage = 1;
mic_defer_left = C1_MIC_DOUBLE_DEFER_TICKS;
}
}
mic_ai_long_fired = 0;
}
else
{
mic_volume(effective_adc_vol, i2c);
}
SET_SHARED_GLOBAL(g_mute_switch, current_mute_switch);
// 改动原因按键切换mic mute后立即通知tile1刷新红灯避免等待下一轮状态同步出现可见延迟。
i_c1_led_ctrl.set_mic_mute_state(current_mute_switch);
last_mute_switch_for_led = current_mute_switch;
debug_printf("C1 key mic mute toggle: %d\n", current_mute_switch);
}
// 改动原因:音量键仅短按——释放时若已满足去抖时长则步进一档(已取消长按连调)。
@@ -888,6 +993,12 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli
}
}
}
else if (pressed_count > 1)
{
// 改动原因:多键同按时取消 mic 双击等待,避免组合键松手后误触发延迟静音。
mic_dbl_stage = 0;
mic_defer_left = 0;
}
// 改动原因:多键同时按下不映射功能,立即清除本轮计时,避免组合键抖动误触发单键动作。
mode_press_ticks = 0;
@@ -1779,29 +1890,60 @@ void send_eq_data(uint8_t data[])
}
}
// 改动原因:封装tile1前面板LED刷新统一执行mode灯R/G/B轮转与mic mute红灯显示策略避免多处直接写端口造成覆盖
static inline void apply_c1_panel_leds(unsigned mode_led_color_idx, unsigned mic_mute_switch, unsigned &led_shadow)
// 改动原因tile1 上电瞬间尚未收到 tile0 的 mode/mic先把 mode 红脚与 PORT_4D 各 bit 拉到熄灭电平(低有效灯:输出 1 为灭),与 apply 中无混色时 mode 红通道熄灭写法一致
static inline void c1_panel_leds_force_all_off_hw(void)
{
p_mode_led_red <: 1;
p_leds <: 0xF;
p_mic_mute_led_blue <: 1;
}
// 改动原因c1_mode 与灯索引一致1=灭 2=蓝 3=绿 4=橙(R+G) 5=紫(R+B);仅驱动 mode 区p_mode_led_red + p_leds bit0/1不受 mic 变声影响。
// mic_voice_fx_on规格变声/美声橙灯在 mic 区——p_leds bit2 红 + bit3 绿同时点亮;关变声时 mic 区仅由静音决定 bit2bit3 熄灭。
static inline void apply_c1_panel_leds(unsigned mode_led_color_idx, unsigned mic_mute_switch, unsigned mic_voice_fx_on, unsigned &led_shadow)
{
unsigned mode_red = 0;
unsigned mode_green = 0;
unsigned mode_blue = 0;
unsigned mic_red = (mic_mute_switch == 1) ? 1 : 0;
if (mode_led_color_idx == 0) {
mode_red = 1;
} else if (mode_led_color_idx == 1) {
mode_green = 1;
} else if (mode_led_color_idx == 2) {
mode_blue = 1;
// 改动原因:与 user_main.h 约定一致——mic_mute_switch 存的是“非静音=1、静音=0”静音时 mic 红灯bit2亮。
unsigned mic_red = 0;
unsigned mic_green = 0;
if (mic_voice_fx_on) {
// 改动原因:变声开为橙=R+G映射到 XS1_PORT_4D 的 mic 专用 bit2/bit3不占用 mode 的 bit0/bit1。
mic_red = 1;
mic_green = 1;
} else {
mic_red = (mic_mute_switch == 0) ? 1 : 0;
mic_green = 0;
}
// 改动原因硬件LED为低电平点亮1灭/0亮因此把逻辑亮灭状态反相后再写端口。
if (mode_led_color_idx == 1) {
mode_red = 0;
mode_green = 0;
mode_blue = 0;
} else if (mode_led_color_idx == 2) {
mode_blue = 1;
} else if (mode_led_color_idx == 3) {
mode_green = 1;
} else if (mode_led_color_idx == 4) {
mode_red = 1;
mode_green = 1;
} else if (mode_led_color_idx == 5) {
mode_red = 1;
mode_blue = 1;
} else {
mode_red = 0;
mode_green = 0;
mode_blue = 0;
}
// 改动原因硬件LED为低电平点亮1灭/0亮低四位 bit0~3 分别为 mode绿、mode蓝、mic红、mic绿统一刷新避免旧代码固定 |=0x8 导致 bit3 永灭。
p_mode_led_red <: (mode_red ? 0 : 1);
led_shadow &= ~0x7;
led_shadow &= ~0xF;
led_shadow |= (mode_green ? 0x0 : 0x1);
led_shadow |= (mode_blue ? 0x0 : 0x2);
led_shadow |= (mic_red ? 0x0 : 0x4);
led_shadow |= 0x8;
led_shadow |= (mic_green ? 0x0 : 0x8);
p_leds <: led_shadow;
// 改动原因该蓝灯当前不参与功能保持熄灭低电平有效下输出1表示灭
p_mic_mute_led_blue <: 1;
@@ -1809,16 +1951,14 @@ static inline void apply_c1_panel_leds(unsigned mode_led_color_idx, unsigned mic
void app_control_slave(server interface c1_led_ctrl_if i_c1_led_ctrl, chanend c_eq_data)
{
// 改动原因:LED端口在tile1统一用shadow变量管理输出位避免不同逻辑直接写端口互相覆盖
// 改动原因低电平点亮初始化全灭应写1bit0/1/2默认置1表示G/B/红灯都灭。
unsigned led_shadow = 0x7;
unsigned mode_led_color_idx = 0xFFFFFFFF; // 0=R,1=G,2=B, 0xFFFFFFFF=未点亮
unsigned mic_mute_switch = 1; // 0=静音(亮红)1=非静音(灭红)
p_mode_led_red <: 0;
p_mic_mute_led_blue <: 1;
p_leds <: led_shadow;
// 改动原因:tile1 先于 tile0 运行,上电先把硬件灯全灭且不调用 apply避免用占位 mic/mode 组合误亮shadow 置为全灭电平便于首包 set_* 时 apply 与端口一致
unsigned led_shadow = 0xF;
unsigned mode_led_color_idx = 1;
unsigned mic_mute_switch = 1;
// 改动原因:变声/美声开时仅 p_leds bit2/bit3 显橙,与 set_mode_led_color 下发的 mode 灯独立;此处状态与 tile0 set_mic_voice_fx 同步。
unsigned mic_voice_fx_on = 0;
c1_panel_leds_force_all_off_hw();
apply_c1_panel_leds(mode_led_color_idx, mic_mute_switch, led_shadow);
unsigned eq_mode_time = 0;
timer eq_mode_timer;
eq_mode_timer :> eq_mode_time;
@@ -1835,12 +1975,19 @@ void app_control_slave(server interface c1_led_ctrl_if i_c1_led_ctrl, chanend c_
unsafe {
select{
case i_c1_led_ctrl.set_mode_led_color(unsigned color_idx):
mode_led_color_idx = color_idx % 3;
apply_c1_panel_leds(mode_led_color_idx, mic_mute_switch, led_shadow);
// 改动原因:color_idx c1_mode 同值 1~5越界时钳位为默认灯码避免未定义索引。
mode_led_color_idx = color_idx;
if (mode_led_color_idx > 5 || mode_led_color_idx < 1)
mode_led_color_idx = C1_MODE_VALUE_DEFAULT;
apply_c1_panel_leds(mode_led_color_idx, mic_mute_switch, mic_voice_fx_on, led_shadow);
break;
case i_c1_led_ctrl.set_mic_mute_state(unsigned mute_state):
mic_mute_switch = (mute_state != 0) ? 1 : 0;
apply_c1_panel_leds(mode_led_color_idx, mic_mute_switch, led_shadow);
apply_c1_panel_leds(mode_led_color_idx, mic_mute_switch, mic_voice_fx_on, led_shadow);
break;
case i_c1_led_ctrl.set_mic_voice_fx(unsigned voice_fx_enabled):
mic_voice_fx_on = (voice_fx_enabled != 0) ? 1 : 0;
apply_c1_panel_leds(mode_led_color_idx, mic_mute_switch, mic_voice_fx_on, led_shadow);
break;
}
}

View File

@@ -8,8 +8,12 @@
#include "app_dsp.h"
// 改动原因mode/mic mute LED物理端口在tile1定义跨tile的LED控制interface供tile0按键逻辑调用tile1统一驱动硬件LED。
interface c1_led_ctrl_if {
void set_mode_led_color(unsigned color_idx); // 0=R, 1=G, 2=B
// 改动原因:与 c1_mode 同值下发 tile11=灭 2=蓝 3=绿 4=橙 5=紫(灯索引即 mode
void set_mode_led_color(unsigned color_idx);
void set_mic_mute_state(unsigned mute_switch); // mute_switch: 0=静音(亮红), 1=非静音(灭红)
// 改动原因:麦克风键双击“变声/美声”仅改灯效时,在 tile1 点亮 p_leds 的 mic 区非0=bit2 红 + bit3 绿0=仅按静音规则驱动 bit2、bit3 熄灭。
// 改动原因:参数名不能用 xC 保留字 onon tile[:]: 语法),否则编译器在解析 interface 时报 parse error before "on"。
void set_mic_voice_fx(unsigned voice_fx_enabled);
};
void switch_handler(void);
void flag_handler();