首页 > 代码库 > 第四十二天:Tiny4412模块驱动开发

第四十二天:Tiny4412模块驱动开发

   因为前面写裸板程序的时候,已经详细的叙述过LED灯的控制,按键的控制,以及watchdog的配置,这里就不赘述了,主要是说明模块如何控制底层硬件的。

  第一个程序是模块程序控制LED灯全亮。 

  因为友善之臂将LED灯的驱动默认加载到内核中,编写模块驱动程序前就要先把原先的LED灯驱动裁剪掉。

  首先进入linux源码目录。执行 make menuconfig 

  进入Device Drivers  --->

      Character devices  --->

   技术分享

   将LED Support for FriendlyARM Tiny4412 GPIO LEDs前的去掉。
    保存退出后执行make 重新编译内核,用重新生成的内核镜像启动Tiny4412.
   编写裸板驱动和编写模块驱动的区别在于,裸板程序直接操作的是物理内存,而模块程序操作的是虚拟内存,模块程序要操作硬件,肯定是要通过物理地址来操作相应的寄存器的值。这时候,就是要通过ioremap()函数,实现物理地址(IO地址空间)到虚拟地址的转换。下面是具体程序:插入模块时灯全亮。
 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <asm/io.h> 4  5 MODULE_LICENSE("GPL"); 6 MODULE_AUTHOR("BUNFLY"); 7  8 unsigned long gpio_virt; 9 unsigned long *gpm4con, *gpm4dat;10 11 int test_init()12 {13     gpio_virt = ioremap(0x110002e0 & ~0xfff, SZ_4K);//映射4k空间14     gpm4con = gpio_virt + (0x110002e0 & 0xfff);15     gpm4dat = gpio_virt + (0x110002e4 & 0xfff);16     17     *gpm4con = 0x1111;18     *gpm4dat = 0;19 20     return 0;21 }22 23 void test_exit()24 {25     iounmap(gpio_virt);26     printk("bye bye\n");27 }28 29 module_init(test_init);30 module_exit(test_exit);

      实现了LED灯的控制后,现在要做的是通过watchdog中断实现LED的闪烁。

  编写驱动程序之前,要先查看linux内核中是否有watchdog的驱动,在之前的学习中,我们得知lwatchdog的中断号是75,那么我们就使用

 cat proc/interrupts  | grep 75 命令查看。

  技术分享

    由上图可以知道内核已经有watchdog驱动,现在我们要做的事情就是裁剪内核。按照下面目录找到S3C2410 Watchdog,将前面的*去掉。

 Device Drivers  --->

  Watchdog Timer Support  --->

        <>S3C2410 Watchdog

用重新生成的内核来启动开发板,这时候内核中就不包含watchdog驱动了。

  技术分享

  现在就通过watchdog每隔一段时间来打印一句话。

 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <asm/io.h> 4 #include <linux/irq.h> 5 #include <linux/interrupt.h> 6 #include <linux/gpio.h> 7 #include <linux/clk.h> 8  9 MODULE_LICENSE("GPL");10 MODULE_AUTHOR("BUNFLY");11 12 unsigned long wdt_virt;13 unsigned long *wtcon, *wtcnt, *wtdat, *wtclrint;14 struct clk *wtclk;15 16 irqreturn_t do_irq(int irq, void *data);17 18 int test_init()19 {20     wdt_virt = ioremap(0x10060000, SZ_4K);21     wtcon = wdt_virt + 0x00;22     wtdat = wdt_virt + 0x04;23     wtcnt = wdt_virt + 0x08;24     wtclrint = wdt_virt + 0x0c;25     26     int ret = request_irq(IRQ_WDT, do_irq, 27         0, "wangcai", "hahah");28     if(ret < 0){29         printk("request irq\n");30         return 1;31     }32     33     wtclk = clk_get(NULL, "watchdog");34     clk_enable(wtclk);35     36     *wtcon = 0 | (1 << 2) | (2 << 3) | (1 << 5) | (50 << 8);37     *wtcnt = 0x8000;38     *wtdat = 0x8000;39     printk("wdt set ok\n");40 41     return 0;42 }43 44 void test_exit()45 {46     clk_disable(wtclk);47     clk_put(wtclk);48     49     free_irq(IRQ_WDT, "hahah");50     iounmap(wdt_virt);51     printk("bye bye\n");52 }

    编译成模块插入后显示:

  技术分享

这时候在/proc/interrupts 文件中就有下面一行:

技术分享  

    首先是将配置看门口的寄存器的物理地址映射到CPU能访问到的存储器地址中。在使用request_irq函数注册中断。request_irq是一个回调函数,当中断发生的时候,就会去do_irq函数中执行,request_irq的最后一个参数是do_irq函数的data参数。

    第三十三行和三十四行是配置时序。

  最后一个程序是按键驱动程序。

 1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <asm/io.h> 4 #include <linux/irq.h> 5 #include <linux/interrupt.h> 6 #include <linux/gpio.h> 7  8 MODULE_LICENSE("GPL"); 9 MODULE_AUTHOR("BUNFLY");10 11 irqreturn_t do_irq(int irq, void *data);12 13 int test_init()14 {15     int irq = gpio_to_irq(EXYNOS4_GPX3(2));16     int ret = request_irq(irq, do_irq, 17         IRQ_TYPE_EDGE_FALLING, "key1", "hahah");18     if(ret < 0){19         printk("request irq\n");20         return 1;21     }22 23     return 0;24 }25 26 void test_exit()27 {28     free_irq(gpio_to_irq(EXYNOS4_GPX3(2)), "hahah");29     printk("bye bye\n");30 }31 32 module_init(test_init);33 module_exit(test_exit);34 35 irqreturn_t do_irq(int irq, void *data)36 {37     printk("key 1 down\n");38     return IRQ_HANDLED;39 }

      这时候我们没有自己定义寄存器的地址,而是直接调用linux内核提供的EXYNOS4_GPX3宏来表示按键。

     

            

  

 
 
 

  

第四十二天:Tiny4412模块驱动开发