STM32CubeMX:串口DMA

DMA:直接储存区访问,DMA传输将数据从一个地址空间复制到另一个空间。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM何IO设备开辟一条直接传输数据的通道,从而可以提高CPU的效率。一般用到DMA,主要作用为CPU减负。
我们用STM32CubeMX来配置一下串口的DMA实现收发。
选择芯片:
STM32CubeMX:串口DMA_第1张图片
根据自己的电路设计情况配置时钟,我用的外部晶振25M
STM32CubeMX:串口DMA_第2张图片

然后配置串口及DMA这里我们配置两个串口串口1和串口2,让这两个串口实现互通,两个串口的配置一样,这里展示串口1的
STM32CubeMX:串口DMA_第3张图片
STM32CubeMX:串口DMA_第4张图片
其他配置和我们平时用串口配置一样,就是在DMA这里开启收发就可以,点击Add添加,收发都添加进去,下面mode设置可以选择Normal表单次传输,传输一次后终止传输,Circular表示循环传输,传输完成后又重新开始继续传输,不断循环永不停止。此处选择单次传输。
Increment Address表示地址指针递增。串口数据发送寄存器只能存储8bit,每次发送一个字节,所以数据长度选择Byte。
STM32CubeMX:串口DMA_第5张图片

最后这里中断要开启,DMA的是默认开启的,如果不开启串口中断,则程序只能发送一次数据,程序不能判断DMA传输是否完成,USART一直处于busy状态。这样我们就配置好了,串口2也如此配置,
STM32CubeMX:串口DMA_第6张图片
然后设置工程名称及存放地址和使用的编译器,点击生成。生成后点击打开工程,可以先编译一下正常的是没有错误的

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

	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_第7张图片
我们定义了一个数组,然后在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中添加位置:添加在
/* UART in mode Transmitter ------------------------------------------------*/的前面,可ctrl+F弹出查找,输入点击查找。
STM32CubeMX:串口DMA_第8张图片

__weak void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart)
{
UNUSED(huart);
}

将上述代码添加到
__weak void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart)函数的前面。
STM32CubeMX:串口DMA_第9张图片

再接着
在stm32f4xx_hal_uart.h中添加
void HAL_UART_IdleCpltCallback(UART_HandleTypeDef *huart);
STM32CubeMX:串口DMA_第10张图片

最后一步在在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_第11张图片
编译程序然后下载通过串口发数据,我们会看到串口1发的数据串口2会收到,同时串口2发的数据串口1会收到。
STM32CubeMX:串口DMA_第12张图片
当然这个方法有个弊端,就是我们直接修改的是库函数 ,那么再次用STM32CubeMX生成代码时stm32f4xx_hal_uart.h和stm32f4xx_hal_uart.c中修改的内容就没有了,需要重新修改,其实库函数的文件在你每次改动STM32CubeMX后生成代码时是不会变的,所以先把改好的这两个文件复制一下,然后在生成后直接粘贴过来替代就行。

你可能感兴趣的:(STM32,STM32CubeMX,stm32,单片机,嵌入式硬件)