有关USART的DMA传输模式,其基本的概念和配置,网上有很多博客和教程都有,这里不再赘述,只是记录一下比较容易忽视而造成调试不通的问题。

1. 串口发送和接收分属两个DMA通道

一般方式操作串口时,读写数据都是只操作DR(数据寄存器),虽然它是由两个寄存器组成的,一个给发送用(TDR),一个给接收用(RDR),但是用户只能操作DR寄存。而DMA模式下,串口发送和接收分属两个DMA通道,需要单独配置。

分别配置的代码如下:

static void USART1_Tx_DMA_Config(void)
{DMA_InitTypeDef DMA_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;            // 配置DMA1_Channel4中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  NVIC_Init(&NVIC_InitStructure); DMA_DeInit(USART_TX_DMA_CHANNEL);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                  // 开启DMA时钟DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;        // 设置DMA源地址:串口数据寄存器地址DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)sendbuff;          // 内存地址(要传输的变量的指针)DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                  // 方向:从内存到外设DMA_InitStructure.DMA_BufferSize = CMD_NUM;                         // 传输大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    // 外设地址不增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;             // 内存地址自增   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单位   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     // 内存数据单位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                       // DMA一次模式  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;               // 优先级:中 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                        // 禁止内存到内存的传输DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);                 // 配置DMA通道DMA1_Channel4DMA_ITConfig(USART_TX_DMA_CHANNEL,DMA_IT_TC,ENABLE); DMA_Cmd (USART_TX_DMA_CHANNEL,DISABLE);                              // 关闭DMA
}static void USART1_Rx_DMA_Config(void)
{DMA_InitTypeDef DMA_InitStructure;//注意,接收没使用接收DMA中断
//    NVIC_InitTypeDef NVIC_InitStructure;
//
//    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
//    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 4;
//    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//    NVIC_Init(&NVIC_InitStructure); DMA_DeInit(USART_RX_DMA_CHANNEL);RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                  // 开启DMA时钟DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;        // 设置DMA源地址:串口数据寄存器地址*/DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_cmd;            // 内存地址(要传输的变量的指针)DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                  // 方向:外设到内存DMA_InitStructure.DMA_BufferSize = CMD_NUM;                         // 传输大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;    // 外设地址不增DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;             // 内存地址自增   DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单位   DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;     // 内存数据单位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                       // DMA一次模式  DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;               // 优先级:中 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                        // 禁止内存到内存的传输DMA_Init(USART_RX_DMA_CHANNEL, &DMA_InitStructure);                 // 配置DMA通道DMA1_Channel5//    DMA_ITConfig(USART_RX_DMA_CHANNEL,DMA_IT_TC,ENABLE); DMA_Cmd (USART_RX_DMA_CHANNEL,ENABLE);                              // 使能DMA
}

注意:在串口的基本配置当中要打开DMA传输模式,函数如下:

    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);                    // 开启串口发送DMAUSART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);                    // 开启串口接收DMA  

2. 间隔单次传输

将DMA传输模式设置为Normal(一次传输),传输完成需要再次传输时,需要再次向DMA通道的传输数量寄存器(CNDTR)写入要传输的字节数。但是,在写入前,需要关闭DMA,写完CNDTR后再打开。

2.1 串口DMA发送

我的设计方法是在初始化的时候,默认先关闭发送DMA,在需要串口发送数据时,先配置CNDTR,再打开DMA,发送完成后进入中断函数,再关闭DMA。

void DMA1_Channel4_IRQHandler(void)
{  DMA_ClearFlag(DMA1_FLAG_TC4);  DMA_Cmd(USART_TX_DMA_CHANNEL,DISABLE);
} *********
//代码片段DMA_SetCurrDataCounter(DMA1_Channel4,(uint16_t)CMD_NUM);      // 关于DMA单次传输,这条非常重要DMA_Cmd (USART_TX_DMA_CHANNEL,ENABLE);

2.2 串口DMA接收

设计方法是:不启用DMA接收通道中断,而使用串口传输中断,在串口中断函数中对DMA处理。注意,一般串口中断我们采用的是接收中断USART_IT_RXNE,接收一次即中断一次。在DMA模式下要使用空闲中断USART_IT_IDLE,空闲中断是在检测到接收数据后,在数据总线上的一个字节时间内,如果没有接收到新的数据,则触发空闲中断,它是在串口的RXNE位被置位之后才开始检测。简单理解是,连续的一串数据发送完成之后,才触发空闲中断。

串口的CR1寄存器的IDLE位被硬件置1,必须采用软件将IDLE位清零才能避免反复进入空闲中断。具体的做法是先读取状态寄存器USART_SR,再读取数据寄存器USART_DR,完成后自动清除。需要注意的是,不能采用库函数USART_ClearFlag()或者USART_ClearItPending()来清除IDEL标志,因为这两个函数接收的中断标志位仅包括:

  • USART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5).

  • USART_FLAG_LBD: LIN Break detection flag.

  • USART_FLAG_TC: Transmission Complete flag.

  • USART_FLAG_RXNE: Receive data register not empty flag.

同理,关闭DMA后,重置传输字节数,再开启DMA(因为串口一直要监测接收数据)。串口中断函数基础代码如下:

void USART1_IRQHandler(void)
{uint32_t temp = 0;if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET) {
//      temp = USART_GetITStatus(USART1,USART_IT_IDLE);       // 在判断时已经读取过一次temp = USART_ReceiveData(USART1);                     // 必须添加这条语句DMA_Cmd(USART_RX_DMA_CHANNEL,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel5,(uint16_t)CMD_NUM);DMA_Cmd(USART_RX_DMA_CHANNEL,ENABLE);}
}

3. 疑问

实际上这里面还有一些隐含方式方法,感兴趣的可以尝试一下,欢迎分享。

  • 现在采用的是串口中断来处理接收问题,是否可以采用DMA接收中断来处理数据接收?就如同DMA发送中断来处理发送数据一样。

4. 参考文献

  1. 《STM32F10X参考手册》
  2. 《32位基于ARM微控制器STM32F101xx与STM32F103xx 固件函数库》
  3. STM32的串口空闲中断
  4. STM32的串口采用DMA方式接收数据测试
  5. STM32使用串口IDLE中断的两种接收不定长数据的方式

转载于:https://www.cnblogs.com/rouwawa/p/7911172.html

STM32基础分析——USART的DMA模式相关推荐

  1. STM32 H7系列ADC DMA模式过采样设置详解 cubeMX

    最近做项目需要用H743的ADC采样,发现和M3及M4的差别还是蛮多的,MPU的配置对DMA读取数据的影响.过采样机制等,其中H7的硬件过采样是个很实用的东西,硬汉大佬也进行了测试: STM32H7的 ...

  2. STM32 USART串口DMA 接收和发送的源码详解!

    硬件平台:STM32F103ZET6: 开发环境:KEIL 4: 先说说应用通讯模式,串口终端的工作方式和迪文屏差不多,终端被动接受MCU发的指令,终端会偶尔主动发送一些数据给MCU(像迪文屏的触摸信 ...

  3. STM32从零到一,从标准库移植到HAL库,UART串口1以DMA模式收发不定长数据代码详解+常见问题 一文解析

    前言 本文的参考资料 感谢提供标准库版本的CSDN同学:这两篇文章至少是我看过的最详细的标准库配置DMA版本.而且代码实测稳定能用. STM32 | DMA配置和使用如此简单(超详细)_...| .. ...

  4. STM32理论 —— DAC、DMA

    文章目录 1. DAC 1.1 内置DAC 1.2 MCP4725 1.3 可编程信号发生器 - AD9833 1.3.1 控制寄存器 1.3.2 频率和相位寄存器 1.3.3 输出引脚 1.3.4 ...

  5. 嵌入式学习笔记——STM32的USART通信概述

    文章目录 前言 常用通信协议分类及其特征介绍 通信协议 通信协议分类 1.同步异步通信 2.全双工/半双工/单工 3.现场总线/板级总线 4. 串行/并行通信 5. 有线通信.无线通信 STM32通信 ...

  6. STM32的USART中RTS、CTS的作用和意义

    USART中RX和TX这两个引脚的功能,这两个引脚是USART串行通信最常见和必不可少的两个引脚.但我们在手册中会发现关于USART的其他引脚:USART_CK.USART_RTS.USART_CTS ...

  7. STM32的USART串口通讯程序

    文章目录 一.STM32 USART串口通信 1.串口通信简介 1.1.物理层 1.2.协议层 2.STM32 的 USART 简介 二.STM32的USART串口通讯程序 1.创建项目 2.添加.s ...

  8. stm32之USART/UART应用实例(详细)

    硬件:STM32F103VCT6 开发工具:Keil uVision4 下载调试工具:ARM仿真器 USART与UART的区别: usart:全双工通用同步/异步串行收发器 uart:全双工通用异步串 ...

  9. STM32的USART串口通讯程序(查询方式)

    STM32的USART串口通讯程序(查询方式) 文章目录 STM32的USART串口通讯程序(查询方式) 一.USART介绍 1.异步通信: 2.同步通信: 二.CubeMX创建项目 1.点击ACCE ...

  10. STM32 CubeMX配置ADC+DMA进行FFT(1)

    STM32 CubeMX配置ADC+DMA,计算采样率和ADC电压 原创:睿智嵌入式 最近,因为项目需要处理音频信号,对AD采集的音频信号进行FFT运算,记录一下学习过程. 主要内容: 1.ADC和D ...

最新文章

  1. libseccomp2 (>= 2.3.0) but 2.2.3-3ubuntu3 is to be installed
  2. WCF 4.0 进阶系列 – 第十二章 实现单向操作和异步操作(下)
  3. 技巧/诀窍:在ASP.NET中重写URL(转)
  4. HDOJ 3732 Ahui Writes Word 多重背包
  5. 区块链BaaS云服务(28)TOP Network 之全分片主链(Layer-1)
  6. linux怎么安装vim?
  7. Attention 扫盲:注意力机制及其 PyTorch 应用实现
  8. Java Mybatis Xml写法 查询语句的动态设计总结
  9. Spark之functions
  10. iptables基本用法和linux网络相关
  11. emerald sword(打倒大魔王)
  12. 供应链金融与区块链01——论文阅读
  13. 计算机美术基础课程标准,《计算机美术基础》教学大纲 课程标准 最全最新.doc...
  14. CSS3 - 新单位vw、vh、vmin、vmax使用详解(附样例)
  15. 手机虚拟键盘的获取按键的code值
  16. 台式机插上耳机,声音仍然外放问题解决
  17. 亲民地理35期-雨中登泰山
  18. echarts上加横线标线_Echarts地图添加引导线效果(labelLine)
  19. TCP报文( tcp dup ack 、TCP Retransmission)
  20. 利用matplotlib实现TMDB电影数据可视化

热门文章

  1. Github博客私人订制(一)
  2. 虚拟化VMware之虚拟机备份(2)
  3. SQLServer2005删除log文件和清空日志的方案
  4. GridView(网格视图)+MotionEvent(触控事件)实现可以拖动排序的网格图
  5. 自适应网页设计(Responsive Web Design)(转)
  6. 【转】我眼中的自动化测试框架设计要点
  7. Linux新硬盘的使用
  8. Oracle 11.2.0.2 Patch 说明
  9. win10设置Python程序定时运行(设置计划任务)
  10. sessionid如何产生?由谁产生?保存在哪里?