tq210 按键驱动
来源:互联网 发布:网络言论自由作文800字 编辑:程序博客网 时间:2024/04/30 03:49
经过前面的配置,S5PV210开发已经可以成功进入Linux控制台了,那么,有了这个环境就可以开始学习Linux驱动的编写和测试了。学习Linux设备驱动,通常是从字符设备驱动开始。我写的第一个驱动程序是Led的,其实也就是熟悉下字符设备驱动的基本结构,本文以中断方式的按键驱动为例,简单的介绍下字符设备驱动程序。
一 按键驱动程序的简单实现
下面是基于中断和消息的按键驱动程序,其工作原理是:当应用程序读取键值时,会调用按键驱动程序的read函数,而我们实现的read函数检测完读取长度后没有直接读取键值而是等待按键消息,如果没有按键,程序会进入休眠状态,这样可以节省大量的CPU,而当我们按键时硬件会产生中断,程序自动进入中断处理函数,在中断处理函数中,驱动程序读取键值存入全局变量并激活read函数中等待的消息,应用程序被迅速唤醒并通过read函数读取键值,如此,完成了获取键值的工作。下面是源码,比较简单,也就不多说了。
源码:
- #include <linux/types.h>
- #include <linux/module.h>
- #include <linux/cdev.h>
- #include <linux/fs.h>
- #include <linux/device.h>
- #include <linux/gpio.h>
- #include <linux/irq.h>
- #include <linux/interrupt.h>
- #include <linux/sched.h>
- #include <linux/wait.h>
- #include <linux/uaccess.h>
- static dev_t devno;
- static struct cdev cdev;
- static struct class* buttons_class;
- static struct device* buttons_device;
- static wait_queue_head_t button_waitq;
- static volatile int pressed = 0;
- static unsigned char key_val;
- struct key_desc{
- unsigned int pin;
- unsigned char value;
- };
- static struct key_desc key_descs[8] = {
- [0] = {
- .pin = S5PV210_GPH0(0),
- .value = 0x00,
- },
- [1] = {
- .pin = S5PV210_GPH0(1),
- .value = 0x01,
- },
- [2] = {
- .pin = S5PV210_GPH0(2),
- .value = 0x02,
- },
- [3] = {
- .pin = S5PV210_GPH0(3),
- .value = 0x03,
- },
- [4] = {
- .pin = S5PV210_GPH0(4),
- .value = 0x04,
- },
- [5] = {
- .pin = S5PV210_GPH0(5),
- .value = 0x05,
- },
- [6] = {
- .pin = S5PV210_GPH2(6),
- .value = 0x06,
- },
- [7] = {
- .pin = S5PV210_GPH2(7),
- .value = 0x07,
- },
- };
- static irqreturn_t buttons_irq(int irq, void *dev_id){
- volatile struct key_desc *key = (volatile struct key_desc *)dev_id;
- if(gpio_get_value(key->pin)){
- key_val = key->value|0x80;
- }
- else{
- key_val = key->value;
- }
- pressed = 1;
- wake_up_interruptible(&button_waitq);
- return IRQ_RETVAL(IRQ_HANDLED);
- }
- static int buttons_open(struct inode *inode, struct file *file){
- int ret;
- ret = request_irq(IRQ_EINT(0), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key1", &key_descs[0]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(1), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key2", &key_descs[1]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(2), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key3", &key_descs[2]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(3), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key4", &key_descs[3]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(4), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key5", &key_descs[4]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(5), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key6", &key_descs[5]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(22), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key7", &key_descs[6]);
- if(ret)
- return ret;
- ret = request_irq(IRQ_EINT(23), buttons_irq, IRQ_TYPE_EDGE_BOTH, "key8", &key_descs[7]);
- if(ret)
- return ret;
- return 0;
- }
- static ssize_t buttons_read(struct file * file, char __user *data, size_t count, loff_t *loff){
- if(count != 1){
- printk(KERN_ERR "The driver can only give one key value once!\n");
- return -ENOMEM;
- }
- wait_event_interruptible(button_waitq, pressed);
- pressed = 0;
- if(copy_to_user(data, &key_val, 1)){
- printk(KERN_ERR "The driver can not copy the data to user area!\n");
- return -ENOMEM;
- }
- return 0;
- }
- static int buttons_close(struct inode *inode, struct file *file){
- free_irq(IRQ_EINT(0), &key_descs[0]);
- free_irq(IRQ_EINT(1), &key_descs[1]);
- free_irq(IRQ_EINT(2), &key_descs[2]);
- free_irq(IRQ_EINT(3), &key_descs[3]);
- free_irq(IRQ_EINT(4), &key_descs[4]);
- free_irq(IRQ_EINT(5), &key_descs[5]);
- free_irq(IRQ_EINT(22), &key_descs[6]);
- free_irq(IRQ_EINT(23), &key_descs[7]);
- return 0;
- }
- struct file_operations buttons_ops = {
- .open = buttons_open,
- .read = buttons_read,
- .release = buttons_close,
- };
- int buttons_init(void){
- int ret;
- cdev_init(&cdev, &buttons_ops);
- cdev.owner = THIS_MODULE;
- ret = alloc_chrdev_region(&devno, 0, 1, "buttons");
- if(ret){
- printk(KERN_ERR "alloc char device region faild!\n");
- return ret;
- }
- ret = cdev_add(&cdev, devno, 1);
- if(ret){
- printk(KERN_ERR "add char device faild!\n");
- goto add_error;
- }
- buttons_class = class_create(THIS_MODULE, "buttonsdrv");
- if(IS_ERR(buttons_class)){
- printk(KERN_ERR "create class error!\n");
- goto class_error;
- }
- buttons_device = device_create(buttons_class, NULL, devno, NULL, "buttons");
- if(IS_ERR(buttons_device)){
- printk(KERN_ERR "create buttons device error!\n");
- goto device_error;
- }
- init_waitqueue_head(&button_waitq);
- return 0;
- device_error:
- class_destroy(buttons_class);
- class_error:
- cdev_del(&cdev);
- add_error:
- unregister_chrdev_region(devno,1);
- return -ENODEV;
- }
- void buttons_exit(void){
- device_destroy(buttons_class, devno);
- class_destroy(buttons_class);
- cdev_del(&cdev);
- unregister_chrdev_region(devno, 1);
- }
- module_init(buttons_init);
- module_exit(buttons_exit);
- MODULE_LICENSE("GPL");
测试程序代码:
- #include <stdio.h>
- #include <fcntl.h>
- int main(){
- int fd = open("/dev/buttons", O_RDWR);
- if(fd < 0){
- printf("open error");;
- return 0;
- }
- unsigned char key;
- while(1){
- read(fd, &key, 1);
- printf("The key = %x\n", key);
- }
- close(fd);
- }
相比轮询方式的按键驱动程序,中断方式编写的按键驱动程序可以很大程度上节省CPU资源,因此,推荐使用中断方式。
二 支持POLL机制
上面这种方式实现的按键驱动程序有个弊端,如果我们不按键,应用程序将会永远阻塞在这里,幸运的是,linux内核提供了poll机制,可以设置超时等待时间,如果在这个时间内读取到键值则正常返回,反之则超时退出。使内核支持poll非常简单,为file_operations的poll成员提供poll处理函数即可。
使内核支持poll还需要以下几步:
添加poll头文件
- #include <linux/poll.h>
编写poll处理函数:
- static unsigned buttons_poll(struct file *file, poll_table *wait){
- unsigned int mask = 0;
- poll_wait(file, &button_waitq, wait);
- if (pressed)
- mask |= POLLIN | POLLRDNORM;
- return mask;
- }
- .poll = buttons_poll,
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <stdio.h>
- #include <poll.h>
- int main(int argc, char **argv){
- int fd;
- unsigned char key_val;
- int ret;
- struct pollfd fds[1];
- fd = open("/dev/buttons", O_RDWR);
- if (fd < 0){
- printf("can't open!\n");
- }
- fds[0].fd = fd;
- fds[0].events = POLLIN;
- while (1){
- ret = poll(fds, 1, 5000);
- if (ret == 0){
- printf("time out\n");
- }
- else{
- read(fd, &key_val, 1);
- printf("key_val = 0x%x\n", key_val);
- }
- }
- return 0;
- }
这样,应用程序可以限制时间,如果在一定时间内读取不到键值就可以做特殊处理,这种思想在网络通信中应用广泛。
三 支持异步机制
很多情况下,我们的程序在等待按键期间需要处理其它任务而不是在这里空等,这时,就需要采用异步模式了。所谓异步模式,实际上是采用消息机制(以本文的按键程序为例),即当驱动程序检测到按键后发送消息给应用程序,应用程序接收到消息后再去读取键值。与前面的两种模式相比,最大的不同在于异步方式是驱动告诉应用程序来读而不是应用程序主动去读。添加异步支持更加简单,首先是为file_operations注册fasync函数,函数内容如下:
- static int buttons_fasync(int fd, struct file * file, int on){
- return fasync_helper(fd, file, on, &button_async);
- }
- static ssize_t buttons_read(struct file * file, char __user *data, size_t count, loff_t *loff){
- if(count != 1){
- printk(KERN_ERR "The driver can only give one key value once!\n");
- return -ENOMEM;
- }
- wait_event_interruptible(button_waitq, pressed);
- pressed = 0;
- if(copy_to_user(data, &key_val, 1)){
- printk(KERN_ERR "The driver can not copy the data to user area!\n");
- return -ENOMEM;
- }
- return 0;
- }
- #include <fcntl.h>
- #include <stdio.h>
- #include <poll.h>
- #include <signal.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <fcntl.h>
- /* sixthdrvtest
- */
- int fd;
- void my_signal_fun(int signum)
- {
- unsigned char key_val;
- read(fd, &key_val, 1);
- printf("key_val: 0x%x\n", key_val);
- }
- int main(int argc, char **argv)
- {
- unsigned char key_val;
- int ret;
- int Oflags;
- signal(SIGIO, my_signal_fun);
- fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
- if (fd < 0){
- printf("can't open!\n");
- return -1;
- }
- fcntl(fd, F_SETOWN, getpid());
- Oflags = fcntl(fd, F_GETFL);
- fcntl(fd, F_SETFL, Oflags | FASYNC);
- int rest;
- while (1){
- printf("Hello\n");
- while(rest = sleep(50)){
- sleep(rest);
- }
- }
- return 0;
- }
到这里,这个驱动程序基本上就算可以了,当然,还有对阻塞和非阻塞的支持,同步与互斥的支持,而阻塞与非阻塞无非是加上个逻辑判断,同步与互斥根应用程序的同步控制也差不多,无非就是信号量或者原子操作,这里就不多说了,如果有朋友需要这些内容可以留言讨论。
- TQ210按键驱动
- tq210 按键驱动
- tq210 usb host驱动
- tq210 nandflash 驱动
- TQ210开发板-led驱动
- TQ210裸机编程——按键点灯
- 按键驱动
- 按键驱动
- 按键驱动
- 按键驱动
- 按键驱动
- 按键驱动
- S5PV210(TQ210)学习笔记——按键驱动程序
- TQ210裸机编程(4)——按键(中断法)
- S5PV210(TQ210)学习笔记——按键驱动程序
- TQ210裸机编程(2)——按键(查询法
- TQ210裸机编程(4)——按键(中断法)
- TQ210裸机编程(4)——按键(中断法)
- vsftpd在opensuse环境下建立ftp服务器
- Android内存(内存溢出 内存不足 内存低 .)优化详解
- 笔记 <Core Java2 卷1>第三章 基本编程结构
- java Xml 解析 Dom
- Android开发eclipse无法签名android程序包的问题
- tq210 按键驱动
- C++错误unresolved external symbol _WinMain@1
- 奇幻RPG(角色技能 与 Strategy模式)
- 异步调用与线程(总结篇)
- android自定义滑动开关控件,适合所有的android系统
- AO与AE的区别(转载)
- 2014 关键时刻
- 百度地图Android SDK 获取API KEY时安全码
- Android应用测试标准