一:ioremap
在内核中有关与物理地址到虚拟地址的映射全都是有mmu,统一开启,而物理地址到虚拟地址的映射关系全都存在一张对应的表格里面,这张表,在开启mmu的时候一起建好,比如在建表的时候是将物理地址0x11111111映射到44444444,那么问题就是: 比如以后我们如果要将0x11111111的地址映射到66666666地址怎么办?在内核中,通过特定的物理地址到虚拟地址的自动对应映射通过ioremap()函数来实现
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 23 24 MODULE_LICENSE("GPL"); 25 MODULE_AUTHOR("bunfly"); 26 27 unsigned long gpio_virt = 0; 28 29 int test_init() 30 { 31 void __iomem *p; 32 33 p = request_mem_region(0x11000000,SZ_4K,"gpio");//注册内存的映射信息 34 if(p==NULL) 35 { 36 printk("request error!\n"); 37 return 1; 38 } 39 40 gpio_virt = ioremap(0x11000000,SZ_4K); 41 //ioremap物理内存到虚拟内存映射 42 *(unsigned long *)(gpio_virt + 0x2e0) =1; 43 *(unsigned long *)(gpio_virt + 0x2e4) =0; 44 printk("hello led!\n"); 45 46 printk("phys: %p\n",virt_to_phys(gpio_virt)); 47 48 return 0; 49 } 50 51 void test_exit() 52 { 53 printk("bye bye!\n"); 54 iounmap(gpio_virt); 55 release_mem_region(0x11000000,SZ_4K); 56 //退出的时候将映射的虚拟内存取消 57 } 58 59 module_init(test_init); 60 module_exit(test_exit); 61 62 63 64
以上是通过映射的方法,把物理内存映射到虚拟内存,然后通过操作虚拟内存来点灯
内核在分配的时候,注册分配的名字,其中的注册信息可以在板子的目录下面查看:
cat /proc/iomem
注意:
在模块运行之前,为了消除内核中自带的LED模块的影响,需要将自带的LED模块:
在linux_3 下make menuconfig
在保存退出之后,重新make成zImage文件,下载到板子,运行。
二:内部watch看门狗中断
内核中的狗中断和裸板的狗中断原理都差不多,只是把物理地址映射到虚拟地址而已,下面是通过狗中断来操作蜂鸣器,而且定时打印一句话,在操作之前,需要线把内核自带的watch dog模块裁剪掉,要不然会出现错误!
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 23 MODULE_LICENSE("GPL"); 24 MODULE_AUTHOR("bunfly"); 25 26 void led_on(void); 27 void led_off(void); 28 void pwm_on(void); 29 void pwm_off(void); 30 31 irqreturn_t do_irq(int irq, void *data); 32 33 unsigned long wdt_virt; 34 unsigned long gpiobase_led; 35 unsigned long gpiobase_pwm; 36 unsigned long *wtcon, *wtdat, *wtcnt, *wtclrint; 37 unsigned long *GPMCON, *GPMDAT; 38 unsigned long *GPD0CON, *GPD0DAT; 39 struct clk *k; 40 41 int test_init() 42 { 43 int ret = 0; 44 ret = request_irq(IRQ_WDT, do_irq, IRQF_SHARED, "bunflydog", 0x1111); 45 //注册中断 中断号 处理中断函数回调函数, 46 if(ret < 0){ 47 printk("request_irq\n"); 48 return 1; 49 } 50 51 k = clk_get(NULL, "watchdog"); 52 clk_enable(k); 53 //频率 54 55 wdt_virt = ioremap(0x10060000, SZ_4K); 56 wtcon = wdt_virt + 0x00; 57 wtdat = wdt_virt + 0x04; 58 wtcnt = wdt_virt + 0x08; 59 wtclrint = wdt_virt + 0x0c; 60 //映射物理地址到虚拟地址 61 62 gpiobase_pwm = ioremap(0x11400000, SZ_4K); 63 GPD0CON = (gpiobase_pwm + 0x00a0); 64 GPD0DAT = (gpiobase_pwm + 0x00a4); 65 //映射蜂鸣器的物理地址 66 67 gpiobase_led = ioremap(0x11000000, SZ_4K); 68 GPMCON = (gpiobase_led + 0x02e0); 69 GPMDAT = (gpiobase_led + 0x02e4); 70 //映射LED的物理地址 71 72 *wtcon = 0 | (1 << 2) | (3 << 3) | (1 << 5) | (50 << 8); 73 *wtdat = 0x8000; 74 *wtcnt = 0x8000; 75 //配置寄存器,将狗打开 76 77 return 0; 78 } 79 80 void pwm_on(void) 81 { 82 //*GPD0CON &= ~0xffff; 83 *GPD0CON = 0x1;//配置寄存器为2 84 *GPD0DAT = 0x1;//date=0xf 85 } 86 87 void pwm_off(void) 88 { 89 //*GPD0CON &= ~0xffff; 90 *GPD0CON = 0x0; 91 } 92 93 void led_on(void) 94 { 95 *GPMCON &= ~0xffff; 96 *GPMCON |= 0x1111;//配置寄存器3-0-----3-3全为1111,全为输出模式 97 *GPMDAT &= ~0x1;//打开置0-4位为0000 98 } 99 void led_off(void) 100 { 101 *GPMCON &= ~0xffff;//清零 102 *GPMCON |= 0x0000;//0---3位清零 103 *GPMDAT |= 0x0;//date=0xf关闭置一 104 } 105 void test_exit() 106 { 107 printk("exit\n"); 108 } 109 110 module_init(test_init); 111 module_exit(test_exit); 112 113 irqreturn_t do_irq(int irq, void *data) 114 { 115 printk("wang wang wang\n"); 116 static int flag=1; 117 if(flag) 118 { 119 pwm_on(); 120 led_on(); 121 flag=0; 122 } 123 else if(flag == 0) 124 { 125 pwm_off(); 126 led_off(); 127 flag=1; 128 } 129 130 *wtclrint = 250; 131 //消除中断 132 return IRQ_HANDLED; 133 //返回处理的中断标志 134 }
通过在板子下 cat /pro/interrupt 文件可以看到目前的中断信息:可以看到,在文件中注册的 bunfly dog 定时狗中断:
三 内核外部中断:
在内核中,外部中断与裸板的外部中断处理差不多,但是与狗中断处理不同的是,开始的时候不需要提前获得中断号,而在外部中断处理之后,系统自动清中断,以下是内核中断代码:
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 #include <linux/irq.h> 23 #include <linux/interrupt.h> 24 25 #include <asm/irq.h> 26 #include <asm/ptrace.h> 27 #include <asm/irq_regs.h> 28 29 MODULE_LICENSE("GPL"); 30 MODULE_AUTHOR("bunfly"); 31 32 void led_on(void); 33 void led_off(void); 34 void pwm_on(void); 35 void pwm_off(void); 36 37 irqreturn_t do_irq(int irq, void *data); 38 39 unsigned long wdt_virt; 40 unsigned long gpiobase_led; 41 unsigned long gpiobase_pwm; 42 unsigned long *wtcon, *wtdat, *wtcnt, *wtclrint; 43 unsigned long *GPMCON, *GPMDAT; 44 unsigned long *GPD0CON, *GPD0DAT; 45 struct clk *k; 46 47 int test_init() 48 { 49 int extern_irq=gpio_to_irq(EXYNOS4_GPX3(2)); 50 //获得外部中断号码 51 int ret = 0; 52 //ret = request_irq(IRQ_WDT, do_irq, IRQF_SHARED, "bunflydog", 0x1111); 53 ret = request_irq(extern_irq, do_irq,IRQ_TYPE_EDGE_FALLING, "exter_interrupt", 0x1111); 54 //注册中断 中断号 处理中断函数回调函数, 55 if(ret < 0){ 56 printk("request_irq\n"); 57 return 1; 58 } 59 60 //k = clk_get(NULL, "watchdog"); 61 //clk_enable(k); 62 //wdt_virt = ioremap(0x10060000, SZ_4K); 63 //wtcon = wdt_virt + 0x00; 64 //wtdat = wdt_virt + 0x04; 65 //wtcnt = wdt_virt + 0x08; 66 //wtclrint = wdt_virt + 0x0c; 67 ////映射物理地址到虚拟地址 68 69 gpiobase_pwm = ioremap(0x11400000, SZ_4K); 70 GPD0CON = (gpiobase_pwm + 0x00a0); 71 GPD0DAT = (gpiobase_pwm + 0x00a4); 72 //映射蜂鸣器的物理地址 73 74 gpiobase_led = ioremap(0x11000000, SZ_4K); 75 GPMCON = (gpiobase_led + 0x02e0); 76 GPMDAT = (gpiobase_led + 0x02e4); 77 //映射LED的物理地址 78 79 // *wtcon = 0 | (1 << 2) | (3 << 3) | (1 << 5) | (50 << 8); 80 // *wtdat = 0x8000; 81 // *wtcnt = 0x8000; 82 // //配置寄存器,将狗打开 83 84 return 0; 85 } 86 87 void pwm_on(void) 88 { 89 //*GPD0CON &= ~0xffff; 90 *GPD0CON = 0x1;//配置寄存器为2 91 *GPD0DAT = 0x1;//date=0xf 92 } 93 94 void pwm_off(void) 95 { 96 //*GPD0CON &= ~0xffff; 97 *GPD0CON = 0x0; 98 } 99 100 void led_on(void) 101 { 102 *GPMCON &= ~0xffff; 103 *GPMCON |= 0x1111;//配置寄存器3-0-----3-3全为1111,全为输出模式 104 *GPMDAT &= ~0x1;//打开置0-4位为0000 105 } 106 void led_off(void) 107 { 108 *GPMCON &= ~0xffff;//清零 109 *GPMCON |= 0x0000;//0---3位清零 110 *GPMDAT |= 0x0;//date=0xf关闭置一 111 } 112 void test_exit() 113 { 114 printk("exit\n"); 115 } 116 117 module_init(test_init); 118 module_exit(test_exit); 119 120 irqreturn_t do_irq(int irq, void *data) 121 { 122 printk("irq is %d\n",irq); 123 printk("extern!\n"); 124 static int flag=1; 125 if(flag) 126 { 127 pwm_on(); 128 led_on(); 129 flag=0; 130 } 131 else if(flag == 0) 132 { 133 pwm_off(); 134 led_off(); 135 flag=1; 136 } 137 138 //*wtclrint = 250; 139 ////消除中断 140 //return IRQ_HANDLED; 141 //返回处理的中断标志 142 } 143 144 145 146
运行成功后,可以看到中断号:
以下是自己选做的作业,通过外部中断控制内部狗中断实现点灯和蜂鸣器驱动:
1 #include <linux/init.h> 2 #include <linux/thread_info.h> 3 #include <linux/module.h> 4 #include <linux/sched.h> 5 #include <linux/errno.h> 6 #include <linux/kernel.h> 7 #include <linux/module.h> 8 #include <linux/slab.h> 9 #include <linux/input.h> 10 #include <linux/init.h> 11 #include <linux/serio.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/miscdevice.h> 15 #include <linux/io.h> 16 #include <linux/ioport.h> 17 #include <asm/uaccess.h> 18 19 #include <linux/gpio.h> 20 #include <mach/gpio.h> 21 #include <plat/gpio-cfg.h> 22 #include <linux/irq.h> 23 #include <linux/interrupt.h> 24 25 #include <asm/irq.h> 26 #include <asm/ptrace.h> 27 #include <asm/irq_regs.h> 28 29 MODULE_LICENSE("GPL"); 30 MODULE_AUTHOR("bunfly"); 31 32 void led_on(void); 33 void led_off(void); 34 void pwm_on(void); 35 void pwm_off(void); 36 37 irqreturn_t do_irq(int irq, void *data); 38 39 unsigned long wdt_virt; 40 unsigned long gpiobase_led; 41 unsigned long gpiobase_pwm; 42 unsigned long *wtcon, *wtdat, *wtcnt, *wtclrint; 43 unsigned long *GPMCON, *GPMDAT; 44 unsigned long *GPD0CON, *GPD0DAT; 45 struct clk *k; 46 int extern_irq; 47 48 int test_init() 49 { 50 extern_irq=gpio_to_irq(EXYNOS4_GPX3(2)); 51 //获得外部中断号码 52 int ret = 0; 53 ret = request_irq(IRQ_WDT, do_irq, IRQF_SHARED, "bunflydog", 0x1111); 54 //注册狗中断 55 ret = request_irq(extern_irq, do_irq,IRQ_TYPE_EDGE_FALLING, "exter_interrupt", 0x1111); 56 //注册外部中断 中断号 处理中断函数回调函数, 57 if(ret < 0){ 58 printk("request_irq\n"); 59 return 1; 60 } 61 62 k = clk_get(NULL, "watchdog"); 63 clk_enable(k); 64 65 wdt_virt = ioremap(0x10060000, SZ_4K); 66 wtcon = wdt_virt + 0x00; 67 wtdat = wdt_virt + 0x04; 68 wtcnt = wdt_virt + 0x08; 69 wtclrint = wdt_virt + 0x0c; 70 ////映射物理地址到虚拟地址 71 72 gpiobase_pwm = ioremap(0x11400000, SZ_4K); 73 GPD0CON = (gpiobase_pwm + 0x00a0); 74 GPD0DAT = (gpiobase_pwm + 0x00a4); 75 //映射蜂鸣器的物理地址 76 77 gpiobase_led = ioremap(0x11000000, SZ_4K); 78 GPMCON = (gpiobase_led + 0x02e0); 79 GPMDAT = (gpiobase_led + 0x02e4); 80 //映射LED的物理地址 81 82 return 0; 83 } 84 85 void pwm_on(void) 86 { 87 //*GPD0CON &= ~0xffff; 88 *GPD0CON = 0x1;//配置寄存器为2 89 *GPD0DAT = 0x1;//date=0xf 90 } 91 92 void pwm_off(void) 93 { 94 //*GPD0CON &= ~0xffff; 95 *GPD0CON = 0x0; 96 } 97 98 void led_on(void) 99 { 100 *GPMCON &= ~0xffff; 101 *GPMCON |= 0x1111;//配置寄存器3-0-----3-3全为1111,全为输出模式 102 *GPMDAT &= ~0x1;//打开置0-4位为0000 103 } 104 void led_off(void) 105 { 106 *GPMCON &= ~0xffff;//清零 107 *GPMCON |= 0x0000;//0---3位清零 108 *GPMDAT |= 0x0;//date=0xf关闭置一 109 } 110 void test_exit() 111 { 112 printk("exit\n"); 113 } 114 115 module_init(test_init); 116 module_exit(test_exit); 117 118 irqreturn_t do_irq(int irq, void *data) 119 { 120 121 if(irq == extern_irq)//如果按键中断 122 { 123 printk("key 1 down\n"); 124 static int flag=1; 125 if(flag) 126 { 127 printk("wtc_on\n"); 128 // wtc_on(); 129 130 *wtcon = 0 | (1 << 2) | (3 << 3) | (1 << 5) | (50 << 8); 131 *wtdat = 0x8000; 132 *wtcnt = 0x8000; 133 //打开狗 134 // 135 flag=0; 136 } 137 else if(flag == 0) 138 { 139 printk("wtc_off\n"); 140 141 *wtcon = 0 ; 142 //关狗 143 led_off(); 144 pwm_off(); 145 flag=1; 146 } 147 148 } 149 if(irq == IRQ_WDT)//如果DOG中断 150 { 151 printk("dog dog dog \n"); 152 static int flag=1; 153 if(flag) 154 { 155 led_on(); 156 pwm_on(); 157 flag=0; 158 } 159 else 160 { 161 led_off(); 162 pwm_off(); 163 flag=1; 164 } 165 *wtclrint = 250; 166 } 167 return IRQ_HANDLED; 168 }
在内核环境构建中,很多与裸板驱动相同,都跳到函数do_irq()中处理中断,所以,在do_irq中和拷贝裸板的驱动。