天天看点

STM32CubeMX:串口DMA

DMA:直接储存区访问,DMA传输将数据从一个地址空间复制到另一个空间。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM何IO设备开辟一条直接传输数据的通道,从而可以提高CPU的效率。一般用到DMA,主要作用为CPU减负。

我们用STM32CubeMX来配置一下串口的DMA实现收发。

选择芯片:

STM32CubeMX:串口DMA

根据自己的电路设计情况配置时钟,我用的外部晶振25M

STM32CubeMX:串口DMA

然后配置串口及DMA这里我们配置两个串口串口1和串口2,让这两个串口实现互通,两个串口的配置一样,这里展示串口1的

STM32CubeMX:串口DMA
STM32CubeMX:串口DMA

其他配置和我们平时用串口配置一样,就是在DMA这里开启收发就可以,点击Add添加,收发都添加进去,下面mode设置可以选择Normal表单次传输,传输一次后终止传输,Circular表示循环传输,传输完成后又重新开始继续传输,不断循环永不停止。此处选择单次传输。

Increment Address表示地址指针递增。串口数据发送寄存器只能存储8bit,每次发送一个字节,所以数据长度选择Byte。

STM32CubeMX:串口DMA

最后这里中断要开启,DMA的是默认开启的,如果不开启串口中断,则程序只能发送一次数据,程序不能判断DMA传输是否完成,USART一直处于busy状态。这样我们就配置好了,串口2也如此配置,

STM32CubeMX:串口DMA

然后设置工程名称及存放地址和使用的编译器,点击生成。生成后点击打开工程,可以先编译一下正常的是没有错误的

发送比较简单 用这个函数直接就可发送,

HAL_UART_Transmit_DMA();
	uint8_t TXbuff[] = "\r\n**** UART-Hyperterminal communication based on DMA ***\r\n ";
	HAL_UART_Transmit_DMA(&huart1, (uint8_t *)TXbuff, sizeof(TXbuff)-1);
           
STM32CubeMX:串口DMA

我们定义了一个数组,然后在while里面循环每隔一秒发一下。

接收就比较麻烦,尤其是不定长的接受,这里我们用到串口的空闲中断,

首先我们要在库函数添加一点代码

/* UART in mode Idle -------------------------------------------------*/
if(((isrflags & USART_SR_IDLE) != RESET) && ((cr1its & USART_CR1_IDLEIE) != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(huart);
HAL_UART_IdleCpltCallback(huart);
return;
}
           

将上述代码添加到stm32f4xx_hal_uart.c中,该文件在工程的Drivers\STM32F4xx_HAL_Driver中添加位置:添加在

的前面,可ctrl+F弹出查找,输入点击查找。

STM32CubeMX:串口DMA
__weak void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
}
           

将上述代码添加到

__weak void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart)函数的前面。

STM32CubeMX:串口DMA

再接着

在stm32f4xx_hal_uart.h中添加

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);

STM32CubeMX:串口DMA

最后一步在在usart.c添加回调函数

void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)

{

}

然后这里我们在回调函数里面直接做一个串口1和串口2的数据中转

//定义一些变量和数据缓冲区
uint16_t RecCount1=0  ;      //接收数据个数
uint16_t RecCount2=0  ;      //接收数据个数

uint8_t UART_1_RxBuffer[1024];
uint8_t UART_2_RxBuffer[1024];

#define  LENGTH  1024     //接收缓存区大小,该值需要大于一帧数据的总字符数
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance == USART1)
	{

			HAL_UART_DMAStop(&huart1);

			//发生空闲中断时,已接收数据个数等于数据总量减去DMA数据流中待接收的数据个数
			RecCount1 = LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);

			HAL_UART_Transmit_DMA(&huart2,(uint8_t*)UART_1_RxBuffer,RecCount1);

			HAL_UART_Receive_DMA(&huart1,(uint8_t*)UART_1_RxBuffer,LENGTH);   //启动DMA接收
		

		
	}
	if(huart->Instance == USART2)
	{
			HAL_UART_DMAStop(&huart2);

			//发生空闲中断时,已接收数据个数等于数据总量减去DMA数据流中待接收的数据个数
			RecCount2 = LENGTH - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);

			HAL_UART_Transmit_DMA(&huart1,(uint8_t*)UART_2_RxBuffer,RecCount2);

			HAL_UART_Receive_DMA(&huart2,(uint8_t*)UART_1_RxBuffer,LENGTH);   //启动DMA接收



	}

}
           

然后在main函数的里启动DMA接收

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);    //使能IDLE中断
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)UART_1_RxBuffer,LENGTH);    //启动DMA接收
	__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);    //使能IDLE中断
	HAL_UART_Receive_DMA(&huart2,(uint8_t*)UART_2_RxBuffer,LENGTH);    //启动DMA接收
           
STM32CubeMX:串口DMA

编译程序然后下载通过串口发数据,我们会看到串口1发的数据串口2会收到,同时串口2发的数据串口1会收到。

STM32CubeMX:串口DMA

当然这个方法有个弊端,就是我们直接修改的是库函数 ,那么再次用STM32CubeMX生成代码时stm32f4xx_hal_uart.h和stm32f4xx_hal_uart.c中修改的内容就没有了,需要重新修改,其实库函数的文件在你每次改动STM32CubeMX后生成代码时是不会变的,所以先把改好的这两个文件复制一下,然后在生成后直接粘贴过来替代就行。

继续阅读