feat(vd960DBN): UART2 RX DMA 循环接收 — 根治打印关中断丢帧 (2026-08-17)
遗留项'UART2 偶发丢帧(1-3分钟一次 checksum fail)'落地:
- 根因: PRINT 临界区(关中断~7.4ms)屏蔽 USART2 RXNE 中断 → 丢字节
- 方案: DMA1_Ch6 循环模式硬件收字节(不依赖CPU中断), 主循环轮询消费
- uart2_dma_init(): Ch6 循环模式 + 512B 环形缓冲(aligned(4)) + 关 RXNE
- uart2_dma_poll(): 主循环读 DMA_GetCurrDataCounter 批量喂 lup_feed_byte
(状态机移入主循环无竞争); 溢出保护(未消费>256B重置+丢帧计数)
- uart_init() 末尾调 uart2_dma_init(); USART2_IRQHandler 清空留 IDLE 注释
- 资源: DMA1 全空闲(WCHNET 独立 ETH DMA/BLE 不用 DMA1) → Ch6 独占
- .bss +512B(缓冲), 栈 6.3KB→5.8KB 仍充裕
This commit is contained in:
@@ -16,6 +16,84 @@ 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; /* 溢出丢弃计数 (主循环消费不及时) */
|
||||
|
||||
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 覆盖, 重置解析器丢帧计数 */
|
||||
if (n > (UART2_DMA_BUF_LEN / 2)) {
|
||||
uart2_dma_drop++;
|
||||
lup_frame_reset();
|
||||
uart2_dma_last = cur;
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if (lup_feed_byte(b)) {
|
||||
/* 帧接收完成 → 复制到 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void uart_init(void){
|
||||
GPIO_InitTypeDef GPIO_InitStructure = {0};
|
||||
USART_InitTypeDef USART_InitStructure = {0};
|
||||
@@ -44,8 +122,7 @@ void uart_init(void){
|
||||
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
|
||||
|
||||
USART_Init(USART2, &USART_InitStructure);
|
||||
// USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
|
||||
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
|
||||
/* RXNE 中断由 uart2_dma_init 关闭 (DMA 接管, 2026-08-17) */
|
||||
|
||||
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
|
||||
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
|
||||
@@ -55,6 +132,8 @@ void uart_init(void){
|
||||
|
||||
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;
|
||||
@@ -90,27 +169,9 @@ void USART1_IRQHandler(void)
|
||||
*/
|
||||
void USART2_IRQHandler(void)
|
||||
{
|
||||
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
|
||||
{
|
||||
uint8_t _dat = USART_ReceiveData(USART2);
|
||||
|
||||
// 喂给帧解析器
|
||||
if (lup_feed_byte(_dat)) {
|
||||
// 帧接收完成,复制到 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 归零
|
||||
g_pkg_uart_2.tick = 0;
|
||||
}
|
||||
}
|
||||
/* 2026-08-17: RXNE 中断已关 (DMA 接管), 本 handler 不再触发。
|
||||
保留空函数 + IDLE 中断注释: 若后续需要帧边界辅助, 在此加
|
||||
USART_IT_IDLE 处理 (读 SR + 读 DR 清标志, 置标志由主循环消费)。 */
|
||||
}
|
||||
|
||||
|
||||
@@ -151,6 +212,8 @@ void uart_srv(void)
|
||||
uint8_t i;
|
||||
uint8_t _report_flag = 0;
|
||||
|
||||
uart2_dma_poll(); /* DMA 环形缓冲 → 帧解析 (2026-08-17) */
|
||||
|
||||
// 检查命令超时
|
||||
lup_cmd_check_timeout();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user