Skip to content

XU316 and MCU Communication Protocol Reference

1. Basic Data Structures

1.1 Ring Buffer

#define RING_BUFFER_SIZE 256  // Ring buffer size, used for UART communication buffering

typedef struct {
    uint8_t buffer[RING_BUFFER_SIZE];  // Data storage area
    volatile uint16_t head;    // Write position pointer
    volatile uint16_t tail;    // Read position pointer
    volatile uint16_t count;   // Current data count
} ring_buffer_t;

1.2 Audio Mode Definitions

// Audio mode configuration array, each mode contains 5-byte configuration data
static const uint8_t audio_modes[][5] = {
    {0x00, 0x80, 0xa9, 0x00, 0x01},  // USB-no-mqa mode
    {0x00, 0x80, 0x01, 0x00, 0x02},  // UAC1 mode
    {0x10, 0x80, 0x65, 0x10, 0x03},  // COAX mode
    {0x00, 0x80, 0x65, 0x10, 0x04},  // OPT mode
    {0x00, 0x80, 0xc5, 0x08, 0x05},  // SPDIF OUT mode
    {0x00, 0x82, 0xd5, 0x81, 0x06},  // I2S IN mode
    {0x20, 0x80, 0x65, 0x10, 0x07}   // HDMI mode
};

// Corresponding mode name array
static const char *mode_names[] = {
    "USB-no-mqa",
    "UAC1",
    "COAX",
    "OPT",
    "SPDIF OUT",
    "I2S IN",
    "HDMI"
};

2. Initialization and Configuration

2.1 Device Initialization Information Print

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

void print_init_info(void)
{
    log_data(LOG_USER, "----------Device initialized----------\n");
    // Print UAC1.0 device information
    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]);
    // Print UAC2.0 device information
    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]);
    // Print product information
    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);
    // Print CRC verification information
    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 Ring Buffer Initialization

void ring_buffer_init(void) {
    uart_ring_buffer.head = 0;    // Initialize write position
    uart_ring_buffer.tail = 0;    // Initialize read position
    uart_ring_buffer.count = 0;   // Initialize data count
}

2.3 Product Information Initialization

void xu316_init(void)
{
    // Default audio mode configuration (USB UAC2.0)
    uint8_t audio_mode[5] = {0x00, 0x80, 0xa9, 0x00, 0x01};

    // Configure UAC1.0 device information
    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

    // Configure UAC2.0 device information
    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

    // Configure product information
    memcpy(mcu_data.product_manufacturer, "Phaten", 6);
    // Set product name
    memcpy(mcu_data.product_name, "XMOS XU316", 11);
    // Set product serial number
    memcpy(mcu_data.product_serial, "123456789ABCDEF", 15);

    // Calculate and store basic information CRC32 checksum (56 bytes)
    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;

    // Initialize device status and audio configuration
    mcu_data.startup_status = 0x00;
    memcpy(&mcu_data.audio_mode, audio_mode, 5);

    // Configure mute duration (400ms)
    mcu_data.mute_duration[0] = MUTE_DURATION_CONFIG >> 8;
    mcu_data.mute_duration[1] = MUTE_DURATION_CONFIG;

    // Initialize volume settings
    mcu_data.mic_volume = MIC_VOLUME_CONFIG;
    // Initialize left and right channel DAC volume
    mcu_data.dac_l_volume = DAC_L_VOLUME_CONFIG;
    mcu_data.dac_r_volume = DAC_R_VOLUME_CONFIG;

    // Calculate and store power configuration CRC32 checksum (10 bytes)
    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;

    // Initialize communication buffer
    ring_buffer_init();
}

3. Communication Buffer Operations

3.1 Ring Buffer Operation Functions

// Write data to ring buffer
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;  // Buffer full, write failed
        }
        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;  // Write successful
}

// Read data from ring buffer
uint8_t ring_buffer_read(uint8_t *data, uint16_t len) {
    uint16_t i;
    if(uart_ring_buffer.count < len) {
        return 0;  // Insufficient data
    }
    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;  // Read successful
}

// Peek data (without moving read pointer)
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;  // Peek successful
}

4. Checksum Calculation

4.1 CRC32 Calculation

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

    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;  // Return CRC32 checksum
}

// Calculate simple checksum
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. Communication Protocol Processing

5.1 Frame Verification

// Verify frame data integrity
uint8_t uart_frame_check(uint8_t *buf, uint8_t len)
{
    // Check minimum length requirement
    if (len < 6) {  // Header(2) + Version(1) + Command(1) + Length(2) + Checksum(1)
        return 0;
    }

    // Verify frame header
    if (buf[0] != FRAME_HEADER_H || buf[1] != FRAME_HEADER_L) {
        return 0;
    }

    // Get data length (big-endian mode)
    uint16_t data_len = buf[4];

    // Verify data length validity
    if (data_len > 256 || len < (data_len + 6)) {
        return 0;
    }

    // Calculate and verify checksum
    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 Data Frame Encapsulation

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

    // Build frame header
    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;

    // Copy data payload
    if (data && len > 0) {
        memcpy(tx_data + 5, data, len);
    }

    // Calculate and add checksum
    tx_data[len + 5] = xu316_calc_checksum(tx_data, len + 5);
    len += 6;

    // Send data
    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 Frame Length Check

int check_frame_length(uint8_t *buf, uint16_t len) {
    // Verify frame header
    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;
    }
    // Get data length (big-endian mode)
    int data_len = buf[4];
    // Return complete frame length
    return data_len + 6;
}

5.4 Data Reception Processing

void uart_data_process(void)
{
    uint8_t peek_buffer[8];      // Peek buffer
    uint8_t process_buffer[256]; // Processing buffer
    int frame_length;

    while(uart_ring_buffer.count >= 6) {  // At least 6 bytes needed to start checking
        // Peek frame header information
        ring_buffer_peek(peek_buffer, 6);

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

        // Check frame length
        frame_length = check_frame_length(peek_buffer, 6);
        if(frame_length < 0) {
            // Invalid frame, discard one byte
            uint8_t dummy;
            ring_buffer_read(&dummy, 1);
            LOG_ERROR("Frame length check failed %02x", dummy);
            continue;
        }

        // Check if there is enough data
        if(uart_ring_buffer.count < frame_length) {
            LOG_ERROR("Not enough data");
            break;  // Wait for more data
        }

        // Read and process complete frame
        if(ring_buffer_read(process_buffer, frame_length)) {
            uart_data_parse();
        }
    }
}

5.5 Data Parsing Processing

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;

    // Verify frame integrity
    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;
    }

    // Parse frame information
    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);

    // Process data according to command type
    switch (cmd) {
        case 0x00:  // Startup information
            // ... Process startup information ...
            break;

        case 0x01:  // Basic information
            // ... Process basic information ...
            break;

        case 0x02:  // Power configuration
            // ... Process power configuration ...
            break;

        // ... Other command processing ...

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

    return ret;
}