前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >linux 中断机制《Rice linux 学习笔记》

linux 中断机制《Rice linux 学习笔记》

作者头像
Rice加饭
发布2022-05-09 21:06:40
4.7K0
发布2022-05-09 21:06:40
举报
文章被收录于专栏:Rice嵌入式Rice嵌入式

这篇文章以按键为例子讲解linux的中断原理,中断的架构,中断的实现。

在我们的开发中,检测按键是否触发,无非就两种方法—轮询和中断。作者认为两种方法最大的区别就是CPU的利用率。

轮询方法:一直占用CPU资源,CPU利用率不高。

中断方法:当产生中断时,占用CPU资源;否则将释放CPU资源。CPU利用率高。

Linux的中断原理:

中断:处理器异常的一种,它是一种特殊的电信号,用硬件发给处理器,处理器接收到中断后,会马上向操作系统反映此信号的到来,然后就由操作系统负责处理这些新到来的数据。

中断服务程序:相应特定中断时,内核会执行的函数。其中:1、中断处理程序是被内核调用来响应中断的。2、而它们运行于我们称之为中断上下文的特殊上下文中。

中断上下部:为了是程序运行得快和完成的工作量多,将中断处理分为两部分,上半部和下半部,其中,上半部(中断处理程序):接收到中断,立马要执行的程序,只做有严格时限的工作。下半部:允许稍后完成的工作。Linux中断的相关函数:

1、注册中断函数:

中断处理函数结构:

typedef irqreturn_t (*irq_handler_t)(int, void*);

注册中断函数:int request_irq(unsigned int irq,

irq_handler_t handler,

unsigned long flags,

const char *name,

void *dev);

其中: irq:要申请的中断号,CPU为每一个外设的中断编号,是一个虚拟的interrupt ID,与硬件无关,给CPU用来标识一个外设中断。

handler:指向该中断的中断处理程序

flags:中断标志

name:设备名

dev,用于共享中断线

2、释放中断函数:void free_irq(unsigned int irq,void* dev );

其中:irq:处理程序要相应的中断处理程序。

dev:与request_irq的参数dev必须一致,将request_irq指定的dev传递给这个参数;

Linux中断处理分析:

第一种方式:应用层的读,不管什么情况下,都会返回。

第二种方式:应用层的读,只有按键按下的时候,才会返回。

Linux中断实现:

这里例子采用等待队列的方式实现(也就是上述的第二种方法)。

等待队列的方法:

1,定义等待队列:wait_queue_head_t my_queue

2,初始化等待队列:init_waitqueue_head ( &my_queue )

3,定义并初始化等待队列:DECLARE_WAIT_QUEUE_HEAD ( my_queue )

定义等待队列并进行初始化(button_wait_queue):

static DECLARE_WAIT_QUEUE_HEAD(button_wait_queue);

请求中断的实现:

open函数的实现:通过reques_irq函数,请求中断。

其中:IRQ_EINT0:按键对应引脚的中断号。

button_irq:该中断指向中断函数。

IRQT_BOTHEDGE:中断类型标志,这里采用双边沿出发。

1. IRQT_NOEDGE

2. IRQT_RISING//上升沿触发

3. IRQT_FALLING //下降沿触发

4. IRQT_BOTHEDGE//双边沿触发

5. IRQT_LOW//低电平触发

6. IRQT_HIGH//高电平触发

7. IRQT_PROBE

"KEY1":设备名。

&pins_desc[0]:用于共享中断线,请求中断和卸载中断会用到。

代码语言:javascript
复制
static int button_open(struct inode *inode, struct file *file)
{
  printk("button irq driver: open\r\n");

  request_irq(IRQ_EINT0, button_irq, IRQT_BOTHEDGE, "KEY1", &pins_desc[0]);

  return 0;
}

其中pins_desc的定义:

代码语言:javascript
复制
struct pin_desc {
  unsigned int pin;
  unsigned int key_val;
};

struct pin_desc pins_desc[4] = {
  { GPF0,0xF0},
};

卸载中断的实现:

close函数的实现:通过free_irq卸载中断。

其中:IRQ_EINT0:按键对应引脚的中断号。与加载的中断号一致。

&pins_desc[0]:用于共享中断线,与加载时保持一致

代码语言:javascript
复制
static int button_close(struct inode *inode, struct file *file)
{
  free_irq(IRQ_EINT0, &pins_desc[0]);
  return 0;
}

中断函数的实现:

button_irq函数的实现:该函数与请求中断注册的中断函数名保持一致。

其中函数参数:irq:中断对应的中断号。

Dev_id:与请求中断函数的第五个参数保持一致。

通过wake_up_interruptible()函数实现等待队列的唤醒。

代码语言:javascript
复制
static irqreturn_t button_irq(int irq, void *dev_id)
{
  struct pin_desc *pindesc = (struct pin_desc *)dev_id;
  unsigned int pin_val;
  
  pin_val = GPIO_GET_PIN(pindesc->pin);

  if(pin_val)
    key_val = 0x08 | pindesc->key_val;
  else
    key_val = pindesc->key_val;
  
  ev_press = 1;
  wake_up_interruptible(&button_wait_queue);

  return IRQ_RETVAL(IRQ_HANDLED);  
}

按键值返回用户层的实现:

read函数的实现:通过检测wait_event_interruptible()等待是否被唤醒,其中当ev_press等于1的时候,程序仅需往下运行,如果等于0,则进入睡眠。

代码语言:javascript
复制
static ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
  wait_event_interruptible(button_wait_queue, ev_press);

  copy_to_user(buf, &key_val, 1);
  
  ev_press = 0;

  return sizeof(key_val);
}

file_operations结构体的实现:

代码语言:javascript
复制
static struct file_operations buttons_fops = {
  .owner    = THIS_MODULE,
  .open    = button_open,
  .read   = button_read,
  .release = button_close,
};

模块初始化函数:

其中:register_chrdev():申请设备号。

class_create():创建一个设备类。

device_create():创建一个设备节点,路径:/dev/xxx

代码语言:javascript
复制
static int __init button_init(void)
{
  major = register_chrdev(0, DEVICE_NAME, &buttons_fops);

  button_class = class_create(THIS_MODULE, "button_driver");
  if (IS_ERR(button_class))
    return PTR_ERR(button_class);

  button_class_device = class_device_create(button_class, NULL, MKDEV(major, 0), NULL, "button");
  if (unlikely(IS_ERR(button_class_device)))
      return PTR_ERR(button_class_device);

  printk("button driver: init\r\n");

  return 0;
}

模块卸载函数:

其中:unregister_chrdev():通过主设备号,注销设备号

device_destroy():注销设备节点

class_destroy():注销设备类

代码语言:javascript
复制
static void __exit button_exit(void)
{
  unregister_chrdev(major, DEVICE_NAME);
  class_device_unregister(button_class_device);
  class_destroy(button_class);
  printk("button drive: exit\r\n");
}

驱动编写好,写个应用程序进行测试:

当有按键按下时,则输出一句话。

代码语言:javascript
复制
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

#define file_name  "/dev/button"

int main(int argc, char **argv)
{
  int fd;
  char key_val;

  fd = open(file_name, O_RDWR);
  if(fd < 0) {
    printf("error: can`t open %s", file_name);
    return 0;
  }
  for(;;)  {
    read(fd, &key_val, 1);
    printf("button input: %d\r\n", key_val);
  }
  return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rice 嵌入式开发技术分享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档