单片机
返回首页

(linux自学笔记)linux按键中断驱动

2024-09-23 来源:cnblogs

通常开发板自带按键中断的驱动,中断已被注册至内核。重新编译linux内核去掉自带驱动才能使用自己编写的驱动。

linux中断程序可分解为顶半部与底半部机制。顶半部完成尽可能少的紧急功能,底半部可以被新的中断打断。

驱动程序


#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include


#define DEVICE_NAME     'key_device'

#define DEVICE_MAJOR    240


void key_tasklet_func(void);


DECLARE_TASKLET(key_tasklet,key_tasklet_func,0);


struct key_irq_desc

{

    int irq;

    int pin;

    int pin_setting;

    int number;

    char *name;


};


static struct key_irq_desc key_irqs [] =

{

    {IRQ_EINT(0), S3C64XX_GPN(0) ,  S3C64XX_GPN0_EINT0 , 0, 'KEY0'},

    {IRQ_EINT(1), S3C64XX_GPN(1) ,  S3C64XX_GPN1_EINT1 , 1, 'KEY1'},

    {IRQ_EINT(2), S3C64XX_GPN(2) ,  S3C64XX_GPN2_EINT2 , 2, 'KEY2'},

    {IRQ_EINT(3), S3C64XX_GPN(3) ,  S3C64XX_GPN3_EINT3 , 3, 'KEY3'},

    {IRQ_EINT(4), S3C64XX_GPN(4) ,  S3C64XX_GPN4_EINT4 , 4, 'KEY4'},

    {IRQ_EINT(5), S3C64XX_GPN(5),   S3C64XX_GPN5_EINT5 , 5, 'KEY5'},

};

static volatile int key_values [] = {1, 1, 1, 1, 1, 1};


//初始化等待列对

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);


static volatile int event_flag = 0;



static irqreturn_t buttons_interrupt(int irq, void *dev_id)

{

    struct key_irq_desc *dev_irqs = (struct key_irq_desc *)dev_id;

    int down;


    down = gpio_get_value(dev_irqs->pin);


    //按键发生了变化

    if (down != (key_values[dev_irqs->number] & 1))

    {

        // Changed


        key_values[dev_irqs->number] = 0 + down;


        event_flag = 1;

        //唤醒列对

        wake_up_interruptible(&button_waitq);

    }


    tasklet_schedule(&key_tasklet);


    return IRQ_RETVAL(IRQ_HANDLED);

}


void key_tasklet_func(void)

{

    printk('<0>=======task========n');

    printk('<0>task function test~n');

    printk('<0>===================n');

}


static int s3c6410_key_open(struct inode *inode, struct file *file)

{

    int i;

    int err = 0;


    //注册中断              结构体未注册完

    for (i = 0; i < sizeof(key_irqs)/sizeof(key_irqs[0]); i++)

    {

        if (key_irqs[i].irq < 0)

        {

            continue;

        }


        //                    中断号                 中断处理寒酸     中断处理属性

        err = request_irq(key_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,

                          key_irqs[i].name, (void *)&key_irqs[i]);

        if (err)

            break;

    }


    //如果注册中断过程中出错,则注销注册成功的中断

    if (err)

    {

        i--;

        for (; i >= 0; i--)

        {

            if (key_irqs[i].irq < 0)

            {

                continue;

            }

            disable_irq(key_irqs[i].irq);

            free_irq(key_irqs[i].irq, (void *)&key_irqs[i]);

        }

        return -EBUSY;

    }



    //显示一次主界面而已

    event_flag = 1;


    return 0;

}



static int s3c6410_keys_close(struct inode *inode, struct file *file)

{

    int i;

    //注销中断

    for (i = 0; i < sizeof(key_irqs)/sizeof(key_irqs[0]); i++)

    {

        if (key_irqs[i].irq < 0)

        {

            continue;

        }

        free_irq(key_irqs[i].irq, (void *)&key_irqs[i]);

    }


    return 0;

}



static int s3c6410_keys_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

    unsigned long err;


    if (!event_flag)

    {

        //没有新按键事件处理,如果用户要求不阻塞就直接返回,否则阻塞在button_waitq队列上。

        if (filp->f_flags & O_NONBLOCK)

            return -EAGAIN;

        else

            wait_event_interruptible(button_waitq, event_flag);

    }


    event_flag = 0;


    err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));


    return err ? -EFAULT : min(sizeof(key_values), count);

}




static struct file_operations key_device_fops =

{

    .owner      =   THIS_MODULE,

    .open       =   s3c6410_key_open,

    .release    =   s3c6410_keys_close,

    .read       =   s3c6410_keys_read,

};




static int __init key_device_init(void)

{

    int ret;

    printk ('<0>key initn');

    //注册字符设备

    ret = register_chrdev(DEVICE_MAJOR,DEVICE_NAME,&key_device_fops);

    if (ret <0)

    {

        printk ('<0>register %s char dev errorn','key');

        return -1;

    }

    printk ('<0>successn');

    return 0;

}


static void __exit key_device_exit(void)

{

    //注销设备

    unregister_chrdev(DEVICE_MAJOR,DEVICE_NAME);

    printk ('<0>module exitn');

}


module_init(key_device_init);

module_exit(key_device_exit);


MODULE_LICENSE('GPL');

MODULE_AUTHOR('hebaichuan');


测试程序,按6键退出死循环。


#include   

#include   

#include   

#include   

  

#define key_exit 5


int main(int argc, char **argv)  

{  

    int fd,ret,i,key_value[6];  

        

    fd = open('/dev/key_device',0);  

    if(fd<0) 

    {  

        printf('open devie errorn');  

        return -1;  

    }  

  

    while(1)

    {  

        ret = read(fd,key_value, sizeof(key_value));  

        if(ret<0) 

        {  

            printf('read errorn');  

            continue;  

        }  

        else if(!key_value[key_exit])

            exit(0);


        for(i=0;i<5;i++)

        {  

            if(key_value[i])

            {

                printf('KEY%d releasedn',(i+1),key_value[i]);

            }  

            else  

            {

                printf('KEY%d pressed n',(i+1),key_value[i]);  

            }    

        }    

        printf('----------------key event----------------n');  

    }  

    close(fd);  

    return 0;  

}  


运行结果:


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 红外线探测报警器

  • 短波AM发射器电路设计图

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 开关电源的基本组成及工作原理

  • 用NE555制作定时器

    相关电子头条文章