串口的 DMA 肯定不用循环模式,一楼的 DMA 配置代码没问题,CubeMX 内也只需要配置 DMA,其他都保持默认。个人为了免去一行独立的 sp(n)printf,推荐使用 va_list 合并到自定义函数内,供参考。
[C] 纯文本查看 复制代码 #define UART_BUF_SIZE 128 // including NUL('\0'), no more than 65535
typedef struct {
UART_HandleTypeDef *huart;
char *pTxMsg; // 发送缓存区指针(H7 注意要使 DMA 可访问)
char *pRxMsg; // 接收缓存区指针(H7 注意要使 DMA 可访问)
} myUART_HandleTypeDef;
void myUART_Start_Receive_DMA(myUART_HandleTypeDef *myhuart)
{
/* 清空缓存区,保证新接收的串口数据尾带有终止符 '\0'(若不使用,也可从回调函数的 Size 参数获得接收长度) */
memset(myhuart->pRxMsg, 0, UART_BUF_SIZE);
/* UART_BUF_SIZE - 1 的 -1 为了保证缓存区末尾一定有 '\0',便于解析处理 */
HAL_UARTEx_ReceiveToIdle_DMA(myhuart->huart, (uint8_t *)myhuart->pRxMsg, UART_BUF_SIZE - 1);
/* 关闭 DMA 半满中断,原因见下文 */
__HAL_DMA_DISABLE_IT(myhuart->huart->hdmarx, DMA_IT_HT);
}
void myUART_Transmit_DMA(myUART_HandleTypeDef *myhuart, const char *format, ...)
{
va_list ap;
va_start(ap, format);
/* 等待串口发送空闲,包括串口发送状态(还在上一次发送函数内)与发送 DMA(还在上一次发送过程内) */
while (myhuart->huart->gState != HAL_UART_STATE_READY || myhuart->huart->hdmatx->State != HAL_DMA_STATE_READY);
/* 使用可变参数列表,以 printf 格式将字符串打印至发送缓存区数组,函数保证 UART_BUF_SIZE 长度内尾部有 '\0' */
vsnprintf(myhuart->pTxMsg, UART_BUF_SIZE, format, ap);
va_end(ap);
/* HAL 库函数发送串口 */
HAL_UART_Transmit_DMA(myhuart->huart, (uint8_t *)myhuart->pTxMsg, strlen(myhuart->pTxMsg));
}
[C] 纯文本查看 复制代码 /* 使用 .sct 文件指定变量存储位置(Keil5 AC6 格式),默认为 DTCM */
#define __ZI_AXI_SRAM __attribute__((section(".bss.RAM_D1")))
#define __RW_AXI_SRAM __attribute__((section(".data.RAM_D1")))
/* 将串口缓存区存储于 DMA 可访问位置,此处取 AXI SRAM */
__ZI_AXI_SRAM char gUART1_TxMsg[UART_BUF_SIZE];
__ZI_AXI_SRAM char gUART1_RxMsg[UART_BUF_SIZE];
myUART_HandleTypeDef myUART = {
.huart = &huart1,
.pTxMsg = gUART1_TxMsg,
.pRxMsg = gUART1_RxMsg,
};
int main(void)
{
/* CubeMX 默认初始化函数 */
/* USER CODE BEGIN 2 */
/* 使用示例:串口发送本机 96 位 UID */
myUART_Transmit_DMA(&myUART, "> UID: %08X %08X %08X\n", HAL_GetUIDw2(), HAL_GetUIDw1(), HAL_GetUIDw0());
/* 开启串口接收 */
myUART_Start_Receive_DMA(&myUART);
/* USER CODE END 2 */
/* Infinite loop */
while (1)
{
}
}
/* 串口接收事件回调函数,可能进入此函数的方式有:
* (1) UART 空闲:完整接收到一批数据,来自 void HAL_UART_IRQHandler(UART_HandleTypeDef *huart);
* (2) DMA 完成:数据量过大且缓存区满,来自 static void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma);
* (3) DMA 半完成:数据量达到缓存区一半,来自 static void UART_DMARxHalfCplt(DMA_HandleTypeDef *hdma);
* 其中 (3) 不应认为数据接收完成,因此在每次开启空闲接收后都关闭该中断
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart == myUART.huart)
{
/* 解析处理串口数据,如果处理不至过于复杂,此处为加前缀回环 */
myUART_Transmit_DMA(&myUART, "RxMsg: %s", myUART.pRxMsg);
/* 再次启动串口接收,不写此句将导致无法再次接收 */
myUART_Start_Receive_DMA(&myUART);
}
}
|