add 示例

This commit is contained in:
2025-06-17 09:23:47 +08:00
parent 51598f47e6
commit 8a517fb674
3 changed files with 929 additions and 0 deletions

View File

@@ -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-65535ms
#define MUTE_DURATION 100
// 麦克风默认音量
#define MIC_VOLUME 10
// DAC左声道默认音量 1 见DAC音量字段说明
#define DAC_L_VOLUME 30
// DAC右声道默认音量 1 见DAC音量字段说明
#define DAC_R_VOLUME 20
```

View File

@@ -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;
```

View File

@@ -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;
```