/** ****************************************************************************** * @file uart_protocol.c * @brief UART 通讯协议模块实现 * 负责协议帧的组装/发送/接收/解析, 以及对 LED 显示的更新触发. ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "uart_protocol.h" #include "main.h" /* DebugUartHandle */ #include "app_state.h" #include "led_driver.h" #include "battery_display.h" /* Private define ------------------------------------------------------------*/ /* ========== 通讯协议常量 ========== */ #define FRAME_HEADER1 0x55u #define FRAME_HEADER2 0xAAu #define CMD_READ 0x01u #define CMD_WRITE 0x02u #define CMD_REPORT 0x81u #define CMD_ACK 0xA0u /* 写操作成功回复 */ /* 接收缓冲区大小 */ #define RX_BUF_SIZE 32u /* UART 发送超时 (ms) */ #define UART_TX_TIMEOUT 100u /* Private variables ---------------------------------------------------------*/ extern UART_HandleTypeDef DebugUartHandle; /* ========== 接收缓冲 (双缓冲) ========== */ #define RX_QUEUE_SIZE 2u static volatile uint8_t g_rxBuf[RX_QUEUE_SIZE][RX_BUF_SIZE]; static volatile uint8_t g_frameLen[RX_QUEUE_SIZE]; static volatile uint8_t g_rxLen = 0u; static volatile uint8_t g_rxWriteIdx = 0u; static volatile uint8_t g_rxReadIdx = 0u; static volatile uint8_t g_frameCount = 0u; uint8_t g_rxByte; /* 单字节接收缓冲 (extern 在 uart_protocol.h) */ /* ========== 发送缓冲 ========== */ static uint8_t g_txBuf[8]; /* Private function prototypes -----------------------------------------------*/ static void UART_SendFrame(uint8_t cmd, uint8_t item, uint8_t value); static void UART_SendWrite(uint8_t item, uint8_t value); static void HandleReportAll(uint8_t *data, uint8_t count); static void HandleReportSingle(uint8_t item, uint8_t value); /* ========== 帧状态查询 ========== */ uint8_t UART_IsFrameReady(void) { return (g_frameCount > 0u) ? 1u : 0u; } void UART_ClearFrameReady(void) { /* 标志位管理移至 ProcessReceivedFrame 内完成 */ } /* ========== UART 接收回调 ========== */ /** * @brief USART2中断接收回调 - 每收到一个字节调用 * 支持变长帧: 5字节标准帧及 AA 81 COUNT ... CRC 多字节上报 */ void UART_RxByteCallback(uint8_t data) { static uint8_t s_state = 0u; /* 0=Wait 55, 1=Wait AA(or A0), 2=Wait CMD, 3=Wait LEN, 4=Wait Body */ static uint8_t s_expectedLen = 0u; static uint16_t s_timeout = 0u; uint8_t *curBuf = (uint8_t *)g_rxBuf[g_rxWriteIdx]; if (s_state != 0u) { s_timeout++; if (s_timeout > 1000u) { s_state = 0u; s_timeout = 0u; } } else { s_timeout = 0u; } if (s_state == 0u) { if (data == FRAME_HEADER1) { curBuf[0] = data; g_rxLen = 1u; s_state = 1u; } else if (data == CMD_ACK) { curBuf[0] = CMD_ACK; if (g_frameCount < RX_QUEUE_SIZE) { g_frameLen[g_rxWriteIdx] = 1u; g_rxWriteIdx = (g_rxWriteIdx + 1u) % RX_QUEUE_SIZE; g_frameCount++; } g_rxLen = 0u; } return; } if (s_state == 1u) { if (data == FRAME_HEADER2) { curBuf[1] = data; g_rxLen = 2u; s_state = 2u; } else { s_state = 0u; } return; } if (s_state == 2u) { curBuf[g_rxLen++] = data; s_state = 3u; return; } if (s_state == 3u) { curBuf[g_rxLen++] = data; s_expectedLen = 5u + data; s_state = 4u; if (s_expectedLen > RX_BUF_SIZE) { s_state = 0u; g_rxLen = 0u; } return; } if (s_state == 4u) { curBuf[g_rxLen++] = data; if (g_rxLen >= s_expectedLen) { uint8_t cmd = curBuf[2]; uint8_t len = curBuf[3]; uint8_t crc = cmd ^ len; for (uint8_t i = 0; i < len; i++) { crc ^= curBuf[4 + i]; } if (crc == data) { if (g_frameCount < RX_QUEUE_SIZE) { g_frameLen[g_rxWriteIdx] = g_rxLen; g_rxWriteIdx = (g_rxWriteIdx + 1u) % RX_QUEUE_SIZE; g_frameCount++; } } s_state = 0u; g_rxLen = 0u; } } } /* ========== 帧处理 ========== */ /** * @brief 处理收到的完整协议帧 */ void ProcessReceivedFrame(void) { if (g_frameCount == 0u) return; uint8_t *frame = (uint8_t *)g_rxBuf[g_rxReadIdx]; uint8_t rxLen = g_frameLen[g_rxReadIdx]; if (rxLen >= 7u) { uint8_t cmd = frame[2]; uint8_t len = frame[3]; if (cmd == CMD_REPORT) { if (len == 2u) { /* 0x81 单条上报: 没有 COUNT 字节, frame[4]=ITEM, frame[5]=VALUE */ HandleReportSingle(frame[4], frame[5]); } else if (len > 2u) { /* 0x81 多条上报: frame[4] 为键值对数量 COUNT */ uint8_t count = frame[4]; HandleReportAll(&frame[5], count); } } else if (cmd == CMD_READ || cmd == CMD_WRITE) { if (len == 2u) { HandleReportSingle(frame[4], frame[5]); } } } /* 释放缓冲区 */ g_rxReadIdx = (g_rxReadIdx + 1u) % RX_QUEUE_SIZE; __disable_irq(); g_frameCount--; __enable_irq(); } /* ========== 发送函数 ========== */ /** * @brief 直接寄存器操作发送单字节 (绕过 HAL, 避免 HAL 锁/超时关中断) * 在等待 TXE 时顺带清除接收端可能发生的 ORE 错误 */ static void UART_DirectSendByte(uint8_t byte) { uint32_t timeout = 0u; while (!(USART2->SR & USART_SR_TXE)) { if (++timeout > 100000u) return; /* 安全超时, 不关中断直接返回 */ /* 等待期间检查并清除 ORE (读 SR 再读 DR 清除) */ if (USART2->SR & USART_SR_ORE) { (void)USART2->DR; } } USART2->DR = byte; } static void UART_SendFrame(uint8_t cmd, uint8_t item, uint8_t value) { uint8_t len = 0x02u; uint8_t crc = (uint8_t)(cmd ^ len ^ item ^ value); g_txBuf[0] = FRAME_HEADER1; g_txBuf[1] = FRAME_HEADER2; g_txBuf[2] = cmd; g_txBuf[3] = len; g_txBuf[4] = item; g_txBuf[5] = value; g_txBuf[6] = crc; for (uint8_t i = 0; i < 7u; i++) { UART_DirectSendByte(g_txBuf[i]); } /* 等待最后一个字节发送完毕 (TC) */ uint32_t timeout = 0u; while (!(USART2->SR & USART_SR_TC)) { if (++timeout > 100000u) break; } } static void UART_SendWrite(uint8_t item, uint8_t value) { UART_SendFrame(CMD_WRITE, item, value); } void SendSetInputSrc(uint8_t src) { UART_SendWrite(ITEM_INPUT_SRC, src); } void SendSetVolLR(uint8_t pct) { if (pct > 100u) pct = 100u; UART_SendWrite(ITEM_VOL_LR, pct); } void SendSetVolBass(uint8_t pct) { if (pct > 100u) pct = 100u; UART_SendWrite(ITEM_VOL_BASS, pct); } void SendSetDacMute(uint8_t unmute) { UART_SendWrite(ITEM_DAC_MUTE, unmute); } void SendBTDisconnect(void) { UART_SendWrite(ITEM_BT_CONN, 0x00u); } void SendSoftReset(void) { UART_SendWrite(ITEM_SOFT_RESET, 0x00u); } void SendBassMute(void) { UART_SendWrite(ITEM_BASS_MUTE, 0x00u); } void SendQueryAll(void) { UART_SendFrame(CMD_READ, ITEM_QUERY_ALL, 0x00u); } /* ========== 上报处理 (私有) ========== */ /** * @brief 处理单条变化上报 (AA 81 ITEM VALUE CRC) */ static void HandleReportSingle(uint8_t item, uint8_t value) { uint8_t firstTime = !g_dataReceived; if (firstTime) { g_dataReceived = 1u; } switch (item) { case ITEM_INPUT_SRC: g_inputSource = value; g_sourceReceived = 1u; UpdateSourceLED(); break; case ITEM_BT_CONN: g_btConnState = value; g_sourceReceived = 1u; UpdateSourceLED(); break; case ITEM_BT_AUDIO: g_audioPlaying = value; g_sourceReceived = 1u; UpdateSourceLED(); break; case ITEM_BATTERY: g_batteryPct = value; g_batteryReceived = 1u; UpdateBatteryLEDs(); break; case ITEM_CHG_FULL: g_chgFull = value; g_batteryReceived = 1u; UpdateBatteryLEDs(); break; case ITEM_CHG_ING: g_chgIng = value; g_batteryReceived = 1u; UpdateBatteryLEDs(); break; case ITEM_DAC_MUTE: g_dacMute = value; break; case ITEM_VOL_LR: if (!g_userActive) { g_volLRLevel = (int16_t)value; } g_volLRReceived = 1u; UpdateVolumeLEDs(); break; case ITEM_VOL_BASS: if (!g_userActive) { g_volBassLevel = (int16_t)value; } g_volBassReceived = 1u; UpdateVolumeLEDs(); break; default: break; } /* 第一次收到数据但不是完整的合并上报, 触发查询获取全部状态 */ if (firstTime) { SendQueryAll(); } } /** * @brief 处理多条上报 (AA 81 COUNT ITEM1 VAL1 ITEM2 VAL2 ... CRC) */ static void HandleReportAll(uint8_t *data, uint8_t count) { if (!g_dataReceived) { g_dataReceived = 1u; } /* 解析每一对 ITEM+VALUE */ for (uint8_t i = 0; i < count; i++) { uint8_t idx = (uint8_t)(i * 2u); uint8_t item = data[idx]; uint8_t value = data[idx + 1u]; switch (item) { case ITEM_INPUT_SRC: g_inputSource = value; g_sourceReceived = 1u; break; case ITEM_BT_CONN: g_btConnState = value; g_sourceReceived = 1u; break; case ITEM_BT_AUDIO: g_audioPlaying = value; g_sourceReceived = 1u; break; case ITEM_DAC_MUTE: g_dacMute = value; break; case ITEM_VOL_LR: if (!g_userActive) { g_volLRLevel = (int16_t)value; } g_volLRReceived = 1u; break; case ITEM_VOL_BASS: if (!g_userActive) { g_volBassLevel = (int16_t)value; } g_volBassReceived = 1u; break; case ITEM_BATTERY: g_batteryPct = value; g_batteryReceived = 1u; break; case ITEM_CHG_FULL: g_chgFull = value; g_batteryReceived = 1u; break; case ITEM_CHG_ING: g_chgIng = value; g_batteryReceived = 1u; break; default: break; } } /* 收到全量状态后,统一刷新所有 LED 显示,实现无缝切换 */ UpdateVolumeLEDs(); UpdateSourceLED(); UpdateBatteryLEDs(); }