你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

一个严谨的STM32串口DMA发送&接收(1.5Mbps波特率)机制

[复制链接]
STMCU 发布时间:2020-9-15 15:49
1 前言

  直接存储器访问(Direct Memory Access),简称DMA。DMA是CPU一个用于数据从一个地址空间到另一地址空间“搬运”(拷贝)的组件,数据拷贝过程不需CPU干预,数据拷贝结束则通知CPU处理。因此,大量数据拷贝时,使用DMA可以释放CPU资源。DMA数据拷贝过程,典型的有:

  • 内存—>内存,内存间拷贝
  • 外设—>内存,如uart、spi、i2c等总线接收数据过程
  • 内存—>外设,如uart、spi、i2c等总线发送数据过程


2 串口有必要使用DMA吗



  串口(uart)是一种低速的串行异步通信,适用于低速通信场景,通常使用的波特率小于或等于115200bps。对于小于或者等于115200bps波特率的,而且数据量不大的通信场景,一般没必要使用DMA,或者说使用DMA并未能充分发挥出DMA的作用。


  对于数量大,或者波特率提高时,必须使用DMA以释放CPU资源,因为高波特率可能带来这样的问题:

  • 对于发送,使用循环发送,可能阻塞线程,需要消耗大量CPU资源“搬运”数据,浪费CPU
  • 对于发送,使用中断发送,不会阻塞线程,但需浪费大量中断资源,CPU频繁响应中断;以115200bps波特率,1s传输11520字节,大约69us需响应一次中断,如波特率再提高,将消耗更多CPU资源
  • 对于接收,如仍采用传统的中断模式接收,同样会因为频繁中断导致消耗大量CPU资源


  因此,高波特率场景下,串口非常有必要使用DMA。


3 实现方式


1 (2).png



4 STM32串口使用DMA



  关于STM32串口使用DMA,不乏一些开发板例程及网络上一些博主的使用教程。使用步骤、流程、配置基本大同小异,正确性也没什么毛病,但都是一些基本的Demo例子,作为学习过程没问题;实际项目使用缺乏严谨性,数据量大时可能导致数据异常。


测试平台:

  • STM32F030C8T6
  • UART1/UART2
  • DMA1 Channel2—Channel5
  • ST标准库
  • 主频48MHz(外部12MHz晶振)


2.png


5 串口DMA接收

5.1 基本流程

3.png


串口接收流程图


5.2 相关配置


关键步骤

【1】初始化串口

【2】使能串口DMA接收模式,使能串口空闲中断

【3】配置DMA参数,使能DMA通道buf半满(传输一半数据)中断、buf溢满(传输数据完成)中断


为什么需要使用DMA 通道buf半满中断?


  很多串口DMA模式接收的教程、例子,基本是使用了“空间中断”+“DMA传输完成中断”来接收数据。实质上这是存在风险的,当DMA传输数据完成,CPU介入开始拷贝DMA通道buf数据,如果此时串口继续有数据进来,DMA继续搬运数据到buf,就有可能将数据覆盖,因为DMA数据搬运是不受CPU控制的,即使你关闭了CPU中断。


  严谨的做法需要做双buf,CPU和DMA各自一块内存交替访问,即是"乒乓缓存” ,处理流程步骤应该是这样:


【1】第一步,DMA先将数据搬运到buf1,搬运完成通知CPU来拷贝buf1数据
【2】第二步,DMA将数据搬运到buf2,与CPU拷贝buf1数据不会冲突
【3】第三步,buf2数据搬运完成,通知CPU来拷贝buf2数据
【4】执行完第三步,DMA返回执行第一步,一直循环



4.png



双缓存DMA数据搬运过程

  STM32F0系列DMA不支持双缓存(以具体型号为准)机制,但提供了一个buf"半满中断",即是数据搬运到buf大小的一半时,可以产生一个中断信号。基于这个机制,我们可以实现双缓存功能,只需将buf空间开辟大一点即可。


【1】第一步,DMA将数据搬运完成buf的前一半时,产生“半满中断”,CPU来拷贝buf前半部分数据
【2】第二步,DMA继续将数据搬运到buf的后半部分,与CPU拷贝buf前半部数据不会冲突
【3】第三步,buf后半部分数据搬运完成,触发“溢满中断”,CPU来拷贝buf后半部分数据
【4】执行完第三步,DMA返回执行第一步,一直循环




5.png



使用半满中断DMA数据搬运过程


  UART2 DMA模式接收配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:

  • 串口接收,DMA通道工作模式设为连续模式
  • 使能DMA通道接收buf半满中断、溢满(传输完成)中断
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据




  1. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
  2. {
  3.           DMA_InitTypeDef DMA_InitStructure;
  4.         
  5.         DMA_DeInit(DMA1_Channel5);
  6.         DMA_Cmd(DMA1_Channel5, DISABLE);
  7.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);/* UART2接收数据地址 */
  8.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr; /* 接收buf */
  9.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
  10.         DMA_InitStructure.DMA_BufferSize                         = mem_size; /* 接收buf大小 */
  11.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
  12.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  13.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
  14.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
  15.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular; /* 连续模式 */
  16.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
  17.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
  18.         DMA_Init(DMA1_Channel5, &DMA_InitStructure);
  19.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、溢满、错误中断 */
  20.         DMA_ClearFlag(DMA1_IT_TC5);        /* 清除相关状态标识 */
  21.         DMA_ClearFlag(DMA1_IT_HT5);
  22.         DMA_Cmd(DMA1_Channel5, ENABLE);
  23. }
复制代码


DMA 错误中断“DMA_IT_TE”,一般用于前期调试使用,用于检查DMA出现错误的次数,发布软件可以不使能该中断。


5.3 接收处理

  基于上述描述机制,DMA方式接收串口数据,有三种中断场景需要CPU去将buf数据拷贝到fifo中,分别是:

  • DMA通道buf溢满(传输完成)场景
  • DMA通道buf半满场景
  • 串口空闲中断场景

  前两者场景,前面文章已经描述。串口空闲中断指的是,数据传输完成后,串口监测到一段时间内没有数据进来,则触发产生的中断信号。


5.3 .1 接收数据大小

  数据传输过程是随机的,数据大小也是不定的,存在几类情况:

  • 数据刚好是DMA接收buf的整数倍,这是理想的状态
  • 数据量小于DMA接收buf或者小于接收buf的一半,此时会触发串口空闲中断

   因此,我们需根据“DMA通道buf大小”、“DMA通道buf剩余空间大小”、“上一次接收的总数据大小”来计算当前接收的数据大小。

  1. /* 获取DMA通道接收buf剩余空间大小 */
  2. uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
复制代码

DMA通道buf溢满场景计算


  1. 接收数据大小 = DMA通道buf大小 - 上一次接收的总数据大小
复制代码


DMA通道buf溢满中断处理函数:

  1. void uart_dmarx_done_isr(uint8_t uart_id)
  2. {
  3.           uint16_t recv_size;
  4.        
  5.         recv_size = s_uart_dev[uart_id].dmarx_buf_size - s_uart_dev[uart_id].last_dmarx_size;

  6.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
  7.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);

  8.         s_uart_dev[uart_id].last_dmarx_size = 0;
  9. }
复制代码

DMA通道buf半满场景计算


  1. 接收数据大小 = DMA通道接收总数据大小 - 上一次接收的总数据大小
  2. DMA通道接收总数据大小 = DMA通道buf大小 - DMA通道buf剩余空间大小
复制代码

DMA通道buf半满中断处理函数:


  1. void uart_dmarx_half_done_isr(uint8_t uart_id)
  2. {
  3.           uint16_t recv_total_size;
  4.           uint16_t recv_size;
  5.        
  6.         if(uart_id == 0)
  7.         {
  8.                   recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart1_get_dmarx_buf_remain_size();
  9.         }
  10.         else if (uart_id == 1)
  11.         {
  12.                 recv_total_size = s_uart_dev[uart_id].dmarx_buf_size - bsp_uart2_get_dmarx_buf_remain_size();
  13.         }
  14.         recv_size = recv_total_size - s_uart_dev[uart_id].last_dmarx_size;
  15.        
  16.         fifo_write(&s_uart_dev[uart_id].rx_fifo,
  17.                                    (const uint8_t *)&(s_uart_dev[uart_id].dmarx_buf[s_uart_dev[uart_id].last_dmarx_size]), recv_size);
  18.         s_uart_dev[uart_id].last_dmarx_size = recv_total_size;/* 记录接收总数据大小 */
  19. }
复制代码


串口空闲中断场景计算


  串口空闲中断场景的接收数据计算与“DMA通道buf半满场景”计算方式是一样的。

串口空闲中断处理函数:

注:
串口空闲中断处理函数,除了将数据拷贝到串口接收fifo中,还可以增加特殊处理,如作为串口数据传输完成标识、不定长度数据处理等等。


5.3.2 接收数据偏移地址

  将有效数据拷贝到fifo中,除了需知道有效数据大小外,还需知道数据存储于DMA 接收buf的偏移地址。有效数据偏移地址只需记录上一次接收的总大小即,可,在DMA通道buf全满中断处理函数将该值清零,因为下一次数据将从buf的开头存储。


在DMA通道buf溢满中断处理函数中将数据偏移地址清零:


  1. void uart_dmarx_done_isr(uint8_t uart_id)
  2. {
  3.         /* todo */
  4.         s_uart_dev[uart_id].last_dmarx_size = 0;
  5. }
复制代码


5.4 应用读取串口数据方法

  经过前面的处理步骤,已将串口数据拷贝至接收fifo,应用程序任务只需从fifo获取数据进行处理。前提是,处理效率必须大于DAM接收搬运数据的效率,否则导致数据丢失或者被覆盖处理。


6 串口DMA发送


5.1 基本流程

6.png



串口发送流程图



5.2 相关配置

关键步骤


【1】初始化串口


【2】使能串口DMA发送模式


【3】配置DMA发送通道,这一步无需在初始化设置,有数据需要发送时才配置使能DMA发送通道


  UART2 DMA模式发送配置代码如下,与其他外设使用DMA的配置基本一致,留意关键配置:


  • 串口发送是,DMA通道工作模式设为单次模式(正常模式),每次需要发送数据时重新配置DMA
  • 使能DMA通道传输完成中断,利用该中断信息处理一些必要的任务,如清空发送状态、启动下一次传输
  • 启动DMA通道前清空相关状态标识,防止首次传输错乱数据




5.3 发送处理

  串口待发送数据存于发送fifo中,发送处理函数需要做的的任务就是循环查询发送fifo是否存在数据,如存在则将该数据拷贝到DMA发送buf中,然后启动DMA传输。前提是需要等待上一次DMA传输完毕,即是DMA接收到DMA传输完成中断信号"DMA_IT_TC"。

串口发送处理函数:


  1. void uart_poll_dma_tx(uint8_t uart_id)
  2. {
  3.           uint16_t size = 0;
  4.        
  5.         if (0x01 == s_uart_dev[uart_id].status)
  6.     {
  7.         return;
  8.     }
  9.         size = fifo_read(&s_uart_dev[uart_id].tx_fifo, s_uart_dev[uart_id].dmatx_buf,
  10.                                          s_uart_dev[uart_id].dmatx_buf_size);
  11.         if (size != 0)
  12.         {
  13.         s_UartTxRxCount[uart_id*2+0] += size;
  14.                   if (uart_id == 0)
  15.                 {
  16.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态 */
  17.                           bsp_uart1_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);
  18.                 }
  19.                 else if (uart_id == 1)
  20.                 {
  21.             s_uart_dev[uart_id].status = 0x01;        /* DMA发送状态,必须在使能DMA传输前置位,否则有可能DMA已经传输并进入中断 */
  22.                         bsp_uart2_dmatx_config(s_uart_dev[uart_id].dmatx_buf, size);
  23.                 }
  24.         }
  25. }
复制代码

注意发送状态标识,必须先置为“发送状态”,然后启动DMA 传输。如果步骤反过来,在传输数据量少时,DMA传输时间短,“DMA_IT_TC”中断可能比“发送状态标识置位”先执行,导致程序误判DMA一直处理发送状态(发送标识无法被清除)



注:
关于DMA发送数据启动函数,有些博客文章描述只需改变DMA发送buf的大小即可;经过测试发现,该方法在发送数据量较小时可行,数据量大后,导致发送失败,而且不会触发DMA发送完成中断。因此,可靠办法是:每次启动DMA发送,重新配置DMA通道所有参数。该步骤只是配置寄存器过程,实质上不会占用很多CPU执行时间。


DMA传输完成中断处理函数:

  1. void uart_dmatx_done_isr(uint8_t uart_id)
  2. {
  3.         s_uart_dev[uart_id].status = 0;        /* 清空DMA发送状态标识 */
  4. }
复制代码



  上述串口发送处理函数可以在几种情况调用:



  • 主线程任务调用,前提是线程不能被其他任务阻塞,否则导致fifo溢出

  1. void thread(void)
  2. {
  3.     uart_dmatx_done_isr(DEV_UART1);
  4.     uart_dmatx_done_isr(DEV_UART2);
  5. }
复制代码


  • 定时器中断中调用
  1. void TIMx_IRQHandler(void)
  2. {
  3.     uart_dmatx_done_isr(DEV_UART1);
  4.     uart_dmatx_done_isr(DEV_UART2);
  5. }
复制代码


  • DMA通道传输完成中断中调用

  1. void DMA1_Channel4_5_IRQHandler(void)
  2. {
  3.         if(DMA_GetITStatus(DMA1_IT_TC4))
  4.         {
  5.                 UartDmaSendDoneIsr(UART_2);
  6.                 DMA_ClearFlag(DMA1_FLAG_TC4);
  7.                 uart_dmatx_done_isr(DEV_UART2);
  8.         }
  9. }
复制代码

每次拷贝多少数据量到DMA发送buf:

  关于这个问题,与具体应用场景有关,遵循的原则就是:只要发送fifo的数据量大于等于DMA发送buf的大小,就应该填满DMA发送buf,然后启动DMA传输,这样才能充分发挥会DMA性能。因此,需兼顾每次DMA传输的效率和串口数据流实时性,参考着几类实现:

  • 周期查询发送fifo数据,启动DMA传输,充分利用DMA发送效率,但可能降低串口数据流实时性
  • 实时查询发送fifo数据,加上超时处理,理想的方法
  • 在DMA传输完成中断中处理,保证实时连续数据流


6 串口设备


6.1 数据结构
  1. /* 串口设备数据结构 */
  2. typedef struct
  3. {
  4.         uint8_t status;                        /* 发送状态 */
  5.         _fifo_t tx_fifo;                /* 发送fifo */
  6.         _fifo_t rx_fifo;                /* 接收fifo */
  7.         uint8_t *dmarx_buf;                /* dma接收缓存 */
  8.         uint16_t dmarx_buf_size;/* dma接收缓存大小*/
  9.         uint8_t *dmatx_buf;                /* dma发送缓存 */
  10.         uint16_t dmatx_buf_size;/* dma发送缓存大小 */
  11.         uint16_t last_dmarx_size;/* dma上一次接收数据大小 */
  12. }uart_device_t;
复制代码


6.2 对外接口


  1. /* 串口注册初始化函数 */
  2. void uart_device_init(uint8_t uart_id)
  3. {
  4.           if (uart_id == 1)
  5.         {
  6.                 /* 配置串口2收发fifo */
  7.                 fifo_register(&s_uart_dev[uart_id].tx_fifo, &s_uart2_tx_buf[0],
  8.                       sizeof(s_uart2_tx_buf), fifo_lock, fifo_unlock);
  9.                 fifo_register(&s_uart_dev[uart_id].rx_fifo, &s_uart2_rx_buf[0],
  10.                       sizeof(s_uart2_rx_buf), fifo_lock, fifo_unlock);
  11.                
  12.                 /* 配置串口2 DMA收发buf */
  13.                 s_uart_dev[uart_id].dmarx_buf = &s_uart2_dmarx_buf[0];
  14.                 s_uart_dev[uart_id].dmarx_buf_size = sizeof(s_uart2_dmarx_buf);
  15.                 s_uart_dev[uart_id].dmatx_buf = &s_uart2_dmatx_buf[0];
  16.                 s_uart_dev[uart_id].dmatx_buf_size = sizeof(s_uart2_dmatx_buf);
  17.                 bsp_uart2_dmarx_config(s_uart_dev[uart_id].dmarx_buf,
  18.                                                            sizeof(s_uart2_dmarx_buf));
  19.                 s_uart_dev[uart_id].status  = 0;
  20.         }
  21. }

  22. /* 串口发送函数 */
  23. uint16_t uart_write(uint8_t uart_id, const uint8_t *buf, uint16_t size)
  24. {
  25.         return fifo_write(&s_uart_dev[uart_id].tx_fifo, buf, size);
  26. }

  27. /* 串口读取函数 */
  28. uint16_t uart_read(uint8_t uart_id, uint8_t *buf, uint16_t size)
  29. {
  30.         return fifo_read(&s_uart_dev[uart_id].rx_fifo, buf, size);
  31. }
复制代码

8 完整源码


串口&DMA底层配置:


  1. #include <stddef.h>
  2. #include <stdint.h>
  3. #include <stdbool.h>
  4. #include "stm32f0xx.h"
  5. #include "bsp_uart.h"

  6. /**
  7. * @brief  
  8. * @param  
  9. * @retval
  10. */
  11. static void bsp_uart1_gpio_init(void)
  12. {
  13.     GPIO_InitTypeDef    GPIO_InitStructure;
  14. #if 0
  15.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
  16.        
  17.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_0);
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_0);
  19.        
  20.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_6 | GPIO_Pin_7;
  21.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
  22.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
  23.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;
  24.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
  25.     GPIO_Init(GPIOB, &GPIO_InitStructure);
  26. #else
  27.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  28.        
  29.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);
  30.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_1);
  31.        
  32.         GPIO_InitStructure.GPIO_Pin         = GPIO_Pin_9 | GPIO_Pin_10;
  33.     GPIO_InitStructure.GPIO_Mode         = GPIO_Mode_AF;
  34.         GPIO_InitStructure.GPIO_OType         = GPIO_OType_PP;
  35.     GPIO_InitStructure.GPIO_Speed          = GPIO_Speed_Level_3;
  36.     GPIO_InitStructure.GPIO_PuPd         = GPIO_PuPd_UP;
  37.     GPIO_Init(GPIOA, &GPIO_InitStructure);
  38. #endif
  39. }

  40. /**
  41. * @brief  
  42. * @param  
  43. * @retval
  44. */
  45. static void bsp_uart2_gpio_init(void)
  46. {
  47.         GPIO_InitTypeDef GPIO_InitStructure;
  48.        
  49.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
  50.        
  51.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_1);
  52.         GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_1);
  53.        
  54.         GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_2 | GPIO_Pin_3;
  55.         GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
  56.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  57.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  58.         GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  59.         GPIO_Init(GPIOA, &GPIO_InitStructure);
  60. }

  61. /**
  62. * @brief  
  63. * @param  
  64. * @retval
  65. */
  66. void bsp_uart1_init(void)
  67. {
  68.         USART_InitTypeDef USART_InitStructure;
  69.         NVIC_InitTypeDef NVIC_InitStructure;
  70.        
  71.         bsp_uart1_gpio_init();
  72.        
  73.         /* 使能串口和DMA时钟 */
  74.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  75.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  76.        
  77.         USART_InitStructure.USART_BaudRate            = 57600;
  78.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
  79.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
  80.         USART_InitStructure.USART_Parity              = USART_Parity_No;
  81.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  82.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
  83.         USART_Init(USART1, &USART_InitStructure);
  84.        
  85.         USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
  86.         USART_OverrunDetectionConfig(USART1, USART_OVRDetection_Disable);
  87.        
  88.         USART_Cmd(USART1, ENABLE);
  89.         USART_DMACmd(USART1, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE); /* 使能DMA收发 */

  90.         /* 串口中断 */
  91.         NVIC_InitStructure.NVIC_IRQChannel         = USART1_IRQn;
  92.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
  93.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
  94.         NVIC_Init(&NVIC_InitStructure);

  95.         /* DMA中断 */
  96.           NVIC_InitStructure.NVIC_IRQChannel                    = DMA1_Channel2_3_IRQn;      
  97.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  98.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
  99.           NVIC_Init(&NVIC_InitStructure);
  100. }

  101. /**
  102. * @brief  
  103. * @param  
  104. * @retval
  105. */
  106. void bsp_uart2_init(void)
  107. {
  108.         USART_InitTypeDef USART_InitStructure;
  109.         NVIC_InitTypeDef NVIC_InitStructure;
  110.        
  111.         bsp_uart2_gpio_init();
  112.        
  113.         /* 使能串口和DMA时钟 */
  114.         RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  115.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  116.         USART_InitStructure.USART_BaudRate            = 57600;
  117.         USART_InitStructure.USART_WordLength          = USART_WordLength_8b;
  118.         USART_InitStructure.USART_StopBits            = USART_StopBits_1;
  119.         USART_InitStructure.USART_Parity              = USART_Parity_No;
  120.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  121.         USART_InitStructure.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
  122.         USART_Init(USART2, &USART_InitStructure);
  123.        
  124.         USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);        /* 使能空闲中断 */
  125.         USART_OverrunDetectionConfig(USART2, USART_OVRDetection_Disable);
  126.        
  127.         USART_Cmd(USART2, ENABLE);
  128.         USART_DMACmd(USART2, USART_DMAReq_Rx|USART_DMAReq_Tx, ENABLE);         /* 使能DMA收发 */

  129.         /* 串口中断 */
  130.         NVIC_InitStructure.NVIC_IRQChannel         = USART2_IRQn;
  131.         NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
  132.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
  133.         NVIC_Init(&NVIC_InitStructure);

  134.         /* DMA中断 */
  135.         NVIC_InitStructure.NVIC_IRQChannel         = DMA1_Channel4_5_IRQn;      
  136.           NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  137.         NVIC_InitStructure.NVIC_IRQChannelCmd      = ENABLE;
  138.           NVIC_Init(&NVIC_InitStructure);
  139. }

  140. void bsp_uart1_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
  141. {
  142.           DMA_InitTypeDef DMA_InitStructure;
  143.        
  144.         DMA_DeInit(DMA1_Channel2);
  145.         DMA_Cmd(DMA1_Channel2, DISABLE);
  146.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->TDR);
  147.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
  148.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
  149.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
  150.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
  151.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  152.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
  153.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
  154.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
  155.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
  156.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
  157.         DMA_Init(DMA1_Channel2, &DMA_InitStructure);  
  158.         DMA_ITConfig(DMA1_Channel2, DMA_IT_TC|DMA_IT_TE, ENABLE);
  159.         DMA_ClearFlag(DMA1_IT_TC2);        /* 清除发送完成标识 */
  160.         DMA_Cmd(DMA1_Channel2, ENABLE);
  161. }

  162. void bsp_uart1_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
  163. {
  164.           DMA_InitTypeDef DMA_InitStructure;
  165.        
  166.         DMA_DeInit(DMA1_Channel3);
  167.         DMA_Cmd(DMA1_Channel3, DISABLE);
  168.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART1->RDR);
  169.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
  170.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
  171.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
  172.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
  173.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  174.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
  175.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
  176.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular;
  177.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
  178.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
  179.         DMA_Init(DMA1_Channel3, &DMA_InitStructure);
  180.         DMA_ITConfig(DMA1_Channel3, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
  181.         DMA_ClearFlag(DMA1_IT_TC3);
  182.         DMA_ClearFlag(DMA1_IT_HT3);
  183.         DMA_Cmd(DMA1_Channel3, ENABLE);
  184. }

  185. uint16_t bsp_uart1_get_dmarx_buf_remain_size(void)
  186. {
  187.         return DMA_GetCurrDataCounter(DMA1_Channel3);        /* 获取DMA接收buf剩余空间 */
  188. }

  189. void bsp_uart2_dmatx_config(uint8_t *mem_addr, uint32_t mem_size)
  190. {
  191.           DMA_InitTypeDef DMA_InitStructure;
  192.        
  193.         DMA_DeInit(DMA1_Channel4);
  194.         DMA_Cmd(DMA1_Channel4, DISABLE);
  195.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->TDR);
  196.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
  197.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralDST;         /* 传输方向:内存->外设 */
  198.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
  199.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
  200.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  201.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
  202.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
  203.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Normal;
  204.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_High;
  205.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
  206.         DMA_Init(DMA1_Channel4, &DMA_InitStructure);  
  207.         DMA_ITConfig(DMA1_Channel4, DMA_IT_TC|DMA_IT_TE, ENABLE);
  208.         DMA_ClearFlag(DMA1_IT_TC4);        /* 清除发送完成标识 */
  209.         DMA_Cmd(DMA1_Channel4, ENABLE);
  210. }

  211. void bsp_uart2_dmarx_config(uint8_t *mem_addr, uint32_t mem_size)
  212. {
  213.           DMA_InitTypeDef DMA_InitStructure;
  214.        
  215.         DMA_DeInit(DMA1_Channel5);
  216.         DMA_Cmd(DMA1_Channel5, DISABLE);
  217.         DMA_InitStructure.DMA_PeripheralBaseAddr         = (uint32_t)&(USART2->RDR);
  218.         DMA_InitStructure.DMA_MemoryBaseAddr                 = (uint32_t)mem_addr;
  219.         DMA_InitStructure.DMA_DIR                                         = DMA_DIR_PeripheralSRC;         /* 传输方向:外设->内存 */
  220.         DMA_InitStructure.DMA_BufferSize                         = mem_size;
  221.         DMA_InitStructure.DMA_PeripheralInc                 = DMA_PeripheralInc_Disable;
  222.         DMA_InitStructure.DMA_MemoryInc                         = DMA_MemoryInc_Enable;
  223.         DMA_InitStructure.DMA_PeripheralDataSize         = DMA_PeripheralDataSize_Byte;
  224.         DMA_InitStructure.DMA_MemoryDataSize                 = DMA_MemoryDataSize_Byte;
  225.         DMA_InitStructure.DMA_Mode                                         = DMA_Mode_Circular;
  226.         DMA_InitStructure.DMA_Priority                                 = DMA_Priority_VeryHigh;
  227.         DMA_InitStructure.DMA_M2M                                         = DMA_M2M_Disable;
  228.         DMA_Init(DMA1_Channel5, &DMA_InitStructure);
  229.         DMA_ITConfig(DMA1_Channel5, DMA_IT_TC|DMA_IT_HT|DMA_IT_TE, ENABLE);/* 使能DMA半满、全满、错误中断 */
  230.         DMA_ClearFlag(DMA1_IT_TC5);
  231.         DMA_ClearFlag(DMA1_IT_HT5);
  232.         DMA_Cmd(DMA1_Channel5, ENABLE);
  233. }

  234. uint16_t bsp_uart2_get_dmarx_buf_remain_size(void)
  235. {
  236.         return DMA_GetCurrDataCounter(DMA1_Channel5);        /* 获取DMA接收buf剩余空间 */
  237. }
复制代码


压力测试:

  • 1.5Mbps波特率,串口助手每毫秒发送1k字节数据,stm32f0 DMA接收数据,再通过DMA发送回串口助手,毫无压力。
  • 1.5Mbps波特率,可传输大文件测试,将接受数据保存为文件,与源文件比较。
  • 串口高波特率测试需要USB转TLL工具及串口助手都支持才可行,推荐CP2102、FT232芯片的USB转TTL工具。


7.png


1.5Mbps串口回环压力测试



收藏 3 评论0 发布时间:2020-9-15 15:49

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版