20150216IMX257实现GPIO-查询按键驱动程序
2015-02-16 李海沿
前面我们介绍了简单的通用字符设备驱动程序,接下来,我们在它的基础上来实现GPIO的查询按键功能。
先附上驱动程序代码
1 /****************************** 2 linux key_query 3 *****************************/ 4 #include <linux/module.h> 5 #include <linux/init.h> 6 #include <linux/kernel.h> 7 #include <linux/delay.h> 8 #include <linux/types.h> 9 #include <linux/ioctl.h> 10 #include <linux/gpio.h> 11 #include <linux/fs.h> 12 #include <linux/device.h>//包含了用于自动创建设备节点的函数device_create 13 #include <linux/uaccess.h>//包含了copy_to_user 函数等 14 15 #include "mx257_gpio.h" 16 #include "mx25_pins.h" 17 #include "iomux.h" 18 19 #define Driver_NAME "key_query" 20 #define DEVICE_NAME "key_query" 21 22 #define GPIO2_21 MX25_PIN_CLKO 23 #define GPIO3_15 MX25_PIN_EXT_ARMCLK 24 #define GPIO2_10 MX25_PIN_A24 25 #define GPIO2_11 MX25_PIN_A25 26 #define GPIO2_8 MX25_PIN_A22 27 #define GPIO2_9 MX25_PIN_A23 28 #define GPIO2_6 MX25_PIN_A20 29 #define GPIO2_7 MX25_PIN_A21 30 //command 31 #define key_input 0 32 #define version 1 33 //用于保存主设备号 34 static int major=0; 35 //用于自动创建设备节点 代替了手动敲mknod命令 36 //auto to create device node 37 static struct class *drv_class = NULL; 38 static struct class_device *drv_class_dev = NULL; 39 40 41 /* 应用程序对设备文件/dev/key_query执行open(...)时, 42 * 就会调用key_open函数*/ 43 static int key_open(struct inode *inode, struct file *file) 44 { 45 printk("<0>function open! "); 46 47 return 0; 48 } 49 /*当应用程序中read(fd,buff,sizeof(buff))时调用此key_read函数*/ 50 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 51 { 52 int ret; 53 //nt cnt=0; 54 unsigned char key_vals[8]; 55 // reading the pins value 56 key_vals[0] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_6)) ? 1 : 0; 57 key_vals[1] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_7)) ? 1 : 0; 58 key_vals[2] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_8)) ? 1 : 0; 59 key_vals[3] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_9)) ? 1 : 0; 60 key_vals[4] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_10)) ? 1 : 0; 61 key_vals[5] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_11)) ? 1 : 0; 62 key_vals[6] = gpio_get_value(IOMUX_TO_GPIO(GPIO2_21)) ? 1 : 0; 63 key_vals[7] = gpio_get_value(IOMUX_TO_GPIO(GPIO3_15)) ? 1 : 0; 64 65 ret = copy_to_user(buff,key_vals,sizeof(key_vals)); 66 if(ret){ 67 ; 68 } 69 70 //printk("<0>%04d key pressed: %d %d %d %d %d %d %d %d ",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]); 71 72 return sizeof(key_vals); 73 } 74 /* 当应用程序中使用write函数时,调用此函数**/ 75 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 76 { 77 printk("<0>function write! "); 78 79 return 1; 80 } 81 82 static int key_release(struct inode *inode, struct file *filp) 83 { 84 printk("<0>function release! "); 85 return 0; 86 } 87 /* 当用户调用ioctl(fd,version,NULL);时,会进入此函数, 88 * 在SWITCH中配对command,然后执行相应的语句 89 * 注意command 一定为整数,需在前面定义*/ 90 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg) 91 { 92 printk("<0>function ioctl! "); 93 switch (command) { 94 case key_input: 95 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_21)); 96 gpio_direction_input(IOMUX_TO_GPIO(GPIO3_15)); 97 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_10)); 98 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_11)); 99 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_8)); 100 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_9)); 101 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_6)); 102 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_7)); 103 //设置所有引脚为上拉模式 104 mxc_iomux_set_pad(GPIO2_6, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 105 mxc_iomux_set_pad(GPIO2_7, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 106 //mxc_iomux_set_pad(GPIO2_8, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 107 mxc_iomux_set_pad(GPIO2_9, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 108 mxc_iomux_set_pad(GPIO2_10, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 109 mxc_iomux_set_pad(GPIO2_11, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 110 mxc_iomux_set_pad(GPIO2_21, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 111 //mxc_iomux_set_pad(GPIO3_15, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); 112 break; 113 case version: 114 printk("<0>hello,the version is 0.1.0 "); 115 break; 116 default: 117 printk("<0>command error "); 118 printk("<0>ioctl(fd, (unsigned int)command, (unsigned long) arg; "); 119 printk("<0>command: <key_input> <version> "); 120 return -1; 121 } 122 return 0; 123 } 124 125 /* 这个结构是字符设备驱动程序的核心 126 * 当应用程序操作设备文件时所调用的open、read、write等函数, 127 * 最终会调用这个结构中指定的对应函数 128 */ 129 static struct file_operations key_fops = { 130 .owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */ 131 .open = key_open, 132 .read = key_read, 133 .write = key_write, 134 .release= key_release, 135 .ioctl = key_ioctl, 136 }; 137 138 /* 139 * 执行insmod命令时就会调用这个函数 140 */ 141 static int __init key_init(void) 142 { 143 printk("<0> Hello,this is %s module! ",Driver_NAME); 144 //register and mknod 145 //注册字符设备,系统会自动分配一个主设备号,保存在major中 146 major = register_chrdev(0,Driver_NAME,&key_fops); 147 //自动在/dev/目录下创建设备节点 148 drv_class = class_create(THIS_MODULE,Driver_NAME); 149 drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME); /*/dev/key_query*/ 150 151 //set all pins to GPIO mod ALF5 152 //设置所有的GPIO引脚为GPIO功能 153 mxc_request_iomux(GPIO2_21, MUX_CONFIG_ALT5); 154 mxc_request_iomux(GPIO3_15, MUX_CONFIG_ALT5); 155 mxc_request_iomux(GPIO2_10, MUX_CONFIG_ALT5); 156 mxc_request_iomux(GPIO2_11, MUX_CONFIG_ALT5); 157 mxc_request_iomux(GPIO2_8, MUX_CONFIG_ALT5); 158 mxc_request_iomux(GPIO2_9, MUX_CONFIG_ALT5); 159 mxc_request_iomux(GPIO2_6, MUX_CONFIG_ALT5); 160 mxc_request_iomux(GPIO2_7, MUX_CONFIG_ALT5); 161 162 163 printk("<0>have setting all pins to gpio input mod ! "); 164 //request IOMUX GPIO 165 gpio_request(IOMUX_TO_GPIO(GPIO2_21), "GPIO2_21"); 166 gpio_request(IOMUX_TO_GPIO(GPIO3_15), "GPIO3_15"); 167 gpio_request(IOMUX_TO_GPIO(GPIO2_10), "GPIO2_10"); 168 gpio_request(IOMUX_TO_GPIO(GPIO2_11), "GPIO2_11"); 169 gpio_request(IOMUX_TO_GPIO(GPIO2_8), "GPIO2_8"); 170 gpio_request(IOMUX_TO_GPIO(GPIO2_9), "GPIO2_9"); 171 gpio_request(IOMUX_TO_GPIO(GPIO2_6), "GPIO2_6"); 172 gpio_request(IOMUX_TO_GPIO(GPIO2_7), "GPIO2_7"); 173 174 return 0; 175 } 176 177 /* 178 * 执行rmmod命令时就会调用这个函数 179 */ 180 static void __exit key_exit(void) 181 { 182 printk("<0> Goodbye,%s! ",Driver_NAME); 183 //卸载字符设备,释放主设备号 184 unregister_chrdev(major,Driver_NAME); 185 //卸载字符设备的设备节点 186 device_unregister(drv_class_dev); 187 class_destroy(drv_class); 188 189 /* free gpios */ 190 mxc_free_iomux(GPIO2_21, MUX_CONFIG_ALT5); 191 mxc_free_iomux(GPIO3_15, MUX_CONFIG_ALT5); 192 mxc_free_iomux(GPIO2_10, MUX_CONFIG_ALT5); 193 mxc_free_iomux(GPIO2_11, MUX_CONFIG_ALT5); 194 mxc_free_iomux(GPIO2_8, MUX_CONFIG_ALT5); 195 mxc_free_iomux(GPIO2_9, MUX_CONFIG_ALT5); 196 mxc_free_iomux(GPIO2_6, MUX_CONFIG_ALT5); 197 mxc_free_iomux(GPIO2_7, MUX_CONFIG_ALT5); 198 199 gpio_free(IOMUX_TO_GPIO(GPIO2_21)); 200 gpio_free(IOMUX_TO_GPIO(GPIO3_15)); 201 gpio_free(IOMUX_TO_GPIO(GPIO2_10)); 202 gpio_free(IOMUX_TO_GPIO(GPIO2_11)); 203 gpio_free(IOMUX_TO_GPIO(GPIO2_8)); 204 gpio_free(IOMUX_TO_GPIO(GPIO2_9)); 205 gpio_free(IOMUX_TO_GPIO(GPIO2_6)); 206 gpio_free(IOMUX_TO_GPIO(GPIO2_7)); 207 208 } 209 210 /* 这两行指定驱动程序的初始化函数和卸载函数 */ 211 module_init(key_init); 212 module_exit(key_exit); 213 214 /* 描述驱动程序的一些信息,不是必须的 */ 215 MODULE_AUTHOR("Lover雪"); 216 MODULE_VERSION("0.1.0"); 217 MODULE_DESCRIPTION("IMX257 key Driver"); 218 MODULE_LICENSE("GPL");
如程序所示,
static int __init key_init(void)
我们把注册字符设备,创建设备节点,GPIO地址映射,以及GPIO模式的初始化,都在此函数中实现。
static void __exit key_exit(void)
在此函数中我们 做了一下三件事,卸载字符设备,释放主设备函,释放GPIO
static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
当应用程序中调用
read(fd, key_vals,sizeof(key_vals));
时,该函数就是负责,读取此时,我们GPIO引脚的电平,然后通过COPY_TO_USER函数将我们的gpio引脚的电平传递给应用程序。
static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)
当应用程序中,执行代码
ioctl(fd,key_input,NULL);
时,就会执行
switch的case key_input中的代码,将所有的gpio引脚都设置成输入模式。
然后将所有的GPIO引脚设置为22K上拉模式:
//设置所有引脚为上拉模式 mxc_iomux_set_pad(GPIO2_6, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); mxc_iomux_set_pad(GPIO2_7, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); //mxc_iomux_set_pad(GPIO2_8, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); mxc_iomux_set_pad(GPIO2_9, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); mxc_iomux_set_pad(GPIO2_10, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); mxc_iomux_set_pad(GPIO2_11, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); mxc_iomux_set_pad(GPIO2_21, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU); //mxc_iomux_set_pad(GPIO3_15, PAD_CTL_HYS_SCHMITZ | PAD_CTL_PKE_ENABLE | PAD_CTL_PUE_PULL | PAD_CTL_22K_PU);
经过测试,貌似,GPIO2_8,GPIO3_15两个引脚不能这样设置,由于暂时对IMX257还不太了解,对于这一现象无法解释。
附上应用程序代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <unistd.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <fcntl.h> 7 #include <termios.h> 8 #include <errno.h> 9 #include <limits.h> 10 #include <asm/ioctls.h> 11 #include <time.h> 12 #include <pthread.h> 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_query",O_RDWR); 27 if(fd < 0){ 28 printf("can't open !!! "); 29 } 30 ioctl(fd,key_input,NULL); 31 while(1){ 32 read(fd, key_vals,sizeof(key_vals)); 33 if(!key_vals[0] | !key_vals[1] | !key_vals[2] | !key_vals[3] | !key_vals[4] | !key_vals[5] | !key_vals[6] | !key_vals[7] ) 34 printf("%04d key pressed: %d %d %d %d %d %d %d %d ",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 }
在应用程序中,
-
我们首先使用open来打开设备/dev/key_query。
-
使用ioctl函数将所有的GPIO引脚配置为输入模式
-
在while循环中使用read函数来不断的读取gpio引脚的电,一旦检测到低电平,就打印所有GPIO引脚当前的电平值
附上MAKEFILE代码
1 ifeq ($(KERNELRELEASE),) 2 KERNELDIR ?= /home/study/system/linux-2.6.31 3 PWD := $(shell pwd) 4 modules: 5 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules 6 modules_install: 7 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install 8 clean: 9 rm -rf *.o *~ core .depend *cmd *.ko *.mod.c .tmp_versions *.markers *.order *.symvers 10 11 else 12 obj-m := key.o 13 endif
编译、加载驱动:
使用交叉编译工具:
make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabi- insmod key.ko ll /dev/key* |
结果如下:
执行应用程序:
cd key/ arm-none-linux-gnueabi-gcc key_test.c –o key_test ./key_test |
结果如下: