20230713-------通过platform实现阻塞IO来驱动按键控制LED灯的亮灭

news2024/11/26 13:42:09

需添加的设备树节点

	myplatform{ 
		compatible = "hqyj,myplatform"; 
		reg=<0X12345678 0X400>; 
    	interrupt-parent=<&gpiof>; 
    	interrupts=<9 0>;   //9表示引用中断父节点时的索引信息  0表示默认设置 
       	led1=<&gpioe 10 0>;

pdev.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/platform_device.h>
 //release函数,用于释放申请的资源
 void   pdev_release(struct device *dev)
 {
      printk("%s:%s:%d\n",__FILE__,__func__,__LINE__);
 }
 //资源数组
  struct resource res[]={
     [0]={
         .start=0X12345678,
         .end=0X12345678+59,
         .flags=IORESOURCE_MEM,     
     },   
     [1]={
         .start=71,
         .end=71,
         .flags=IORESOURCE_IRQ,     
     },   
  };
    //定义并初始化
 struct platform_device pdev={
     .name="aaaaa",
     .id=PLATFORM_DEVID_AUTO,
     .dev={
         .release=pdev_release,     
     },
     .num_resources=ARRAY_SIZE(res),
     .resource=res,
 };
 
static int __init mycdev_init(void)
{
    //注册
    platform_device_register(&pdev);
    return 0;
}
static void __exit mycdev_exit(void)
{
    //注销
    platform_device_unregister(&pdev);
 
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");

pdrv.c

#include <linux/init.h>
#include <linux/module.h>
#include<linux/platform_device.h>
#include<linux/mod_devicetable.h>
#include<linux/of.h>
#include<linux/of_gpio.h>
#include<linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/of_irq.h>


struct resource *res;
unsigned int irqno;
struct gpio_desc *gpiono;
 
struct cdev *cdev;
unsigned int major = 500;
unsigned int minor = 0; // 次设备号的起始值
dev_t devno;
struct class *cls;
struct device *dev;
// 定义等待队列头
wait_queue_head_t wq_head;
unsigned int condition = 0;

unsigned int number;
struct device_node *dnode;

// 中断处理函数
irqreturn_t myirq_handler(int irqno, void *dev_id)
{
    if (number == 0)
    {
        number = 1;
        gpiod_set_value(gpiono, 1); // 亮灯
    }
    else
    {
        number = 0;
        gpiod_set_value(gpiono, 0); // 灭灯
    }
    condition=1;//表示硬件数据就绪
    wake_up_interruptible(&wq_head);//唤醒休眠的进程
    //printk("number = %d\n", number); // 打印此时的number的值
    return IRQ_HANDLED;
}
//封装字符设备驱动相关API
//open()
int mycdev_open(struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    return 0;
}
//ioctl()
/*{
    //根据给功能码实现相关控制工作
}*/
ssize_t mycdev_read(struct file *file, char *ubuf, size_t size, loff_t *lof)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);
    
    // 向用户空间读取拷贝
    if (size > sizeof(number)) // 用户空间期待读取的大小内核满足不了,那就给内核支持的最大大小
        size = sizeof(number);
    wait_event_interruptible(wq_head, condition); // 将进程切换为休眠
    ret = copy_to_user(ubuf, &number, size);
    if (ret) // 拷贝失败
    {
        printk("copy_to_user filed\n");
        return ret;
    }
    condition = 0; // 表示下一次硬件数据没有准备好
    return 0;
}
ssize_t mycdev_write(struct file *file, const char *ubuf, size_t size, loff_t *lof)
{
    return 0;
}
//close()
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,
};
//probe函数用于匹配设备成功后执行
 int pdrv_probe(struct platform_device *pdev)//当和设备匹配成功之后执行probe
 {
    int ret, i;
    // 初始化等待队列头
    init_waitqueue_head(&wq_head);
    
    //1.为字符设备驱动对象分配空间  cdev_alloc()
    cdev = cdev_alloc();
    if (cdev == NULL)
    {
        printk("申请字符设备驱动对象空间失败\n");
        ret = -EFAULT;
        goto out1;
    }
    printk("字符设备驱动对象申请成功\n");
    
    //2.完成驱动对象部分初始化//cdev_init
    cdev_init(cdev, &fops);
    //3.申请设备号  register_chrdev_region()/alloc_chrdev_region
    if (major > 0) // 静态申请设备号
    {
        ret = register_chrdev_region(MKDEV(major, minor), 3, "myplatform");
        if (ret)
        {
            printk("静态指定设备号失败\n");
            goto out2;
        }
    }
    else // 动态申请设备号
    {
        ret = alloc_chrdev_region(&devno, minor, 3, "myplatform");
        if (ret)
        {
            printk("动态申请设备号失败\n");
            goto out2;
        }
        major = MAJOR(devno); // 根据设备号得到主设备号
        minor = MINOR(devno); // 根据设备号得到次设备号
    }
    printk("申请设备号成功\n");
    //4.注册驱动对象   cdev_add
    ret = cdev_add(cdev, MKDEV(major, minor), 3);
    if (ret)
    {
        printk("注册字符设备驱动对象失败\n");
        goto out3;
    }
    printk("注册字符设备驱动对象成功\n");
    //5.向上提交目录   class_create()
    cls = class_create(THIS_MODULE, "myplatform");
    if (IS_ERR(cls))
    {
        printk("向上提交目录失败\n");
        ret = -PTR_ERR(cls);
        goto out4;
    }
    printk("向上提交目录成功\n");
    //6.向上提交设备节点  device_create()
     for (i = 0; i < 3; i++)
    {
        dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myplatform%d", i);
        if (IS_ERR(dev))
        {
            printk("向上提交节点信息失败\n");
            ret = -PTR_ERR(dev);
            goto out5;
        }
    }
    printk("向上提交设备节点信息成功\n");

    //7.根据设备树节点结构体指针解析GPIO信息
     dnode = of_find_node_by_name(NULL, "myplatform");
    if (dnode == NULL)
    {
        printk("解析设备树节点失败\n");
        return -ENXIO;
    }
    printk("解析设备树节点成功\n");

    // 获取软中断号
    irqno = irq_of_parse_and_map(dnode, 0);
    if (!irqno)
    {
        printk("软中断号获取失败\n");
        return -ENOMEM;
    }
    printk("软中断号获取成功 irqno = %d\n", irqno);

    // 注册中断
    ret = request_irq(irqno, myirq_handler, IRQF_TRIGGER_FALLING, "myplatform1", NULL);
    if (ret)
    {
        printk("注册驱动失败\n");
        return ret;
    }
    printk("key1中断注册成功\n");

    // 根据设备树节点解析led1 gpio结构体并向内核注册
    gpiono = gpiod_get_from_of_node(dnode,"led1",0,GPIOD_OUT_LOW,NULL);
    if (IS_ERR(gpiono))
    {
        printk("申请gpio失败\n");
        return -PTR_ERR(gpiono);
    }

    gpiod_set_value(gpiono, 0); // 灭灯
 
     return 0; 

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; 
    //8.LED灯灭
    
 }
 //remove用于和设备分离时执行
  int pdrv_remove(struct platform_device *pdev)//当设备和驱动分离时执行remove
  {
    int i;
    //1.释放GPIO信息
    gpiod_put(gpiono);
    //2.销毁设备信息
    
    for (i = 0; i < 3; i++)
    {
        device_destroy(cls, MKDEV(major, i));
    }
    //3.销毁目录信息
    class_destroy(cls);
    //4.注销驱动对象
     cdev_del(cdev);
    //5.释放设备号
    unregister_chrdev_region(MKDEV(major, minor), 3);
    //6.释放字符设备驱动对象空间
     kfree(cdev);
      return 0;  
  }
//在驱动中创建一个用于设备树匹配的表
struct of_device_id oftable[]={
    {.compatible="hqyj,myled",},
    {},
};
  //分配对象并初始化
struct platform_driver pdrv={
    .probe=pdrv_probe,
    .remove=pdrv_remove,
    .driver={
        .name="aaaaa", 
        .of_match_table=oftable,  //指定设备树匹配表的首地址 
    },
 
};
//一键注册宏,代替入口出口
module_platform_driver(pdrv);
MODULE_LICENSE("GPL");

test.c

#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>
 
 
int main(int argc, char const *argv[])
{
    unsigned int number;
    int fd = open("/dev/myplatform0", O_RDWR);
    if (fd < 0)
    {
        printf("打开设备文件失败\n");
        exit(-1);
    }
    
        while(1)
        {
        read(fd,&number,sizeof(number));//读取数据
        printf("number = %d\n",number);
    
        }
    close(fd);
 
    return 0;
}

实验结果

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

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

相关文章

linux 安装pytorch3d的坑

事实上&#xff0c;只要按照官方文档的说明就可以完美安装。其中坑的地方在于conda的管理可能会导致下载的版本不符合你的要求&#xff08;例如下载成了cpu版本、下载的cuda版本&#xff09;而同样尝试使用源码编译以及其他方式下载库都会导致同样的问题&#xff0c;这里主要的…

【动手学深度学习】层和块

层和块 简单介绍 块&#xff1a;描述单个层&#xff0c;由多个层组成的组件或整个模型本身。使用块进行抽象的一个好处是可以将一些块组合成更大的组件&#xff0c;这一过程通常是递归的 简单入门 import torch from torch import nn from torch.nn import functional as F# …

【分布式系统案例课】计数服务之需求收集和总架构设计

面试题 对B站视频观看量进行实时的计数 技术问题是一个比较普遍的问题&#xff0c;比如对头条作者的粉丝或者是对获赞进行计数。或者是对企业的业务指标进行计数&#xff0c;例如注册登录下单数这些等。 需求澄清 问题一&#xff1a;用户点击观察视频之后&#xff0c;这个数量…

Gateway网关组件(在Spring Cloud整合Gateway(idea19版本))

Spring Cloud Gateway官网:Spring Cloud Gateway 局域网中就有网关这个概念&#xff0c;局域网接收数据或发送数据都要通过网关&#xff0c;比如使用VMware虚拟机软件搭建虚拟机集群的时候&#xff0c;往往我们需要选择IP段中的⼀个IP作为网关地址,网关可以对请求进行控制,提升…

Shell第三章——循环语句与函数

循环&#xff1a;重复执行一段代码的结构&#xff0c;通过循环可以在满足一定的条件之下多次执行相同的代码。 循环语句&#xff1a;包换循环体&#xff0c;代码的总结构&#xff0c;循环条件&#xff0c;当循环条件满足时&#xff0c;循环体的代码才会执行&#xff0c;条件不…

RabbitMQ-同步和异步通讯、安装和入门案例、SpringAMQP(5个消息发送接收Demo,jackson消息转换器)

文章目录 1.初识MQ1.1.同步和异步通讯1.1.1.同步通讯1.1.2.异步通讯 1.2.技术对比&#xff1a; 2.快速入门2.1.安装RabbitMQ2.2.RabbitMQ消息模型2.3.导入Demo工程2.4.入门案例2.4.1.publisher实现2.4.2.consumer实现 2.5.总结 3.SpringAMQP3.1.Basic Queue 简单队列模型3.1.1.…

【设计模式】23种设计模式——工厂模式(原理讲解+应用场景介绍+案例介绍+Java代码实现)

工厂模式 需求了解 看一个披萨的项目&#xff1a;要便于披萨种类的扩展&#xff0c;要便于维护 披萨的种类很多(比如 GreekPizz、CheesePizz 等)披萨的制作有 prepare&#xff08;准备材料&#xff09;,bake&#xff08;烘焙&#xff09;,cut&#xff08;切割&#xff09;,b…

Hive SQL 迁移 Flink SQL 在快手的实践

摘要&#xff1a;本文整理自快手数据架构工程师张芒&#xff0c;阿里云工程师刘大龙&#xff0c;在 Flink Forward Asia 2022 生产实践专场的分享。本篇内容主要分为四个部分&#xff1a; Flink 流批一体引擎 Flink Batch 生产实践 核心优化解读 未来规划 点击查看原文视频…

切换.net Framework 版本后,出现NuGet 包是使用不同于当前目标框架的目标框架安装的,可能需要重新安装

问题现象&#xff1a; 由于添加新的dll文件&#xff0c;依赖的.NET Framework版本与当前的不一致&#xff0c;在vs 中切换了目标框架版本后&#xff0c;运行程序&#xff0c;出现以下的warnning信息&#xff1a; 一些 NuGet 包是使用不同于当前目标框架的目标框架安装的&#…

springboot社区疫情防控平台

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven…

剑指offer68-I.二叉搜索树的最近公共祖先

把p的所有祖先找出来&#xff0c;把q的所有祖先找出来&#xff0c;因为是是搜索树&#xff0c;找出来的祖先都是排好序的&#xff0c;所以可以把找出来的祖先从后面往前面遍历&#xff0c;第一个相同的值的数就是最近的公共祖先&#xff0c;这是我一开始的想法,但是它最后报错了…

(六)人工智能应用--深度学习原理与实战--理解张量与运算图

Tensorflow名称中的Tensor即张量&#xff0c;不仅仅是Tensorflow&#xff0c;几乎所有的深度学习平台都以张量为基本的数据结构。简单来说&#xff0c;张量就是多维数组&#xff0c;本质上是一种数据容器&#xff0c;它可以有任意维度&#xff0c;比如矩阵就是二维张量(二维数组…

数字图像处理【11】OpenCV-Canny边缘提取到FindContours轮廓发现

本章主要介绍图像处理中一个比较基础的操作&#xff1a;Canny边缘发现、轮廓发现 和 绘制轮廓。概念不难&#xff0c;主要是结合OpenCV 4.5的API相关操作&#xff0c;为往下 "基于距离变换的分水岭图像分割" 做知识储备。 Canny边缘检测 在讲述轮廓之前&#xff0c;…

数字孪生,建设智慧城市的新型“加速器”

城市是什么&#xff1f; 是现代文明与生态的联结&#xff0c;是自然与人友好栖息的空间&#xff0c;是运转复杂庞大的系统。 今天&#xff0c;中国的城市在历经十余年的“智慧城市”建设后已经被赋予了数智融合的全新解读。随着近年来5G、云计算、人工智能爆发式能量增长&#…

常见的bug---4、在DataGrip上跑本地模式报return 2异常

文章目录 问题描述原因分析&#xff1a;解决方案&#xff1a; 问题描述 FAILED: Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask 在DataGrip上设置了Hive的本地模式。虽然可以建表、但是无法对表进行插入数据 原因分析&#xff1a; 在插…

深入理解张量维度的真正含义

在人工智能领域&#xff0c;比如深度学习&#xff0c;机器学习&#xff0c;张量这一概念被频繁使用。虽然网上有很多关于张量的文章&#xff0c;但基本都是废话太多&#xff0c;而且复制粘贴说不到重点。 今天我就来讲解一下张量维度的真正含义。 首先. 张量并不是一个简单多…

STM32F103驱动VL53L0X激光测距模块

STM32F103驱动VL53L0X激光测距模块 简介引脚定义STM32F103ZET6开发板与VL53L0X模块接线测试代码实验结果 简介 TOF 是飞行时间&#xff08;Tlme of Flight&#xff09;技术的缩写&#xff0c;即传感器发出经过调制的近红外光&#xff0c;遇到物体后反射&#xff0c;传感器通过…

bug:file name too long文件名超出系统最大限制

各操作系统支持最长的文件和目录名称长度&#xff08;Linux、Win、Mac&#xff09; 今天开发需求的时候发现无法新建文件&#xff0c;提示file name too lang&#xff0c;于是翻阅和查询了一些资料&#xff0c;发现不同操作系统下文件名和目录名最长的长度不同。 操作系统文件名…