STM32中USART的DMA 实现

发布者:科技梦行者最新更新时间:2016-10-04 来源: eefocus关键字:STM32  USART  DMA 手机看文章 扫描二维码
随时随地手机看文章
对于没玩过DMA 的朋友,这里简单说一下DMA,用自己的语言说吧,那就是,从某个位置
传输数据到某个位置,如果不用DMA,那要CPU参与操作,一个字节一个字节地搬,效率高
点的,就一个字一个字地搬.但当你用了DMA 后,那就是只需要设置:A.从哪里开始搬; B,
搬到哪里去;C以字节方式搬还是半字还是字;D:一共搬多少个.之后,启动DMA.CPU内部
就会开始搬数据了,整个搬数据的过程都不需要指令的参与,唯一要做的,就是检测什么时
候搬完.你可以扫描寄存器,也可以用中断.这里,我使用了中断.
具体设置功能看注释就可以明白了.注意一点就是,有一个设置:
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
这个是外设的地址不递增.也就是说,每次搬动,都是从源头,也就是USART1的DR寄存器
搬,但内存地址却是递增的:
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

这个历程实现了 接受 串口的数据 写到FLASH 之中工作,而DMA的作用在于将 串口收寄存器 USART1->DR 的 数据写到内存之中 比如某个数组之中 u8 USART1_DMA_Buf1[512]; 写满512个字节之后将进入DMA中断(通道5)在这里修改DMA 的内存写入入口
u8 USART1_DMA_Buf2[512]; ,同时标记 下次的入口Free_Buf_No=BUF_NO1; 与 Buf_Ok=TRUE; 证明已有数据准备完成。这时CUP将USART1_DMA_Buf1中的数据写入FLASH .

又抄了一点

这次使用的是双缓冲,也有人
叫乒乓缓冲.因为一般情况下,串口的数据DMA 传输进BUF1 的过程中,是不建议对
BUF1 进行操作的.但由于串口数据是不会等待的直传,所以你总不能等BUF1 满了,
才往FLASH 上写,因为这时候串口数据依旧是源源不断.于是,使用双缓冲就变的理
所当然了.当BUF1 满了的时候,就马上设置DMA的目标为BUF2,并且BUF1的数据
往25F080上灌.当串口DMA写满了BUF2的时候,再设置DMA的目标为BUF1,此时
再操作BUF2写进25F080.如此一直循环,就好像打乒乓球那样吧,所以就叫乒乓缓冲.
用这个方法的速度极限就是,你必须确保两点a.DMA 灌满了BUF1 的时候,会发生中
断,此时切换DMA 的目标缓冲为BUF2,而且切换的过程必须在新的串口数据溢出之
前完成.b.在DMA的BUF1满之前,另外一个有数据的BUF2必须能全部写进25F080,
其中包括了遇到新的扇区边界而要刷除扇区的操作时间!!
可以看出,BUF的增大,并不能够很大程度的提升速度极限.

STM32中USART的DMA 实现 - java - stm32学习日志
 

假设 USART 与 FLASH 的 底层驱动已经写好了。   点击查看。
/************DMA方式传输***************************/

#define SRC_USART1_DR    (&(USART1->DR))        //串口接收寄存器作为源头

//DMA目标缓冲,这里使用双缓冲
u8 USART1_DMA_Buf1[512];
u8 USART1_DMA_Buf2[512];
bool Buf_Ok;    //BUF是否已经可用
BUF_NO Free_Buf_No;        //空闲的BUF号  typedef enum{BUF_NO1=0,BUF_NO2=1}BUF_NO;


DMA_InitTypeDef DMA_InitStructure;

void USART_DMAToBuf1(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                             //开DMA时钟
    DMA_DeInit(DMA1_Channel5);                                                                               //将DMA的通道1寄存器重设为缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)SRC_USART1_DR;                //源头BUF 既是 (&(USART1->DR)) 
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;                //目标BUF 既是要写在哪个个数组之中
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                      //外设作源头//外设是作为数据传输的目的地还是来源
    DMA_InitStructure.DMA_BufferSize = 512;                                        //DMA缓存的大小  单位在下边设定
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;               //外设地址寄存器不递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                        //内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;        //外设字节为单位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;            //内存字节为单位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                                //工作在循环缓存模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;                            //4优先级之一的(高优先) VeryHigh/High/Medium/Low
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                //非内存到内存
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);                                    //根据DMA_InitStruct中指定的参数初始化DMA的通道1寄存器
    
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);                                //DMA5传输完成中断

STM32中USART的DMA 实现 - java - stm32学习日志
 
     USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);                               //使能USART1的接收DMA请求
   /*****************************************************************************************************************************************************/
    //初始化BUF标志
    Free_Buf_No=BUF_NO2; //因为 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;  
    Buf_Ok=FALSE;  //此时没有数据准备完成 当然FALSE
    DMA_Cmd(DMA1_Channel5, ENABLE);                                                //正式允许DMA
    
}

再来看看DMA中断:

//u16 DataCounter;
extern DMA_InitTypeDef DMA_InitStructure;
void DMA1_Channel5_IRQHandler(void)
{
    if(DMA_GetITStatus(DMA1_IT_TC5))  //通道5传输完成中断TC 还有传输 过半中断HT  错误中断TE  全局中断GL
     {
        //DataCounter = DMA_GetCurrDataCounter(DMA1_Channel5);//获取剩余长度,一般都为0,调试用
        DMA_ClearITPendingBit(DMA1_IT_GL5);    //清除全部中断标志
        
        //转换可操作BUF
        if(Free_Buf_No==BUF_NO1)
        {    
            DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf1;
            DMA_Init(DMA1_Channel5, &DMA_InitStructure);
            Free_Buf_No=BUF_NO2;
        }
        else
        {
            DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_DMA_Buf2;
            DMA_Init(DMA1_Channel5, &DMA_InitStructure);
            Free_Buf_No=BUF_NO1;
        }
        Buf_Ok=TRUE; //有准备好的数据了
    
     }
}

写FLASH的操作
  while(1)
    {
        if(Buf_Ok==TRUE)
        {
            LED1_ON;   //一个标记
            Buf_Ok=FALSE; //操作了准备好的数据
            if((addr%4096)==0)        //跨越一个扇区,则需要先刷除
            {
                SST25SectorErase(addr);    
                sector_count++;
            }
            if(Free_Buf_No==BUF_NO1)
                SST25Write(addr,USART1_DMA_Buf1,512);
            else
                SST25Write(addr,USART1_DMA_Buf2,512);
            addr+=512;
            Timer1=5000; //时间重置
            LED1_OFF;
        }
        
        //检测超时  开了 定时器
        if(Timer1==0) //五秒内没准备好的数据
        {
            //获取长度
            len=512-DMA_GetCurrDataCounter(DMA1_Channel5);
            //写入最后数据
            if(Free_Buf_No==BUF_NO1)
                SST25Write(addr,USART1_DMA_Buf2,len);
            else
                SST25Write(addr,USART1_DMA_Buf1,len);
            addr+=len;
            
            break;
        }
    }

STM32中USART的DMA 实现 - java - stm32学习日志
  
还是很简单的。
有一点比较困扰 就是 FlagStatus标志位  与 ITStatus中断标志位 的区别。 其实就 DMA 来说  DMA_IT值 与 DMA_FLAG值 是一样的
甚至2者值的获取 都是读 DMA ISR register 的值  清除也是设置 DMA_IFCR 寄存器来清除的所以貌似没有区别.........同理这个问题在别的中断也存在但我还不可保证 IT 与FLAG 的值总是相同的这个存在也许是为了兼容但一定有其意义务必不可混用即使有时用错也正确.....
关键字:STM32  USART  DMA 引用地址:STM32中USART的DMA 实现

上一篇:STM32 的GPIO使用
下一篇:SST25VF080B SPI接口FLASH STM32驱动

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

stm32用什么软件编程 stm32的32指什么 stm32单片机的基本组成
  stm32用什么软件编程   STM32可以使用多种软件进行编程,包括:   Keil MDK-ARM:Keil是一种非常流行的ARM Cortex-M微控制器编程软件。它包含了一个完整的开发环境,包括代码编辑器、编译器、调试器和仿真器等。Keil MDK-ARM支持C/C++语言编程,可用于快速开发STM32应用程序。   STM32CubeIDE:STM32CubeIDE是STMicroelectronics推出的一个免费的集成开发环境,支持STM32微控制器的开发。它包含了一个基于Eclipse的IDE、编译器、调试器和STM32软件库等。STM32CubeIDE支持C/C++语言编程,具有丰富的工具和功能,可以大大提高
[单片机]
stm32-学习经验总结 ———UCOSIII-软件定时器
一、软件定时器简介 UC/OS-III提供了软件定时器服务,定时器的本质就是一个递减的计数器,当计数器减到0的时候可以触发某种动作的执行,这个动作通过回调函数来实现,回调函数是用户自己定义的,可以是简单的打开一个LED灯,或者开启电机等。当定时器计时完成时,定义的回调函数就会被立即调用,应用程序可以有任意数量的定时器。 注意!一定不要在回调函数中使用阻塞调用或者可以阻塞或删除定时器任务的函数,比如:OSTimeDly() OSTimeDlyHMSM() 等等一些函数。 UCOSIII的软件定时器服务的相关代码是在OS_TIMR.c文件中,当设置OS_CFG.H中的OS_CFG_TMR_EN置为1的时候软件定时器服务被使能。 UCO
[单片机]
STM32使用systick定时器定义硬件精准延时函数
前言 博文基于STM32F103ZET6和标准固件库V3.5.0在MDK5环境下开发; 本博文讨论的是芯片不运行操作系统的情况下完成1s的延时功能; 如有不足之处还请多多指教; SysTick—系统滴答定时器是什么? 是一个24位的硬件倒计数定时器; SysTick的功能是什么?(分两种情况) 芯片运行操作系统(UCOS)情况下做:为操作系统(例如UCOS)提供硬件上的定时中断(滴答中断),作为整个系统的时基,为多个任务分配不同的时间片,确保不会出现一个任务霸占系统的情况;或者把每个定时器周期的某个时间范围赐予特定的任务等;还有操作系统提供的各种定时功能; 不运行操作系统,单纯做定时器:提供精准的定时功能; SysTick的特
[单片机]
<font color='red'>STM32</font>使用systick定时器定义硬件精准延时函数
stm32中的错误以及排除方法
1、error: #256: invalid redeclaration of type name s32 (declared at line 470 of C:\Keil_STM32\ARM\INC\ST\STM32F10x\stm32f10x.h ) 解决方法:打开stm32f10x_conf.h文件,将第21行的“#include stm32f10x_type.h ”注释掉,再保存stm32f10x_conf.h文件,重新编译即可。 2、 warning: #47-D: incompatible redefinition of macro HSE_Value (declared at line 511 of C:\K
[单片机]
STM32标准库编程之LED闪烁
【1】工程截图 【2】代码解读 有了前面文章的基础,工程建立我不再赘述,我们来看主函数所在的源文件(这里完全可以把开头的宏定义和函数声明放到同名的头文件中)。 这里我只引用了delay.h和sys.h 可以看到这三个文件放到一个文件夹里,说明他们三个比较特殊,这里就详细介绍一下(辅助编程文件夹里的开发指南里说的很详细)。SYSTEM 文件夹下包含了 delay、 sys、 usart 等三个文件夹。分别包含了 delay.c、 sys.c、 usart.c及其头文件。通过这 3 个 c 文件,可以快速的给任何一款 STM32F1 构建最基本的框架。使用起来是很方便的。 delay 延时的编程思想: CM3 内核的处理
[单片机]
<font color='red'>STM32</font>标准库编程之LED闪烁
STM32标准库和HAL库有什么不同 我们怎么用
摘要:通常新手在入门STM32的时候,首先都要先选择一种要用的开发方式,不同的开发方式会导致你编程的架构是完全不一样的。一般大多数都会选用标准库和HAL库,而极少部分人会通过直接配置寄存器进行开发。 网上关于标准库、HAL库的描述相信是数不胜数。可是一个对于很多刚入门的朋友还是没法很直观的去真正了解这些不同开发发方式彼此之间的区别,所以笔者想以一种非常直白的方式,用自己的理解去将这些东西表述出来,如果有描述的不对的地方或者是不同意见的也可以大家提出。 一、配置寄存器不少先学了51的朋友可能会知道,会有一小部分人或是教程是通过汇编语言直接操作寄存器实现功能的,这种方法到了STM32就变得不太容易行得通了。 因为STM32的
[单片机]
<font color='red'>STM32</font>标准库和HAL库有什么不同 我们怎么用
CRC校验 、STM32中CRC计算单元、 CRC应用
从这一段时间后台反馈的问题可以看得出来,好些朋友对CRC没有什么概念,今天就在这里讲述一下关于CRC校验、STM32中CRC计算单元相关内容。 1关于CRC校验 CRC:Cyclic Redundancy Check,即循环冗余校验码。 CRC是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。 循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。 ---来自百度百科 学电子、计算机相关专业的同学都应该学习过CRC的基础原理。其原理说难不难,可以说就是一个公式。同时,说简单也不简单,这个公
[单片机]
CRC校验 、<font color='red'>STM32</font>中CRC计算单元、 CRC应用
基于ARM处理器的HDLC通信的DMA实现
    摘要: 以ARM7TDMI为内核的Samsung公司S3C4510B网络微控制器(Networking MCU)为基础,重点论述如何通过DMA(直接内存访问)方式实现HDLC通信。对软件设计中缓冲描述符、DMA状态配置和控制、ISR服务程序设计以及相关的硬件配置进行详细的描述,并讨论如何编写在操作系统下的驱动程序。     关键词: HDLC 缓冲描述符 循环链表 中断服务程序 DMA 目前在嵌入式产品开发设计中,通常是在OS(Operating System)厂商提供的BSP基础上进行开发工作;对于底层硬件的操作,程序设计人员很少关注或只是少量的修改。实际上很多产品,我们完全可以
[工业控制]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

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

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

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