diff --git a/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/产品内容相关.md b/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/产品内容相关.md new file mode 100644 index 0000000..00e01e9 --- /dev/null +++ b/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/产品内容相关.md @@ -0,0 +1,92 @@ + + +``` +#define SWAP16(x) ((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00)) +#define SWAP32(x) ((((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000)) +/*协议解析*/ +#define UART_FRAME_HEAD 0xAA +#define UART_FRAME_TAIL 0x55 +#define HEX_UPPERCASE 0 +#define HEX_LOWERCASE 1 +#define HEX_TABLE HEX_UPPERCASE +// 数据帧结构 +typedef struct +{ + uint8_t head; // 帧头 0xAA + uint8_t cmd; // 命令字 + uint8_t len; // 数据长度 + uint8_t data[32]; // 数据区 + uint8_t check; // 校验和 + uint8_t tail; // 帧尾 0x55 +} uart_frame_t; + +/*串口*/ +/* buffer sizes */ +#define RX_BUFFER_SIZE 256 +#define TX_BUFFER_SIZE 256 +#define TX_BUFFER_NUM 2 // 双缓冲 + +typedef struct +{ + uint8_t buffer[TX_BUFFER_SIZE]; + uint16_t size; + uint8_t busy; +} TX_BUFFER; + +typedef struct +{ + TX_BUFFER tx_buf[TX_BUFFER_NUM]; + uint8_t curr_buf; + uint8_t next_buf; + uint8_t dma_sending; +} UART_TX_MANAGER; +typedef enum +{ + LOG_SEND, + LOG_RECV, + LOG_USER, + LOG_ERR +} log_dir_t; +``` + + + +``` +// I2S主从模式配置 +#define I2S_MODE I2S_MODE_MASTER // I2S主模式,XU316作为I2S主设备 +// 同步模式配置 +#define SYNC_MODE SYNC // 同步模式,用于音频时钟同步 +// MIDI接口配置 +#define MIDI_MODE MIDI_MODE_ENABLE // MIDI功能启用 +// SPDIF输入接口配置 +#define SPDIF_IN_MODE SPDIF_IN_MODE_ENABLE // SPDIF输入功能启用 +// SPDIF输出接口配置 +#define SPDIF_OUT_MODE SPDIF_OUT_MODE_ENABLE // SPDIF输出功能启用 +// ADAT输入接口配置 (ADAT是一种8通道数字音频传输格式) +#define ADAT_IN_MODE ADAT_IN_MODE_ENABLE // ADAT输入功能启用 +// ADAT输出接口配置 +#define ADAT_OUT_MODE ADAT_OUT_MODE_ENABLE // ADAT输出功能启用 +// DSD输出接口配置 (DSD是直接数字流,用于高解析度音频) +#define DSD_OUT_MODE DSD_OUT_MODE_ENABLE // DSD输出功能启用 + +// 初始化音频采样率 +#define AUDIO_SAMPLE_RATE (uint8_t)AUDIO_SAMPLE_RATE_44100 +#define MQA_MODE MQA_MODE_DISABLE +#define AUDIO_CLASS AUDIO_CLASS_UAC1 +#define AUDIO_WIDTH AUDIO_WIDTH_24 +// 初始化音频通道 +#define AUDIO_INPUT_CHANNEL 9 +#define AUDIO_OUTPUT_CHANNEL 3 +// 初始化音频类型 +#define AUDIO_MODE USB_MODE +// 静音时间 2 0-65535(ms) +#define MUTE_DURATION 100 +// 麦克风默认音量 +#define MIC_VOLUME 10 +// DAC左声道默认音量 1 见DAC音量字段说明 +#define DAC_L_VOLUME 30 +// DAC右声道默认音量 1 见DAC音量字段说明 +#define DAC_R_VOLUME 20 + +``` + diff --git a/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/命令的相关宏.md b/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/命令的相关宏.md new file mode 100644 index 0000000..69eb964 --- /dev/null +++ b/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/命令的相关宏.md @@ -0,0 +1,248 @@ +**命令的相关宏** + +**// 帧格式定义** + +``` +#define FRAME_HEADER_H 0x55 +#define FRAME_HEADER_L 0xAA +#define PROTOCOL_VERSION 0x01 +#define PROTOCOL_VERSION_RX 0x03 +``` + +**/* MCU 命令接收数据长度宏定义 */** + +``` +#define CMD00_XU316_DATA_LEN 0x11 // 0x00 命令:开始启动(启动选项) +#define CMD01_XU316_DATA_LEN 0x00 // 0x01 命令:读取产品信息(60字节固件信息) +#define CMD02_XU316_DATA_LEN 0x00 // 0x02 命令:读取上电配置(14字节配置参数) +#define CMD03_XU316_DATA_LEN 0x00 // 0x03 命令:获取音频模式(5字节模式参数) +#define CMD04_XU316_DATA_LEN 0x00 // 0x04 命令:获取用户配置(14字节用户设置) +#define CMD05_XU316_DATA_LEN 0x15 // 0x05 命令:启动完成(无数据字段) + +#define CMDF1_XU316_DATA_LEN 0x03 // 0xF1 命令:版本号查询(3字节版本信息) + +#define CMD20_XU316_DATA_LEN 0x14 // 0x20 命令:设置音量 +#define CMD21_XU316_DATA_LEN 0x00 // 0x21 命令:音效模式 +#define CMD22_XU316_DATA_LEN 0x02 // 0x22 命令:设备状态查询 +#define CMD23_XU316_DATA_LEN 0x00 // 0x23 命令:蓝牙控制 +#define CMD24_XU316_DATA_LEN 0x02 // 0x24 命令:发送播放音量 +#define CMD25_XU316_DATA_LEN 0x01 // 0x25 命令:发送录音音量 +#define CMD27_XU316_DATA_LEN 0x00 // 0x27 命令:特殊静音解除(无数据字段) +#define CMD28_XU316_DATA_LEN 0x01 // 0x28 命令:音频格式延迟设置(1字节参数) + +#define CMD_HID_TRANSPARENT_DATA_LEN 0x39 // 0xEE 命令:HID透传/OTA升级(57字节) +``` + +**/* MCU 命令回复数据长度宏定义 */** + +``` +#define CMD00_MCU_DATA_LEN 0x01 // 0x00 命令:开始启动(启动选项) +#define CMD01_MCU_DATA_LEN 0x3C // 0x01 命令:读取产品信息(60字节固件信息) +#define CMD02_MCU_DATA_LEN 0x0E // 0x02 命令:读取上电配置(14字节配置参数) +#define CMD03_MCU_DATA_LEN 0x05 // 0x03 命令:获取音频模式(5字节模式参数) +#define CMD04_MCU_DATA_LEN 0x0E // 0x04 命令:获取用户配置(14字节用户设置) +#define CMD05_MCU_DATA_LEN 0x00 // 0x05 命令:启动完成(无数据字段) + +#define CMDF1_MCU_DATA_LEN 0x00 // 0xF1 命令:版本号查询(无数据字段) + +#define CMD20_MCU_DATA_LEN 0x00 // 0x20 命令:设置音量 +#define CMD21_MCU_DATA_LEN 0x01 // 0x21 命令:音效模式 +#define CMD22_MCU_DATA_LEN 0x00 // 0x22 命令:设备状态查询 +#define CMD23_MCU_DATA_LEN 0x05 // 0x23 命令:设置音频模式 +#define CMD24_MCU_DATA_LEN 0x00 // 0x24 命令:发送播放音量 +#define CMD25_MCU_DATA_LEN 0x00 // 0x25 命令:发送录音音量 +#define CMD27_MCU_DATA_LEN 0x00 // 0x27 命令:特殊静音解除(无数据字段) +#define CMD28_MCU_DATA_LEN 0x00 // 0x28 命令:音频格式延迟设置(无数据字段) + +#define CMD_HID_TRANSPARENT_MCU_DATA_LEN 0x39 // 0xEE 命令:HID透传/OTA升级(57字节) +``` + + + +**MCU 通信协议命令字枚举** + +**遵循分层分类原则,按功能模块划分命名空间** + +``` +typedef enum +{ + /****** 基础控制命令 (0x00-0x05) ******/ + CMD_STARTUP = 0x00, // 系统启动 + CMD_GET_PRODUCT_INFO = 0x01, // 产品信息查询 + CMD_GET_BOOT_CFG = 0x02, // 上电配置读取 + CMD_GET_AUD_MODE = 0x03, // 音频模式获取 + CMD_GET_USER_CFG = 0x04, // 用户配置获取 + CMD_STARTUP_COMPLETE = 0x05, // 启动完成通知 + + CMD_GET_VERSION = 0xF1, // 版本号查询 + + /****** 业务控制命令 (0x20-0x2F) ******/ + CMD_REPORT_STATUS = 0x20, // 状态报告 + CMD_MEDIA_CONTROL = 0x21, // 媒体控制 + CMD_SET_PLAY_FORMAT = 0x22, // 音频格式设置 + CMD_SET_AUDIO_MODE = 0x23, // 音频模式设置 + CMD_SET_PLAY_VOL = 0x24, // 播放音量设置 + CMD_SET_REC_VOL = 0x25, // 录音音量设置 + CMD_SPECIAL_UNMUTE = 0x27, // 特殊静音解除 + CMD_SET_AUDIO_FORMAT_DELAY = 0x28, // 音频格式延迟设置 + + CMD_HID_TRANSPARENT = 0xEE, // HID透传/OTA升级命令 + + CMD_COUNT = CMD_SET_REC_VOL + 1 // 当前支持命令总数 + +} mcu_command_t; +``` + + + +**启动选项枚举(Bitmask模式)** + +**使用uint8_t确保单字节存储,支持多选项组合** + +``` +typedef enum +{ + BOOT_OPTION_USE_DEFAULTS = 0x00, // 使用所有默认配置 + BOOT_OPTION_UPDATE_BASIC_INFO = 0x01, // 更新基础产品信息 (bit0) + BOOT_OPTION_UPDATE_POWER_CFG = 0x02, // 更新上电配置信息 (bit1) + BOOT_OPTION_UPDATE_OTHER_CFG = 0x04 // 预留配置,目前没用 (bit2) +} boot_option_t; + +typedef enum +{ + MEDIA_KEY_VOLUME_UP = 0x00, // 音量增加 + MEDIA_KEY_VOLUME_DOWN = 0x01, // 音量减小 + MEDIA_KEY_PLAY_PAUSE = 0x02, // 播放/暂停 + MEDIA_KEY_NEXT_TRACK = 0x03, // 下一曲目 + MEDIA_KEY_PREV_TRACK = 0x04, // 上一曲目 + MEDIA_KEY_FAST_FORWARD = 0x05, // 快进 + MEDIA_KEY_REWIND = 0x06, // 快退 + MEDIA_KEY_MUTE = 0x07 // 静音切换 + + // 预留扩展位(0x08-0xFF为协议保留区域) + +} media_control_t; +``` + + + +**音频流格式枚举(兼容AES67-2020标准)** + +**显式指定底层类型为uint8_t确保1字节存储** + +``` +typedef enum +{ + // PCM采样率格式 (0x00-0x10) + AUDIO_PCM_44100 = 0x00, + AUDIO_PCM_48000 = 0x01, + AUDIO_PCM_88200 = 0x02, + AUDIO_PCM_96000 = 0x03, + AUDIO_PCM_176400 = 0x04, + AUDIO_PCM_192000 = 0x05, + AUDIO_PCM_352800 = 0x06, + AUDIO_PCM_384000 = 0x07, + AUDIO_PCM_705600 = 0x08, + AUDIO_PCM_768000 = 0x09, + AUDIO_PCM_1441200 = 0x0A, + AUDIO_PCM_1536000 = 0x0B, + AUDIO_PCM_32000 = 0x0C, + AUDIO_PCM_64000 = 0x0D, + AUDIO_PCM_128000 = 0x0E, + AUDIO_PCM_256000 = 0x0F, + AUDIO_PCM_512000 = 0x10, + + // DSD格式 (0x11-0x15) + AUDIO_DSD_64 = 0x11, + AUDIO_DSD_128 = 0x12, + AUDIO_DSD_256 = 0x13, + AUDIO_DSD_512 = 0x14, + AUDIO_DSD_1024 = 0x15, + + // MQA格式 (0x16-0x2D) + AUDIO_MQA_44100 = 0x16, + AUDIO_MQA_88200 = 0x17, + AUDIO_MQA_176400 = 0x18, + AUDIO_MQA_352800 = 0x19, + AUDIO_MQA_705600 = 0x1A, + AUDIO_MQA_1411200 = 0x1B, + AUDIO_MQA_2822400 = 0x1C, + AUDIO_MQA_5644800 = 0x1D, + + // -- 基于48kHz系列 -- + AUDIO_MQA_48000 = 0x1E, + AUDIO_MQA_96000 = 0x1F, + AUDIO_MQA_192000 = 0x20, + AUDIO_MQA_384000 = 0x21, + AUDIO_MQA_768000 = 0x22, + AUDIO_MQA_1536000 = 0x23, + AUDIO_MQA_3072000 = 0x24, + AUDIO_MQA_6144000 = 0x25, + + // -- 其他基频系列 -- + AUDIO_MQA_64000 = 0x26, + AUDIO_MQA_128000 = 0x27, + AUDIO_MQA_256000 = 0x28, + AUDIO_MQA_512000 = 0x29, + AUDIO_MQA_1024000 = 0x2A, + AUDIO_MQA_2048000 = 0x2B, + AUDIO_MQA_4096000 = 0x2C, + AUDIO_MQA_8192000 = 0x2D, + + // 特殊保留值 + AUDIO_NO_USED = 0xFF + +} audio_format_t; +``` + + + +**音频类型枚举** + +**显式指定底层类型为uint8_t确保1字节存储** + +``` +typedef enum +{ + AUDIO_TYPE_PCM = 0x00, // PCM标准音频 + AUDIO_TYPE_RESERVE = 0x01, // 协议保留字段 + AUDIO_TYPE_MQA = 0x02, // MQA编码音频 + AUDIO_TYPE_MQB = 0x03, // MQB编码音频(二级扩展) + AUDIO_TYPE_MQA_STUDIO = 0x04, // MQA Studio母带级 + AUDIO_TYPE_DSD = 0x05 // Direct Stream Digital + + // 0x06-0xFF为未来扩展保留 + +} audio_type_t; +``` + +``` +typedef uint8_t byte_pair[2]; +// 命令数据结构体 +typedef struct __attribute__((packed)) +{ + uint8_t boot_option; // 0x00: 启动选项数据 + // 产品基础信息 + uint8_t vid_uac1[2]; // UAC1.0厂商ID + uint8_t pid_uac1[2]; // UAC1.0产品ID + uint8_t vid_uac2[2]; // UAC2.0厂商ID + uint8_t pid_uac2[2]; // UAC2.0产品ID + uint8_t product_manufacturer[16]; // 制造商名称 + uint8_t product_name[16]; // 产品名称 + uint8_t product_serial[16]; // 序列号 + uint8_t basic_info_crc[4]; // 基础信息CRC32 + // 用户配置就是上电配置信息也是应用运行参数 + uint8_t startup_status; // 启动状态 + uint8_t audio_mode[5]; + uint8_t mute_duration[2]; // 静音时间(ms) + uint8_t mic_volume; // 麦克风音量也是录音音量 + uint8_t dac_l_volume; // 左声道音量(0-255) + uint8_t dac_r_volume; // 右声道音量(0-255) + uint8_t power_cfg_crc[4]; // 上电配置CRC32 + // 应用运行参数 + uint8_t media_control; // 媒体控制命令 + uint8_t audio_format; // 音频格式代码 + uint8_t audio_type; // 音频类型代码 +} mcu_data_t; +``` + diff --git a/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/命令相关.md b/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/命令相关.md new file mode 100644 index 0000000..d2f64de --- /dev/null +++ b/zh/docs/dev_doc/sdk_examples/XU316和MCU通讯的示例代码/命令相关.md @@ -0,0 +1,589 @@ +**命令相关** + +``` +#define RING_BUFFER_SIZE 256 // 环形缓冲区大小 + +typedef struct { + uint8_t buffer[RING_BUFFER_SIZE]; + volatile uint16_t head; // 写入位置 + volatile uint16_t tail; // 读取位置 + volatile uint16_t count; // 当前数据量 +} ring_buffer_t; +``` + +``` +// 定义音频模式数组 +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" +}; +``` + +**上电时的打印信息** + +``` +#define AUDIO_MODE_COUNT (sizeof(audio_modes) / sizeof(audio_modes[0])) + +void print_init_info(void) +{ + log_data(LOG_USER, "----------Device initialized----------\n"); + 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]); + 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); + 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]); +} +``` + +``` +// 初始化环形缓冲区 +void ring_buffer_init(void) { + uart_ring_buffer.head = 0; + uart_ring_buffer.tail = 0; + uart_ring_buffer.count = 0; +} +``` + +**产品信息初始化** + +``` +void xu316_init(void) +{ + // Usb-no-mqa: 0x00, 0x80, 0xa9, 0x00, 0x01 + // UAC1: 0x00, 0x80, 0x01, 0x00, 0x02 + // COAX: 0x10, 0x80, 0x65, 0x10, 0x03 + // OPT: 0x00, 0x80, 0x65, 0x10, 0x04 + // SPDIF OUT: 0x00, 0x80, 0xc5, 0x08, 0x05 + // I2S IN: 0x00, 0x82, 0xd5, 0x81, 0x06 + // HDMI: 0x20, 0x80, 0x65, 0x10, 0x07 + + uint8_t audio_mode[5] = {0x00, 0x80, 0xa9, 0x00, 0x01}; // usb uac2.0 + // 设置 UAC1.0 设备的 VID (Vendor ID) + mcu_data.vid_uac1[0] = 0x20; + mcu_data.vid_uac1[1] = 0xB1; // VID = 0x20B1 + + // 设置 UAC1.0 设备的 PID (Product ID) + mcu_data.pid_uac1[0] = 0x00; + mcu_data.pid_uac1[1] = 0x17; // PID = 0x0017 + + // 设置 UAC2.0 设备的 VID + mcu_data.vid_uac2[0] = 0x20; + mcu_data.vid_uac2[1] = 0xB1; // VID = 0x20B1 + + // 设置 UAC2.0 设备的 PID + 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校验值(包含VID、PID、制造商信息等,总长56字节) + + crc = calculate_crc32(mcu_data.vid_uac1, 56); + // 存储CRC校验值(大端序) + 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); + // 设置静音持续时间(0x190 = 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校验值(音频模式相关配置,长度0x0a字节) + + crc = calculate_crc32((uint8_t *)&mcu_data.audio_mode, 0x0a); + // 存储CRC校验值(大端序) + 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(); + +} +``` + +**从环形缓冲区读写数据** + +``` +// 写入数据到环形缓冲区 +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; // 预读成功 + +} +``` + + + + **计算CRC32** + +``` +uint32_t calculate_crc32(const uint8_t *buffer, uint32_t length) +{ + uint32_t crc = 0xFFFFFFFF; + const uint32_t poly = 0xEDB88320; + + 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; + +} + +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; +} +``` + + + +``` +// 校验帧数据 +// buf: 输入数据数组 +// len: 数据长度 +// 返回值: 0-校验失败,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 = 0; + sum = xu316_calc_checksum(buf, data_len + 5); + + // 检查校验和 + if (sum != buf[data_len + 5]) + { + return 0; + } + + LOG_TEMP(LOG_RECV, "", buf, len); + return 1; +} +``` + + + +**封装数据帧** + +``` +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((uint8_t *)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; // 返回总帧长度 + +} +``` + + + + **检查帧的完整性并返回帧长度** + +``` +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; +} +``` + + + +``` +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(); + } + } +} +``` + + + +**数据解析** + +``` +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: + { + LOG_INFO("Processing boot info"); + LOG_VERBOSE("boot_info->reboot_reason: %02X", buffer[0]); + LOG_VERBOSE("boot_info->vid_uac1: %02X%02X", buffer[1], buffer[2]); + LOG_VERBOSE("boot_info->pid_uac1: %02X%02X", buffer[3], buffer[4]); + LOG_VERBOSE("boot_info->vid_uac2: %02X%02X", buffer[5], buffer[6]); + LOG_VERBOSE("boot_info->pid_uac2: %02X%02X", buffer[7], buffer[8]); + LOG_VERBOSE("boot_info->basic_info_crc: %02X%02X%02X%02X", buffer[9], buffer[10], buffer[11], buffer[12]); + LOG_VERBOSE("boot_info->power_cfg_crc: %02X%02X%02X%02X", buffer[13], buffer[14], buffer[15], buffer[16]); + mcu_data.boot_option = 0; + + if (memcmp(buffer + 9, mcu_data.basic_info_crc, 4) != 0) + { + mcu_data.boot_option |= BOOT_OPTION_UPDATE_BASIC_INFO; + } + if (memcmp(buffer + 13, mcu_data.power_cfg_crc, 4) != 0) + { + mcu_data.boot_option |= BOOT_OPTION_UPDATE_POWER_CFG; + } + ret = xu316_pack_frame(cmd, &mcu_data.boot_option, CMD00_MCU_DATA_LEN); + break; + } + + case 0x01: + { + LOG_VERBOSE("Haven't received any data"); + 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; + ret = xu316_pack_frame(cmd, mcu_data.vid_uac1, CMD01_MCU_DATA_LEN); + break; + } + + case 0x02: + { + LOG_VERBOSE("Haven't received any data"); + 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; + ret = xu316_pack_frame(cmd, (uint8_t *)&mcu_data.audio_mode, CMD02_MCU_DATA_LEN); + break; + } + + case 0x03: + { + LOG_VERBOSE("Haven't received any data"); + ret = xu316_pack_frame(cmd, (uint8_t *)&mcu_data.audio_mode, CMD03_MCU_DATA_LEN); + break; + } + + case 0x04: + { + LOG_VERBOSE("Haven't received any data"); + memcpy((uint8_t *)&mcu_data.audio_mode, (uint8_t *)audio_modes[g_current_mode], 5); + crc = calculate_crc32((uint8_t *)&mcu_data.audio_mode, 10); + memcpy(buffer, &mcu_data.audio_mode, 10); + 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; + ret = xu316_pack_frame(cmd, (uint8_t *)&mcu_data.audio_mode, CMD04_MCU_DATA_LEN); + break; + } + + case 0x05: + { + LOG_VERBOSE("Haven't received any data"); + memcpy(&mcu_data.startup_status, buffer, 15); + LOG_VERBOSE("audio_mode.startup_status: %02X", mcu_data.startup_status); + LOG_VERBOSE("audio_mode.mode: %02X%02X%02X%02X%02X", mcu_data.audio_mode[0], mcu_data.audio_mode[1], mcu_data.audio_mode[2], mcu_data.audio_mode[3], mcu_data.audio_mode[4]); + LOG_VERBOSE("audio_mode.mute_duration: %02X%02X", mcu_data.mute_duration[0], mcu_data.mute_duration[1]); + LOG_VERBOSE("audio_mode.mic_volume: %02X", mcu_data.mic_volume); + LOG_VERBOSE("audio_mode.dac_l_volume: %02X", mcu_data.dac_l_volume); + LOG_VERBOSE("audio_mode.dac_r_volume: %02X", mcu_data.dac_r_volume); + LOG_VERBOSE("audio_mode.power_cfg_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]); + ret = xu316_pack_frame(cmd, NULL, CMD05_MCU_DATA_LEN); + break; + } + + case 0x20: + { + memcpy(&mcu_data.audio_mode, buffer, 14); + LOG_VERBOSE("audio_mode.mode: %02X%02X%02X%02X%02X", mcu_data.audio_mode[0], mcu_data.audio_mode[1], mcu_data.audio_mode[2], mcu_data.audio_mode[3], mcu_data.audio_mode[4]); + LOG_VERBOSE("audio_mode.mute_duration: %02X%02X", mcu_data.mute_duration[0], mcu_data.mute_duration[1]); + LOG_VERBOSE("audio_mode.mic_volume: %02X", mcu_data.mic_volume); + LOG_VERBOSE("audio_mode.dac_l_volume: %02X", mcu_data.dac_l_volume); + LOG_VERBOSE("audio_mode.dac_r_volume: %02X", mcu_data.dac_r_volume); + LOG_VERBOSE("audio_mode.power_cfg_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]); + ret = xu316_pack_frame(cmd, NULL, CMD20_MCU_DATA_LEN); + break; + } + + case 0x22: + { + memcpy(&mcu_data.audio_format, buffer, 2); + LOG_VERBOSE("audio_mode.audio_format: %02X", mcu_data.audio_format); + LOG_VERBOSE("audio_mode.audio_type: %02X", mcu_data.audio_type); + ret = xu316_pack_frame(cmd, NULL, CMD22_MCU_DATA_LEN); + break; + } + case 0x24: + { // 发送播放音量 + memcpy(&mcu_data.dac_l_volume, buffer, 2); + LOG_VERBOSE("audio_mode.dac_l_volume: %02X", mcu_data.dac_l_volume); + LOG_VERBOSE("audio_mode.dac_r_volume: %02X", mcu_data.dac_r_volume); + ret = xu316_pack_frame(cmd, NULL, CMD24_MCU_DATA_LEN); + break; + } + case 0x25: + { // 发送录音音量, 预留 + LOG_VERBOSE("audio_mode.mic_volume: %02X", mcu_data.mic_volume); + ret = xu316_pack_frame(cmd, NULL, CMD25_MCU_DATA_LEN); + break; + } + + case 0x27: // 处理 unmute 命令的响应 (0x27) + { + // g_rx_data[2] 是接收到的版本号, g_rx_data[4] 是接收到的数据长度 + if (g_rx_data[2] == 0x00 && g_rx_data[4] == CMD27_XU316_DATA_LEN) // XU316 返回的版本号应为 0x00, 数据长度应为 0 + { + LOG_INFO("0x27 (Unmute) response received successfully."); + // unmute 成功,可以在这里添加其他逻辑,例如更新设备状态 + } + else + { + LOG_ERROR("0x27 (Unmute) response error: version 0x%02X, len %d", g_rx_data[2], g_rx_data[4]); + } + break; + } + + case 0x28: // 处理设置音频格式时间延迟命令的响应 (0x28) + { + if (g_rx_data[2] == 0x00 && g_rx_data[4] == CMD28_XU316_DATA_LEN) // XU316 返回的版本号应为 0x00, 数据长度应为 0 + { + LOG_INFO("0x28 (Set Audio Format Delay) response received successfully."); + // 可以在这里添加其他逻辑 + } + else + { + LOG_ERROR("0x28 (Set Audio Format Delay) response error: version 0x%02X, len %d", g_rx_data[2], g_rx_data[4]); + } + break; + } + + case 0xEE: // HID/OTA (0xEE) - 简单响应,暂不处理升级 + { + // XU316 发送给 MCU: 版本号 0x00, 数据长度 0x39 (57) + // MCU 回复 XU316: 版本号 0x03, 数据长度 0x39 (57) + if (g_rx_data[2] == 0x00 && g_rx_data[4] == CMD_HID_TRANSPARENT_DATA_LEN) + { + LOG_INFO("Received 0xEE (HID/OTA) from XU316: version=0x%02X, data_len=%d", g_rx_data[2], g_rx_data[4]); + + // 简单回复接收到的数据(echo模式) + xu316_pack_frame(0xEE, buffer, CMD_HID_TRANSPARENT_MCU_DATA_LEN); + LOG_INFO("Sent 0xEE (HID/OTA) response to XU316."); + } + else + { + LOG_ERROR("0xEE (HID/OTA) packet error from XU316: expected version 0x00 and data_len %d (0x%02X), " + "but got version 0x%02X and data_len %d (0x%02X)", + CMD_HID_TRANSPARENT_DATA_LEN, CMD_HID_TRANSPARENT_DATA_LEN, + g_rx_data[2], g_rx_data[4], g_rx_data[4]); + } + break; + } + + default: // 未知命令处理 + LOG_ERROR("Unknown command: 0x%02X", cmd); + break; +``` +