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

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

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

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

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

  进入Device Drivers  --->

      Character devices  --->

   第四十二天:Tiny4412模块驱动开发_第1张图片

   将LED Support for FriendlyARM Tiny4412 GPIO LEDs前的去掉。
    保存退出后执行make 重新编译内核,用重新生成的内核镜像启动Tiny4412.
   编写裸板驱动和编写模块驱动的区别在于,裸板程序直接操作的是物理内存,而模块程序操作的是虚拟内存,模块程序要操作硬件,肯定是要通过物理地址来操作相应的寄存器的值。这时候,就是要通过ioremap()函数,实现物理地址(IO地址空间)到虚拟地址的转换。下面是具体程序:插入模块时灯全亮。
 1 #include 
 2 #include 
 3 #include 
 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 
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 7 #include 
 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 }
module_init(test_init);
module_exit(test_exit);

irqreturn_t do_irq(int irq, void *data)
{
    printk("wang wang wang\n");
    return IRQ_HANDLED;
}










    编译成模块插入后显示:

  第四十二天:Tiny4412模块驱动开发_第2张图片

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

  

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

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

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

 1 #include 
 2 #include 
 3 #include 
 4 #include 
 5 #include 
 6 #include 
 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宏来表示按键。

     

            

  

 
 
 

  

你可能感兴趣的:(Linux)