热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

STM32F407USART2串口使用DMA接收不定长数据和DMA中断发送

一、前言使用DMA通信的好处是,不占用单片机资源(不像普通串口中断,发送一个字节触发一次中断,发送100个字节触发100次中
一、前言    

        使用DMA通信的好处是,不占用单片机资源(不像普通串口中断,发送一个字节触发一次中断,发送100个字节触发100次中断;接收一个字节触发一次中断,接收200个字节触发200次中断),数据接收完毕触发一次DMA中断;发送数据完毕触发一次DMA中断。

        下图是STM32F407单片机DMA通道关系图。

 

 

#define USART2_DMA_RX_BUFFER_MAX_LENGTH (255)
#define USART2_DMA_TX_BUFFER_MAX_LENGTH (255)
uint8_t USART2_DMA_RX_Buffer[USART2_DMA_RX_BUFFER_MAX_LENGTH];
uint8_t USART2_DMA_TX_Buffer[USART2_DMA_TX_BUFFER_MAX_LENGTH];

1、USART2  TX DMA初始化程序

void USART2_DMA_Tx_Configuration(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE); //DMA1时钟使能DMA_DeInit(DMA1_Stream6);while (DMA_GetCmdStatus(DMA1_Stream6) != DISABLE); //等待DMA可配置DMA_InitStructure.DMA_Channel = DMA_Channel_4; //DMA通道配置DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART2_DMA_TX_Buffer; //发送缓存指针DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; //DMA传输方向:内存--->外设DMA_InitStructure.DMA_BufferSize = USART2_DMA_TX_BUFFER_MAX_LENGTH; //数据传输字节数量DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级 DMA_Priority_HighDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输DMA_Init(DMA1_Stream6, &DMA_InitStructure); //初始化DMA StreamDMA_Cmd(DMA1_Stream6, DISABLE); //开启DMA传输
}

2、USART2  RX DMA初始化程序

void USART2_DMA_Rx_Configuration(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1 , ENABLE); //DMA1时钟使能DMA_DeInit(DMA1_Stream5);while (DMA_GetCmdStatus(DMA1_Stream5) != DISABLE); //等待DMA可配置 DMA_InitStructure.DMA_Channel = DMA_Channel_4; //通道选择DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR; //DMA外设地址DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)USART2_DMA_RX_Buffer; //接收缓存指针DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory ; //DMA传输方向:外设到存储器模式:外设--->内存DMA_InitStructure.DMA_BufferSize = USART2_DMA_RX_BUFFER_MAX_LENGTH; //缓冲大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器增量模式DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; //使用普通模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //中等优先级 DMA_Priority_VeryHighDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输DMA_Init(DMA1_Stream5 , &DMA_InitStructure); //初始化DMA_Stream DMA_Cmd(DMA1_Stream5, ENABLE); //开启DMA传输
}

 3、USART2  启动DMA发送初始化程序


void USART2_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendCount)
{ if (nSendCount }

4、USART2  DMA方式端口初始化程序(包含DMA配置)


void USART2_Configuration(void)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2 , ENABLE); //for USART2, USART3, UART4 or UART5. RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;GPIO_Init(GPIOA, &GPIO_InitStructure);USART_InitStructure.USART_BaudRate = 115200;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;USART_Init(USART2, &USART_InitStructure);USART_Cmd(USART2, ENABLE);USART_ClearFlag(USART2, USART_FLAG_TC); //清除发送完成标志 while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); //等待空闲帧发送完成后再清零发送完成标志(警告:如果不使能USART_Mode_Tx,会导致单片机在这里死机)USART_ClearFlag(USART2, USART_FLAG_TC); //清除发送完成标志USART_ITConfig(USART2, USART_IT_RXNE, DISABLE); //禁止USART1接收不为空中断USART_ITConfig(USART2, USART_IT_TXE, DISABLE); //禁止USART1发送空中断USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); //开启USART1空闲中断 USART_ITConfig(USART2, USART_IT_TC, ENABLE); //开启USART1传输完成中断 USART_DMACmd(USART2 , USART_DMAReq_Tx,ENABLE); //使能串口的DMA发送USART_DMACmd(USART2 , USART_DMAReq_Rx,ENABLE); //使能串口的DMA接收}

5、USART2   DMA中断接收和DMA中断发送

void USART2_IRQHandler(void)
{int16_t ch;if (USART_GetITStatus(USART2,USART_IT_IDLE) != RESET){ USART_ClearITPendingBit(USART2 , USART_IT_IDLE); //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。ch = USART_ReceiveData(USART2); //必须先清除总线空闲中断标识,然后读一下数据寄存器,DMA接收才会正确(先读SR,然后读DR才能清除空闲中断标识)注意:这句必须要,否则不能够清除中断标志位。#ifdef __DEBUG_stm32f407____DEBUG_USART2_IT_IDLE++;#endifDMA_Cmd(DMA1_Stream5 , DISABLE); //关闭DMA,防止处理其间有数据DMA_ClearFlag(DMA1_Stream5 , DMA_FLAG_TCIF5 | DMA_FLAG_FEIF5 | DMA_FLAG_DMEIF5 | DMA_FLAG_TEIF5 | DMA_FLAG_HTIF5);//清零标志位ch = USART2_DMA_RX_BUFFER_MAX_LENGTH - DMA_GetCurrDataCounter(DMA1_Stream5);if (ch > 0){//if (MB_USART2.MBHosts_Query_isr == enum_MODBUS_STATUS_SEND_OVER){MB_USART2.Outtime_mark = TRUE;MB_USART2.receCount = ch;memcpy(MB_USART2.mscomm_buffer , USART2_DMA_RX_Buffer , MB_USART2.receCount);}}DMA_SetCurrDataCounter(DMA1_Stream5 , USART2_DMA_RX_BUFFER_MAX_LENGTH);DMA_Cmd(DMA1_Stream5, ENABLE);}else if (USART_GetITStatus(USART2,USART_IT_TC)!= RESET) {USART_ClearITPendingBit(USART2, USART_IT_TC);#ifdef __DEBUG_stm32f407____DEBUG_USART2_IT_TC++;#endifDMA_ClearFlag(DMA1_Stream6 , DMA_FLAG_TCIF6 | DMA_FLAG_FEIF6 | DMA_FLAG_DMEIF6 | DMA_FLAG_TEIF6 | DMA_FLAG_HTIF6);DMA_SetCurrDataCounter(DMA1_Stream6 , 0); //清除数据长度if (MB_USART2.MBHosts_Query_isr == enum_MODBUS_STATUS_SEND_BEGINING){MB_USART2.MBHosts_Query_isr = enum_MODBUS_STATUS_SEND_OVER; TIM_Cmd(TIM1, DISABLE);TIM_SetCounter(TIM1, 0x00);TIM_Cmd(TIM1, ENABLE);} GPIO_USART2_RS485_RECIVE_enable();}}

6、主程序

void main(void)
{USART2_Configuration();USART2_DMA_Tx_Configuration();USART2_DMA_Rx_Configuration();while (1){//在合适的时候调用USART2_DMA_Begin_Send(uint8_t *send_buffer , uint16_t nSendBytes)// 通过DMA中断方式将数据发送出去}
}

本程序经过多款产品验证OK


推荐阅读
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 本文介绍了在go语言中利用(*interface{})(nil)传递参数类型的原理及应用。通过分析Martini框架中的injector类型的声明,解释了values映射表的作用以及parent Injector的含义。同时,讨论了该技术在实际开发中的应用场景。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
author-avatar
mobiledu2502890777
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有