驱动开发-按键中断

编写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");
相关推荐
康熙38bdc1 小时前
Linux 进程优先级
linux·运维·服务器
hhzz1 小时前
Linux Shell编程快速入门以及案例(Linux一键批量启动、停止、重启Jar包Shell脚本)
android·linux·jar
只是有点小怂1 小时前
parted是 Linux 系统中用于管理磁盘分区的命令行工具
linux·运维·服务器
三枪一个麻辣烫2 小时前
linux基础命令
linux·运维·服务器
cuisidong19972 小时前
如何在 Kali Linux 上安装 Google Chrome 浏览器
linux·运维·chrome
嵌入式小能手3 小时前
开发环境搭建之VScode的安装及使用
vscode·编辑器
光通信学徒3 小时前
ubuntu图形界面右上角网络图标找回解决办法
linux·服务器·ubuntu·信息与通信·模块测试
南种北李3 小时前
Linux自动化构建工具Make/Makefile
linux·运维·自动化
小飞猪Jay3 小时前
面试速通宝典——10
linux·服务器·c++·面试
憧憬一下4 小时前
驱动中的device和device_driver结构体
驱动开发·嵌入式