驱动作业,按键中断阻塞LED灯

news2025/2/22 13:59:03

 驱动程序

#include "head.h"
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/seqlock.h>
#include <linux/atomic.h>
#include <linux/wait.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/poll.h>
#include <linux/time.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>

char kbuf[128] = {0}; // 用来接用户空间传递的数据

// 定义指针接收映射成功的虚拟内存首地址
gpio_t *vir_led1;
gpio_t *vir_led2;
gpio_t *vir_led3;
unsigned int *vir_rcc;

// 接收struct class/struct device 类型空间的首地址
struct class *cls;
struct device *dev;

struct cdev *cdev;      // 字符驱动设备空间地址
unsigned int major = 0; // 主设备号
unsigned int minor = 0; // 次设备号
dev_t devnum;           // 设备号

unsigned int condition=0;   //定义有无数据判断值

//定义等待队列头
wait_queue_head_t wq_head;

// 设备节点指针
struct device_node *dnode;
// 中断号
unsigned int key_irqnum[3];
// gpio_desc对象指针
struct gpio_desc *desc[3];
unsigned number=0;

// 定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{
    number=!number;
    switch ((int)dev)
    {
    case 0: // key1
        printk("key1 interrupt\n");
        gpiod_set_value(desc[0], !gpiod_get_value(desc[1]));
        break;
    case 1: //key2
        printk("key2 interrupt\n");
        gpiod_set_value(desc[1], !gpiod_get_value(desc[1]));
        break;
    case 2: //key3
        printk("key3 interrupt\n");
        gpiod_set_value(desc[2], !gpiod_get_value(desc[2]));
        break;
    }
    condition=1;
    wake_up_interruptible(&wq_head);
    return IRQ_HANDLED; // 中断正常处理
}

// 封装操作方法,自己封装的函数
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    //把进程切换为休眠态
    wait_event_interruptible(wq_head,condition);
    //把准备好的硬件数据拷贝到用户空间
    ret=copy_to_user(ubuf,&number,size);
    if(ret)
    {
        printk("cpoy_to_user error\n");
        return ret;
    }
    condition=0;    //表示下次数据没有准备好
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    printk("%s:%s:%d:%s\n", __FILE__, __func__, __LINE__, kbuf);
    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,
    .read = mycdev_read,
    .write = mycdev_write,
    .release = mycdev_close,
};

static int __init mycdev_init(void)
{
    int i; // 设备节点
    int ret;
    //初始化等待队列头
    init_waitqueue_head(&wq_head);
    // 1.分配字符设备驱动对象
    // 申请一个字符设备对象驱动空间
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("cdev_alloc error\n");
        ret = -EFAULT;
        goto OUT1;
    }
    printk("申请字符设备驱动成功\n");
    // 2.初始化字符设备驱动对象
    cdev_init(cdev, &fops);
    // 3.申请设备号
    if (major > 0) // 静态申请设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "mychrdev");
        if (ret)
        {
            printk("静态申请设备号失败\n");
            goto OUT2;
        }
        printk("静态申请设备号成功\n");
    }
    else if (major == 0)
    {
        ret = alloc_chrdev_region(&devnum, minor, 3, "mychrdev");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto OUT2;
        }
        printk("动态申请设备号成功\n");
        minor = MINOR(devnum); // 根据设备号获取次设备号
        major = MAJOR(devnum); // 根据设备号获取主设备号
    }
    // 4.注册字符设备驱动对象
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto OUT3;
    }
    printk("注册字符设备驱动对象成功\n");
    // 5.向上提交目录信息
    cls = class_create(THIS_MODULE, "mycdev");
    if (IS_ERR(cls))
    {
        printk("向上提交目录信息失败\n");
        ret = -PTR_ERR(cls);
        goto OUT4;
    }
    printk("向上提交目录信息成功\n");
    // 6.向上提交设备节点信息
    for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "mycdev%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交设备节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto OUT5;
        }
    }
    printk("向上提交设备节点信息成功\n");

    //-------------------------解析按键的设备树节点--------------------------
    dnode = of_find_compatible_node(NULL, NULL, "zmgh,myirq");
    if (dnode == NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析设备树节点成功\n");
    for (i = 0; i < 3; i++)
    {
        // 根据按键的设备树节点解析按键1的软中断号
        key_irqnum[i] = irq_of_parse_and_map(dnode, i);
        if (!key_irqnum[i])
        {
            printk("解析按键1的软中断号失败\n");
            goto OUT6;
        }
        printk("解析按键1的软中断号成功  %d\n", key_irqnum[i]);
        // 注册中断,下降沿触发
        ret = request_irq(key_irqnum[i], key_handler, IRQF_TRIGGER_FALLING, "key", (void *)i);
        if (ret < 0)
        {
            printk("注册中断失败\n");
            return -ENXIO;
        }
    }
    printk("注册中断成功\n");

    // ------------------------解析LED设备树节点,根据路径解析--------------------
    dnode = of_find_node_by_path("/leds");
    if (dnode == NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析设备树节点成功\n");
    // 申请gpio_desc对象并设置输出为低电平
    desc[0] = gpiod_get_from_of_node(dnode, "led-gpios", 0, GPIOD_OUT_LOW, NULL);
    desc[1] = gpiod_get_from_of_node(dnode, "led-gpios", 1, GPIOD_OUT_LOW, NULL);
    desc[2] = gpiod_get_from_of_node(dnode, "led-gpios", 2, GPIOD_OUT_LOW, NULL);
    for (i = 0; i < 3; i++)
    {
        if (IS_ERR(desc[0]))
        {
            printk("申请gpio对象失败\n");
            return -PTR_ERR(desc[i]);
        }
    }
    printk("申请gpio对象成功\n");

    return 0;
OUT6:
    //注销软中断号
    for (--i; i >= 0; i--)
    {
        // 注销中断
        free_irq(key_irqnum[i], NULL);
    }
OUT5:
    // 注销目录信息
    for (--i; i >= 0; i--)
    {
        // 释放提交成功的设备节点信息
        device_destroy(cls, MKDEV(major, i));
    }
    class_destroy(cls); // 销毁目录
OUT4:
    // 注销字符设备驱动对象
    cdev_del(cdev);
OUT3:
    // 注销设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
OUT2:
    // 静态设备号申请失败,就要释放申请的字符驱动空间
    kfree(cdev);
OUT1:
    return ret;
}
static void __exit mycdev_exit(void)
{
    unsigned int i;
    //----------------------------------------销毁目录/设备信息-------------------------------
    //<1.设备销毁,现有目录后有设备,所以先销设备,后销目录
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    //<2.目录销毁
    class_destroy(cls);
    // 注销字符设备驱动对象
    cdev_del(cdev);
    // 释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    // 释放对象空间
    kfree(cdev);
    
    //注销软中断号
    for (--i; i >= 0; i--)
    {
        // 注销中断
        free_irq(key_irqnum[i], NULL);
    }

    // 灭灯
    for (i = 0; i < 3; i++)
    {
        gpiod_set_value(desc[i], 0);
        // 注销GPIO编号
        gpiod_put(desc[i]);
    }
}

module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

应用程序

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include "head.h"

int main(int argc,const char * argv[])
{
    int fd;
    int number;
    fd = open("/dev/mycdev0",O_RDWR);
    if(fd < 0)
    {
        perror("error open ");
        return -1;
    }
    printf("打开设备文件成功\n");
    while(1)
    {
        read(fd,&number,sizeof(number));
        printf("number=%d\n",number);
    }
    return 0;
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/940607.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

抖音转化率是什么意思,怎么计算,如何提升效果?

抖音转化率是指在抖音平台上进行某种特定行为的用户数量与总用户数量之间的比率。作为短视频搜索领域的最大平台&#xff0c;抖音转化率一直是企业用户的攻克难题。 茹莱神兽认为&#xff0c;想要提升抖音的转化率&#xff0c;就需要研究它的转化路径。常见的转化行为包括点击…

Java Predicate用法

Java Predicate用法 无需写sql.只要拼接条件就行 Java Predicate用法

未来智造:珠三角引领人工智能产业集群

原创 | 文 BFT机器人 产业集群是指产业或产业群体在地理位置上集聚的现象&#xff0c;产业集群的研究对拉动区域经济发展&#xff0c;提高区域产业竞争力具有重要意义。 从我国人工智能产业集群形成及区域布局来看&#xff0c;我国人工智能产业发展主要集聚在京津冀、长三角、…

澳大利亚以及美国纽扣电池新标准讲解!

一、什么是澳大利亚纽扣电池认证标准&#xff1f; 答&#xff1a;近期很多商家的产品无论是成品还是单独的纽扣电池&#xff0c;只要成品中带有纽扣的电池的功能都是需要办理澳洲的认证的&#xff0c;最近抽查的比较严格&#xff0c;因为电池测试这方面是为降低与使用纽扣电池相…

【优选算法】—— 二分查找

序言&#xff1a; 本期&#xff0c;我们将要介绍的是有关 二分查找算法 并通过题目帮组大家更好的理解&#xff01; 目录 &#xff08;一&#xff09;基本介绍 1、基本思想 2、解题流程 3、复杂度以及注意事项 &#xff08;二&#xff09;题目讲解 1、在排序数组中查找…

微信好友误删别担心,这几个方法助你轻松加回!

有些人一生气&#xff0c;一怒之下就把好友给删了。事后又想加回来&#xff0c;那要怎么办呢&#xff1f; 删除的好友怎么才能加回来&#xff1f;还是有一些办法的&#xff0c;小伙伴们可以试试&#xff01; 对方的微信号肯定是记不住的&#xff0c;要加回来肯定是一脸糊涂了。…

【C++】C++ 引用详解 ⑧ ( 普通引用与常量引用 | 常量引用概念与语法 )

文章目录 一、普通引用1、概念说明2、代码示例 - 普通引用 二、常量引用1、常量引用引入2、常量引用概念与语法2、代码示例 - 常量引用不可修改 一、普通引用 1、概念说明 之前的 【C】C 引用详解 ① ~ ⑦ 博客中 , 讲解的都是 普通引用 , 也就是 将 普通变量 赋值给 引用 , 过…

docker容器一直在restarting

chown: changing ownership of ‘/var/lib/mysql/’: Permission denied 问题也同样处理 参考

【Zblog搭建博客网站】windows环境搭建博客并上线

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

【管理运筹学】第 6 章 | 运输问题(3,表上作业法 | 最优解的判断)

文章目录 引言二、表上作业法2.2 最优解的判断2.2.1 闭回路法2.2.2 位势法 写在最后 引言 承接前文&#xff0c;我们继续学习表上作业法的第二步 —— 最优解的判断。 二、表上作业法 表上作业法的求解工作在运输表上进行&#xff0c;运输问题解的每一个分量&#xff0c;都唯…

MFC-RIBBON/QT混合编程‘完美’方案

[工程建立] 在&#xff36;&#xff23;用向导生成一个&#xff31;&#xff34; &#xff27;&#xff35;&#xff29;程序&#xff0c;将里面的代码文件等删除&#xff08;.vcxproj等保留&#xff09;在&#xff36;&#xff23;用向导生成另一个&#xff2d;&#xff26;…

怎么学习黑客?最全黑客自学路线

谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一般分为三大圈&#xff1a;娱乐圈 技术圈 职业圈。 娱乐圈&#xff1a;主要是初中生和高中生较多&#xff0c;玩网恋…

乱糟糟的YOLOv8-detect和pose训练自己的数据集

时代在进步&#xff0c;yolo在进步&#xff0c;我还在踏步&#xff0c;v8我浅搞了一下detect和pose&#xff0c;记录一下&#xff0c;我还是要吐槽一下&#xff0c;为啥子这个模型就放在了这个文件深处&#xff0c;如图。 以下教程只应用于直接应用yolov8&#xff0c;不修改。…

k8s之存储篇---数据卷Volume

数据卷概述 Kubernetes Volume&#xff08;数据卷&#xff09;主要解决了如下两方面问题&#xff1a; 数据持久性&#xff1a;通常情况下&#xff0c;容器运行起来之后&#xff0c;写入到其文件系统的文件暂时性的。当容器崩溃后&#xff0c;kubelet 将会重启该容器&#xff…

C++中为什么有模板的函数不能和.h文件分离,即分别声明和定义

目录 1.查看问题 2.探索问题 3.解决问题 1.查看问题 1.先看下面三个文件 stack.h stack.cpp test.cpp 2.探索问题 有了解的小伙伴应该知道大概率是在预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接中编译环节出错了&#xff0c;它在其他文件中无法通过定义找到函…

高效UI设计必备的4个UI设计软件,真的好用!

随着UI设计工作的不断发展&#xff0c;工作中的需求变得更加多样&#xff0c;一个好用的 UI 设计软件将极大减轻设计师的工作负担&#xff0c;提高设计师的工作效率&#xff0c;今天本文精选了4款好用的UI设计软件&#xff0c;并将逐一介绍的它们各自的特点和用法&#xff0c;给…

linux+QT+FFmpeg 6.0,把多个QImage组合成一个视频

直接上代码吧: RecordingThread.h#ifndef RECORDINGTHREAD_H #define RECORDINGTHREAD_H #include "QTimer" #include <QObject> #include <QImage> #include <QQueue>extern "C"{//因为FFmpeg是c语言,QT里面调用的话需要extern "C…

基于Java swing和mysql实现的学生选课管理系统(源码+数据库+运行指导视频)

一、项目简介 本项目是一套Java swing和mysql实现的学生选课管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严…

浮点动作 转 布尔动作(FloatActionToBooleanAction)

文章目录 思维导图具体步骤1、“浮点动作”的创建2、“布尔动作”的创建3、用“FloatToBoolean.cs”把两个动作联系起来 注意 思维导图 具体步骤 1、“浮点动作”的创建 “右手柄 扳机键 按下”需借助“Input.UnityInputManager.1DAxisAction”预设体成为FloatAction&#xf…

Java 读取TIFF JPEG GIF PNG PDF

Java 读取TIFF JPEG GIF PNG PDF 本文解决方法基于开源 tesseract 下载适合自己系统版本的tesseract &#xff0c;官网链接&#xff1a;https://digi.bib.uni-mannheim.de/tesseract/ 2. 下载之后安装&#xff0c;安装的时候选择选择语言包&#xff0c;我选择了中文和英文 3.…