一、前言
DMA可以独立于CPU之外进行数据的搬移操作,因此在大量数据需要进行迁移时可以利用DMA的优势,减轻CPU的负担,从而提高系统的性能。
二、实验目标
使用DMA模块将一片RAM内存的数据搬移到另外一片内存。
三、实验分析
S3C2440A 支持 4 通道处于系统总线和外设总线间的 DMA 控制器。DMA 控制器的每个通道都可以无限制的执行系统总线与/或外设总线之间设备的数据移动。换句话说,每个通道都可以处理以下 4 种情况:
(1) 源和目标都在系统总线上
(2) 当目标在外设总线上时源在系统总线上
(3))当目标在系统总线上时源在外设总线上
(4))源和目标都在外设总线上
DMA 的主要优点是在无 CPU 干预的情况下能进行数据传输。DMA 的运行可以由软件开始,也可以来自内部外设或外部请求引脚的请求。
配置DMA无非就是要确定几个条件:源地址、进行一次搬移的数据长度、要搬移数据的总共大小、目标地址。
四、程序编写
代码分为以下几个文件:
head.S:启动文件。
init.c:初始化函数,关闭看门狗,初始化系统时钟。
main.c:DMA实验相关函数。
Makefile:用来编译代码。
各个文件内容分别如下:
head.S
@*************************************************************************
@ File:head.S
@ 功能:设置FCLK到400MHz
@*************************************************************************
.text
.global _start
_start:
ldr sp, =4096 @设置堆栈,因为要调用C语言函数
bl disable_watch_dog @关WATCH DOG
bl init_system_clk @初始化系统时钟,FCLK=400MHz,HCLK=100MHz,PCLK=50MHz
bl main @跳转执行main函数
halt_loop:
b halt_loop
init.c
/* WOTCH DOG register */
#define REG_WTCON (*(volatile unsigned long *)0x53000000)
/* Sys Clk Config */
#define REG_CLKDIVN (*(volatile unsigned long *)0x4C000014)
#define REG_CAMDIVN (*(volatile unsigned long *)0x4C000018)
#define REG_MPLLCON (*(volatile unsigned long *)0x4C000004)
void disable_watch_dog();
void init_system_clk();
/*上电后,WATCH DOG默认是开着的,要把它关掉 */
void disable_watch_dog()
{
REG_WTCON = 0;
}
void init_system_clk()
{
//HCLK = FCLK/4, 当 CAMDIVN[9] = 0 时
//PCLK 设置为 HCLK/2
//完成配置FCLK : HCLK : PCLK = 1 : 1/4 : 1/8,DIVN_UPLL是USB的时钟不用管
REG_CLKDIVN = (2 << 1) | (1 << 0);
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
__asm__(
"mrc p15, 0, r1, c1, c0, 0n" /* 读出控制寄存器 */
"orr r1, r1, #0xc0000000n" /* 设置为“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0n" /* 写入控制寄存器 */
);
//m=MDIV+8, p=PDIV+2, s=SDIV, Mpll = ( 2 × m × Fin ) / ( p × 2^s )
//FCLK = (2 * (92 + 8) * 12000000) / ((1 + 2) * 2) = 400000000 = 400MHz
//配置完MPLL后时钟停振,CPU停止运行等待时钟输出稳定,之后FCLK=400MHz,HCLK=100MHz,PCLK=50MHz */
REG_MPLLCON = (92<<12)|(1<<4)|(1<<0);
}
main.c
#define BYTE unsigned char
#define WORD unsigned short
#define DWORD unsigned int
/* GPIO Configure*/
#define GPFCON (*(volatile unsigned long *)0x56000050)
#define GPFDAT (*(volatile unsigned long *)0x56000054)
#define GPFUP (*(volatile unsigned long *)0x56000058)
/* DMA */
#define REG_DISRC0 (*(volatile unsigned long *)0x4B000000)
#define REG_DISRCC0 (*(volatile unsigned long *)0x4B000004)
#define REG_DIDST0 (*(volatile unsigned long *)0x4B000008)
#define REG_DIDSTC0 (*(volatile unsigned long *)0x4B00000C)
#define REG_DCON0 (*(volatile unsigned long *)0x4B000010)
#define REG_DSTAT0 (*(volatile unsigned long *)0x4B000014)
#define REG_DMASKTRIG0 (*(volatile unsigned long *)0x4B000020)
void wait(volatile unsigned long dly)
{
for(; dly > 0; dly--);
}
void init_led(void)
{
GPFCON &= ~((DWORD)(3 << (2 * 4)) | (3 << (2 * 5)) | (3 << (2 * 6)));
GPFCON |= ((DWORD)(1 << (2 * 4)) | (1 << (2 * 5)) | (1 << (2 * 6))); //GPF4、GPF5、GPF6输出模式
GPFDAT |= (1 << 4) | (1 << 5) | (1 << 6); //输出高电平,LED全灭
}
int main()
{
WORD i;
DWORD dwSrc[100];
DWORD dwDst[100];
init_led();
//填充原始数据
for (i = 0; i < sizeof(dwSrc) / sizeof(dwSrc[0]); i++)
{
dwSrc[i] = i + 1;
dwDst[i] = 0;
}
//配置DMA传输
//设置源地址
REG_DISRC0 = (DWORD)&dwSrc[0];
//源位于AHB总线, 源地址递增
REG_DISRCC0 = (0<<1) | (0<<0);
//设置目标地址
REG_DIDST0 = (DWORD)&dwDst[0];
//在 TC 到达 0 时发生中断, 目的位于AHB总线, 目的地址递增
REG_DIDSTC0 = (0<<2) | (0<<1) | (0<<0);
//AHP总线同步,禁止中断,全速传输(BIT27),软件触发(BIT23)
REG_DCON0 = (1<<30)|(0<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(sizeof(dwSrc)<<0);
//打开通道,软件启动DMA
REG_DMASKTRIG0 = (1<<1) | (1<<0);
//等待DMA传输完成
while(REG_DSTAT0 != 0);
GPFDAT &= ~((DWORD)1 << 4); //亮第1个灯,表示DMA传输完成
//检验数据
for (i = 0; i < (sizeof(dwSrc) / sizeof(dwSrc[0])); i++)
{
if(dwSrc[i] != dwDst[i])
{
while(1);
}
}
//判断数据传输是否正确
if(i != (sizeof(dwSrc) / sizeof(dwSrc[0])))
{
i = 5; //传输错误,第2个灯闪烁
}
else
{
i = 6; //传输正确,第3个灯闪烁
}
while(1)
{
//对应LED闪烁
GPFDAT ^= (DWORD)1 << i;
wait(30000);
}
return 0;
}
Makefile
objs := head.o init.o main.o
DMA.bin: $(objs)
arm-linux-ld -Ttext 0x0000000 -g -o DMA_elf $^
arm-linux-objcopy -O binary -S DMA_elf $@
arm-linux-objdump -D -m arm DMA_elf > DMA.dis
%.o:%.c
arm-linux-gcc -Wall -O2 -c -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -O2 -c -o $@ $<
clean:
rm -f DMA.bin DMA_elf DMA.dis *.o
main函数中定义了两个数组dwSrc和dwDst,它们都是400字节的数组,一开始时dwSrc先赋值为1~400的整数,然后将dwDst清0,接下来配置DMA传输,代码中卡了一个while循环来等待DMA传输完毕,其实在这段时间里CPU可以去做其他的事情,DMA传输完毕后,再去比较dwSrc和dwDst两个数组的数据是否相等来判断DMA是否传输正确,因为没接串口,所以实验是否成功的依据是开发板上3个LED的状态,第1个LED亮代表DMA传输完毕,之后如果第2个LED在闪烁代表传输数据不匹配即传输错误,若是第3个LED在闪烁代表数据匹配DMA传输正确,
执行make命令后将生成的bin文件烧写到开发板的NandFlash中,然后选择Nand启动即可看到正确的现象。
五、实验总结
DMA是CPU的好帮手,可以有效的提高系统性能。
上一篇:【JZ2440笔记】串口通信
下一篇:【JZ2440笔记】定时器
推荐阅读最新更新时间:2024-11-13 11:24
设计资源 培训 开发板 精华推荐
- 使用 Analog Devices 的 LT1317CS8 的参考设计
- 使用 Diodes Incorporated 的 AP34063 的参考设计
- 使用 NXP Semiconductors 的 BGA3012 的参考设计
- PD电源顶板
- LT1076HVCT 抽头电感降压转换器的典型应用
- 使用 Seeed Technology Co.,Ltd 的 DS1307 的参考设计
- 使用 ROHM Semiconductor 的 BD9A301MUV-LBE2 的参考设计
- 使用 Analog Devices 的 LT1182CS 的参考设计
- LTC2408 的典型应用 - 4 通道/8 通道 24 位功率无延迟 Delta-Sigma ADC
- 同轴光pcb