摸索了一个星期,终于把海思HI3515开发板的按键中断程序搞出来了,hi3515的核心芯片与网上例子较多的s3c之类的有一些区别,以至于浪费了好些时间去琢磨。管脚配置方式不一样,中断的使用情况也不一样。而比较麻烦的是网上关于海思的资料太少了。对于水平不太高的人,老自己摸索还是会走不少弯路。现在就把本人写的能在开饭板测试运行通过的程序贴出来,但愿这个不会涉及到保密协议的内容,代码可都是我自己写的哈。希望能给后来者带来些帮助,也希望大家多提意见,一起进步·,^_^ 第一步,编写按键驱动程序,button.c代码如下: /*所有模块都需要的头文件*/ #include<linux/module.h> /*声明printk()这个内核态的函数*/ #include<linux/kernel.h> /*文件系统有关的,结构体file_operations也在fs头文件定义*/ #include<linux/fs.h> /*init和exit相关宏*/ #include<linux/init.h> #include<linux/delay.h> #include<linux/poll.h> /*linux中断定义*/ #include<linux/irq.h> /**/ #include<asm/irq.h> /*包含与中断相关的大部分宏及结构体的定义,request_irq()等*/ #include<linux/interrupt.h> /*linux中的用户态内存交互函数,copy_from_user(),copy_to_user()等*/ #include<asm/uaccess.h> //#include<mach/regs-gpio.h> //#include<mach/hardware.h> #include<linux/platform_device.h> #include<linux/cdev.h> /*misc混合设备注_册与注销*/ #include<linux/miscdevice.h> #include <asm/io.h> #include <asm/system.h> #define BUTTON_READ 0x01 #define DEVICE_NAME "BUTTON_irq" #define REG_WRITE(addr,value) ((*(volatile unsigned int *)(addr)) = (value)) #define REG_READ(Addr) (*(volatile unsigned int *)(Addr)) static unsigned int gpio3_virtual_addr = 0; static unsigned int reg_virtual_addr = 0; /*数组中是否有数据标志,0表示无数据可读,1表示有数字可读*/ static volatile char key; /*定义和初始化一个等待队列头*/ static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /*定义一个整形变量,判断按键是否按下*/ static volatile int ev_press = 0; /* *定义结构体类型,由它把按钮中断的信息综合起来 */ struct button_irq_desc { int irq;/*中断号*/ int pin;/*中断标志寄存器,有中断产生时为1,无中断时为0*/ int number;/*编号*/ char *name;/*名称*/ }; static struct button_irq_desc button_irqs[]={ {8,2,1,"KEY1"}, }; static void hi3515_button_pin_cfg(void) { /*配置作为普通输入*/ REG_WRITE(reg_virtual_addr + 0x08,0x1);/*reg2管脚复用配置gpio3_0,按键1*/ REG_WRITE(reg_virtual_addr + 0x0c,0x1);/*reg3管脚复用配置gpio3_1,按键1*/ REG_WRITE(reg_virtual_addr + 0x10,0x1);/*reg4管脚复用配置gpio3_2,按键1*/ REG_WRITE(reg_virtual_addr + 0x14,0x1);/*reg5管脚复用配置gpio3_3,按键2*/ /*管脚中断配置*/ REG_WRITE(gpio3_virtual_addr + 0x0400,0x3);/*dir设置管脚为0-1:输出,2-3:输入*/ REG_WRITE(gpio3_virtual_addr + 0x0404,0xc);/*is边沿触发中断*/ REG_WRITE(gpio3_virtual_addr + 0x040c,0x0);/*iev低电平触发*/ REG_WRITE(gpio3_virtual_addr + 0x041c,0xff);/*ic清除中断*/ REG_WRITE(gpio3_virtual_addr + 0x0410,0x04);/*ie启用中断*/ } /* *gpio地址映射 */ static int virtual_addr_map(void) { reg_virtual_addr = (unsigned int)ioremap_nocache(0x200f0000,0x4000); if(!reg_virtual_addr) { printk("0x200f0000 ioremap addr failed !\n"); return -1; } gpio3_virtual_addr = (unsigned int)ioremap_nocache(0x20180000,0x4000); if(!gpio3_virtual_addr) { printk("0x20180000 ioremap addr failed !\n"); return -1; } } /*取消地址映射*/ static void virtual_addr_unmap(void) { iounmap((void*)gpio3_virtual_addr); iounmap((void*)reg_virtual_addr); } /* *read调用的具体函数,由它读取键盘输入的结果, *实质上就是读取key_values数组的值 *完成键盘输入设备的核心功能,根据标志位ev_press判断是否可读 *如果可读,则读取数据到用户buffer中,如果不可读, *则进程进入等待队列等待,直到数组可读为止 *等待队列机制,所中断管理中常用的机制。 */ static int button_irq_read(struct file *filp, char __user *buff, size_t count,loff_t *offp) { unsigned long err; #if 1 if(!ev_press) /*ev_press=0,则表示没有数据可读*/ { if(filp->f_flags & O_NONBLOCK) return -EAGAIN; else /*无数据可读时,进程休眠,放进button_waitq等待队列*/ wait_event_interruptible(button_waitq,ev_press); /* *wait_event_interruptible()函数将进程置为可中断的挂起状态 *反复检查ev_press=1是否成立,如果不成立,则继续休眠。 *条件满足后,即把本程序置为运行态, */ } /*ev_press=1之后,进程退出等待队列。从此处开始运行*/ ev_press = 0;/*置0标志位,表明本次中断已经处理*/ err = copy_to_user(buff,&key,sizeof(key)); /*把按键值传会用户空间*/ #endif return 0; } static irqreturn_t irq_interrupt(int irq,void *dev_id) { #if 1 /*对传入的中断资源进行处理,获得中断控制寄存器的值(即是否有数据) 取反后赋值给down,为0时说明有数据, 注意按下依次按钮有两次中断, 对数组可读标志位进行设置,ev_press=1表示数组已经可以读了*/ //struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id; int down; down = 0x0c & REG_READ(gpio3_virtual_addr + 0x0414); REG_WRITE(gpio3_virtual_addr + 0x041c,0xff);/*ic清除按键中断*/ mdelay(5);/**/ if(down != 0x0c ) { key = (char)down; ev_press = 1; /*置1标志位,唤醒等待队列进程,在read函数中使用*/ /* *唤醒休眠的进程,用户空间程序使用调用read函数时, *如果没有产生中断,进程就会进入休眠状态,一直等待,直到产生中断 *中断产生后,通过wake_up_interruptible()函数唤醒休眠进程 */ wake_up_interruptible(&button_waitq); } #endif REG_WRITE(gpio3_virtual_addr + 0x0410,0x0c);/*ie启用按键中断*/ return IRQ_HANDLED; //IRQ_HANDLED=1 } /* *poll调用的具体函数,poll实质上是select的调用函数 *如果有按键数据,则select会立刻返回 *如果没有按键数据,则等待 *实质上这是键盘等待输入的机制 *poll_wait()会监测进程队列button_waitq里的进程 *例如button_irq_read所在的进程的标志ev_press置为1了 *那么就不再等待,这实质上就所select函数的运行机制 */ static unsigned int button_irq_poll(struct file *file, struct poll_table_struct *wait) { #if 1 /* *poll调用的具体函数,poll实质上是select的调用函数 *如果有按键数据,则select会立刻返回 *如果没有按键数据,则等待 *实质上这是键盘等待输入的机制。 *select调用是用户程序里面使用的。 */ unsigned int mask = 0; poll_wait(file,&button_waitq,wait); /*poll_wait会检测button_waitq里的进程*/ if(ev_press) mask |=POLLIN | POLLRDNORM; return mask; #endif } static int button_irq_open(struct inode *inode,struct file *file) { #if 1 int err;/*中断注_册返回值*/ virtual_addr_map(); /*地址映射*/ hi3515_button_pin_cfg();/*管脚配置,要先进行地址映射*/ /*注_册中断*/ err = request_irq(8,irq_interrupt,IRQF_SHARED,\ "KEY",(void *)&button_irqs); if(err)/*如果注_册中断失败,则释放已经成功注_册的中断*/ { return -EBUSY; } ev_press = 1; #endif return 0; } static int button_irq_close(struct inode *inode,struct file *file) { free_irq(8,(void *)&button_irqs); virtual_addr_unmap();/*取消地址映射*/ return 0; } int button_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { unsigned int __user *argp = (unsigned int __user *)arg; int value; value = *(unsigned int *)arg; switch (cmd) { case 1: if(value == 0) /*led1亮*/ REG_WRITE(gpio3_virtual_addr +0x4,0); else if(value == 1) /*led1灭*/ REG_WRITE(gpio3_virtual_addr +0x4,1); break; case 2: if(value == 0) /*led2亮*/ REG_WRITE(gpio3_virtual_addr +0x8,0); else if(value == 1) /*led2灭*/ REG_WRITE(gpio3_virtual_addr +0x8,2); break; default: return -1; } return 0; } static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = button_irq_open, .release = button_irq_close, .ioctl = button_ioctl, .read = button_irq_read, .poll = button_irq_poll,/*用户程序使用select调用的时候才会用到poll*/ }; /* *misc混合设备注_册和注销 */ static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR,/*次设备号*/ .name = DEVICE_NAME,/*设备名*/ .fops = &dev_fops,/*设备文件操作结构体*/ }; static int __init button_init(void) { int ret; ret = misc_register(&misc); if(0 != ret) { printk("register device failed! !\n"); return -1; } printk("register device success !\n"); return 0; } static void __exit button_exit(void) { misc_deregister(&misc); printk("unregister device success !\n"); } module_init(button_init); module_exit(button_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Dong"); 第二步。,编写测试程序,test_button.c代码如下 /* *按键中断测试程序 *按键被按下时,产生中断 *打印按下信息,切换led显示状态 */ #include <stdio.h> #include <ctype.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<linux/delay.h> int main(int argc , char* argv[]) { int fd = -1; unsigned int led1; unsigned int led2; ; char key; fd = open("/dev/BUTTON_irq", 0); if (fd<0) { printf("Open BUTTON_irq dev error!\n"); return -1; } for(;;) { int ret; ret = read(fd,&key,sizeof(key)); if(ret< 0) { perror("read button:"); return -1; } if(key == 4)/*按键1被按下*/ { printf("K1 is press!\n"); led1 = (~led1)&0x1; ioctl(fd, 0x01, &led1); /*切换led1的状态*/ } if(key == 8)/*按键2被按下*/ { printf("K2 is press!\n"); led2 = (~led2)&0x1; ioctl(fd, 0x02, &led2); /*切换led2的状态*/ } //printf("\n"); } close(fd); return 0; } 第三步,makefile文件,代码如下: LINUXROOT = /opt/Hi3515_SDK_V1.0.5.1/source/os/linux-2.6.24 #这是放内核的路径 CC = arm-hismall-linux-gcc obj-m := button.o default: $(CC) -g -Wall -o test_button test_button.c @make -C $(LINUXROOT) M=$(PWD) modules rm -rf *.o *.mod.c *.symvers clean: @make -C $(LINUXROOT) M=$(PWD) clean |
|