OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 9130|回复: 15

【ALIENTEK 战舰STM32开发板例程系列连载+教学】第二十六章 DMA实验

[复制链接]

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
发表于 2013-3-14 23:05:05 | 显示全部楼层 |阅读模式

第二十六章 DMA实验      

本章我们将向大家介绍STM32DMA。在本章中,我们将利用STM32DMA来实现串口数据传送,并在TFTLCD模块上显示当前的传送进度。本章分为如下几个部分:

26.1 STM32 DMA简介

26.2 硬件设计

26.3 软件设计

26.4 下载验证

26.1 STM32 DMA简介

DMA,全称为:Direct Memory Access,即直接存储器访问。DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAMI/O设备开辟一条直接传送数据的通路,能使CPU的效率大为提高。

STM32最多有2DMA控制器(DMA2仅存在大容量产品中),DMA17个通道。DMA25个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁起来协调各个DMA请求的优先权。

STM32DMA有以下一些特性:

每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。

在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推)

独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。

支持循环的缓冲器管理

每个通道都有3个事件标志(DMA 半传输,DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。

存储器和存储器间的传输

外设和存储器,存储器和外设的传输

闪存、SRAM、外设的SRAMAPB1 APB2AHB外设均可作为访问的源和目标。

可编程的数据传输数目:最大为65536

STM32F103ZET6有两个DMA控制器,DMA1DMA2,本章,我们仅针对DMA1进行介绍。

从外设(TIMxADCSPIxI2CxUSARTx)产生的DMA请求,通过逻辑或输入到DMA控制器,这就意味着同时只能有一个请求有效。外设的DMA请求,可以通过设置相应的外设寄存器中的控制位,被独立地开启或关闭。

26.1.1DMA1各通道一览表:

          26.1.1 DMA1个通道一览表

这里解释一下上面说的逻辑或,例如通道1的几个DMA1请求(ADC1TIM2_CH3TIM4_CH1),这几个是通过逻辑或到通道1的,这样我们在同一时间,就只能使用其中的一个。其他通道也是类似的。

这里我们要使用的是串口1DMA传送,也就是要用到通道4。接下来,我们介绍一下DMA设置相关的几个寄存器。

第一个是DMA中断状态寄存器(DMA_ISR)。该寄存器的各位描述如图26.1.1所示:

 

26.1.1 DMA_ISR寄存器各位描述

我们如果开启了DMA_ISR中这些中断,在达到条件后就会跳到中断服务函数里面去,即使没开启,我们也可以通过查询这些位来获得当前DMA传输的状态。这里我们常用的是TCIFx,即通道DMA传输完成与否的标志。注意此寄存器为只读寄存器,所以在这些位被置位之后,只能通过其他的操作来清除。

第二个是DMA中断标志清除寄存器(DMA_IFCR)。该寄存器的各位描述如图26.1.2所示:


26.1.2 DMA_IFCR寄存器各位描述

DMA_IFCR的各位就是用来清除DMA_ISR的对应位的,通过写0清除。在DMA_ISR被置位后,我们必须通过向该位寄存器对应的位写入0来清除。

第三个是DMA通道x配置寄存器(DMA_CCRx)(x=1~7,下同)。该寄存器的我们在这里就不贴出来了,见《STM32参考手册》第15010.4.3一节。该寄存器控制着DMA的很多相关信息,包括数据宽度、外设及存储器的宽度、通道优先级、增量模式、传输方向、中断允许、使能等都是通过该寄存器来设置的。所以DMA_CCRxDMA传输的核心控制寄存器。

第四个是DMA通道x传输数据量寄存器(DMA_CNDTRx)。这个寄存器控制DMA通道x的每次传输所要传输的数据量。其设置范围为0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前DMA传输的进度。

第五个是DMA通道x的外设地址寄存器(DMA_CPARx)。该寄存器用来存储STM32外设的地址,比如我们使用串口1,那么该寄存器必须写入0x40013804(其实就是&USART1_DR)。如果使用其他外设,就修改成相应外设的地址就行了。

最后一个是DMA通道x的存储器地址寄存器(DMA_CMARx),该寄存器和DMA_CPARx差不多,但是是用来放存储器的地址的。比如我们使用SendBuf[5200]数组来做存储器,那么我们在DMA_CMARx中写入&SendBuff就可以了。

DMA相关寄存器就为大家介绍到这里,此节我们要用到串口1的发送,属于DMA1的通道4,接下来我们就介绍下DMA1通道4的配置步骤:

1)设置外设地址。

设置外设地址通过DMA1_CPAR4来设置,我们只要在这个寄存器里面写入&USART1_DR的值就可以了。该地址将作为DMA传输的目标地址。

2)设置存储器地址。

设置存储器地址,我们通过DMA1_CMAR4来设置,假设我们要把数组SendBuf作为存储器,那么我们在该寄存器写入&SendBuf就可以了。该地址将作为DMA传输的源地址。

3)设置传输数据量。

通过DMA1_CNDTR4来设置DMA1通道4的数据传输量,这里面写入此次你要传输的数据量就可以了,也就是SendBuf的大小。该寄存器的数值将在DMA启动后自减,每次新的DMA传输,都重新向该寄存器写入要传输的数据量。

4)设置通道4的配置信息。

配置信息通过DMA1_CCR4来设置。这里我们设置存储器和外设的数据位宽均为8,且模式是存储器到外设的存储器增量模式。优先级可以随便设置,因为我们只有一个通道被开启了。假设有多个通道开启(最多7个),那么就要设置优先级了,DMA仲裁器将根据这些优先级的设置来决定先执行那个通道的DMA。优先级越高的,越早执行,当优先级相同的时候,根据硬件上的编号来决定哪个先执行(编号越小越优先)。

5)使能DMA1通道4,启动传输。

在以上配置都完成了之后,我们就使能DMA1_CCR4的最低位开启DMA传输,这里注意要设置USART1的使能DMA传输位,通过USART1->CR3的第七位设置。

通过以上5步设置,我们就可以启动一次USART1DMA传输了。

26.2 硬件设计

所以本章用到的硬件资源有:

1)  指示灯DS0

2)  KEY0按键

3)  串口

4) TFTLCD模块

5) DMA

本章我们将利用外部按键KEY0来控制DMA的传送,每按一次KEY0DMA就传送一次数据到USART1,然后在TFTLCD模块上显示进度等信息。DS0还是用来做为程序运行的指示灯。

本章实验需要注意P6口的RXDTXD是否和PA9PA10连接上,如果没有,请先连接。

26.3 软件设计

打开上一章的工程,首先在HARDWARE文件夹下新建一个DMA的文件夹。然后新建一个dma.c的文件和dma.h的头文件,保存在DMA文件夹下,并将DMA文件夹加入头文件包含路径。

打开dma.c文件,输入如下代码:

#include "dma.h"

#include "delay.h"

u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度           

//DMA1的各通道配置

//这里的传输形式是固定的,这点要根据不同的情况来修改

//从存储器->外设模式/8位数据宽度/存储器增量模式

//DMA_CHxMA通道CHx

//cpar:外设地址

//cmar:存储器地址

//cndtr:数据传输量 

void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)

{

    RCC->AHBENR|=1<<0;          //开启DMA1时钟

    delay_ms(5);                //等待DMA时钟稳定

    DMA_CHx->CPAR= cpar;        //DMA1 外设地址

    DMA_CHx->CMAR=(u32)cmar;    //DMA1,存储器地址

    DMA1_MEM_LEN=cndtr;         //保存DMA传输数据量

    DMA_CHx->CNDTR=cndtr;       //DMA1,传输数据量

    DMA_CHx->CCR=0X00000000;    //复位

    DMA_CHx->CCR|=1<<4;         //从存储器读

    DMA_CHx->CCR|=0<<5;         //普通模式

    DMA_CHx->CCR|=0<<6;         //外设地址非增量模式

    DMA_CHx->CCR|=1<<7;         //存储器增量模式

    DMA_CHx->CCR|=0<<8;         //外设数据宽度为8

    DMA_CHx->CCR|=0<<10;        //存储器数据宽度8

    DMA_CHx->CCR|=1<<12;        //中等优先级

    DMA_CHx->CCR|=0<<14;        //非存储器到存储器模式         

}

//开启一次DMA传输

void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)

{

    DMA_CHx->CCR&=~(1<<0);       //关闭DMA传输

    DMA_CHx->CNDTR=DMA1_MEM_LEN; //DMA1,传输数据量

    DMA_CHx->CCR|=1<<0;          //开启DMA传输

}

该部分代码仅仅2个函数,MYDMA_Config函数,基本上就是按照我们上面介绍的步骤来初始化DMA的,该函数在外部只能修改通道、源地址、目标地址和传输数据量等几个参数,更多的其他设置只能在该函数内部修改。

MYDMA_Enable函数用来产生一次DMA传输,该函数每执行一次,DMA就发送一次。

保存dma.c,并把dma.c加入到HARDWARE组下,接下来打开dma.h,输入如下内容:

#ifndef __DMA_H

#define __DMA_H   

#include "sys.h"                                                

void MYDMA_Config(DMA_Channel_TypeDef*DMA_CHx,u32 cpar,u32 cmar,u16 cndtr);

//配置DMA1_CHx

void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx);//使能DMA1_CHx         

#endif

保存dma.h,最后我们在test.c里面修改main函数如下:

u8 SendBuff[5168];

const u8 TEXT_TO_SEND[]={"ALIENTEK Warship STM32 DMA 串口实验"};   

int main(void)

{  

    u16 i;

    u8 t=0;

    u8 j,mask=0;

    float pro=0;//进度

    Stm32_Clock_Init(9);    //系统时钟设置

    uart_init(72,9600);    //串口初始化为9600

    delay_init(72);        //延时初始化

    LED_Init();             //初始化与LED连接的硬件接口

    LCD_Init();            //初始化LCD

    usmart_dev.init(72);    //初始化USMART     

    KEY_Init();             //按键初始化          

    MYDMA_Config(DMA1_Channel4,(u32)&USART1->DR,(u32)SendBuff,5168);

//DMA1通道4,外设为串口1,存储器为SendBuff,长度5168.

    POINT_COLOR=RED;//设置字体为红色

    LCD_ShowString(60,50,200,16,16,"WarShip STM32");   

    LCD_ShowString(60,70,200,16,16,"DMA TEST");

    LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");

    LCD_ShowString(60,110,200,16,16,"2012/9/8");   

    LCD_ShowString(60,130,200,16,16,"KEY0:Start");

    //显示提示信息 

    j=sizeof(TEXT_TO_SEND);   

    for(i=0;i<5168;i++)//填充ASCII字符集数据

    {

        if(t>=j)//加入换行符

        {

            if(mask)

            {

                SendBuff=0x0a;

                t=0;

            }else

            {

                SendBuff=0x0d;

                mask++;

            }  

        }else//复制TEXT_TO_SEND语句

        {

            mask=0;

            SendBuff=TEXT_TO_SEND[t];

            t++;

        }          

    }      

    POINT_COLOR=BLUE;//设置字体为蓝色    

    i=0;

    while(1)

    {

        t=KEY_Scan(0);

        if(t==KEY_RIGHT)//KEY0按下

        {

            LCD_ShowString(60,150,200,16,16,"Start Transimit....");

            LCD_ShowString(60,170,200,16,16,"   %");//显示百分号

            printf("\r\nDMA DATA:\r\n");

            delay_ms(5);       

            USART1->CR3=1<<7;           //使能串口1DMA发送      

            MYDMA_Enable(DMA1_Channel4);//开始一次DMA传输!     

            //等待DMA传输完成,此时我们来做另外一些事,点灯

            //实际应用中,传输数据期间,可以执行另外的任务

            while(1)

            {

                if(DMA1->ISR&(1<<13))//等待通道4传输完成

                {

                    DMA1->IFCR|=1<<13;//清除通道4传输完成标志

                    break;

                }

                pro=DMA1_Channel4->CNDTR;//得到当前还剩余多少个数据

                pro=1-pro/5168;//得到百分比  

                pro*=100;      //扩大100

                LCD_ShowNum(60,170,pro,3,16);    

            }              

            LCD_ShowNum(60,170,100,3,16);//显示100%  

            LCD_ShowString(60,150,200,16,16,"Transimit Finished!");

//提示传送完成

        }

        i++;

        delay_ms(10);

        if(i==20)

        {

            LED0=!LED0;//提示系统正在运行  

            i=0;

        }         

    }

}

至此,DMA串口传输的软件设计就完成了。

26.4 下载验证

在代码编译成功之后,我们通过串口下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示如图26.4.1所示:


26.4.1 DMA实验测试图

伴随DS0的不停闪烁,提示程序在运行。我们打开串口调试助手,然后按KEY0,可以看到串口显示如图26.4.2所示的内容:




26.4.2 串口收到的数据内容

上图中,接收到的数据,并没有进行换行,而实际上,我们的串口是发送了换行符的,这是SSCOM3.3串口调试助手的又一个小bug。大家可以换一个别的串口调试助手,一般都是可以收到整齐的数据的。

同时可以看到TFTLCD上显示了进度等信息,如图26.4.3所示:


26.4.3 DMA串口数据传输中

至此,我们整个DMA实验就结束了,希望大家通过本章的学习,掌握STM32DMA使用。DMA是个非常好的功能,它不但能减轻CPU负担,还能提高数据传输速度,合理的应用DMA,往往能让你的程序设计变得简单。

 

实验21 DMA实验.rar

111.33 KB, 下载次数: 331

《STM32开发指南》第二十五章 DMA实验.rar

754.34 KB, 下载次数: 391

我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

13

主题

202

帖子

0

精华

高级会员

Rank: 4

积分
527
金钱
527
注册时间
2012-10-27
在线时间
65 小时
发表于 2013-3-15 13:18:28 | 显示全部楼层
沙发!!支持原子兄!!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2013-3-15 13:46:16 | 显示全部楼层
回复【2楼】arm51avr:
---------------------------------
谢谢支持
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

0

主题

16

帖子

0

精华

新手上路

积分
37
金钱
37
注册时间
2013-4-9
在线时间
0 小时
发表于 2013-8-20 10:16:18 | 显示全部楼层
顶原子哥啊,在你这学了不少东西
回复 支持 反对

使用道具 举报

0

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
91
金钱
91
注册时间
2014-11-17
在线时间
12 小时
发表于 2014-11-17 11:15:56 | 显示全部楼层
原子哥,我在DMA实验中我设置的数据传输量为38,在串口调试中为什么第一按key0后输出实验中DMA DATA:有换行 ,以后再按key0后就没有换行了呢,如下图:
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2014-11-17 20:09:32 | 显示全部楼层
你在DMA DATA:自行后加入一个换行吧
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

3

主题

9

帖子

0

精华

新手上路

积分
41
金钱
41
注册时间
2015-2-1
在线时间
0 小时
发表于 2015-2-1 22:20:17 | 显示全部楼层
回复【6楼】正点原子:
---------------------------------
原子哥,那个5168是怎么来的啊?我调试你发的那个数据加上结束符是36个
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2015-2-1 23:21:37 | 显示全部楼层
回复【7楼】fendouyishenging:
---------------------------------
36+回车换行=38.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

3

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
111
金钱
111
注册时间
2015-4-5
在线时间
18 小时
发表于 2015-4-9 20:58:44 | 显示全部楼层
原子哥,我在操作DMA控制器的时候一直有一个疑惑。关于源地址和目标地址我都能理解。但是在操作uart的时候,USART_DMACmd()这个函数中的RX和TX到底怎么确定。好比我设置从UART_RX中把数据写入内存数组RX_BUFFER[]中,那给USART_DMACmd()是发送DMA请求还是接收DMA请求啊
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165540
金钱
165540
注册时间
2010-12-1
在线时间
2117 小时
 楼主| 发表于 2015-4-9 22:23:55 | 显示全部楼层
回复【9楼】小t是个好孩纸:
---------------------------------
你要看这个函数里面的设置了。
我都是用寄存器的,你看寄存器描述。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

3

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
111
金钱
111
注册时间
2015-4-5
在线时间
18 小时
发表于 2015-4-11 11:10:14 | 显示全部楼层
回复【10楼】正点原子:
---------------------------------
以前都用的是库,看来是把自己惯懒了,现在好好学习寄存器~
回复 支持 反对

使用道具 举报

3

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
111
金钱
111
注册时间
2015-4-5
在线时间
18 小时
发表于 2015-4-11 20:23:42 | 显示全部楼层
回复【10楼】正点原子:
---------------------------------
问题解决了,是自己想得太复杂了~
回复 支持 反对

使用道具 举报

36

主题

94

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
275
金钱
275
注册时间
2015-3-29
在线时间
3 小时
发表于 2015-4-12 10:36:53 | 显示全部楼层
回复【9楼】小t是个好孩纸:
---------------------------------
是不是用串口接收就使能UART_RX?
回复 支持 反对

使用道具 举报

3

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
111
金钱
111
注册时间
2015-4-5
在线时间
18 小时
发表于 2015-4-12 13:58:54 | 显示全部楼层
回复【13楼】wodezpyzz:
---------------------------------
回复 支持 反对

使用道具 举报

36

主题

94

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
275
金钱
275
注册时间
2015-3-29
在线时间
3 小时
发表于 2015-4-13 16:19:33 | 显示全部楼层
回复【14楼】小t是个好孩纸:
---------------------------------
必须要用专门的DMA通道对应的有UART_RX功能才行是吗??
回复 支持 反对

使用道具 举报

3

主题

19

帖子

0

精华

初级会员

Rank: 2

积分
111
金钱
111
注册时间
2015-4-5
在线时间
18 小时
发表于 2015-4-13 19:42:11 | 显示全部楼层
回复【15楼】wodezpyzz:
---------------------------------
恩,是的
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-23 16:36

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表