编者:按键驱动程序涉及到linux中断程序的编写。
1、按键原理图。
2、驱动程序的编写移植。
在/linux-2.6.32.2/drivers/char/目录下创建一个新的驱动程序文件mini2440_buttons.c,内容及详细注释如下:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "buttons" //设备名称 /*定义中断所用的结构体*/ struct button_irq_desc { int irq; //按键对应的中断号 int pin; //按键所对应的GPIO 端口 int pin_setting; //按键对应的引脚描述,实际并未用到,保留 int number; //定义键值,以传递给应用层/用户态 char *name; //每个按键的名称 }; /*结构体实体定义*/ static struct button_irq_desc button_irqs [] = { {IRQ_EINT8 , S3C2410_GPG(0) , S3C2410_GPG0_EINT8 , 0, "KEY0"}, {IRQ_EINT11, S3C2410_GPG(3) , S3C2410_GPG3_EINT11 , 1, "KEY1"}, {IRQ_EINT13, S3C2410_GPG(5) , S3C2410_GPG5_EINT13 , 2, "KEY2"}, {IRQ_EINT14, S3C2410_GPG(6) , S3C2410_GPG6_EINT14 , 3, "KEY3"}, {IRQ_EINT15, S3C2410_GPG(7) , S3C2410_GPG7_EINT15 , 4, "KEY4"}, {IRQ_EINT19, S3C2410_GPG(11), S3C2410_GPG11_EINT19, 5, "KEY5"}, }; /*开发板上按键的状态变量,注意这里是’0’,对应的ASCII 码为30*/ static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'}; /*因为本驱动是基于中断方式的,在此创建一个等待队列,以配合中断函数使用;当有按键按下并读取到键 值时,将会唤醒此队列,并设置中断标志,以便能通过 read 函数判断和读取键值传递到用户态;当没有按 键按下时,系统并不会轮询按键状态,以节省时钟资源*/ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*中断标识变量,配合上面的队列使用,中断服务程序会把它设置为1,read 函数会把它清零*/ static volatile int ev_press = 0; /*本按键驱动的中断服务程序*/ static irqreturn_t buttons_interrupt(int irq, void *dev_id) { struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; int down; // udelay(0); /*获取被按下的按键状态*/ down = !s3c2410_gpio_getpin(button_irqs->pin); /*状态改变,按键被按下,从这句可以看出,当按键没有被按下的时候,寄存器的值为1(上拉),但按 键被按下的时候,寄存器对应的值为0*/ if (down != (key_values[button_irqs->number] & 1)) { // Changed /*如果key1 被按下,则key_value[0]就变为’1’,对应的ASCII 码为31*/ key_values[button_irqs->number] = '0' + down; ev_press = 1; /*设置中断标志为1*/ wake_up_interruptible(&button_waitq); /*唤醒等待队列*/ } return IRQ_RETVAL(IRQ_HANDLED); } /* *在应用程序执行open(“/dev/buttons”,…)时会调用到此函数,在这里,它的作用主要是注册6 个按键的中断。 *所用的中断类型是IRQ_TYPE_EDGE_BOTH,也就是双沿触发,在上升沿和下降沿均会产生中断,这样做 是为了更加有效地判断按键状态 */ static int s3c24xx_buttons_open(struct inode *inode, struct file *file) { int i; int err = 0; for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { if (button_irqs[i].irq < 0) { continue; } /*注册中断函数*/ err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]); if (err) break; } if (err) { /*如果出错,释放已经注册的中断,并返回*/ i--; for (; i >= 0; i--) { if (button_irqs[i].irq < 0) { continue; } disable_irq(button_irqs[i].irq); free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } return -EBUSY; } /*注册成功,则中断队列标记为1,表示可以通过read 读取*/ ev_press = 1; /*正常返回*/ return 0; } /* *此函数对应应用程序的系统调用close(fd)函数,在此,它的主要作用是当关闭设备时释放6 个按键的中断* 处理函数 */ static int s3c24xx_buttons_close(struct inode *inode, struct file *file) { int i; for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) { if (button_irqs[i].irq < 0) { continue; } /*释放中断号,并注销中断处理函数*/ free_irq(button_irqs[i].irq, (void *)&button_irqs[i]); } return 0; } /* *对应应用程序的read(fd,…)函数,主要用来向用户空间传递键值 */ static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; if (!ev_press) { if (filp->f_flags & O_NONBLOCK) /*当中断标识为0 时,并且该设备是以非阻塞方式打开时,返回*/ return -EAGAIN; else /*当中断标识为0 时,并且该设备是以阻塞方式打开时,进入休眠状态,等待被唤醒*/ wait_event_interruptible(button_waitq, ev_press); } /*把中断标识清零*/ ev_press = 0; /*一组键值被传递到用户空间*/ err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count)); return err ? -EFAULT : min(sizeof(key_values), count); } static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; /*把调用poll 或者select 的进程挂入队列,以便被驱动程序唤醒*/ poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } /*设备操作集*/ static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = s3c24xx_buttons_open, .release = s3c24xx_buttons_close, .read = s3c24xx_buttons_read, .poll = s3c24xx_buttons_poll, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; /*设备初始化,主要是注册设备*/ static int __init dev_init(void) { int ret; /*把按键设备注册为misc 设备,其设备号是自动分配的*/ ret = misc_register(&misc); printk (DEVICE_NAME"tinitializedn"); return ret; } /*注销设备*/ static void __exit dev_exit(void) { misc_deregister(&misc); } module_init(dev_init); //模块初始化,仅当使用insmod/podprobe 命令加载时有用,如果设备不是通过模块方 式加载,此处将不会被调用 module_exit(dev_exit); //卸载模块,当该设备通过模块方式加载后,可以通过rmmod 命令卸载,将调用此函数 MODULE_LICENSE("GPL");//版权信息 MODULE_AUTHOR("FriendlyARM Inc.");//作者名字 3 把按键驱动加入内核 接下来,我们把按键驱动加入到内核中,打开linux-2.6.32.2_fa/drivers/char/Kconfig 文件,加入如下红色部分内容: config MINI2440_BUTTONS tristate "Buttons driver for FriendlyARM Mini2440 development boards" depends on MACH_MINI2440 default y if MACH_MINI2440 help this is buttons driver for FriendlyARM Mini2440 development boards config MINI2440_BUZZER tristate "Buzzer driver for FriendlyARM Mini2440 development boards" depends on MACH_MINI2440 default y if MACH_MINI2440 help this is buzzer driver for FriendlyARM Mini2440 development boards 接下来,再根据该驱动的配置定义,把对应的驱动目标文件加入内核中,打开linux-2.6.32.2/drivers/char/Makefile 文件,添加如下红色部分内容: obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o obj-$(CONFIG_LEDS_MINI2440) += mini2440_leds.o obj-$(CONFIG_MINI2440_BUTTONS) += mini2440_buttons.o obj-$(CONFIG_MINI2440_ADC) += mini2440_adc.o # Files generated that shall be removed upon make clean clean-files := consolemap_deftbl.c defkeymap.c 这样,我们就在内核中添加做好了LED 驱动 4 配置编译新内核 接上面的步骤,在内核源代码目录下执行:make menuconfig 重新配置内核,依次选择进入如下子菜单项: Device Drivers ---> Character devices ---> 在此按空格键选择“<*> Buttons driver for FriendlyARM Mini2440 development boards(NEW) ”选项,并退出保存内核配置。在内核源代码根目录下执行;make zImage,把生成的新内核烧写到开发板中。 5 测试按键 为了测试上面的按键驱动,创建一个按键测试程序:btn-test.c,内容如下: #include #include #include #include #include #include #include #include #include #include int main(void) { int buttons_fd; char buttons[6] = {'0', '0', '0', '0', '0', '0'}; //定义按键值变量,对于驱动函数中的key_values 数组 /*打开按键设备/dev/buttons*/ buttons_fd = open("/dev/buttons", 0); if (buttons_fd < 0) { /*打开失败则退出*/ perror("open device buttons"); exit(1); } /*永读按键并打印键值和状态*/ for (;;) { char current_buttons[6]; int count_of_changed_key; int i; /*使用read 函数读取一组按键值(6 个)*/ if (read(buttons_fd, current_buttons, sizeof current_buttons) != sizeof current_buttons) { perror("read buttons:"); exit(1); } /*逐个分析读取到的按键值*/ for (i = 0, count_of_changed_key = 0; i < sizeof buttons / sizeof buttons[0]; i++) { if (buttons[i] != current_buttons[i]) { buttons[i] = current_buttons[i]; /*打印按键值,并标明按键按下/抬起的状态*/ printf("%skey %d is %s", count_of_changed_key? ", ": "", i+1, buttons[i] == '0' ? "up" : "down"); count_of_changed_key++; } } if (count_of_changed_key) { printf("n"); } } /*关闭按键设备文件*/ close(buttons_fd); return 0; } 以下是测试图片
上一篇:按键驱动在2440上的实例开发(带去抖动)
下一篇:MINI2440 内核基础开发
推荐阅读最新更新时间:2024-11-05 07:44
设计资源 培训 开发板 精华推荐
- 使用 Microchip Technology 的 MIC2810-4GPYML 的参考设计
- NCV33164电压监测器典型应用电路
- LTC2946CMS-1 6V 至 300V 高端电源、电荷和能量监视器的典型应用
- 使用 Semtech 的 SC2450ISWTR 的参考设计
- 30W、5V 交流转直流单路输出电源
- DER-665 - 基于InnoSwitch3-CP的10/18 W快速充电3.0充电器
- LTC2637-HZ12 四通道、12 位数模转换器的典型应用
- LTC1754 的典型应用 - 具有 SOT-23 关断功能的微功率、稳压 3.3V/5V 电荷泵
- 16 位、2MSPS、1CH 用于仪表
- 【物联网】鸿蒙物联网