Stm32F4 系列使用DMA发送串口数据

关注+星标公众,及时获取更多技术分享~ 

 作者 | 冰茶奥利奥

微信公众号 | 嵌入式电子创客街 

 


以前在使用stm32F103系列单片机时,若要将串口配置成DMA发送,只需要正确配置DMA通道,然后发送数据时候,给DMA通道的CNDTR寄存器赋以发送长度,再使能DMA即可。

如下代码所示:F103版本

while (DMA_GetCurrDataCounter(DMA1_Channel7));// 检查DMA发送通道内是否还有数据

memcpy(DMASendBuf, Buffer, (ucSend_num > 1024 ? 1024 : ucSend_num));

//DMA发送数据-要先关 设置发送长度 开启DMA
DMA_Cmd(DMA1_Channel7, DISABLE);
DMA1_Channel7->CNDTR = ucSend_num;// 设置发送长度
DMA_Cmd(DMA1_Channel7, ENABLE);  // 启动DMA发送

这次在F407上使用,发现像上面一样设置并不能用,DMA只能发送一次,后面直接不发送了。

如下代码所示:F407 错误配置DMA

while (DMA_GetCurrDataCounter(DEBUG_USART_DMA_STREAM));// 检查DMA发送通道内是否还有数据

memcpy((uint8_t *)SendBuff, Buf,(Len > 1024 ? 1024 : Len));

//DMA发送数据 设置发送长度 开启DMA
DMA_Cmd(DEBUG_USART_DMA_STREAM, DISABLE);  // 关闭DMA发送
DEBUG_USART_DMA_STREAM->NDTR = Len;
DMA_Cmd(DEBUG_USART_DMA_STREAM, ENABLE);  // 启动DMA发送

后来查阅手册,发现串口使用DMA通信的话,官方要求使能DMA发送完成中断,并在中断中清除TC标志位。

如下代码所示:官方手册中要求的DMA发送过程

// 配置DMA
void USART_DMA_Init()
{
    //...
    NVIC_InitTypeDef NVIC_InitStructure;
   NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;  
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  	 
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  
	NVIC_Init(&NVIC_InitStructure);  
	DMA_ITConfig(DMA2_Stream7,DMA_IT_TC,ENABLE);
}
// DMA中断
void DMA2_Stream7_IRQHandler(void)
{
	//清除标志
	if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
	{ 
		DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7);//清除DMA2_Steam7传输完成标志
	}
}

void Send_Data(u8 *Buf, uint16_t Len)
{
    //...
    //发送函数 此时不用配置DMA TC中断
    while (DMA_GetCurrDataCounter(DMA2_Stream7));// 检查DMA发送通道内是否还有数据
    
    memcpy((uint8_t *)SendBuff, Buf,(Len > 1024 ? 1024 : Len));

    //DMA发送数据 设置发送长度 开启DMA
    DMA_Cmd(DMA2_Stream7, DISABLE);  // 关闭DMA发送
    DMA2_Stream7->NDTR = Len;
    DMA_Cmd(DMA2_Stream7, ENABLE);  // 启动DMA发送
}

我认为此举没有必要,便关闭了DMA TC中断,再每次发送前清除TC标志位即可。

如下代码所示:在之前代码基础上增加一条清除DMA TC标志位的指令。

//发送函数 此时不用配置DMA TC中断
while (DMA_GetCurrDataCounter(DMA2_Stream7));// 检查DMA发送通道内是否还有数据
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) /*确保发送结束*/
DMA_ClearFlag(DMA2_Stream7, DMA_FLAG_TCIF7);
if(Buf) 
{
    memcpy((uint8_t *)Rs485No1DmaSendBuf, Buf,(Len > 1024 ? 1024 : Len));
}

//DMA发送数据 设置发送长度 开启DMA
DMA_Cmd(DMA2_Stream7, DISABLE);  // 关闭DMA发送
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE){}	//确保DMA可以被设置 
DMA_SetCurrDataCounter(DMA2_Stream7, Len);// 设置发送长度
DMA_Cmd(DMA2_Stream7, ENABLE);  // 启动DMA发送

但是发现一个问题,如果不使用DMA TC中断,在DMA在初始化配置时,缓存区大小不能设置为0,即:

DMA_Initstructure1.DMA_BufferSize = 1;        //此值随便赋,但是不能为0。

我猜测这可能是芯片的一个BUG,着实有点坑。

以下是Stm32F1手册中关于串口DMA发送的介绍

以下是Stm32F4手册中关于串口DMA发送的介绍


如果您觉得这篇文章帮到了你,请点赞或者留下您的评论,您的鼓励是我前进的动力~

关注博主公众号 “嵌入式电子创客街” 获取更多及时技术分享~

  关注+星标公众,及时获取更多技术分享~ 

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
您可以按照以下步骤来使用STM32F4串口DMA发送数据: 1. 首先,确保已经正确初始化了UART和DMA外设,并使能相应的时钟。 2. 配置UART的发送模式为DMA模式。可以使用函数`HAL_UART_Transmit_DMA()`来实现。 3. 创建一个数据缓冲区,将要发送数据存储在其中。 4. 配置DMA的传输参数,设置源地址为数据缓冲区的地址,目的地址为UART的数据寄存器地址,传输方向为从内存到外设。 5. 启动DMA传输,使用函数`HAL_DMA_Start()`。 6. 当DMA传输完成后,会触发一个中断。在中断服务函数中,可以执行一些操作来处理传输完成的事件。 下面是一个简单的示例代码片段,演示了如何使用STM32F4串口DMA发送数据: ```c #include "stm32f4xx_hal.h" #define UART_HANDLE huart2 #define UART_DMA_HANDLE hdma_usart2_tx UART_HandleTypeDef UART_HANDLE; DMA_HandleTypeDef UART_DMA_HANDLE; uint8_t txData[] = "Hello, world!"; int main(void) { // 初始化HAL库 HAL_Init(); // 使能UART和DMA外设的时钟 __HAL_RCC_USART2_CLK_ENABLE(); __HAL_RCC_DMA1_CLK_ENABLE(); // 配置UART引脚和参数 UART_HANDLE.Instance = USART2; UART_HANDLE.Init.BaudRate = 115200; UART_HANDLE.Init.WordLength = UART_WORDLENGTH_8B; UART_HANDLE.Init.StopBits = UART_STOPBITS_1; UART_HANDLE.Init.Parity = UART_PARITY_NONE; UART_HANDLE.Init.Mode = UART_MODE_TX; HAL_UART_Init(&UART_HANDLE); // 配置DMA参数 UART_DMA_HANDLE.Instance = DMA1_Stream6; UART_DMA_HANDLE.Init.Channel = DMA_CHANNEL_4; UART_DMA_HANDLE.Init.Direction = DMA_MEMORY_TO_PERIPH; UART_DMA_HANDLE.Init.PeriphInc = DMA_PINC_DISABLE; UART_DMA_HANDLE.Init.MemInc = DMA_MINC_ENABLE; UART_DMA_HANDLE.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; UART_DMA_HANDLE.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; UART_DMA_HANDLE.Init.Mode = DMA_NORMAL; UART_DMA_HANDLE.Init.Priority = DMA_PRIORITY_LOW; UART_DMA_HANDLE.Init.FIFOMode = DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&UART_DMA_HANDLE); // 关联UART和DMA __HAL_LINKDMA(&UART_HANDLE, hdmatx, UART_DMA_HANDLE); // 启动DMA传输 HAL_UART_Transmit_DMA(&UART_HANDLE, txData, sizeof(txData)); // 死循环 while (1) { // 可以执行其他操作 } } ``` 请根据您的具体需求进行适当的更改和调整。希望这对您有所帮助!如果您有任何问题,请随时提问。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值