硬汉嵌入式论坛

 找回密码
 立即注册
查看: 673|回复: 11
收起左侧

[DMA] 串口DMA

[复制链接]

2

主题

8

回帖

14

积分

新手上路

积分
14
发表于 2025-6-4 11:19:35 | 显示全部楼层 |阅读模式
uint8_t RecData[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  HAL_UART_Transmit_IT(&huart1, RecData, sizeof(RecData));
  HAL_UART_Receive_IT(&huart1, RecData, sizeof(RecData));
}

  HAL_UART_Receive_IT(&huart1, RecData, sizeof(RecData));
这段代码就可以正常回传消息,这个是串口中断

但是使用DMA后没办法正常收发是哪里出了问题吗?代码如下

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  HAL_UART_Transmit_DMA(&huart1, RecData, sizeof(RecData));
  HAL_UART_Receive_DMA(&huart1, RecData, sizeof(RecData));
}
  HAL_UART_Receive_DMA(&huart1, RecData, sizeof(RecData));


dma配置:

    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1 DMA Init */
    /* USART1_RX Init */
    hdma_usart1_rx.Instance = DMA1_Stream1;
    hdma_usart1_rx.Init.Request = DMA_REQUEST_USART1_RX;
    hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_rx.Init.Mode = DMA_NORMAL;
    hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);

    /* USART1_TX Init */
    hdma_usart1_tx.Instance = DMA1_Stream0;
    hdma_usart1_tx.Init.Request = DMA_REQUEST_USART1_TX;
    hdma_usart1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_usart1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_usart1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_usart1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_usart1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_usart1_tx.Init.Mode = DMA_NORMAL;
    hdma_usart1_tx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_usart1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    if (HAL_DMA_Init(&hdma_usart1_tx) != HAL_OK)
    {
      Error_Handler();
    }

    __HAL_LINKDMA(uartHandle,hdmatx,hdma_usart1_tx);




回复

使用道具 举报

2

主题

8

回帖

14

积分

新手上路

积分
14
 楼主| 发表于 2025-6-4 14:14:29 | 显示全部楼层
HAL库的一些配置
微信图片_20250604141055.png
微信图片_20250604141130.png
微信图片_20250604141143.png
微信图片_20250604141202.png
微信图片_20250604141213.png
微信图片_20250604141249.png
回复

使用道具 举报

25

主题

232

回帖

307

积分

高级会员

积分
307
QQ
发表于 2025-6-4 16:32:01 | 显示全部楼层
DMA不是这样直接替换的,需要更改串口 API 的使用方法。

建议参考
stbanana/Dataflow: Dataflow middleware. Enhanced use of DMA and half-duplex.

DMA 需要摒弃寄存器操作的思路,采用合理的通道思维。
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2025-6-5 07:33:37 | 显示全部楼层
我这个例子是一个简易的串口DMA例子,适合楼主对比参考

【STM32H743实验例程】实验14:STM32H743串口DMA方式收发,DMA方式问题比较多,要注意数据一致性
https://forum.anfulai.cn/forum.p ... 6271&fromuid=58
(出处: 硬汉嵌入式论坛)
回复

使用道具 举报

4

主题

24

回帖

36

积分

新手上路

积分
36
发表于 2025-6-5 11:26:36 | 显示全部楼层
H7的DMA1能访问的SRAM只有SRAM1~SRAM3,具体看一下手册,访问不了AXI-RAM和DTCM。配置没啥问题。我也是这么配置的,如果配置了Cache,还得注意数据一致性。你前面没有配置MPU,注意DMA1的访问权限吧。坛里硬汉大佬的关于MPU和Cache还是四通八达的矩阵(具体忘了)那个帖子里面视频说的很详细的。
回复

使用道具 举报

25

主题

232

回帖

307

积分

高级会员

积分
307
QQ
发表于 2025-6-5 16:18:02 | 显示全部楼层
lyj41801 发表于 2025-6-5 11:26
H7的DMA1能访问的SRAM只有SRAM1~SRAM3,具体看一下手册,访问不了AXI-RAM和DTCM。配置没啥问题。我也是这么 ...

我也了解过这种说法,stm32H7放在D2里。
但是我用GD的H7,DMA buffer区就放在 AXI 也能正常工作,这是为啥?
回复

使用道具 举报

4

主题

24

回帖

36

积分

新手上路

积分
36
发表于 2025-6-5 20:17:31 | 显示全部楼层
yono 发表于 2025-6-5 16:18
我也了解过这种说法,stm32H7放在D2里。
但是我用GD的H7,DMA buffer区就放在 AXI 也能正常工作,这是为 ...

GD32H7和STM32H7是两个东西,不要混为一谈,STM32H7B0和STM32H743差别还很大呢。就事论事
回复

使用道具 举报

2

主题

8

回帖

14

积分

新手上路

积分
14
 楼主| 发表于 2025-6-9 10:32:47 | 显示全部楼层
eric2013 发表于 2025-6-5 07:33
我这个例子是一个简易的串口DMA例子,适合楼主对比参考

【STM32H743实验例程】实验14:STM32H743串口DMA ...

好的佬,我全看看
回复

使用道具 举报

2

主题

8

回帖

14

积分

新手上路

积分
14
 楼主| 发表于 2025-6-9 10:33:15 | 显示全部楼层
yono 发表于 2025-6-4 16:32
DMA不是这样直接替换的,需要更改串口 API 的使用方法。

建议参考

好的佬,我去看看
回复

使用道具 举报

39

主题

1516

回帖

1638

积分

至尊会员

积分
1638
发表于 2025-6-10 08:43:42 | 显示全部楼层
lyj41801 发表于 2025-6-5 11:26
H7的DMA1能访问的SRAM只有SRAM1~SRAM3,具体看一下手册,访问不了AXI-RAM和DTCM。配置没啥问题。我也是这么 ...

DMA1可以访问AXI SRAM以及SRAM1~SRAM4,不要误人子弟了
回复

使用道具 举报

2

主题

8

回帖

14

积分

新手上路

积分
14
 楼主| 发表于 2025-6-10 20:15:48 | 显示全部楼层
sanit 发表于 2025-6-10 08:43
DMA1可以访问AXI SRAM以及SRAM1~SRAM4,不要误人子弟了

您好,就是我在轮询里面 HAL_UART_Transmit_DMA(&huart1, RecData, sizeof(RecData));使用这个函数也不行,我让这个RecData为hello world,循环模式,普通模式都发不出东西
回复

使用道具 举报

4

主题

90

回帖

102

积分

初级会员

积分
102
发表于 2025-6-11 13:25:38 | 显示全部楼层
串口的 DMA 肯定不用循环模式,一楼的 DMA 配置代码没问题,CubeMX 内也只需要配置 DMA,其他都保持默认。个人为了免去一行独立的 sp(n)printf,推荐使用 va_list 合并到自定义函数内,供参考。


  • bsp_uart.c/.h

[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));
}



  • main.c/.h

[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);
	}
}


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-12 03:40 , Processed in 0.063511 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表