STM32F4串口DMA配置

发布者:转眼人老最新更新时间:2018-08-13 来源: eefocus关键字:STM32F4  串口  DMA配置 手机看文章 扫描二维码
随时随地手机看文章

在使用串口时,一般采用查询发送,中断接收。但当要接收一串很长的数据时,每收到一个字节进入一次串口中断,有可能会导致中断占用时间过长。如果有一种方式,能够让串口收完一串数据,才进一次中断,那将是对写底层驱动的人来说,是极其好的一件事。经过查资料看手册,发现可以采用串口空闲中断和DMA接收来实现这个功能。具体更详细的说明后续补充,现只贴出代码,以供参考。 

调试的过程中发现几个问题: 

1、要串口初始化放在DMA初始化之前,否则会出现DMA发送和接收使用不了的问题; 

2、DMA接收配置中DMA模式要配置为循环模式,如果配置成为正常模式会导致只能接收到一次数据,问题未知; 

如果有某位大神知道这两个问题的原因,望不吝赐教。


画布多少,直接上代码:


//usart.h 头文件中要定义如下:


//该结构主要用来存放所有的串口相关的配置参数

typedef struct

{

    uint32_t baud_rate;

    uint16_t gpio_tx;

    uint16_t gpio_rx;

    uint8_t  gpio_tx_pin_source;

  uint8_t  gpio_rx_pin_source;  

    GPIO_TypeDef * usart_tx_port;

    GPIO_TypeDef * usart_rx_port;

    USART_TypeDef * usart_base;

    uint32_t  gpio_tx_rcc;

    uint32_t  gpio_rx_rcc;

    uint32_t apb_periph_rcc ;

    uint8_t  gpio_af;

    uint32_t dma_rx_rcc;

    DMA_Stream_TypeDef* dma_rx_stream;

    uint32_t dma_rx_channel;

    uint32_t dma_tx_rcc;

    DMA_Stream_TypeDef* dma_tx_stream;

    uint32_t dma_tx_channel;


    uint8_t tx_buff[48];

    uint8_t rx_buff[48];

    uint8_t len;

}UsartParameter_Typedef;


/******************************USART6***********************************/

#define USART6_GPIO_TX       GPIO_Pin_9

#define USART6_TX_PIN_SOURCE GPIO_PinSource9

#define USART6_GPIO_RX       GPIO_Pin_14

#define USART6_RX_PIN_SOURCE GPIO_PinSource14

#define USART6_TX_PORT       GPIOG

#define USART6_RX_PORT       GPIOG

#define USART6_GPIO_TX_RCC   RCC_AHB1Periph_GPIOG

#define USART6_GPIO_RX_RCC   RCC_AHB1Periph_GPIOG

#define USART6_APBPeriph_RCC RCC_APB2Periph_USART6

#define USART6_GPIO_AF       GPIO_AF_USART6



#define USART6_RX_DMA_RCC      RCC_AHB1Periph_DMA2

#define USART6_RX_DMA_STREAM   DMA2_Stream1

#define USART6_RX_DMA_CHANNEL  DMA_Channel_5


#define USART6_TX_DMA_RCC      RCC_AHB1Periph_DMA2

#define USART6_TX_DMA_STREAM   DMA2_Stream6

#define USART6_TX_DMA_CHANNEL  DMA_Channel_5

#define USART6_DMA_IT_TCIF     DMA_IT_TCIF6


#define USE_USART6_TX_DMA //使能发送DMA

#define USE_USART6_RX_DMA //使能接收DMA

 

//usart.c


//指定初始化结构

UsartParameter_Typedef Usart6_parameter=

{

    .baud_rate = USART6_BaudRate,

    .gpio_tx = USART6_GPIO_TX,

    .gpio_rx = USART6_GPIO_RX,

    .gpio_tx_pin_source = USART6_TX_PIN_SOURCE,

  .gpio_rx_pin_source = USART6_RX_PIN_SOURCE,   

    .usart_tx_port = USART6_TX_PORT,

    .usart_rx_port = USART6_RX_PORT,

    .usart_base = USART6,

    .gpio_tx_rcc = USART6_GPIO_TX_RCC,

    .gpio_rx_rcc = USART6_GPIO_RX_RCC,

    .apb_periph_rcc = USART6_APBPeriph_RCC ,

    .gpio_af = USART6_GPIO_AF,

    .dma_rx_rcc = USART6_RX_DMA_RCC,

    .dma_rx_stream =USART6_RX_DMA_STREAM ,

    .dma_rx_channel = USART6_RX_DMA_CHANNEL,

    .dma_tx_rcc = USART6_TX_DMA_RCC,

    .dma_tx_stream = USART6_TX_DMA_STREAM,

    .dma_tx_channel = USART6_TX_DMA_CHANNEL,    


};

//串口初始化模板函数

static void InitUsart(const UsartParameter_Typedef* usart)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(usart->apb_periph_rcc, ENABLE);

    RCC_AHB1PeriphClockCmd( usart->gpio_tx_rcc, ENABLE);

    RCC_AHB1PeriphClockCmd( usart->gpio_rx_rcc, ENABLE);


    USART_InitStructure.USART_BaudRate = usart->baud_rate;

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    USART_InitStructure.USART_StopBits = USART_StopBits_1;

    USART_InitStructure.USART_Parity = USART_Parity_No;

    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USART_Init(usart->usart_base, &USART_InitStructure);


//  USART_ITConfig(usart->usart_base, USART_IT_RXNE, ENABLE);


    USART_ITConfig(usart->usart_base,USART_IT_IDLE,ENABLE);

    USART_Cmd(usart->usart_base, ENABLE);


    /*初始化串口后再,配置Io口,防止串口初始化中会发一个无效的0x00或者0xfe*/

    GPIO_InitStructure.GPIO_Pin = usart->gpio_tx ;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(usart->usart_tx_port, &GPIO_InitStructure);

    GPIO_PinAFConfig(usart->usart_tx_port, usart->gpio_tx_pin_source, usart->gpio_af);


    GPIO_InitStructure.GPIO_Pin = usart->gpio_rx ;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(usart->usart_tx_port, &GPIO_InitStructure);

    GPIO_PinAFConfig(usart->usart_rx_port, usart->gpio_rx_pin_source, usart->gpio_af);


    USART_GetFlagStatus(usart->usart_base,USART_FLAG_TC);//清除TXE,防止第一个数据没有发送出来


}


void UsartDmaTxConfig(const UsartParameter_Typedef* usart)

{

  DMA_InitTypeDef DMA_InitStructure;

  /*开启DMA时钟*/

  RCC_AHB1PeriphClockCmd(usart->dma_tx_rcc, ENABLE);



//————————————————————————————————发送————————————————————//  


  /* 复位初始化DMA数据流 */

  DMA_DeInit(usart->dma_tx_stream);


  /* 确保DMA数据流复位完成 */

  while (DMA_GetCmdStatus(usart->dma_tx_stream) != DISABLE)  {

  }


  /*usart1 tx对应dma2,通道4,数据流7*/  

  DMA_InitStructure.DMA_Channel = usart->dma_tx_channel;  

  /*设置DMA源:串口数据寄存器地址*/

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&usart->usart_base->DR);     

  /*内存地址(要传输的变量的指针)*/

  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)usart->tx_buff;

  /*方向:从内存到外设*/     

  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;   

  /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/  

  DMA_InitStructure.DMA_BufferSize = MAX_DATA_LEN;

  /*外设地址不增*/        

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 

  /*内存地址自增*/

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   

  /*外设数据单位*/    

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

  /*内存数据单位 8bit*/

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;   

  /*DMA模式:普通模式*/

  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  

  /*优先级:中*/ 

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;      

  /*禁用FIFO*/

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        

  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  /*存储器突发传输 16个节拍*/

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    

  /*外设突发传输 1个节拍*/

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    

  /*配置DMA2的数据流6*/          

  DMA_Init(usart->dma_tx_stream, &DMA_InitStructure);

    /*配置发送完成中断*/

  DMA_ITConfig(usart->dma_tx_stream, DMA_IT_TC,ENABLE);  //此处必须开启中断,否则DMA_GetITStatus(DMA2_Stream6,DMA_IT_TCIF6)一直为0

  /*使能DMA*/

  DMA_Cmd(usart->dma_tx_stream, ENABLE);    

    USART_DMACmd(usart->usart_base,USART_DMAReq_Tx,ENABLE);    //使能DMA发送

}



//DMA TX RX配置模板函数

static void UsartDmaRxConfig(const UsartParameter_Typedef* usart)

{

    DMA_InitTypeDef DMA_InitStructure;

/*-----------------------接收------------------------------------*/

    RCC_AHB1PeriphClockCmd(usart->dma_rx_rcc, ENABLE);

//  RCC_AHB1PeriphResetCmd(usart->dma_rx_rcc, ENABLE);

  /* 复位初始化DMA数据流 */

  DMA_DeInit(usart->dma_rx_stream);


  /* 确保DMA数据流复位完成 */

  while (DMA_GetCmdStatus(usart->dma_rx_stream) != DISABLE)  {

  }


  /*usart6 rx对应dma2,通道5,数据流1*/  

  DMA_InitStructure.DMA_Channel = usart->dma_rx_channel;  

  /*设置DMA源:串口数据寄存器地址*/

  DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)(&usart->usart_base->DR)/*((uint32_t)usart->usart_base+0x04)usart->usart_base->DR*/;   

  /*内存地址(要传输的变量的指针)*/

  DMA_InitStructure.DMA_Memory0BaseAddr = (u32)usart->rx_buff;

  /*方向:从内存到外设*/     

  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;   

  /*传输大小DMA_BufferSize=SENDBUFF_SIZE*/  

  DMA_InitStructure.DMA_BufferSize = MAX_DATA_LEN;

  /*外设地址不增*/        

  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 

  /*内存地址自增*/

  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;   

  /*外设数据单位*/    

  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;

  /*内存数据单位 8bit*/

  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;   

  /*DMA模式:普通模式*/

  DMA_InitStructure.DMA_Mode = /*DMA_Mode_Normal*/DMA_Mode_Circular;     

  /*优先级:中*/ 

  DMA_InitStructure.DMA_Priority = DMA_Priority_High;      

  /*禁用FIFO*/

  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;        

  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;    

  /*存储器突发传输 16个节拍*/

  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;    

  /*外设突发传输 1个节拍*/

  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;    

  /*配置DMA2的数据流6*/          

  DMA_Init(usart->dma_rx_stream, &DMA_InitStructure);

      /*使能DMA*/

  DMA_Cmd(usart->dma_rx_stream, ENABLE);

  USART_DMACmd(usart->usart_base,USART_DMAReq_Rx,ENABLE);    //使能DMA接收

}



//串口初始化函数


void init_usart(void)

{

     InitUsart(&Usart6_parameter);  

#ifdef USE_USART6_TX_DMA

       UsartDmaTxConfig(&Usart6_parameter);

#endif              

#ifdef USE_USART6_RX_DMA

        UsartDmaRxConfig(&Usart6_parameter);

#endif

}



//发送函数

static void Usart6Send(const uint8_t *buf,const uint8_t len)

{


#ifdef USE_USART6_TX_DMA

    UsartDMASend(buf,len,&Usart6_parameter);

#else

    UsartSend(buf,len,&Usart6_parameter);

#endif  

}



//中断接收函数


void USART6_IRQHandler(void)

{

#ifdef USE_USART6_RX_DMA

     if(USART_GetITStatus(USART6, USART_IT_IDLE) != RESET)  

    {  

        Usart6_parameter.usart_base->SR;  

        Usart6_parameter.usart_base->DR; //清USART_IT_IDLE标志  

        //关闭DMA  

        DMA_Cmd(Usart6_parameter.dma_rx_stream,DISABLE);  

        //清除标志位  

        DMA_ClearFlag(Usart6_parameter.dma_rx_stream,DMA_FLAG_TCIF6);  

        //获得接收帧帧长  由于SnDTR寄存器用于指示要传输的剩余数据选项,每传输一次,递减1

        Usart6_parameter.len = MAX_DATA_LEN - DMA_GetCurrDataCounter(Usart6_parameter.dma_rx_stream);  

    //      Usart6.onrx(NULL,Usart6_parameter.rx_buff,Usart6_parameter.len);


        DMA_Cmd(Usart6_parameter.dma_rx_stream,ENABLE);  

            Usart6Send(Usart6_parameter.rx_buff,Usart6_parameter.len );

             memset(Usart6_parameter.rx_buff,0,sizeof(Usart6_parameter.rx_buff));   //清空接收缓冲区

            Usart6_parameter.len = 0; 

        //  Usart6DmaTx(TmpBuff,len);

    }   

#else

        uint8_t temp = 0;

    if(USART_GetFlagStatus(Usart6_parameter.usart_base,USART_FLAG_RXNE)==SET)

    {

        USART_ClearITPendingBit(Usart6_parameter.usart_base, USART_IT_RXNE);

        temp = USART_ReceiveData(Usart6_parameter.usart_base);

        Usart6Send(&temp,1);

    //  Usart6ReceivedProsess();

    }

#endif  

}


关键字:STM32F4  串口  DMA配置 引用地址:STM32F4串口DMA配置

上一篇:STM32F407的串口DMA收发数据
下一篇:STM32串口程序(寄存器版)

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

使用DMA可以较好地避免将采集到的数据丢失的方法
ADC采集到的数据都存储在一个固定的寄存器中。当常规采样方式采样多个通道时候,使用DMA可以较好地避免将采集到的数据丢失。当ADC的DMA功能被使能的时候,每个通道转换完毕时都会发出一个DMA请求。DMA方式也不能完全避免数据丢失问题,要实现数据不丢失需要在DMA的同时开启OVERRUN模式,当数据丢失时就停止数据转换。我们只需要检测是否有OVR时间发生,就能解决采样数据丢失造成的问题。比如,通道错位什么的。 在STM32F4的Reference manual中可以查到ADC1 的DMA映射在DMA1、CH0、Stream0上。 【实验1、DMA方式采集单一通道数据】 配置ADC1的DMA初始化设置如下: //DMA初始化
[单片机]
串口助手下发命令~单片机回传不同的数据
/**********串口助手下发命令,单片机回传不同的数据**************/ /** *时间:2014年3月15日 07:32:22 *作者:寒竹子 **/ #include reg52.h typedef unsigned int uint; typedef unsigned char uchar; bit cmdAddrived = 0;//收到下发命令的标志 uchar cmd = 0;//上位机下发的命令 uchar cnt;//发送字节的个数 uchar * sendStr = ;//单片机发送的数据指针 //预定义待发送的数据 uchar * pstr1 = Welcome to MCU! ;
[单片机]
STM32的串口(UART)及串口通信原理
一、通信接口介绍 1、处理器与外部设备通信的两种方式: 并行通信 - 传输原理:数据各个位同时传输。 -优点:速度快 -缺点:占用引脚资源多 串行通信 - 传输原理:数据按位顺序传输(一位一位传输)。 -优点:占用引脚资源少 -缺点:速度相对较慢 2、串行通信三种传送方式: 单工: 数据传输只支持数据在一个方向上传输 半双工: 允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切
[单片机]
STM32的<font color='red'>串口</font>(UART)及<font color='red'>串口</font>通信原理
STM32USART串口调节与printf重定义
首先,printf重定义后可以直接使用printf函数从串口发送数据 在usart.c中添加代码: #ifdef __GNUC__ /* With GCC/RAISONANCE, small printf (option LD Linker- Libraries- Small printf set to 'Yes') calls __io_putchar() */ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, F
[单片机]
STM32用串口(USB串口)下载程序的方法(只能用UASRT1)
一、 STM32串口下载程序步骤 1)安装《CH340驱动(USB串口驱动)_XP_WIN7共用》 2)打开串口下载软件《STM32F4串口下载软件(FLYMCU)》 3)开发板USB接口连接到电脑USB口,给开发板供电 4)下载软件设置如下;点搜索串口,选择正确的串口号,波特率不用管,选择HEX文件,点开始编程就可以下载了。 二、 硬件连接 USB转串口CH340接线图如下。 ARM串口TXD连接到CH340G的RXD脚,RXD连接到CH340G的TXD脚; CH340是5V供电,可以由USB供电。boot0置1(接到3.3V) ,boot1置0(接地)。 三、说明:对于已加密的STM32芯片可以通过串口下载程序来解锁下
[单片机]
STM32用<font color='red'>串口</font>(USB<font color='red'>串口</font>)下载程序的方法(只能用UASRT1)
串口通信的实验
串口操作相关库函数(省略入口参数): void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能 void USART_Cmd();//使能串口 void USART_ITConfig();//使能相关中断 void USART_SendData();//发送数据到串口,DR uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据 FlagStatus USART_GetFlagStatus();//获取状态标志位 void USART_ClearFlag();//清除状态标志位 ITStatus USART_GetITStatus();/
[单片机]
<font color='red'>串口</font>通信的实验
用ST16C2552实现DSP高速串行通讯扩展
1 引言 随着大规模集成电路工艺技术的迅速发展,DSP已经越来越广泛地应用于工业场合。工业现场由于许多场合通讯双方相距较远,为了保证通讯成本和可靠性,必须采用串行方式进行通讯。目前TI公司DSP都只有一个UART口,比如TMS320LF2407,当同时需要与上位机和下面的被控对象进行通讯,或者同时连接几个上位机时,则需要对DSP进行串行通讯口扩展,而使用可编程的ST16C2552可以一次扩展两组相互独立的串口,满足各种条件下串行通讯的要求。 2 ST16C2552介绍 ST16C2552是Exar公司生产的通用异步通讯扩展器件,并与飞利浦公司的SC16C2552相兼容,可扩展2路独立的串行通讯,可编程设定通讯起始位、停止位和
[工业控制]
STM32F030学习之串口收发程序
使用USART最简单的情况是只使用3根线:Tx用于 数据发送,Rx用于数据接收,GND是信号地线,提供通信双方的参考电平。 实现的功能: 1、通过串口发送数据; 2、中断方式接收数据,并将接收到的数据回送。 首先,第一步:配置引脚,将串口映射到PA9(Tx),PA10(Rx)。 void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; /* Connect GPIOs to USART1 RX&TX */ GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_1); //Tx GPIO_Pin
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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