找回密码
 立即注册
查看: 66|回复: 3

请问AI8051 SPI使用DMA功能驱动WS2812的灯珠,但只要一读取ADC,WS2812就无法点亮

[复制链接]
  • 打卡等级:初来乍到
  • 打卡总天数:1
  • 最近打卡:2026-07-03 23:23:23

1

主题

0

回帖

5

积分

新手上路

积分
5
发表于 2026-7-3 23:23:23 | 显示全部楼层 |阅读模式
最近想做一个音乐频谱,WS2812驱动正常了,设置ADC只要ADC一读取数值WS2812的灯光就不能正常显示,不知道是DMA冲突还是程序堵塞导致DMA不正常,程序如下:
#include "AI8051U.h"
#include <intrins.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
/************* 功能说明 **************

专为 EV1527 格式 24 位遥控器优化:
- 位0:高电平 0.4ms,低电平 1.2ms
- 位1:高电平 1.2ms,低电平 0.4ms
- 停止位:高 0.4ms,低 0.4ms
- 地址码:高 16 位(用户码),键码:低 8 位

******************************************/

/************* 常量声明 **************/
#define LED_NUM                60
#define SPI_NUM                (LED_NUM*12)
#define MODE_NUM            4
#define COLOR_NUM            9
#define BRIGHTNESS_LEVEL_MAX 10
#define BRIGHTNESS_MIN        10
#define BRIGHTNESS_MAX        255
#define UART_BUF_SIZE        32
bit uart_print_enabled = 1;   // 串口打印开关,默认开启
#define     MAIN_Fosc        24000000UL    //定义主时钟
   
#define BRT (65536 - MAIN_Fosc /9600/ 4)
// 遥控器地址(16 位,请根据实际遥控器修改)
#define REMOTE_ADDR            0xFFFF

// RF 解码常量(20us 中断计数,24MHz)
#define SYNC_LOW_MIN        400        // 8ms  (典型同步头低电平 9ms)
#define SYNC_LOW_MAX        800        // 12ms
#define BIT1_THRESHOLD        40        // 高电平阈值 (0码~20, 1码~60, 取中间值40)
unsigned int learned_addr = REMOTE_ADDR;   // 当前使用的遥控器地址(可由学习更新)


// EEPROM 地址
// 原有参数仍保存在 0x0000 扇区(0x0000~0x01FF)
#define EE_ADDR_MODE        0x0000
#define EE_ADDR_LIGHT_ON    0x0001
#define EE_ADDR_BRIGHT_LEV  0x0002
#define EE_ADDR_COLOR_IDX   0x0003
#define EE_ADDR_SPEED_LEV   0x0004

// 遥控器地址单独存放在 0x0200 扇区(0x0200~0x03FF)
#define EE_ADDR_REMOTE_H    0x0200   // 地址高字节
#define EE_ADDR_REMOTE_L    0x0201   // 地址低字节
/************* 变量声明 **************/
unsigned char xdata led_RGB[LED_NUM][3];
unsigned char xdata led_SPI[SPI_NUM];
bit B_SPI_DMA_busy;

bit light_on = 1;
unsigned char mode = 0;
unsigned char ColorIdex = 0;
unsigned char brightness_level = 10;
unsigned char speed_level = 5;
unsigned int speed_value = 32;
unsigned char global_brightness = 255;
unsigned char last_mode_before_off = 0;

bit ModeNeedSave = 0;
bit LightNeedSave = 0;
bit BrightNeedSave = 0;
bit ColorNeedSave = 0;
bit SpeedNeedSave = 0;

const unsigned char code colorTable[COLOR_NUM][3] = {
    {255,0,0}, {0,255,0}, {0,0,255},
    {255,255,0}, {255,0,255}, {0,255,255},
    {255,255,255}, {255,165,0}, {128,0,128}
};

unsigned char uartBuf[UART_BUF_SIZE];
unsigned char uartCount = 0;
bit uartCmdReady = 0;
//端口定义
sbit ModeKey = P2^6;
sbit ColorKey = P3^4;
sbit BrightKey = P3^5;
sbit SpeedKey = P3^6;
sbit RF_IN = P3^3;

// 按键变量

bit ModeKeyPrev = 1, ColorKeyPrev = 1, BrightKeyPrev = 1, SpeedKeyPrev = 1;
unsigned long mode_press_start = 0;
bit mode_long_handled = 0;
unsigned long color_press_start = 0;
bit color_long_handled = 0;
bit learn_mode = 0;
unsigned long learn_timeout = 0;

// RF 解码变量(阈值法)
volatile unsigned long rf_code_32 = 0;
volatile bit rf_rec_ok = 0;
volatile unsigned char rf_bit_cnt = 0;
volatile bit rf_sync_detected = 0;
volatile unsigned int rf_high_cnt = 0;
volatile unsigned int rf_low_cnt = 0;
volatile bit rf_last_level = 0;
volatile unsigned long system_ticks = 0;

volatile unsigned int sync_timeout = 0;
#define SYNC_TIMEOUT_MAX  80

volatile unsigned long last_rf_time = 0;
volatile unsigned long last_rf_code = 0;

volatile unsigned long rf_mode_first_time = 0;
volatile unsigned char rf_mode_frame_cnt = 0;
volatile bit rf_mode_press_detected = 0;
volatile bit rf_mode_short_handled = 0;
volatile bit rf_mode_long_handled = 0;
volatile bit rf_mode_short_flag = 0;
volatile bit rf_mode_long_flag = 0;
volatile bit rf_sync_high_phase = 0; // 同步头高电平阶段标志

// 遥控器按键宏(根据 STC8H 定义)
#define REMOTE_KEY_POWER_ON        0x01   // 电源开关(短按开关灯)
#define REMOTE_KEY_LIGHT_MODE_UP   0x02   // 模式+
#define REMOTE_KEY_LIGHT_ON_OFF    0x03   // 点按开灯或关灯(独立开关)
#define REMOTE_KEY_LIGHT_SPEED_DOWN 0x04  // 速度-
#define REMOTE_KEY_DEMO            0x05   // 演示(恢复默认)
#define REMOTE_KEY_LIGHT_SPEED_UP  0x06   // 速度+
#define REMOTE_KEY_COLOR_UP        0x07   // 颜色+
#define REMOTE_KEY_LIGHT_MODE_DOWN 0x08   // 模式-
#define REMOTE_KEY_LIGHT_BRIGHT_UP 0x09   // 亮度+
#define REMOTE_KEY_LIGHT_COLOR_DOWN 0x0A  // 颜色-
#define REMOTE_KEY_MODE_DOWN       0x0B   // 模式转换(可忽略或用作频谱切换,但无频谱,可忽略)
#define REMOTE_KEY_LIGHT_BRIGHT_DOWN 0x0C // 亮度-
#define REMOTE_KEY_PRE             0x0D   // 上一曲(忽略)
#define REMOTE_KEY_NEXT            0x0E   // 下一曲(忽略)
#define REMOTE_KEY_PLAY            0x0F   // 播放/暂停(忽略)
#define REMOTE_KEY_VOL_DOWN        0x10   // 音量减(忽略)
#define REMOTE_KEY_VOL_UP          0x11   // 音量加(忽略)
#define REMOTE_KEY_EQ              0x12   // EQ(忽略)

#define FRAME_HEADER        0xEA
#define FRAME_TYPE_BT       0x05
#define FRAME_TYPE_STATUS   0x01
#define FRAME_TYPE_LC       0x03

// 事件映射表(与 STC8H 一致)
typedef struct {
    unsigned char event_id;
    unsigned char cmd_byte;
} event_map_t;

static const event_map_t lc_event_map[] = {
    {0x01, 0x80},  // 电源
    {0x02, 0x40},  // MODE+
    {0x03, 0xC0},  // 开关
    {0x04, 0x20},  // SPEED-
    {0x05, 0xA0},  // DEMO
    {0x06, 0x60},  // SPEED+
    {0x07, 0xE0},  // COLOR+
    {0x08, 0x10},  // MODE-
    {0x09, 0x90},  // BRIGHT+
    {0x0A, 0x50},  // COLOR-
    {0x0B, 0xD0},  // MODE转换
    {0x0C, 0x30},  // BRIGHT-
    {0x0D, 0xB0},  // 上一曲
    {0x0E, 0x70},  // 下一曲
    {0x0F, 0xF0},  // 播放/暂停
    {0x10, 0x08},  // VOL-
    {0x11, 0x88},  // VOL+
    {0x12, 0x48}   // EQ
};

// ---- ADC 双通道音频采样 ----
unsigned int Lamp = 0, Ramp = 0;          // 左右声道峰值(平滑后)
unsigned int Lmax = 0, Lmin = 1023;       // 左声道极值(用于计算幅值)
unsigned int Rmax = 0, Rmin = 1023;       // 右声道极值
unsigned char adc_channel = 0;            // 当前采样通道(0左,1右)

/************* 函数声明 **************/
void SPI_Config(unsigned char SPI_io, unsigned char SPI_speed);
void LoadSPI(void);
void SPI_DMA_TxTRIG(unsigned char xdata *TxBuf, unsigned int num);
void delay_ms(unsigned int ms);
void delay_us(unsigned int us);
void SetLedRGB(unsigned char i, unsigned char r, unsigned char g, unsigned char b);
void ClearAllLED(void);
void HSV_to_RGB(unsigned int hue, unsigned char sat, unsigned char val, unsigned char *rgb);
void Timer0Init(void);
void ProcessRFCommand(void);
void CheckRFTimeout(void);
void HandleRFCommands(void);
void UART_Init(void);
void UART_SendChar(unsigned char dat);
void UART_SendString(char *str);
void UART_SendNumber(unsigned char num);
void UART_SendUint16(unsigned int num);
void UART_SendHexByte(unsigned char byte);
void ChangeColor(void);
void ChangeBrightness(void);
void ChangeSpeed(void);
void ParseUartCommand(void);
void SaveAllParams(void);
void LoadAllParams(void);
void IapIdle(void);
unsigned char IapRead(unsigned int addr);
void IapProgram(unsigned int addr, unsigned char dat);
void IapErase(unsigned int addr);
void Effect_RainbowFlow(void);
void Effect_Breath(void);
void Effect_StaticColor(void);
void Key_Scan(void);
void SaveLearnedAddr(void);
void ChangeSpectrumScheme(void);
void SendEventToBluetooth(unsigned char event_id);
void SendToBluetooth(unsigned char type, unsigned char data_len, unsigned char data1, unsigned char data2);
void ProcessBluetoothCommand(unsigned char *buf, unsigned char len);
void ADC_Init(void);
void ADC_StartConversion(unsigned char channel);
bit ADC_GetResult(unsigned int *val);
void Effect_AudioSpectrum(void);


/************* 函数指针数组 **************/
void (*effect_func[])(void) = {
    Effect_AudioSpectrum,   // 模式 0
    Effect_RainbowFlow,     // 模式 1(原模式0)
    Effect_Breath,          // 模式 2(原模式1)
    Effect_StaticColor      // 模式 3(原模式2)
};

/*************** 主函数 *******************************/
void main(void) {
    unsigned int i;
    // ---- ADC 非阻塞状态变量 ----
    static unsigned char adc_ch = 0;          // 当前采样通道(0左,1右)
    static unsigned char adc_busy = 0;        // 是否已启动转换
    static unsigned int adc_val_L = 0, adc_val_R = 0;
    static unsigned int last_L = 0, last_R = 0;
    static unsigned char sample_cnt = 0;
    static unsigned char timeout_cnt = 0;
       unsigned int val;
    EAXFR = 1;
    WTST = 0;
    CKCON = 0;

    // IO 配置
    P1M0 = 0; P1M1 = 0;
    P3M1 |= 0x08; P3M0 &= ~0x08; P3PU &= ~0x08;   // RF 输入 P3.3
    P2M1 |= 0x40; P2M0 &= ~0x40; P2PU |= 0x40;   // ModeKey P2.6
    P3M1 |= 0x70; P3M0 &= ~0x70; P3PU |= 0x70;   // Color, Bright, Speed

    SPI_Config(0, 1);
    P1M1 |= 0x40; P1PD |= 0x40;   // MISO 下拉

    Timer0Init();
    UART_Init();
    LoadAllParams();
   
    // ---- 初始化 ADC(非中断方式) ----
   // ADC_Init();
    // 启动第一次转换(通道0)
   // ADC_StartConversion(0);
    adc_busy = 1;
   
    EA = 1;
    UART_SendString("\r\n=== System Ready ===\r\n");

    ClearAllLED();
    LoadSPI();
    SPI_DMA_TxTRIG(led_SPI, SPI_NUM);

    while (1) {
        // ==================== 1. 处理各种事件 ====================
        ParseUartCommand();
        ProcessRFCommand();
        HandleRFCommands();
        CheckRFTimeout();
        Key_Scan();

        if (learn_mode && (system_ticks > learn_timeout)) {
            learn_mode = 0;
            UART_SendString("Learn timeout\r\n");
        }

        // ==================== 2. 灯效计算 ====================
        if (light_on) {
            if (mode < MODE_NUM) effect_func[mode]();
            else Effect_RainbowFlow();
            for (i = 0; i < LED_NUM; i++) {
                led_RGB[i][0] = (unsigned int)led_RGB[i][0] * global_brightness / 255;
                led_RGB[i][1] = (unsigned int)led_RGB[i][1] * global_brightness / 255;
                led_RGB[i][2] = (unsigned int)led_RGB[i][2] * global_brightness / 255;
            }
        } else {
            ClearAllLED();
        }

        // ==================== 3. SPI-DMA 传输 ====================
        LoadSPI();
        SPI_DMA_TxTRIG(led_SPI, SPI_NUM);   // 此函数应尽快返回,不操作 ADC

        // ==================== 4. 保存参数 ====================
        if (ModeNeedSave || LightNeedSave || BrightNeedSave || ColorNeedSave || SpeedNeedSave) {
            SaveAllParams();
            ModeNeedSave = 0;
            LightNeedSave = 0;
            BrightNeedSave = 0;
            ColorNeedSave = 0;
            SpeedNeedSave = 0;
        }

        // ==================== 5. 延时,等待 DMA 完成 ====================
        delay_ms(20);

        // ==================== 6. ADC 非阻塞采样(仅在频谱模式) ====================
        if (mode == 0) {
            // 每 N 个周期启动一次新转换(降低频率)
            sample_cnt++;
            if (sample_cnt >= 5) {   // 5 个周期约 100ms
                sample_cnt = 0;
                // 若上次转换已完成,则启动新一次
                if (!adc_busy) {
                    ADC_StartConversion(adc_ch);
                    adc_busy = 1;
                }
            }

            // 检查是否有转换完成
         
            if (adc_busy && ADC_GetResult(&val)) {
                adc_busy = 0;
                // 根据当前通道存储结果
                if (adc_ch == 0) {
                    adc_val_L = val;
                    // 平滑滤波
                    adc_val_L = (last_L * 3 + adc_val_L) / 4;
                    last_L = adc_val_L;
                    Lamp = adc_val_L;
                } else {
                    adc_val_R = val;
                    adc_val_R = (last_R * 3 + adc_val_R) / 4;
                    last_R = adc_val_R;
                    Ramp = adc_val_R;
                }
                // 切换通道,准备下一次启动
                adc_ch ^= 1;
            }

            // 超时保护:如果 ADC 长时间未完成,强制重新启动
            if (adc_busy) {
                timeout_cnt++;
                if (timeout_cnt > 10) {   // 约 200ms 未完成
                    timeout_cnt = 0;
                    // 强制清除标志并重新启动
                    ADC_CONTR &= ~0x10;    // 清除标志
                    ADC_StartConversion(adc_ch);
                }
            } else {
                timeout_cnt = 0;
            }
        } else {
            // 非频谱模式:清零变量
            Lamp = 0;
            Ramp = 0;
            // 若 ADC 处于忙状态,可取消转换(可选)
            if (adc_busy) {
                adc_busy = 0;
                ADC_CONTR &= ~0x10;       // 清除标志
            }
        }
    }
}

// 切换频谱模式的配色方案(仅当当前模式为频谱模式时有效)
void ChangeSpectrumScheme(void) {

}

/************* 按键扫描 **************/
void Key_Scan(void)
{
    // 模式键 P2.6
    if (ModeKey == 0 && ModeKeyPrev == 1) {
        mode_press_start = system_ticks;
        mode_long_handled = 0;
    }
    if (ModeKey == 0 && !mode_long_handled) {
        if (system_ticks - mode_press_start > 500) {
            light_on = !light_on;
            LightNeedSave = 1;
            if (!light_on) last_mode_before_off = mode;
            UART_SendString(light_on ? "Key Light ON\r\n" : "Key Light OFF\r\n");
            mode_long_handled = 1;
        }
    }
    if (ModeKey == 1 && ModeKeyPrev == 0) {
        if (!mode_long_handled) {
            if (light_on) {
                mode = (mode + 1) % MODE_NUM;
                ModeNeedSave = 1;
                UART_SendString("Mode "); UART_SendNumber(mode); UART_SendString("\r\n");
            } else {
                light_on = 1;
                mode = last_mode_before_off;
                LightNeedSave = 1;
                UART_SendString("Key Light ON\r\n");
            }
        }
    }
    ModeKeyPrev = ModeKey;

    // 颜色键 P3.4
    if (ColorKey == 0 && ColorKeyPrev == 1) {
        color_press_start = system_ticks;
        color_long_handled = 0;
    }
    if (ColorKey == 0 && !color_long_handled) {
        if (system_ticks - color_press_start > 500) {
            if (!light_on && !learn_mode) {
                learn_mode = 1;
                learn_timeout = system_ticks + 10000UL;
                UART_SendString("Learn mode, press remote...\r\n");
            } else if (light_on) {
                UART_SendString("Turn off light to learn\r\n");
            }
            color_long_handled = 1;
        }
    }
    if (ColorKey == 1 && ColorKeyPrev == 0) {
        if (!color_long_handled && light_on) {
            ColorIdex = (ColorIdex + 1) % COLOR_NUM;
            ColorNeedSave = 1;
            UART_SendString("Color "); UART_SendNumber(ColorIdex); UART_SendString("\r\n");
        }
    }
    ColorKeyPrev = ColorKey;

    // 亮度键 P3.5
    if (BrightKey == 0 && BrightKeyPrev == 1 && light_on) {
        static unsigned char level = 5;
        unsigned char i, b;
        for (i = 1; i <= BRIGHTNESS_LEVEL_MAX; i++) {
            b = BRIGHTNESS_MIN + (i-1) * (BRIGHTNESS_MAX - BRIGHTNESS_MIN) / (BRIGHTNESS_LEVEL_MAX - 1);
            if (global_brightness <= b) { level = i; break; }
        }
        level++;
        if (level > BRIGHTNESS_LEVEL_MAX) level = 1;
        global_brightness = BRIGHTNESS_MIN + (level-1) * (BRIGHTNESS_MAX - BRIGHTNESS_MIN) / (BRIGHTNESS_LEVEL_MAX - 1);
        brightness_level = level;
        BrightNeedSave = 1;
        UART_SendString("Bright "); UART_SendNumber(level); UART_SendString("/10\r\n");
    }
    BrightKeyPrev = BrightKey;

    // 速度键 P3.6
    if (SpeedKey == 0 && SpeedKeyPrev == 1 && light_on) {
        speed_level = (speed_level % BRIGHTNESS_LEVEL_MAX) + 1;
        speed_value = (11 - speed_level) * 3 + 8;
        SpeedNeedSave = 1;
        UART_SendString("Speed "); UART_SendNumber(speed_level); UART_SendString("/10\r\n");
    }
    SpeedKeyPrev = SpeedKey;
}

/************* 定时器0(阈值法解码,适配 EV1527) **************/
void Timer0Init(void) {
    AUXR |= 0x80;            // ?¨ê±?÷0éè???a1T?£ê?
    TMOD &= 0xF0;
    TMOD |= 0x01;            // 16???¨ê±?÷£?ê??ˉ??×°
   
    TL0 = 0x20;
    TH0 = 0xFE;
    TF0 = 0;
    TR0 = 1;
    ET0 = 1;
}

void Timer0_ISR(void) interrupt 1
{
    static unsigned char tick_50 = 0;
    bit current_level;

    TL0 = 0x20; TH0 = 0xFE;
    if (++tick_50 >= 50) { tick_50 = 0; system_ticks++; }

    current_level = RF_IN;
    if (current_level != rf_last_level) {
        if (current_level == 1) {   // 上升沿(低电平结束)
            if (rf_low_cnt > SYNC_LOW_MIN && rf_low_cnt < SYNC_LOW_MAX) {
                if (!rf_sync_detected || rf_bit_cnt == 0) {
                    rf_sync_detected = 1;
                    rf_bit_cnt = 0;
                    rf_code_32 = 0;
                    sync_timeout = 0;
                }
            } else {
                // 低电平异常,但只有未采集数据时才复位
                if (rf_sync_detected && rf_bit_cnt == 0) {
                    rf_sync_detected = 0;
                }
            }
            rf_low_cnt = 0;
        } else {   // 下降沿(高电平结束)
            if (rf_sync_detected && rf_bit_cnt < 24) {
                if (rf_high_cnt > BIT1_THRESHOLD)
                    rf_code_32 = (rf_code_32 << 1) | 1;
                else
                    rf_code_32 = (rf_code_32 << 1) | 0;
                rf_bit_cnt++;
                if (rf_bit_cnt == 24) {
                    rf_rec_ok = 1;
                    rf_sync_detected = 0;
                    sync_timeout = 0;
                }
            }
            rf_high_cnt = 0;
        }
        rf_last_level = current_level;
    } else {
        if (current_level) rf_high_cnt++;
        else rf_low_cnt++;
    }
}

void ProcessRFCommand(void)
{
    unsigned long cur_code, cur_time;
    unsigned char key_code;
    unsigned int addr;

    if (!rf_rec_ok) return;
    rf_rec_ok = 0;

    cur_code = rf_code_32;
    cur_time = system_ticks;

    // 过滤全0或全1
    if (cur_code == 0 || cur_code == 0xFFFFFF) {
        UART_SendString("RF invalid (all zeros/ones)\r\n");
        return;
    }

    addr = (unsigned int)(cur_code >> 8);
    key_code = (unsigned char)(cur_code & 0xFF);

    // ---- 学习模式 ----
    if (learn_mode) {
        learn_mode = 0;
        learned_addr = addr;
        SaveLearnedAddr();
        UART_SendString("Learn success, addr: 0x");
        UART_SendHexByte((unsigned char)(addr >> 8));
        UART_SendHexByte((unsigned char)(addr & 0xFF));
        UART_SendString("\r\n");
        return;
    }

    // 过滤地址无效
    if (addr == 0x0000 || addr == 0xFFFF) {
        UART_SendString("RF invalid address\r\n");
        return;
    }

    // 调试打印(可选)
    UART_SendString("RF raw: 0x");
    UART_SendHexByte((unsigned char)(cur_code >> 16));
    UART_SendHexByte((unsigned char)(cur_code >> 8));
    UART_SendHexByte((unsigned char)cur_code);
    UART_SendString("\r\n");

    // 去重
    if (cur_code == last_rf_code && (cur_time - last_rf_time) < 300) {
        UART_SendString("RF duplicate ignored\r\n");
        return;
    }
    last_rf_code = cur_code;
    last_rf_time = cur_time;

    // 地址匹配
    if (addr != learned_addr) {
        UART_SendString("RF addr mismatch\r\n");
        return;
    }

    // ---- 处理按键(直接映射) ----
    UART_SendString("RF Key: 0x");
    UART_SendHexByte(key_code);
    UART_SendString("\r\n");

    switch (key_code) {
        // ---------- 灯光控制按键 ----------
        case REMOTE_KEY_POWER_ON:          // 0x01 电源开关
            light_on = !light_on;
            LightNeedSave = 1;
            if (!light_on) last_mode_before_off = mode;
            UART_SendString(light_on ? "RF Power ON\r\n" : "RF Power OFF\r\n");
            SendEventToBluetooth(0x01);    // 事件ID 0x01 -> 电源键
            break;

        case REMOTE_KEY_LIGHT_MODE_UP:     // 0x02 模式+
            if (light_on) {
                mode = (mode + 1) % MODE_NUM;
                ModeNeedSave = 1;
                UART_SendString("RF Mode+\r\n");
                SendEventToBluetooth(0x02);    // 模式+
            }
            break;

        case REMOTE_KEY_LIGHT_ON_OFF:      // 0x03 开关(独立)
            light_on = !light_on;
            LightNeedSave = 1;
            if (light_on) {
                mode = last_mode_before_off;
            } else {
                last_mode_before_off = mode;
            }
            UART_SendString(light_on ? "RF Light ON (toggle)\r\n" : "RF Light OFF (toggle)\r\n");
            SendEventToBluetooth(0x03);    // 开关事件
            break;

        case REMOTE_KEY_LIGHT_SPEED_DOWN:  // 0x04 速度-
            if (light_on && speed_level > 1) {
                speed_level--;
                speed_value = (11 - speed_level) * 3 + 8;
                SpeedNeedSave = 1;
                UART_SendString("RF Speed-\r\n");
                SendEventToBluetooth(0x04);    // 速度-
            }
            break;

        case REMOTE_KEY_DEMO:              // 0x05 演示(恢复默认)
            mode = 0;
            brightness_level = 10;
            global_brightness = 255;
            ColorIdex = 0;
            speed_level = 5;
            speed_value = (11 - speed_level) * 3 + 8;
            light_on = 1;
            last_mode_before_off = 0;
            ModeNeedSave = LightNeedSave = BrightNeedSave = ColorNeedSave = SpeedNeedSave = 1;
            UART_SendString("RF Demo reset\r\n");
            SendEventToBluetooth(0x05);    // DEMO事件
            break;

        case REMOTE_KEY_LIGHT_SPEED_UP:    // 0x06 速度+
            if (light_on && speed_level < 10) {
                speed_level++;
                speed_value = (11 - speed_level) * 3 + 8;
                SpeedNeedSave = 1;
                UART_SendString("RF Speed+\r\n");
                SendEventToBluetooth(0x06);    // 速度+
            }
            break;

        case REMOTE_KEY_COLOR_UP:          // 0x07 颜色+
            if (light_on) {
                ColorIdex = (ColorIdex + 1) % COLOR_NUM;
                ColorNeedSave = 1;
                UART_SendString("RF Color+\r\n");
                SendEventToBluetooth(0x07);    // 颜色+
            }
            break;

        case REMOTE_KEY_LIGHT_MODE_DOWN:   // 0x08 模式-
            if (light_on) {
                mode = (mode - 1 + MODE_NUM) % MODE_NUM;
                ModeNeedSave = 1;
                UART_SendString("RF Mode-\r\n");
                SendEventToBluetooth(0x08);    // 模式-
            }
            break;

        case REMOTE_KEY_LIGHT_BRIGHT_UP:   // 0x09 亮度+
            if (light_on && brightness_level < 10) {
                brightness_level++;
                global_brightness = BRIGHTNESS_MIN + (brightness_level - 1) * (BRIGHTNESS_MAX - BRIGHTNESS_MIN) / (BRIGHTNESS_LEVEL_MAX - 1);
                BrightNeedSave = 1;
                UART_SendString("RF Bright+\r\n");
                SendEventToBluetooth(0x09);    // 亮度+
            }
            break;

        case REMOTE_KEY_LIGHT_COLOR_DOWN:  // 0x0A 颜色-
            if (light_on) {
                ColorIdex = (ColorIdex - 1 + COLOR_NUM) % COLOR_NUM;
                ColorNeedSave = 1;
                UART_SendString("RF Color-\r\n");
                SendEventToBluetooth(0x0A);    // 颜色-
            }
            break;

        case REMOTE_KEY_MODE_DOWN:         // 0x0B 模式转换(原用于频谱切换)
            // 当前无频谱功能,但可发送事件给蓝牙(若需要)
            UART_SendString("RF Mode convert (ignored)\r\n");
            SendEventToBluetooth(0x0B);    // 模式转换事件(蓝牙可能无响应)
            break;

        case REMOTE_KEY_LIGHT_BRIGHT_DOWN: // 0x0C 亮度-
            if (light_on && brightness_level > 1) {
                brightness_level--;
                global_brightness = BRIGHTNESS_MIN + (brightness_level - 1) * (BRIGHTNESS_MAX - BRIGHTNESS_MIN) / (BRIGHTNESS_LEVEL_MAX - 1);
                BrightNeedSave = 1;
                UART_SendString("RF Bright-\r\n");
                SendEventToBluetooth(0x0C);    // 亮度-
            }
            break;

        // ---------- 音频控制按键(仅转发蓝牙,不控制灯光) ----------
        case REMOTE_KEY_PRE:          // 0x0D 上一曲
            UART_SendString("RF Audio: PRE\r\n");
            SendEventToBluetooth(0x0D);
            break;

        case REMOTE_KEY_NEXT:         // 0x0E 下一曲
            UART_SendString("RF Audio: NEXT\r\n");
            SendEventToBluetooth(0x0E);
            break;

        case REMOTE_KEY_PLAY:         // 0x0F 播放/暂停
            UART_SendString("RF Audio: PLAY\r\n");
            SendEventToBluetooth(0x0F);
            break;

        case REMOTE_KEY_VOL_DOWN:     // 0x10 音量减
            UART_SendString("RF Audio: VOL-\r\n");
            SendEventToBluetooth(0x10);
            break;

        case REMOTE_KEY_VOL_UP:       // 0x11 音量加
            UART_SendString("RF Audio: VOL+\r\n");
            SendEventToBluetooth(0x11);
            break;

        case REMOTE_KEY_EQ:           // 0x12 EQ切换
            UART_SendString("RF Audio: EQ\r\n");
            SendEventToBluetooth(0x12);
            break;

        default:
            UART_SendString("RF unknown key\r\n");
            break;
    }
}

// 长按/短按超时检测
void CheckRFTimeout(void)
{
    unsigned long cur_time = system_ticks;
    if (rf_mode_press_detected) {
        if (!rf_mode_long_handled && rf_mode_frame_cnt >= 4) {
            rf_mode_long_flag = 1;
            rf_mode_long_handled = 1;
            rf_mode_short_handled = 1;
        }
        if ((cur_time - rf_mode_first_time) > 500) {
            if (!rf_mode_long_handled && !rf_mode_short_handled && rf_mode_frame_cnt >= 2) {
                rf_mode_short_flag = 1;
            }
            rf_mode_press_detected = 0;
            rf_mode_frame_cnt = 0;
            rf_mode_short_handled = 0;
            rf_mode_long_handled = 0;
        }
    }
}

// 执行RF命令
void HandleRFCommands(void)
{
    if (rf_mode_long_flag) {
        rf_mode_long_flag = 0;
        light_on = !light_on;
        LightNeedSave = 1;
        if (!light_on) last_mode_before_off = mode;
        UART_SendString(light_on ? "Long ON\r\n" : "Long OFF\r\n");
    }
    if (rf_mode_short_flag) {
        rf_mode_short_flag = 0;
        if (light_on) {
            mode = (mode + 1) % MODE_NUM;
            ModeNeedSave = 1;
            UART_SendString("Mode "); UART_SendNumber(mode); UART_SendString("\r\n");
        } else {
            light_on = 1;
            mode = last_mode_before_off;
            LightNeedSave = 1;
            UART_SendString("RF Light ON\r\n");
        }
    }
}

//================ 灯效 ==============
void Effect_RainbowFlow(void)
{
    static unsigned int hue_offset = 0;
    unsigned int i, hue;
    unsigned char rgb[3];
    hue_offset = (hue_offset + speed_level * 2) % 360;
    for (i = 0; i < LED_NUM; i++) {
        hue = (i * 360 / LED_NUM + hue_offset) % 360;
        HSV_to_RGB(hue, 255, 255, rgb);
        SetLedRGB((unsigned char)i, rgb[1], rgb[0], rgb[2]);
    }
}

void Effect_Breath(void)
{
    static unsigned char breath = 10;
    static signed char breath_dir = 1;
    static unsigned char breath_color_idx = 0;
    unsigned char r, g, b;
    unsigned int i;
    breath += breath_dir * ((speed_level + 2) / 3);
    if (breath >= 250) { breath = 250; breath_dir = -1; }
    if (breath <= 0) {
        breath = 0;
        if (breath_dir == -1) breath_color_idx = (breath_color_idx + 1) % 7;
        breath_dir = 1;
    }
    r = (unsigned int)colorTable[breath_color_idx][0] * breath / 255;
    g = (unsigned int)colorTable[breath_color_idx][1] * breath / 255;
    b = (unsigned int)colorTable[breath_color_idx][2] * breath / 255;
    for (i = 0; i < LED_NUM; i++) SetLedRGB((unsigned char)i, r, g, b);
}

void Effect_StaticColor(void)
{
    unsigned int i;
    for (i = 0; i < LED_NUM; i++) {
        SetLedRGB((unsigned char)i, colorTable[ColorIdex][0], colorTable[ColorIdex][1], colorTable[ColorIdex][2]);
    }
}

//================ LED 操作 ==============
void SetLedRGB(unsigned char i, unsigned char r, unsigned char g, unsigned char b)
{
    if (!light_on) { r = g = b = 0; }
    led_RGB[i][0] = g;
    led_RGB[i][1] = r;
    led_RGB[i][2] = b;
}

void ClearAllLED(void)
{
    unsigned int i;
    for (i = 0; i < LED_NUM; i++) {
        led_RGB[i][0] = 0;
        led_RGB[i][1] = 0;
        led_RGB[i][2] = 0;
    }
}

//================ HSV to RGB ==============
void HSV_to_RGB(unsigned int hue, unsigned char sat, unsigned char val, unsigned char *rgb)
{
    unsigned char r, g, b, region, remainder;
    unsigned int p, q, t;
    if (sat == 0) { r = g = b = val; }
    else {
        if (hue >= 360) hue %= 360;
        region = hue / 60;
        remainder = (hue % 60) * 255 / 60;
        p = val * (255 - sat) / 255;
        q = val * (255 - (sat * remainder / 255)) / 255;
        t = val * (255 - (sat * (255 - remainder) / 255)) / 255;
        switch (region) {
            case 0: r = val; g = t; b = p; break;
            case 1: r = q; g = val; b = p; break;
            case 2: r = p; g = val; b = t; break;
            case 3: r = p; g = q; b = val; break;
            case 4: r = t; g = p; b = val; break;
            default: r = val; g = p; b = q; break;
        }
    }
    rgb[0] = g; rgb[1] = r; rgb[2] = b;
}

//================ SPI ==============
void LoadSPI(void)
{
    unsigned char xdata *px;
    unsigned int i, j;
    unsigned char k, dat;
    for (i = 0; i < SPI_NUM; i++) led_SPI[i] = 0;
    px = &led_RGB[0][0];
    for (i = 0, j = 0; i < (LED_NUM * 3); i++) {
        dat = *px; px++;
        for (k = 0; k < 4; k++) {
            if (dat & 0x80) led_SPI[j] = 0xE0;
            else led_SPI[j] = 0x80;
            if (dat & 0x40) led_SPI[j] |= 0x0E;
            else led_SPI[j] |= 0x08;
            dat <<= 2;
            j++;
        }
    }
}

void SPI_Config(unsigned char SPI_io, unsigned char SPI_speed)
{
    SPCTL = (SPI_speed & 3) | (1<<6) | (1<<4); // SPEN=1, MSTR=1
    P_SW1 = (P_SW1 & ~0x0c) | ((SPI_io << 2) & 0x0c);
    HSCLKDIV = 1;
    SPI_CLKDIV = 1;
    if (SPI_io == 0) {
        P1M1 &= ~0xA0;
        P1M0 |= 0xA0;
        P15 = 0;
    }
}

void SPI_DMA_TxTRIG(unsigned char xdata *TxBuf, unsigned int num) {
    unsigned int j;
   
    // ---- DMA 传输期间关闭 ADC 电源 ----
    ADC_CONTR &= ~0x80;     // 关闭 ADC 电源
   
    SPSTAT = 0xC0;
    HSSPI_CFG = 3;
    HSSPI_CFG2 = (1<<5) | (1<<4) | 3;
    j = (unsigned int)TxBuf;
    DMA_SPI_TXAH = (unsigned char)(j >> 8);
    DMA_SPI_TXAL = (unsigned char)j;
    DMA_SPI_AMTH = (unsigned char)((num - 1) / 256);
    DMA_SPI_AMT = (unsigned char)((num - 1) % 256);
    DMA_SPI_ITVH = 0;
    DMA_SPI_ITVL = 0;
    DMA_SPI_STA = 0x00;
    DMA_SPI_CFG = (1<<7) | (1<<6) | (0<<2) | 0;
    DMA_SPI_CFG2 = 0;
    DMA_SPI_CR = (1<<7) | (1<<6) | 1;
    B_SPI_DMA_busy = 1;
   
    // ---- DMA 启动后恢复 ADC 电源 ----
    ADC_CONTR |= 0x80;      // 恢复 ADC 电源
}

void SPI_DMA_ISR(void) interrupt DMA_SPI_VECTOR {
    DMA_SPI_STA = 0;
    B_SPI_DMA_busy = 0;
    EADC = 1;               // DMA 传输完成,恢复 ADC 中断
}

//================ 串口 ==============
void UART_Init(void)
{
   
    SCON = 0x50;
   
    T2L = (unsigned char)(BRT & 0xFF);
    T2H = (unsigned char)(BRT >> 8);
    AUXR |= 0x15;
    ES = 1;
}

void UART_SendChar(unsigned char dat)
{     if (!uart_print_enabled) return;
        SBUF = dat;
        while (!TI);
        TI = 0;
}
void UART_SendString(char *str)
{     if (!uart_print_enabled) return;
            while (*str)
            UART_SendChar(*str++);
}
void UART_SendNumber(unsigned char num)
{    if (!uart_print_enabled) return;
    if (num >= 100) { UART_SendChar(num/100 + '0'); UART_SendChar((num%100)/10 + '0'); UART_SendChar(num%10 + '0'); }
    else if (num >= 10) { UART_SendChar(num/10 + '0'); UART_SendChar(num%10 + '0'); }
    else UART_SendChar(num + '0');
}
void UART_SendHexByte(unsigned char byte)
{
    unsigned char n;
    n = (byte >> 4) & 0x0F; UART_SendChar((n < 10) ? (n + '0') : (n - 10 + 'A'));
    n = byte & 0x0F; UART_SendChar((n < 10) ? (n + '0') : (n - 10 + 'A'));
}
void UART_SendUint16(unsigned int num) {
        if (!uart_print_enabled) return;
    if (num >= 10000) UART_SendChar(num / 10000 + '0');
    if (num >= 1000)  UART_SendChar((num % 10000) / 1000 + '0');
    if (num >= 100)   UART_SendChar((num % 1000) / 100 + '0');
    if (num >= 10)    UART_SendChar((num % 100) / 10 + '0');
    UART_SendChar(num % 10 + '0');
}

void UART_ISR(void) interrupt 4
{
    unsigned char ch;
    static unsigned char bin_state = 0;      // 0:等待头, 1:收到头, 2:收到长度, 3:收到数据
    static unsigned char bin_buf[8];
    static unsigned char bin_idx = 0;
    static unsigned char bin_len = 0;
    static unsigned char bin_sum = 0;

    if (RI) {
        RI = 0;
        ch = SBUF;

        // ----- 二进制帧接收状态机(优先处理) -----
        switch (bin_state) {
            case 0:  // 等待帧头 0xEA
                if (ch == FRAME_HEADER) {
                    bin_state = 1;
                    bin_idx = 0;
                    bin_sum = 0;
                    bin_buf[bin_idx++] = ch;
                    bin_sum += ch;
                } else {
                    goto ascii_parse;   // 不是二进制帧,交给 ASCII 处理
                }
                break;
            case 1:  // 接收长度
                bin_buf[bin_idx++] = ch;
                bin_sum += ch;
                bin_len = ch;
                if (bin_len >= 1 && bin_len <= 4) {  // 数据长度 1~4 字节
                    bin_state = 2;
                } else {
                    bin_state = 0;   // 长度非法,复位
                }
                break;
            case 2:  // 接收数据(包括类型和参数)
                bin_buf[bin_idx++] = ch;
                bin_sum += ch;
                if (bin_idx == bin_len + 2) {   // 头+长度+数据(不含校验)
                    bin_state = 3;
                }
                break;
            case 3:  // 接收校验和
                if (ch == bin_sum) {
                    // 校验通过,处理命令
                    ProcessBluetoothCommand(bin_buf, bin_len + 3); // 总长度 = 头+长度+数据+校验
                } else {
                    // 校验失败,丢弃
                }
                bin_state = 0;
                break;
        }
        return;   // 二进制帧处理完毕,不再进入 ASCII

ascii_parse:
        // ----- ASCII 命令(原有逻辑) -----
        if (ch == '\r' || ch == '\n') {
            if (uartCount > 0) {
                uartBuf[uartCount] = '\0';
                uartCmdReady = 1;
            }
            uartCount = 0;
        } else if (uartCount < UART_BUF_SIZE - 1) {
            uartBuf[uartCount++] = ch;
        }
    }
    if (TI) {
        TI = 0;
    }
}

void ParseUartCommand(void)
{
    unsigned char *cmd = uartBuf;
    unsigned int num;
    if (!uartCmdReady) return;
    uartCmdReady = 0;

    if (strncmp(cmd, "THRESHOLD", 9) == 0) {
        // 此命令已不再需要,保留为空
    }
    else if (strncmp(cmd, "MODE", 4) == 0) {
        num = atoi(cmd + 5);
        if (num < MODE_NUM) {
            mode = (unsigned char)num;
            ModeNeedSave = 1;
            if (!light_on) { light_on = 1; LightNeedSave = 1; }
            UART_SendString("Mode "); UART_SendNumber(mode); UART_SendString("\r\n");
        }
    }
    else if (strncmp(cmd, "SPEED", 5) == 0) {
        num = atoi(cmd + 6);
        if (num >= 1 && num <= 10) {
            speed_level = (unsigned char)num;
            speed_value = (11 - speed_level) * 3 + 8;
            SpeedNeedSave = 1;
            UART_SendString("Speed "); UART_SendNumber(speed_level); UART_SendString("\r\n");
        }
    }
    else if (strncmp(cmd, "BRIGHT", 6) == 0) {
        num = atoi(cmd + 7);
        if (num >= 1 && num <= 10) {
            brightness_level = (unsigned char)num;
            global_brightness = 10 + (245 * (brightness_level - 1)) / 9;
            BrightNeedSave = 1;
            UART_SendString("Bright "); UART_SendNumber(brightness_level); UART_SendString("\r\n");
        }
    }
    else if (strncmp(cmd, "COLOR", 5) == 0) {
        num = atoi(cmd + 6);
        if (num < COLOR_NUM) {
            ColorIdex = (unsigned char)num;
            ColorNeedSave = 1;
            UART_SendString("Color "); UART_SendNumber(ColorIdex); UART_SendString("\r\n");
        }
    }
    else if (strncmp(cmd, "ON", 2) == 0) {
        if (!light_on) { light_on = 1; LightNeedSave = 1; UART_SendString("Light ON\r\n"); }
    }
    else if (strncmp(cmd, "OFF", 3) == 0) {
        if (light_on) { light_on = 0; last_mode_before_off = mode; LightNeedSave = 1; ClearAllLED(); UART_SendString("Light OFF\r\n"); }
    }
    else if (strncmp(cmd, "STATUS", 6) == 0) {
        UART_SendString("Mode:"); UART_SendNumber(mode);
        UART_SendString(" Speed:"); UART_SendNumber(speed_level);
        UART_SendString(" Bright:"); UART_SendNumber(brightness_level);
        UART_SendString(" Color:"); UART_SendNumber(ColorIdex);
        UART_SendString(" Light:"); UART_SendString(light_on ? "ON" : "OFF");
        UART_SendString("\r\n");
    }
    else if (strncmp(cmd, "LEARN", 5) == 0) {
        if (!light_on && !learn_mode) {
            learn_mode = 1;
            learn_timeout = system_ticks + 10000UL;
            UART_SendString("Learn mode, press remote...\r\n");
        } else if (light_on) {
            UART_SendString("Turn off light first\r\n");
        } else {
            UART_SendString("Already learning\r\n");
        }
    }
}

//================ EEPROM ==============
void IapIdle(void) { IAP_CONTR = 0; IAP_CMD = 0; IAP_TRIG = 0; IAP_ADDRH = 0x80; IAP_ADDRL = 0; }
unsigned char IapRead(unsigned int addr)
{
    unsigned char dat;
    IAP_CONTR = 0x80;
    IAP_TPS = (unsigned char)(MAIN_Fosc / 1000000L);
    IAP_CMD = 1;
    IAP_ADDRL = (unsigned char)(addr & 0xFF);
    IAP_ADDRH = (unsigned char)(addr >> 8);
    IAP_TRIG = 0x5A; IAP_TRIG = 0xA5;
    _nop_();
    dat = IAP_DATA;
    IapIdle();
    return dat;
}
void IapProgram(unsigned int addr, unsigned char dat)
{
    IAP_CONTR = 0x80;
    IAP_TPS = (unsigned char)(MAIN_Fosc / 1000000L);
    IAP_CMD = 2;
    IAP_ADDRL = (unsigned char)(addr & 0xFF);
    IAP_ADDRH = (unsigned char)(addr >> 8);
    IAP_DATA = dat;
    IAP_TRIG = 0x5A; IAP_TRIG = 0xA5;
    _nop_();
    IapIdle();
}
void IapErase(unsigned int addr)
{
    IAP_CONTR = 0x80;
    IAP_TPS = (unsigned char)(MAIN_Fosc / 1000000L);
    IAP_CMD = 3;
    IAP_ADDRL = (unsigned char)(addr & 0xFF);
    IAP_ADDRH = (unsigned char)(addr >> 8);
    IAP_TRIG = 0x5A; IAP_TRIG = 0xA5;
    _nop_();
    IapIdle();
}

void LoadAllParams(void)
{
    unsigned char val;
      unsigned char h = IapRead(EE_ADDR_REMOTE_H);
    unsigned char l = IapRead(EE_ADDR_REMOTE_L);
    // ---- 加载常用参数(0x0000 扇区) ----
    val = IapRead(EE_ADDR_MODE);
    if (val < MODE_NUM) mode = val; else mode = 0;

    val = IapRead(EE_ADDR_LIGHT_ON);
    if (val == 0 || val == 1) light_on = val; else light_on = 1;

    val = IapRead(EE_ADDR_BRIGHT_LEV);
    if (val >= 1 && val <= 10) {
        brightness_level = val;
        global_brightness = 10 + (245 * (brightness_level - 1)) / 9;
    } else {
        brightness_level = 10;
        global_brightness = 255;
    }

    val = IapRead(EE_ADDR_COLOR_IDX);
    if (val < COLOR_NUM) ColorIdex = val; else ColorIdex = 0;

    val = IapRead(EE_ADDR_SPEED_LEV);
    if (val >= 1 && val <= 10) speed_level = val; else speed_level = 5;
    speed_value = (11 - speed_level) * 3 + 8;

    // ---- 加载遥控器地址(独立扇区 0x0200) ----
  
    if (h != 0xFF || l != 0xFF) {
        learned_addr = (h << 8) | l;
    } else {
        learned_addr = REMOTE_ADDR;      // 未学习,使用默认
    }

    UART_SendString("EEPROM loaded\r\n");
}
void SaveAllParams(void)
{
    EA = 0;
    IapErase(0x0000);                    // 仅擦除 0x0000 扇区
    IapProgram(EE_ADDR_MODE, mode);
    IapProgram(EE_ADDR_LIGHT_ON, light_on ? 1 : 0);
    IapProgram(EE_ADDR_BRIGHT_LEV, brightness_level);
    IapProgram(EE_ADDR_COLOR_IDX, ColorIdex);
    IapProgram(EE_ADDR_SPEED_LEV, speed_level);
    // 注意:遥控器地址不在此保存
    EA = 1;
    UART_SendString("EEPROM saved\r\n");
}
void SaveLearnedAddr(void)
{
    EA = 0;
    IapErase(0x0200);                    // 擦除 0x0200 扇区(含地址位置)
    IapProgram(EE_ADDR_REMOTE_H, (unsigned char)(learned_addr >> 8));
    IapProgram(EE_ADDR_REMOTE_L, (unsigned char)(learned_addr & 0xFF));
    EA = 1;
    UART_SendString("Remote address saved\r\n");
}
void delay_ms(unsigned int ms)
{
    unsigned int i;
    do {
        i = MAIN_Fosc / 6000;
        while (--i);
    } while (--ms);
}
void delay_us(unsigned int us)
{
    while (us--) {
        _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_(); _nop_(); _nop_();
        _nop_(); _nop_();
    }
}


// 修改颜色(循环切换)
void ChangeColor(void)
{
    ColorIdex = (ColorIdex + 1) % COLOR_NUM;
    ColorNeedSave = 1;
}

// 修改亮度(循环切换等级)
void ChangeBrightness(void)
{
    brightness_level = (brightness_level % BRIGHTNESS_LEVEL_MAX) + 1;
    global_brightness = BRIGHTNESS_MIN + (brightness_level - 1) * (BRIGHTNESS_MAX - BRIGHTNESS_MIN) / (BRIGHTNESS_LEVEL_MAX - 1);
    BrightNeedSave = 1;
}

// 修改速度(循环切换等级)
void ChangeSpeed(void)
{
    speed_level = (speed_level % BRIGHTNESS_LEVEL_MAX) + 1;
    speed_value = (11 - speed_level) * 3 + 8;
    SpeedNeedSave = 1;
}
// 发送给蓝牙模块的帧(通过 UART1)
void SendToBluetooth(unsigned char type, unsigned char data_len, unsigned char data1, unsigned char data2)
{
    unsigned char buf[8], idx = 0, i, sum = 0;
    buf[idx++] = FRAME_HEADER;
    buf[idx++] = 1 + data_len;
    buf[idx++] = type;
    buf[idx++] = data1;
    if (data_len == 2) buf[idx++] = data2;
    for (i = 0; i < idx; i++) sum += buf[i];
    buf[idx++] = sum;

    EA = 0;
    for (i = 0; i < idx; i++) {
        SBUF = buf[i];
        while (!TI);
        TI = 0;
    }
    EA = 1;
}

// 发送蓝牙事件
void SendEventToBluetooth(unsigned char event_id)
{
    unsigned char cmd = 0;
    unsigned char i;
    for (i = 0; i < sizeof(lc_event_map)/sizeof(lc_event_map[0]); i++) {
        if (lc_event_map[i].event_id == event_id) {
            cmd = lc_event_map[i].cmd_byte;
            break;
        }
    }
    if (cmd != 0) {
        SendToBluetooth(FRAME_TYPE_LC, 1, cmd, 0xFF);
    }
}
// 处理蓝牙二进制命令(帧格式:EA 03 05 11 02)
void ProcessBluetoothCommand(unsigned char *buf, unsigned char len)
{
    unsigned char type = buf[2];   // 第3字节为类型
    unsigned char data1 = buf[3];
    unsigned char data2 = (len >= 5) ? buf[4] : 0xFF;

    if (type == FRAME_TYPE_BT) {
        switch (data1) {
            case 0x0B:  // 关灯
                if (light_on) {
                    light_on = 0;
                    last_mode_before_off = mode;
                    LightNeedSave = 1;
                    ClearAllLED();
                }
                break;
            case 0x0C:  // 开灯
                if (!light_on) {
                    light_on = 1;
                    mode = last_mode_before_off;
                    LightNeedSave = 1;
                }
                break;
            case 0x03:  // 灯光开关(单按)
                light_on = !light_on;
                LightNeedSave = 1;
                ClearAllLED();
                break;
            case 0x43:  // 模式+
            case 0x07:
                mode = (mode + 1) % MODE_NUM;
                ModeNeedSave = 1;
                break;
            case 0x42:  // 模式-
                mode = (mode - 1 + MODE_NUM) % MODE_NUM;
                ModeNeedSave = 1;
                break;
            case 0x45:  // COLOR+
                ChangeColor();
                break;
            case 0x44:  // COLOR-
                ColorIdex = (ColorIdex == 0) ? COLOR_NUM - 1 : ColorIdex - 1;
                ColorNeedSave = 1;
                break;
            case 0x46:  // 亮度+
                ChangeBrightness();
                break;
            case 0x47:  // 亮度-
                if (brightness_level > 1) {
                    brightness_level--;
                    global_brightness = BRIGHTNESS_MIN + (brightness_level-1) * (BRIGHTNESS_MAX - BRIGHTNESS_MIN) / (BRIGHTNESS_LEVEL_MAX - 1);
                    BrightNeedSave = 1;
                }
                break;
            case 0x48:  // 速度+
                ChangeSpeed();
                break;
            case 0x49:  // 速度-
                if (speed_level > 1) {
                    speed_level--;
                    speed_value = (11 - speed_level) * 3 + 8;
                    SpeedNeedSave = 1;
                }
                break;
            case 0x00:  // 恢复默认
                mode = 0;
                brightness_level = 10;
                global_brightness = 255;
                ColorIdex = 0;
                speed_level = 5;
                speed_value = (11 - speed_level) * 3 + 8;
                light_on = 1;
                last_mode_before_off = 0;
                ModeNeedSave = LightNeedSave = BrightNeedSave = ColorNeedSave = SpeedNeedSave = 1;
                break;
            case 0x11:  // 进入学习模式
                if (!light_on && !learn_mode) {
                    learn_mode = 1;
                    learn_timeout = system_ticks + 10000UL;
                    UART_SendString("Learn mode by BT\r\n");
                }
                break;
            case 0x16:  // 白灯模式(若有)
                // 可忽略或根据需要实现
                break;
            default:
                UART_SendString("BT unknown\r\n");
                break;
        }
    }
    // 可扩展其他类型
}
// ==================== ADC 相关函数(新增/替换) ====================

// ---- ADC 初始化(官方推荐配置) ----
void ADC_Init(void) {
    // 配置 P1.0 和 P1.1 为高阻输入(ADC 输入)
    P1M0 &= ~0x03;
    P1M1 |= 0x03;
   
    // 设置 ADC 时序(官方推荐值)
    P_SW2 |= 0x80;          // 允许访问扩展寄存器
    ADCTIM = 0x3F;          // 采样时间最大
    P_SW2 &= 0x7F;
   
    ADCCFG = 0x2F;          // 右对齐,12位,时钟分频
    ADC_CONTR = 0x80;       // 开启 ADC 电源
    _nop_(); _nop_(); _nop_(); _nop_();
    delay_ms(2);            // 等待电源稳定
   
    // 不使能 ADC 中断(使用查询方式)
    EADC = 0;
   
    // 清除可能残留的标志
    ADC_CONTR &= ~0x10;
}

// ---- 非阻塞:启动一次 ADC 转换 ----
void ADC_StartConversion(unsigned char channel) {
    // 确保 ADC 电源开启(如果之前未关闭)
    ADC_CONTR = 0x80;                       // 电源开启
    ADC_CONTR |= 0x40;                      // 启动转换
    ADC_CONTR = (ADC_CONTR & 0xF0) | (channel & 0x07); // 设置通道
}

// ---- 非阻塞:检查 ADC 转换是否完成,完成则返回结果 ----
bit ADC_GetResult(unsigned int *val) {
    if (ADC_CONTR & 0x10) {                 // 检查 ADC_FLAG
        *val = ((unsigned int)(ADC_RES & 0x0F) << 8) | ADC_RESL;
        ADC_CONTR &= ~0x10;                 // 清除标志
        return 1;
    }
    return 0;
}

// ---- 双声道频谱灯效(模式 0) ----
void Effect_AudioSpectrum(void) {
    unsigned char half = LED_NUM / 2;
    unsigned char height_L, height_R;
    unsigned char i, r, g, b;
    unsigned int val_L, val_R;
    static unsigned int last_L = 0, last_R = 0;
      static unsigned char test_cnt = 0;
    // 采样左声道(P1.0)
    //val_L = SampleADC(0);
    // 采样右声道(P1.1)
   // val_R = SampleADC(1);
      // ---- 测试用固定值 ----
    height_L = half / 2;    // 固定 50% 高度
    height_R = half / 2;
   
    // 或者使用正弦波模拟
  
    test_cnt++;
    height_L = (unsigned long)(128 + 127 * sin(test_cnt * 0.1)) * half / 255;
    // 简单平滑滤波(减少跳动)
    val_L = (last_L * 3 + val_L) / 4;
    val_R = (last_R * 3 + val_R) / 4;
    last_L = val_L;
    last_R = val_R;
   
    // 映射到灯珠高度(0 ~ half)
    height_L = (unsigned long)val_L * half / 4095;
    height_R = (unsigned long)val_R * half / 4095;
    if (height_L > half) height_L = half;
    if (height_R > half) height_R = half;
   
    r = colorTable[ColorIdex][0];
    g = colorTable[ColorIdex][1];
    b = colorTable[ColorIdex][2];
   
    // 左半部分:从左到右上升
    for (i = 0; i < half; i++) {
        if (i < height_L)
            SetLedRGB(i, r, g, b);
        else
            SetLedRGB(i, 0, 0, 0);
    }
    // 右半部分:从中间往右上升
    for (i = 0; i < half; i++) {
        if (i < height_R)
            SetLedRGB(half + i, r, g, b);
        else
            SetLedRGB(half + i, 0, 0, 0);
    }
}      请问有没有大神可以指导一下!谢谢!


回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:316
  • 最近打卡:2026-07-22 06:56:11
已绑定手机

70

主题

874

回帖

3225

积分

论坛元老

DIY玩家

积分
3225
发表于 2026-7-6 14:32:25 | 显示全部楼层
使用SPI会占用几个引脚
可能外设共用引脚
通常可以切换
永怀这学徒心
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-7 16:32:47 | 显示全部楼层
截图202607071632454475.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:467
  • 最近打卡:2026-07-21 09:31:46

851

主题

1万

回帖

2万

积分

管理员

积分
24394
发表于 2026-7-7 16:33:09 | 显示全部楼层

现在我已经获取了足够的信息。让我来分析这个问题并给出专业的解答。


问题分析:AI8051U SPI DMA 驱动 WS2812 时,读取 ADC 导致 WS2812 无法点亮

感谢您提供的帖子链接。这是一个非常典型的问题,我来帮您分析根本原因并给出解决方案。

根本原因分析

根据手册 "DMA,支持解放CPU的外设到外设传输(P2P)" 章节中的 DMA使用注意事项,问题出在 CKCON 寄存器设置DMA与CPU同时访问XRAM的总线仲裁 上:

将CKCON设置为0时,xdata访问速度设置为最快,写xdata为2T,读xdata为3T。此时如果硬件DMA和CPU同时访问xdata,会出现总线竞争,需要进行总线仲裁。由于芯片内部的仲裁电路设计不够完美,会导致CPU访问xdata出错。

将CKCON设置为1,可完美解决硬件DMA和CPU同时访问xdata出现的仲裁错误问题。

您的程序中 CKCON = 0;,当 SPI DMA 正在传输 WS2812 数据时,如果 CPU 去读取 ADC(ADC 操作涉及访问 XRAM 寄存器),就会发生 DMA 与 CPU 同时访问 XRAM 总线的情况,导致 SPI DMA 传输出错,WS2812 时序被破坏,灯珠无法正常点亮。

解决方案

方案一(推荐):将 CKCON 设置为 1

这是手册明确推荐的方案,可完美解决问题:

CKCON = 1;   // 设置访问片内 xdata 速度,写xdata为3T,读xdata为4T

代价是 xdata 访问会多 1 个时钟周期,但对于 WS2812 的 SPI DMA 驱动来说影响极小。

方案二:确保 DMA 传输期间 CPU 不访问 xdata

手册指出:

当硬件DMA访问xdata过程中,用户程序不得访问xdata。用户程序只能在DMA不工作时才能读写DMA在xdata中的缓冲区。

这意味着您需要在启动 SPI DMA 后,等待 DMA 传输完成(查询 SPIIF 标志或等待 DMA 中断),然后再进行 ADC 读取。但 WS2812 需要持续刷新(通常每帧都要重新发送数据),这个方案实现起来比较麻烦。

具体修改建议

在您的 main() 函数初始化部分,将:

CKCON = 0;   // 设置访问片内的 xdata 速度,赋值为 0 表示用最快速度访问

改为:

CKCON = 1;   // 设置访问片内的 xdata 速度,赋值为 1 可解决 DMA 与 CPU 同时访问 xdata 的仲裁问题

其他可能的影响因素

  1. ADC 采样时间过长阻塞了主循环:如果 ADC 采用查询方式(while(!ADC_FLAG);),且 ADC 时钟设置较慢,长时间的等待可能导致 WS2812 刷新不及时。建议使用 ADC 中断方式,或确保 ADC 转换速度足够快。

  2. SPI DMA 间隔时间设置:手册提到 DMA_SPI_ITVH 和 DMA_SPI_ITVL 上电默认值为 3,如需加快 DMA 速度,可设置为 0:

    DMA_SPI_ITVH = 0;   // 减少 SPI DMA 字节间间隔时间
    DMA_SPI_ITVL = 0;
    
  3. 开启高速 SPI 的 FIFO 模式:可提高 SPI+DMA 传输效率:

    HSSPI_CFG2 |= 0x10;   // 使能 FIFO 模式
    HSSPI_CFG2 |= 0x20;   // 使能高速 SPI 模式
    

总结

最核心的解决方案:将 CKCON 从 0 改为 1,即可解决 SPI DMA 与 ADC 同时访问 XRAM 导致的总线仲裁问题。

请先尝试这个修改,如果问题仍然存在,欢迎继续反馈,我再帮您进一步排查。


🔍 搜索 "STC 单片机 论坛 技术讨论"

⏱️ 查找用时 14.3s | 总回答用时 24.1s

回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 09:16 , Processed in 0.078737 second(s), 59 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表