DMA 改造(fed4335)后 UART2 RX 走 lup_feed_byte 只认 0x7F, Loop bootloader
回的 0x9F 响应帧(pre_ok/addr_ok/data ACK)全被吞; OTA 是停等协议
(0xA7 WITH_BACK 每块必回 ACK), 工具等不到 ACK 升级必然卡死。
- loop_uart_proto: 新增 lup_feed_byte_ota() 0x9F 帧状态机 (SUM 校验无 XOR,
复用 g_lup_parser), 枚举追加 OTA 专用状态
- usart_biz: uart2_dma_poll 按 g_flag_counter_ota.flag 切换 0x9F/0x7F 解析器,
模式切换 reset, OTA 溢出阈值放宽整缓冲, 补收帧 tick 归零;
uart_srv OTA 分支 0x9F 帧透传回 BLE + 清 flag
- 单测 tests/test_lup_ota_parser.c 8 断言全过 (bootloader 真实帧向量)
- 遗留: g_flag_counter_ota.flag 无退出机制, 升级后需断电重启
314 lines
12 KiB
C
314 lines
12 KiB
C
/*
|
|
* usart_biz.c
|
|
*
|
|
* Created on: 2026-02-26
|
|
* Author: wangfq
|
|
* Updated: 2026-07-02 — 使用 loop_uart_proto 帧解析器替代 timeout heuristic
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "cmcng.h"
|
|
#include "loop_uart_proto.h"
|
|
#include <string.h>
|
|
#include "dbn_ble_srv.h"
|
|
|
|
void USART1_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
|
void USART2_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
|
|
|
|
|
|
/*==================== UART2 RX DMA (2026-08-17 方案A) ====================
|
|
* 根因: PRINT 临界区(关中断 ~7.4ms)期间 USART2 RXNE 中断被屏蔽 → 丢字节 → checksum fail
|
|
* 方案: DMA1_Ch6 循环模式硬件收字节 (不依赖 CPU 中断), 主循环轮询消费
|
|
* - 512B 环形缓冲: 能装 ~7 帧 (70B), 覆盖主循环长阻塞窗口 (SPI 擦除 45ms)
|
|
* - 无新增中断 (关 RXNE, DMA 硬件接管) → 无优先级冲突; WCHNET 用独立 ETH DMA,
|
|
* BLE 栈不用 DMA1 → Ch6 独占无冲突
|
|
* - lup_feed_byte 状态机移入主循环 (原中断上下文) → 无中断竞争更安全
|
|
*======================================================================*/
|
|
#define UART2_DMA_BUF_LEN 512
|
|
static uint8_t uart2_dma_buf[UART2_DMA_BUF_LEN] __attribute__((aligned(4)));
|
|
static uint16_t uart2_dma_last = 0; /* 主循环消费位置 (DMA 写指针由硬件维护) */
|
|
static uint32_t uart2_dma_drop = 0; /* 溢出丢弃计数 (主循环消费不及时) */
|
|
static uint8_t uart2_dma_ota_mode = 0xFF; /* 上次 OTA 模式 (0xFF=未初始化, 首轮强制 reset) */
|
|
|
|
void uart2_dma_init(void)
|
|
{
|
|
DMA_InitTypeDef DMA_InitStructure;
|
|
|
|
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
|
|
|
|
DMA_DeInit(DMA1_Channel6);
|
|
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&USART2->DATAR);
|
|
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)uart2_dma_buf;
|
|
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
|
|
DMA_InitStructure.DMA_BufferSize = UART2_DMA_BUF_LEN;
|
|
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
|
|
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
|
|
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
|
|
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
|
|
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
|
|
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
|
|
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
|
|
DMA_Init(DMA1_Channel6, &DMA_InitStructure);
|
|
|
|
/* DMA 接管 USART2 RX: 硬件收字节, 打印关中断不丢 */
|
|
USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);
|
|
DMA_Cmd(DMA1_Channel6, ENABLE);
|
|
|
|
/* 关闭 RXNE 中断 (DMA 接管后逐字节中断不再需要) */
|
|
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
|
|
|
|
uart2_dma_last = 0;
|
|
}
|
|
|
|
/* 主循环每轮调用: 消费 DMA 环形缓冲新字节 → 批量喂 lup_feed_byte 状态机 */
|
|
void uart2_dma_poll(void)
|
|
{
|
|
uint16_t cur = (uint16_t)(UART2_DMA_BUF_LEN - DMA_GetCurrDataCounter(DMA1_Channel6));
|
|
uint16_t n = (uint16_t)((cur + UART2_DMA_BUF_LEN - uart2_dma_last) & (UART2_DMA_BUF_LEN - 1));
|
|
|
|
if (n == 0) return;
|
|
|
|
/* 溢出保护: 未消费 > 半缓冲 (256B) → 消费太慢被 DMA 覆盖, 重置解析器丢帧计数
|
|
2026-08-19: OTA 模式下 0x9F ACK 帧仅 7B, 停等协议同一时刻 Loop 至多回 1 帧,
|
|
放宽阈值到整缓冲 — 256B 阈值在 TX 阻塞窗口可能误杀 ACK */
|
|
{
|
|
uint16_t drop_thr = g_flag_counter_ota.flag ? UART2_DMA_BUF_LEN : (UART2_DMA_BUF_LEN / 2);
|
|
if (n > drop_thr) {
|
|
uart2_dma_drop++;
|
|
lup_frame_reset();
|
|
uart2_dma_last = cur;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* OTA 模式切换 (0x7F ↔ 0x9F) 时重置帧解析器, 防旧模式残留状态污染 */
|
|
if (g_flag_counter_ota.flag != uart2_dma_ota_mode) {
|
|
lup_frame_reset();
|
|
uart2_dma_ota_mode = g_flag_counter_ota.flag;
|
|
}
|
|
|
|
while (n--) {
|
|
uint8_t b = uart2_dma_buf[uart2_dma_last];
|
|
uart2_dma_last = (uint16_t)((uart2_dma_last + 1) & (UART2_DMA_BUF_LEN - 1));
|
|
|
|
/* OTA 模式走 0x9F 帧解析 (bootloader 的 pre_ok/addr_ok/data ACK),
|
|
非 OTA 保持 0x7F 协议解析 */
|
|
int done = g_flag_counter_ota.flag ? lup_feed_byte_ota(b) : lup_feed_byte(b);
|
|
if (done) {
|
|
/* 帧接收完成 → 复制到 g_pkg_uart_2 (与中断版同逻辑, 主循环无竞争) */
|
|
const uint8_t *frame = lup_frame_data();
|
|
uint16_t frame_len = lup_frame_len();
|
|
if (frame_len <= BUFF_STACK_SIZE) {
|
|
memcpy(g_pkg_uart_2.pkg, frame, frame_len);
|
|
g_pkg_uart_2.offset = frame_len;
|
|
g_pkg_uart_2.flag = 1;
|
|
g_pkg_uart_2.tick = 0;
|
|
}
|
|
lup_frame_reset();
|
|
} else if (g_lup_parser.state != LUP_FRAME_STATE_IDLE) {
|
|
/* 收帧中 → tick 归零 (对齐中断版行为, 防 TIM3 半帧兜底误判) */
|
|
g_pkg_uart_2.tick = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void uart_init(void){
|
|
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
|
USART_InitTypeDef USART_InitStructure = {0};
|
|
NVIC_InitTypeDef NVIC_InitStructure = {0};
|
|
|
|
// usart1 : peripheral / DEBUG
|
|
|
|
|
|
//usart2 :loop mcu
|
|
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
|
|
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
|
|
|
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
|
|
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // Tx
|
|
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // Rx
|
|
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
|
|
|
USART_InitStructure.USART_BaudRate = 192000; // Loop MCU 实际波特率
|
|
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
|
|
USART_InitStructure.USART_StopBits = USART_StopBits_1;
|
|
USART_InitStructure.USART_Parity = USART_Parity_No;
|
|
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
|
|
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
|
|
|
USART_Init(USART2, &USART_InitStructure);
|
|
/* RXNE 中断由 uart2_dma_init 关闭 (DMA 接管, 2026-08-17) */
|
|
|
|
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
|
|
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
|
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
|
|
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
|
|
NVIC_Init(&NVIC_InitStructure);
|
|
|
|
USART_Cmd(USART2, ENABLE);
|
|
|
|
uart2_dma_init(); /* DMA 循环接收接管 (2026-08-17) */
|
|
|
|
|
|
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
|
|
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
|
|
GPIO_Init(GPIOA, &GPIO_InitStructure);
|
|
|
|
// 初始化帧解析器
|
|
lup_frame_reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************
|
|
* @fn USART1_IRQHandler
|
|
*
|
|
* @brief This function handles USART1 global interrupt request.
|
|
*
|
|
* @return none
|
|
*/
|
|
void USART1_IRQHandler(void)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
* @fn USART2_IRQHandler
|
|
*
|
|
* @brief USART2 RX — 使用 lup_feed_byte() 帧解析器
|
|
* 当解析出完整帧时,复制到 g_pkg_uart_2.pkg 并设置 flag
|
|
*
|
|
* @return none
|
|
*/
|
|
void USART2_IRQHandler(void)
|
|
{
|
|
/* 2026-08-17: RXNE 中断已关 (DMA 接管), 本 handler 不再触发。
|
|
保留空函数 + IDLE 中断注释: 若后续需要帧边界辅助, 在此加
|
|
USART_IT_IDLE 处理 (读 SR + 读 DR 清标志, 置标志由主循环消费)。 */
|
|
}
|
|
|
|
|
|
void UART2_SendString(uint8_t *buf, uint16_t len)
|
|
{
|
|
uint16_t _len = len;
|
|
while(_len){
|
|
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
|
|
USART_SendData(USART2, *buf++);
|
|
_len--;
|
|
}
|
|
while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
|
|
}
|
|
|
|
void UART1_SendString(uint8_t *buf, uint16_t len)
|
|
{
|
|
uint16_t _len = len;
|
|
while(_len){
|
|
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
|
|
USART_SendData(USART1, *buf++);
|
|
_len--;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* uart_srv — 主循环中调用,处理已接收完整的 UART2 帧
|
|
*
|
|
* 处理流程:
|
|
* 1. 0x7F + 0xC0/0x0C → 传感器数据上报
|
|
* - 若无 BLE 连接 → 标记 `_report_flag`,在 TCP JSON 中处理
|
|
* - 若有 BLE 连接且 acs_enable → 转 0x8F 前缀发给 BLE
|
|
* 2. 0x7F + 其他 CMD → 响应帧,交给 lup_process_frame() 匹配挂起命令
|
|
* 3. 非 0x7F → 调试打印(可能是字符串等)
|
|
*/
|
|
void uart_srv(void)
|
|
{
|
|
uint8_t i;
|
|
uint8_t _report_flag = 0;
|
|
|
|
uart2_dma_poll(); /* DMA 环形缓冲 → 帧解析 (2026-08-17) */
|
|
|
|
// 检查命令超时
|
|
lup_cmd_check_timeout();
|
|
|
|
if(g_pkg_uart_2.flag){
|
|
|
|
if(g_flag_counter_ota.flag == 0){
|
|
if(g_pkg_uart_2.pkg[0] == 0x7F){
|
|
uint8_t cmd = g_pkg_uart_2.pkg[3];
|
|
|
|
// --- 所有 0x7F 帧先经过 lup_process_frame 校验 ---
|
|
// 0xC0: 校验后通过回调直接推送 TCP JSON
|
|
// 其他: 校验后匹配挂起命令
|
|
lup_process_frame(g_pkg_uart_2.pkg, g_pkg_uart_2.offset);
|
|
|
|
// --- 传感器上报 (0xC0) 分流 ---
|
|
// 回调已处理 TCP 推送,此处仅处理 BLE 转发
|
|
if(cmd == LUP_CMD_SENSOR_REPORT)
|
|
{
|
|
if(g_dbn_ble_state_acs_enable.flag != 0){
|
|
// BLE ACS 已连接 → 改 Magic 为 0x8F 发给 BLE
|
|
g_pkg_uart_2.pkg[0] = 0x8F;
|
|
g_flag_notify_temp = set_response_tran_to_notify(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &g_notify_buftemp); // 保留给 BLE
|
|
}
|
|
/* 2026-08-13 修复: 0xC0 帧消费后必须清 flag —
|
|
原缺失, auth 未通过时 tcp_json_push_sensor 提前 return
|
|
也不清 → 坏帧/正常帧反复处理 (checksum fail 刷屏) */
|
|
InitPkgUart(&g_pkg_uart_2);
|
|
}
|
|
else {
|
|
if(g_flag_bt_state){
|
|
g_flag_notify_temp = set_response_tran_to_notify(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &g_notify_buftemp);
|
|
InitPkgUart(&g_pkg_uart_2);
|
|
}
|
|
else {
|
|
/* 2026-08-13 修复: 非 0xC0 + 无 BLE 连接也清 flag (原缺失) */
|
|
InitPkgUart(&g_pkg_uart_2);
|
|
}
|
|
}
|
|
|
|
// 调试打印 (2026-08-13: LUP Rx 已在 lup_process_frame 缓冲打印,
|
|
// 此处重复逐字节打印冗余且高频 — 暂时关闭)
|
|
#if 0
|
|
for(i = 0; i < g_pkg_uart_2.offset; i++){
|
|
PRINT(" %02X", g_pkg_uart_2.pkg[i]);
|
|
}
|
|
PRINT("\n");
|
|
#endif
|
|
|
|
}
|
|
else {
|
|
// 非 0x7F 魔法字节 — hex 打印 (原 %s 会把二进制当字符串 → 乱码)
|
|
PRINT("Rcv_len:%d,dat:", g_pkg_uart_2.offset);
|
|
for(i = 0; i < g_pkg_uart_2.offset; i++){
|
|
PRINT(" %02X", g_pkg_uart_2.pkg[i]);
|
|
}
|
|
PRINT("\n");
|
|
}
|
|
}
|
|
else{
|
|
/* OTA 模式 (2026-08-19): Loop bootloader 回的 0x9F 帧
|
|
(back_9F_pre_ok / addr_ok / data ACK) 透传回 BLE 工具,
|
|
否则停等协议 (0xA7 WITH_BACK) 永远等不到 ACK */
|
|
if(g_flag_bt_state){
|
|
g_flag_notify_temp = set_response_tran_to_notify(g_pkg_uart_2.pkg, g_pkg_uart_2.offset, &g_notify_buftemp);
|
|
}
|
|
/* OTA 模式同样清 flag — 残留帧会在 OTA 结束后被误处理 */
|
|
InitPkgUart(&g_pkg_uart_2);
|
|
}
|
|
|
|
|
|
if(g_flag_bt_state){
|
|
|
|
}
|
|
else{
|
|
g_dbn_ble_state_acs_enable.flag = 0;
|
|
}
|
|
|
|
}
|
|
}
|