STM32—cubeMX+DMA+USART 接收任意长度的数据
阅读原文时间:2021年04月20日阅读:1

前言

之前的一篇文章中我为了可以实现USART接收任意长度的数据,对HAL的库进行了修改,可以实现接收以0x0a结尾的任意长度数据,即认为接收到0x0a时接收结束,见链接:HAL USART接收任意长度
  然而,上述这种方法并不合适,原则上HAL库一般不去修改,不便于其他人移植程序,降低了程序中库的适用性,这是很不好的习惯,所以这种方法并不可取。
  后查资料得知STM32中还可以利用DMA的方式实现串口的任意长度数据的接收,故开始学习DMA+串口接收任意长度的数据这种方式。

cubeMX软件配置过程

首先,第一步都是进行时钟树的配置,配置好系统的时钟,不同的芯片配置不同的时钟频率,如图。

接着,配置USART1,选择异步asynchronous,软件自动配置了PA9和PA10管脚。

然后,继续添加USART1的发送和接收DMA,其余默认即可。

接着,勾选上USART1的中断使能。

最后,生成MDK-ARM V5版本环境的程序。

UASRT串口程序

//添加变量,为什么用关键字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的中文手册给出了该寄存器的具体说明。

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的基本原理、参数和函数解析在下一篇文章进行分析(链接)

本文章的源代码下载地址:https://download.csdn.net/download/u014470361/10234803

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章