原文:http://www.javashuo.com/article/p-kdcsduhv-hc.htmlgit
以前的一篇文章中我为了能够实现USART接收任意长度的数据,对HAL的库进行了修改,能够实现接收以0x0a结尾的任意长度数据,即认为接收到0x0a时接收结束,见连接:HAL USART接收任意长度。
然而,上述这种方法并不合适,原则上HAL库通常不去修改,不便于其余人移植程序,下降了程序中库的适用性,这是很很差的习惯,因此这种方法并不可取。
后查资料得知STM32中还能够利用DMA的方式实现串口的任意长度数据的接收,故开始学习DMA+串口接收任意长度的数据这种方式。github
首先,第一步都是进行时钟树的配置,配置好系统的时钟,不一样的芯片配置不一样的时钟频率,如图。
接着,配置USART1,选择异步asynchronous,软件自动配置了PA9和PA10管脚。
而后,继续添加USART1的发送和接收DMA,其他默认便可。
接着,勾选上USART1的中断使能。
最后,生成MDK-ARM V5版本环境的程序。
数组
//添加变量,为何用关键字volatile见连接:[连接](http://blog.csdn.net/u014470361/article/details/78830147) volatile uint8_t rx_len=0; volatile uint8_t recv_end_flag=0; uint8_t rx_buffer[200]; static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } //上面的usart配置代码为cubemx自动生成的,在下方添加使能idle中断和打开串口DMA接收语句 __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//使能idle中断 HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//打开DMA接收,数据存入rx_buffer数组中。 }
接下来修改串口中断函数。缓存
void USART1_IRQHandler(void) { uint32_t tmp_flag = 0; uint32_t temp; tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位 if((tmp_flag != RESET))//idle标志被置位 { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位 temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器能够实现清除SR寄存器的功能 temp = huart1.Instance->DR; //读取数据寄存器中的数据 HAL_UART_DMAStop(&huart1); // temp = hdma_usart1_rx.Instance->NDTR;// 获取DMA中未传输的数据个数,NDTR寄存器分析见下面 rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,获得已经接收的数据个数 recv_end_flag = 1; // 接受完成标志位置1 } HAL_UART_IRQHandler(&huart1); }
DMA通道结构体中定义了NDTR寄存器,那为何是未传输的数据数呢,STM32的中文手册给出了该寄存器的具体说明。markdown
typedef struct { __IO uint32_t CR; /*!< DMA stream x configuration register */ __IO uint32_t NDTR; /*!< DMA stream x **number of data register** */ __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ } DMA_Stream_TypeDef;
接着,编写主函数中串口中断的处理函数。异步
int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); while (1) { if(recv_end_flag ==1) { printf("rx_len=%d\r\n",rx_len);//打印接收长度 HAL_UART_Transmit(&huart1,rx_buffer, rx_len,200);接收数据打印出来 for(uint8_t i=0;i<rx_len;i++) { rx_buffer[i]=0;//清接收缓存 } rx_len=0;//清除计数 recv_end_flag=0;//清除接收结束标志位 } HAL_UART_Receive_DMA(&huart1,rx_buffer,BUFFER_SIZE);//从新打开DMA接收 } }
程序的运行效果以下图所示 ,输入任意长度数据,串口打印出接收的数据长度并打印出接收的数据。本程序设置的接收长度最大BUFFER_SIZE是200,若想接收更长的数据,也能够把BUFFER_SIZE和数组长度改大。
###DMA参数和函数解析
DMA的基本原理、参数和函数解析在下一篇文章进行分析(连接)。async
本文章的源代码下载地址:https://download.csdn.net/download/u014470361/10234803函数