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 } 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可以做其他事情 MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK MEM_CPY_DMA OK # ./dmatest nodma //CPU拷贝,各种竞争CPU MEM_CPY_NO_DMA MEM_CPY_NO_DMA MEM_CPY_NO_DMA ———————————————————————————————————————————————————————————————————————————— 熟悉了DMA与寄存器配置后, 下面来看下裸奔S3C2440的时候如何使用DMA来播放音频: 转自:http://blog.csdn.net/zhaocj/article/details/5583935 下面我们就用DMA的方式来实现音频的播放。由于是用DMA的方式,因此在播放的过程中不占用系统资源,我们可以很容易的实现声音的各种操作而丝毫不影响播放的效果,如音量的提高和降低、静音、暂停等。在这里,还需要强调一点,利用DMA传输数据,一次最多可以传输的字节大小为:DSZ×TSZ×TC,DSZ表示的是数据大小(字节、半字还是字,即是1、2还是4),TSZ表示的是传输大小(单元传输还是突发传输,即1还是4),TC表示传输计数值(即寄存器DCONn的低20位存放的数据),因此如果需要传输的字节大小超出了这三个参数乘积的大小,则还要进一步处理,在我们给出的程序中,我们就考虑了这方面的问题。下面就是具体的程序,其中我们是利用UART来实现音频信号的播出、停止、暂停、静音、音量的提高和降低的。 …… …… //一段纯音频数据数组 unsigned char music[] = { 0xF9, 0xFF, 0xF5, 0xFF, 0xF8, 0xFF, 0xF8, 0xFF, 0xF6, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xF9, 0xFF, 0xF6, 0xFF, 0xF6, 0xFF, 0xFA, 0xFF, 0xFD, 0xFF, 0xFA, 0xFF, 0xFA, 0xFF, 0xF7, 0xFF, 0xF6, 0xFF, …… …… }; int result; int remainder; char flag; char cmd; char play_state; void __irq uartISR(void) { char ch; rSUBSRCPND |= 0x1; rSRCPND |= 0x1<<28; rINTPND |= 0x1<<28; ch=rURXH0; switch(ch) {
上一篇:学习笔记--- S3C2440 对NANDFLASH操作原理与测试代码分析
下一篇:S3C2440的RAM和启动过程!
推荐阅读最新更新时间:2024-11-23 18:42
设计资源 培训 开发板 精华推荐
- LTC1261LCMS8-4 低输出电压发生器的典型应用电路
- RT9161A 300/500mA 低压差线性稳压器典型应用
- CH347
- CH455H_数码管显示模块
- 直流和步进电机驱动IC —— TC78H660FTG
- LTC3121IDE 2.5V/4.2V 至 175mA 单节锂电池 3-LED 驱动器的典型应用电路
- 均流5V/12A电源应用电路
- H3LIS331DL 3轴数字加速度计适配器板,标准DIL 24插座,兼容STEVAL-MKI109V2
- AM1G-2407SH30Z 7.2V 1 瓦 DC-DC 转换器的典型应用
- 基于S2-LP无线电的Sub-1 GHz 915 MHz 射频扩展板,用于STM32 Nucleo