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

发布者:学海飘香最新更新时间:2018-07-23 来源: eefocus关键字:STM32  USAR  串口  DMA  接收和发送 手机看文章 扫描二维码
随时随地手机看文章

硬件平台: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);// 设置串口接收数据寄存器

    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  USAR  串口  DMA  接收和发送 引用地址:STM32 USART 串口 DMA 接收和发送的源码详解

上一篇:以stm32的TIM2作为实例一步步配置成为定时器
下一篇:基于STM32的WAV音频格式播放器

推荐阅读最新更新时间:2024-03-16 16:09

STM32_EXIT中断
今天的软件工程下载地址(360云盘): https://yunpan.cn/cPhvyer3vIwXh 访问密码 57e1 STM32F10x的资料可以在我360云盘下载: https://yunpan.cn/crBUdUGdYKam2 访问密码 ca90 工程概要说明:定义一个按键(可自己定义),每按键一次,响应中断一次,在中断函数中LED提示灯变化一次,用户可更加实际情况在中断函数做出相应操作,这里只是一个模板。 关于“STM32F103 EXIT中断” 我把重要的几点在下面分别讲述,若不明白,请关注微信公众号“EmbeddDeveloper”查阅或留言。 一、RCC时钟配置 该函数位于在bsp.c
[单片机]
STM32_EXIT中断
记录2--s3c2440 DMA的操作
一. typedef struct tagDMA { volatile U32 DISRC; //0x0 DMA初始源寄存器 volatile U32 DISRCC; //0x4 DMA初始源控制寄存器 volatile U32 DIDST; //0x8 DMA初始目的寄存器 volatile U32 DIDSTC; //0xc DMA初始目的控制寄存器 volatile U32 DCON; //0x10 DMA控制寄存器 volatile U32 DSTAT; //0x14 DMA状态寄存器 volatile U32 DCSRC; //0x18 当前源
[单片机]
求一种STM32单片机交通灯控制系统设计方案
一、功能简介 本项目使用Proteus8.12仿真STM32单片机控制器,使用数码管、按键、交通信号灯模块等。 系统运行后,交通灯系统开始运行,数码管显示初始时间,默认南北绿灯5S,东西红灯8S,绿灯过后南北黄灯3秒;然后切换东西绿灯5S,南北红灯8S,绿灯过后南北黄灯3秒;如此循环。可使用K1键进入红绿灯时间设置,K2和K3进行加减调节,设定好后,K4键确定并继续运行。在运行过程中,可通过K2键禁止通行,此时东南西北方向红灯亮,K3键允许南北通行,此时南北绿灯亮,东西红灯亮。K4键允许东西通行,此时南北红灯亮,东西绿灯亮。此时可按下K1键返回交通灯自动控制模式运行。 主要功能如下: (1)东西南北红黄绿灯控制; (2)交
[单片机]
求一种<font color='red'>STM32</font>单片机交通灯控制系统设计方案
stm32的外设初始化步骤,以定时器为例。
我这个程序的目的在于让定时器开启更新中断,实现定时器1S中断一次然后在中断函数中LED电平翻转一次。 1.首先先开启这个外设的时钟:RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //开启定时器1 的时钟APB2 2.定义这个外设的初始化结构体:TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定时器模式初始化结构体 定义别名为TIM_TimeBaseInitStructure 3.把这个结构体内成员参数设置: TIM_TimeBaseInitStructure.TIM_Period = 999
[单片机]
如何使用CubeMx生成一个DFU工程
1 前言 DFU用来做IAP是很方便的,可以直接通过USB来对APP进行升级,因此,掌握DFU的制作还是挺有好处,特别是使用CubeMx工具可以快速制作,本文将基于STM3240G-EVL评估板来一步一步实现一个DFU的IAP工程。 2 制作CubeMx工程 新建一个STM32F407IGHx工程:Pinout: Peripherals: RCC- High Speed Clock(HSE):Crystal/Ceramic Resonator SYS- Debug:Serial Wire USB_OTG_FS- Mode:Device_Only MiddleWares USB_DEVICE- Class For FS IP:D
[单片机]
STM32 之 ISP下载
(1) 首先是调整启动方式: 下载时:Boot0设置为1,Boot1设置为0,即从系统存储器(System Memory)启动。 运行时:Boot0设置为0,Boot1设置为0,即从用户闪存存储器(User Falsh)启动。 (2)打开Flash Loader Demo ,选择串口,波特率,设置无效验 (3)自动探测到Falsh的大小 (4)自动检测到单片机型号 (5)这里选择要烧写的文件(我这里选择扩展名为.hex的文件) (6)选择HEX文件 (7)开始烧写并效验 注意: (1)下载时的启动方式和下载完运行的启动方式是不同的,第一步已经讲明白了。 (2)如果打开软件
[单片机]
<font color='red'>STM32</font> 之 ISP下载
基于C8051F020的通用串口适配器的设计
  1引言   串行通信的广泛应用可使各种传输设备有机地连成一体,能够安全可靠地进行数据交换和信息传递。但是由于各个设备传输信道上的信号不同,所应用的串口也各种各样,要完成众多设备相互之间的通信,就必然要涉及到各个设备之间的串口转换问题。   为了使计算机的RS-232接口与各种不同的串口进行通信,从而测试或控制不同设备的工作状态,本文设计了一种通用串口适配器,适配器有4个对外接口,其中,1口连接控制计算机,2、3、4口连接不同标准串口的通信设备,如图1所示。   2 硬件设计   为了实现对适配器的自动控制以及扩展其他接口,采用了C8051F020单片机。适配器选用了C8051F020微处理器、MAX4534电
[单片机]
基于C8051F020的通用<font color='red'>串口</font>适配器的设计
分析一个关于STM32 芯片异常复位的经典案例!
前言 本篇主要是介绍一种处理问题的思路,即当我们在做STM32应用开发过程中,遇到芯片异常复位,或者进入了异常处理时,如何通过集成开发环境,如IAR,KEIL等查看相应的ARM内核寄存器,定位出应用软件产生异常的地方! 问题描述 某STM32用户反馈,当使用STM32L4芯片的时候,程序运行一段时间后,会忽然复位。复位后程序继续运行,但是还会继续复位,原因不详! 问题分析 针对于此类问题,我们可以按照一个统一的思路去处理。分析本案例的大致步骤如下: 1、初步确定复位的原因,是硬件复位,如外部NRST被拉低,还是软件复位,包括软件直接调用复位,或者看门狗复位,还是低功耗模式如standby模式被唤醒时产生中断; 2、查看复位
[单片机]
分析一个关于<font color='red'>STM32</font> 芯片异常复位的经典案例!
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved