161 break;
162 default:
163 printk('<0>command error n');
164 printk('<0>ioctl(fd, (unsigned int)command, (unsigned long) arg;n');
165 printk('<0>command: 166 return -1; 167 } 168 return 0; 169 } 170 171 /* 这个结构是字符设备驱动程序的核心 172 * 当应用程序操作设备文件时所调用的open、read、write等函数, 173 * 最终会调用这个结构中指定的对应函数 174 */ 175 static struct file_operations key_fops = { 176 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 177 .open = key_open, 178 .read = key_read, 179 .write = key_write, 180 .release= key_release, 181 .ioctl = key_ioctl, 182 }; 183 184 /* 185 * 执行insmod命令时就会调用这个函数 186 */ 187 static int __init key_irq_init(void) 188 { 189 printk('<0>nHello,this is %s module!nn',Driver_NAME); 190 //register and mknod 191 major = register_chrdev(0,Driver_NAME,&key_fops); 192 drv_class = class_create(THIS_MODULE,Driver_NAME); 193 drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME); /*/dev/key_query*/ 194 195 //set all pins to GPIO mod ALF5 196 mxc_request_iomux(GPIO2_21, MUX_CONFIG_ALT5); 197 mxc_request_iomux(GPIO3_15, MUX_CONFIG_ALT5); 198 mxc_request_iomux(GPIO2_10, MUX_CONFIG_ALT5); 199 mxc_request_iomux(GPIO2_11, MUX_CONFIG_ALT5); 200 mxc_request_iomux(GPIO2_8, MUX_CONFIG_ALT5); 201 mxc_request_iomux(GPIO2_9, MUX_CONFIG_ALT5); 202 mxc_request_iomux(GPIO2_6, MUX_CONFIG_ALT5); 203 mxc_request_iomux(GPIO2_7, MUX_CONFIG_ALT5); 204 //request IOMUX GPIO 205 gpio_request(IOMUX_TO_GPIO(GPIO2_21), 'GPIO2_21'); 206 gpio_request(IOMUX_TO_GPIO(GPIO3_15), 'GPIO3_15'); 207 gpio_request(IOMUX_TO_GPIO(GPIO2_10), 'GPIO2_10'); 208 gpio_request(IOMUX_TO_GPIO(GPIO2_11), 'GPIO2_11'); 209 gpio_request(IOMUX_TO_GPIO(GPIO2_8), 'GPIO2_8'); 210 gpio_request(IOMUX_TO_GPIO(GPIO2_9), 'GPIO2_9'); 211 gpio_request(IOMUX_TO_GPIO(GPIO2_6), 'GPIO2_6'); 212 gpio_request(IOMUX_TO_GPIO(GPIO2_7), 'GPIO2_7'); 213 214 215 return 0; 216 } 217 218 /* 219 * 执行rmmod命令时就会调用这个函数 220 */ 221 static void __exit key_irq_exit(void) 222 { 223 printk('<0>nGoodbye,%s!nn',Driver_NAME); 224 225 unregister_chrdev(major,Driver_NAME); 226 device_unregister(drv_class_dev); 227 class_destroy(drv_class); 228 229 /* free gpios */ 230 mxc_free_iomux(GPIO2_21, MUX_CONFIG_ALT5); 231 mxc_free_iomux(GPIO3_15, MUX_CONFIG_ALT5); 232 mxc_free_iomux(GPIO2_10, MUX_CONFIG_ALT5); 233 mxc_free_iomux(GPIO2_11, MUX_CONFIG_ALT5); 234 mxc_free_iomux(GPIO2_8, MUX_CONFIG_ALT5); 235 mxc_free_iomux(GPIO2_9, MUX_CONFIG_ALT5); 236 mxc_free_iomux(GPIO2_6, MUX_CONFIG_ALT5); 237 mxc_free_iomux(GPIO2_7, MUX_CONFIG_ALT5); 238 239 gpio_free(IOMUX_TO_GPIO(GPIO2_21)); 240 gpio_free(IOMUX_TO_GPIO(GPIO3_15)); 241 gpio_free(IOMUX_TO_GPIO(GPIO2_10)); 242 gpio_free(IOMUX_TO_GPIO(GPIO2_11)); 243 gpio_free(IOMUX_TO_GPIO(GPIO2_8)); 244 gpio_free(IOMUX_TO_GPIO(GPIO2_9)); 245 gpio_free(IOMUX_TO_GPIO(GPIO2_6)); 246 gpio_free(IOMUX_TO_GPIO(GPIO2_7)); 247 248 } 249 250 /* 这两行指定驱动程序的初始化函数和卸载函数 */ 251 module_init(key_irq_init); 252 module_exit(key_irq_exit); 253 254 /* 描述驱动程序的一些信息,不是必须的 */ 255 MODULE_AUTHOR('Lover雪'); 256 MODULE_VERSION('0.1.0'); 257 MODULE_DESCRIPTION('IMX257 key Driver'); 258 MODULE_LICENSE('GPL'); 大概讲解一下中断的实现: 如上面的程序所示: 配置中断 当我们要使用中断时,第一步肯定是要配置中断。所以,在我们的ioctl函数的gpio_input命令中,如下代码主要的功能就是配置各个引脚为中断模式,下降沿触发,中断的名称为key_GPIOm_n。 还有一个就是注册中断函数,告诉内核,我们中断的处理函数为key_irq。 1 Request_irq(IOMUX_TO_IRQ(GPIO2_7), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_7', (void *)1); 2 3 request_irq(IOMUX_TO_IRQ(GPIO2_6), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_6', (void *)1); 4 5 request_irq(IOMUX_TO_IRQ(GPIO2_9), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_9', (void *)1); 6 7 request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_10', (void *)1); 8 9 request_irq(IOMUX_TO_IRQ(GPIO2_11), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_11', (void *)1); 10 11 request_irq(IOMUX_TO_IRQ(GPIO2_21), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_21', (void *)1); read函数中睡眠 中断配置好后,就开始使用中断,当我们用户程序read函数到来,而我们的按键却并没有按下时,为了节省资源,我们就可以让程序睡眠SLEEP, 1 //如果按键没有按下,没有中断,休眠 2 3 wait_event_interruptible(key_interrupt_wait,ev_press); 4 5 ev_press = 0; //清除中断时设置的ev_pre = 1 中断唤醒程序 当中断到来时,由于前面我们的程序在read函数中已经处于睡眠态了,所以我们中断的作用就是把我们的程序唤醒,在read函数中继续运行,读取相应的数据 1 /* 中断程序key_irq */ 2 3 static irqreturn_t key_irq(int irq, void *dev_id) 4 { 5 //发生了中断 6 printk('<0>function interrupt key_irq!nn'); 7 8 ev_press = 1; 9 10 wake_up_interruptible(&key_interrupt_wait); 11 return IRQ_RETVAL(IRQ_HANDLED); 12 } 接下来,附上应用程序源代码; 1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 14 #include 'mx257_gpio.h' 15 16 #define key_input 0 17 #define version 1 18 19 20 int main(int argc, char **argv) 21 { 22 int fd; 23 int i=0,cnt=0; 24 unsigned char key_vals[8]; 25 26 fd = open('/dev/key_interrupt',O_RDWR); 27 if(fd < 0){ 28 printf('can't open !!!n'); 29 } 30 ioctl(fd,version,NULL); 31 ioctl(fd,key_input,NULL); 32 while(1){ 33 read(fd, key_vals,sizeof(key_vals)); 34 printf('%04d key pressed: %d %d %d %d %d %d %d %dn',cnt++,key_vals[0],key_vals[1],key_vals[2],key_vals[3],key_vals[4],key_vals[5],key_vals[6],key_vals[7]); 35 } 36 return 0; 37 } 应用程序中,我们就不需要利用应用程序来检测是否按下,可以把检测的if判断语句去掉。 接下来的编译工作就不赘述: Insmod ./test/key_test & (这里加 & 的意思是,在后台执行) cat /proc/interrupts 结果如图所示: 查看中断,可以看到我们前面设置的中断名字。 当我们的按键按下,应用程序在后台检测到,就会打印出我们当前的引脚电平 程序编写过程中碰到的错误: error: expected '=', ',', ';', 'asm' or '__attribute__' before '{' token : 很难想象,当我们的函数名字与头文件中的函数发生冲突时,gcc报错报的不是名字冲突,而是告诉我们前面缺少了;或者括号, 因为这个问题,耗费了我足足一个下午加一个晚上的时间,事实证明,写程序时一定要仔细,否则查找错误的过程真的很痛苦,花的时间远比重新编写程序的时间多得多。 分析: 开始,我们的init函数名称为key_init, 头文件包含为linux/inerrupt.h 经过测试使用 linux/rtc.h 或者 linux/serial_reg.h 时效果一样。 编译后结果如图所示: 而当我们将函数名修改掉: 编译通过,如图所示 这里出现问题的原因时,我们的key_init函数,和我们内核中包含有free_irq的头文件中的函数发生冲突。 总结一下第一个问题: 首先,要声明并不是所有的这样的错误都是如上面的一样。 有的时候,可能真的是因为前面的代码的语法错误而导致的,具体的情况要看具体的代码,具体分析。 error: 'TASK_INTERRUPTIBLE' undeclared 第二个问题就是典型的头文件未包含的问题。 因为在sched.h 头文件中包含了它的申明。 #include #include 例外附上: 本人研究linux内核源代码时,发现很多文件中使用了 free_irq等中断申请和释放的函数,但是却未包含inrerrupt.h 的头文件,进过本人测试总结, 可以使用interrupt.h,rtc.h, seria_reg.h 达到同样的效果,因为这些头文件中包含了interrupt.h。
上一篇:【改进】IMX257实现GPIO-IRQ中断按键获取键值驱动程序
下一篇:IMX257实现GPIO-查询按键驱动程序
推荐阅读最新更新时间:2024-11-10 20:24