Browse Source

DMA当前存在问题:不规则波形的485发送会触发不可逆错误,暂时切换方案。

dma_temp
Lizongdi 7 days ago
parent
commit
4d9b983d3c
  1. 214
      bspMCU/bsp_uart.c
  2. 2
      bspMCU/include/bsp_uart.h
  3. 13
      project/Spoolend/Spoolend.c

214
bspMCU/bsp_uart.c

@ -38,6 +38,10 @@ static TUartTab g_ptUartTab[] = {
}; };
#endif #endif
/* HAL 回调只有 huart 指针,这里维护 "UART 实例 -> TComCtrl" 的映射,
* 使 bsp_uart.c project g_ptRS485_x */
static TComCtrl *s_aptComCtrl[sizeof(g_ptUartTab) / sizeof(g_ptUartTab[0])] = {0};
static void bsp_uart_set_baudrate(UART_HandleTypeDef *_pUartHandle, uint32_t _uiBaudRate) static void bsp_uart_set_baudrate(UART_HandleTypeDef *_pUartHandle, uint32_t _uiBaudRate)
{ {
_pUartHandle->Init.BaudRate = _uiBaudRate; _pUartHandle->Init.BaudRate = _uiBaudRate;
@ -70,17 +74,48 @@ TUartUserData *UART_userdata_init(int _iID, int32_t _uiBaudRate, uint32_t _uiSiz
return ptUartUserData; return ptUartUserData;
} }
/* 根据 UART 实例反查 TComCtrl(在 HAL 回调中使用) */
static TComCtrl *bsp_uart_find_comctrl(UART_HandleTypeDef *_pUart)
{
uint32_t i;
for (i = 0; i < sizeof(g_ptUartTab) / sizeof(g_ptUartTab[0]); i++)
{
if (g_ptUartTab[i].m_uart == _pUart)
{
return s_aptComCtrl[i];
}
}
return NULL;
}
void UART_IT_init(TComCtrl *_ptComCtrl) void UART_IT_init(TComCtrl *_ptComCtrl)
{ {
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData; TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
#ifdef CONFIG_UART_IT_IDLEDMA #ifdef CONFIG_UART_IT_IDLEDMA
// 开启串口空闲中断 (IDLE Interrupt) /* 记录 UART 实例 -> TComCtrl 的映射,供 HAL 回调反查 */
// 这是核心!DMA 负责搬运,IDLE 负责通知“一帧结束了” {
__HAL_UART_ENABLE_IT(ptUartUserData->m_uart, UART_IT_IDLE); uint32_t i;
for (i = 0; i < sizeof(g_ptUartTab) / sizeof(g_ptUartTab[0]); i++)
{
if (g_ptUartTab[i].m_uart == ptUartUserData->m_uart)
{
s_aptComCtrl[i] = _ptComCtrl;
break;
}
}
}
// 启动 DMA 接收 (Circular 模式) /* 用 HAL 官方的 "接收直到 IDLE" API 启动循环 DMA 接收。
// 注意:这里不需要在中断里重启 DMA,因为 Circular 模式会自动循环 * Circular DMA + 使 IDLE + ReceptionType=TOIDLE
if (HAL_UART_Receive_DMA(ptUartUserData->m_uart, ptUartUserData->m_dma_rx_buf, ptUartUserData->m_buf_size) != HAL_OK) *
* HAL UART DMA
* - IDLE/HT/TC -> HAL_UARTEx_RxEventCallback()
* - (ORE/FE/NE/PE) -> HAL_UART_ErrorCallback()
* HAL 485 线
* HAL abort/ current_pos */
if (HAL_UARTEx_ReceiveToIdle_DMA(ptUartUserData->m_uart,
ptUartUserData->m_dma_rx_buf,
ptUartUserData->m_buf_size) != HAL_OK)
{ {
Error_Handler(); Error_Handler();
} }
@ -89,49 +124,148 @@ void UART_IT_init(TComCtrl *_ptComCtrl)
#endif #endif
} }
void UART_DMA_RX_IRQHandler(TComCtrl *_ptComCtrl)
{
#ifdef CONFIG_UART_IT_IDLEDMA #ifdef CONFIG_UART_IT_IDLEDMA
/* 共享的 DMA 环形缓冲数据搬运逻辑:
* DMA rd_ComRecvProc */
static void bsp_uart_process_rx(TComCtrl *_ptComCtrl)
{
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData; TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
// 1. 检查是否是空闲中断 (IDLE) uint16_t current_pos = ptUartUserData->m_buf_size
if (__HAL_UART_GET_FLAG(ptUartUserData->m_uart, UART_FLAG_IDLE) != RESET) - __HAL_DMA_GET_COUNTER(ptUartUserData->m_hdma_rx);
{ uint16_t data_len = 0;
// 2. 清除空闲标志 (必须先读 SR 再读 DR,HAL 宏已封装好) if (current_pos >= ptUartUserData->m_last_dma_pos)
__HAL_UART_CLEAR_IDLEFLAG(ptUartUserData->m_uart); {
data_len = current_pos - ptUartUserData->m_last_dma_pos;
// 3. 计算本次接收了多少数据 if (data_len > 0)
// 公式:总长度 - DMA 当前剩余未传输的数量 {
uint16_t current_pos = ptUartUserData->m_buf_size - __HAL_DMA_GET_COUNTER(ptUartUserData->m_hdma_rx); rd_ComRecvProc(_ptComCtrl,
(const char *)&ptUartUserData->m_dma_rx_buf[ptUartUserData->m_last_dma_pos],
uint16_t data_len = 0; data_len);
}
}
else
{
/* DMA 写指针回绕 (Wrap around) */
uint16_t part1 = ptUartUserData->m_buf_size - ptUartUserData->m_last_dma_pos;
uint16_t part2 = current_pos;
if (current_pos >= ptUartUserData->m_last_dma_pos) if (part1 > 0)
{ {
data_len = current_pos - ptUartUserData->m_last_dma_pos; rd_ComRecvProc(_ptComCtrl,
if (data_len > 0) (const char *)&ptUartUserData->m_dma_rx_buf[ptUartUserData->m_last_dma_pos],
{ part1);
rd_ComRecvProc(_ptComCtrl, (const char*)&ptUartUserData->m_dma_rx_buf[ptUartUserData->m_last_dma_pos], data_len);
}
} }
else if (part2 > 0)
{ {
// DMA 指针发生了回绕 (Wrap around) rd_ComRecvProc(_ptComCtrl,
// 分两段拷贝:[last_pos -> End] 和 [0 -> current_pos] (const char *)&ptUartUserData->m_dma_rx_buf[0],
uint16_t part1 = ptUartUserData->m_buf_size - ptUartUserData->m_last_dma_pos; part2);
uint16_t part2 = current_pos;
if (part1 > 0)
{
rd_ComRecvProc(_ptComCtrl, (const char*)&ptUartUserData->m_dma_rx_buf[ptUartUserData->m_last_dma_pos], part1);
}
if (part2 > 0)
{
rd_ComRecvProc(_ptComCtrl, (const char*)&ptUartUserData->m_dma_rx_buf[0], part2);
}
} }
}
ptUartUserData->m_last_dma_pos = current_pos;
}
#endif /* CONFIG_UART_IT_IDLEDMA */
#ifdef CONFIG_UART_IT_IDLEDMA
/* HAL 的 "接收直到 IDLE" 事件回调:
* HAL_UART_IRQHandler(USART ) HAL_DMA_IRQHandler(DMA )
* Circular IDLE/HT/TC DMA
* IDLE */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
TComCtrl *ptComCtrl = bsp_uart_find_comctrl(huart);
(void)Size;
if (ptComCtrl == NULL)
{
return;
}
if (HAL_UARTEx_GetRxEventType(huart) != HAL_UART_RXEVENT_IDLE)
{
return; /* 只在 IDLE 帧结束时搬运数据 */
}
bsp_uart_process_rx(ptComCtrl);
}
#endif /* CONFIG_UART_IT_IDLEDMA */
#ifdef CONFIG_UART_IT_IDLEDMA
/* HAL 通信错误回调:
* 485 线 ORE/FE/NE/PEHAL
* UART_EndRxTransfer()(RxState=READY) + HAL_DMA_Abort_IT()
* (hdmarx->State=READY, CR3.DMAR) HAL */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
TComCtrl *ptComCtrl = bsp_uart_find_comctrl(huart);
TUartUserData *ptUartUserData;
if (ptComCtrl == NULL)
{
return;
}
ptUartUserData = (TUartUserData *)ptComCtrl->m_pUserData;
/* 故意留空:错误恢复完全交给主循环 UART_RX_CheckError()。
* DMA CR3.EIE
* 饿
* CR3.DMAR DMA HAL */
(void)ptUartUserData;
}
#endif /* CONFIG_UART_IT_IDLEDMA */
ptUartUserData->m_last_dma_pos = current_pos; // 更新位置 #ifdef CONFIG_UART_IT_IDLEDMA
/* 在主循环中调用:检测 DMA 是否被 HAL 错误路径杀死(CR3.DMAR 位被清),
* DMA 线
* 使 abort DMA
* 饿 */
void UART_RX_CheckError(TComCtrl *_ptComCtrl)
{
if (_ptComCtrl == NULL)
return;
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
if (ptUartUserData == NULL)
return;
UART_HandleTypeDef *huart = ptUartUserData->m_uart;
/* DMA 正常运行时 CR3.DMAR 置位;HAL 错误路径会清掉它并 abort DMA。 */
if ((huart->Instance->CR3 & USART_CR3_DMAR) == 0U)
{
/* 清掉所有错误/空闲标志,防止重启后立刻又触发 */
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF | UART_CLEAR_FEF |
UART_CLEAR_NEF | UART_CLEAR_PEF |
UART_CLEAR_IDLEF);
/* 重置 HAL 软件状态,使 ReceiveToIdle_DMA 能成功启动 */
huart->RxState = HAL_UART_STATE_READY;
huart->ErrorCode = HAL_UART_ERROR_NONE;
ptUartUserData->m_last_dma_pos = 0;
(void)HAL_UARTEx_ReceiveToIdle_DMA(huart,
ptUartUserData->m_dma_rx_buf,
ptUartUserData->m_buf_size);
}
}
#endif /* CONFIG_UART_IT_IDLEDMA */
/* 兼容入口:供仍手动在 UART 中断里调用本函数的旧模块使用。
* Spoolend USART1/USART3 HAL_UART_IRQHandler
* IDLE DMA */
void UART_DMA_RX_IRQHandler(TComCtrl *_ptComCtrl)
{
#ifdef CONFIG_UART_IT_IDLEDMA
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
UART_HandleTypeDef *huart = ptUartUserData->m_uart;
if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
bsp_uart_process_rx(_ptComCtrl);
} }
#endif #endif
} }

2
bspMCU/include/bsp_uart.h

@ -27,6 +27,7 @@ typedef struct
DMA_HandleTypeDef *m_hdma_rx; DMA_HandleTypeDef *m_hdma_rx;
uint8_t *m_dma_rx_buf; uint8_t *m_dma_rx_buf;
uint16_t m_last_dma_pos; uint16_t m_last_dma_pos;
volatile uint8_t m_rx_error;
#else #else
uint8_t m_temp; uint8_t m_temp;
#endif #endif
@ -36,6 +37,7 @@ extern TUartUserData *UART_userdata_init(int _iID, int32_t _uiBaudRate, uint32_t
extern void UART_IT_init(TComCtrl *_ptComCtrl); extern void UART_IT_init(TComCtrl *_ptComCtrl);
extern void UART_DMA_RX_IRQHandler(TComCtrl *_ptComCtrl); extern void UART_DMA_RX_IRQHandler(TComCtrl *_ptComCtrl);
extern void UART_RX_IRQHandler(TComCtrl *_ptComCtrl); extern void UART_RX_IRQHandler(TComCtrl *_ptComCtrl);
extern void UART_RX_CheckError(TComCtrl *_ptComCtrl);
#endif #endif
#endif /* end of include guard: BSP_UART_H */ #endif /* end of include guard: BSP_UART_H */

13
project/Spoolend/Spoolend.c

@ -167,13 +167,13 @@ static void handle_power_on_off(void)
void SpoolendInit(void) void SpoolendInit(void)
{ {
/* RS485_1: Modbus 从机 — check/decode 回调由 rd_ComRead 自动调用 */ /* RS485_1: Modbus 从机 — check/decode 回调由 rd_ComRead 自动调用 */
TUartUserData *ptUartUserData = UART_userdata_init(1, 115200, 512); TUartUserData *ptUartUserData = UART_userdata_init(1, 115200, 4096);
g_ptRS485_1 = rd_ComCreate(Modbus_Slave_Check, Modbus_Slave_Decode, g_ptRS485_1 = rd_ComCreate(Modbus_Slave_Check, Modbus_Slave_Decode,
RS485_1_Send, ptUartUserData->m_buf_size, ptUartUserData); RS485_1_Send, ptUartUserData->m_buf_size, ptUartUserData);
UART_IT_init(g_ptRS485_1); UART_IT_init(g_ptRS485_1);
/* RS485_2: Modbus 主机(DMK 驱动器)*/ /* RS485_2: Modbus 主机(DMK 驱动器)*/
TUartUserData *ptUartUserData2 = UART_userdata_init(3, 115200, 512); TUartUserData *ptUartUserData2 = UART_userdata_init(3, 115200, 4096);
g_ptRS485_2 = rd_ComCreate(NULL, NULL, RS485_2_Send, g_ptRS485_2 = rd_ComCreate(NULL, NULL, RS485_2_Send,
ptUartUserData2->m_buf_size, ptUartUserData2); ptUartUserData2->m_buf_size, ptUartUserData2);
UART_IT_init(g_ptRS485_2); UART_IT_init(g_ptRS485_2);
@ -203,6 +203,11 @@ void SpoolendTask(void)
/* 喂狗 */ /* 喂狗 */
HAL_IWDG_Refresh(&hiwdg); HAL_IWDG_Refresh(&hiwdg);
/* 通信错误恢复:DMA 被 HAL 错误路径杀死后,在这里(主循环/线程上下文)
* CR3.DMAR DMA */
UART_RX_CheckError(g_ptRS485_1);
UART_RX_CheckError(g_ptRS485_2);
/* 读取 Modbus 从机帧(rd_ComRead 内部自动调用 Modbus_Slave_Decode)*/ /* 读取 Modbus 从机帧(rd_ComRead 内部自动调用 Modbus_Slave_Decode)*/
__disable_irq(); __disable_irq();
int i485_1 = rd_ComRead(g_ptRS485_1, pcBuffer1, sizeof(pcBuffer1)); int i485_1 = rd_ComRead(g_ptRS485_1, pcBuffer1, sizeof(pcBuffer1));
@ -258,6 +263,10 @@ void SpoolendTask(void)
holdingRegisters[10] = 1; holdingRegisters[10] = 1;
} }
/* 通信错误恢复(中断里只设标志,重启在这里做,避免中断死循环)*/
UART_RX_CheckError(g_ptRS485_1);
UART_RX_CheckError(g_ptRS485_2);
/* 发送处理 */ /* 发送处理 */
rd_ComSendProc(g_ptRS485_1); rd_ComSendProc(g_ptRS485_1);
rd_ComSendProc(g_ptRS485_2); rd_ComSendProc(g_ptRS485_2);

Loading…
Cancel
Save