原创
S3C44B0X公板Keyboard Driver For Uclinux
S3C44B0X公板Keyboard Driver For Uclinux By panasonic.lin@163.com S3C44B0X公板Keyboard连接如下: KEYIN0->EXINT4/GPG4 KEYIN1->EXINT5/GPG5 KEYIN2->EXINT6/GPG6 KEYIN3->EXINT7/GPG7 这四个按键通过OR的方式公用一条中断线EINT4567(编号21的外部中断),所以要通过EXTINPND寄存器的标志位来区别这四个按键。 通过这个实例,基本上LDD第十章之前的内容都有涉及,为后面打下坚实的基础。 注意的是2.4内核和2.6内核的驱动有很大区别,如: 1,read函数中的__user宏,2.4没有; 2,中断处理函数返回值,2.4是void; 3,定时器的setuptimer,2.4没有; 4,poll函数的poll_table。第一步,创建s3c44b0key.c如下: /* ************************************************************************ *Name : s3c44b0key.c *Author : panasonic.lin@163.com *Date : 11/1/2010 *Copyright : GPL *Description : s3c44b0x keyboard driver ************************************************************************ */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/poll.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/types.h> /* size_t */ #include <asm/uaccess.h> /* copy_*_user */ #include <asm/system.h> /* cli(), *_flags */ #include <linux/fcntl.h> /* O_ACCMODE */ #include <linux/module.h> #include <linux/types.h> #include <linux/mm.h> #include <linux/sched.h> #include <asm/io.h> #include <asm/ioctl.h> #include <asm/irq.h> //#include <asm/arch/irqs.h>All IRQ numbers of the S3C44B0X CPU //#include <mach/hardware.h> #include <asm/arch/hardware.h> #define DEVICE_NAME "s3c44b0keyboard" //设备名称 #define DEVICE_MAJOR 232 //主设备号 #define KEY_TIMER_DELAY1 (HZ/50) //按键-按下-去抖延时20毫秒 #define KEY_TIMER_DELAY2 (HZ/10) //按键-抬起-去抖延时100毫秒 #define KEY_DOWN 0 //按键按下 #define KEY_UP 1 //按键抬起 #define KEY_UNCERTAIN 2 //按键不确定 #define KEY_COUNT 4 //4个按键 #define EINT4 1<<4 #define EINT5 1<<5 #define EINT6 1<<6 #define EINT7 1<<7 #define PCONG_EINT4 0x3<<8 #define PCONG_EINT5 0x3<<10 #define PCONG_EINT6 0x3<<12 #define PCONG_EINT7 0x3<<14 #define EINT4567 21 #define MASK_BIT(bit) (1<<(bit)) static volatile int ev_press = 0; //按键按下产生标志 static volatile int key_status[KEY_COUNT]; //记录4个按键的状态 static struct timer_list key_timers[KEY_COUNT]; //定义4个按键去抖动定时器 static DECLARE_WAIT_QUEUE_HEAD(button_waitq); //定义并初始化等待队列头 //组织硬件资源结构体 struct button_irq_desc { int irq; //中断号uclinux/include/asm-armnommu/arch-S3C44B0X/irqs.h int pin; //对应引脚 int dev_id; //id识别 char *name; //按键名称 }; //定义4个按键资源结构体数组#define S3C44B0X_INTERRUPT_EINT4567 21 /* External int. 4,5,6 and 7 */ static struct button_irq_desc button_irqs[KEY_COUNT] ={ {S3C44B0X_INTERRUPT_EINT4567,EINT4,0,"KEY0"}, {S3C44B0X_INTERRUPT_EINT4567,EINT5,1,"KEY1"}, {S3C44B0X_INTERRUPT_EINT4567,EINT6,2,"KEY2"}, {S3C44B0X_INTERRUPT_EINT4567,EINT7,3,"KEY3"}, }; /*-------------------------------------------------------------------- *获取GPGDATA引脚电平 *--------------------------------------------------------------------*/ static int s3c44b0_gpio_getpin(int pin_bit){ return ((*(volatile unsigned *)S3C44B0X_PDATG)&pin_bit); } /*-------------------------------------------------------------------- *中断服务例程 *--------------------------------------------------------------------*/ void buttons_interrupt(int irq,void *dev_id, struct pt_regs *regs){ //获取当前按键资源的索引 volatile int key; key=(*(volatile unsigned *)S3C44B0X_EXTINPND);//读取EXTINPND标志 if(key>0){ switch(key&0x0f){ case 1 :{key=0;}break; case 2 :{key=1;}break; case 4 :{key=2;}break; case 8 :{key=3;}break; default:break; } //如果按键在其他状态下,直接退出中断 if(key_status[key]== KEY_UP){ //设置当前按键的状态为不确定 key_status[key]= KEY_UNCERTAIN; //设置当前按键按下去抖定时器的延时并启动定时器后退出中断,然后在定时器服务程序里面判断 key_timers[key].expires = jiffies + KEY_TIMER_DELAY1; add_timer(&key_timers[key]); } // 清除相应标志位 (*(volatile unsigned *)S3C44B0X_EXTINPND) |= (1<<key);//write 1 to extintpend to clear (*(volatile unsigned *)S3C44B0X_I_ISPC) |=(1 << S3C44B0X_INTERRUPT_EINT4567); } } /*-------------------------------------------------------------------- **定时器服务例程 ** *--------------------------------------------------------------------*/ static void buttons_timer(unsigned long arg){ //获取当前按键资源的索引dev_id int key = (int)arg; // 下降沿定时时间到,获取当前按键引脚上的电平值来判断按键是按下还是抬起 int up = s3c44b0_gpio_getpin(button_irqs[key].pin); if(!up){//==0低电平,按键按下 if(key_status[key]== KEY_UNCERTAIN){ //标识当前按键状态为按下 key_status[key]= KEY_DOWN; //标识当前按键已按下并唤醒等待队列 ev_press = 1; wake_up_interruptible(&button_waitq); } //设置当前按键- 抬起- 去抖定时器的延时并启动定时器 key_timers[key].expires = jiffies + KEY_TIMER_DELAY2; add_timer(&key_timers[key]); } else{//高电平,按键抬起 //标识当前按键状态为抬起 key_status[key]= KEY_UP; } } /*-------------------------------------------------------------------- **fileoperation open funtion ** *--------------------------------------------------------------------*/ static int buttons_open(struct inode *inode,struct file *file) { int i; int ret=0; //设置PORTG4个IO口funtion是EINTX (*(volatile unsigned *)S3C44B0X_PCONG) |=0XFF00; //设置中断模式INTMOD和中断控制INTCON寄存器 (*(volatile unsigned *)S3C44B0X_INTMOD) &=~MASK_BIT(EINT4567); (*(volatile unsigned *)S3C44B0X_INTCON) &=~MASK_BIT(1); //设置中断下降沿为有效触发 (*(volatile unsigned *)S3C44B0X_EXTINT)=((*(volatile unsigned *)S3C44B0X_EXTINT)&0X0000FFFF)|0X33330000; ret = request_irq(button_irqs[0].irq,buttons_interrupt,SA_INTERRUPT,button_irqs[0].name,NULL); for(i = 0;i<KEY_COUNT;i++){ //初始化4个按键的状态为抬起 key_status= KEY_UP; //初始化并设置4个去抖定时器 // setup_timer(&key_timers,buttons_timer,i); init_timer(&key_timers); key_timers.function = buttons_timer; key_timers.data = i; } if(ret){ //中断申请失败处理 printk(KERN_ALERT "request button_irqs[0].irq fail!\n"); //释放已注册成功的中断 disable_irq(button_irqs[0].irq); free_irq(button_irqs[0].irq,NULL); return -EBUSY; } else{ printk(KERN_ALERT "request irqs sucess!\n"); } return 0; } /*-------------------------------------------------------------------- **fileoperation close funtion ** *--------------------------------------------------------------------*/ static int buttons_close(struct inode *inode,struct file *file) { int i; //释放4个定时器和中断 for(i = 0;i < KEY_COUNT;i++){ del_timer(&key_timers); } disable_irq(button_irqs[0].irq); free_irq(button_irqs[0].irq,NULL); return 0; } /*-------------------------------------------------------------------- **fileoperation read funtion ** *--------------------------------------------------------------------*/ ssize_t buttons_read(struct file *filp,char *buf,size_t count,loff_t *offp) { ssize_t ret=0; if(!ev_press){//==0判断按键按下产生标识,0没有产生// if(filp->f_flags & O_NONBLOCK){ //应用程序若采用非阻塞方式读取则返回错误// return -EAGAIN; } else{ //以阻塞方式读取且按键按下没有产生,让等待队列进入睡眠// wait_event_interruptible(button_waitq,ev_press); } } //1为按键按下产生,并清除标识为0,准备给下一次判断用// ev_press = 0; //将内核中的按键状态数据拷贝到用户空间给应用程序使用// ret = copy_to_user(buf,&key_status,min(sizeof(key_status),count)); //ret==0,sucessful,other is fail return ret?-EFAULT:min(sizeof(key_status),count); } /*-------------------------------------------------------------------- **fileoperation poll funtion ** *--------------------------------------------------------------------*/ //驱动程序中的轮询,用于应用程序中的轮询查询是否可对设备进行访问 static unsigned int buttons_poll(struct file *file,poll_table *wait) { unsigned int mask = 0; //添加等待队列到等待队列表中(poll_table) poll_wait(file,&button_waitq,wait); if(ev_press){//==1 //标识数据可以获得 mask |= POLLIN|POLLRDNORM; } return mask; } /*-------------------------------------------------------------------- **file_operations ** *--------------------------------------------------------------------*/ //设备操作列表 static struct file_operations buttons_fops = { .owner = THIS_MODULE, .open = buttons_open, .release = buttons_close, .read = buttons_read, .poll = buttons_poll, }; /*-------------------------------------------------------------------- **button_init(void) ** *--------------------------------------------------------------------*/ static int __init button_init(void) { int ret; //注册字符设备 ret = register_chrdev(DEVICE_MAJOR,DEVICE_NAME,&buttons_fops); if(ret< 0){ printk(DEVICE_NAME"register faild!\n"); return ret; } else{ printk(KERN_ALERT "register_chardev sucessful!\n"); } return 0; } /*-------------------------------------------------------------------- **button_exit(void) ** *--------------------------------------------------------------------*/ static void __exit button_exit(void) { //注销字符设备 unregister_chrdev(DEVICE_MAJOR,DEVICE_NAME); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("panasonic.lin@163.com"); MODULE_DESCRIPTION("s3c44box buttons driver");第二步,创建Makefile如下: 注意-D__linux__不是-D__LINUX__,关于这些CFLAGS可以在编译uclinux的时候重定向到某个文本文件,然后任意找到一个字符设备驱动的字段拷贝过来就行了,当然理解还是很重要的。 # Change it here or specify it on the "make" command line CC=arm-elf-gcc KERNELDIR = /home/panasonic/Data/uClinux-dist20050311/linux-2.4.x include $(KERNELDIR)/.config CFLAGS = -D__KERNEL__ -D__linux__ -DMODULE -DNO_MM -mapcs-32 -march=armv4 -mshort-load-bytes -msoft-float -mtune=arm7tdmi -fno-strict-aliasing -fno-common -pipe -fno-builtin -fomit-frame-pointer -fsigned-char -O -Wall -Wstrict-prototypes -Wno-trigraphs -I$(KERNELDIR)/include all: s3c44b0key.o s3c44b0key.o:s3c44b0key.c $(CC) $(CFLAGS) -c s3c44b0key.c clean: rm -f s3c44b0key.o第三步,make即可生成模块。 第四步,创建应用程序buttons_test.c如下: /* ************************************************************************ *Name : buttons_test.c *Author : panasonic.lin@163.com *Date : 11/1/2010 *Copyright : GPL *Description : s3c44b0x key driver ************************************************************************ */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> int main(int argc, char **argv) { int fd; int key_status[4]; //以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK fd = open("/dev/s3c44b0keyboard",O_RDONLY|O_NONBLOCK); if(fd < 0) { printf("Open Buttons Device Faild!\n"); exit(1); } while(1) { int i; int ret; fd_set rds; FD_ZERO(&rds); FD_SET(fd, &rds); //应用程序进行轮询,查询是否可对设备进行访问 ret = select(fd + 1, &rds, NULL, NULL, NULL); if(ret < 0) { printf("Read Buttons Device Faild!\n"); exit(1); } if(ret == 0) { printf("Read Buttons Device Timeout!\n"); } else if(FD_ISSET(fd, &rds)) { //读设备 ret = read(fd,&key_status, sizeof(key_status)); if(ret != sizeof(key_status)) { if(errno != EAGAIN) { printf("Read Button Device Faild!\n"); } continue; } else { for(i = 0; i < 4; i++) { //对应驱动中按键的状态,为0即按键被按下 if(key_status== 0) { printf("Key[%d] Press Down!\n",i); } } } } } close(fd); return 0; }第五步: $arm-elf-gcc -static -elf2flt -o buttons_test buttons_test.c 将生成的buttons_test应用程序和s3c44b0key.o模块拷贝到nfs输出目录,这里拷贝到bin目录下。第六步:启动minicom,打开你的板子,开始测试。
关闭
站长推荐
/2
文章评论(0条评论)
登录后参与讨论