fix: move DMA buffers from DTCM to RAM_D1, improve bsp_usart error recovery

- Add .dma_buf section in RAM_D1 for DMA-accessible USART instance pool.
  DTCM (0x20000000) is not reachable by DMA on STM32H7, causing bus errors
  (TE) when DMA writes to malloc'd buffers placed in DTCM by the XG linker
  script.
- Replace malloc with static pool (usart_pool[]) placed in .dma_buf section.
- In RxEventCallback, set recv_buff_size from Size so module callbacks know
  how many bytes were actually received.
- Use USART_RXBUFF_LIMIT (256) as the DMA transfer size instead of per-module
  recv_buff_size. The actual buffer is always 256 bytes.
- Remove recv_buff_size from USART_Init_Config_s; it is now set at runtime.
- Flush RDR, clear error flags, and clear stale DMAR in error callback to
  prevent residual conditions from re-triggering errors on DMA restart.
- Switch Vision module from UART10 to UART1.
This commit is contained in:
2026-06-02 16:03:21 +08:00
parent 9313310a87
commit 88702e2832
9 changed files with 34 additions and 20 deletions
+8 -1
View File
@@ -241,7 +241,14 @@ SECTIONS
. = ALIGN(8);
} >DTCMRAM
/* DMA-accessible buffers in RAM_D1 (SRAM). DTCM is not reachable by DMA */
.dma_buf (NOLOAD) :
{
. = ALIGN(4);
*(.dma_buf)
*(.dma_buf*)
. = ALIGN(4);
} >RAM_D1
/* Remove information from the standard libraries */
/DISCARD/ :
+1 -1
View File
@@ -228,7 +228,7 @@ void RobotCMDInit()
PIDInit(&vision_pitch_pid, &pitch_vpid_config);
VisionKalmanFilterInit();
Vision_Uart_Init(&huart10);
Vision_Uart_Init(&huart1);
laser_off();
}
+24 -11
View File
@@ -10,9 +10,12 @@
*/
#include "bsp_usart.h"
#include "bsp_log.h"
#include "stdlib.h"
#include "memory.h"
/* Static pool in RAM_D1 (.dma_buf section) for DMA-accessible USART instances.
DTCM is not reachable by DMA, so malloc'd instances would cause bus errors. */
static USARTInstance usart_pool[DEVICE_USART_CNT] __attribute__((section(".dma_buf")));
/* usart service instance, modules' info would be recoreded here using USARTRegister() */
/* usart服务实例,所有注册了usart的模块信息会被保存在这里 */
static uint8_t idx;
@@ -28,7 +31,7 @@ static USARTInstance *usart_instance[DEVICE_USART_CNT] = {NULL};
*/
void USARTServiceInit(USARTInstance *_instance)
{
HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, _instance->recv_buff_size);
HAL_UARTEx_ReceiveToIdle_DMA(_instance->usart_handle, _instance->recv_buff, USART_RXBUFF_LIMIT);
// 关闭dma half transfer中断防止两次进入HAL_UARTEx_RxEventCallback()
// 这是HAL库的一个设计失误,发生DMA传输完成/半完成以及串口IDLE中断都会触发HAL_UARTEx_RxEventCallback()
// 我们只希望处理第一种和第三种情况,因此直接关闭DMA半传输中断
@@ -46,11 +49,10 @@ USARTInstance *USARTRegister(USART_Init_Config_s *init_config)
while (1)
LOGERROR("[bsp_usart] USART instance already registered!");
USARTInstance *instance = (USARTInstance *)malloc(sizeof(USARTInstance));
USARTInstance *instance = &usart_pool[idx];
memset(instance, 0, sizeof(USARTInstance));
instance->usart_handle = init_config->usart_handle;
instance->recv_buff_size = init_config->recv_buff_size;
instance->module_callback = init_config->module_callback;
usart_instance[idx++] = instance;
@@ -102,17 +104,18 @@ uint8_t USARTIsReady(USARTInstance *_instance)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
for (uint8_t i = 0; i < idx; ++i)
{ // find the instance which is being handled
{
if (huart == usart_instance[i]->usart_handle)
{ // call the callback function if it is not NULL
{
usart_instance[i]->recv_buff_size = Size; // 实际接收到的数据量
if (usart_instance[i]->module_callback != NULL)
{
usart_instance[i]->module_callback();
memset(usart_instance[i]->recv_buff, 0, Size); // 接收结束后清空buffer,对于变长数据是必要的
memset(usart_instance[i]->recv_buff, 0, Size);
}
HAL_UARTEx_ReceiveToIdle_DMA(usart_instance[i]->usart_handle, usart_instance[i]->recv_buff, usart_instance[i]->recv_buff_size);
HAL_UARTEx_ReceiveToIdle_DMA(usart_instance[i]->usart_handle, usart_instance[i]->recv_buff, USART_RXBUFF_LIMIT);
__HAL_DMA_DISABLE_IT(usart_instance[i]->usart_handle->hdmarx, DMA_IT_HT);
return; // break the loop
return;
}
}
}
@@ -130,9 +133,19 @@ void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
if (huart == usart_instance[i]->usart_handle)
{
HAL_UARTEx_ReceiveToIdle_DMA(usart_instance[i]->usart_handle, usart_instance[i]->recv_buff, usart_instance[i]->recv_buff_size);
LOGWARNING("[bsp_usart] USART error callback, idx [%d], ErrorCode: 0x%02X", i, huart->ErrorCode);
/* Flush RDR and clear error flags before restarting DMA */
(void)READ_REG(huart->Instance->RDR);
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF | UART_CLEAR_FEF | UART_CLEAR_NEF | UART_CLEAR_PEF);
huart->ErrorCode = HAL_UART_ERROR_NONE;
/* Clear stale DMAR to avoid a residual DMA request triggering a
bus error when UART_Start_Receive_DMA re-enables the stream */
ATOMIC_CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
HAL_UARTEx_ReceiveToIdle_DMA(usart_instance[i]->usart_handle, usart_instance[i]->recv_buff, USART_RXBUFF_LIMIT);
__HAL_DMA_DISABLE_IT(usart_instance[i]->usart_handle->hdmarx, DMA_IT_HT);
LOGWARNING("[bsp_usart] USART error callback triggered, instance idx [%d]", i);
return;
}
}
+1 -2
View File
@@ -24,7 +24,7 @@ typedef enum
typedef struct
{
uint8_t recv_buff[USART_RXBUFF_LIMIT]; // 预先定义的最大buff大小,如果太小请修改USART_RXBUFF_LIMIT
uint8_t recv_buff_size; // 模块接收一包数据的大小
uint16_t recv_buff_size; // 实际接收到的数据量(由RxEventCallback赋值)
UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle
usart_module_callback module_callback; // 解析收到的数据的回调函数
} USARTInstance;
@@ -32,7 +32,6 @@ typedef struct
/* usart 初始化配置结构体 */
typedef struct
{
uint8_t recv_buff_size; // 模块接收一包数据的大小
UART_HandleTypeDef *usart_handle; // 实例对应的usart_handle
usart_module_callback module_callback; // 解析收到的数据的回调函数
} USART_Init_Config_s;
-1
View File
@@ -33,7 +33,6 @@ HC05 *HC05Init(UART_HandleTypeDef *hc05_usart_handle)
USART_Init_Config_s conf;
conf.module_callback = HC05RxCallback;
conf.usart_handle = hc05_usart_handle;
conf.recv_buff_size = HC05_BUFFERSIZE;
hc05_usart_instance = USARTRegister(&conf);
hc05_init_flag = 1;
-1
View File
@@ -80,7 +80,6 @@ void Vision_Uart_Init(UART_HandleTypeDef *_handle)
{
USART_Init_Config_s conf = {
.module_callback = DecodeVision,
.recv_buff_size = 64,
.usart_handle = _handle,
};
cv_usart_instance = USARTRegister(&conf);
-1
View File
@@ -125,7 +125,6 @@ referee_info_t *RefereeInit(UART_HandleTypeDef *referee_usart_handle)
USART_Init_Config_s conf;
conf.module_callback = RefereeRxCallback;
conf.usart_handle = referee_usart_handle;
conf.recv_buff_size = RE_RX_BUFFER_SIZE; // mx 255(u8)
referee_usart_instance = USARTRegister(&conf);
Daemon_Init_Config_s daemon_conf = {
-1
View File
@@ -229,7 +229,6 @@ RC_ctrl_t *RemoteControlInit(UART_HandleTypeDef *rc_usart_handle)
USART_Init_Config_s conf;
conf.module_callback = RemoteControlRxCallback;
conf.usart_handle = rc_usart_handle;
conf.recv_buff_size = REMOTE_CONTROL_FRAME_SIZE;
rc_usart_instance = USARTRegister(&conf);
// 进行守护进程的注册,用于定时检查遥控器是否正常工作
-1
View File
@@ -59,7 +59,6 @@ board_comm_data_t* BoardCommInit(UART_HandleTypeDef *uart_handle)
USART_Init_Config_s conf;
conf.module_callback = BoardCommRxCallback;
conf.usart_handle = uart_handle;
conf.recv_buff_size = BOARD_COMM_FRAME_SIZE;
board_comm_usart_instance = USARTRegister(&conf);
// 暂时注释掉 daemon 注册,专注于数据传输