以驱动方式测试dma的工作原理,用户层程序不停调用ioctl触发驱动里面完成的dma操作,把内存的数据块从一个连续地址搬到另一个连续地址,然后用memcmp测试搬移后的数据和搬移前是不是完全一样。
简单的驱动代码:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MEM_CPY_NO_DMA 0 #define MEM_CPY_USE_DMA 1 #define DMA_BUF_SIZE (512*1024) #define GRH_MODULE_NAME "grh_dma" #define DMA_REG_BASE_ADDR_0 0x4B000000 #define DMA_REG_BASE_ADDR_1 0x4B000040 #define DMA_REG_BASE_ADDR_2 0x4B000080 #define DMA_REG_BASE_ADDR_3 0x4B0000C0 static struct dma_regs{ volatile unsigned long *disrc; volatile unsigned long *disrcc; volatile unsigned long *didst; volatile unsigned long *didstc; volatile unsigned long *dcon; volatile unsigned long *dstat; volatile unsigned long *dcsrc; volatile unsigned long *dcdst; volatile unsigned long *dmasktrig; }; static struct dma_regs dma_regs_ins; static int major = 0; static char *src; static char *dst; static unsigned int src_phys; //数据源的物理地址 static unsigned int dst_phys; //目标位置的物理地址 static struct class *dma_class; static struct class_device *dma_class_device; //dma中断处理的相关数据 static DECLARE_WAIT_QUEUE_HEAD(dma_waitq); //休眠的进程队列头 static volatile int sleep_for_dma; //这个变量为0的时候ioctl函数会休眠,中断里面将其置1,read函数末尾将其设置为0 static int dma_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int i; memset(src, 0xaa, DMA_BUF_SIZE); memset(dst, 0x55, DMA_BUF_SIZE); switch(cmd){ case MEM_CPY_NO_DMA:{ for(i=0; i if(0 == memcmp(src, dst, DMA_BUF_SIZE)) printk(KERN_EMERG"dma success!n"); else printk(KERN_EMERG"dma fail!n"); break; } case MEM_CPY_USE_DMA:{ *(dma_regs_ins.disrc) = src_phys; //将数据源的物理地址存入disrc寄存器 *(dma_regs_ins.disrcc) = (0<<0) | (0<<1); //源数据和目的位置是位于主存上的,设置dma的地址递增 *(dma_regs_ins.didst) = dst_phys; 将目标物理地址存入didst寄存器 *(dma_regs_ins.didstc) = (0<<0) | (0<<1) | (0<<2); //不使用自动加载,地址递增,目的地址是位于主存中的 *(dma_regs_ins.dcon) = (1<<30) | (1<<29) | (0<<28) | (1<<27) | (0<<23) | (0<<20) | (DMA_BUF_SIZE<<0); //使能中断,不使用突发传输,dma为软件触发,传输的最小单位是1字节 //启动dma *(dma_regs_ins.dmasktrig) = (1<<1) | (1<<0); //休眠进程 wait_event_interruptible(dma_waitq, sleep_for_dma); sleep_for_dma = 0; if(0 == memcmp(src, dst, DMA_BUF_SIZE)) printk(KERN_EMERG"dma success!n"); else printk(KERN_EMERG"dma fail!n"); break; } } return 0; } static struct file_operations dma_fops = { .owner = THIS_MODULE, .ioctl = dma_ioctl, }; //dma中断处理函数 static irqreturn_t grh_handle_dma_int(int irq, void *dev_id){ printk(KERN_EMERG"dma interrupt happened!n"); sleep_for_dma = 1; wake_up_interruptible(&dma_waitq); return IRQ_HANDLED; } static int dma_init(void){ unsigned long *p_start; unsigned long **pp; int i; //分配dma使用的缓存空间 src = dma_alloc_writecombine(NULL, DMA_BUF_SIZE, &src_phys, GFP_KERNEL); dst = dma_alloc_writecombine(NULL, DMA_BUF_SIZE, &dst_phys, GFP_KERNEL); if(NULL==src){ printk(KERN_EMERG"allocate buffer for dma error!n"); return -ENOMEM; } if(NULL==dst){ dma_free_writecombine(NULL, DMA_BUF_SIZE, src, src_phys); printk(KERN_EMERG"allocate buffer for dma error!n"); return -ENOMEM; } //进行地址映射 p_start = (unsigned long*)ioremap(DMA_REG_BASE_ADDR_0, sizeof(struct dma_regs)); pp = &(dma_regs_ins.disrc); for(i=0; i<9; i++){ *(pp++) = p_start++; } //申请dma的中断 if(request_irq(IRQ_DMA0, grh_handle_dma_int, 0, "dma0", 1)){ printk(KERN_EMERG"request irq for dma error!n"); return -ENOMEM; } //默认会等待dma进行休眠 sleep_for_dma = 0; major = register_chrdev(0, GRH_MODULE_NAME, &dma_fops); //create my own device class dma_class = class_create(THIS_MODULE, "grh_dma_class"); //create my device of my own class dma_class_device = device_create(dma_class, NULL, MKDEV(major,0), NULL, "grh_dma_device"); return 0; } static void dma_exit(void){ unregister_chrdev(major, GRH_MODULE_NAME); device_unregister(dma_class_device); class_destroy(dma_class); iounmap(dma_regs_ins.disrc); dma_free_writecombine(NULL, DMA_BUF_SIZE, src, src_phys); dma_free_writecombine(NULL, DMA_BUF_SIZE, dst, dst_phys); free_irq(IRQ_DMA0, 1); //释放dma中断 } module_init(dma_init); module_exit(dma_exit); MODULE_AUTHOR("GRH"); MODULE_VERSION("1.0"); MODULE_DESCRIPTION("DMA DRIVER"); MODULE_LICENSE("GPL"); //用户层测试程序 #include #include #include #include #include #include #include #define MEM_CPY_NO_DMA 0 #define MEM_CPY_USE_DMA 1 void print_usage(const char *command){ printf("usage: %s } int main(int argc, char *argv[]){ int count; int fd; fd = open("/dev/grh_dma_device", O_RDWR); if(fd < 0){ printf("open device error!n"); return -1; } if(2 != argc){ printf("command format error!n"); print_usage(argv[0]); return -1; } if(0 == strcmp(argv[1],"nodma")){ while(1){ ioctl(fd, MEM_CPY_NO_DMA); } } else if(0 == strcmp(argv[1],"dma")){ while(1){ ioctl(fd, MEM_CPY_USE_DMA); } } else{ printf("parameter error!n"); print_usage(argv[0]); return -1; } close(fd); return 0; } 运行结果:使用dma时和不使用dma时数据搬移的效率明显加快,cpu消耗量锐减,系统响应速度提升。
上一篇:mini2440 按键驱动添加输入子系统,让按键可以真正当做键盘一样用
下一篇:简单外中断实验(最近又要搞arm相关的东西,复习一下中断)
推荐阅读最新更新时间:2024-11-09 11:50
设计资源 培训 开发板 精华推荐
- 使用 Cypress Semiconductor 的 CY7C9689 的参考设计
- ADR430B 2.048 Vout 超低噪声 XFET 电压基准的典型应用,具有灌电流和拉电流能力
- Frame_PCB-voron0
- NCP301LSN18T1 1.8V 电压检测器的典型应用,用于具有附加迟滞的微处理器复位电路
- 【创意PCB】玉兔剪PCB
- TCR5SB33U、200mA、3.3V输出电压CMOS低压降稳压器的典型应用
- TAR5S16U 点稳压器(低压差稳压器)的典型应用
- MC33269 800mA 可调输出、低压降稳压器的典型应用
- AM1G-1212DH30Z ±12V 1 瓦 DC/DC 转换器的典型应用
- 使用 ROHM Semiconductor 的 BD4846 的参考设计