ARM PL330 DMA控制器开发(二)


  362人阅读  评论(1)  收藏  举报

四、S5PC100 PL330测试例子

由于PL330学习起来略显烦琐,因此建议在理解代码的基础上做实验,这样才能对DMA的学习有一定的深入。篇幅有限这里只给出核心的代码,若读者想参考完整源代码,请去华清远见官方论坛上下载。

下面的代码目标要实现内存间的数据拷贝。对于S5PC100,有3个DMA控制器。要实现内存间的DMA访问,需要使用DMA_mem。

如下图所示为DMAC控制流程。



图DMAC控制流程


配合上面的流程图,可以编写代码如下。

(1)相关的宏定义。

#define MAX 100

#define Inp(addr)            (*(volatileunsigned int *)(addr))

#define Outp(addr, data)     (*(volatileunsigned int *)(addr) = (data))

 

extern void printf(const char *fmt, ...);

void int_dma();

volatile char sour[32] = "012345678901234567890123456789\n";

volatile char dest[32] = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\n"; 

//最终实现将32个字节从sour的数据传输到dest

(2)设置SAR、CCR、DAR寄存器。

//main函数开始

uart0_init();

volatile char instr_seq[MAX];

int size = 0, x;

int loopstart, loopnum = 2;  //每个循环传输16个字节,传输2次

unsigned int source, destination,start, temp;

source = (unsigned int)sour;

destination = (unsigned int)dest;

start = (unsigned int)instr_seq;   //记录DMA指令的首地址

/*DMAMOV SAR0*/

instr_seq[size + 0] =(char)(0xbc);

instr_seq[size + 1] = (char)(0x0);

instr_seq[size + 2] =(char)((source>>0) & 0xff);  //设置数据源地址

instr_seq[size + 3] =(char)((source>>8) & 0xff);

instr_seq[size + 4] =(char)((source>>16) & 0xff);

instr_seq[size + 5] =(char)((source>>24) & 0xff);

size = 6;

/*DMAMOV DAR0*/

instr_seq[size + 0] = (char)(0xbc);

instr_seq[size + 1] = (char)(0x2);

instr_seq[size + 2] =(char)((destination>>0) & 0xff); //设置数据目标地址

instr_seq[size + 3] =(char)((destination>>8) & 0xff);

instr_seq[size + 4] =(char)((destination>>16) & 0xff);

instr_seq[size + 5] = (char)((destination>>24)& 0xff);

size += 6;

/*DMAMOV CC0. burst_size 8byte,burst_len 2*/ 

instr_seq[size + 0] =(char)(0xbc);

instr_seq[size + 1] = (char)(0x1);

//设置数据传输规则,每个循环传输burst_size* burst_len、原和目标地址变化规则、burst操作等

instr_seq[size + 2] = (char)(0x17);

instr_seq[size + 3] =(char)(0xc0);

instr_seq[size + 4] = (char)(0x5);

instr_seq[size + 5] = (char)(0x0);

size += 6;

(3)设置指令段的起始地址及执行第一次数据装载并输出FIFO。

/*DMALP LC0*/

instr_seq[size + 0] =(char)(0x20);

instr_seq[size + 1] =(char)(loopnum - 1);// 记录循环的次数

size += 2;

loopstart = size;

/*DMALD*/

instr_seq[size + 0] =(char)(0x04);   //从源读数据

size += 1;

/*DMARMB*/

instr_seq[size + 0] =(char)(0x12);

size += 1;

/*DMAST*/

instr_seq[size + 0] =(char)(0x08);  //写数据到目标地址

size += 1;

/*DMAWMB*/

instr_seq[size + 0] =(char)(0x13);

size += 1;

(4)产生中断,并延时一段时间。

/*可以在DMA指令执行过程中做延时。此处可以利用延时保证DMA传输完成后再停止DMA*/

/*DMALP LC0*/

instr_seq[size + 0] = (char)(0x20);

instr_seq[size + 1] = (char)(250);   //循环次数

size += 2;

loopstart = size;

/*DMANOP*/

instr_seq[size + 0] = (char)(0x18);   //DMA的NOP空指令,可以实现延时

size += 1;

/*DMALPEND 0*/

instr_seq[size + 0] = (char)(0x38);

instr_seq[size + 1] = (char)(size - loopstart);

size += 2;

/*DMASEV*/

instr_seq[size + 0] = (char)(0x34);

instr_seq[size + 1] = (char)(1<<3);  //通过DMA通道1发出中断申请,也可以选择其它的通道

size += 2;

#endif

(5)结束DMAC控制。

/*DMAEND*/

instr_seq[size + 0] = (char)(0x0);

size += 1;

(6)开始DMAC控制,设置相应的中断处理,并进行测试结果。

VIC0VECADDR18 = (unsigned int)int_dma;//DMA_mem的处理函数

INTERRUPT.VIC0INTENABLE |= 1<<18;        //使能中断控制器对应的中断位

Outp(0xE8100000+0x20, 0x2); //使能控制器的1中断通道,此处可以选择其它的通道,要和DMASEV对应

/*DMAGO*/

do{

       x = Inp(0xE8100D00);//检测DMA状态,确认可以操作

} while ((x&0x1)==0x1);

Outp(0xE8100D00+0x8,(0<<24)|(0xa0<<16)|(0<<8)|(0<<0));//DBGINST0  通道1

Outp(0xE8100D00+0xC, start);//DBGINST1

Outp(0xE8100D00+0x4, 0);//DBGCMD  执行DBGINST0、1中的DMAGO指令,start为开始地址

while(1);

//main函数结束

(7)ISR函数的实现如下。

void do_irq()

{

printf("in do_irq\n");

((void (*)(void))VIC0ADDRESS)();

}

/*ISR*/

void int_dma()

{

VIC0ADDRESS = 0;

Outp(0xE8100000+0x2C, 0x2);//清处DMA中断挂起位,因为上面选择的是通道1,所以清楚对应的位

printf("DMA Ending!\n");

printf("sour = %s", sour);

printf("dest = %s", dest);

}

实验调试过程与结果:

(1)将程序编译后生成.bin文件,打开终端使用uboot的dnw命令通过USB线将.bin文件下载到0x20008000这个地址,接着使用go命令去执行测试程序。

(2)可以看到下图所示的测试结果,如图所示。

五、小节

本文从PL330出发,介绍了基本的S5PC100下DMA控制器的操作方式和编程模型,旨在将最新的DMA控制技术以最简单的方式呈现。笔者还实现了串口控制器的收发之间使用DMA传输的例程。读者如果感兴趣可以在华清远见研发中心关于FS_S5PC100平台的论坛上下载。硬件控制器关联的DMA和内存间的DMA控制差别比较大的是DMA每次传输依赖控制器发出申请,DMA需要等待到一次申请后才能完成一次传输。而内存间的DMA可以由程序主动发起。


  1. DMA memtomem 单次传输成功。
    PL330 的DMA驱动和以往驱动的不同之处:
    s3c2440/s3c6410 处理器内部的 DMA 都是提供特殊功能寄存器 SFR 对 DMA 进行编程,但 PL330 不是这样。
    PL330 DMA 的基本工作原理是:
    a) DMA 有自己的特殊指令集,基本是1-6个字节不定长,主要有 DMAMOV/DMALD/DMAST/DMAGO
    b) DMA 的驱动主要包含两个工作:
    ### 1) 开辟一个buf,在buf里面存放DMA的二进制指令,可以完成数据传输操作。
    ### 2) 通过执行 DMAGO 指令,使得 DMA 内部的取指部件 pc 跳转到 buf,从 buf 取出并执行相应的指令。
    DMAGO 指令的本身的执行,是通过写 S5PV210 芯片的 DBGCMD/DBGINST0/DBGINST1 这3个寄存器实现的。
    c) linux 下的 pl330.c 是可以参考使用的驱动,主要包含了一系列 _emit_MOV/_emit_LD/_emit_ST 等类似指令的接口。
    这些接口完成类似编译器的工作,把一个DMA操作函数,转换为存放在 buf 数组里面的二进制指令。
    d) 驱动编程的关键在于形成 buf 内部的正确机器指令,然后通过对 DBGCMD/DBGINST0/DBGINST1 这3个寄存器进行编程,完成跳转到 buf 执行指令。

  2. DMALP/DMALPEND 还未成功,仍在调试



你可能感兴趣的:(ARM PL330 DMA控制器开发(二))