diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn index 832b143..88bd8e0 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/core/xu316_qf60.xn @@ -45,8 +45,8 @@ - - + diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc index f828e54..76b3533 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/audiohw.xc @@ -14,6 +14,7 @@ #include "debug_print.h" #include "user_uart.h" #include "htr3236.h" +#include "tx1_led_effects.h" #include "eq_flash_storage.h" #include "lfs_io.h" #include "roleswitchflag.h" @@ -29,6 +30,8 @@ extern "C" { } #define TIMER_PERIOD 2000000 +/* 改动原因:与 jok TIMER_2_PERIOD 一致,50ms 驱动 RGB 灯效/工厂复位 LED */ +#define TIMER_2_PERIOD 5000000 #define DISABLE_REBOOT 1 @@ -94,6 +97,8 @@ unsigned g_hid_expand_gain_request = (unsigned)-1; unsigned g_hid_lmt_threshold_request = (unsigned)-1; unsigned g_hid_angle_values[C1_EX3D_ANGLE_CHANNELS] = {0}; unsigned g_request_factory_reset = 0; +/* 改动原因:HTR3236 bringup 须在 codec_init 完成之后执行,避免与 NAU88 I2C 抢线/阻塞 */ +unsigned g_audiohw_codec_init_done = 0; // CODEC I2C lines on tile[0]: port p_scl = PORT_I2C_SCL; @@ -110,13 +115,17 @@ on tile[0]: out port p_led_tile0 = PORT_LED_D10_8_11_9; // 8D bit7-4 = D10/D8/D // TX1 HTR3236 RGB LED driver control - declared in tx1_led_helper.xc -// TX1 Button bit masks +// TX1 原理图位掩码(与 jok buttons.h BIT_* 一致,接在 4F/4E 端口上) #define TX1_BIT_FPS_MODE (1<<3) // 4F bit3 #define TX1_BIT_GAME_MODE (1<<2) // 4F bit2 #define TX1_BIT_MIC_MUTE (1<<1) // 4F bit1 #define TX1_BIT_VOL_PLUS (1<<3) // 4E bit3 #define TX1_BIT_VOL_MINUS (1<<2) // 4E bit2 +/* 改动原因:与 jok PCB 相同,面板从左到右为 FPS / VOL+ / VOL- / GAME / MIC, + * 原理图端口位序不同。采用 lib_board_support buttons.xc #else 映射(非 #if 0 原理图直读)。 */ +#define TX1_BUTTON_MAP_PANEL_LAYOUT (0) + // TX1 Button timing thresholds (in 50ms ticks) #define TX1_SHORT_PRESS_TICKS 1 // 50ms minimum press #define TX1_LONG_PRESS_TICKS 20 // 1s = 20 * 50ms @@ -376,75 +385,101 @@ void mic_volume(unsigned level, client interface i2c_master_if i2c) } } -/* 改动原因:按 phaten_golden_6ch/audiohw.xc 流程初始化——先 SDB 使能并延时,再 wake/freq/通道配置, - * 带 I2C 重试。xC 禁止 client interface 全局变量,SDB 在本函数内直接用 led_if 控制(不经 htr3236_hw_enable)。 */ +/* 改动原因:jok 默认 10 过暗肉眼难辨;自检/灯效用 80,音量条仍可用较低亮度 */ +#define TX1_RGB_GLOBAL_BRIGHTNESS 40 +#define TX1_RGB_SELFTEST_BRIGHTNESS 200 +#define TX1_RGB_SCALE8(c) ((uint8_t)(((unsigned)(c) * TX1_RGB_GLOBAL_BRIGHTNESS) / 255)) + +/* 改动原因:bringup 失败时灯效任务不刷屏 I2C */ +static unsigned g_htr3236_ready = 0; + +// RGB LED to HTR3236 OUT channel mapping (D1-D12),须在 bringup 自检前定义 +static const uint8_t rgb_led_map[13][3] = { + {0, 0, 0}, + {24, 23, 22}, {21, 20, 19}, {18, 17, 16}, {15, 14, 13}, + {12, 11, 10}, {9, 8, 7}, {6, 5, 4}, {3, 2, 1}, + {36, 35, 34}, {33, 32, 31}, {30, 29, 28}, {27, 26, 25} +}; + +/* 改动原因:与 jok tile1_io_control_task 相同,仅 tile1 1K 拉高 SDB,勿动 tile0 8C(非 SDB) */ +static void tx1_htr3236_sdb_enable(client interface tx1_led_if led_if) +{ + led_if.set_htr3236_sdb(1); + delay_milliseconds(10); +} + +/* 改动原因:按 jok rgb_led 流程 D1-D12 白灯自检,最后统一 UPDATE */ +static void tx1_htr3236_self_test_rgb(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t led; + for (led = 1; led <= 12; led++) { + htr3236_set_pwm(dev, i2c, rgb_led_map[led][0], TX1_RGB_SELFTEST_BRIGHTNESS); + htr3236_set_pwm(dev, i2c, rgb_led_map[led][1], TX1_RGB_SELFTEST_BRIGHTNESS); + htr3236_set_pwm(dev, i2c, rgb_led_map[led][2], TX1_RGB_SELFTEST_BRIGHTNESS); + } + htr3236_update(dev, i2c); +} + +/* 改动原因:按 jok + golden 流程;避开 AudioHwInit 与 codec 抢 I2C;读回 0x00 验证唤醒 */ static void tx1_htr3236_bringup(htr3236_t *dev, client interface i2c_master_if i2c, client interface tx1_led_if led_if) { int ch; unsigned retry; - uint8_t zeros[36]; + int wake_ok = 0; + int freq_ok = 0; - led_if.init(); - led_if.set_htr3236_sdb(1); - delay_milliseconds(2); + g_htr3236_ready = 0; + + /* 改动原因:上电已在 Remote2 入口拉过 SDB,此处仅再脉冲一次确保硬件退出关断 */ + tx1_htr3236_sdb_enable(led_if); htr3236_init(dev, HTR3236_ADDR_GND); + debug_printf("HTR3236 addr 0x%02x (jok same HW)\n", dev->i2c_addr); - retry = 0; - while (htr3236_software_wake(dev, i2c) != I2C_REGOP_SUCCESS && retry < 10) { - retry++; - delay_milliseconds(1); - debug_printf("HTR3236 wake retry %u\n", retry); + for (retry = 0; retry < 10; retry++) { + if (htr3236_software_wake(dev, i2c) == I2C_REGOP_SUCCESS) { + wake_ok = 1; + break; + } + delay_milliseconds(2); + htr3236_software_reset(dev, i2c); + delay_milliseconds(2); + debug_printf("HTR3236 wake retry %u\n", retry + 1); } - if (retry >= 10) { - debug_printf("HTR3236 LED driver wake failed\n"); - } else { - debug_printf("HTR3236 LED driver wake ok\n"); + if (!wake_ok) { + debug_printf("HTR3236 wake FAILED (no I2C ACK, check SDB+addr)\n"); + return; } - retry = 0; - while (htr3236_set_frequency(dev, i2c, HTR3236_FREQ_22KHZ) != I2C_REGOP_SUCCESS && retry < 10) { - retry++; - delay_milliseconds(1); - debug_printf("HTR3236 freq retry %u\n", retry); - } - if (retry >= 10) { - debug_printf("HTR3236 set frequency failed\n"); - } else { - debug_printf("HTR3236 set frequency ok\n"); - } + debug_printf("HTR3236 wake ok\n"); - for (ch = 1; ch <= 35; ch++) { + for (retry = 0; retry < 10; retry++) { + if (htr3236_set_frequency(dev, i2c, HTR3236_FREQ_22KHZ) == I2C_REGOP_SUCCESS) { + freq_ok = 1; + break; + } + delay_milliseconds(2); + debug_printf("HTR3236 freq retry %u\n", retry + 1); + } + if (!freq_ok) { + debug_printf("HTR3236 set frequency FAILED\n"); + return; + } + debug_printf("HTR3236 set frequency ok\n"); + + /* 改动原因:与 jok xk_audio_316_mc_ab_board 一致,逐通道配置 LED 电流/使能 */ + for (ch = 1; ch <= 36; ch++) { htr3236_set_led_config(dev, i2c, ch, HTR3236_CURRENT_HALF, 1); } - htr3236_global_enable(dev, i2c, 1); - for (ch = 0; ch < 36; ch++) { - zeros[ch] = 0; - } - htr3236_set_pwm_bulk(dev, i2c, 1, zeros, 36); - htr3236_update(dev, i2c); + tx1_htr3236_self_test_rgb(dev, i2c); + debug_printf("HTR3236 self-test: D1-D12 white PWM=%u, hold 5s\n", TX1_RGB_SELFTEST_BRIGHTNESS); + delay_milliseconds(5000); + + g_htr3236_ready = 1; } -// RGB LED to HTR3236 OUT channel mapping (D1-D12) -// Each LED: {out_b, out_g, out_r} -static const uint8_t rgb_led_map[13][3] = { - {0, 0, 0}, // [0] invalid - {24, 23, 22}, // D1 - {21, 20, 19}, // D2 - {18, 17, 16}, // D3 - {15, 14, 13}, // D4 - {12, 11, 10}, // D5 - {9, 8, 7}, // D6 - {6, 5, 4}, // D7 - {3, 2, 1}, // D8 - {36, 35, 34}, // D9 - {33, 32, 31}, // D10 - {30, 29, 28}, // D11 - {27, 26, 25} // D12 -}; - static void tx1_rgb_led_set(htr3236_t *dev, client interface i2c_master_if i2c, uint8_t led, uint8_t r, uint8_t g, uint8_t b) { @@ -465,6 +500,73 @@ static void tx1_rgb_led_all_off(htr3236_t *dev, client interface i2c_master_if i htr3236_update(dev, i2c); } +/* 改动原因:单颗 RGB 只写 PWM 寄存器,最后统一 htr3236_update(对照 ui_app 303-336 的 bulk 刷新) */ +static void tx1_rgb_pwm_write_led(htr3236_t *dev, client interface i2c_master_if i2c, + uint8_t led, uint8_t r, uint8_t g, uint8_t b) +{ + if (led < 1 || led > 12) return; + htr3236_set_pwm(dev, i2c, rgb_led_map[led][0], b); + htr3236_set_pwm(dev, i2c, rgb_led_map[led][1], g); + htr3236_set_pwm(dev, i2c, rgb_led_map[led][2], r); +} + +/* 改动原因:按 feature_mode + 音量格数刷新 HTR3236 D1-D12 音量条(jok ui_app led_ui 303-336) */ +static void tx1_rgb_volume_bar_refresh(htr3236_t *dev, client interface i2c_master_if i2c, + tx1_feature_mode_t mode, unsigned feature_vol_0_12) +{ + uint8_t zeros[36]; + int i; + uint8_t led; + unsigned bar_level = 0; + uint8_t r = 0, g = 0, b = 0; + + for (i = 0; i < 36; i++) zeros[i] = 0; + htr3236_set_pwm_bulk(dev, i2c, 1, zeros, 36); + + switch (mode) { + case FEATURE_MODE_SYSTEM_VOLUME: { + unsigned vol_pct; + GET_SHARED_GLOBAL(vol_pct, g_volume_level); + bar_level = (vol_pct * 12u) / 100u; + if (bar_level > 12) bar_level = 12; + r = TX1_RGB_SCALE8(255); + g = TX1_RGB_SCALE8(255); + b = 0; + break; + } + case FEATURE_MODE_GUNSHOT_LEVEL: + bar_level = feature_vol_0_12; + if (bar_level > 12) bar_level = 12; + r = TX1_RGB_SCALE8(128); + g = 0; + b = TX1_RGB_SCALE8(128); + break; + case FEATURE_MODE_FOOTSTEPS_LEVEL: + bar_level = feature_vol_0_12; + if (bar_level > 12) bar_level = 12; + r = TX1_RGB_SCALE8(255); + g = TX1_RGB_SCALE8(165); + b = 0; + break; + case FEATURE_MODE_MIC_LEVEL: + bar_level = feature_vol_0_12; + if (bar_level > 12) bar_level = 12; + r = 0; + g = 0; + b = TX1_RGB_SCALE8(255); + break; + case FEATURE_MODE_NONE: + default: + htr3236_update(dev, i2c); + return; + } + + for (led = 1; led <= bar_level; led++) { + tx1_rgb_pwm_write_led(dev, i2c, led, r, g, b); + } + htr3236_update(dev, i2c); +} + void save_value(unsigned char * unsafe path, unsigned char value); unsigned char load_value(unsigned char * unsafe path); /* 改动原因:c_dfu 仅接收 FIRMWARE_UPGRADE_START,在此线程执行 handle_firmware_upgrade_start。 @@ -481,7 +583,8 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli unsigned eq_mode_time = 0; unsigned se_time = 0; unsigned se_count = 0; - timer tmr, se_tmr, eq_mode_timer, eq_sync_timer; + timer tmr, se_tmr, eq_mode_timer, eq_sync_timer, led_fx_tmr; + unsigned led_fx_time = 0; unsigned eq_sync_time = 0; unsigned old_format = 14; unsigned unmute_dac_state; @@ -520,13 +623,19 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli eq_mode_time += EQ_MODE_DELAY; eq_sync_timer :> eq_sync_time; eq_sync_time += EQ_SYNC_DELAY; + led_fx_tmr :> led_fx_time; + led_fx_time += TIMER_2_PERIOD; // TX1: Initialize tile[0] GPIO LEDs (all off, active low) led_tile0_shadow = 0xF0; p_led_tile0 <: led_tile0_shadow; - // TX1: tile[1] GPIO + HTR3236 SDB,再经 I2C 按 golden 流程配置 HTR3236 - tx1_htr3236_bringup(&htr3236_dev, i2c, led_if); + /* 改动原因:仅预拉 SDB;完整 bringup 在 codec_init 完成后执行(见 tmr 分支) */ + led_if.init(); + tx1_htr3236_sdb_enable(led_if); + htr3236_init(&htr3236_dev, HTR3236_ADDR_GND); + + unsigned htr3236_bringup_done = 0; // TX1 button previous state (active low: 0=pressed, 1=released) unsigned prev_fps = 1, prev_game = 1, prev_mic = 1; @@ -552,6 +661,7 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli // TX1 LED effect state for game mode indicators unsigned gpio_leds_dirty = 1; // refresh LEDs on first tick + unsigned led_pattern_step = TX1_EFFECT_RACE; // 改动原因:BYPASS 下 RGB 装饰灯效,VOL++/VOL- 循环 #if HID_DFU_EN firmware_upgrade_init(); @@ -594,6 +704,17 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli uint32_t now, old_time; time += TIMER_PERIOD; unsigned dac_vol, adc_vol, dac_mode, new_dac_mode; + unsigned codec_init_done; + + /* 改动原因:codec_init 经 chan 在本任务处理;完成后再做 HTR3236,避免假成功 */ + if (!htr3236_bringup_done) { + GET_SHARED_GLOBAL(codec_init_done, g_audiohw_codec_init_done); + if (codec_init_done) { + debug_printf("HTR3236 bringup start (after codec)\n"); + tx1_htr3236_bringup(&htr3236_dev, i2c, led_if); + htr3236_bringup_done = 1; + } + } now = get_reference_time(); @@ -1054,15 +1175,27 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli // ========== TX1 BUTTON SCANNING ========== { unsigned btn1, btn2; - p_button_fps_game_mic :> btn1; // bit3=FPS, bit2=GAME, bit1=MIC - p_button_vol :> btn2; // bit3=VOL+, bit2=VOL- + unsigned fps, game, mic, vol_plus, vol_minus; - // Extract button states (active low: 0=pressed, 1=released) - unsigned fps = (btn1 & TX1_BIT_FPS_MODE) ? 1 : 0; - unsigned game = (btn1 & TX1_BIT_GAME_MODE) ? 1 : 0; - unsigned mic = (btn1 & TX1_BIT_MIC_MUTE) ? 1 : 0; - unsigned vol_plus = (btn2 & TX1_BIT_VOL_PLUS) ? 1 : 0; - unsigned vol_minus = (btn2 & TX1_BIT_VOL_MINUS) ? 1 : 0; + p_button_fps_game_mic :> btn1; + p_button_vol :> btn2; + + /* 改动原因:低有效,1=弹起 0=按下;逻辑值 1=released 与 jok buttons.xc 一致 */ +#if TX1_BUTTON_MAP_PANEL_LAYOUT + /* 面板位映射(jok buttons.xc #else):左→右 FPS, VOL+, VOL-, GAME, MIC */ + vol_minus = (btn1 & TX1_BIT_FPS_MODE) ? 1 : 0; + vol_plus = (btn1 & TX1_BIT_GAME_MODE) ? 1 : 0; + fps = (btn1 & TX1_BIT_MIC_MUTE) ? 1 : 0; + mic = (btn2 & TX1_BIT_VOL_PLUS) ? 1 : 0; + game = (btn2 & TX1_BIT_VOL_MINUS) ? 1 : 0; +#else + /* 原理图直读(jok buttons.xc #if 0):4F=FPS/GAME/MIC, 4E=VOL+/VOL- */ + fps = (btn1 & TX1_BIT_FPS_MODE) ? 1 : 0; + game = (btn1 & TX1_BIT_GAME_MODE) ? 1 : 0; + mic = (btn1 & TX1_BIT_MIC_MUTE) ? 1 : 0; + vol_plus = (btn2 & TX1_BIT_VOL_PLUS) ? 1 : 0; + vol_minus = (btn2 & TX1_BIT_VOL_MINUS) ? 1 : 0; +#endif // Combo key detection (highest priority) tx1_combo_t new_combo = COMBO_NONE; @@ -1091,10 +1224,14 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli gpio_leds_dirty = 1; debug_printf("TX1: GAME+MIC combo - high_perf_mode=%d\n", high_perf_mode); } else if (current_combo == COMBO_VOL_UP_DOWN) { - // Cycle LED effects in BYPASS mode only + /* 改动原因:对照 jok on_combo_vol_up_down,BYPASS 下循环 RGB 灯效 */ if (game_mode == GAME_MODE_BYPASS && feature_mode == FEATURE_MODE_NONE) { - // For now, just toggle feature mode indicator - debug_printf("TX1: VOL++VOL- combo - LED effect cycle\n"); + led_pattern_step = (led_pattern_step + 1) % TX1_EFFECT_MAX; + debug_printf("TX1: VOL++VOL- combo - led_pattern_step=%u\n", led_pattern_step); + } else if (feature_mode != FEATURE_MODE_NONE) { + feature_mode = FEATURE_MODE_NONE; + feature_timeout_ticks = 0; + gpio_leds_dirty = 1; } } } @@ -1114,24 +1251,29 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli factory_reset_6s_fired = 0; factory_reset_10s_fired = 0; factory_reset_done = 0; + tx1_factory_reset_countdown_start(); debug_printf("TX1: Factory reset countdown started\n"); } else if (!factory_reset_done) { unsigned elapsed = now - factory_reset_start_time; if (elapsed >= 10600000000ull && !factory_reset_done) { // 10.6s factory_reset_done = 1; + tx1_factory_reset_triggered(); debug_printf("TX1: FACTORY RESET TRIGGERED!\n"); SET_SHARED_GLOBAL(g_request_factory_reset, 1); } else if (elapsed >= 10000000000ull && !factory_reset_10s_fired) { factory_reset_10s_fired = 1; + tx1_factory_reset_countdown_10s(); debug_printf("TX1: Factory reset 10s warning\n"); } else if (elapsed >= 6000000000ull && !factory_reset_6s_fired) { factory_reset_6s_fired = 1; + tx1_factory_reset_countdown_6s(); debug_printf("TX1: Factory reset 6s warning\n"); } } } else { if (factory_reset_counting) { factory_reset_counting = 0; + tx1_factory_reset_cancel(); debug_printf("TX1: Factory reset cancelled\n"); } } @@ -1328,9 +1470,35 @@ void AudioHwRemote2(streaming chanend c, client interface i2c_master_if i2c, cli if (mic_muted) { led_if.led_on(TX1_GPIO_LED_D2); } + + /* 改动原因:HTR3236 RGB D1-D12 音量条;工厂复位灯效占用 RGB 时不刷条 */ + if (g_htr3236_ready && !tx1_factory_reset_rgb_active()) { + tx1_rgb_volume_bar_refresh(&htr3236_dev, i2c, feature_mode, feature_volume); + } } break; + + /* 改动原因:第二定时器 50ms,移植 jok led_effects_period_update_task */ + case led_fx_tmr when timerafter(led_fx_time) :> void : + if (g_htr3236_ready) { + unsigned gm; + static unsigned led_fx_dbg_cnt; + GET_SHARED_GLOBAL(gm, g_3d_fps); + tx1_led_effects_periodic(&htr3236_dev, i2c, + gm, + (unsigned)feature_mode, + led_pattern_step); + /* 改动原因:确认 50ms 灯效任务在跑;每约 5s 打一次 gm/fm/pattern */ + led_fx_dbg_cnt++; + if ((led_fx_dbg_cnt % 100) == 1) { + debug_printf("led_fx gm=%u fm=%u pat=%u ready=%u\n", + gm, (unsigned)feature_mode, led_pattern_step, g_htr3236_ready); + } + } + led_fx_time += TIMER_2_PERIOD; + break; + case se_tmr when timerafter(se_time) :> void : se_time += SE_DELAY; se_count ++; @@ -1426,8 +1594,8 @@ void AudioHwRemote(streaming chanend c, streaming chanend c_dfu, client interfac par { - /* 改动原因:与 golden_6ch 一致使用 300kHz I2C,原 100kHz 非 HTR3236 失效主因但统一时序 */ - i2c_master(i2c, 1, p_scl, p_sda, 300); + /* 改动原因:与 jok xk_audio_316_mc_ab 一致 100kHz;300kHz 可能导致 HTR3236 长包异常 */ + i2c_master(i2c, 1, p_scl, p_sda, 100); AudioHwRemote2(c, i2c[0], c_dfu, led_if); } } @@ -1438,8 +1606,8 @@ void AudioHwRemote(streaming chanend c, client interface tx1_led_if led_if) par { - /* 改动原因:与 golden_6ch 一致使用 300kHz I2C,原 100kHz 非 HTR3236 失效主因但统一时序 */ - i2c_master(i2c, 1, p_scl, p_sda, 300); + /* 改动原因:与 jok xk_audio_316_mc_ab 一致 100kHz */ + i2c_master(i2c, 1, p_scl, p_sda, 100); AudioHwRemote2(c, i2c[0], led_if); } } @@ -1478,11 +1646,13 @@ void AudioHwInit() codec_init(); - debug_printf("AudioHwInit completed\n"); - CODEC_REGWRITE(NAU88_I2C_DEVICE_ADDR, 0x0003, 0x0053); CODEC_REGWRITE(NAU88_I2C_DEVICE_ADDR, 0x002B, 0x4002); CODEC_REGWRITE(NAU88_I2C_DEVICE_ADDR, 0x002C, 0x0082); + + debug_printf("AudioHwInit completed\n"); + /* 改动原因:全部 codec 寄存器写完后再置位,触发 HTR3236 bringup */ + SET_SHARED_GLOBAL(g_audiohw_codec_init_done, 1); } void unmute_dac(void) diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.h index b6c51a4..1b9c7e9 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.h +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.h @@ -173,7 +173,7 @@ int htr3236_update(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)); /** * @brief 全局 LED 使能控制 * @param dev 设备结构体指针 - * @param enable 1:关闭所有LED, 0:正常模式 + * @param enable 1:正常输出, 0:关闭全部 36 路 LED(4Ah G_EN=1) * @return 0:成功, -1:失败 */ int htr3236_global_enable(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t enable); @@ -200,4 +200,7 @@ uint8_t htr3236_gamma_32(uint8_t index); */ uint8_t htr3236_gamma_64(uint8_t index); +/* 改动原因:bringup 读回 0x00 关断寄存器,确认 I2C 真连通且非 NACK 假象 */ +int htr3236_read_reg(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t reg, uint8_t *value); + #endif /* HTR3236_H */ \ No newline at end of file diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc new file mode 100644 index 0000000..582bf4f --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/htr3236.xc @@ -0,0 +1,214 @@ +/** + * @file htr3236.xc + * @brief HTR3236 36 路 LED PWM 驱动(与 phaten_golden_6ch 同源) + * @version 1.1 + * + * 改动原因:与 jok 相同,SDB 仅 tile1 PORT_HTR3236_SDB(1K),由 tx1_led_if 控制,不在此操作 GPIO。 + */ + +#include "htr3236.h" +#include + +/*========================================================================= + Gamma 校正查找表 + -----------------------------------------------------------------------*/ + +static const uint8_t gamma_table_32[32] = { + 0, 1, 2, 4, 6, 10, 13, 18, + 22, 28, 33, 39, 46, 53, 61, 69, + 78, 86, 96, 106, 116, 126, 138, 149, + 161, 173, 186, 199, 212, 226, 240, 255 +}; + +static const uint8_t gamma_table_64[64] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 10, 12, 14, 16, 18, 20, 22, + 24, 26, 29, 32, 35, 38, 41, 44, + 47, 50, 53, 57, 61, 65, 69, 73, + 77, 81, 85, 89, 94, 99, 104, 109, + 114, 119, 124, 129, 134, 140, 146, 152, + 158, 164, 170, 176, 182, 188, 195, 202, + 209, 216, 223, 230, 237, 244, 251, 255 +}; + +static int write_reg(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t reg, uint8_t data) +{ + uint8_t buf[2] = {reg, data}; + size_t n; + + /* 改动原因:与 jok/htr3236 及 lib_i2c write_reg 一致,按字节数判断(100kHz 下更稳) */ + unsafe { + i2c.write(dev->i2c_addr, buf, 2, n, 1); + } + + if (n == 0) { + return I2C_REGOP_DEVICE_NACK; + } + if (n < 2) { + return I2C_REGOP_INCOMPLETE; + } + + return I2C_REGOP_SUCCESS; +} + +static int write_reg_bulk(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t start_reg, + const uint8_t *data, uint8_t len) +{ + uint8_t buf[36 + 1]; + size_t n; + + buf[0] = start_reg; + + for (int i = 0; i < len; i++) { + buf[i + 1] = data[i]; + } + + unsafe { + i2c.write(dev->i2c_addr, buf, len + 1, n, 1); + } + + if (n == 0) { + return I2C_REGOP_DEVICE_NACK; + } + if (n < (len + 1)) { + return I2C_REGOP_INCOMPLETE; + } + + return I2C_REGOP_SUCCESS; +} + +void htr3236_init(htr3236_t *dev, uint8_t addr) +{ + dev->i2c_addr = addr; +} + +/* 改动原因:jok 在 tile1_io_control_task 拉 SDB,此处保持空实现 */ +void htr3236_hw_enable(htr3236_t *dev) +{ +} + +void htr3236_hw_disable(htr3236_t *dev) +{ +} + +int htr3236_software_wake(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_SHUTDOWN, HTR3236_NORMAL_OP); +} + +int htr3236_software_shutdown(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_SHUTDOWN, HTR3236_SHUTDOWN_SOFT); +} + +int htr3236_software_reset(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_RESET, 0x00); +} + +int htr3236_set_pwm(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t channel, uint8_t brightness) +{ + if (channel < 1 || channel > 36) { + return -1; + } + + uint8_t reg = HTR3236_REG_PWM_START + (channel - 1); + return write_reg(dev, i2c, reg, brightness); +} + +int htr3236_set_pwm_bulk(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t start_ch, + const uint8_t *values, uint8_t len) +{ + if (start_ch < 1 || start_ch > 36 || len == 0) { + return -1; + } + + if (start_ch + len - 1 > 36) { + return -1; + } + + uint8_t start_reg = HTR3236_REG_PWM_START + (start_ch - 1); + return write_reg_bulk(dev, i2c, start_reg, values, len); +} + +int htr3236_set_led_config(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t channel, + htr3236_current_t current, uint8_t enable) +{ + if (channel < 1 || channel > 36) { + return -1; + } + + uint8_t reg = HTR3236_REG_LED_CTRL_START + (channel - 1); + uint8_t data = (enable ? 1 : 0) | (current << 1); + + return write_reg(dev, i2c, reg, data); +} + +int htr3236_set_led_config_bulk(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t start_ch, + const uint8_t *configs, uint8_t len) +{ + if (start_ch < 1 || start_ch > 36 || len == 0) { + return -1; + } + + if (start_ch + len - 1 > 36) { + return -1; + } + + uint8_t start_reg = HTR3236_REG_LED_CTRL_START + (start_ch - 1); + return write_reg_bulk(dev, i2c, start_reg, configs, len); +} + +int htr3236_update(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c)) +{ + return write_reg(dev, i2c, HTR3236_REG_PWM_UPDATE, 0x00); +} + +int htr3236_global_enable(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t enable) +{ + /* 改动原因:数据手册 4Ah D0——0=正常工作,1=关闭全部 LED;原 enable?1:0 会误关断 */ + uint8_t data = enable ? 0 : 1; + return write_reg(dev, i2c, HTR3236_REG_GLOBAL_CTRL, data); +} + +int htr3236_set_frequency(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), htr3236_freq_t freq) +{ + uint8_t data = (freq == HTR3236_FREQ_22KHZ) ? 1 : 0; + return write_reg(dev, i2c, HTR3236_REG_FREQ_SET, data); +} + +uint8_t htr3236_gamma_32(uint8_t index) +{ + if (index >= 32) { + index = 31; + } + return gamma_table_32[index]; +} + +uint8_t htr3236_gamma_64(uint8_t index) +{ + if (index >= 64) { + index = 63; + } + return gamma_table_64[index]; +} + +/* 改动原因:自检读 shutdown 寄存器,区分「I2C 有应答」与「芯片已唤醒」 */ +int htr3236_read_reg(htr3236_t *dev, CLIENT_INTERFACE(i2c_master_if, i2c), uint8_t reg, uint8_t *value) +{ + i2c_regop_res_t result; + + if (value == NULL) { + return -1; + } + + /* 改动原因:使用 lib_i2c 标准 read_reg(重复起始),与 jok 一致 */ + unsafe { + *value = i2c.read_reg(dev->i2c_addr, reg, result); + } + + if (result == I2C_REGOP_SUCCESS) { + return I2C_REGOP_SUCCESS; + } + return I2C_REGOP_DEVICE_NACK; +} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_effects.h b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_effects.h new file mode 100644 index 0000000..4ab02f5 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_effects.h @@ -0,0 +1,54 @@ +/** + * @file tx1_led_effects.h + * @brief TX1 HTR3236 RGB 灯效与工厂复位 LED(移植自 jok led_effects.xc / ui_app.c) + */ +#ifndef TX1_LED_EFFECTS_H +#define TX1_LED_EFFECTS_H + +#include "htr3236.h" +#include +#include + +/* 与 jok ui_app.h factory_reset_effect_state_t 一致 */ +typedef enum { + TX1_FACTORY_RESET_IDLE = 0, + TX1_FACTORY_RESET_WAITING, + TX1_FACTORY_RESET_COUNTDOWN, + TX1_FACTORY_RESET_COUNTDOWN_2, + TX1_FACTORY_RESET_TRIGGERED +} tx1_factory_reset_state_t; + +/* 与 jok led_effect_t 一致 */ +typedef enum { + TX1_EFFECT_RACE = 0, + TX1_EFFECT_BREATH_ALL, + TX1_EFFECT_RAINBOW_RACE, + TX1_EFFECT_RAINBOW_FILL, + TX1_EFFECT_COLOR_WAVE, + TX1_EFFECT_STROBE, + TX1_EFFECT_PULSE, + TX1_EFFECT_SPARKLE, + TX1_EFFECT_GRADIENT, + TX1_EFFECT_MAX +} tx1_led_effect_t; + +extern tx1_factory_reset_state_t g_tx1_factory_reset_state; + +void tx1_factory_reset_countdown_start(void); +void tx1_factory_reset_countdown_6s(void); +void tx1_factory_reset_countdown_10s(void); +void tx1_factory_reset_triggered(void); +void tx1_factory_reset_cancel(void); + +/* 改动原因:工厂复位灯效占用 RGB 时,gpio_leds_dirty 不应再刷音量条 */ +unsigned tx1_factory_reset_rgb_active(void); + +void tx1_factory_reset_led_tick(htr3236_t *dev, client interface i2c_master_if i2c); + +/* 改动原因:50ms 定时器入口,对照 jok led_effects_period_update_task */ +void tx1_led_effects_periodic(htr3236_t *dev, client interface i2c_master_if i2c, + unsigned game_mode, + unsigned feature_mode, + unsigned led_pattern_step); + +#endif /* TX1_LED_EFFECTS_H */ diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_effects.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_effects.xc new file mode 100644 index 0000000..5e1e534 --- /dev/null +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_effects.xc @@ -0,0 +1,414 @@ +/** + * @file tx1_led_effects.xc + * @brief HTR3236 RGB 灯效 + 工厂复位 LED(移植自 jok led_effects.xc,50ms 步进) + */ +#include "tx1_led_effects.h" +#include + +#define TX1_RGB_LED_MAX 12 + +/* 改动原因:与 audiohw 提高后的亮度一致,否则灯效仍几乎不可见 */ +/* 改动原因:与 bringup 自检亮度一致,便于肉眼确认灯效 */ +#define TX1_FX_RGB_BRIGHTNESS 200 +#define TX1_FX_SCALE8(c) ((uint8_t)(((unsigned)(c) * TX1_FX_RGB_BRIGHTNESS) / 255)) + +/* D1-D12 B,G,R 通道映射(与 audiohw rgb_led_map 相同) */ +static const uint8_t tx1_rgb_led_map[13][3] = { + {0, 0, 0}, + {24, 23, 22}, {21, 20, 19}, {18, 17, 16}, {15, 14, 13}, + {12, 11, 10}, {9, 8, 7}, {6, 5, 4}, {3, 2, 1}, + {36, 35, 34}, {33, 32, 31}, {30, 29, 28}, {27, 26, 25} +}; + +static void tx1_fx_pwm_led(htr3236_t *dev, client interface i2c_master_if i2c, + uint8_t led, uint8_t r, uint8_t g, uint8_t b) +{ + if (led < 1 || led > TX1_RGB_LED_MAX) return; + htr3236_set_pwm(dev, i2c, tx1_rgb_led_map[led][0], b); + htr3236_set_pwm(dev, i2c, tx1_rgb_led_map[led][1], g); + htr3236_set_pwm(dev, i2c, tx1_rgb_led_map[led][2], r); +} + +static void tx1_fx_update(htr3236_t *dev, client interface i2c_master_if i2c) +{ + htr3236_update(dev, i2c); +} + +static void tx1_fx_all_off(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t zeros[36]; + int i; + for (i = 0; i < 36; i++) zeros[i] = 0; + htr3236_set_pwm_bulk(dev, i2c, 1, zeros, 36); + htr3236_update(dev, i2c); +} + +static void tx1_fx_all_color(htr3236_t *dev, client interface i2c_master_if i2c, + uint8_t r, uint8_t g, uint8_t b) +{ + uint8_t led; + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + tx1_fx_pwm_led(dev, i2c, led, r, g, b); + } + tx1_fx_update(dev, i2c); +} + +/*========================================================================= + 灯效状态(jok led_effects.xc) + -----------------------------------------------------------------------*/ +static int head_pos = 0; +static int breath_idx = 0; +static int rainbow_hue = 0; +static int color_wave_pos = 0; +static uint32_t sparkle_seed = 1; +static int gradient_pos = 0; + +static const uint8_t breath_curve[] = { + 0, 8, 16, 28, 40, 55, 70, 88, 106, 125, 144, 163, 180, 196, 210, 222, + 232, 240, 246, 250, 252, 253, 252, 250, 246, 240, 232, 222, 210, 196, + 180, 163, 144, 125, 106, 88, 70, 55, 40, 28, 16, 8, 0 +}; +#define BREATH_STEPS (sizeof(breath_curve) / sizeof(breath_curve[0])) + +static const uint8_t trail_table[] = {255, 200, 140, 80, 40, 20, 8}; +#define TRAIL_LEN (sizeof(trail_table) / sizeof(trail_table[0])) + +static const uint8_t rainbow_trail_table[] = {255, 200, 150, 100, 60, 30, 15}; +#define RAINBOW_TRAIL_LEN (sizeof(rainbow_trail_table) / sizeof(rainbow_trail_table[0])) + +static uint32_t tx1_simple_rand(void) +{ + sparkle_seed = sparkle_seed * 1103515245u + 12345u; + return (sparkle_seed >> 16) & 0x7FFFu; +} + +/* 改动原因:移植 jok get_rainbow_color */ +static void tx1_get_rainbow_color(int hue, uint8_t *r, uint8_t *g, uint8_t *b) +{ + int phase = hue % 240; + + if (phase < 40) { + *r = 255; *g = (uint8_t)((phase * 255) / 40); *b = 0; + } else if (phase < 80) { + *r = (uint8_t)(255 - ((phase - 40) * 255) / 40); *g = 255; *b = 0; + } else if (phase < 120) { + *r = 0; *g = 255; *b = (uint8_t)(((phase - 80) * 255) / 40); + } else if (phase < 160) { + *r = 0; *g = (uint8_t)(255 - ((phase - 120) * 255) / 40); *b = 255; + } else if (phase < 200) { + *r = (uint8_t)(((phase - 160) * 255) / 40); *g = 0; *b = 255; + } else { + *r = 255; *g = 0; *b = (uint8_t)(255 - ((phase - 200) * 255) / 40); + } +} + +/*========================================================================= + 工厂复位 LED(jok factory_reset_led_effect_loop) + -----------------------------------------------------------------------*/ +tx1_factory_reset_state_t g_tx1_factory_reset_state = TX1_FACTORY_RESET_IDLE; +static int g_tx1_factory_reset_timer_counter = 0; +static int g_tx1_factory_reset_blink_counter = 0; + +void tx1_factory_reset_countdown_start(void) +{ + g_tx1_factory_reset_state = TX1_FACTORY_RESET_WAITING; + g_tx1_factory_reset_timer_counter = 0; + g_tx1_factory_reset_blink_counter = 0; +} + +void tx1_factory_reset_countdown_6s(void) +{ + g_tx1_factory_reset_state = TX1_FACTORY_RESET_COUNTDOWN; + g_tx1_factory_reset_timer_counter = 0; + g_tx1_factory_reset_blink_counter = 0; +} + +void tx1_factory_reset_countdown_10s(void) +{ + g_tx1_factory_reset_state = TX1_FACTORY_RESET_COUNTDOWN_2; + g_tx1_factory_reset_timer_counter = 0; + g_tx1_factory_reset_blink_counter = 0; +} + +void tx1_factory_reset_triggered(void) +{ + g_tx1_factory_reset_state = TX1_FACTORY_RESET_TRIGGERED; + g_tx1_factory_reset_timer_counter = 0; + g_tx1_factory_reset_blink_counter = 0; +} + +void tx1_factory_reset_cancel(void) +{ + g_tx1_factory_reset_state = TX1_FACTORY_RESET_IDLE; + g_tx1_factory_reset_timer_counter = 0; + g_tx1_factory_reset_blink_counter = 0; +} + +unsigned tx1_factory_reset_rgb_active(void) +{ + return (g_tx1_factory_reset_state != TX1_FACTORY_RESET_IDLE + && g_tx1_factory_reset_state != TX1_FACTORY_RESET_WAITING) ? 1 : 0; +} + +void tx1_factory_reset_led_tick(htr3236_t *dev, client interface i2c_master_if i2c) +{ + switch (g_tx1_factory_reset_state) { + case TX1_FACTORY_RESET_WAITING: + break; + + case TX1_FACTORY_RESET_COUNTDOWN: + g_tx1_factory_reset_timer_counter++; + if (g_tx1_factory_reset_timer_counter >= 10) { + g_tx1_factory_reset_timer_counter = 0; + g_tx1_factory_reset_blink_counter++; + if ((g_tx1_factory_reset_blink_counter % 2) == 0) { + tx1_fx_all_color(dev, i2c, + 0, TX1_FX_SCALE8(255), 0); + } else { + tx1_fx_all_color(dev, i2c, + TX1_FX_SCALE8(139), TX1_FX_SCALE8(91), TX1_FX_SCALE8(246)); + } + } + break; + + case TX1_FACTORY_RESET_COUNTDOWN_2: + g_tx1_factory_reset_timer_counter++; + if (g_tx1_factory_reset_timer_counter >= 2) { + g_tx1_factory_reset_timer_counter = 0; + if (g_tx1_factory_reset_blink_counter == 0) { + g_tx1_factory_reset_blink_counter = 1; + tx1_fx_all_color(dev, i2c, 0, 0, 0); + } else { + g_tx1_factory_reset_blink_counter = 0; + tx1_fx_all_color(dev, i2c, TX1_FX_SCALE8(255), 0, 0); + } + } + break; + + case TX1_FACTORY_RESET_TRIGGERED: + tx1_fx_all_color(dev, i2c, 0, 0, 0); + break; + + default: + break; + } +} + +/*========================================================================= + 装饰灯效(每步由 50ms 定时器调用一次) + -----------------------------------------------------------------------*/ +static void tx1_effect_race(htr3236_t *dev, client interface i2c_master_if i2c) +{ + int i; + for (i = 0; i < TX1_RGB_LED_MAX; i++) { + int dist = (head_pos - i + TX1_RGB_LED_MAX) % TX1_RGB_LED_MAX; + uint8_t br = 0; + if (dist < (int)TRAIL_LEN) { + br = TX1_FX_SCALE8(trail_table[dist]); + } + tx1_fx_pwm_led(dev, i2c, (uint8_t)(i + 1), br, br, br); + } + tx1_fx_update(dev, i2c); + head_pos = (head_pos + 1) % TX1_RGB_LED_MAX; +} + +static void tx1_effect_breath_all(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t br = TX1_FX_SCALE8(breath_curve[breath_idx]); + uint8_t led; + breath_idx = (breath_idx + 1) % (int)BREATH_STEPS; + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + tx1_fx_pwm_led(dev, i2c, led, br, br, br); + } + tx1_fx_update(dev, i2c); +} + +static void tx1_effect_rainbow_race(htr3236_t *dev, client interface i2c_master_if i2c) +{ + int i; + for (i = 0; i < TX1_RGB_LED_MAX; i++) { + int dist = (head_pos - i + TX1_RGB_LED_MAX) % TX1_RGB_LED_MAX; + if (dist < (int)RAINBOW_TRAIL_LEN) { + uint8_t brightness = rainbow_trail_table[dist]; + uint8_t r, g, b; + int hue = (head_pos * 20) % 240; + tx1_get_rainbow_color(hue, &r, &g, &b); + r = (uint8_t)((r * brightness) / 255); + g = (uint8_t)((g * brightness) / 255); + b = (uint8_t)((b * brightness) / 255); + tx1_fx_pwm_led(dev, i2c, (uint8_t)(i + 1), + TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } else { + tx1_fx_pwm_led(dev, i2c, (uint8_t)(i + 1), 0, 0, 0); + } + } + tx1_fx_update(dev, i2c); + head_pos = (head_pos + 1) % TX1_RGB_LED_MAX; +} + +static void tx1_effect_rainbow_fill(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t led; + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + int hue = (rainbow_hue + ((int)led * 20)) % 240; + uint8_t r, g, b; + tx1_get_rainbow_color(hue, &r, &g, &b); + tx1_fx_pwm_led(dev, i2c, led, TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } + tx1_fx_update(dev, i2c); + rainbow_hue = (rainbow_hue + 4) % 240; +} + +static void tx1_effect_color_wave(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t led; + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + int hue = (((int)led * 20) + color_wave_pos) % 240; + uint8_t r, g, b; + tx1_get_rainbow_color(hue, &r, &g, &b); + tx1_fx_pwm_led(dev, i2c, led, TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } + tx1_fx_update(dev, i2c); + color_wave_pos = (color_wave_pos + 8) % 240; +} + +static void tx1_effect_strobe(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t r = (uint8_t)(tx1_simple_rand() % 256); + uint8_t g = (uint8_t)(tx1_simple_rand() % 256); + uint8_t b = (uint8_t)(tx1_simple_rand() % 256); + uint8_t led; + + if (tx1_simple_rand() % 2) { + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + tx1_fx_pwm_led(dev, i2c, led, + TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } + } else { + tx1_fx_all_off(dev, i2c); + return; + } + tx1_fx_update(dev, i2c); +} + +static void tx1_effect_pulse(htr3236_t *dev, client interface i2c_master_if i2c) +{ + uint8_t brightness = breath_curve[breath_idx]; + uint8_t r, g, b; + uint8_t led; + + breath_idx = (breath_idx + 1) % (int)BREATH_STEPS; + if (brightness < 128) { + r = brightness; + g = (uint8_t)(brightness / 3); + b = 0; + } else { + r = 255; g = 255; b = 255; + } + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + tx1_fx_pwm_led(dev, i2c, led, + TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } + tx1_fx_update(dev, i2c); +} + +static void tx1_effect_sparkle(htr3236_t *dev, client interface i2c_master_if i2c) +{ + int sparkle_count = 3 + (int)(tx1_simple_rand() % 3); + int i; + uint8_t led; + + tx1_fx_all_off(dev, i2c); + for (i = 0; i < sparkle_count; i++) { + int led_idx = (int)(tx1_simple_rand() % TX1_RGB_LED_MAX); + uint8_t brightness = (uint8_t)(100 + (tx1_simple_rand() % 156)); + uint8_t color_choice = (uint8_t)(tx1_simple_rand() % 6); + uint8_t r = 0, g = 0, b = 0; + + switch (color_choice) { + case 0: r = brightness; break; + case 1: g = brightness; break; + case 2: b = brightness; break; + case 3: r = brightness; g = brightness; break; + case 4: g = brightness; b = brightness; break; + default: r = brightness; g = brightness; b = brightness; break; + } + tx1_fx_pwm_led(dev, i2c, (uint8_t)(led_idx + 1), + TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } + tx1_fx_update(dev, i2c); +} + +static void tx1_effect_gradient(htr3236_t *dev, client interface i2c_master_if i2c) +{ + int progress = gradient_pos; + uint8_t r, g, b; + uint8_t led; + + if (progress < 128) { + r = 255; g = 0; b = (uint8_t)((progress * 2) * 255 / 256); + } else { + r = (uint8_t)(255 - ((progress - 128) * 2) * 255 / 256); + g = 0; b = 255; + } + for (led = 1; led <= TX1_RGB_LED_MAX; led++) { + tx1_fx_pwm_led(dev, i2c, led, + TX1_FX_SCALE8(r), TX1_FX_SCALE8(g), TX1_FX_SCALE8(b)); + } + tx1_fx_update(dev, i2c); + gradient_pos = (gradient_pos + 2) % 256; +} + +/* game_mode / feature_mode 数值与 audiohw tx1_*_t 枚举一致:BYPASS=0, FEATURE_NONE=0 */ +void tx1_led_effects_periodic(htr3236_t *dev, client interface i2c_master_if i2c, + unsigned game_mode, + unsigned feature_mode, + unsigned led_pattern_step) +{ + if (g_tx1_factory_reset_state != TX1_FACTORY_RESET_IDLE) { + tx1_factory_reset_led_tick(dev, i2c); + return; + } + + if (feature_mode != 0) { + return; + } + + /* 非 BYPASS 时暂不跑方位灯效(ex3d 后续可接);熄灭 RGB 装饰 */ + if (game_mode != 0) { + return; + } + + switch (led_pattern_step) { + case TX1_EFFECT_RACE: + tx1_effect_race(dev, i2c); + break; + case TX1_EFFECT_BREATH_ALL: + tx1_effect_breath_all(dev, i2c); + break; + case TX1_EFFECT_RAINBOW_RACE: + tx1_effect_rainbow_race(dev, i2c); + break; + case TX1_EFFECT_RAINBOW_FILL: + tx1_effect_rainbow_fill(dev, i2c); + break; + case TX1_EFFECT_COLOR_WAVE: + tx1_effect_color_wave(dev, i2c); + break; + case TX1_EFFECT_STROBE: + tx1_effect_strobe(dev, i2c); + break; + case TX1_EFFECT_PULSE: + tx1_effect_pulse(dev, i2c); + break; + case TX1_EFFECT_SPARKLE: + tx1_effect_sparkle(dev, i2c); + break; + case TX1_EFFECT_GRADIENT: + tx1_effect_gradient(dev, i2c); + break; + default: + tx1_fx_all_off(dev, i2c); + break; + } +} diff --git a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_helper.xc b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_helper.xc index a4331b8..67ce6e8 100644 --- a/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_helper.xc +++ b/sw_usb_audio/app_usb_aud_fosi_c1_v71/src/extensions/tx1_led_helper.xc @@ -32,7 +32,8 @@ void tx1_led_helper_task(server interface tx1_led_if led_if) p_led_d3 <: 0xF; p_led_d5 <: 1; p_led_d4_d7_d6 <: 0xF; - p_htr3236_sdb <: 0; // HTR3236 disabled initially + /* 改动原因:上电即拉高 SDB,与 jok tile1_io_control_task 一致;勿长期拉低否则 I2C 配置无效 */ + p_htr3236_sdb <: 1; p_ctl_mute <: 1; // Muted initially while (1)