Linux 驱动——INTERRUPT(TASKLET)

说明:

  平台设备:正点原子IMX6ULL

第一部分:

  修改设备树,在跟节点下添加key设备: 

key {
  #address-cells = <1>;
  #size-cells = <1>;
  compatible = "atkalpha-key";
  pinctrl-names = "default";
  pinctrl-0 = <&pinctrl_key>;
  key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
  interrupt-parent = <&gpio1>;
  interrupts = <18 IRQ_TYPE_EDGE_BOTH>;
  status = "okay";
};

第二部分:

  irqkeytasklet.c 驱动文件:

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define KEY_CNT 1
#define KEY_NAME "irqkeytasklet"

atomic_t key_status = ATOMIC_INIT(0); //定义整型原子变量, 保存按键状态, 设置初始值为0

struct tasklet_struct btn_tasklet;    //使用tasklet实现中断分层,在此申请tasklet_struct结构体变量

struct key_dev{
  dev_t devid;                       //设备号
  struct cdev cdev;                  //字符设备
  struct class *class;               //
  struct device *device;             //设备
  int major;                         //主设备号
  int minor;                         //次设备号
  struct device_node *nd;            //设备节点

  int gpio_number;
  int irq_number;
};

struct key_dev irqkey;

void btn_tasklet_handler(unsigned long data)                                                          //中断下半部分
{
  int counter = 1;

  mdelay(200);
  printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
  mdelay(200);
  printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
  mdelay(200);
  printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
  mdelay(200);
  printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
  mdelay(200);
  printk(KERN_ERR "button_tasklet_hander counter = %d \n", counter++);
}


static irqreturn_t irqkey_hander(int irq, void *dev_id)
{

  printk(KERN_ERR "button_irq_hander----------inter \n");
  atomic_inc(&key_status);                                                                           //按键状态加1

   tasklet_schedule(&button_tasklet);                                                                 //触发中断下半部分

  printk(KERN_ERR "button_irq_hander----------exit \n");

  return IRQ_HANDLED;
}


static int irqkey_open(struct inode *inode, struct file *filp)
{
  int error = -1;

  irqkey.nd = of_find_node_by_path("/key");                                                          //获取按键设备树节点
  if(NULL == irqkey.nd)
  {
    printk("of_find_node_by_path error!");
    return -1;
  }

  irqkey.gpio_number = of_get_named_gpio(irqkey.nd, "key-gpio", 0);                                  //获取按键所使用的GPIO
  if(0 == irqkey.nd)
  {
    printk("of_get_named_gpio error!");
    return -1;
  }
  error = gpio_request(irqkey.gpio_number, "irqkey_gpio");                                           //申请GPIO 
  if(error != 0)
  {
    printk("gpio_request error");
    gpio_free(irqkey.gpio_number);
    return -1;
  }
  gpio_direction_input(irqkey.gpio_number);                                                          //引脚设置, 将引脚设置为输入
  irqkey.irq_number = irq_of_parse_and_map(irqkey.nd, 0);                                            //获取引脚中断号
  error = request_irq(irqkey.irq_number, irqkey_hander, IRQF_TRIGGER_RISING, "irq_key", &irqkey);    //申请中断
  if(error != 0)
  {
    printk("request_irq error");
    free_irq(irqkey.irq_number, &irqkey);
    return -1;
  }

  tasklet_init(&btn_tasklet, btn_tasklet_handler, 0);                                                //tasklet初始化
  return 0;
}

static ssize_t irqkey_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
  int error = -1;
  int button_countervc = 0;

  button_countervc = atomic_read(&key_status);                                                       //读取按键状态值
  error = copy_to_user(buf, &button_countervc, sizeof(button_countervc));                            //拷贝到用户空间
  if(error < 0)
  {
    printk("copy_to_user error");
    return -1;
  }
  atomic_set(&key_status, 0);                                                                        //清零按键状态值
  return 0;
}

static ssize_t irqkey_release(struct inode *inode, struct file *filp)
{
  gpio_free(irqkey.gpio_number);                                                                     //释放引脚
  free_irq(irqkey.irq_number, &irqkey);                                                              //释放中断
  return 0;
}

static struct file_operations irqkey_fops = {
  .owner = THIS_MODULE,
  .open = irqkey_open,
  .read = irqkey_read,
  .release = irqkey_release,
};

static int __init irqkey_init(void)
{

  if(irqkey.major)
  {
    irqkey.devid = MKDEV(irqkey.major, 0);
    register_chrdev_region(irqkey.devid, KEY_CNT, KEY_NAME);
  }
  else
  {
    alloc_chrdev_region(&irqkey.devid, 0, KEY_CNT, KEY_NAME);
    irqkey.major = MAJOR(irqkey.devid);
    irqkey.minor = MINOR(irqkey.devid);
  }
  printk("irqkey major = %d, minor = %d \r\n", irqkey.major, irqkey.minor);


  irqkey.cdev.owner = THIS_MODULE;                                                                   //初始化字符设备并向Linux内核添加
  cdev_init(&irqkey.cdev, &irqkey_fops);
  cdev_add(&irqkey.cdev, irqkey.devid, KEY_CNT);

  irqkey.class = class_create(THIS_MODULE, KEY_NAME);                                                //创建设备类
  if(IS_ERR(irqkey.class))
  {
    return PTR_ERR(irqkey.class);
  }

  irqkey.device = device_create(irqkey.class, NULL, irqkey.devid, NULL, KEY_NAME);                   //创建设备
  if(IS_ERR(irqkey.device))
  {
    return PTR_ERR(irqkey.device);
  }
  return 0;
}


static void __exit irqkey_exit(void)
{
  cdev_del(&irqkey.cdev);                   //注销字符设备
  unregister_chrdev_region(irqkey.devid, KEY_CNT);      //注销设备号
  device_destroy(irqkey.class, irqkey.devid);           //注销设备节点
  class_destroy(irqkey.class);                          //注销设备类
}

module_init(irqkey_init);
module_exit(irqkey_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("LMENG");

第三部分:

  Makefile 文件:

KERNELDIR := /home/lmeng/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek

CURRENT_PATH := $(shell pwd)

obj-m := irqkey.o

build : kernel_modules

kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean

第四部分:

  irqkeyApp.c 文件:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
  int error = -20;
  int button_status = 0;

  int fd;
  char *filename;

  filename = argv[1];
  fd = open(filename, O_RDWR);

  if(fd < 0) 
  {
    printf("can‘t open file %s\r\n", filename);
    return -1;
  }

  printf("wait button down... \n");
  printf("wait button down... \n");

  do
  {
    error = read(fd, &button_status, sizeof(button_status)); //读取按键状态

    if (error < 0)
    {
      printf("read file error! \n");
    }
    usleep(1000 * 100); 

  } while (0 == button_status);

  printf("button Down !\n");

  error = close(fd);

  if (error < 0)
  { 
    printf("close file error! \n");
  }
  return 0;
}

第五部分:

实验结果:

Linux 驱动——INTERRUPT(TASKLET)

 

Linux 驱动——INTERRUPT(TASKLET)

上一篇:Vim文本编辑器的下载与使用


下一篇:HTML5 Canvas实战之刮奖效果