海思HI3515按键中断驱动程序

安防监控

19人已加入

描述

  海思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

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分