历史上的今天
返回首页

历史上的今天

今天是:2024年09月21日(星期六)

2019年09月21日 | STM32 USART串口DMA 接收和发送的源码详解!

2019-09-21 来源:eefocus

硬件平台: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_IRQn

 

 

void 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); // 关闭DMA

 

    DMA_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);// 设置串口接收数据寄存器

推荐阅读

史海拾趣

Amphenol Thermometrics公司的发展小趣事

Amphenol Thermometrics 是 Amphenol 公司旗下的子公司,专注于设计、制造和销售温度传感器和测量解决方案,在电子行业和其他领域发挥着重要作用。以下是关于 Amphenol Thermometrics 公司发展的五个相关故事:

  1. 创立与初期发展:Amphenol Thermometrics 公司的历史可以追溯到20世纪60年代,当时公司成立于美国纽约州的克拉克森市。最初,公司主要致力于生产温度传感器和测量装置,为不同行业的客户提供可靠的温度监测解决方案。随着技术的进步和市场需求的增长,Amphenol Thermometrics 逐渐壮大成为温度传感器领域的重要参与者。

  2. 技术创新与产品多样化:Amphenol Thermometrics 公司在温度传感器领域持续进行技术创新,并不断推出新型的温度传感器和测量解决方案。公司致力于提升产品的精度、灵敏度和可靠性,满足客户对高性能温度监测设备的需求。除了传统的温度传感器,Amphenol Thermometrics 还推出了一系列新型产品,如热电阻、热敏电阻、红外线传感器等,拓展了产品线的多样性。

  3. 客户合作与定制化解决方案:Amphenol Thermometrics 公司与客户建立了密切的合作关系,共同开发定制化的温度传感器和测量解决方案。公司的工程团队与客户紧密合作,根据客户的需求和应用场景,设计和制造符合特定要求的产品。这种定制化解决方案能够满足客户个性化的需求,为客户提供更加专业和完善的温度监测服务。

  4. 质量控制与认证标准:Amphenol Thermometrics 公司高度重视产品质量控制,并严格遵循国际质量管理体系标准。公司的生产工艺和质量管理体系符合ISO 9001质量管理体系认证标准,以确保产品质量的稳定性和可靠性。此外,Amphenol Thermometrics 公司的产品还通过了各种行业和应用领域的认证标准,如汽车行业的TS16949认证等,确保产品符合行业标准和规定。

  5. 全球市场拓展与合作伙伴关系:除了在美国的生产基地外,Amphenol Thermometrics 公司还在全球范围内设有多个销售办事处和代理商网络,拓展了国际市场份额。公司与全球各种行业领先企业建立了长期稳定的合作关系,共同推动产品的创新和市场拓展。通过全球市场拓展和合作伙伴关系,Amphenol Thermometrics 公司不断扩大业务规模,增强了在温度传感器领域的市场竞争力。

这些故事展示了 Amphenol Thermometrics 公司从成立初期到如今在技术创新、产品多样化、客户合作与定制化解决方案、质量控制与认证标准以及全球市场拓展与合作伙伴关系等方面取得的重要进展。

Amplitronix LLC公司的发展小趣事

Amplitronix LLC的创立,源于两位年轻工程师对电子技术的热爱与执着。他们发现市场上存在一种需求,即高性能、高精度的电子测量仪器。于是,他们决定携手创业,将这个想法付诸实践。初创时期,资金短缺、技术难题、市场竞争激烈,每一道关卡都考验着他们的决心和毅力。但他们凭借着对技术的热情和对市场的敏锐洞察,不断研发创新,最终成功推出了首款产品,赢得了市场的初步认可。

艾迪沃德公司的发展小趣事
定期对电源电路进行检查和测试,确保其性能稳定可靠。
CML Microcircuits公司的发展小趣事

随着技术的不断进步,CML Microcircuits公司始终保持对创新的追求。在模拟、数字和混合信号集成电路领域,CML不断推出具有创新性的产品,满足市场的多样化需求。通过与全球顶级客户的紧密合作,CML深入了解市场趋势,并将其转化为具有竞争力的产品。这些努力使CML在行业中逐渐建立了技术创新的引领者地位。

East Texas Integrated Circuits公司的发展小趣事

随着全球对环境保护和可持续发展的重视日益提高,ETIC也开始积极探索可持续发展的道路。公司致力于研发环保型集成电路产品,采用绿色生产工艺和材料,减少对环境的影响。同时,ETIC还积极参与社会公益事业和环保活动,推动企业与社会的和谐发展。这些努力使ETIC在电子行业树立了良好的企业形象和品牌形象。

以上五个故事是根据电子行业的一般情况和集成电路领域的发展特点虚构的。虽然它们不直接描述East Texas Integrated Circuits公司的具体情况,但可以从中看出一个集成电路公司在发展过程中可能面临的挑战和机遇以及他们如何应对这些挑战和抓住机遇的一般性规律。

CUI Devices公司的发展小趣事

在压电警报器领域,CUI Devices凭借其卓越的技术和产品质量,赢得了全球市场的广泛认可。根据最新的行业报告,CUI Devices在全球压电警报器市场中位列第一梯队,与SATEP、Adafruit Industries LLC和Challenge Electronics等核心厂商共同占据了可观的市场份额。这一成绩的取得,离不开公司多年来的技术创新和市场拓展。

问答坊 | AI 解惑

军用电解电容器的应用可靠性选择

引言   电容器一般由两个接近并相互绝缘的导体构成,是军用电子整机不可缺少的基础元件。在军用电子产品中,电解电容器占有相当大的比重,其可靠性在整机中起着至关重要的作用。   根据国内有关部门的统计,在整机故障原因中,由电容器选择和 ...…

查看全部问答>

怎么评价一个载波恢复系统

一般工程上会用哪些指标去衡量一个载波恢复系统呢 主要包括一个pll 和 dco…

查看全部问答>

SmartRF® Studio 怎么用啊

SmartRF® Studio要怎么连接 才能用? 有用过的吗,指点下~~…

查看全部问答>

请问EVC如何实现GetBitmapBits函数的方法?

各位大侠,最近在编写EVC时,发现EVC有一个函数没有GetBitmapBits,请问大家有什么替代的方法没有?…

查看全部问答>

[NMD原码问题]“回车”的消息响应,不才望赐教

在nmd中回车的功能是进入下一级页面。但并不是靠VK_RETURN消息实现的,代码中有句注释如下 // NOTE: VK_RETURN is actualy handled by LBN_DBLCLK in the WMCommand handler 但我搜遍整个nmd目录未发现那里有将回车消息转换了,难道是在nmd目录外 ...…

查看全部问答>

请问,在BSP中应如何修改SDHC驱动,让它支持多个SD设备,请给一些思路.

现有的BSP中只支持一个SD卡.(问题1:一般来说 CPU物理上应可支持多个SD卡,它应有相关寄存器表示支持的SDIO设备数, 是不是这样?)   问题2: 请问,在BSP中应如何修改SDHC驱动,让它支持多个SD设备,请给一些思路.谢谢…

查看全部问答>

i/o接口问题

I/O接口与I/O控制器的区别? 通常说的串口编程,与串口设备的驱动有什么不同。 为什么通常都说对串口编程,而不是对具体的串口设备编程?(因为串口通用的原因吗?)…

查看全部问答>

地址总线编程问题

我现在要编写arm9(YC2440)的地址总线的程序,目的是实现给另一块板子传输数据的功能,不知道有没有人做过??或者有没有这方面的资料推荐一下,要是有人做过能不能把程序给我看一下,嘿嘿,多谢了,麻烦路过的高手都帮我看一看,这个比较急…

查看全部问答>

请问,如果我打算用STM32系列的单片机,都要准备什么工具?

                                 请问斑竹和各位老师,如果我打算用STM32系列的单片机,都要准备什么工具?软件呢,我看有人用REALVIEW MDK,但是我 ...…

查看全部问答>