驱动开发练习,platform实现如下功能
实验要求
驱动代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
#include <linux/of_gpio.h>
#include <linux/unistd.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/string.h>
int i;
struct resource *res;
struct device_node *dnode;
unsigned int irqno[3];
unsigned int irqflag;
struct gpio_desc *gpiono[3];
unsigned char number;
struct cdev *cdev;
char kbuf[128] = {0};
unsigned int major = 0;
unsigned int minor = 0;
dev_t devno;
module_param(major, uint, 0664); // 方便在命令行传递major的值
struct class *cls;
struct device *dev;
unsigned int condition = 0;
// 定义一个等待队列头
wait_queue_head_t wq_head;
// 定义中断处理函数
irqreturn_t key_handler(int irq, void *dev)
{int which = (int)dev;if(number==0){number=1;kbuf[0]='1';}else{number=0;kbuf[0]='0';}switch (which){case 0:gpiod_set_value(gpiono[0], number); break;case 1:gpiod_set_value(gpiono[1], number);break;case 2:gpiod_set_value(gpiono[2], number);break;}condition = 1; // 表示硬件数据就绪wake_up_interruptible(&wq_head);return IRQ_HANDLED;
}
// 封装probe函数
int pdrv_probe(struct platform_device *pdev)
{res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res == NULL){printk("获取men资源失败\n");return -ENXIO;}printk("mem资源%x\n", res->start);for (i = 0; i < 3; i++){irqno[0] = platform_get_irq(pdev, 0);}if (irqno[0] < 0){printk("获取中断类型资源失败\n");return -ENXIO;}printk("irq资源%d\n", irqno[0]);printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);// 设备树匹配成功后,设备树节点指针可以通过pdev->dev.of_node获取// 基于设备树节点信息获取gpio_desc对象指针gpiono[0] = gpiod_get_from_of_node(pdev->dev.of_node, "led1-gpio", 0, GPIOD_OUT_LOW, NULL);if (IS_ERR(gpiono)){printk("解析GPIO管脚信息失败\n");return -ENXIO;}gpiono[1] = gpiod_get_from_of_node(pdev->dev.of_node, "led2-gpio", 0, GPIOD_OUT_LOW, NULL);if (IS_ERR(gpiono)){printk("解析GPIO管脚信息失败\n");return -ENXIO;}gpiono[2] = gpiod_get_from_of_node(pdev->dev.of_node, "led3-gpio", 0, GPIOD_OUT_LOW, NULL);if (IS_ERR(gpiono)){printk("解析GPIO管脚信息失败\n");return -ENXIO;}return 0;
}
// 封装操作方法
int mycdev_open(struct inode *inode, struct file *file)
{printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
int mycdev_close(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;// 判断IO方式if (file->f_flags & O_NONBLOCK) // 非阻塞{}else // 阻塞{wait_event_interruptible(wq_head, condition); // 先检查condition再将进程休眠}ret = copy_to_user(ubuf, kbuf, size);if (ret){printk("copy_to_user err\n");return -EIO;}condition = 0; // 下一次硬件数据没有就绪return 0;
}
// 定义操作方法结构体变量并赋值
struct file_operations fops = {.open = mycdev_open,.release = mycdev_close,.read = mycdev_read,
};
// 封装remvoe函数
int pdrv_remove(struct platform_device *pdev)
{// 释放GPIO信息for (i = 0; i < 3; i++){gpiod_put(gpiono[i]);}int i;for (i = 0; i < 3; i++){free_irq(irqno[i], (void *)i);}// 销毁设备文件// 注销驱动printk("%s:%s:%d\n", __FILE__, __func__, __LINE__);return 0;
}
// 构建设备树匹配表
struct of_device_id oftable[] = {{.compatible = "hqyj,myplatform"},{},
};
// 定义驱动信息对象并初始化
struct platform_driver pdrv = {.probe = pdrv_probe,.remove = pdrv_remove,.driver = {.name = "ccccc",.of_match_table = oftable, // 用来设备树匹配},
};static int __init mycdev_init(void)
{// 注册platform_driver_register(&pdrv);// 解析按键的设备树节点dnode = of_find_compatible_node(NULL, NULL, "hqyj,myirq");if (dnode == NULL){printk("解析设备树节点失败\n");return -ENXIO;}printk("解析设备树节点成功\n");// 解析按键的软中断号int i;for (i = 0; i < 3; i++){irqno[i] = irq_of_parse_and_map(dnode, i);if (!irqno[i]){printk("解析按键1软中断号失败\n");return -ENXIO;}printk("解析按键软中断号成功%d\n", irqno[i]);// 注册 按键中断int ret = request_irq(irqno[i], key_handler, IRQF_TRIGGER_FALLING, "key_int", (void *)i);if (ret < 0){printk("注册按键中断失败\n");return ret;}}printk("注册按键中断成功\n");init_waitqueue_head(&wq_head);int ret;// 为字符设备驱动对象申请空间cdev = cdev_alloc();if (cdev == NULL){printk("字符设备驱动对象申请空间失败\n");ret = -EFAULT;goto out1;}printk("申请对象空间成功\n");// 初始化字符设备驱动对象cdev_init(cdev, &fops);// 申请设备号if (major > 0) // 静态指定设备号{ret = register_chrdev_region(MKDEV(major, minor), 3, "myled");if (ret){printk("静态申请设备号失败\n");goto out2;}}else if (major == 0) // 动态申请设备号{ret = alloc_chrdev_region(&devno, minor, 3, "myled");if (ret){printk("动态申请设备号失败\n");goto out2;}major = MAJOR(devno); // 获取主设备号minor = MINOR(devno); // 获取次设备号}printk("申请设备号成功\n");// 注册字符设备驱动对象ret = cdev_add(cdev, MKDEV(major, minor), 3);if (ret){printk("注册字符设备驱动对象失败\n");goto out3;}printk("注册字符设备驱动对象成功\n");// 向上提交目录信息cls = class_create(THIS_MODULE, "myled");if (IS_ERR(cls)){printk("向上提交目录失败\n");ret = -PTR_ERR(cls);goto out4;}printk("向上提交目录成功\n");// 向上提交设备节点信息for (i = 0; i < 3; i++){dev = device_create(cls, NULL, MKDEV(major, i), NULL, "myled%d", i);if (IS_ERR(dev)){printk("向上提交设备节点信息失败\n");ret = -PTR_ERR(dev);goto out5;}}printk("向上提交设备信息成功\n");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;return 0;
}
static void __exit mycdev_exit(void)
{// 注册platform_driver_unregister(&pdrv);
}
module_init(mycdev_init);
module_exit(mycdev_exit);
MODULE_LICENSE("GPL");
应用程序代码
#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[])
{char buf[128] = {0};int fd = open("/dev/myled0", O_RDWR);if (fd < 0){printf("打开设备文件失败\n");exit(-1);}while (1){read(fd,buf,sizeof(buf));printf("%s\n",buf);}return 0;
}
实验现象
相关文章:

驱动开发练习,platform实现如下功能
实验要求 驱动代码 #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/mod_devicetable.h> #include <linux/of_gpio.h> #include <linux/unistd.h> #include <linux/interrupt…...
QT之QString的用法介绍
QT之QString的用法介绍 成员函数常见用法 成员函数 1)QString &append(const QString &str) 将 str 字符串追加到当前字符串末尾,并返回修改后的 QString 对象的引用。 2)QString &prepend(const QString &str) 将 str 字符…...

基于Java+SpringBoot+Vue3+Uniapp前后端分离考试学习一体机设计与实现2.0版本(视频讲解,已发布上线)
博主介绍:✌全网粉丝4W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验…...

springboot 获取参数
1.获取简单参数 2.实体对象参数...

【笔记】离线Ubuntu20.04+mysql 5.7.36 + xtrabackup定时增量备份脚本
一、环境 ● Ubuntu版本查看 lsb_release -a● mysql 版本查看 mysql --version我的是ubuntu 20.04,mysql是5.7.36,所以要用 install_percona-xtrabackup-24 二、原理 备份 通过ubuntu自带的定时器运行增量备份脚本备份文件可以存储在映射后的其他…...
树哈希与换根dp:CF763D
采用的树哈希函数是: d p x w x ∑ y ∈ x d p y 2 w x 2 \Large dp_xw_x\times \sum_{y\in x}dp_y^2w_x^2 dpxwxy∈x∑dpy2wx2 发现从 x x x 到 y y y 时只有 x x x 与 y y y 的哈希值会变化,分别维护即可 #include<bits/stdc.h&…...

npm、yarn、pnpm如何清除缓存?
前端工程化创建项目会经常使用各种安装包管理工具,安装各种前端依赖包。例如,npm、yarn、pnpm等。时间一长,各种安装包管理工具的在安装依赖时,留下的缓存文件就会变得很大,以至于影响系统的运行,因此必要时…...

12款最火的AI画图软件,助你探索创新设计
ChatGPT火爆出圈,AI画图软件也如雨后春笋般流行起来。各类AI画图的软件工具横空出世,设计师与其焦虑工作会不会被人工智能取代,不如践行“工欲善其事必先利其器”,开拓思路,打开格局,好好地探索下如何利用好…...

cookie信息无法获取问题研究
背景 在oneapi这个前后端都有的开源项目中,我想接入chatnextweb到oneapi的后端。 由于需要二开chatnextweb,添加登录注册功能,考虑到java后端的性能问题和内存占用,决定不重启写个服务,而是将登录注册接入到oneapi的…...

Linux:冯诺依曼系统和操作系统的概念
文章目录 冯诺依曼体系结构冯诺依曼体系的理解 操作系统操作系统的基本定位操作系统的理解1 操作系统的理解2总结 本篇主要总结的是操作系统的基本认知和一些概念 冯诺依曼体系结构 那么上图表示的就是冯诺依曼体系结构,那这个体系结构是什么?为什么要先…...

【操作系统笔记十一】进程间通信
Linux文件系统 inode 节点 (index node):给每个文件赋予一个称为 i 节点的数据结构。 inode 一开始是存储在硬盘中的,只有当文件被打开的时候,其对应的 i 节点才加载到内存中。 总结: Linux 中,…...
【操作系统】聊聊Linux软中断
什么是中断 中断是系统用来响应硬件设备请求的一种机制,会打断进程的正常调度和执行,转而去执行内核中的中断处理程序。 比如你正在看书,你女朋友叫你出去逛街。你就需要先放下手里的事情,然后逛街。回来之后,在接着看…...

公众号迁移个人可以迁移吗?
公众号账号迁移的作用是什么?只能变更主体吗?很多小伙伴想做公众号迁移,但是不知道公众号迁移有什么作用,今天跟大家具体讲解一下。首先公众号迁移最主要的就是修改公众号的主体了,比如我们公众号原来是A公司的&#x…...

全国职业技能大赛云计算--高职组赛题卷⑤(容器云)
全国职业技能大赛云计算--高职组赛题卷⑤(容器云) 第二场次题目:容器云平台部署与运维任务2 基于容器的web应用系统部署任务(15分)任务3 基于容器的持续集成部署任务(15分)任务4 Kubernetes容器…...

支撑位和阻力位在Renko和烛台图如何使用?FPmarkets澳福3秒回答
很多投资者都知道,Renko图表和普通日本烛台都会采用相同的交易信号,即支撑位和阻力位。那么支撑位和阻力位在Renko和烛台图如何使用?FPmarkets澳福3秒回答。 这些信号在任何时间框架上都会出现,且在蜡烛图交易中颇受欢迎。对于Renko图表而言…...

如何在32位MCU用printf()函数打印64位数据
1. 在32位MCU上定义64位变量: unsigned long long time_base; unsigned long long temp_time;2. 调用打印函数: printf("RFID:time_base:%d\r\n",time_base); printf("RFID:temp_time:%d\r\n",temp_time); printf("RFID:Ru…...

Python爬虫程序设置代理常见错误代码及解决方法
Python爬虫程序设置代理是爬虫程序中常用的技巧,可以有效地绕过IP限制,提高爬虫程序的稳定性和效率。然而,在设置代理时,常会出现各种错误代码,这些错误代码可能会影响程序的正常运行,甚至导致程序崩溃。本…...

3D点云目标检测:Centerformer训练waymo数据集
一、环境准备 项目地址:centerformer 1.0、基础环境 python 3.8.0 torch 1.9.1cu111 waymo-open-dataset-tf-2-6-0 1.4.9 spconv 1.2.1 其余按照requirement.txt里安装就行 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt由于我本人是在…...

火山引擎DataLeap推出两款大模型应用: 对话式检索与开发 打破代码语言屏障
更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 自上世50年代,以“计算机”作为代表性象征的信息革命开始,社会对于先进生产力的认知便开始逐步更迭——从信息化(通常认为是把企…...

windows上配置vscode C/C++代码跳转
windows上配置vscode C/C代码跳转 安装插件 C/C 官方的 C/C 插件,必备的插件,是代码跳转、自动补全、代码大纲显示等功能的基础。 Gtags C/C GNU Global GNU Global除了安装该插件之外,还需要在本地下载安装GNU Global工具。多看下插件…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
管理学院权限管理系统开发总结
文章目录 🎓 管理学院权限管理系统开发总结 - 现代化Web应用实践之路📝 项目概述🏗️ 技术架构设计后端技术栈前端技术栈 💡 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 🗄️ 数据库设…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...