Chinaunix首页 | 论坛 | 博客
  • 博客访问: 95217
  • 博文数量: 38
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 384
  • 用 户 组: 普通用户
  • 注册时间: 2014-04-06 16:52
文章分类

全部博文(38)

文章存档

2014年(38)

我的朋友

分类: 嵌入式

2014-05-16 15:49:46

DMA是一个独立的模块存在于处理器,DMA是不通过CPU而是直接访问内存,使用DMA,可以释放CPU的"压力",使得它不会一直在做一件事,使用了DMA也能达到直接使用CPU的效果

DMA的编写套路:
    1. 注册DMA中断,分配缓冲区
    2. 注册字符设备,并提供文件操作集合fops
    3. 硬件相关操作 

几点:
    1.cat /proc/interrupts 发现DMA0和DMA1都被使用,所以这里使用SDMA1,需要设置SDMA_SEL寄存器来使能SDMA
    2.使用SDMA的寄存器要先使能时钟,否则会造成寄存器不能读写
    3.本例子只是用简单程序的拷贝来演示DMA的作用,所以采用字符设备方式编写

驱动代码如下

点击(此处)折叠或打开

  1. //moudle.h 包含了大量加载模块需要的函数和符号的定义
  2. #include <linux/module.h>
  3. //kernel.h以便使用printk()等函数
  4. #include <linux/kernel.h>
  5. //fs.h包含常用的数据结构,如struct file等
  6. #include <linux/fs.h>
  7. //uaccess.h 包含copy_to_user(),copy_from_user()等函数
  8. #include <linux/uaccess.h>
  9. //io.h 包含inl(),outl(),readl(),writel()等IO口操作函数
  10. #include <linux/io.h>
  11. #include <linux/miscdevice.h>
  12. #include <linux/pci.h>
  13. //init.h来指定你的初始化和清理函数,例如:module_init(init_function)、module_exit(cleanup_function)
  14. #include <linux/init.h>
  15. #include <linux/delay.h>
  16. #include <linux/device.h>
  17. #include <linux/cdev.h>
  18. #include <linux/gpio.h>
  19. #include <linux/irq.h>
  20. #include <linux/sched.h>
  21. #include <linux/interrupt.h>
  22. #include <linux/poll.h>
  23. //irq.h中断与并发请求事件
  24. #include <asm/irq.h>
  25. //下面这些头文件是IO口在内核的虚拟映射地址,涉及IO口的操作所必须包含
  26. //#include <mach/gpio.h>
  27. #include <mach/regs-gpio.h>
  28. #include <plat/gpio-cfg.h>
  29. #include <mach/hardware.h>
  30. #include <mach/map.h>
  31. #include <linux/clk.h>

  32. #define MEM_CPY_NO_DMA 0
  33. #define MEM_CPY_DMA 1

  34. static int major = 0;
  35. static struct class *cls;

  36. static char *src; //
  37. static u32 src_phys; //源的物理地址

  38. static char *dst; //目的
  39. static u32 dst_phys; //目的的物理地址

  40. #define BUF_SIZE (512*1024)

  41. #define DMAC0_BASE_ADDR 0x75000000
  42. #define DMAC1_BASE_ADDR 0x75100000
  43. #define SDMAC0_BASE_ADDR 0x7DB00000
  44. #define SDMAC1_BASE_ADDR 0x7DC00000
  45. struct s3c_dma_regs {            //dma寄存器
  46.     unsigned long DMACIntStatus;
  47.     unsigned long DMACIntTCStatus;
  48.     unsigned long DMACIntTCClear;
  49.     unsigned long DMACIntErrorStatus;
  50.     unsigned long DMACIntErrClr;
  51.     unsigned long DMACRawIntTCStatus;
  52.     unsigned long DMACRawIntErrorStatus;
  53.     unsigned long DMACEnbldChns;
  54.     unsigned long DMACSoftBReq;
  55.     unsigned long DMACSoftSReq;
  56.     unsigned long reserved1[2];
  57.     unsigned long DMACConfiguration;
  58.     unsigned long DMACSync;
  59.     unsigned long reserved2[50];
  60.         unsigned long DMACC0SrcAddr;
  61.     unsigned long DMACC0DestAddr;
  62.     unsigned long DMACC0LLI;
  63.     unsigned long DMACC0Control0;
  64.     unsigned long DMACC0Control1;
  65.     unsigned long DMACC0Configuration;
  66.     unsigned long DMACC0ConfigurationExp;
  67.     unsigned long reserved3;
  68.         unsigned long DMACC1SrcAddr;
  69.     unsigned long DMACC1DestAddr;
  70.     unsigned long DMACC1LLI;
  71.     unsigned long DMACC1Control0;
  72.     unsigned long DMACC1Control1;
  73.     unsigned long DMACC1Configuration;
  74.     unsigned long DMACC1ConfigurationExp;
  75.     unsigned long reserved4;
  76.         unsigned long DMACC2SrcAddr;
  77.     unsigned long DMACC2DestAddr;
  78.     unsigned long DMACC2LLI;
  79.     unsigned long DMACC2Control0;
  80.     unsigned long DMACC2Control1;
  81.     unsigned long DMACC2Configuration;
  82.     unsigned long DMACC2ConfigurationExp;
  83.     unsigned long reserved5;
  84.         unsigned long DMACC3SrcAddr;
  85.     unsigned long DMACC3DestAddr;
  86.     unsigned long DMACC3LLI;
  87.     unsigned long DMACC3Control0;
  88.     unsigned long DMACC3Control1;
  89.     unsigned long DMACC3Configuration;
  90.     unsigned long DMACC3ConfigurationExp;
  91.     unsigned long reserved6;
  92.         unsigned long DMACC4SrcAddr;
  93.     unsigned long DMACC4DestAddr;
  94.     unsigned long DMACC4LLI;
  95.     unsigned long DMACC4Control0;
  96.     unsigned long DMACC4Control1;
  97.     unsigned long DMACC4Configuration;
  98.     unsigned long DMACC4ConfigurationExp;
  99.     unsigned long reserved7;
  100.         unsigned long DMACC5SrcAddr;
  101.     unsigned long DMACC5DestAddr;
  102.     unsigned long DMACC5LLI;
  103.     unsigned long DMACC5Control0;
  104.     unsigned long DMACC5Control1;
  105.     unsigned long DMACC5Configuration;
  106.     unsigned long DMACC5ConfigurationExp;
  107.     unsigned long reserved8;
  108.         unsigned long DMACC6SrcAddr;
  109.     unsigned long DMACC6DestAddr;
  110.     unsigned long DMACC6LLI;
  111.     unsigned long DMACC6Control0;
  112.     unsigned long DMACC6Control1;
  113.     unsigned long DMACC6Configuration;
  114.     unsigned long DMACC6ConfigurationExp;
  115.     unsigned long reserved9;
  116.         unsigned long DMACC7SrcAddr;
  117.     unsigned long DMACC7DestAddr;
  118.     unsigned long DMACC7LLI;
  119.     unsigned long DMACC7Control0;
  120.     unsigned long DMACC7Control1;
  121.     unsigned long DMACC7Configuration;
  122.     unsigned long DMACC7ConfigurationExp;    
  123. };

  124. static volatile struct s3c_dma_regs *dma_regs;
  125. static volatile unsigned long *SDMA_SEL = NULL; //系统控制器的安全 DMA 控制寄存器

  126. static DECLARE_WAIT_QUEUE_HEAD(dma_waitq);
  127. /* 中断事件标志, 中断服务程序将它置1,ioctl将它清0 */
  128. static volatile int ev_dma = 0;

  129. static long s3c_dma_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  130. {
  131.     int i;

  132.     memset(src, 0xAA, BUF_SIZE);//把源地址设为0xAA,长度BUF_SIZE
  133.     memset(dst, 0x55, BUF_SIZE);//把目的地址设为0x55,长度BUF_SIZE
  134.     
  135.     switch (cmd)
  136.     {
  137.         case MEM_CPY_NO_DMA://不使用DMA
  138.         {
  139.             for (i = 0; i < BUF_SIZE; i++)
  140.                 dst[i] = src[i];
  141.             if (memcmp(src, dst, BUF_SIZE) == 0)
  142.                 printk("MEM_CPY_NO_DMA OK\n");
  143.             else
  144.                 printk("MEM_CPY_NO_DMA ERROR\n");
  145.             break;
  146.         }
  147.         
  148.         case MEM_CPY_DMA://使用DMA
  149.         {
  150.             ev_dma = 0;

  151.             /*开总的DMA 使能*/
  152.             dma_regs->DMACConfiguration = (1<<0);
  153.             
  154.             /*通过写 DMACIntTCClr 和 DMACIntErrClr 寄存器,清除通道中要用到的未处理的中断。先前的通道操作可能使剩余的中断有效。*/
  155.             dma_regs->DMACIntTCClear = 0x11;
  156.             dma_regs->DMACIntErrClr = 0x11;
  157.             
  158.             /* 如果外设的工作时钟与DMA控制器的时钟不相同, 要使能"同步逻辑" */
  159.             dma_regs->DMACSync = 0x0;
  160.             
  161.             /*选用通道7*/
  162.             /*写源地址到 DMACCxSrcAddr 寄存器中*/
  163.             dma_regs->DMACC7SrcAddr = src_phys;
  164.             
  165.             /*写目标址到 DMACCxDestAddr 寄存器中*/
  166.             dma_regs->DMACC7DestAddr = dst_phys;
  167.             
  168.             /* 不使用 linked list */
  169.             dma_regs->DMACC7LLI = 0;
  170.             
  171.             /*写DMACC7Control0和DMACC7Control1寄存器,设置DMA通道控制信息*/
  172.             dma_regs->DMACC7Control0 = (2 << 18) //源传输宽度,一次传输4字节(32bit)数据
  173.                         |(2 << 21) //目的传输宽度,一次传输4字节(32bit)数据
  174.                         |(1 << 26) //源增量。每个传输后,随设置的源地址递增
  175.                         |(1 << 27) //目标增量。每个传输后,随设置的目标地址递增。
  176.                         |(1 << 28) //保护
  177.                         |(1 << 31); //终端计数中断启动位。控制是否当前的 LLI 期望引发终端计数中断
  178.                 
  179.             dma_regs->DMACC7Control1 = BUF_SIZE; //写入拷贝的size;
  180.             
  181.             dma_regs->DMACC7Configuration |= (0<<18) // enable DMA requests
  182.                         | (0<<16) // disables locked transfers
  183.                                            | (1<<15) // Teminal count interrupt enable
  184.                                            | (1<<14) // Interrupt error mask
  185.                                            | (0<<13)
  186.                                            | (0<<12)
  187.                                            | (0<<11) //传输模式,000表示内存到内存
  188.                         | (1<<0); //开channel7 DMA             
  189.                         
  190.             /* 如何知道DMA什么时候完成? */
  191.             /* 休眠 */
  192.             wait_event_interruptible(dma_waitq, ev_dma);

  193.             if (memcmp(src, dst, BUF_SIZE) == 0)
  194.             {
  195.                 printk("MEM_CPY_DMA OK\n");
  196.             }
  197.             else
  198.             {
  199.                 printk("MEM_CPY_DMA ERROR\n");
  200.                 //s3c_dma_ioctl(file, cmd, arg);
  201.             }
  202.             
  203.             break;
  204.         }
  205.     }
  206.     
  207.     return 0;
  208. }

  209. static irqreturn_t s3c_dma_irq(int irq, void *devid)
  210. {
  211.     /* 唤醒 */
  212.     ev_dma = 1;    
  213.     dma_regs->DMACC7Configuration &= ~(1<<15);//屏蔽终端计数中断        
  214.     wake_up_interruptible(&dma_waitq); /* 唤醒休眠的进程 */
  215.     
  216.     return IRQ_HANDLED;
  217. }

  218. static struct file_operations dma_fops = {
  219.     .owner = THIS_MODULE,
  220.     .unlocked_ioctl = s3c_dma_ioctl,
  221. };

  222. static int s3c_dma_init(void)
  223. {
  224.     struct clk *clk;    
  225.     
  226.     if (request_irq(IRQ_SDMA1, s3c_dma_irq, 0, "s3c_dma", 1))
  227.     {
  228.         printk("can't request_irq for DMA\n");
  229.         return -EBUSY;
  230.     }
  231.         
  232.     /*分配SRC, DST对应的缓冲区*/
  233.     src = dma_alloc_writecombine(NULL, BUF_SIZE, &src_phys, GFP_KERNEL);
  234.     /*不能用kmalloc函数,因为kmalloc函数返回的虚拟地址虽然是连续的,但物理地址不一定连续,而dma不能处理不连续的物理地址*/
  235.     
  236.     if (NULL == src)
  237.     {
  238.         printk("can't alloc buffer for src\n");
  239.         return -ENOMEM;
  240.     }
  241.     
  242.     dst = dma_alloc_writecombine(NULL, BUF_SIZE, &dst_phys, GFP_KERNEL);
  243.     if (NULL == dst)
  244.     {
  245.         dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);//免得内存泄漏
  246.         printk("can't alloc buffer for dst\n");
  247.         return -ENOMEM;
  248.     }    
  249.     
  250.     SDMA_SEL = (volatile unsigned long *)ioremap(0x7E00F110 , 16);//系统控制器的安全 DMA 控制寄存器
  251.     /* 使用SDMA */
  252.     *SDMA_SEL = 0x0;
  253.     
  254.     clk = clk_get(NULL, "sdma1");//struct clk *clk_get(struct device *dev, const char *id)
  255.     clk_enable(clk);//使能时钟,否则dma_regs寄存器不能读写    
  256.     dma_regs = ioremap(SDMAC1_BASE_ADDR, sizeof(struct s3c_dma_regs));//使用SDMA1
  257.     
  258.     major = register_chrdev(0, "s3c_dma", &dma_fops);
  259.     /* 为了自动创建设备节点 */
  260.     cls = class_create(THIS_MODULE, "s3c_dma");
  261.     device_create(cls, NULL, MKDEV(major, 0), NULL, "mydma"); /* /dev/mydma */
  262.     
  263.     return 0;
  264. }

  265. static void s3c_dma_exit(void)
  266. {
  267.     device_destroy(cls,MKDEV(major, 0));
  268.     class_destroy(cls);
  269.     unregister_chrdev(major, "s3c_dma");
  270.     dma_free_writecombine(NULL, BUF_SIZE, src, src_phys);
  271.     dma_free_writecombine(NULL, BUF_SIZE, dst, dst_phys);
  272.     iounmap(dma_regs);
  273.     iounmap(SDMA_SEL);
  274.     free_irq(IRQ_SDMA1, 1);
  275. }

  276. module_init(s3c_dma_init);
  277. module_exit(s3c_dma_exit);
  278. MODULE_LICENSE("GPL");

测试程序代码

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <fcntl.h>
  5. #include <sys/ioctl.h>
  6. #include <string.h>

  7. /* ./dma_test nodma
  8.  * ./dma_test dma
  9.  */
  10. #define MEM_CPY_NO_DMA 0
  11. #define MEM_CPY_DMA 1

  12. void print_usage(char *name)
  13. {
  14.     printf("Usage:\n");
  15.     printf("%s \n", name);
  16. }


  17. int main(int argc, char **argv)
  18. {
  19.     int fd;
  20.     
  21.      if (argc != 2)
  22.     {
  23.         print_usage(argv[0]);
  24.         return -1;
  25.     }

  26.     fd = open("/dev/mydma", O_RDWR);
  27.     if (fd < 0)
  28.     {
  29.         printf("can't open /dev/mydma\n");
  30.         return -1;
  31.     }

  32.     if (strcmp(argv[1], "nodma") == 0)
  33.     {
  34.         while (1)
  35.         {
  36.             ioctl(fd, MEM_CPY_NO_DMA);
  37.         }
  38.     }
  39.     else if (strcmp(argv[1], "dma") == 0)
  40.     {
  41.         while (1)
  42.         {
  43.             ioctl(fd, MEM_CPY_DMA);
  44.         }
  45.     }
  46.     else
  47.     {
  48.         print_usage(argv[0]);
  49.         return -1;
  50.     }
  51.     return 0;     
  52. }




阅读(1790) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

presssoft2014-08-10 19:00:31

期待你的大作!