Skip to content

XU316与MCU通信协议参考

1. 基础数据结构

1.1 环形缓冲区

#define RING_BUFFER_SIZE 256  // 环形缓冲区大小,用于UART通信缓存

typedef struct {
    uint8_t buffer[RING_BUFFER_SIZE];  // 数据存储区
    volatile uint16_t head;    // 写入位置指针
    volatile uint16_t tail;    // 读取位置指针
    volatile uint16_t count;   // 当前数据量
} ring_buffer_t;

1.2 音频模式定义

// 音频模式配置数组,每个模式包含5字节配置数据
static const uint8_t audio_modes[][5] = {
    {0x00, 0x80, 0xa9, 0x00, 0x01},  // USB-no-mqa模式
    {0x00, 0x80, 0x01, 0x00, 0x02},  // UAC1模式
    {0x10, 0x80, 0x65, 0x10, 0x03},  // COAX模式
    {0x00, 0x80, 0x65, 0x10, 0x04},  // OPT模式
    {0x00, 0x80, 0xc5, 0x08, 0x05},  // SPDIF OUT模式
    {0x00, 0x82, 0xd5, 0x81, 0x06},  // I2S IN模式
    {0x20, 0x80, 0x65, 0x10, 0x07}   // HDMI模式
};

// 对应的模式名称数组
static const char *mode_names[] = {
    "USB-no-mqa",
    "UAC1",
    "COAX",
    "OPT",
    "SPDIF OUT",
    "I2S IN",
    "HDMI"
};

2. 初始化与配置

2.1 设备初始化信息打印

#define AUDIO_MODE_COUNT (sizeof(audio_modes) / sizeof(audio_modes[0]))

void print_init_info(void)
{
    log_data(LOG_USER, "----------Device initialized----------\n");
    // 打印UAC1.0设备信息
    log_data(LOG_USER, "VID1: %02X%02X", mcu_data.vid_uac1[0], mcu_data.vid_uac1[1]);
    log_data(LOG_USER, "PID1: %02X%02X", mcu_data.pid_uac1[0], mcu_data.pid_uac1[1]);
    // 打印UAC2.0设备信息
    log_data(LOG_USER, "VID2: %02X%02X", mcu_data.vid_uac2[0], mcu_data.vid_uac2[1]);
    log_data(LOG_USER, "PID2: %02X%02X", mcu_data.pid_uac2[0], mcu_data.pid_uac2[1]);
    // 打印产品信息
    log_data(LOG_USER, "Manufacturer: %s", mcu_data.product_manufacturer);
    log_data(LOG_USER, "Name: %s", mcu_data.product_name);
    log_data(LOG_USER, "Serial: %s", mcu_data.product_serial);
    // 打印CRC校验信息
    log_data(LOG_USER, "Basic Info CRC: %02X%02X%02X%02X", 
             mcu_data.basic_info_crc[0], mcu_data.basic_info_crc[1], 
             mcu_data.basic_info_crc[2], mcu_data.basic_info_crc[3]);
    log_data(LOG_USER, "Power Config CRC: %02X%02X%02X%02X", 
             mcu_data.power_cfg_crc[0], mcu_data.power_cfg_crc[1], 
             mcu_data.power_cfg_crc[2], mcu_data.power_cfg_crc[3]);
}

2.2 环形缓冲区初始化

void ring_buffer_init(void) {
    uart_ring_buffer.head = 0;    // 初始化写入位置
    uart_ring_buffer.tail = 0;    // 初始化读取位置
    uart_ring_buffer.count = 0;   // 初始化数据计数
}

2.3 产品信息初始化

void xu316_init(void)
{
    // 默认音频模式配置(USB UAC2.0)
    uint8_t audio_mode[5] = {0x00, 0x80, 0xa9, 0x00, 0x01};

    // 配置UAC1.0设备信息
    mcu_data.vid_uac1[0] = 0x20;
    mcu_data.vid_uac1[1] = 0xB1;  // VID = 0x20B1
    mcu_data.pid_uac1[0] = 0x00;
    mcu_data.pid_uac1[1] = 0x17;  // PID = 0x0017

    // 配置UAC2.0设备信息
    mcu_data.vid_uac2[0] = 0x20;
    mcu_data.vid_uac2[1] = 0xB1;  // VID = 0x20B1
    mcu_data.pid_uac2[0] = 0x00;
    mcu_data.pid_uac2[1] = 0x16;  // PID = 0x0016

    // 配置产品信息
    memcpy(mcu_data.product_manufacturer, "Phaten", 6);
    // 设置产品名称
    memcpy(mcu_data.product_name, "XMOS XU316", 11);
    // 设置产品序列号
    memcpy(mcu_data.product_serial, "123456789ABCDEF", 15);

    // 计算并存储基础信息CRC32校验值(56字节)
    crc = calculate_crc32(mcu_data.vid_uac1, 56);
    mcu_data.basic_info_crc[0] = (crc >> 24) & 0xFF;
    mcu_data.basic_info_crc[1] = (crc >> 16) & 0xFF;
    mcu_data.basic_info_crc[2] = (crc >> 8) & 0xFF;
    mcu_data.basic_info_crc[3] = crc & 0xFF;

    // 初始化设备状态和音频配置
    mcu_data.startup_status = 0x00;
    memcpy(&mcu_data.audio_mode, audio_mode, 5);

    // 配置静音持续时间(400ms)
    mcu_data.mute_duration[0] = MUTE_DURATION_CONFIG >> 8;
    mcu_data.mute_duration[1] = MUTE_DURATION_CONFIG;

    // 初始化音量设置
    mcu_data.mic_volume = MIC_VOLUME_CONFIG;
    // 初始化左右声道DAC音量
    mcu_data.dac_l_volume = DAC_L_VOLUME_CONFIG;
    mcu_data.dac_r_volume = DAC_R_VOLUME_CONFIG;

    // 计算并存储电源配置CRC32校验值(10字节)
    crc = calculate_crc32((uint8_t *)&mcu_data.audio_mode, 0x0a);
    mcu_data.power_cfg_crc[0] = (crc >> 24) & 0xFF;
    mcu_data.power_cfg_crc[1] = (crc >> 16) & 0xFF;
    mcu_data.power_cfg_crc[2] = (crc >> 8) & 0xFF;
    mcu_data.power_cfg_crc[3] = crc & 0xFF;

    // 初始化通信缓冲区
    ring_buffer_init();
}

3. 通信缓冲区操作

3.1 环形缓冲区操作函数

// 写入数据到环形缓冲区
uint8_t ring_buffer_write(uint8_t *data, uint16_t len) {
    uint16_t i;
    for(i = 0; i < len; i++) {
        if(uart_ring_buffer.count >= RING_BUFFER_SIZE) {
            return 0;  // 缓冲区满,写入失败
        }
        uart_ring_buffer.buffer[uart_ring_buffer.head] = data[i];
        uart_ring_buffer.head = (uart_ring_buffer.head + 1) % RING_BUFFER_SIZE;
        uart_ring_buffer.count++;
    }
    return 1;  // 写入成功
}

// 从环形缓冲区读取数据
uint8_t ring_buffer_read(uint8_t *data, uint16_t len) {
    uint16_t i;
    if(uart_ring_buffer.count < len) {
        return 0;  // 数据不足
    }
    for(i = 0; i < len; i++) {
        data[i] = uart_ring_buffer.buffer[uart_ring_buffer.tail];
        uart_ring_buffer.tail = (uart_ring_buffer.tail + 1) % RING_BUFFER_SIZE;
        uart_ring_buffer.count--;
    }
    return 1;  // 读取成功
}

// 预读数据(不移动读取指针)
uint8_t ring_buffer_peek(uint8_t *data, uint16_t len) {
    uint16_t i;
    uint16_t temp_tail = uart_ring_buffer.tail;
    for(i = 0; i < len; i++) {
        data[i] = uart_ring_buffer.buffer[temp_tail];
        temp_tail = (temp_tail + 1) % RING_BUFFER_SIZE;
    }
    return 0;  // 预读成功
}

4. 校验和计算

4.1 CRC32计算

uint32_t calculate_crc32(const uint8_t *buffer, uint32_t length)
{
    uint32_t crc = 0xFFFFFFFF;
    const uint32_t poly = 0xEDB88320;  // CRC32多项式

    for (size_t i = 0; i < length; i++) {
        crc ^= buffer[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 1) {
                crc = (crc >> 1) ^ poly;
            } else {
                crc >>= 1;
            }
        }
    }
    return ~crc;  // 返回CRC32校验值
}

// 计算简单校验和
uint8_t xu316_calc_checksum(uint8_t *data, uint8_t len)
{
    uint8_t sum = 0;
    for (uint8_t i = 0; i < len; i++) {
        sum += data[i];
    }
    return sum;
}

5. 通信协议处理

5.1 帧校验

// 校验帧数据完整性
uint8_t uart_frame_check(uint8_t *buf, uint8_t len)
{
    // 检查最小长度要求
    if (len < 6) {  // 帧头(2) + 版本(1) + 命令(1) + 长度(2) + 校验(1)
        return 0;
    }

    // 验证帧头
    if (buf[0] != FRAME_HEADER_H || buf[1] != FRAME_HEADER_L) {
        return 0;
    }

    // 获取数据长度(大端模式)
    uint16_t data_len = buf[4];

    // 验证数据长度合法性
    if (data_len > 256 || len < (data_len + 6)) {
        return 0;
    }

    // 计算并验证校验和
    uint8_t sum = xu316_calc_checksum(buf, data_len + 5);
    if (sum != buf[data_len + 5]) {
        return 0;
    }

    LOG_TEMP(LOG_RECV, "", buf, len);
    return 1;
}

5.2 数据帧封装

int xu316_pack_frame(uint8_t cmd, uint8_t *data, uint8_t len)
{
    uint8_t tx_data[256] = {0};
    if (len >= 255) {
        return 0;
    }

    // 构建帧头
    tx_data[0] = FRAME_HEADER_H;
    tx_data[1] = FRAME_HEADER_L;
    tx_data[2] = PROTOCOL_VERSION_RX;
    tx_data[3] = cmd;
    tx_data[4] = len;

    // 复制数据负载
    if (data && len > 0) {
        memcpy(tx_data + 5, data, len);
    }

    // 计算并添加校验和
    tx_data[len + 5] = xu316_calc_checksum(tx_data, len + 5);
    len += 6;

    // 发送数据
    usart_dma_send(tx_data, len);
    LOG_INFO("Sending frame: cmd=0x%02X, len=%d", cmd, len);
    LOG_TEMP(LOG_SEND, "", tx_data, len);
    return len;
}

5.3 帧长度检查

int check_frame_length(uint8_t *buf, uint16_t len) {
    // 验证帧头
    if (buf[0] != FRAME_HEADER_H || buf[1] != FRAME_HEADER_L) {
        LOG_DEBUG("Frame header check failed %02x  %02x", buf[0], buf[1]);
        return -1;
    }
    // 获取数据长度(大端模式)
    int data_len = buf[4];
    // 返回完整帧长度
    return data_len + 6;
}

5.4 数据接收处理

void uart_data_process(void)
{
    uint8_t peek_buffer[8];      // 预读缓冲区
    uint8_t process_buffer[256]; // 处理缓冲区
    int frame_length;

    while(uart_ring_buffer.count >= 6) {  // 至少需要6字节才能开始检查
        // 预读帧头信息
        ring_buffer_peek(peek_buffer, 6);

        // 调试输出
        for(int i = 0; i < 6; i++) {
            LOG_DEBUG("peek_buffer[%d]: %02x", i, peek_buffer[i]);
        }

        // 检查帧长度
        frame_length = check_frame_length(peek_buffer, 6);
        if(frame_length < 0) {
            // 帧无效,丢弃一个字节
            uint8_t dummy;
            ring_buffer_read(&dummy, 1);
            LOG_ERROR("Frame length check failed %02x", dummy);
            continue;
        }

        // 检查是否有足够的数据
        if(uart_ring_buffer.count < frame_length) {
            LOG_ERROR("Not enough data");
            break;  // 等待更多数据
        }

        // 读取并处理完整帧
        if(ring_buffer_read(process_buffer, frame_length)) {
            uart_data_parse();
        }
    }
}

5.5 数据解析处理

int uart_data_parse(void)
{
    int ret = 0;
    uint8_t cmd = 0;
    uint16_t data_len = 0;
    uint16_t rx_len = 0;
    uint8_t tmp;
    static uint8_t buffer[256] = {0};
    rx_len = g_rx_count;

    // 验证帧完整性
    ret = uart_frame_check((uint8_t *)g_rx_data, rx_len);
    if (ret == 0) {
        LOG_ERROR("Frame check failed %d", rx_len);
        LOG_TEMP(LOG_RECV, "", g_rx_data, rx_len);
        return -1;
    }

    // 解析帧信息
    data_len = rx_len - 6;
    cmd = g_rx_data[3];
    if (g_rx_data[4] != data_len) {
        LOG_ERROR("Data length mismatch: expected %d, got %d", g_rx_data[4], data_len);
        return -1;
    }

    LOG_DEBUG("Received frame: cmd=0x%02X, len=%d", cmd, data_len);
    LOG_VERBOSE("--------------------------------");
    LOG_VERBOSE("cmd : %02X", cmd);
    memcpy(buffer, g_rx_data + 5, data_len);

    // 根据命令类型处理数据
    switch (cmd) {
        case 0x00:  // 启动信息
            // ... 处理启动信息 ...
            break;

        case 0x01:  // 基础信息
            // ... 处理基础信息 ...
            break;

        case 0x02:  // 电源配置
            // ... 处理电源配置 ...
            break;

        // ... 其他命令处理 ...

        default:
            LOG_ERROR("Unknown command: 0x%02X", cmd);
            break;
    }

    return ret;
}