历史上的今天
返回首页

历史上的今天

今天是:2025年08月13日(星期三)

正在发生

2018年08月13日 | STM32F4串口DMA配置

2018-08-13 来源:eefocus

在使用串口时,一般采用查询发送,中断接收。但当要接收一串很长的数据时,每收到一个字节进入一次串口中断,有可能会导致中断占用时间过长。如果有一种方式,能够让串口收完一串数据,才进一次中断,那将是对写底层驱动的人来说,是极其好的一件事。经过查资料看手册,发现可以采用串口空闲中断和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  

}


推荐阅读

史海拾趣

Babcock Inc公司的发展小趣事

Babcock Inc公司的创立源于创始人对电子技术的深厚热爱和前瞻视野。在创业初期,公司便致力于电子技术的研发与创新,不断积累核心技术和专利。通过持续的技术投入和人才培养,Babcock逐渐在电子行业崭露头角,为后续的快速发展奠定了坚实的基础。

绿宝石(BERYL)公司的发展小趣事

随着国内市场的饱和,绿宝石公司开始积极拓展国际市场。公司加强了与国际知名企业的合作,共同开发新产品、新技术。同时,绿宝石公司还在海外设立了研发中心和生产基地,以便更好地服务全球客户。通过一系列的市场拓展和国际化战略,绿宝石公司的国际影响力不断提升。

HIT(日立)公司的发展小趣事
对于可能产生热量的元件(如可控硅),应采取适当的散热措施,防止元件过热损坏。
DILABS公司的发展小趣事

随着公司规模的扩大,DILABS开始意识到品质控制的重要性。他们引进了先进的生产设备和管理系统,确保每一款产品的质量都达到行业最高标准。同时,DILABS还加大了品牌宣传力度,通过参加国际展会、发布技术白皮书等方式,逐步建立起公司在电子行业中的品牌形象。

圣邦微电子(Fangtek)公司的发展小趣事

随着公司规模的扩大,DILABS开始意识到品质控制的重要性。他们引进了先进的生产设备和管理系统,确保每一款产品的质量都达到行业最高标准。同时,DILABS还加大了品牌宣传力度,通过参加国际展会、发布技术白皮书等方式,逐步建立起公司在电子行业中的品牌形象。

DETCO公司的发展小趣事

在电子产品行业日益关注环保和可持续发展的背景下,ElectronicsCorp采取了一系列积极措施。公司开始使用环保材料制造产品,并优化生产流程以减少能源消耗和废物排放。此外,ElectronicsCorp还推出了一系列回收计划,鼓励消费者将旧电子产品回收再利用。这些措施不仅提高了公司的环保形象,还增强了消费者对公司品牌的忠诚度。

问答坊 | AI 解惑

捡了个开发板

在实验室里翻箱倒柜找到两块开发板 本人菜鸟不知道怎么用。一个是赛灵思的 写有XUP VIRTEX-II PRO DEVELOPMENT SYSTEM字样 可惜只有板子和电源 没有下载线也没显示模块 还有一个很全开没开包 写有PCI EXPRESS DEVELOPMENT KIT 是STRATIX   ...…

查看全部问答>

lpc1114 ucos

求助LPC1114的UCOS例程?或者提供UC/OSii与ARM处理器相关的具体函数的例程?感激………

查看全部问答>

液晶屏相关的一些问题

测试仪器用液晶屏 1,液晶屏的尺寸如何给出的?是对角线的尺寸吗? 2,对角线4~5左右的单色液晶屏,要求视场角较大的,有哪些牌子可选择?价格? 3,单片机接液晶屏时,应带有驱动程序吧? 希望了解的朋友指点。…

查看全部问答>

有没有这样的网卡:自身能实现双网卡冗余容错备份,我QQ:270996889 EMAIL:SDTZ_ZZ@126.COM

有没有这样的网卡:自身能实现双网卡冗余容错备份,我QQ:270996889     EMAIL:SDTZ_ZZ@126.COM…

查看全部问答>

如果导出arm中的lib.a文件中的函数list

我有arm版的lib.a文件,想看里面到底有什么函数实现,请问如何导出,就像vc下的dumpbin的工具一样的导出函数list…

查看全部问答>

wince的基础知识?

各位大虾:安装vs2005后,wince5.0就安装到系统中了吗?在哪里可以找到啊?谢谢!…

查看全部问答>

【设计工具】xilinx sp605 PCIe EDK使用方法(之二:pci-e的windows 驱动)

$(\'swf_HNg\').innerHTML=AC_FL_RunContent(\'width\', \'550\', \'height\', \'400\', \'allowNetworking\', \'internal\', \'allowScriptAccess\', \'never\', \'src\', encodeURI(\'http://player.youku.com/player.php/sid/XMTkxODk3ODQ4/v.sw ...…

查看全部问答>

碉堡了!牛人自制真正的变形金刚机器人

这个我就不介绍了  直接看视频吧 激发你们的创意!!! $(\'flv_s3f\').innerHTML=(AC_FL_RunContent(\'width\', \'500\', \'height\', \'375\', \'allowNetworking\', \'internal\', \'allowScriptAccess\', \'never\', \'src\', \'htt ...…

查看全部问答>

ADI常用集成电路功能一览表

ADI常用集成电路功能一览表   序号    型  号    产品描述    1    AD1380JD      16位 20us高性能 ...…

查看全部问答>

只为uC而生,uS成长历程5

昨晚我已经完成了 通过LED闪烁,测试uSer中的任务是否已经成功加入了定时器中断。 然而,最后我们也发现了,main函数的初始化显示出了 这个虽然很简单但已经显示出混乱的迹象。 这是个危险的苗头。 所以我们在继续开始以前,必须先理清条理。 ...…

查看全部问答>