硬件平台:STM32F103ZET6;

开发环境:KEIL 4;

先说说应用通讯模式,串口终端的工作方式和迪文屏差不多,终端被动接受MCU发的指令,终端会偶尔主动发送一些数据给MCU(像迪文屏的触摸信息上传)。

串口DMA发送:

发送数据的流程:

前台程序中有数据要发送,则需要做如下几件事

1.      在数据发送缓冲区内放好要发送的数据,说明:此数据缓冲区的首地址必须要在DMA初始化的时候写入到DMA配置中去。

2.      将数据缓冲区内要发送的数据字节数赋值给发送DMA通道,(串口发送DMA和串口接收DAM不是同一个DMA通道)

3.      开启DMA,一旦开启,则DMA开始发送数据,说明一下:在KEIL调试好的时候,DMA和调试是不同步的,即不管Keil 是什么状态,DMA总是发送数据。

4.      等待发送完成标志位,即下面的终端服务函数中的第3点设置的标志位。或者根据自己的实际情况来定,是否要一直等待这个标志位,也可以通过状态机的方式来循环查询也可以。或者其他方式。

判断数据发送完成:

启动DMA并发送完后,产生DMA发送完成中断,在中断函数中做如下几件事:

1. 清DMA发送完成中断标志位

2. 关闭串口发送DMA通道

3. 给前台程序设置一个软件标志位,说明数据已经发送完毕

串口DMA接收:

接收数据的流程:

串口接收DMA在初始化的时候就处于开启状态,一直等待数据的到来,在软件上无需做任何事情,只要在初始化配置的时候设置好配置就可以了。

判断数据数据接收完成:

这里判断接收完成是通过串口空闲中断的方式实现,即当串口数据流停止后,就会产生IDLE中断。这个中断里面做如下几件事:

1.      关闭串口接收DMA通道,2点原因:1.防止后面又有数据接收到,产生干扰。2.便于DMA的重新配置赋值,下面第4点。

2.      清除DMA 所有标志位

3.      从DMA寄存器中获取接收到的数据字节数

4.      重新设置DMA下次要接收的数据字节数,注意,这里是给DMA寄存器重新设置接收的计数值,这个数量只能大于或者等于可能接收的字节数,否则当DMA接收计数器递减到0的时候,又会重载这个计数值,重新循环递减计数,所以接收缓冲区的数据则会被覆盖丢失。

5.  开启DMA通道,等待下一次的数据接收,注意,对DMA的相关寄存器配置写入,如第4条的写入计数值,必须要在关闭DMA的条件进行,否则操作无效。

说明一下,STM32的IDLE的中断在串口无数据接收的情况下,是不会一直产生的,产生的条件是这样的,当清除IDLE标志位后,必须有接收到第一个数据后,才开始触发,一断接收的数据断流,没有接收到数据,即产生IDLE中断。

USART 和 DMA 硬件初始化配置/*--- LumModule Usart Config ---------------------------------------*/#define LUMMOD_UART                      USART3#define LUMMOD_UART_GPIO                 GPIOC#define LUMMOD_UART_CLK                  RCC_APB1Periph_USART3#define LUMMOD_UART_GPIO_CLK        RCC_APB2Periph_GPIOC#define LUMMOD_UART_RxPin               GPIO_Pin_11#define LUMMOD_UART_TxPin               GPIO_Pin_10#define LUMMOD_UART_IRQn                USART3_IRQn#define LUMMOD_UART_DR_Base                  (USART3_BASE + 0x4)  //0x40013804#define LUMMOD_UART_Tx_DMA_Channel      DMA1_Channel2#define LUMMOD_UART_Tx_DMA_FLAG         DMA1_FLAG_GL2//DMA1_FLAG_TC2 | DMA1_FLAG_TE2 #define LUMMOD_UART_Tx_DMA_IRQ          DMA1_Channel2_IRQn#define LUMMOD_UART_Rx_DMA_Channel      DMA1_Channel3#define LUMMOD_UART_Rx_DMA_FLAG         DMA1_FLAG_GL3//DMA1_FLAG_TC3 | DMA1_FLAG_TE3 #define LUMMOD_UART_Rx_DMA_IRQ      DMA1_Channel3_IRQnvoid Uart_Init(void){NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;/* System Clocks Configuration *///= System Clocks Configuration ==============================///* Enable GPIO clock */RCC_APB2PeriphClockCmd(LUMMOD_UART_GPIO_CLK ,  ENABLE ); // 开启串口所在IO端口的时钟/* Enable USART Clock */RCC_APB1PeriphClockCmd(LUMMOD_UART_CLK, ENABLE); // 开始串口时钟//=NVIC_Configuration======================================///* Configure the NVIC Preemption Priority Bits */NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);/* Enable the DMA Interrupt */NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_Tx_DMA_IRQ;   // 发送DMA通道的中断配置NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;     // 优先级设置NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);/* Enable the USART Interrupt */NVIC_InitStructure.NVIC_IRQChannel = LUMMOD_UART_IRQn;     // 串口中断配置NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);//=GPIO_Configuration====================================================//GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);  // 我这里没有用默认IO口,所以进行了重新映射,这个可以根据自己的硬件情况配置选择/* Configure USART3 Rx as input floating */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 串口接收IO口的设置GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_RxPin;GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);/* Configure USART3 Tx as alternate function push-pull */GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   // 串口发送IO口的设置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // 这里设置成复用形式的推挽输出   GPIO_InitStructure.GPIO_Pin = LUMMOD_UART_TxPin;GPIO_Init(LUMMOD_UART_GPIO, &GPIO_InitStructure);DMA_Uart_Init();   // 串口 DMA 配置/* USART Format configuration ------------------------------------------------------*/USART_InitStructure.USART_WordLength = USART_WordLength_8b;    // 串口格式配置USART_InitStructure.USART_StopBits = USART_StopBits_1;USART_InitStructure.USART_Parity = USART_Parity_No;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;/* Configure USART3 */USART_InitStructure.USART_BaudRate = 115200;  //  波特率设置USART_Init(LUMMOD_UART, &USART_InitStructure);/* Enable USART3 Receive and Transmit interrupts */USART_ITConfig(LUMMOD_UART, USART_IT_IDLE, ENABLE);  // 开启 串口空闲IDEL 中断/* Enable the USART3 */USART_Cmd(LUMMOD_UART, ENABLE);  // 开启串口/* Enable USARTy DMA TX request */USART_DMACmd(LUMMOD_UART, USART_DMAReq_Tx, ENABLE);  // 开启串口DMA发送USART_DMACmd(LUMMOD_UART, USART_DMAReq_Rx, ENABLE); // 开启串口DMA接收}void DMA_Uart_Init(void){DMA_InitTypeDef DMA_InitStructure;/* DMA clock enable */RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 开启DMA1时钟//=DMA_Configuration===============================================///*--- LUMMOD_UART_Tx_DMA_Channel DMA Config ---*/DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE);                           // 关DMA通道DMA_DeInit(LUMMOD_UART_Tx_DMA_Channel);                                 // 恢复缺省值DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口发送数据寄存器DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Tx_Buf;         // 设置发送缓冲区首地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                      // 设置外设位目标,内存缓冲区 ->外设寄存器DMA_InitStructure.DMA_BufferSize = LUMMOD_TX_BSIZE;                     // 需要发送的字节数,这里其实可以设置为0,因为在实际要发送的时候,会重新设置次值DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式DMA_Init(LUMMOD_UART_Tx_DMA_Channel, &DMA_InitStructure);               // 写入配置DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG);                                 // 清除DMA所有标志DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE); // 关闭DMADMA_ITConfig(LUMMOD_UART_Tx_DMA_Channel, DMA_IT_TC, ENABLE);            // 开启发送DMA通道中断/*--- LUMMOD_UART_Rx_DMA_Channel DMA Config ---*/DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE);                           // 关DMA通道DMA_DeInit(LUMMOD_UART_Rx_DMA_Channel);                                 // 恢复缺省值DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&LUMMOD_UART->DR);// 设置串口接收数据寄存器DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)LumMod_Rx_Buf;         // 设置接收缓冲区首地址DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                     // 设置外设为数据源,外设寄存器 -> 内存缓冲区DMA_InitStructure.DMA_BufferSize = LUMMOD_RX_BSIZE;                     // 需要最大可能接收到的字节数DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;        // 外设地址不做增加调整,调整不调整是DMA自动实现的DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                 // 内存缓冲区地址增加调整DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据宽度8位,1个字节DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;         // 内存数据宽度8位,1个字节DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                           // 单次传输模式DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;                 // 优先级设置DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                            // 关闭内存到内存的DMA模式DMA_Init(LUMMOD_UART_Rx_DMA_Channel, &DMA_InitStructure);               // 写入配置DMA_ClearFlag(LUMMOD_UART_Rx_DMA_FLAG);                                 // 清除DMA所有标志DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, ENABLE);                            // 开启接收DMA通道,等待接收数据}void BSP_Init(void){Uart_Init();}//============================================================//DMA 发送应用源码void DMA1_Channel2_IRQHandler(void){if(DMA_GetITStatus(DMA1_FLAG_TC2)){LumMod_Uart_DAM_Tx_Over();}}void LumMod_Uart_DAM_Tx_Over(void){DMA_ClearFlag(LUMMOD_UART_Tx_DMA_FLAG);         // 清除标志DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, DISABLE);   // 关闭DMA通道OSMboxPost(mbLumModule_Tx, (void*)1);           // 设置标志位,这里我用的是UCOSII ,可以根据自己的需求进行修改}void LumMod_Cmd_WriteParam( uint8 sample_num, uint8 *psz_param ){uint8 err;uint8 LumMod_Tx_Index ;LumMod_Tx_Index = 0;LumMod_Tx_Buf[LumMod_Tx_Index++] = 1;LumMod_Tx_Buf[LumMod_Tx_Index++] = 2;LumMod_Tx_Buf[LumMod_Tx_Index++] = 3;LumMod_Tx_Buf[LumMod_Tx_Index++] = 4;LumMod_Tx_Buf[LumMod_Tx_Index++] = 5;LumMod_Tx_Buf[LumMod_Tx_Index++] = 6;LumMod_Tx_Buf[LumMod_Tx_Index++] = 7;LumMod_Tx_Buf[LumMod_Tx_Index++] = 8;LumMod_Uart_Start_DMA_Tx( LumMod_Tx_Index );OSMboxPend(mbLumModule_Tx, 0, &err);}void LumMod_Uart_Start_DMA_Tx(uint16_t size)
{LUMMOD_UART_Tx_DMA_Channel->CNDTR = (uint16_t)size; // 设置要发送的字节数目DMA_Cmd(LUMMOD_UART_Tx_DMA_Channel, ENABLE);        //开始DMA发送}//============================================================//DMA 接收应用源码void USART3_IRQHandler(void){if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET)  // 空闲中断{LumMod_Uart_DMA_Rx_Data();USART_ReceiveData( USART3 ); // Clear IDLE interrupt flag bit}}void LumMod_Uart_DMA_Rx_Data(void){DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, DISABLE);       // 关闭DMA ,防止干扰DMA_ClearFlag( LUMMOD_UART_Rx_DMA_FLAG );           // 清DMA标志位LumMod_Rx_Data.index = LUMMOD_RX_BSIZE - DMA_GetCurrDataCounter(LUMMOD_UART_Rx_DMA_Channel); //获得接收到的字节数LUMMOD_UART_Rx_DMA_Channel->CNDTR = LUMMOD_RX_BSIZE;    //  重新赋值计数值,必须大于等于最大可能接收到的数据帧数目DMA_Cmd(LUMMOD_UART_Rx_DMA_Channel, ENABLE);        /* DMA 开启,等待数据。注意,如果中断发送数据帧的速率很快,MCU来不及处理此次接收到的数据,中断又发来数据的话,这里不能开启,否则数据会被覆盖。有2种方式解决。}

1. 在重新开启接收DMA通道之前,将LumMod_Rx_Buf缓冲区里面的数据复制到另外一个数组中,然后再开启DMA,然后马上处理复制出来的数据。

2. 建立双缓冲,在LumMod_Uart_DMA_Rx_Data函数中,重新配置DMA_MemoryBaseAddr 的缓冲区地址,那么下次接收到的数据就会保存到新的缓冲区中,不至于被覆盖。*/

OSMboxPost(mbLumModule_Rx,  LumMod_Rx_Buf); // 发送接收到新数据标志,供前台程序查询

STM32 USART串口DMA 接收和发送的源码详解!相关推荐

  1. linux 串口 dma,STM32 USART串口DMA 接收和发送流程详解

    1.dma发送流程 1.配置DMA发送中断 - NVIC_Init 2.配置串口中断 - NVIC_Init 3.GPIO配置 - GPIO_Init 4.DMA发送配置 -DMA_Init  DMA ...

  2. STM32 PAJ7620U2手势识别模块(IIC通信)程序源码详解

    最近在自学设计下基于STM32单片机的项目,想用手势识别模块做一点好玩的,做个手势控制家居设备开关,另外正好借此巩固I²C 通信.因此,我想借这个机会在这里和大家分享一下自己学习STM32单片机时的所 ...

  3. STM32采用串口DMA方式实现发送数据

    文章目录 一.实验工具 二.DMA介绍 三.配置工程 1.新建工程 2.引脚配置 3.设置RCC 4.设置串口 5.DMA设置 6.时钟源设置 7.项目文件设置 8.创建代码 四.Keil中修改代码 ...

  4. STM32 IAP 串口在线升级源码详解

    本博文基于本人修改的代码进行编写如需代码, 请到[代码下载](https://download.csdn.net/download/qq_36257571/10695681)自行下载. 详细内容不做过 ...

  5. android 串口一直打开_STM32之串口DMA接收不定长数据

    STM32之串口DMA接收不定长数据 引言 在使用stm32或者其他单片机的时候,会经常使用到串口通讯,那么如何有效地接收数据呢?假如这段数据是不定长的有如何高效接收呢? 同学A:数据来了就会进入串口 ...

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

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

  7. STM32—USART串口发送+接收

    STM32-USART串口发送+接收 本文来自于<STM32--江科大>的笔记整理. 文章目录 STM32-USART串口发送+接收 10.3 串口发送 串口调试助手 10.3.1 数据模 ...

  8. STM32 串口DMA接收 Openmv / K210 整数、小数字符串数据 (基于HAL库)

    目录 前言 一.工程配置 二.串口DMA部分代码 1.源文件UART_DMA.c 2.头文件UART_DMA.h 3.stm32f1xx_it.c的修改 4.串口收发DMA测试 三.字符串数字提取代码 ...

  9. 嵌入式作业STM32采用串口DMA方式发送数据

    目录 前言 要求 一.DMA的基本介绍 DMA的基本定义 DMA的主要特征 STM32F411x系列芯片DMA控制器 二.通过CubeMX配置项目 1.创建项目 2.选择芯片STM32F103C8T6 ...

最新文章

  1. Mobileye独创性创新
  2. Google获得MapReduce专利
  3. java中的reader_java中BufferedReader 有什么用
  4. lighttpd安装配置支持php
  5. 中国最为著名的古塔,你认识几座?
  6. Cacti 使用安装详解-企业级实例
  7. java 集合迭代器_Java中的集合迭代器
  8. 如何用python的i2c教程_Micropython TPYBoard I2C的用法
  9. 一般单元测试不可以并行进行_铝型材配件可以不可以进行二次加工
  10. node.js 框架基本功能
  11. oracle 9 10 11 比较好,atitit.Oracle 九 10 11 12新特性attilax总结
  12. android ndk opencv 3,opencv_and_opencv_contrib
  13. 【数据库】SQL建表
  14. Chipmump文档
  15. 【工具】“Error while running C:\ti\xdctools_3_31_01_33_core/xs --xdcpath=C:\ti\tir……”
  16. 强智教务系统验证码识别 java
  17. 实验吧 WEB 猫抓老鼠
  18. 龙ol一键端服务器维护,龙OL一键端纯一键点两下OK问题及解答汇总贴.doc
  19. 宜信区块链|一篇干货文读懂宜信的区块链实践
  20. 正交、独立、不相关区别

热门文章

  1. 比特币这么火热,看看这篇比特币初学者指南
  2. PYTHON调用JENKINS的API来进行CI
  3. Binary Tree Postorder Traversal
  4. Junit实现接口类测试
  5. Canvas实用库Fabric.js使用手册
  6. 学界 | 数据并行化对神经网络训练有何影响?谷歌大脑进行了实证研究
  7. DLUX组件扩展下篇-实践
  8. Ubuntu文件管理点击没有反应
  9. Visual Studio 2010 Ultimate敏捷功能特性(上)
  10. scala面向对象之trait