什么是DMA
DMA即直接存储器访问,Direct Memory Access.是外设和存储器或存储器之间的告诉数据传输。DMA传输方式不用CPU直接控制传输,而是在RAM和IO设备之间直接进行数据传输的通道,大大提高CPU的效率。
通过STM32cubeMX来配置串口DMA模式
首先打开串口,这里使用的是串口2,波特率115200,字长8Bit。并且使能串口2中断。
然后给串口2的两个IO口配置DMA通道。
在keil中编写相关程序代码
1.在uart.c中添加串口重定向函数
//串口发送函数HAL_UART_Transmit()重定向到printf()
#include "stdio.h"
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart2, (uint8_t*) &ch, 1, 0xffff);
return ch;
}
2.在main.h中创建一个用于串口接收数据的结构体
#define RX_BUF_MAX_LEN 512
typedef struct
{
uint8_t rx_buff[RX_BUF_MAX_LEN];//缓存区
uint16_t len; //接收长度
uint8_t flag; //接收标志位,1为接收完毕
}USRAT_RX;
3.打开stm32xxxx_it.c添加如下代码
首先定义一个结构体变量
USRAT_RX usart1_rx;
再找到串口中断服务函数
添加下列代码
void USART2_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart2);
if(__HAL_UART_CLEAR_FLAG(&huart2,UART_FLAG_IDLE) != RESET)
{//如果没有清除空闲中断(表示接收到了数据)
//清除空闲中断
__HAL_UART_CLEAR_IDLEFLAG(&huart2);
//停止串口2的DMA传输
HAL_UART_DMAStop(&huart2);
//计算接收到的数据长度
usart1_rx.len = RX_BUF_MAX_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
//计算接收到的数据长度
usart1_rx.flag = 1;
//如果没有接收到数据
if(usart1_rx.len == 0)
{
//flag置0,表示未接收到数据
usart1_rx.flag = 0;
//以DMA模式接收一定长度的字符串
HAL_UART_Receive_DMA(&huart2,(uint8_t*)usart1_rx.rx_buff,RX_BUF_MAX_LEN);
//恢复DMA传输
HAL_UART_DMAResume(&huart2);
}
}
}
4.在main.c中添加代码
首先声明结构体变量
extern USRAT_RX usart1_rx;
加入串口结构体初始化函数
void Clear_Usart(USRAT_RX *usart_rx)
//初始化(清空)串口结构体
{
//全写0
memset((uint8_t*)usart_rx,0,sizeof(USRAT_RX));
}
在主函数中添加功能
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
Clear_Usart(&usart1_rx); //初始化串口结构体
__HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE); //使能串口2的空闲中断
HAL_UART_Receive_DMA(&huart2,(uint8_t*)usart1_rx.rx_buff,RX_BUF_MAX_LEN); //打开DMA传输模式
while (1)
{
if(usart1_rx.flag == 1) //判断接收完成
{
printf("%s\r\n",usart1_rx.rx_buff); //打印发送的数据
if(strstr((const char *)usart1_rx.rx_buff,"LED_ON") != NULL)
{
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);
printf("Turn on the LED\r\n");
}
else if(strstr((const char *)usart1_rx.rx_buff,"LED_OFF") != NULL)
{
HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);
printf("Turn off the LED\r\n");
}
else
{
printf("error\r\n");
}
Clear_Usart(&usart1_rx); //清空串口结构体
HAL_UART_Receive_DMA(&huart2,(uint8_t*)usart1_rx.rx_buff,RX_BUF_MAX_LEN); //打开DMA模式
HAL_UART_DMAResume(&huart2); //恢复DMA传输
}
}
}
5.最终效果
通过串口工具,输入LED_ON打开LED灯,并反馈Turn on the LED;输入LED_OFF则关闭LED灯,并反馈Turn off the LED;输入其他数据则反馈error。