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.

283 lines
9.5 KiB

2 months ago
#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
2 months ago
#ifdef CONFIG_UART_IT_IDLEDMA
static TUartTab g_ptUartTab[] = {
{&hlpuart1, &hdma_usart2_rx},
2 months ago
{&huart1, &hdma_usart1_rx},
{&huart2, &hdma_usart2_rx},
{&huart3, &hdma_usart3_rx},
#ifdef IOC_100PIN
{&huart4, &hdma_uart4_rx},
{&huart5, &hdma_uart5_rx},
2 months ago
{&huart6, &hdma_usart6_rx},
{&huart7, &hdma_uart7_rx}
#endif
2 months ago
};
#else
static TUartTab g_ptUartTab[] = {
{&hlpuart1, NULL},
2 months ago
{&huart1, NULL},
{&huart2, NULL},
{&huart3, NULL},
#ifdef IOC_100PIN
2 months ago
{&huart4, NULL},
{&huart5, NULL},
{&huart6, NULL},
{&huart7, NULL}
#endif
2 months ago
};
#endif
/* HAL 回调只有 huart 指针,这里维护 "UART 实例 -> TComCtrl" 的映射,
* 使 bsp_uart.c project g_ptRS485_x */
static TComCtrl *s_aptComCtrl[sizeof(g_ptUartTab) / sizeof(g_ptUartTab[0])] = {0};
2 months ago
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)
2 months ago
{
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;
}
2 months ago
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;
}
}
}
2 months ago
/* 用 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)
2 months ago
{
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)
{
2 months ago
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)
2 months ago
{
data_len = current_pos - ptUartUserData->m_last_dma_pos;
if (data_len > 0)
2 months ago
{
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)
2 months ago
{
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);
2 months ago
}
}
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 */
2 months ago
#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 */
#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);
2 months ago
}
#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