驱动开发-按键中断

编写LED灯的驱动,使用GPIO子系统,里面添加按键的中断处理

1.应用程序发送指令控制LED亮灭

2.按键1 按下,led1电位反转 按键2按下,led2电位反转 按键3 按下,led3电位反转

功能函数

复制代码
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include<unistd.h>
#include<string.h>
#include<sys/ioctl.h>

//功能码
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)

int main(int argc, char const *argv[])
{
    char buf[128] = {0};
    int a,b;
    int fd;
    while (1)
    {
        printf("请输入要控制的灯:0(led1) 1(led2) 2(led3)\n");
        scanf("%d",&a);
        if(a == 0)
        {
            fd = open("/dev/myled0", O_RDWR);
            if (fd < 0)
            {
                printf("打开设备文件失败\n");
                exit(-1);
            }
        }

        else if(a == 1)
        {
            fd = open("/dev/myled1", O_RDWR);
            if (fd < 0)
            {
                printf("打开设备文件失败\n");
                exit(-1);
            }
        }

        else if(a == 2)
        {
            int fd = open("/dev/myled2", O_RDWR);
            if (fd < 0)
            {
                printf("打开设备文件失败\n");
                exit(-1);
            }
        }
    
        printf("请输入控制命令:0(关闭) 1(开灯)>");

        scanf("%d",&b);
        switch(b)
        {
            case 1:
                ioctl(fd,LED_ON);
                break;
            case 0:
                ioctl(fd,LED_OFF);
                break;
        }
        close(fd);
    }

    return 0;
}

驱动代码

复制代码
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/module.h>
#include<linux/fs.h>
#include<linux/io.h>
#include<linux/device.h>


//功能码
#define LED_ON _IOW('l',1,int)
#define LED_OFF _IOW('l',0,int)

unsigned int major;//定义一个变量保存主设备号

char kbuf[128]={0};
struct class *cls;
struct device*device;


struct device_node *dev_led;
struct device_node *dev;
unsigned int irqno1,irqno2,irqno3;
struct gpio_desc *gpiono1;
struct gpio_desc *gpiono2;
struct gpio_desc *gpiono3;


/*
    myirq{
    interrupt-parent=<&gpiof>;//引用中断父节点
    interrupts=<9 0>,<7 0>,<8 0>;//声明和中断父节点的关系 9表示索引号,0表示默认设置
};
*/

//中断处理函数
irqreturn_t myirq_handler1(int irq,void *dev)
{

    gpiod_set_value(gpiono1, !gpiod_get_value(gpiono1)); // LED1
    return IRQ_HANDLED;
}
irqreturn_t myirq_handler2(int irq,void *dev)
{

    gpiod_set_value(gpiono2, !gpiod_get_value(gpiono2)); // LED2
    return IRQ_HANDLED;
}
irqreturn_t myirq_handler3(int irq,void *dev)
{
    gpiod_set_value(gpiono3, !gpiod_get_value(gpiono3)); // LED3
    return IRQ_HANDLED;
}

//封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{
    int a = inode->i_rdev;//获取当前设备文件对应的设备号
    file->private_data=(void*)MINOR(a);//将次设备号保存到当前文件的file结构中
    printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}

long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    unsigned int a=(unsigned int)file->private_data;
    switch(a)
    {
        case 0:
            if(cmd == LED_ON)
            gpiod_set_value(gpiono1,1);
            else
            gpiod_set_value(gpiono1,0);
            break;   
        case 1:
            if(cmd == LED_ON)
            gpiod_set_value(gpiono2,1);
            else
            gpiod_set_value(gpiono2,0);
            break; 
        case 2:
            if(cmd == LED_ON)
            gpiod_set_value(gpiono3,1);
            else
            gpiod_set_value(gpiono3,0);
            break;                          
    }
    return 0;
}

int mycdev_close(struct inode *inode, struct file *file)
{
     printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
    return 0;
}
//定义一个操作方法结构体变量并且初始化
struct file_operations fops={
    .open=mycdev_open,
    .release=mycdev_close,
    .unlocked_ioctl = mycdev_ioctl,
};

static int __init mycdev_init(void)
{
    int i;
    // 字符设备驱动注册
    major = register_chrdev(0, "mychrdev", &fops);
    if (major < 0)
    {
        printk("字符设备驱动注册失败\n");
        return major;
    }
    printk("字符设备驱动注册成功:major=%d\n", major);
    // 向上提交目录
    cls = class_create(THIS_MODULE, "myled");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        return -PTR_ERR(cls);
    }
    printk("向上提交目录信息成功\n");
    // 向上提交设备节点信息
    for (i = 0; i < 3; i++)
    {
        device = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);
        if (IS_ERR(device))
        {
            printk("向上提交设备节点信息失败\n");
            return -PTR_ERR(device);
        }
    }
    printk("向上提交设备节点成功\n");
    

    int ret1,ret2,ret3;
    //解析按键的设备树节点
    dev = of_find_node_by_path("/myirq");
    if(dev==NULL)
    {
        printk("解析设备树节点失败\n");
        return -EFAULT;
    }
    printk("解析设备树节点成功\n");
    //根据设备树节点的路径解析设备树信息
    dev_led = of_find_node_by_path("/leds");
    if(dev_led==NULL)
    {
        printk("解析设备树信息失败\n");
        return -EFAULT;
    }
    printk("解析设备树信息成功\n");
    
    //根据设备树节点解析出软中断号
    irqno1 = irq_of_parse_and_map(dev,0);//按键1索引号为0
    irqno2 = irq_of_parse_and_map(dev,1);//按键2索引号为1
    irqno3 = irq_of_parse_and_map(dev,2);//按键3索引号为2
    if(!irqno1|!irqno1|!irqno2)
    {
        printk("解析软中断号失败\n");
        return -ENXIO;
    }
    printk("解析软中断号成功 irqno=%d %d %d\n",irqno1,irqno2,irqno3);
    //注册中断
    ret1 = request_irq(irqno1,myirq_handler1,IRQF_TRIGGER_FALLING,"key1",NULL);
    ret2 = request_irq(irqno2,myirq_handler2,IRQF_TRIGGER_FALLING,"key2",NULL);
    ret3 = request_irq(irqno3,myirq_handler3,IRQF_TRIGGER_FALLING,"key3",NULL);
    if(ret1|ret2|ret3)
    {
        printk("注册中断失败\n");
        return -EFAULT;
    }
    printk("注册中断成功\n");

    //根据解析到的设备树信息解析出led的gpio编号
    // 申请gpio_desc对象并设置输出为低电平
    gpiono1 = gpiod_get_from_of_node(dev_led, "led1-gpios", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono1))
    {
        printk("申请gpio1对象失败\n");
        return -PTR_ERR(gpiono1);
    }
    printk("申请gpio1对象成功\n");

    gpiono2 = gpiod_get_from_of_node(dev_led, "led2-gpios", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono2))
    {
        printk("申请gpio2对象失败\n");
        return -PTR_ERR(gpiono2);
    }
    printk("申请gpio2对象成功\n");

    gpiono3 = gpiod_get_from_of_node(dev_led, "led3-gpios", 0, GPIOD_OUT_LOW, NULL);
    if (IS_ERR(gpiono3))
    {
        printk("申请gpio对象失败\n");
        return -PTR_ERR(gpiono3);
    }
    printk("申请gpio3对象成功\n");


    return 0;
}
static void __exit mycdev_exit(void)
{

    //销毁设备节点信息
    int i;
    for(i=0;i<3;i++)
    {
        device_destroy(cls,MKDEV(major,i));
    }
    //销毁目录信息
    class_destroy(cls);
    //注销字符设备驱动
    unregister_chrdev(major,"mychrdev");

    //注销中断
    free_irq(irqno1,NULL);
    free_irq(irqno2,NULL);
    free_irq(irqno3,NULL);

    // 灭灯
    gpiod_set_value(gpiono1, 0);
    // 释放gpio编号
    gpiod_put(gpiono1);

    gpiod_set_value(gpiono2, 0);
    gpiod_put(gpiono2);
    
    gpiod_set_value(gpiono3, 0);
    gpiod_put(gpiono3);

}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
相关推荐
AlfredZhao14 小时前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户97183563346620 小时前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪21 小时前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5202 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩2 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈2 天前
Unix 与 Linux 异同小叙
linux·服务器·unix
凡人叶枫2 天前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
2601_961875242 天前
决战申论100题2026|最新|范文
linux·容器·centos·debian·ssh·fabric·vagrant