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

学习笔记 --- S3C2440 DMA操作原理

发布时间:2024-05-20 发布时间:
|

DMA(Direct Memory Access,直接内存访问)是一种不经过CPU而直接从内存存取数据的数据交换模式。在需要进行大量数据交换的场合,用好DMA,可以大大提高系统的性能,因为DMA操作几乎不占用CPU资源。s3c2440提供了4个通道的DMA


每个DMA通道能处理下面四种情况的数据传输:

(1)源器件和目的器件都在系统总线 APB

(2)源器件在系统总线,目的器件在外设总线

(3)源器件在外设总线,目的器件在系统总线

(4)源器件和目的器件都在外设总线 AHB

下面的DMA驱动程序使用的是第四种,内存属于AHB总线上的,我们打算在内存中开辟两个连续空间,分别作为源和目的。我们用两个方法将源中的数据写到目的中,一种方法是让cpu去做,另外一种方法是让DMA去做:


#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define MEM_CPY_NO_DMA 0

#define MEM_CPY_DMA 1

#define BUF_SIZE (512*1024)

#define DMA0_BASE_ADDR 0x4B000000

#define DMA1_BASE_ADDR 0x4B000040

#define DMA2_BASE_ADDR 0x4B000080

#define DMA3_BASE_ADDR 0x4B0000C0

struct s3c_dma_regs {

unsigned long disrc;

unsigned long disrcc;

unsigned long didst;

unsigned long didstc;

unsigned long dcon;

unsigned long dstat;

unsigned long dcsrc;

unsigned long dcdst;

unsigned long dmasktrig;

};

static int major = 0;

static char *src;

static u32 src_phys;

static char *dst;

static u32 dst_phys;

static struct class *cls;

static volatile struct s3c_dma_regs *dma_regs;

static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);

/* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */

static volatile int ev_dma = 0;

static int s3c_dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{

int i;

memset(src, 0xAA, BUF_SIZE);

memset(dst, 0x55, BUF_SIZE);

switch (cmd)

{

//这是非DMA模式

case MEM_CPY_NO_DMA :

{

for (i = 0; i < BUF_SIZE; i++)

dst[i] = src[i]; //CPU直接将源拷贝到目的

if (memcmp(src, dst, BUF_SIZE) == 0)

{

printk("MEM_CPY_NO_DMA OKn");

}

else

{

printk("MEM_CPY_DMA ERRORn");

}

break;

}

//这是DMA模式

case MEM_CPY_DMA :

{

ev_dma = 0;

/* 把源,目的,长度告诉DMA */

/* 关于下面寄存器的具体情况,我们在注释3里面来详细讲一下 */

dma_regs->disrc = src_phys; /* 源的物理地址 */

dma_regs->disrcc = (0<<1) | (0<<0); /* 源位于AHB总线, 源地址递增 */

dma_regs->didst = dst_phys; /* 目的的物理地址 */

dma_regs->didstc = (0<<2) | (0<<1) | (0<<0); /* 目的位于AHB总线, 目的地址递增 */

dma_regs->dcon = (1<<30)|(1<<29)|(0<<28)|(1<<27)|(0<<23)|(0<<20)|(BUF_SIZE<<0); /* AHP总线同步,使能中断,全速传输(BIT27),软件触发(BIT23), */

/* 启动DMA */

dma_regs->dmasktrig = (1<<1) | (1<<0); //打开通道,软件启动

/* 如何知道DMA什么时候完成? */

/* 休眠 */

wait_event_interruptible(dma_waitq, ev_dma);

if (memcmp(src, dst, BUF_SIZE) == 0)

{

printk("MEM_CPY_DMA OKn");

}

else

{

printk("MEM_CPY_DMA ERRORn");

}

break;

}

}

return 0;

}

static struct file_operations dma_fops = {

.owner = THIS_MODULE,

.ioctl = s3c_dma_ioctl,

};

static irqreturn_t s3c_dma_irq(int irq, void *devid)

{

/* 唤醒 */

ev_dma = 1;

wake_up_interruptible(&dma_waitq); /* 唤醒休眠的进程 */

return IRQ_HANDLED;

}

static int s3c_dma_init(void)

{

/* 这里注册一个中断,当DMA数据传输完毕之后会发生此中断 */

if (request_irq(IRQ_DMA3, s3c_dma_irq, 0, "s3c_dma", 1))

{

printk("can't request_irq for DMAn");

return -EBUSY;

}

/* 分配SRC, DST对应的缓冲区:之前我们知道在内核中开辟空间可以用kmalloc函数,这里却用了dma_alloc_writecombine,这是为什么呢?这是因为kmalloc开辟的空间其逻辑地址虽然是连续的,但是其实际的物理地址可能不是连续的。而DMA传输数据时,要求物理地址是连续的,dma_alloc_writecombine就满足这一点,这个函数的原型是:dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)其中size代表开辟的空间的大小,handle代表开辟的空间的物理地址,返回值是开辟的空间的逻辑地址*/

src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);//源

if (NULL == src)

{

printk("can't alloc buffer for srcn");

free_irq(IRQ_DMA3, 1);

return -ENOMEM;

}

dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);//目的

if (NULL == dst)

{

free_irq(IRQ_DMA3, 1);

dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);

printk("can't alloc buffer for dstn");

return -ENOMEM;

}

major = register_chrdev(0, "s3c_dma", &dma_fops);//注册字符设备

/* 为了自动创建设备节点 */

cls = class_create(THIS_MODULE, "s3c_dma");

class_device_create(cls, NULL, MKDEV(major, 0), NULL, "dma"); /* /dev/dma */

dma_regs = ioremap(DMA3_BASE_ADDR, sizeof(struct s3c_dma_regs));//这边是将DMA控制寄存器映射到内核空间

return 0;

}

static void s3c_dma_exit(void)

{

iounmap(dma_regs);

class_device_destroy(cls, MKDEV(major, 0));

class_destroy(cls);

unregister_chrdev(major, "s3c_dma");

dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);

dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys);

free_irq(IRQ_DMA3, 1);

}

module_init(s3c_dma_init);

module_exit(s3c_dma_exit);

MODULE_LICENSE("GPL");

测试程序:


#include

#include

#include

#include

#include

#include

/* ./dma_test nodma

* ./dma_test dma

*/

#define MEM_CPY_NO_DMA 0

#define MEM_CPY_DMA 1

void print_usage(char *name)

{

printf("Usage:n");

printf("%s n", name);

}

int main(int argc, char **argv)

{

int fd;

if (argc != 2)

{

print_usage(argv[0]);

return -1;

}

fd = open("/dev/dma", O_RDWR);

if (fd < 0)

{

printf("can't open /dev/dman");

return -1;

}

if (strcmp(argv[1], "nodma") == 0)

{

while (1)

{

ioctl(fd, MEM_CPY_NO_DMA);

}

}

else if (strcmp(argv[1], "dma") == 0)

{

while (1)

{

ioctl(fd, MEM_CPY_DMA);

}

}

else

{

print_usage(argv[0]);

return -1;

}

return 0;

}

测试方法:

# insmod dma.ko //加载驱动

# cat /proc/interrupts //查看中断

CPU0

30: 52318 s3c S3C2410 Timer Tick

33: 0 s3c s3c-mci

34: 0 s3c I2SSDI

35: 0 s3c I2SSDO

36: 0 s3c s3c_dma

37: 12 s3c s3c-mci

42: 0 s3c ohci_hcd:usb1

43: 0 s3c s3c2440-i2c

51: 2725 s3c-ext eth0

60: 0 s3c-ext s3c-mci

70: 97 s3c-uart0 s3c2440-uart

71: 100 s3c-uart0 s3c2440-uart

83: 0 - s3c2410-wdt

Err: 0


# ls /dev/dma //查看设备

/dev/dma


# ./dmatest //如此就会打印用法

Usage:

./dmatest


# ./dmatest dma //以DMA方式拷贝,CPU可以做其他事情

ME

[1] [2] [3] [4]
s3c2440DMA寄存器

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

热门文章 更多
浅谈AVR中定时器几种工作模式