tiny6410 Linux 按键控制LED驱动
来源:互联网 发布:淘宝人生无弹窗 编辑:程序博客网 时间:2024/04/30 14:23
前一段时间项目忙,中断了更新。
要想提高能力,最重要的还是实际动手写代码,这样才能遇到一些问题,解决问题增加经验。
今天介绍的功能是: 在linux中添加一个button驱动,按键控制LED灯亮灭,并且支持应用程序读取按键事件。
关键词:misc设备驱动,工作队列,等待队列。
下面根据代码来一点一点介绍:
1、列出我们定义的数据结构以及全局变量:
#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/irq.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/interrupt.h>#include <asm/uaccess.h>#include <mach/hardware.h>#include <linux/platform_device.h>#include <linux/cdev.h>#include <linux/miscdevice.h>#include <linux/slab.h> #include <mach/map.h>#include <mach/regs-clock.h>#include <mach/regs-gpio.h>#include <plat/gpio-cfg.h>#include <mach/gpio-bank-n.h>#include <mach/gpio-bank-l.h>#include <mach/gpio-bank-k.h>#define DEVICE_NAME "my_buttons"/*button 中断描述*/struct button_irq_dec{ int irq; int number; char * name;};/*事件结构,应用程序中要与此结构一致,上报按键消息时直接把该结构体copy到用户空间*/struct uevent{ char name[5]; char isDown;};static LIST_HEAD(button_list);// 消息队列static DECLARE_WAIT_QUEUE_HEAD(button_waitq); // 等待队列头static DEFINE_MUTEX(button_mtx); // 锁/*中断消息,里面包含上报应用程序的event,以及维护的链表和工作队列*/struct message{ struct uevent event; struct list_head list; struct work_struct my_work;};/*预先定义好的buttom中断描述*/struct button_irq_dec button_irqs[] = { {IRQ_EINT(0), 0, "key1"}, {IRQ_EINT(1), 1, "KEY2"}, {IRQ_EINT(2), 2, "KEY3"}, {IRQ_EINT(3), 3, "KEY4"}, {IRQ_EINT(4), 4, "KEY5"}, {IRQ_EINT(5), 5, "KEY6"}, {IRQ_EINT(19), 6, "KEY7"}, {IRQ_EINT(20), 7, "KEY8"},};
2、模块初始化函数
static struct file_operations dev_fops = { .owner = THIS_MODULE, .read = button_message_read,};static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};static int __init dev_init(void){int ret, i, err; unsigned tmp; /*设置LED全灭状态*/ tmp = readl(S3C64XX_GPKCON);tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);writel(tmp, S3C64XX_GPKCON);tmp = readl(S3C64XX_GPKDAT);tmp |= (0xF << 4);writel(tmp, S3C64XX_GPKDAT); /*申请中断*/ for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {if (button_irqs[i].irq < 0) {continue;} /*IRQ_TYPE_EDGE_BOTH 边沿触发 中断处理函数: buttons_interrupt 参数:&button_irqs[i] */ 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; } ret = misc_register(&misc);printk (DEVICE_NAME"\tinitialized\n");return ret;}当中断产生时,就会调用到button_interrupt函数。
3、button_interrupt函数
static irqreturn_t buttons_interrupt(int irq, void* pram){ struct button_irq_dec *irq_dec = (struct button_irq_dec *)pram; char down = 1; int number; unsigned tmp_key; unsigned tmp_led; struct message *msg; /*获取产生中断对应的按键号*/ number = irq_dec->number; /*根据按键号处理LED灯状态*/ switch(number) { case 0: case 1: case 2: case 3: tmp_led = readl(S3C64XX_GPKDAT); tmp_led = tmp_led & ~(0x10 << number);/*点亮LED灯*/ writel(tmp_led, S3C64XX_GPKDAT); break; case 4: case 5: case 6: case 7: tmp_led = readl(S3C64XX_GPKDAT); tmp_led = tmp_led | (1 << number);/*熄灭LED灯*/ writel(tmp_led, S3C64XX_GPKDAT); break; } //处理按键消息,获取按键号对应的状态:down or up switch(number) { case 0: case 1: case 2: case 3: case 4: case 5: tmp_key = readl(S3C64XX_GPNDAT); down = !(tmp_key & (1 << number)); break; case 6: case 7: tmp_key = readl(S3C64XX_GPLDAT); down = !(tmp_key & (1 << (number + 5))); break; default: break; } /*申请一个msg空间,并填充其中的event,然后调用工作队列*/ msg = (struct message *)kmalloc(sizeof(struct message), GFP_ATOMIC); if(msg != NULL) { strcpy(msg->event.name, irq_dec->name); msg->event.isDown = down; /*设置工作队列的函数*/ INIT_WORK(&msg->my_work, add_message); /*启动工作队列*/ schedule_work(&msg->my_work); } return IRQ_RETVAL(IRQ_HANDLED);}上面的中断函数使用工作队列的原因是:新建一个message,填充好event后,要添加到button_list中,但是在read函数中要把该链表的event返回到应用程序,所以要加锁控制同步,在中断程序中不能调用加锁函数(因为会导致睡眠),所以此处使用工作队列。
工作队列的使用:INIT_WORK中只设置了函数地址,而函数的参数是work_struct对象地址。所以要把工作队列定义在message结构体中,这样在工作队列的函数中可以通过contain_of来获取我们要传递的参数。
add_message函数:
static void add_message(struct work_struct * pram){ struct message *msg = container_of(pram, struct message, my_work); if(msg ==NULL) return ; mutex_lock(&button_mtx); list_add(&msg->list, &button_list); wake_up_interruptible(&button_waitq); mutex_unlock(&button_mtx); return ;}到这里,按键中断的处理以及返回给应用程序的按键消息已经封装好了,下面来看read函数的实现。
4、read函数
static ssize_t button_message_read(struct file *file, char __user *buf,size_t count, loff_t *ppos){ unsigned int number, i = 0, counts; char is_wait = 0; struct message * msg, *pre = NULL; DECLARE_WAITQUEUE(my_wait,current); mutex_lock(&button_mtx); /*如果链表为空,说明没有按键消息,那么就等待*/ if(list_empty(&button_list)) { mutex_unlock(&button_mtx); is_wait = 1; /*添加等待队列,设置当前进程状态,schedule()让出cpu*/ add_wait_queue(&button_waitq, &my_wait); current->state = TASK_INTERRUPTIBLE; schedule(); } else { mutex_unlock(&button_mtx); } /*返回整数个struct uevent结构体*/ number = count / (sizeof(struct uevent)); mutex_lock(&button_mtx); /*遍历链表,获取每个event*/ list_for_each_entry_reverse(msg, &button_list, list) { /*遍历的同时,删除上一个event*/ if(pre != NULL) { list_del(&pre->list); kfree(pre); pre = NULL; } /*把event copy到用户空间*/ counts = copy_to_user(buf + (i *(sizeof(struct uevent))), &msg->event, sizeof(struct uevent)); pre = msg; i++; if(i == number) break; } if(pre != NULL) { list_del(&pre->list); kfree(pre); pre = NULL; } mutex_unlock(&button_mtx); if(is_wait){ /*移出等待队列*/ remove_wait_queue(&button_waitq, &my_wait); set_current_state(TASK_RUNNING); } return number*(sizeof(struct uevent));}这样,驱动的主要函数都已经实现了,下面就列出应用程序如何来完成。
5、应用程序代码
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>struct uevent{ char name[5]; char isDown;};int main(){ struct uevent event; int fd = open("/dev/my_buttons", 0);if (fd < 0) {perror("open device leds");exit(1);} while(1) { read(fd, &event, sizeof(struct uevent)); printf("%s is %s", event.name, event.isDown? "down\n": "up\n"); } close(fd);}6、代码的编译
驱动的makefile文件:
KERNELDIR:=/work/tiny6410/Linux/linux-2.6.38 //这是我的kernel代码位置PWD:=$(shell pwd)obj-m:=button.oall:make -C $(KERNELDIR) M=$(PWD) modulesclean:rm -rf *.o *.ko应用程序的编译:
arm-linux-gcc test.c -omain
本人所有文章目录:http://blog.csdn.net/lrs030740304/article/details/7941984
- tiny6410 Linux 按键控制LED驱动
- linux驱动按键控制led灯
- 按键控制led驱动
- 按键控制led驱动
- 【原创】Tiny6410简单驱动 --- LED控制
- mini2440 按键控制LED 驱动
- Linux驱动开发--通过按键控制led灯
- Linux button按键驱动 多次中断控制相应LED灯亮灭闪
- tiny6410 linux混杂设备 led驱动
- Tiny6410 LED 驱动笔记
- tiny6410 platform led驱动
- 4412驱动-fifth_fasyn 控制led 蜂鸣器 按键
- Linux下驱动开发之二(LED驱动)-------Tiny6410
- Linux字符设备驱动之Tiny6410 LED驱动分析
- TINY6410 按键驱动分析
- tiny6410按键驱动总结
- Linux驱动开发之三----按键驱动(Tiny6410)
- Tiny6410 led 驱动实现分析
- 你俩是一对
- Java程序的编码规范
- DECODE函数的用法和实际运用
- Memory Layout for Multiple and Virtual Inheritance (By Edsko de Vries, January 2006)
- HibernateWork12
- tiny6410 Linux 按键控制LED驱动
- 给自己的警告
- Frame,Bounds的区别,center属性
- 深入理解Flash的沙箱 – Security Domains-1
- Smartupload上传组件用法
- [Maven] Explore maven code by code
- 设计模式之工厂模式
- machine learning
- STL学习系列之一——标准模板库STL介绍