2026-08-26 07:26:58 +08:00
# include "includes.h"
rt_mutex_t TM1629_Mutex = RT_NULL ;
rt_mutex_t TM1617_Mutex = RT_NULL ;
rt_mq_t MainTask_msg = RT_NULL ;
rt_mq_t UITask_msg = RT_NULL ;
rt_mq_t BLTask_msg = RT_NULL ;
rt_sem_t UART4_sem = RT_NULL ;
// 定义定时器ID
rt_timer_t OutTime_Stop_timer = RT_NULL ;
const Touch_AreaTypeDef * pCurrentTouch_Area = Touch_ModeSelect_Areas ;
GUI_SWITCH mGuiData [ ] = {
{ 0 , 0 , 11 , GUI_TRANSPOSE } , // 0
{ 78 , 40 , 260 , GUI_SPEED } , // 1
{ 0 , 0 , 1 , GUI_MODE_PARAM } , // 2 ?? <20> Զ<EFBFBD> <D4B6> <EFBFBD> <EFBFBD> <EFBFBD> <E0BFAA> 0=<3D> <> 1=<3D> <>
{ 0 , 0 , 4 , GUI_TIMBRE_SELECT } , // 3
{ 0 , 0 , 3 , GUI_PLAY_SECTION } , // 4
{ 0 , 0 , 3 , GUI_TAB_INDEX } , // 5
{ 0 , 0 , 1 , GUI_AUTOBAND_SW } , // 6 ?? <20> Զ<EFBFBD> <D4B6> <EFBFBD> <EFBFBD> <EFBFBD> <E0BFAA> 0=<3D> <> 1=<3D> <>
{ 1 , 0 , 1 , GUI_BL_SW } ,
{ 0 , 0 , 3 , GUI_TAB_LAST_INDEX } ,
} ;
GUI_SWITCH ParamGuiData [ ] =
{
2026-09-04 17:06:02 +08:00
{ 0 , 0 , 30 , SONG_MODE_PARAM } , // 0 节奏类型( 1.bin, 约 31 首)
2026-09-04 22:15:46 +08:00
{ 0 , 0 , 0 , EXPRESS_MODE_PARAM } , // 1 本地曲目( 2.bin; Max 由 UI_ReloadTonePreset←LOCAL_SONG_COUNT)
2026-08-26 07:26:58 +08:00
{ 0 , 0 , 2 , ALL_MODE_PARAM } , // 2
} ;
const LEBEL_UI UI_Label [ ] = {
2026-09-03 11:58:08 +08:00
/* 0819: 移调/速度 标签11、主值16 */
{ UI0902_MARGIN_X , UI0902_CARD_W , UI0902_CARD_Y , UI0902_CARD_H ,
( const uint8_t * ) " \xD2 \xC6 \xB5 \xF7 " , LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_LABEL ,
LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_HERO , GUI_TRANSPOSE } ,
{ UI0902_MARGIN_X + UI0902_CARD_W + UI0902_CARD_GAP , UI0902_CARD_W , UI0902_CARD_Y , UI0902_CARD_H ,
( const uint8_t * ) " \xCB \xD9 \xB6 \xC8 " , LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_LABEL ,
LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_HERO , GUI_SPEED } ,
/* 0819: TAB/行标题11、曲目正文13 */
{ UI0902_MARGIN_X , UI0902_CONTENT_W , UI0902_MODE_Y , UI0902_MODE_H ,
2026-09-07 13:48:18 +08:00
( const uint8_t * ) " \xB8 \xE8 \xC7 \xFA \xD1 \xA1 \xD4 \xF1 " , LCD_ALIGN_LEFT , WHITE , BLACK , UI0902_FONT_LABEL ,
2026-09-03 11:58:08 +08:00
LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_BODY , GUI_MODE_PARAM } ,
{ UI0902_MARGIN_X , UI0902_CONTENT_W , UI0902_TIMBRE_Y , UI0902_TIMBRE_H ,
2026-09-07 12:14:27 +08:00
( const uint8_t * ) " \xCF \xD2 \xD2 \xF4 \xC9 \xAB \xD1 \xA1 \xD4 \xF1 " , LCD_ALIGN_LEFT , WHITE , BLACK , UI0902_FONT_LABEL ,
2026-09-03 11:58:08 +08:00
LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_BODY , GUI_TIMBRE_SELECT } ,
/* 0819: 演奏段落 字号13 */
{ UI0902_MARGIN_X , UI0902_CONTENT_W , UI0902_SECTION_Y , UI0902_SECTION_H ,
( const uint8_t * ) " \xD1 \xDD \xD7 \xE0 \xB6 \xCE \xC2 \xE4 " , LCD_ALIGN_LEFT , WHITE , BLACK , UI0902_FONT_BODY ,
LCD_ALIGN_CENTER , WHITE , BLACK , UI0902_FONT_BODY , GUI_PLAY_SECTION } ,
} ;
2026-08-26 07:26:58 +08:00
2026-09-04 14:43:32 +08:00
/* 0904 / 0902-06: 左→右 伴奏|弦|麦|麦混响, 轨宽31 顶≈58 */
2026-08-26 07:26:58 +08:00
Fader_t faders [ 4 ] = {
2026-09-04 14:43:32 +08:00
{ 27 , 58 , 31 , 215 , 7 , 0 , 0 , " \xB0 \xE9 \xD7 \xE0 " } , /* 0: 伴奏 */
{ 78 , 58 , 31 , 215 , 7 , 1 , 0 , " \xCF \xD2 " } , /* 1: 弦 */
{ 130 , 58 , 31 , 215 , 7 , 2 , 0 , " \xC2 \xF3 " } , /* 2: 麦 */
{ 182 , 58 , 31 , 215 , 7 , 3 , 0 , " \xC2 \xF3 \xBB \xEC \xCF \xEC " } /* 3: 麦混响 */
2026-08-26 07:26:58 +08:00
} ;
const Touch_AreaTypeDef Touch_Areas [ ] =
{
2026-09-03 11:58:08 +08:00
/* Xmin,Xmax,Ymin,Ymax, Flag, Value, ID, action — 与 UI_Label 网格同步 */
2026-09-04 06:39:00 +08:00
{ 12 , 64 , UI0902_CARD_Y , UI0902_CARD_Y + UI0902_CARD_H - 1 , - 1 , 0 , GUI_TRANSPOSE , NULL } ,
{ 65 , 116 , UI0902_CARD_Y , UI0902_CARD_Y + UI0902_CARD_H - 1 , 1 , 0 , GUI_TRANSPOSE , NULL } ,
2026-08-26 07:26:58 +08:00
2026-09-04 06:39:00 +08:00
{ 125 , 176 , UI0902_CARD_Y , UI0902_CARD_Y + UI0902_CARD_H - 1 , - 1 , 0 , GUI_SPEED , NULL } ,
{ 177 , 228 , UI0902_CARD_Y , UI0902_CARD_Y + UI0902_CARD_H - 1 , 1 , 0 , GUI_SPEED , NULL } ,
2026-08-26 07:26:58 +08:00
2026-09-03 12:43:19 +08:00
/* 节奏类型 / 本地曲目 TAB( 0902: 12-66 / 72-127) */
{ 12 , 66 , UI0902_MODE_Y , UI0902_MODE_Y + UI0902_TAB_H - 1 , 0 , 0 , GUI_AUTOBAND_SW , NULL } ,
{ 72 , 127 , UI0902_MODE_Y , UI0902_MODE_Y + UI0902_TAB_H - 1 , 0 , 1 , GUI_AUTOBAND_SW , NULL } ,
2026-08-26 07:26:58 +08:00
2026-09-03 12:43:19 +08:00
{ 13 , 120 , UI0902_MODE_Y + UI0902_TAB_H + 4 , UI0902_MODE_Y + UI0902_MODE_H - 1 , - 1 , 0 , GUI_MODE_PARAM , NULL } ,
{ 121 , 227 , UI0902_MODE_Y + UI0902_TAB_H + 4 , UI0902_MODE_Y + UI0902_MODE_H - 1 , 1 , 0 , GUI_MODE_PARAM , NULL } ,
2026-08-26 07:26:58 +08:00
2026-09-03 12:43:19 +08:00
{ 13 , 120 , UI0902_TIMBRE_Y , UI0902_TIMBRE_Y + UI0902_TIMBRE_H - 1 , - 1 , 0 , GUI_TIMBRE_SELECT , NULL } ,
{ 121 , 227 , UI0902_TIMBRE_Y , UI0902_TIMBRE_Y + UI0902_TIMBRE_H - 1 , 1 , 0 , GUI_TIMBRE_SELECT , NULL } ,
2026-08-26 07:26:58 +08:00
2026-09-04 06:39:00 +08:00
/* 演奏段落 1~4( 0902-04: 12/69/125/182, 宽 47, 步进 57) */
2026-09-03 12:43:19 +08:00
{ 12 , 58 , UI0902_SECTION_Y , UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1 , 0 , 0 , GUI_PLAY_SECTION , NULL } ,
2026-09-04 06:39:00 +08:00
{ 69 , 115 , UI0902_SECTION_Y , UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1 , 0 , 1 , GUI_PLAY_SECTION , NULL } ,
{ 126 , 172 , UI0902_SECTION_Y , UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1 , 0 , 2 , GUI_PLAY_SECTION , NULL } ,
{ 183 , 229 , UI0902_SECTION_Y , UI0902_SECTION_Y + UI0902_SECTION_BTN_H - 1 , 0 , 3 , GUI_PLAY_SECTION , NULL } ,
2026-09-03 11:58:08 +08:00
2026-09-09 11:01:40 +08:00
/* 底栏四键:万能 | 普通 | 专业 | 设置( 60× 55, y265– 319) */
{ UI0902_NAV_X0 ( 0 ) , UI0902_NAV_X1 ( 0 ) , UI0902_NAV_Y0 , UI0902_NAV_Y1 , 0 , UI0902_NAV_UNIVERSAL , GUI_NAV_BAR , NULL } ,
{ UI0902_NAV_X0 ( 1 ) , UI0902_NAV_X1 ( 1 ) , UI0902_NAV_Y0 , UI0902_NAV_Y1 , 0 , UI0902_NAV_NORMAL , GUI_NAV_BAR , NULL } ,
{ UI0902_NAV_X0 ( 2 ) , UI0902_NAV_X1 ( 2 ) , UI0902_NAV_Y0 , UI0902_NAV_Y1 , 0 , UI0902_NAV_EXPERT , GUI_NAV_BAR , NULL } ,
{ UI0902_NAV_X0 ( 3 ) , UI0902_NAV_X1 ( 3 ) , UI0902_NAV_Y0 , UI0902_NAV_Y1 , 0 , UI0902_NAV_SETTING , GUI_NAV_BAR , NULL } ,
2026-08-26 07:26:58 +08:00
} ;
2026-09-05 21:56:14 +08:00
/* 推子 0~10 共 11 档; 档10 取 AW816 上限附近 */
int16_t MIC_VOL_MAP [ 11 ] = { - 9000 , - 7000 , - 5000 , - 3000 , - 1000 , 1000 , 3000 , 5000 , 7000 , 9000 , 1200 } ;
2026-08-26 07:26:58 +08:00
uint8_t TOUCH_AREA_NUM = ( sizeof ( Touch_Areas ) / sizeof ( Touch_AreaTypeDef ) ) ;
2026-09-21 15:52:58 +08:00
const uint8_t Version [ 3 ] = { 0 , 2 , 18 } ; /* major.minor.patch — 0.2.18: free-mode even-beat release stop; keep KEY_ID during wind-down (no octave jump); universal outro all-green LED; expert local song continues after section+fret */
2026-08-26 07:26:58 +08:00
uint8_t Led = 8 ;
bool PressFlag = 0 ;
2026-09-15 19:13:10 +08:00
void Return_Light_buff ( uint8_t key_id_1629 ) ;
uint8_t Band_IsPlaying ( void ) ;
2026-08-26 07:26:58 +08:00
uint8_t AutoCloseTime = 15 ;
uint16_t mAutoPowerOffCount = 15 * 60 ;
bool AutoCloseFlag = true ;
void MainTask_Sendmsg ( uint16_t ID , uint16_t ID2 , uint16_t HiByte , uint16_t LoByte )
{
DisplayTaskMessage_Type msg ;
2026-08-31 11:24:10 +08:00
rt_err_t ret ;
2026-08-26 07:26:58 +08:00
msg . MessageType = ID ; /* 业务消息 ID( MSG_ID_xxx) */
msg . ID = ID2 ;
msg . HiByte = HiByte ;
msg . LoByte = LoByte ;
2026-08-31 11:24:10 +08:00
ret = rt_mq_send ( MainTask_msg , & msg , sizeof ( DisplayTaskMessage_Type ) ) ;
if ( ret ! = RT_EOK ) {
LOG_E ( " MSG " , " send fail id=%u ret=%d " , ( unsigned ) ID , ( int ) ret ) ;
} else if ( ID = = MSG_ID_TOUCH | | ID = = MSG_ID_TOUCH_LONG_REPEAT | |
ID = = MSG_ID_POWER_ON | | ID = = MSG_ID_POWER_OFF | |
ID = = MSG_ID_BP_KEY | | ID = = MSG_ID_TAP_SPEED | |
ID = = MSG_ID_BP_TRANSPOSE ) {
LOG_I ( " MSG " , " id=%u hi=%u lo=%u " , ( unsigned ) ID , ( unsigned ) HiByte , ( unsigned ) LoByte ) ;
}
2026-08-26 07:26:58 +08:00
}
void UITask_Sendmsg ( uint16_t ID , uint16_t ID2 , uint16_t HiByte , uint16_t LoByte )
{
DisplayTaskMessage_Type msg ;
msg . MessageType = ID ; /* 业务消息 ID( MSG_ID_xxx) */
msg . ID = ID2 ;
msg . HiByte = HiByte ;
msg . LoByte = LoByte ;
rt_mq_send ( UITask_msg , & msg , sizeof ( DisplayTaskMessage_Type ) ) ;
}
void BLTask_Sendmsg ( uint16_t ID , uint16_t ID2 , uint16_t HiByte , uint16_t LoByte )
{
DisplayTaskMessage_Type msg ;
msg . MessageType = ID ; /* 业务消息 ID( MSG_ID_xxx) */
msg . ID = ID2 ;
msg . HiByte = HiByte ;
msg . LoByte = LoByte ;
rt_mq_send ( BLTask_msg , & msg , sizeof ( DisplayTaskMessage_Type ) ) ;
}
# define KEY_PRESS_LEVEL RESET
2026-09-14 20:19:45 +08:00
# define PWR_ON_HOLD_MS 3000 /* 关机态长按 3s → 开机 */
# define PWR_OFF_HOLD_MS 2000 /* 开机态长按 2s → 关机 */
# define PWR_OFF_GRACE_MS 1500 /* 请求优雅关机后,超时仍未切轨则紧急断电+复位 */
2026-08-26 07:26:58 +08:00
typedef enum
{
KEY_STATE_IDLE ,
KEY_STATE_PRESS ,
KEY_STATE_NONE ,
} PwrKeyState ;
static PwrKeyState pwr_key_state = KEY_STATE_IDLE ;
static uint32_t pwr_press_tick = 0 ;
/* 当前开关机状态( UI_Idle.c 等 extern 引用) */
bool powon = false ;
2026-09-14 20:19:45 +08:00
/* 电源轨是否已切断( 优雅关机与紧急关机共用; ISR 依此判断是否还需强切) */
static volatile uint8_t s_pwr_rails_cut = 0 ;
/* TMR7: 关机长按已触发请求后的紧急倒计时( ms) */
static volatile uint16_t s_pwr_off_grace_ms = 0 ;
2026-08-26 07:26:58 +08:00
/* ==================== 开机/关机动作 ==================== */
2026-09-14 20:19:45 +08:00
/* 仅 GPIO/背光:可在 ISR / HardFault 中调用,禁止 RTOS/Flash/日志 */
void PowerOff_CutRails ( void )
{
s_pwr_rails_cut = 1u ;
powon = false ;
LCD_BLK_Clr ( ) ;
BSP_MainPowerEnable ( 0 ) ;
BSP_DreamCorePowerEnable ( 0 ) ;
BSP_HT7178PowerEnable ( 0 ) ;
BSP_BlueToothPowerEnable ( 0 ) ;
}
static void PowerOff_EmergencyReset ( void )
{
PowerOff_CutRails ( ) ;
/* 主循环已死时无法画充电页:复位后按 USB 状态进充电/待机 */
nvic_system_reset ( ) ;
}
2026-08-31 08:20:24 +08:00
void System_PowerOn ( void )
2026-08-26 07:26:58 +08:00
{
2026-08-31 11:24:10 +08:00
LOG_I ( " PWR " , " power_on begin " ) ;
2026-09-17 16:18:14 +08:00
/* 软关机不复位 MCU: 清掉上次会话演奏状态, 默认当前调一级大和弦( C 调=do mi sol) */
StartFlag = 0 ;
Accomp_ApplyDefaultIChord ( ) ;
app_tm1629_reset_key_state ( ) ;
2026-09-14 20:19:45 +08:00
s_pwr_rails_cut = 0u ;
s_pwr_off_grace_ms = 0u ;
2026-09-16 15:10:34 +08:00
/* 本次长按已用于开机:先消耗,再置 powon, 防止 TMR7 ISR 把同一按压力即判为关机 */
Power_Key_ConsumePress ( ) ;
2026-09-07 08:26:45 +08:00
/* 控制电源硬件;蓝牙按 NVM/系统设置开关恢复,勿强制常开 */
2026-08-26 07:26:58 +08:00
BSP_MainPowerEnable ( 1 ) ;
BSP_DreamCorePowerEnable ( 1 ) ;
BSP_HT7178PowerEnable ( 1 ) ;
2026-09-08 21:46:14 +08:00
/* 蓝牙按 NVM/系统设置开关恢复,勿强制常开 */
2026-09-07 08:26:45 +08:00
BSP_BlueToothPowerEnable ( ( uint8_t ) ( mGuiData [ GUI_BL_SW ] . Current ? 1 : 0 ) ) ;
2026-08-26 07:26:58 +08:00
powon = true ; /* 更新开机标志 */
2026-09-02 12:16:05 +08:00
/* 先通知 UI 亮屏,避免外设初始化阻塞导致长时间黑屏 */
LOG_I ( " PWR " , " send POWER_ON " ) ;
MainTask_Sendmsg ( MSG_ID_POWER_ON , 0 , 0 , 0 ) ;
2026-08-26 07:26:58 +08:00
AD85020B_Init ( ) ;
TM1629D_Key_LED_Init ( ) ;
app_tm1617_init ( ) ;
XPT2046_Init ( ) ;
App_Auto_Init ( ) ;
2026-09-08 21:46:14 +08:00
/* App 协议走 UART4: 须在开机后即解析, 不能等到触摸选模式才 StartTask */
StartTask ( ) ;
LOG_I ( " PWR " , " periph ready + BT UART recv started " ) ;
2026-08-26 07:26:58 +08:00
}
2026-08-31 08:20:24 +08:00
static void PowerOn ( void )
{
System_PowerOn ( ) ;
}
2026-08-26 07:26:58 +08:00
static void PowerOff ( void )
{
2026-08-31 11:24:10 +08:00
uint8_t nvm_ret ;
LOG_I ( " PWR " , " power_off begin " ) ;
2026-08-26 07:26:58 +08:00
powon = false ; /* 更新关机标志 */
2026-09-14 20:19:45 +08:00
s_pwr_off_grace_ms = 0u ; /* 主路径已接管,取消紧急倒计时 */
2026-08-26 07:26:58 +08:00
StopFullTask ( ) ;
2026-09-06 12:29:42 +08:00
2026-09-07 08:26:45 +08:00
/* 关机前把当前调音台推子与蓝牙开关写入 NVM, 保证再次开机保持 */
2026-09-06 12:29:42 +08:00
{
NvmParam_Type * nvm = drv_nvm_param_ptr ( ) ;
nvm - > param . BandVol = faders [ 0 ] . val ;
nvm - > param . StringVol = faders [ 1 ] . val ;
nvm - > param . MIcVol = faders [ 2 ] . val ;
nvm - > param . MicRev = faders [ 3 ] . val ;
2026-09-07 08:26:45 +08:00
nvm - > param . BluetoothSW = ( uint8_t ) ( mGuiData [ GUI_BL_SW ] . Current ? 1 : 0 ) ;
2026-09-06 12:29:42 +08:00
}
2026-08-31 11:24:10 +08:00
nvm_ret = drv_nvm_save_to_flash ( ) ;
LOG_I ( " PWR " , " nvm_save=%u " , ( unsigned ) nvm_ret ) ;
2026-08-26 07:26:58 +08:00
LCD_FillByColor ( 0 , 0 , 240 , 320 , BLACK ) ;
LCD_BLK_Clr ( ) ;
2026-09-09 07:03:52 +08:00
LCD_WR_REG ( 0x28 ) ; /* Display OFF */
2026-08-26 07:26:58 +08:00
app_tm1629_all_off ( ) ;
2026-09-09 07:03:52 +08:00
2026-08-26 07:26:58 +08:00
app_tm1617_off ( ) ;
2026-09-14 20:19:45 +08:00
/* BT 先留窗口转发已排队 ACK, 再切全部电源轨 */
2026-09-09 07:03:52 +08:00
rt_thread_mdelay ( 30 ) ;
2026-09-14 20:19:45 +08:00
PowerOff_CutRails ( ) ;
2026-09-05 19:39:15 +08:00
CurrUIProcress = IdleProcess ;
MainTask_Sendmsg ( MSG_ID_POWER_OFF , 0 , 0 , 0 ) ;
2026-09-09 07:03:52 +08:00
LOG_I ( " PWR " , " power_off done (soft-off) " ) ;
2026-09-05 19:58:42 +08:00
2026-09-09 07:03:52 +08:00
/* Type-C 有供电:维持现有逻辑,显示 Charging Image( POWOFF_CHARG) */
2026-09-05 19:58:42 +08:00
{
uint8_t i ;
for ( i = 0 ; i < 4 ; i + + )
{
app_adc_in1_scan_part ( 4 ) ;
if ( usb_charging_state | | app_adc_usb_is_plugged ( ) )
break ;
rt_thread_mdelay ( 5 ) ;
}
if ( usb_charging_state | | app_adc_usb_is_plugged ( ) )
{
usb_charging_state = 1 ;
usb_charging_laststate = 1 ;
2026-09-09 07:03:52 +08:00
LOG_I ( " PWR " , " soft-off with Type-C → charge UI " ) ;
2026-09-05 19:58:42 +08:00
MainTask_Sendmsg ( MSG_ID_POWOFF_CHARG , percentage , 1 , 0 ) ;
}
}
2026-08-26 07:26:58 +08:00
}
2026-09-08 10:00:55 +08:00
/* App remote power-off request (BLE cmd 05 00): executed in main-loop context,
so StopFullTask ( ) never detaches the calling BT thread itself . */
static volatile uint8_t s_sys_poweroff_req = 0 ;
void System_RequestPowerOff ( void )
{
s_sys_poweroff_req = 1 ;
2026-09-14 20:19:45 +08:00
/* 主循环若已卡死: TMR7 紧急倒计时仍会强切电源 */
if ( s_pwr_off_grace_ms = = 0u & & ! s_pwr_rails_cut )
s_pwr_off_grace_ms = PWR_OFF_GRACE_MS ;
2026-09-08 10:00:55 +08:00
}
void System_PowerOff_Poll ( void )
{
if ( s_sys_poweroff_req )
{
s_sys_poweroff_req = 0 ;
if ( powon )
PowerOff ( ) ;
}
}
2026-09-14 20:19:45 +08:00
/*
* TMR7 1 ms ( 优 先 级 高 于 伴 奏 TMR6 ) : 与 主 循 环 解 耦 的 关 机 键 扫 描 。
* 正 常 : 请 求 优 雅 关 机 ; 超 时 未 切 轨 → 紧 急 断 电 并 复 位 。
*/
2026-09-16 15:10:34 +08:00
static uint16_t s_isr_hold_ms = 0 ;
static uint8_t s_isr_fired = 0 ;
/* 开机时消耗本次按键:同一次长按已用于开机,须松开后重新长按 2s 才关机。
* 须 在 powon 置 true 前 调 用 , 避 免 ISR 在 开 机 瞬 间 把 本 次 长 按 误 判 为 关 机 。 */
void Power_Key_ConsumePress ( void )
{
s_isr_fired = 1 ;
}
2026-09-14 20:19:45 +08:00
void Power_Key_IsrTick ( void )
{
2026-09-14 21:49:16 +08:00
flag_status st ;
/* RTT/USB 烧 ExtFlash 期间禁止软关机,避免长按/粘键打断 FLASH_ACK */
if ( app_log_flash_busy ( ) )
return ;
st = BSP_GetPowerKey ( ) ;
2026-09-14 20:19:45 +08:00
if ( st = = KEY_PRESS_LEVEL )
{
2026-09-16 15:10:34 +08:00
if ( s_isr_hold_ms < 60000u )
s_isr_hold_ms + + ;
2026-09-14 20:19:45 +08:00
}
else
{
2026-09-16 15:10:34 +08:00
s_isr_hold_ms = 0 ;
s_isr_fired = 0 ;
2026-09-14 20:19:45 +08:00
}
/* 仅处理「开机态关机」;开机仍由主循环负责(需完整外设初始化) */
2026-09-16 15:10:34 +08:00
if ( powon & & ! s_isr_fired & & s_isr_hold_ms > = PWR_OFF_HOLD_MS )
2026-09-14 20:19:45 +08:00
{
2026-09-16 15:10:34 +08:00
s_isr_fired = 1 ;
2026-09-14 20:19:45 +08:00
System_RequestPowerOff ( ) ;
}
if ( s_pwr_off_grace_ms > 0u )
{
s_pwr_off_grace_ms - - ;
if ( s_pwr_off_grace_ms = = 0u & & ! s_pwr_rails_cut )
PowerOff_EmergencyReset ( ) ;
}
}
/* HardFault / MemManage / BusFault / UsageFault: 死循环中仍可长按关机 */
void Power_Key_FaultLoop ( void )
{
uint32_t hold = 0 ;
for ( ; ; )
{
if ( BSP_GetPowerKey ( ) = = KEY_PRESS_LEVEL )
{
hold + + ;
if ( hold > = PWR_OFF_HOLD_MS )
{
PowerOff_EmergencyReset ( ) ;
}
}
else
{
hold = 0 ;
}
Delay_us ( 1000 ) ;
}
}
static rt_err_t Power_HardFault_Hook ( void * context )
{
( void ) context ;
Power_Key_FaultLoop ( ) ;
return - RT_ERROR ; /* 不可达 */
}
void Power_Key_WatchdogInit ( void )
{
rt_hw_exception_install ( Power_HardFault_Hook ) ;
wk_tmr7_pwrkey_init ( ) ;
}
2026-08-26 07:26:58 +08:00
void Power_Key_Scan ( )
{
flag_status status = BSP_GetPowerKey ( ) ;
uint32_t now = rt_tick_get ( ) ;
uint32_t holdtick = 0 ;
2026-09-05 19:58:42 +08:00
/* 复位/烧录后:无 USB 则自动开机一次;有 Type-C 则进关机充电画面(勿进 Logo) */
2026-08-31 08:20:24 +08:00
if ( powon = = false & & pwr_key_state = = KEY_STATE_IDLE & & pwr_press_tick = = 0 )
{
static uint8_t s_auto_poweron_pending = 1 ;
if ( s_auto_poweron_pending )
{
2026-09-05 19:58:42 +08:00
uint8_t i ;
2026-08-31 08:20:24 +08:00
s_auto_poweron_pending = 0 ;
2026-09-05 19:58:42 +08:00
/* 多采几次 USB 检测,避免上电瞬间误判 */
for ( i = 0 ; i < 8 ; i + + )
{
app_adc_in1_scan_part ( 4 ) ;
if ( usb_charging_state | | app_adc_usb_is_plugged ( ) )
break ;
rt_thread_mdelay ( 5 ) ;
}
if ( usb_charging_state | | app_adc_usb_is_plugged ( ) )
{
usb_charging_state = 1 ;
usb_charging_laststate = 1 ;
LOG_I ( " PWR " , " boot with USB → charge UI (no logo) " ) ;
CurrUIProcress = IdleProcess ;
MainTask_Sendmsg ( MSG_ID_POWOFF_CHARG , percentage , 1 , 0 ) ;
pwr_key_state = KEY_STATE_NONE ;
return ;
}
2026-08-31 11:24:10 +08:00
LOG_I ( " PWR " , " auto power_on " ) ;
2026-08-31 08:20:24 +08:00
PowerOn ( ) ;
pwr_key_state = KEY_STATE_NONE ; /* 忽略开机瞬间可能残留的按键电平 */
return ;
}
}
2026-08-26 07:26:58 +08:00
switch ( pwr_key_state )
{
case KEY_STATE_IDLE :
if ( status = = KEY_PRESS_LEVEL )
{
pwr_press_tick = now ;
pwr_key_state = KEY_STATE_PRESS ;
}
break ;
case KEY_STATE_PRESS :
if ( status ! = KEY_PRESS_LEVEL ) /* 提前松开,放弃 */
{
pwr_press_tick = 0 ;
pwr_key_state = KEY_STATE_IDLE ;
break ;
}
holdtick = now - pwr_press_tick ;
2026-09-14 20:19:45 +08:00
/* 关机长按 3s 开机;开机态关机改由 TMR7 ISR( 主循环卡死仍可关) */
2026-08-26 07:26:58 +08:00
if ( powon = = false & & holdtick > = PWR_ON_HOLD_MS )
{
2026-08-31 11:24:10 +08:00
LOG_I ( " PWR " , " key power_on hold=%u " , ( unsigned ) holdtick ) ;
2026-08-26 07:26:58 +08:00
PowerOn ( ) ;
pwr_key_state = KEY_STATE_NONE ;
}
break ;
case KEY_STATE_NONE :
if ( status ! = KEY_PRESS_LEVEL ) /* 松开后复位,允许下次触发 */
{
pwr_key_state = KEY_STATE_IDLE ;
pwr_press_tick = 0 ;
}
break ;
}
}
/* ============================================================
* 缺 失 符 号 补 定 义 ( AutoBandTop1 模 块 源 码 缺 失 , 这 些 是 它 依 赖 的 变 量 )
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
/* UI 当前处理函数指针( UI_global.c extern 引用) */
void ( * CurrUIProcress ) ( DisplayTaskMessage_Type xEvent ) ;
/* UI 公共全局变量 */
2026-09-04 11:12:22 +08:00
volatile uint32_t ADDRESS = FLASH_ADDR_MODE_UNIVERSAL ;
2026-08-26 07:26:58 +08:00
uint8_t KEY_ID_1629 = 0 ;
bool StartFlag = false ;
bool InModeFlag = false ;
uint8_t key_toggle [ 4 ] = { 0 } ;
/* ============================================================
* UI 公 共 函 数 补 定 义
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
2026-09-05 18:13:31 +08:00
/* 自动关机:有操作时复位秒计数;主循环每秒递减,到 0 关机 */
2026-08-26 07:26:58 +08:00
void ResetAutoPowerCount ( void )
{
2026-09-05 18:13:31 +08:00
if ( ! AutoCloseFlag | | AutoCloseTime = = 0 )
return ;
mAutoPowerOffCount = ( uint16_t ) ( AutoCloseTime * 60 ) ;
}
void AutoPowerOff_Scan ( void )
{
static uint32_t s_last_sec_tick = 0 ;
uint32_t now ;
if ( ! powon | | ! AutoCloseFlag | | AutoCloseTime = = 0 )
return ;
now = rt_tick_get ( ) ;
if ( s_last_sec_tick = = 0 )
{
s_last_sec_tick = now ;
return ;
}
if ( ( now - s_last_sec_tick ) < RT_TICK_PER_SECOND )
return ;
s_last_sec_tick = now ;
if ( mAutoPowerOffCount > 0 )
mAutoPowerOffCount - - ;
if ( mAutoPowerOffCount = = 0 )
{
LOG_I ( " PWR " , " auto power_off idle=%umin " , ( unsigned ) AutoCloseTime ) ;
PowerOff ( ) ;
}
2026-08-26 07:26:58 +08:00
}
/* GUI 数值调整(简单实现:更新当前值) */
uint8_t GUI_Item_AjustValue ( GUI_SWITCH * Group , int8_t Dir )
{
uint8_t ret = 0 ;
if ( Dir > 0 ) {
if ( Group - > Current < Group - > Max ) {
Group - > Current + + ;
ret = 1 ;
}
} else if ( Dir < 0 ) {
if ( Group - > Current > Group - > Min ) {
Group - > Current - - ;
ret = 1 ;
}
}
return ret ;
}
void GUI_Item_AjustLoopValue ( GUI_SWITCH * Group , int8_t Dir )
{
if ( Dir > 0 ) {
if ( Group - > Current < Group - > Max ) {
Group - > Current + + ;
} else {
Group - > Current = Group - > Min ;
}
} else if ( Dir < 0 ) {
if ( Group - > Current > Group - > Min ) {
Group - > Current - - ;
} else {
Group - > Current = Group - > Max ;
}
}
}
void Send_volume ( uint8_t vol )
{
uint8_t mMasterSysExVolume [ 12 ] = { 0xF0 , 0x41 , 0x00 , 0x42 , 0x12 , 0x40 , 0x00 , 0x04 , 0x00 , 0x00 , 0x00 , 0xF7 } ;
memset ( mMasterSysExVolume + 8 , vol , 1 ) ;
SendMidiDataToDreamDSP ( mMasterSysExVolume , 12 ) ;
}
2026-09-10 18:58:24 +08:00
/* 重发当前主音量:开机补发 / 进模式主页 / 开始播放前调用。
* Dream 上 电 早 期 可 能 丢 SysEx , 丢 失 后 旋 钮 不 动 就 不 再 重 发 ,
* 会 出 现 " 图标低音量、实际音量大 " ; 在 关 键 时 机 重 发 可 自 愈 */
void MasterVolume_Reapply ( void )
{
uint8_t vol = app_adc_get_volume ( ) ;
app_adc_sync_volume ( vol ) ; /* 兜底刷新 level, 保证图标与下发值一致 */
Send_volume ( vol ) ;
}
2026-09-17 16:18:14 +08:00
/* 当前调一级大和弦: 把位1 + maj + 自然音(再经 GUI_TRANSPOSE 移调 → C调即 do mi sol) */
void Accomp_ApplyDefaultIChord ( void )
{
KEY_ID_1629 = 1 ;
chord_type_index_map [ 1 ] . type = 0 ; /* maj */
chord_type_index_map [ 1 ] . PitchOffset = 1 ; /* 自然,非 # */
PressFlag = 0 ;
}
2026-09-04 17:06:02 +08:00
void Accomp_UpdatePlayingChord ( void )
{
uint8_t midi_note_buff [ 4 ] = { 0 , } ;
uint8_t note_cnt = 0 ;
uint8_t i ;
if ( ! StartFlag )
return ;
if ( KEY_ID_1629 = = 0 | | KEY_ID_1629 = = 22 | | KEY_ID_1629 = = 23 )
2026-09-17 16:18:14 +08:00
Accomp_ApplyDefaultIChord ( ) ;
2026-09-04 17:06:02 +08:00
note_cnt = GetChordNotesByType ( KEY_ID_1629 ,
chord_type_index_map [ KEY_ID_1629 ] . type ,
midi_note_buff ) ;
for ( i = 0 ; i < note_cnt ; i + + )
AutoBandTop1_Note_On ( midi_note_buff [ i ] , 0x50 ) ;
}
2026-09-21 15:13:53 +08:00
/* 尾奏期间冻结走向灯:保持全绿,避免 LOOP_LIGHT 按小节刷红 */
static uint8_t s_outro_led_all_green = 0 ;
2026-09-14 12:24:35 +08:00
/* 尾奏起奏: Postamble(0) + 同步起奏 + 当前和弦 Note_On。
* 供 ADC 独 立 尾 奏 键 与 万 能 模 式 第 4 段 落 键 共 用 。
* 万 能 走 piece 循 环 , 须 先 退 出 piece , 否 则 Postamble 舞 台 切 不 过 去 ;
* 尾 奏 MIDI 须 由 音 师 写 入 3. bin ( 见 Doc / 音 师 需 求 _万能3 . bin尾奏_20260914 . md ) 。 */
void AutoBand_StartOutro ( void )
{
uint8_t midi_note_buff [ 4 ] = { 0 , } ;
uint8_t note_cnt = 0 ;
2026-09-17 16:18:14 +08:00
if ( KEY_ID_1629 = = 0 | | KEY_ID_1629 = = 22 | | KEY_ID_1629 = = 23 )
Accomp_ApplyDefaultIChord ( ) ;
2026-09-14 12:24:35 +08:00
/* 退出和弦走向 piece, 再进风格尾奏舞台 */
AutoBandTop1_SetPiecePlayMode ( 0 , 0 , 0 ) ;
AutoBandTop1_Postamble ( 0 ) ;
2026-09-21 15:13:53 +08:00
/* 尾奏:指板 LED 全绿(停走 LOOP 走向红灯) */
s_outro_led_all_green = 1 ;
app_tm1629_all_on ( LED_COLOR_G ) ;
2026-09-14 12:24:35 +08:00
/* 尾奏起奏前补发主音量,保证与图标一致 */
MasterVolume_Reapply ( ) ;
AutoBandTop1_SyncStart ( ) ;
wk_delay_ms ( 50 ) ;
note_cnt = GetChordNotesByType ( KEY_ID_1629 , chord_type_index_map [ KEY_ID_1629 ] . type , midi_note_buff ) ;
for ( uint8_t i = 0 ; i < note_cnt ; i + + )
{
AutoBandTop1_Note_On ( midi_note_buff [ i ] , 0x50 ) ;
}
}
2026-08-26 07:26:58 +08:00
void ADC_IN1_KEY_Handle ( uint8_t key , bool on )
{
uint8_t midi_note_buff [ 4 ] = { 0 , } ;
uint8_t note_cnt = 0 ;
2026-09-04 17:06:02 +08:00
uint8_t is_preamble = 0 ;
2026-08-26 07:26:58 +08:00
key_toggle [ key ] = ! key_toggle [ key ] ;
if ( key_toggle [ key ] = = 1 )
{
//ResetAutoPowerCount();
2026-09-14 12:24:35 +08:00
if ( key = = 1 )
2026-08-26 07:26:58 +08:00
{
2026-09-14 12:24:35 +08:00
/* 尾奏: 与万能模式第4键共用同一起奏路径 */
AutoBand_StartOutro ( ) ;
2026-08-26 07:26:58 +08:00
}
2026-09-14 12:24:35 +08:00
else
2026-09-04 17:06:02 +08:00
{
2026-09-21 15:13:53 +08:00
s_outro_led_all_green = 0 ; /* 前奏/断奏:恢复走向灯 */
2026-09-14 12:24:35 +08:00
switch ( key )
{
case 3 : AutoBandTop1_Preamble ( 0 ) ; is_preamble = 1 ; break ;
case 0 : AutoBandTop1_Preamble ( 1 ) ; is_preamble = 1 ; break ;
case 2 : AutoBandTop1_Postamble ( 1 ) ; break ;
}
2026-09-17 16:18:14 +08:00
/* 前奏:须在 SyncStart 前落到当前调一级,否则前 50ms 伴奏按旧把位进 */
if ( is_preamble )
Accomp_ApplyDefaultIChord ( ) ;
else if ( KEY_ID_1629 = = 0 | | KEY_ID_1629 = = 22 | | KEY_ID_1629 = = 23 )
Accomp_ApplyDefaultIChord ( ) ;
2026-09-14 12:24:35 +08:00
/* 前奏/断奏起奏前补发主音量,保证与图标一致 */
MasterVolume_Reapply ( ) ;
//AutoBandTop1_Start();
AutoBandTop1_SyncStart ( ) ;
wk_delay_ms ( 50 ) ;
note_cnt = GetChordNotesByType ( KEY_ID_1629 , chord_type_index_map [ KEY_ID_1629 ] . type , midi_note_buff ) ;
for ( uint8_t i = 0 ; i < note_cnt ; i + + )
{
AutoBandTop1_Note_On ( midi_note_buff [ i ] , 0x50 ) ;
}
2026-09-17 16:18:14 +08:00
if ( is_preamble )
StartFlag = 1 ;
2026-08-26 07:26:58 +08:00
}
}
else
{
Stop_AutoBand ( ) ;
//StartFlag = 0;
//tick_ever_positive = false;
for ( uint8_t i = 0 ; i < 4 ; i + + ) key_toggle [ i ] = 0 ;
}
}
void TM1617_Handle ( uint8_t key )
{
if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 3 )
{
uint8_t value = 0 ;
switch ( key )
{
case 0 : value = 0 ; break ;
case 1 : value = 1 ; break ;
case 2 : value = 2 ; break ;
case 3 : value = 3 ; break ;
}
mGuiData [ GUI_PLAY_SECTION ] . Current = value ;
//AutoBandTop1_SetRunVar(mGuiData[GUI_PLAY_SECTION].Current);
//Refresh_SectionSelect(last_value, &UI_Label[GUI_PLAY_SECTION], &mGuiData[GUI_PLAY_SECTION]);
//Draw_Tab_Section_Menu(&UI_Label[GUI_PLAY_SECTION], mGuiData[GUI_PLAY_SECTION].Current);
} else
{
switch ( key )
{
case 0 : MainTask_Sendmsg ( MSG_ID_TOUCH , 1 , 40 , 255 ) ; break ;
case 1 : MainTask_Sendmsg ( MSG_ID_TOUCH , 1 , 90 , 255 ) ; break ;
case 2 : MainTask_Sendmsg ( MSG_ID_TOUCH , 1 , 140 , 255 ) ; break ;
case 3 : MainTask_Sendmsg ( MSG_ID_TOUCH , 1 , 200 , 255 ) ; break ;
}
}
}
void Tap_to_Speed ( )
{
# define TAP_MIN_BPM 40 // 鏈€灏忛€熷害
# define TAP_MAX_BPM 260 // 鏈€澶ч€熷害
# define TAP_TIMEOUT_MS 1000 // 2绉掓病鏁插氨澶嶄綅
static int32_t tap_last_time = 0 ; // 涓婃鎸夐敭鏃堕棿
static uint8_t tap_state = 0 ; // 0=绌洪棽 1=宸叉暡涓€娆?
static uint16_t ui_tap_tempo = 120 ; // 鏈€缁堥€熷害 40~260
uint32_t now = rt_tick_get ( ) ;
if ( now - tap_last_time > TAP_TIMEOUT_MS ) {
tap_state = 0 ;
}
if ( tap_state = = 0 ) {
tap_last_time = now ;
tap_state = 1 ;
uint8_t AllNoteOff_buff [ 3 ] = { 0x99 , 0x3C , 0x50 } ;
SendMidiDataToDreamDSP ( AllNoteOff_buff , sizeof ( AllNoteOff_buff ) ) ;
}
else {
uint32_t delta = now - tap_last_time ;
if ( delta > = 200 & & delta < = 1500 )
{
ui_tap_tempo = 60000 / delta ;
if ( ui_tap_tempo < TAP_MIN_BPM ) ui_tap_tempo = TAP_MIN_BPM ;
if ( ui_tap_tempo > TAP_MAX_BPM ) ui_tap_tempo = TAP_MAX_BPM ;
MainTask_Sendmsg ( MSG_ID_TAP_SPEED , ui_tap_tempo , 0 , 0 ) ;
uint8_t AllNoteOff_buff [ 3 ] = { 0x99 , 0x3C , 0x50 } ;
SendMidiDataToDreamDSP ( AllNoteOff_buff , sizeof ( AllNoteOff_buff ) ) ;
}
tap_state = 0 ;
}
}
2026-09-21 15:23:10 +08:00
/* 普通模式松手: 对齐到下一偶拍末再停( 拍1→拍2末, 拍3→拍4末) 。
* piece 窗 口 1920 = 4 拍 , 每 拍 480 tick ( 与 起 奏 SetPiecePlayMode ( 1 , 0 , 1920 ) 一 致 ) 。 */
# define FREE_BEAT_TICKS 480u
# define FREE_PIECE_TICKS 1920u
static uint8_t s_free_even_stop_arm = 0 ;
static uint64_t s_free_even_stop_tick = 0 ;
static int s_free_even_stop_beat = 2 ; /* 目标偶拍 2 或 4 */
static uint8_t s_free_even_seen_target = 0 ;
static void FreeMode_CancelEvenBeatStop ( void )
{
s_free_even_stop_arm = 0 ;
s_free_even_stop_tick = 0 ;
s_free_even_stop_beat = 2 ;
s_free_even_seen_target = 0 ;
}
/* 再次按住指板:取消待停并恢复 piece 循环 */
static void FreeMode_ResumePieceLoop ( void )
{
if ( ! s_free_even_stop_arm )
return ;
FreeMode_CancelEvenBeatStop ( ) ;
if ( StartFlag & & mGuiData [ GUI_TAB_INDEX ] . Current = = 2 & &
! mGuiData [ GUI_AUTOBAND_SW ] . Current )
{
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , FREE_PIECE_TICKS ) ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
}
}
static void FreeMode_ArmEvenBeatStop ( void )
{
uint64_t play ;
int beat ;
int target ;
if ( ! StartFlag )
{
FreeMode_CancelEvenBeatStop ( ) ;
return ;
}
play = AutoBandTop1_GetCurrentPlayTick ( ) ;
beat = AutoBandTop1_GetCurrentBeat ( ) ;
if ( beat < 1 )
beat = 1 ;
/* 拍1→2, 拍2→2, 拍3→4, 拍4→4 */
target = ( ( beat + 1 ) / 2 ) * 2 ;
s_free_even_stop_beat = target ;
s_free_even_seen_target = ( beat > = target ) ? 1u : 0u ;
/* ((beat0/2)+1)*2*480 → 拍1/2→960, 拍3/4→1920, 跨小节同理 */
s_free_even_stop_tick =
( ( play / FREE_BEAT_TICKS ) / 2ull + 1ull ) * 2ull * ( uint64_t ) FREE_BEAT_TICKS ;
s_free_even_stop_arm = 1 ;
/* 禁循环并裁 piece, 库播到 stop tick 自停; Poll 作兜底 */
AutoBandTop1_SetLoopMode ( 0 , 0 ) ;
if ( s_free_even_stop_tick < = 0xFFFFFFFFull )
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , ( uint32_t ) s_free_even_stop_tick ) ;
LOG_I ( " pick " , " free even-stop arm beat=%d target=%d play=%u stop=%u " ,
beat , target , ( unsigned ) play , ( unsigned ) s_free_even_stop_tick ) ;
}
void FreeMode_PollEvenBeatStop ( void )
{
int beat ;
uint8_t tick_done ;
uint8_t beat_done ;
if ( ! s_free_even_stop_arm )
return ;
if ( PressFlag | | mGuiData [ GUI_TAB_INDEX ] . Current ! = 2 | | ! StartFlag )
{
FreeMode_CancelEvenBeatStop ( ) ;
return ;
}
beat = AutoBandTop1_GetCurrentBeat ( ) ;
if ( beat = = s_free_even_stop_beat )
s_free_even_seen_target = 1 ;
/* 已到达目标偶拍后离开该拍 = 该拍播完;或 play tick 到点 */
beat_done = ( s_free_even_seen_target & & beat ! = s_free_even_stop_beat & & beat ! = 0 ) ? 1u : 0u ;
tick_done = ( AutoBandTop1_GetCurrentPlayTick ( ) > = s_free_even_stop_tick ) ? 1u : 0u ;
if ( tick_done | | beat_done )
{
FreeMode_CancelEvenBeatStop ( ) ;
Stop_AutoBand ( ) ;
LOG_I ( " pick " , " free even-stop done beat=%d tick=%u beatDone=%u " ,
beat , ( unsigned ) tick_done , ( unsigned ) beat_done ) ;
}
}
2026-08-26 07:26:58 +08:00
void Stop_AutoBand ( )
{
2026-09-21 15:23:10 +08:00
FreeMode_CancelEvenBeatStop ( ) ;
2026-08-26 07:26:58 +08:00
AutoBandTop1_Stop ( ) ;
for ( uint8_t i = 0 ; i < 16 ; i + + )
{
2026-09-17 16:05:13 +08:00
uint8_t AllNoteOff_buff [ 3 ] = { ( uint8_t ) ( 0xB0 | i ) , 0x7B , 0x00 } ;
2026-08-26 07:26:58 +08:00
SendMidiDataToDreamDSP ( AllNoteOff_buff , sizeof ( AllNoteOff_buff ) ) ;
2026-09-17 16:05:13 +08:00
uint8_t AllControlOff_buff [ 3 ] = { ( uint8_t ) ( 0xB0 | i ) , 0x79 , 0x00 } ;
2026-08-26 07:26:58 +08:00
SendMidiDataToDreamDSP ( AllControlOff_buff , sizeof ( AllControlOff_buff ) ) ;
}
StartFlag = 0 ;
2026-09-21 15:52:58 +08:00
KEY_ID_1629 = 0 ; /* 停奏后再清,避免收束段音区映射突变 */
2026-09-21 15:13:53 +08:00
s_outro_led_all_green = 0 ;
2026-08-26 07:26:58 +08:00
//tick_ever_positive = false;
for ( uint8_t i = 0 ; i < 4 ; i + + ) key_toggle [ i ] = 0 ;
}
uint8_t BP_Transpose ( uint8_t key_idx )
{
if ( pick_hold_up )
{
/* 快速移调模式:按键只用于选择移调值,不切换和弦指型 */
uint8_t new_trans = 15 ;
switch ( key_idx )
{
case 2 : new_trans = 0 ; break ;
case 3 : new_trans = 1 ; break ;
case 5 : new_trans = 2 ; break ;
case 6 : new_trans = 3 ; break ;
case 8 : new_trans = 4 ; break ;
case 9 : new_trans = 5 ; break ;
case 11 : new_trans = 5 ; break ;
case 12 : new_trans = 6 ; break ;
case 14 : new_trans = 7 ; break ;
case 15 : new_trans = 8 ; break ;
case 17 : new_trans = 9 ; break ;
case 18 : new_trans = 10 ; break ;
case 20 : new_trans = 11 ; break ;
}
if ( new_trans ! = 15 )
{
MainTask_Sendmsg ( MSG_ID_BP_TRANSPOSE , new_trans , 0 , 0 ) ;
return 1 ;
}
}
return 0 ;
}
2026-09-15 19:13:10 +08:00
/* 伴奏活动跟踪: AutoBand PlayTick 前进即视为播放中。
* 供 专 业 模 式 指 板 键 区 分 「 播 放 中 换 和 弦 」 与 「 段 落 结 束 后 续 奏 重 启 」 。 */
static uint64_t s_play_tick_last = 0 ;
static uint32_t s_play_adv_tick = 0 ; /* 最近一次 PlayTick 前进的系统 tick */
void Band_TrackPlayTick ( void )
{
uint64_t pt ;
if ( ! StartFlag )
{
s_play_tick_last = 0 ;
s_play_adv_tick = 0 ;
return ;
}
pt = AutoBandTop1_GetCurrentPlayTick ( ) ;
if ( pt ! = s_play_tick_last )
{
s_play_tick_last = pt ;
s_play_adv_tick = rt_tick_get ( ) ;
}
}
2026-09-21 15:13:53 +08:00
/* 段落键 SetRunVar 后 PlayTick 可能短暂不变;主动刷新以免误判为已停。 */
void Band_NotifyPlayActivity ( void )
{
if ( ! StartFlag )
return ;
s_play_tick_last = AutoBandTop1_GetCurrentPlayTick ( ) ;
s_play_adv_tick = rt_tick_get ( ) ;
}
2026-09-15 19:13:10 +08:00
uint8_t Band_IsPlaying ( void )
{
if ( ! StartFlag | | s_play_adv_tick = = 0 )
return 0 ;
return ( rt_tick_get ( ) - s_play_adv_tick ) < ( RT_TICK_PER_SECOND / 4 ) ;
}
/* 专业模式公共起奏:拨片首次起奏与起奏后的指板续奏共用。
* 节 奏 库 走 piece 循 环 ; 本 地 曲 目 按 当 前 段 落 走 StartWithNote 。 */
static void Expert_StartWithChord ( uint8_t chord_id , uint8_t * midi_note_buff ,
uint8_t note_cnt , const char * trigger )
{
uint8_t midi = 0 ;
2026-09-21 15:13:53 +08:00
uint8_t session = StartFlag ? 1u : 0u ;
2026-09-15 19:13:10 +08:00
StartFlag = 1 ;
if ( mGuiData [ GUI_AUTOBAND_SW ] . Current )
{
2026-09-21 15:13:53 +08:00
/* 会话内勿 Stop: 段落圆键切变奏后指板续奏若 Stop 会直接关音;
* 与 节 奏 类 型 一 致 , 仅 换 和 弦 / 重 进 当 前 段 。 冷 启 动 仍 Stop 清 残 留 。 */
if ( ! session )
AutoBandTop1_Stop ( ) ;
2026-09-15 19:13:10 +08:00
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
AutoBandTop1_SetRunVar ( mGuiData [ GUI_PLAY_SECTION ] . Current ) ;
midi = 0x3C + mGuiData [ GUI_TRANSPOSE ] . Current ;
AutoBandTop1_StartWithNote ( midi ) ;
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
2026-09-21 15:13:53 +08:00
Band_NotifyPlayActivity ( ) ;
2026-09-15 19:13:10 +08:00
LOG_I ( " pick " , " local/expert root=%u name=%s sw=%u tab=%u trigger=%s chord=%u " ,
midi ,
AutoBandTop1_GetPresetName ( ) ? AutoBandTop1_GetPresetName ( ) : " ? " ,
( unsigned ) mGuiData [ GUI_AUTOBAND_SW ] . Current ,
( unsigned ) mGuiData [ GUI_TAB_INDEX ] . Current ,
trigger ,
( unsigned ) chord_id ) ;
}
else
{
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , 1920 ) ;
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
2026-09-21 15:13:53 +08:00
Band_NotifyPlayActivity ( ) ;
2026-09-15 19:13:10 +08:00
LOG_I ( " pick " , " expert/rhythm piece name=%s trigger=%s chord=%u " ,
AutoBandTop1_GetPresetName ( ) ? AutoBandTop1_GetPresetName ( ) : " ? " ,
trigger ,
( unsigned ) chord_id ) ;
}
}
2026-08-26 07:26:58 +08:00
void TM1629_Handle ( uint8_t key )
{
STRING_MIDI * MIDI ;
const STRING_MIDI * ORGAN_MIDI ;
2026-09-14 12:36:39 +08:00
/* 万能:指板不改和弦、不启停伴奏;保留拍速/停止 */
if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 0 )
{
if ( key = = 22 ) { Tap_to_Speed ( ) ; return ; }
if ( key = = 23 ) { Stop_AutoBand ( ) ; return ; }
return ; /* 忽略 0~21: 不改 KEY_ID/USE_MIDI/PressFlag, 不启停 OutTime_Stop */
}
2026-08-26 07:26:58 +08:00
if ( BP_Transpose ( key ) ) return ;
switch ( key )
{
2026-09-14 12:55:22 +08:00
case 0 :
2026-08-26 07:26:58 +08:00
PressFlag = 0 ;
2026-09-14 12:55:22 +08:00
if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 2 )
{
2026-09-21 15:52:58 +08:00
/* 普通:松手对齐偶拍末再停。
* 收 束 前 勿 清 KEY_ID : App_Auto 音 区 指 纹 依 赖 指 板 键 , 清 零 会 关 掉 III + 的 - 12 ,
* 表 现 为 停 前 音 高 突 然 升 高 。 */
2026-09-14 12:55:22 +08:00
if ( StartFlag )
2026-09-21 15:23:10 +08:00
FreeMode_ArmEvenBeatStop ( ) ;
2026-09-21 15:52:58 +08:00
else
KEY_ID_1629 = 0 ;
2026-09-14 12:55:22 +08:00
}
else if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 0 )
2026-08-26 07:26:58 +08:00
{
//StartFlag = 0;
//tick_ever_positive = false;
//osTimerStart(Delay_stop_timer,1000);
rt_timer_start ( OutTime_Stop_timer ) ;
2026-09-14 12:55:22 +08:00
}
2026-08-26 07:26:58 +08:00
break ;
case 22 : Tap_to_Speed ( ) ; break ;
case 23 : Stop_AutoBand ( ) ; break ;
2026-09-14 12:55:22 +08:00
default : // 1 - 21
if ( key > = 1 & & key < = 21 )
2026-08-26 07:26:58 +08:00
{
if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 0 )
{
PressFlag = 1 ;
rt_timer_stop ( OutTime_Stop_timer ) ;
//osTimerStop(Delay_stop_timer);
}
2026-09-14 12:55:22 +08:00
else if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 2 )
{
2026-09-21 15:23:10 +08:00
/* 普通:按住指板;若正在偶拍收束则恢复循环续奏 */
FreeMode_ResumePieceLoop ( ) ;
2026-09-14 12:55:22 +08:00
PressFlag = 1 ;
}
2026-08-26 07:26:58 +08:00
uint8_t group = ( key - 1 ) / 3 ; /* 1~3→0, 4~6→1, ..., 19~21→6 */
KEY_ID_1629 = key ;
MIDI = ( STRING_MIDI * ) Chord_Midi_Table [ group ] ;
ORGAN_MIDI = OrganChord_Midi_Table [ group ] ;
uint8_t type = chord_type_index_map [ key ] . type ;
switch ( chord_type_index_map [ key ] . PitchOffset )
{
case 1 :
USE_MIDI . Midi_1 = ORGAN_MIDI [ type ] . Midi_1 ;
USE_MIDI . Midi_2 = ORGAN_MIDI [ type ] . Midi_2 ;
USE_MIDI . Midi_3 = ORGAN_MIDI [ type ] . Midi_3 ;
USE_MIDI . Midi_4 = ORGAN_MIDI [ type ] . Midi_4 ;
USE_MIDI . Midi_5 = ORGAN_MIDI [ type ] . Midi_5 ;
USE_MIDI . Midi_6 = ORGAN_MIDI [ type ] . Midi_6 ;
break ;
case 2 :
USE_MIDI . Midi_1 = MIDI [ type ] . Midi_1 ;
USE_MIDI . Midi_2 = MIDI [ type ] . Midi_2 ;
USE_MIDI . Midi_3 = MIDI [ type ] . Midi_3 ;
USE_MIDI . Midi_4 = MIDI [ type ] . Midi_4 ;
USE_MIDI . Midi_5 = MIDI [ type ] . Midi_5 ;
USE_MIDI . Midi_6 = MIDI [ type ] . Midi_6 ;
break ;
}
USE_MIDI . Midi_1 + = mGuiData [ GUI_TRANSPOSE ] . Current ;
USE_MIDI . Midi_2 + = mGuiData [ GUI_TRANSPOSE ] . Current ;
USE_MIDI . Midi_3 + = mGuiData [ GUI_TRANSPOSE ] . Current ;
USE_MIDI . Midi_4 + = mGuiData [ GUI_TRANSPOSE ] . Current ;
USE_MIDI . Midi_5 + = mGuiData [ GUI_TRANSPOSE ] . Current ;
USE_MIDI . Midi_6 + = mGuiData [ GUI_TRANSPOSE ] . Current ;
2026-09-15 19:13:10 +08:00
if ( mGuiData [ GUI_TAB_INDEX ] . Current = = 1 & & StartFlag )
2026-08-26 07:26:58 +08:00
{
2026-09-15 19:13:10 +08:00
/* 不按 Last_Tm1926D_Key 去重: App 曲目相邻两节可能是同一和弦键,
* 松 手 再 按 同 一 键 也 必 须 能 续 奏 ( 与 「 指 板 + 拨 片 」 一 致 , 拨 片 本 无 去 重 ) */
2026-09-17 07:05:04 +08:00
if ( ! Band_IsPlaying ( ) )
2026-08-26 07:26:58 +08:00
{
2026-09-21 15:13:53 +08:00
/* 节奏:段落自然停后指板续奏 → piece 重启。
* 本 地 曲 : 圆 键 切 段 后 PlayTick 常 短 暂 停 滞 , 若 走 Stop + Start 会 关 音 ;
* 与 节 奏 「 播 放 中 换 把 」 一 致 , 仅 换 和 弦 音 持 续 播 放 。 段 落 后 续 奏 用 拨 片 / 再 按 圆 键 。 */
if ( mGuiData [ GUI_AUTOBAND_SW ] . Current )
{
uint8_t midi_note_buff [ 4 ] = { 0 , } ;
uint8_t note_cnt = GetChordNotesByType ( KEY_ID_1629 , chord_type_index_map [ KEY_ID_1629 ] . type , midi_note_buff ) ;
for ( uint8_t i = 0 ; i < note_cnt ; i + + )
AutoBandTop1_Note_On ( midi_note_buff [ i ] , 0x50 ) ;
Return_Light_buff ( KEY_ID_1629 ) ;
Band_NotifyPlayActivity ( ) ;
LOG_I ( " pick " , " expert/fret change(local) chord=%u " , ( unsigned ) KEY_ID_1629 ) ;
}
else
{
MainTask_Sendmsg ( MSG_ID_BP_KEY , 0 , 0 , 0 ) ;
LOG_I ( " pick " , " expert/fret continue chord=%u " , ( unsigned ) KEY_ID_1629 ) ;
}
2026-09-15 19:13:10 +08:00
}
else
{
2026-09-17 07:05:04 +08:00
/* 播放中(节奏库/本地曲):仅换和弦音,不重启段落 */
2026-09-15 19:13:10 +08:00
uint8_t midi_note_buff [ 4 ] = { 0 , } ;
uint8_t note_cnt = GetChordNotesByType ( KEY_ID_1629 , chord_type_index_map [ KEY_ID_1629 ] . type , midi_note_buff ) ;
for ( uint8_t i = 0 ; i < note_cnt ; i + + )
{
AutoBandTop1_Note_On ( midi_note_buff [ i ] , 0x50 ) ;
}
Return_Light_buff ( KEY_ID_1629 ) ;
LOG_I ( " pick " , " expert/fret change chord=%u " , ( unsigned ) KEY_ID_1629 ) ;
2026-08-26 07:26:58 +08:00
}
}
}
break ;
}
}
void Return_Light_buff ( uint8_t key_id_1629 )
{
uint8_t buff [ 7 ] = { 0xF0 , 0x51 , 0x04 , 0x01 , 0x01 , 0x08 , 0xF7 } ;
switch ( key_id_1629 )
{
case 1 : case 2 : case 3 : buff [ 3 ] = 0x00 ; break ;
case 4 : case 5 : case 6 : buff [ 3 ] = 0x01 ; break ;
case 7 : case 8 : case 9 : buff [ 3 ] = 0x02 ; break ;
case 10 : case 11 : case 12 : buff [ 3 ] = 0x03 ; break ;
case 13 : case 14 : case 15 : buff [ 3 ] = 0x04 ; break ;
case 16 : case 17 : case 18 : buff [ 3 ] = 0x05 ; break ;
case 19 : case 20 : case 21 : buff [ 3 ] = 0x06 ; break ;
}
switch ( chord_type_index_map [ key_id_1629 ] . type )
{
case 0 : buff [ 5 ] = 0 ; break ;
case 1 : buff [ 5 ] = 8 ; break ;
case 2 : buff [ 5 ] = 2 ; break ;
case 3 : buff [ 5 ] = 13 ; break ;
case 4 : buff [ 5 ] = 10 ; break ;
case 5 : buff [ 5 ] = 31 ; break ;
case 6 : buff [ 5 ] = 30 ; break ;
case 7 : buff [ 5 ] = 4 ; break ;
case 8 : buff [ 5 ] = 11 ; break ;
}
if ( Led = = buff [ 3 ] )
USART4_SendData ( buff , sizeof ( buff ) ) ;
}
void Pick_Handle ( )
{
uint8_t midi = 0 ;
uint8_t midi_note_buff [ 4 ] = { 0 , } ;
2026-09-06 07:20:08 +08:00
uint8_t note_cnt = 0 ;
uint8_t chord_id = KEY_ID_1629 ;
2026-09-14 20:19:45 +08:00
/* 空库/加载失败时禁止起奏:否则 TMR6 ISR 内 AutoBand 可能 HardFault,
* LCD 卡 死 且 主 循 环 Power_Key_Scan 不 再 跑 → 只 能 RESET */
if ( ! App_Auto_IsPresetReady ( ) )
{
LOG_E ( " pick " , " abort: preset not ready tab=%u addr=0x%08X " ,
( unsigned ) mGuiData [ GUI_TAB_INDEX ] . Current , ( unsigned ) ADDRESS ) ;
return ;
}
2026-09-14 12:55:22 +08:00
/* 左手未触发和弦板(或落在拍速/停止键)时:拨片默认发当前调一级大和弦伴奏;
2026-09-14 21:49:16 +08:00
* 普 通 模 式 除 外 — — 必 须 左 手 按 住 指 板 再 拨 , 不 做 一 级 兜 底 ( 说 明 书 需 求 ) */
2026-09-14 12:55:22 +08:00
if ( mGuiData [ GUI_TAB_INDEX ] . Current ! = 2 & &
( chord_id = = 0 | | chord_id = = 22 | | chord_id = = 23 ) )
2026-09-06 07:20:08 +08:00
{
2026-09-17 16:18:14 +08:00
Accomp_ApplyDefaultIChord ( ) ;
chord_id = KEY_ID_1629 ;
2026-09-06 07:20:08 +08:00
}
note_cnt = GetChordNotesByType ( chord_id , chord_type_index_map [ chord_id ] . type , midi_note_buff ) ;
2026-08-26 07:26:58 +08:00
Return_Light_buff ( KEY_ID_1629 ) ;
2026-09-10 18:58:24 +08:00
/* 首次起奏前补发主音量:防开机 SysEx 丢失后"图标低、实际大" */
if ( StartFlag = = 0 )
MasterVolume_Reapply ( ) ;
2026-08-26 07:26:58 +08:00
switch ( mGuiData [ GUI_TAB_INDEX ] . Current )
{
case 3 :
switch ( mGuiData [ GUI_TAB_LAST_INDEX ] . Current )
{
case 2 :
if ( StartFlag = = 0 ) //当播放了之后,拨片不再起作用
{
StartFlag = 1 ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
midi = 0x3C + mGuiData [ GUI_TRANSPOSE ] . Current ;
AutoBandTop1_StartWithNote ( midi ) ;
2026-09-06 07:20:08 +08:00
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
2026-08-26 07:26:58 +08:00
}
break ;
case 1 :
StartFlag = 1 ;
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , 1920 ) ;
2026-09-06 07:20:08 +08:00
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
2026-08-26 07:26:58 +08:00
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
break ;
case 0 :
2026-09-09 11:35:14 +08:00
/* 设置页内沿用万能:与主路径一致,始终 loop */
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , 480 ) ;
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
2026-08-26 07:26:58 +08:00
StartFlag = 1 ;
break ;
}
break ;
case 2 :
2026-09-14 21:49:16 +08:00
/* 普通:左手按住指板 + 拨片起奏;松手由 TM1629 停。
* 节 奏 用 piece ; 本 地 曲 目 用 StartWithNote */
2026-09-14 12:55:22 +08:00
if ( PressFlag = = 0 | | chord_id = = 0 | | chord_id > 21 )
2026-09-14 21:49:16 +08:00
{
LOG_I ( " pick " , " normal skip press=%u chord=%u (need fretboard) " ,
( unsigned ) PressFlag , ( unsigned ) chord_id ) ;
break ;
}
2026-09-14 12:55:22 +08:00
StartFlag = 1 ;
if ( mGuiData [ GUI_AUTOBAND_SW ] . Current )
2026-09-14 21:49:16 +08:00
{
AutoBandTop1_Stop ( ) ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
AutoBandTop1_SetRunVar ( mGuiData [ GUI_PLAY_SECTION ] . Current ) ;
midi = 0x3C + mGuiData [ GUI_TRANSPOSE ] . Current ;
AutoBandTop1_StartWithNote ( midi ) ;
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
2026-09-14 12:55:22 +08:00
LOG_I ( " pick " , " local/normal root=%u name=%s " , midi ,
AutoBandTop1_GetPresetName ( ) ? AutoBandTop1_GetPresetName ( ) : " ? " ) ;
2026-09-14 21:49:16 +08:00
}
else
{
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , 1920 ) ;
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
LOG_I ( " pick " , " normal/rhythm piece name=%s notes=%u chord=%u " ,
AutoBandTop1_GetPresetName ( ) ? AutoBandTop1_GetPresetName ( ) : " ? " ,
( unsigned ) note_cnt , ( unsigned ) chord_id ) ;
}
2026-08-26 07:26:58 +08:00
break ;
2026-09-06 07:20:08 +08:00
case 1 :
2026-09-17 07:05:04 +08:00
/* 专业:播放中再拨片不 Stop+Start( 库会把未消费的 Start 标志清掉 → 静音);
* 本 地 曲 播 放 中 仅 换 和 弦 音 续 播 , 节 奏 路 径 piece 重 启 本 就 安 全 。 */
if ( Band_IsPlaying ( ) & & mGuiData [ GUI_AUTOBAND_SW ] . Current )
{
Accomp_UpdatePlayingChord ( ) ;
LOG_I ( " pick " , " expert/pick skip restart playing chord=%u " , ( unsigned ) chord_id ) ;
break ;
}
2026-09-15 19:13:10 +08:00
Expert_StartWithChord ( chord_id , midi_note_buff , note_cnt , " pick " ) ;
2026-08-26 07:26:58 +08:00
break ;
case 0 :
2026-09-09 11:35:14 +08:00
/* 万能:和弦走向用 piece 模式;须 SetLoopMode, 否则无循环时常听不到伴奏 */
2026-08-26 07:26:58 +08:00
StartFlag = 1 ;
2026-09-21 15:13:53 +08:00
s_outro_led_all_green = 0 ; /* 拨片重开 piece, 恢复走向灯 */
2026-09-09 11:35:14 +08:00
AutoBandTop1_SetPiecePlayMode ( 1 , 0 , 480 ) ;
AutoBandTop1_StartWithNotes ( midi_note_buff , note_cnt ) ;
AutoBandTop1_SetLoopMode ( 0 , 1 ) ;
LOG_I ( " pick " , " universal piece name=%s press=%u notes=%u chord=%u " ,
AutoBandTop1_GetPresetName ( ) ? AutoBandTop1_GetPresetName ( ) : " ? " ,
( unsigned ) PressFlag , ( unsigned ) note_cnt , ( unsigned ) chord_id ) ;
2026-08-26 07:26:58 +08:00
break ;
}
}
void Loop_Light_Handle ( uint8_t measure )
{
switch ( ParamGuiData [ ALL_MODE_PARAM ] . Current )
{
case 0 :
switch ( measure )
{
case 1 : app_tm1629_set_led ( LED_L1 , LED_COLOR_R ) ; break ;
case 2 : app_tm1629_set_led ( LED_L6 , LED_COLOR_R ) ; break ;
case 3 : app_tm1629_set_led ( LED_L4 , LED_COLOR_R ) ; break ;
case 4 : app_tm1629_set_led ( LED_L5 , LED_COLOR_R ) ; break ;
}
break ;
case 1 :
switch ( measure )
{
case 1 : app_tm1629_set_led ( LED_L1 , LED_COLOR_R ) ; break ;
case 2 : app_tm1629_set_led ( LED_L5 , LED_COLOR_R ) ; break ;
case 3 : app_tm1629_set_led ( LED_L6 , LED_COLOR_R ) ; break ;
case 4 : app_tm1629_set_led ( LED_L4 , LED_COLOR_R ) ; break ;
}
break ;
case 2 :
switch ( measure )
{
case 1 : app_tm1629_set_led ( LED_L1 , LED_COLOR_R ) ; break ;
case 2 : app_tm1629_set_led ( LED_L3 , LED_COLOR_R ) ; break ;
case 3 : app_tm1629_set_led ( LED_L6 , LED_COLOR_R ) ; break ;
case 4 : app_tm1629_set_led ( LED_L4 , LED_COLOR_R ) ; break ;
}
break ;
}
}
void BL_Set_led ( uint8_t led , uint8_t chord_type )
{
uint8_t chord_offset ;
Led = led ;
switch ( chord_type ) {
case 0x00 :
chord_offset = 0 ;
break ;
case 0x08 :
chord_offset = 1 ;
break ;
case 0x02 :
chord_offset = 4 ;
break ;
case 0x13 :
chord_offset = 3 ;
break ;
case 0x0A : //A
chord_offset = 2 ;
break ;
case 0x1F : //1F
chord_offset = 7 ;
break ;
case 0x1E : //1E
chord_offset = 8 ;
break ;
case 0x04 :
chord_offset = 5 ;
break ;
case 0x0B : //B
chord_offset = 6 ;
break ;
}
switch ( led ) {
case LED_L1 :
app_tm1629_set_led ( LED_L1 , LED_COLOR_R ) ;
chord_type_index_map [ 2 ] . type = chord_offset ;
break ;
case LED_L2 :
app_tm1629_set_led ( LED_L2 , LED_COLOR_G ) ;
chord_type_index_map [ 5 ] . type = chord_offset ;
break ;
case LED_L3 :
app_tm1629_set_led ( LED_L3 , LED_COLOR_B ) ;
chord_type_index_map [ 8 ] . type = chord_offset ;
break ;
case LED_L4 :
app_tm1629_set_led ( LED_L4 , LED_COLOR_YELL ) ;
chord_type_index_map [ 11 ] . type = chord_offset ;
break ;
case LED_L5 :
app_tm1629_set_led ( LED_L5 , LED_COLOR_PURP ) ;
chord_type_index_map [ 14 ] . type = chord_offset ;
break ;
case LED_L6 :
app_tm1629_set_led ( LED_L6 , LED_COLOR_CYA ) ;
chord_type_index_map [ 17 ] . type = chord_offset ;
break ;
case LED_L7 :
app_tm1629_set_led ( LED_L7 , LED_COLOR_WHITE ) ;
chord_type_index_map [ 20 ] . type = chord_offset ;
break ;
default :
// 鏈煡鐏彿锛屽叏閮ㄥ叧闂?
app_tm1629_all_off ( ) ;
break ;
}
}
void BT_Pitch_offset_map ( uint8_t pos_offset , uint8_t pitch_offset )
{
2026-09-10 06:51:32 +08:00
if ( pos_offset < 1u | | pos_offset > 21u )
return ;
2026-08-26 07:26:58 +08:00
chord_type_index_map [ pos_offset ] . PitchOffset = pitch_offset ;
}
void BT_Chord_offset_map ( uint8_t pos_offset , uint8_t chord_offset )
{
//char Info[20];
STRING_MIDI * MIDI ;
const STRING_MIDI * ORGAN_MIDI ;
2026-09-10 06:51:32 +08:00
uint8_t group ;
2026-08-26 07:26:58 +08:00
uint8_t chord_APP_Local_offset = 0 ;
2026-09-10 06:51:32 +08:00
if ( pos_offset < 1u | | pos_offset > 21u )
return ;
group = ( pos_offset - 1 ) / 3 ; /* 1~3→0, 4~6→1, ..., 19~21→6 */
2026-08-26 07:26:58 +08:00
switch ( chord_offset ) {
case 0x00 :
chord_APP_Local_offset = 0 ;
break ;
case 0x08 :
chord_APP_Local_offset = 1 ;
break ;
case 0x02 :
chord_APP_Local_offset = 4 ;
break ;
case 0x13 :
chord_APP_Local_offset = 3 ;
break ;
case 0x0A : //A
chord_APP_Local_offset = 2 ;
break ;
case 0x1F : //1F
chord_APP_Local_offset = 7 ;
break ;
case 0x1E : //1E
chord_APP_Local_offset = 8 ;
break ;
case 0x04 :
chord_APP_Local_offset = 5 ;
break ;
case 0x0B : //B
chord_APP_Local_offset = 6 ;
break ;
}
//sprintf (Info, "%d %d %d %d", chord_offset,chord_APP_Local_offset,pos_offset,chord_type_index_map[pos_offset].PitchOffset);
//LCD_ShowString(2, 176+16, (const uint8_t*)Info, RED, WHITE, 16, 0);
chord_type_index_map [ pos_offset ] . type = chord_APP_Local_offset ;
MIDI = ( STRING_MIDI * ) Chord_Midi_Table [ group ] ;
ORGAN_MIDI = OrganChord_Midi_Table [ group ] ;
switch ( chord_type_index_map [ pos_offset ] . PitchOffset ) {
case 1 :
MIDI - > Midi_1 = ORGAN_MIDI - > Midi_1 ;
MIDI - > Midi_2 = ORGAN_MIDI - > Midi_2 ;
MIDI - > Midi_3 = ORGAN_MIDI - > Midi_3 ;
MIDI - > Midi_4 = ORGAN_MIDI - > Midi_4 ;
MIDI - > Midi_5 = ORGAN_MIDI - > Midi_5 ;
MIDI - > Midi_6 = ORGAN_MIDI - > Midi_6 ;
break ;
case 2 :
MIDI - > Midi_1 = ORGAN_MIDI - > Midi_1 + 1 ;
MIDI - > Midi_2 = ORGAN_MIDI - > Midi_2 + 1 ;
MIDI - > Midi_3 = ORGAN_MIDI - > Midi_3 + 1 ;
MIDI - > Midi_4 = ORGAN_MIDI - > Midi_4 + 1 ;
MIDI - > Midi_5 = ORGAN_MIDI - > Midi_5 + 1 ;
MIDI - > Midi_6 = ORGAN_MIDI - > Midi_6 + 1 ;
break ;
}
}
uint8_t GetChordNotesByType ( uint8_t MIDI_Index , uint8_t type , uint8_t * buff )
{
2026-09-04 17:06:02 +08:00
uint8_t n = 0 ;
uint8_t i ;
uint8_t root ;
buff [ 0 ] = buff [ 1 ] = buff [ 2 ] = buff [ 3 ] = 0 ;
2026-08-26 07:26:58 +08:00
switch ( MIDI_Index )
{
case 1 : case 2 : case 3 :
switch ( type )
{
case 0 :
buff [ 0 ] = 48 ; buff [ 1 ] = 52 ; buff [ 2 ] = 55 ;
break ;
case 1 :
buff [ 0 ] = 48 ; buff [ 1 ] = 51 ; buff [ 2 ] = 55 ;
break ;
case 2 :
buff [ 0 ] = 48 ; buff [ 1 ] = 51 ; buff [ 2 ] = 55 ; buff [ 3 ] = 58 ;
break ;
case 3 :
buff [ 0 ] = 48 ; buff [ 1 ] = 52 ; buff [ 2 ] = 55 ; buff [ 3 ] = 58 ;
break ;
case 4 :
buff [ 0 ] = 48 ; buff [ 1 ] = 52 ; buff [ 2 ] = 55 ; buff [ 3 ] = 59 ;
break ;
case 5 :
buff [ 0 ] = 48 ; buff [ 1 ] = 52 ; buff [ 2 ] = 55 ; buff [ 3 ] = 62 ;
break ;
case 6 :
buff [ 0 ] = 48 ; buff [ 1 ] = 51 ; buff [ 2 ] = 54 ; buff [ 3 ] = 58 ;
break ;
case 7 :
buff [ 0 ] = 48 ; buff [ 1 ] = 50 ; buff [ 2 ] = 55 ;
break ;
case 8 :
buff [ 0 ] = 48 ; buff [ 1 ] = 53 ; buff [ 2 ] = 55 ;
break ;
}
break ;
case 4 : case 5 : case 6 :
switch ( type )
{
case 0 :
buff [ 0 ] = 50 ; buff [ 1 ] = 54 ; buff [ 2 ] = 57 ;
break ;
case 1 :
buff [ 0 ] = 50 ; buff [ 1 ] = 53 ; buff [ 2 ] = 57 ;
break ;
case 2 :
buff [ 0 ] = 50 ; buff [ 1 ] = 53 ; buff [ 2 ] = 57 ; buff [ 3 ] = 60 ;
break ;
case 3 :
buff [ 0 ] = 50 ; buff [ 1 ] = 54 ; buff [ 2 ] = 57 ; buff [ 3 ] = 60 ;
break ;
case 4 :
buff [ 0 ] = 50 ; buff [ 1 ] = 54 ; buff [ 2 ] = 57 ; buff [ 3 ] = 61 ;
break ;
case 5 :
buff [ 0 ] = 50 ; buff [ 1 ] = 54 ; buff [ 2 ] = 57 ; buff [ 3 ] = 64 ;
break ;
case 6 :
buff [ 0 ] = 50 ; buff [ 1 ] = 53 ; buff [ 2 ] = 56 ; buff [ 3 ] = 60 ;
break ;
case 7 :
buff [ 0 ] = 50 ; buff [ 1 ] = 52 ; buff [ 2 ] = 57 ;
break ;
case 8 :
buff [ 0 ] = 50 ; buff [ 1 ] = 55 ; buff [ 2 ] = 57 ;
break ;
}
break ;
case 7 : case 8 : case 9 :
switch ( type )
{
case 0 :
buff [ 0 ] = 52 ; buff [ 1 ] = 56 ; buff [ 2 ] = 59 ;
break ;
case 1 :
buff [ 0 ] = 52 ; buff [ 1 ] = 55 ; buff [ 2 ] = 59 ;
break ;
case 2 :
buff [ 0 ] = 52 ; buff [ 1 ] = 55 ; buff [ 2 ] = 59 ; buff [ 3 ] = 62 ;
break ;
case 3 :
buff [ 0 ] = 52 ; buff [ 1 ] = 56 ; buff [ 2 ] = 59 ; buff [ 3 ] = 62 ;
break ;
case 4 :
buff [ 0 ] = 52 ; buff [ 1 ] = 56 ; buff [ 2 ] = 59 ; buff [ 3 ] = 63 ;
break ;
case 5 :
buff [ 0 ] = 52 ; buff [ 1 ] = 56 ; buff [ 2 ] = 59 ; buff [ 3 ] = 66 ;
break ;
case 6 :
buff [ 0 ] = 52 ; buff [ 1 ] = 55 ; buff [ 2 ] = 58 ; buff [ 3 ] = 62 ;
break ;
case 7 :
buff [ 0 ] = 52 ; buff [ 1 ] = 54 ; buff [ 2 ] = 59 ;
break ;
case 8 :
buff [ 0 ] = 52 ; buff [ 1 ] = 57 ; buff [ 2 ] = 59 ;
break ;
}
break ;
case 10 : case 11 : case 12 :
switch ( type )
{
case 0 :
buff [ 0 ] = 53 ; buff [ 1 ] = 57 ; buff [ 2 ] = 60 ;
break ;
case 1 :
buff [ 0 ] = 53 ; buff [ 1 ] = 56 ; buff [ 2 ] = 60 ;
break ;
case 2 :
buff [ 0 ] = 53 ; buff [ 1 ] = 56 ; buff [ 2 ] = 60 ; buff [ 3 ] = 63 ;
break ;
case 3 :
buff [ 0 ] = 53 ; buff [ 1 ] = 57 ; buff [ 2 ] = 60 ; buff [ 3 ] = 63 ;
break ;
case 4 :
buff [ 0 ] = 53 ; buff [ 1 ] = 57 ; buff [ 2 ] = 60 ; buff [ 3 ] = 64 ;
break ;
case 5 :
buff [ 0 ] = 53 ; buff [ 1 ] = 57 ; buff [ 2 ] = 60 ; buff [ 3 ] = 67 ;
break ;
case 6 :
buff [ 0 ] = 53 ; buff [ 1 ] = 56 ; buff [ 2 ] = 59 ; buff [ 3 ] = 63 ;
break ;
case 7 :
buff [ 0 ] = 53 ; buff [ 1 ] = 55 ; buff [ 2 ] = 60 ;
break ;
case 8 :
buff [ 0 ] = 53 ; buff [ 1 ] = 58 ; buff [ 2 ] = 60 ;
break ;
}
break ;
case 13 : case 14 : case 15 :
switch ( type )
{
case 0 :
buff [ 0 ] = 43 ; buff [ 1 ] = 47 ; buff [ 2 ] = 50 ;
break ;
case 1 :
buff [ 0 ] = 43 ; buff [ 1 ] = 46 ; buff [ 2 ] = 50 ;
break ;
case 2 :
buff [ 0 ] = 43 ; buff [ 1 ] = 46 ; buff [ 2 ] = 50 ; buff [ 3 ] = 53 ;
break ;
case 3 :
buff [ 0 ] = 43 ; buff [ 1 ] = 47 ; buff [ 2 ] = 50 ; buff [ 3 ] = 53 ;
break ;
case 4 :
buff [ 0 ] = 43 ; buff [ 1 ] = 47 ; buff [ 2 ] = 50 ; buff [ 3 ] = 54 ;
break ;
case 5 :
buff [ 0 ] = 43 ; buff [ 1 ] = 47 ; buff [ 2 ] = 50 ; buff [ 3 ] = 57 ;
break ;
case 6 :
buff [ 0 ] = 43 ; buff [ 1 ] = 46 ; buff [ 2 ] = 49 ; buff [ 3 ] = 53 ;
break ;
case 7 :
buff [ 0 ] = 43 ; buff [ 1 ] = 45 ; buff [ 2 ] = 50 ;
break ;
case 8 :
buff [ 0 ] = 43 ; buff [ 1 ] = 48 ; buff [ 2 ] = 50 ;
break ;
}
break ;
case 16 : case 17 : case 18 :
switch ( type )
{
case 0 :
buff [ 0 ] = 45 ; buff [ 1 ] = 49 ; buff [ 2 ] = 52 ;
break ;
case 1 :
buff [ 0 ] = 45 ; buff [ 1 ] = 48 ; buff [ 2 ] = 52 ;
break ;
case 2 :
buff [ 0 ] = 45 ; buff [ 1 ] = 48 ; buff [ 2 ] = 52 ; buff [ 3 ] = 55 ;
break ;
case 3 :
buff [ 0 ] = 45 ; buff [ 1 ] = 49 ; buff [ 2 ] = 52 ; buff [ 3 ] = 55 ;
break ;
case 4 :
buff [ 0 ] = 45 ; buff [ 1 ] = 49 ; buff [ 2 ] = 52 ; buff [ 3 ] = 56 ;
break ;
case 5 :
buff [ 0 ] = 45 ; buff [ 1 ] = 49 ; buff [ 2 ] = 52 ; buff [ 3 ] = 59 ;
break ;
case 6 :
buff [ 0 ] = 45 ; buff [ 1 ] = 48 ; buff [ 2 ] = 51 ; buff [ 3 ] = 55 ;
break ;
case 7 :
buff [ 0 ] = 45 ; buff [ 1 ] = 47 ; buff [ 2 ] = 52 ;
break ;
case 8 :
buff [ 0 ] = 45 ; buff [ 1 ] = 50 ; buff [ 2 ] = 52 ;
break ;
}
break ;
case 19 : case 20 : case 21 :
switch ( type )
{
case 0 :
buff [ 0 ] = 47 ; buff [ 1 ] = 51 ; buff [ 2 ] = 53 ;
break ;
case 1 :
buff [ 0 ] = 47 ; buff [ 1 ] = 50 ; buff [ 2 ] = 54 ;
break ;
case 2 :
buff [ 0 ] = 47 ; buff [ 1 ] = 50 ; buff [ 2 ] = 54 ; buff [ 3 ] = 57 ;
break ;
case 3 :
buff [ 0 ] = 47 ; buff [ 1 ] = 50 ; buff [ 2 ] = 53 ; buff [ 3 ] = 57 ;
break ;
case 4 :
buff [ 0 ] = 47 ; buff [ 1 ] = 51 ; buff [ 2 ] = 53 ; buff [ 3 ] = 57 ;
break ;
case 5 :
buff [ 0 ] = 47 ; buff [ 1 ] = 51 ; buff [ 2 ] = 54 ; buff [ 3 ] = 58 ;
break ;
case 6 :
buff [ 0 ] = 47 ; buff [ 1 ] = 51 ; buff [ 2 ] = 54 ; buff [ 3 ] = 60 ;
break ;
case 7 :
buff [ 0 ] = 47 ; buff [ 1 ] = 49 ; buff [ 2 ] = 54 ;
break ;
case 8 :
buff [ 0 ] = 47 ; buff [ 1 ] = 52 ; buff [ 2 ] = 54 ;
break ;
case 9 :
buff [ 0 ] = 47 ; buff [ 1 ] = 50 ; buff [ 2 ] = 53 ;
break ;
}
break ;
2026-09-04 17:06:02 +08:00
default :
return 0 ;
2026-08-26 07:26:58 +08:00
}
2026-09-04 17:06:02 +08:00
n = ( buff [ 3 ] ! = 0 ) ? 4 : 3 ;
switch ( chord_type_index_map [ MIDI_Index ] . PitchOffset )
2026-08-26 07:26:58 +08:00
{
2026-09-04 17:06:02 +08:00
case 2 : /* # 升:整体 +1 半音 */
for ( i = 0 ; i < n ; i + + )
buff [ i ] + = 1 ;
break ;
default :
break ;
}
for ( i = 0 ; i < n ; i + + )
buff [ i ] + = mGuiData [ GUI_TRANSPOSE ] . Current ;
2026-09-07 09:28:35 +08:00
/* 音师:仅所有调的 #四级(键 10~12 + 升)— 整组低八度,音域压在一个八度内
* 最 低 音 = # IV , 最 高 音 ≤ 自 然 IV 。 勿 对 六 级 等 其 它 升 号 和 弦 套 用 。 */
if ( MIDI_Index > = 10 & & MIDI_Index < = 12 & &
chord_type_index_map [ MIDI_Index ] . PitchOffset = = 2 )
2026-08-26 07:26:58 +08:00
{
2026-09-04 17:06:02 +08:00
for ( i = 0 ; i < n ; i + + )
{
if ( buff [ i ] > = 12 )
buff [ i ] - = 12 ;
}
root = buff [ 0 ] ;
for ( i = 1 ; i < n ; i + + )
{
2026-09-10 06:51:32 +08:00
uint8_t guard ;
for ( guard = 0 ; guard < 2 & & buff [ i ] > = ( uint8_t ) ( root + 12 ) ; guard + + )
2026-09-04 17:06:02 +08:00
buff [ i ] - = 12 ;
2026-09-10 06:51:32 +08:00
for ( guard = 0 ; guard < 2 & & buff [ i ] < root ; guard + + )
2026-09-04 17:06:02 +08:00
buff [ i ] + = 12 ;
}
2026-08-26 07:26:58 +08:00
}
2026-09-04 17:06:02 +08:00
return n ;
2026-08-26 07:26:58 +08:00
}
void Read_Cur_Measure ( )
{
uint32_t now = rt_tick_get ( ) ;
2026-09-06 08:41:30 +08:00
static uint32_t last_tick_i = 0 ;
uint8_t tab = ( uint8_t ) mGuiData [ GUI_TAB_INDEX ] . Current ;
uint8_t last = ( uint8_t ) mGuiData [ GUI_TAB_LAST_INDEX ] . Current ;
/* 普通(2)与万能(0)均按「和弦走向」同步亮灯;设置页(3)沿用进入前模式 */
uint8_t loop_light_ok =
( tab = = 0 | | tab = = 2 ) | |
( tab = = 3 & & ( last = = 0 | | last = = 2 ) ) ;
2026-09-15 19:13:10 +08:00
Band_TrackPlayTick ( ) ;
2026-09-21 15:23:10 +08:00
FreeMode_PollEvenBeatStop ( ) ;
2026-09-15 19:13:10 +08:00
2026-09-21 15:13:53 +08:00
/* 尾奏中保持全绿,不刷新和弦走向红灯 */
if ( loop_light_ok & & StartFlag & & ! s_outro_led_all_green )
2026-08-26 07:26:58 +08:00
{
if ( now - last_tick_i > = 10 )
{
last_tick_i = now ;
uint8_t Measure = AutoBandTop1_GetCurrentRunMeasure ( ) ;
MainTask_Sendmsg ( MSG_ID_LOOP_LIGHT , Measure , 0 , 0 ) ;
}
}
}