×
单片机 > 单片机程序设计 > 详情

第7课:DMA

发布时间:2020-06-19 发布时间:
|
DMA用来连接外部设备和内部设备之间的数据传输。有了它就可以绕开CPU,直接进行数据传输。但同时DMA的传输也会用到数据总线。所以它的使用也是有利有弊的。

DMA利用了有限状态机来分析。它分成3个状态。

状态1等待REQ,一切状态为0

状态2这个状态已经接受到了REQ,这个时候把CNT中的值导入到DMA中的CURR—TC中,然后ACK就变成了1

状态3这个时候分为2个有限状态机,子机用来作数据传输,从源地址取数据,再写到目标地址。而主机负责计数,以及计数到0的中断等。。。

其实 有限状态机FSM这种分析方法简单来说就是把过程的变化无视掉,转而只看状态稳定后的结果。

以下来介绍在传输中的3种基本分类。

第一类 是 单元式 和 突发式 unit and brust

unit 发送1个Byte,而brust发送4个。

第2类 服务类 单一服务 和 连续服务 single and whole

单一服务本质上来讲就是做一个原子操作 必须再从状态1到状态3再走一遍,也就是说 需要等待REQ的这比有请求。而连续服务则在CURR_TC中的计数到0才停止。但是需要注意的一点:虽然是连续的,但是它也要短暂的释放数据总线,来给“饥渴”的CPU控制。

第3类  请求式和握手式  demand and shake

请求式就是当ACK结束整个一次传输后,恢复到0,发现REQ仍然是1的话,继续进行传输。

而shake mode 指的是 ,ACK完成一次传输后,一定要等到REQ 变成0,来确认才能继续进行。不然会一直等待。

 

请求式单服务,握手式单服务,握手式全服务,请求式全服务。一般如果是外设对外设的话,推荐使用请求全服务。

而从速度上来讲,连续服务的确比单服务快一点。

简单说下需要设置的寄存器。

因为我们有4组DMA,每组要设置的寄存器是9个,所以总共算术36个。

在9个中,前4个设置源地址,源地址的控制,目标地址,目标地址的控制等选项。

第5个是DMA控制寄存器,它要可以设置DMA的模式等具体的内容。

第6,7个是看源地址和目标地址在现在的数值。

而第8个是看CT内部计数器中,递减到了几。

第9个是控制DMA启动和停止的寄存器。例如在单一模式下,传完一次 on/off位会自动置0,需要再次置1才能再次传输

 

以下来进行举例:

写一个程序 利用分别利用DMA传输1M数据,另外一个正常拷贝,利用定时器PWM来计时 并输出到UART串口中。基本上除了中断,前2次用到的设备这次都利用到了。

#include"s3c2440.h"
#define UART_CLK 50000000
#define UART_BAUD_RATE 115200
#define UART_BRD (int)(UART_CLK/(UART_BAUD_RATE *16))-1

int n=0;                                                                                    //定义外部变量

void init_uart()
{
        rGPHCON |=0;
        rGPHUP = 0x0c;
        rULCON0 = 0x3;
        rUCON0 = 0x5;
        rUFCON0 = 0;
        rUMCON0 = 0;
        rUBRDIV0 = UART_BRD;
}

void init_timer0()                                                    //初始化pwm,0.5秒计65535次数
{
 rTCFG0 = 0x63;
 rTCFG1 = 0x1;
 rTCNTB0 = 0xFFFF;
 rTCON = 0xa;
 rTCON = 0x9;
}

void init_irq()
{
        rINTMOD = 0;
        rINTMSK = ~(1<<10);                                    //计时到0进入ISR,其中进行计数的累加,和小灯亮暗处理,小灯亮暗 为了说明进入了中断
}

void uart_write(char *data)
{
    while (*data != '\0') {
        while (!(rUTRSTAT0 & 0x4));
        rUTXH0 = *data;
        data++;
    }
}

void init_dma()
{
 rDISRC0 = 0x31000000;                                               //初始化源地址
        rDISRCC0 = 0x0;                                                     
        rDIDST0 = 0x32000000;    
        rDIDSTC0 = 0x4;
        rDCON0 = 0xb820ffff;                                           //设置为模式

void long2char(long n)   // 65535 is half second   把计数的值变成2进制,输出到uart中。
{
 int c=32;
 while(c!=0){
 if(n&(0x1<<31))
  uart_write("1");
 else
  uart_write("0");
 c--;
 n=n<<1;
 }
}

void init_led()
{
        rGPECON = GPE12_out|GPE13_out;
}

int main()
{
 init_uart();
 init_led();
 init_irq();
 init_timer0();
 long t,t1,t2,num;
 char *src = (char *)0x31000000;
        char *obj = (char *)0x32000000;
 num = 1024*1024;
 n=0;
 t1= (long)rTCNTO0;                                                                                  //t1第一次计数            
 uart_write("This is the time of normal memory copy :");
 while(num>0){
  *obj++ = *src++;                                                                                       //正常的拷贝
  num--;
 }
 t2= (long)rTCNTO0;                                                                                  //t2第2次
 t = (t1-t2)+65535*n;                                                                                   //计算时间差
 long2char(t);
 uart_write("\r\n");

 init_dma();
 n = 0;
 t1 = rTCNTO0;
 rDMASKTRIG0 = 3;                                                                                //set the reg and then DMA will operate
 while(rDSTAT0 & 0x1<<20);
 t2 = rTCNTO0;
 t= (t1-t2)+65535*n;
        uart_write("This is the time of DMA :");
        long2char(t);
        uart_write("\r\n");
}

这个是中断是ISR

#include"s3c2440.h"

void ISR_Handle()
{
 extern int n;                                                        //外部变量的声明
 n++;
 rGPEDAT ^= (3<<12);
 rSRCPND = 1<<10;
 rINTPND = 1<<10;
}

关键字:DMA

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM 汇编的必知必会