You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
282 lines
9.5 KiB
282 lines
9.5 KiB
#include "bsp_uart.h"
|
|
#ifdef USE_UART
|
|
extern DMA_HandleTypeDef hdma_usart1_rx;
|
|
extern DMA_HandleTypeDef hdma_usart2_rx;
|
|
extern DMA_HandleTypeDef hdma_usart3_rx;
|
|
#ifdef IOC_100PIN
|
|
extern DMA_HandleTypeDef hdma_uart4_rx;
|
|
extern DMA_HandleTypeDef hdma_uart5_rx;
|
|
extern DMA_HandleTypeDef hdma_usart6_rx;
|
|
extern DMA_HandleTypeDef hdma_uart7_rx;
|
|
#endif
|
|
|
|
#ifdef CONFIG_UART_IT_IDLEDMA
|
|
static TUartTab g_ptUartTab[] = {
|
|
{&hlpuart1, &hdma_usart2_rx},
|
|
{&huart1, &hdma_usart1_rx},
|
|
{&huart2, &hdma_usart2_rx},
|
|
{&huart3, &hdma_usart3_rx},
|
|
#ifdef IOC_100PIN
|
|
{&huart4, &hdma_uart4_rx},
|
|
{&huart5, &hdma_uart5_rx},
|
|
{&huart6, &hdma_usart6_rx},
|
|
{&huart7, &hdma_uart7_rx}
|
|
#endif
|
|
};
|
|
#else
|
|
static TUartTab g_ptUartTab[] = {
|
|
{&hlpuart1, NULL},
|
|
{&huart1, NULL},
|
|
{&huart2, NULL},
|
|
{&huart3, NULL},
|
|
#ifdef IOC_100PIN
|
|
{&huart4, NULL},
|
|
{&huart5, NULL},
|
|
{&huart6, NULL},
|
|
{&huart7, NULL}
|
|
#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)
|
|
{
|
|
_pUartHandle->Init.BaudRate = _uiBaudRate;
|
|
if (HAL_UART_Init(_pUartHandle) != HAL_OK)
|
|
{
|
|
Error_Handler();
|
|
}
|
|
}
|
|
|
|
TUartUserData *UART_userdata_init(int _iID, int32_t _uiBaudRate, uint32_t _uiSize)
|
|
{
|
|
if (_iID < 0 || _iID >= (sizeof(g_ptUartTab)/sizeof(g_ptUartTab[0])))
|
|
return NULL;
|
|
|
|
TUartUserData *ptUartUserData = (TUartUserData *)RD_CALLOC(1, sizeof(TUartUserData));
|
|
ptUartUserData->m_buf_size = (_uiSize == 0) ? CONFIG_UART_BUFFER_SIZE : _uiSize;
|
|
#ifdef CONFIG_UART_IT_IDLEDMA
|
|
uint8_t *pDMABuf = (uint8_t *)RD_CALLOC(1, ptUartUserData->m_buf_size);
|
|
ptUartUserData->m_dma_rx_buf = pDMABuf;
|
|
ptUartUserData->m_last_dma_pos = 0;
|
|
|
|
ptUartUserData->m_hdma_rx = g_ptUartTab[_iID].m_hdma_rx;
|
|
#endif
|
|
ptUartUserData->m_uart = g_ptUartTab[_iID].m_uart;
|
|
|
|
if (_uiBaudRate > 0)
|
|
{
|
|
bsp_uart_set_baudrate(g_ptUartTab[_iID].m_uart, _uiBaudRate);
|
|
}
|
|
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)
|
|
{
|
|
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
|
|
#ifdef CONFIG_UART_IT_IDLEDMA
|
|
/* 记录 UART 实例 -> TComCtrl 的映射,供 HAL 回调反查 */
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 用 HAL 官方的 "接收直到 IDLE" API 启动循环 DMA 接收。
|
|
* 它内部会:启动 Circular DMA + 使能 IDLE 中断 + 设 ReceptionType=TOIDLE。
|
|
*
|
|
* 之后所有事件统一由 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();
|
|
}
|
|
#else
|
|
HAL_UART_Receive_IT(ptUartUserData->m_uart, &ptUartUserData->m_temp, 1);
|
|
#endif
|
|
}
|
|
|
|
#ifdef CONFIG_UART_IT_IDLEDMA
|
|
/* 共享的 DMA 环形缓冲数据搬运逻辑:
|
|
* 根据 DMA 当前写指针与上次处理位置,把新收到的字节送入 rd_ComRecvProc。 */
|
|
static void bsp_uart_process_rx(TComCtrl *_ptComCtrl)
|
|
{
|
|
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
|
|
uint16_t current_pos = ptUartUserData->m_buf_size
|
|
- __HAL_DMA_GET_COUNTER(ptUartUserData->m_hdma_rx);
|
|
uint16_t data_len = 0;
|
|
|
|
if (current_pos >= ptUartUserData->m_last_dma_pos)
|
|
{
|
|
data_len = current_pos - ptUartUserData->m_last_dma_pos;
|
|
if (data_len > 0)
|
|
{
|
|
rd_ComRecvProc(_ptComCtrl,
|
|
(const char *)&ptUartUserData->m_dma_rx_buf[ptUartUserData->m_last_dma_pos],
|
|
data_len);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* DMA 写指针回绕 (Wrap around) */
|
|
uint16_t part1 = ptUartUserData->m_buf_size - ptUartUserData->m_last_dma_pos;
|
|
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/PE,HAL 检测到后会在本回调被调用前
|
|
* 同步完成: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 */
|
|
|
|
#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
|
|
}
|
|
|
|
void UART_RX_IRQHandler(TComCtrl *_ptComCtrl)
|
|
{
|
|
#ifndef CONFIG_UART_IT_IDLEDMA
|
|
TUartUserData *ptUartUserData = (TUartUserData *)_ptComCtrl->m_pUserData;
|
|
rd_ComRecvProc(_ptComCtrl, (const char*)&ptUartUserData->m_temp, 1);
|
|
HAL_UART_Receive_IT(ptUartUserData->m_uart, &ptUartUserData->m_temp, 1);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|