Linux驱动:外部中断

Linux驱动程序:外部中断

实验平台

内核版本:Linux-4.19.5
开发板:SAMSUNG JZ2440

主要函数

1.中断注册函数
函数原型
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
				const char *name, void *dev_id)
函数参数解析

irq:中断编号,在\arch\arm\mach-s3c24xx\include\mach\irqs.h头文件中定义了各个中断编号的宏定义
handler:中断执行函数,就是发生中断的时候会自动跳转执行的函数
flags:中断类型,在include\linux\interrupt.h头文件中定义了各个中断类型的宏定义
name:中断的名称
dev_id:传参,中断执行函数有两个传入参数一个是中断号irq,另一个就是dev_id指针

函数功能

注册一个中断,并设置对应的引脚

2.中断释放函数
函数原型
void *free_irq(unsigned int irq, void *dev_id)
函数参数解析

irq:中断编号,free_irq函数通过传入的irq,dev_id来释放中断,填写和request_irq中相同的参数,就可以释放对应的中断。
dev_id:同上。

函数功能

释放一个中断

3.中断执行函数
函数原型
irqreturn_t (*irq_handler_t)(int irq, void *dev_id)
函数参数解析

irq:中断编号,触发该中断的中断编号
dev_id:注册中断时候request_irq,设置的dev_id指针的值

函数功能

中断执行函数,每次中断触发都会执行该函数

实验程序

/* 驱动程序 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>

int major;	//主设备号

static struct class *buttons_class;

volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;	//返回给用户的键值


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);	//休眠队列
static volatile int ev_press = 0;	//条件变量

const int t_s3c2440_devid[4] = {1, 2, 3, 4};	//键值数组

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
    int i_pinselect = *((int *)dev_id);
    int i_pinval = 0; 
    unsigned long ul_regval;

    switch(i_pinselect)
    {
        case 1:
        case 2:
            ul_regval = *gpfdat;		//读寄存器gpfdat的值
            if (i_pinselect == 1)
                i_pinval = (ul_regval & (1<<0)) ? 1 : 0;
            else
                i_pinval = (ul_regval & (1<<2)) ? 1 : 0;
            break;
        case 3:
        case 4:
            ul_regval = *gpgdat;		//读寄存器gpgdat的值
            if (i_pinselect == 3)
                i_pinval = (ul_regval & (1<<3)) ? 1 : 0;
            else
                i_pinval = (ul_regval & (1<<11)) ? 1 : 0;
            break;
    }

    if (i_pinval)	//按下读回来的值为0,松开读回来的值为1
    {
        /* 松开 */
        key_val = 0x80 | i_pinselect;
    }
    else
    {
        /* 按下 */
        key_val = i_pinselect;
    }

    ev_press = 1;                  			/* 表示中断发生了 */
    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */


    return IRQ_RETVAL(IRQ_HANDLED);
}


static int buttons_open(struct inode *inode, struct file *file)
{
    int i_ret;
	/* 注册一个名为S2的外部中断,上升沿和下降沿触发,中断执行函数为buttons_irq */
    i_ret = request_irq(IRQ_EINT0,  buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S2", (void *)&t_s3c2440_devid[0]);	
    i_ret = request_irq(IRQ_EINT2,  buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S3", (void *)&t_s3c2440_devid[1]);	
    i_ret = request_irq(IRQ_EINT11, buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S4", (void *)&t_s3c2440_devid[2]);	
    i_ret = request_irq(IRQ_EINT19, buttons_irq, IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING, "S5", (void *)&t_s3c2440_devid[3]);	

    return 0;
}

ssize_t buttons_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    int i_ret;
    if (size != 1)
        return -EINVAL;

    /* 如果没有按键动作, 休眠 */
    wait_event_interruptible(button_waitq, ev_press);

    /* 如果有按键动作, 返回键值 */
    i_ret = copy_to_user(buf, &key_val, 1);
    ev_press = 0;

    return 1;
}

int buttons_drv_close(struct inode *inode, struct file *file)
{
    free_irq(IRQ_EINT0, (void *)&t_s3c2440_devid[0]);	//释放中断
    free_irq(IRQ_EINT2, (void *)&t_s3c2440_devid[1]);
    free_irq(IRQ_EINT11, (void *)&t_s3c2440_devid[2]);
    free_irq(IRQ_EINT19, (void *)&t_s3c2440_devid[3]);
    return 0;
}


static struct file_operations buttons_fops=
{
    .owner = THIS_MODULE,
    .open = buttons_open,
    .read = buttons_read,
    .release =  buttons_drv_close,
};

static int buttons_init(void)
{
    major = register_chrdev(0, "buttons", &buttons_fops);	//注册一个字符设备

    buttons_class = class_create(THIS_MODULE, "buttons");	//创建一个类

    device_create(buttons_class, NULL, MKDEV(major,0), NULL, "buttons");	//在类下面创建一个设备


    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);		//寄存器指针重定位
    gpfdat = gpfcon + 1;

    gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);		//寄存器指针重定位
    gpgdat = gpgcon + 1;

    return 0;
}

static void buttons_exit(void)
{
    device_destroy(buttons_class, MKDEV(major,0));

    class_destroy(buttons_class);

    unregister_chrdev(major, "buttons");

    iounmap(gpfcon);
    iounmap(gpgcon);
}

module_init(buttons_init);

module_exit(buttons_exit);

MODULE_LICENSE("GPL");
/* 测试程序 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

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

        fd = open("/dev/buttons", O_RDWR);
        if (fd < 0)
        {
                printf("can't open!\n");
        }

        while (1)
        {
                read(fd, &key_val, 1);
                printf("key_val = 0x%x\n", key_val);
        }

        return 0;
}

Linux驱动:外部中断
Linux驱动:外部中断
装载模块以后运行测试程序,可以发现CPU占用率不高,一般都是处于休眠状态。然后按下和释放按键可以打印出对应的键值。