驱动(RK3588S)第八课时:平台设备总线

目录

  • 目标
  • 一、平台设备总线的概念
    • 1、什么是平台设备总线
    • 2、平台设备总线 platform 的匹配
    • 3、设备树和平台设备总线的关系,以及匹配
  • 二、平台设备总线的函数接口
    • 1、注册设备端的资源信息
    • 2、设备端提供的资源的信息
    • 3、注销申请的设备端的资源
    • 4、驱动端的函数(全)
    • 5、从设备端获取资源
    • 6、设备树和驱动端匹配的时候需要用到的结构体
    • 7、设备树和驱动端设备树使用的节点信息
  • 三、代码与现象
    • 一、只用平台设备总线
      • 1、代码:
      • 2、现象
    • 二、平台设备总线与设备树
      • 1、代码:
      • 2、现象

目标

1.利用平台设备总线编写LED灯驱动代码
2.利用驱动端+设备树编写LED灯驱动代码

一、平台设备总线的概念

1、什么是平台设备总线

I2c spi 等他们是真实存在的总线,他不像今天讲的 platform(平台) 总线他是内核虚拟出来的一条总线,这个总线的作用就是为了匹配设备端和驱动端,在咱们没有讲设备树之前,编写的驱动代码都是采取平台设备总线的形式去编写的,就是老内核他们是怎么去编写设备驱动的,他们都是采取平台设备总线的形式,因为设备的资源比较多,要管理的内容也比较多,就会造成内核比较凌乱。此时他把一个驱动的代码给分为了两个端,一个是设备端,一个是驱动端,这个两个端他们分别负责不同的功能,设备端主要就是负责提供设备的信息,就和硬件相关的,只提供信息资源,不做任何设备的驱动,这个一般是咱们自己写的,比如前面说的的设备树就是设备端,而驱动端他统一交给了内核去管理,他主要负责驱动设备,他要从设备端先去获取设备的资源信息。对于一些大的设备来说,一般驱动端内核里都会自带,比如 LCD 屏,内核已经自动写入了一部分驱动端代码。
在后期驱动开发一般是有两种方法
1.平台设备总线
2.使用设备树+杂项/linux2.6
3.你也可以使用设备树+平台设备总线(驱动端)
驱动端:负责字符设备的框架使用 Linux2.6/杂项
设备端:只负责提供你驱动端所需要的所有的设备信息资源。

2、平台设备总线 platform 的匹配

因为的驱动代码已经分为了两端,一个是驱动端,一个设备端。驱动端和
设备端,怎么找到对方,这里有一个比较重要的信息,就是你要相互找到对
方,只有设备端和驱动端匹配成功了,那么你的驱动端,才可以去驱动对应的硬件,这里驱动端和设备端匹配的方法也是比较多的,目前最长用的就是通过名字找对方。这里你编译出来会有两个.ko ,他们加载的顺序是没有关系的,没有先后的顺序,不管你先加载谁他们都可以通过名字找对方。设备端和驱动端都有一个属于自己的核心结构体在他们各自的核心结构体里都有一个成员变量Name,你在编码的时候需要保证这个来个结构体里的名字必须是一样的,否则他们两个是无非进行匹配的。
以下是驱动和设备端编译出来的文件。
在这里插入图片描述

3、设备树和平台设备总线的关系,以及匹配

平台设备总线他是分为了两个端设备端和驱动端,其实这里的设备端被咱们设备树给取代了。设备端的作用就只有一个,那就是提供设备的信息,而咱们的设备恰好是描述设备硬件信息,所以设备树就可以代替设备端,同样你也可以使用平台设备总线的驱动断和设备树做匹配,既然设备树替代了设备端,那么资源驱动端也可以从设备树里去获取资源,其实这里就通过设备树里的 Compatible属性来获取信息的。这样我们就可以少些一部分设备层的代码。
在这里插入图片描述

二、平台设备总线的函数接口

1、注册设备端的资源信息

函数功能:注册设备端的资源信息
函数原型:int platform_device_register(struct platform_device *pdev)
函数头文件:#include<linux/platform_device.h>
函数参数:pdev:设备端的核心结构体
设备端的核心结构体
struct platform_device {
const char *name;//他就是和驱动端进行匹配的名字
int id;// 写 -1
struct device dev;//关于设备的信息的结构体
这里有一个函数需要咱们填写,这个函数里什么都不用
写,函数必须要有。void (*release)(struct device *dev);
u32 num_resources;//设备端提供资源的数量
ARRAY_SIZE() — 他来计算你提供了多少资源
struct resource *resource;//你提供的具体的资源信息
}

2、设备端提供的资源的信息

设备端提供的资源的信息
struct resource {resource_size_t start;resource_size_t end;const char *name;unsigned long flags;};
start:设备资源的起始资源
end:设备资源的结束资源
name:没有什么太大的意义一般是表示 给那个设备提供资源 取一个名

flags:你提供资源的类型
在这里插入图片描述
资源的类型内核已经帮你写好了固定的宏
在这里插入图片描述
#define IORESOURCE_IO 0x00000100//IO 空间,一般在 x86 框
架中存 在,ARM 一般没有
#define IORESOURCE_MEM 0x00000200//内存资源,大部分资源
都是这个

#define IORESOURCE_IRQ 0x00000400//中断资源 实际上就是
中断号
#define IORESOURCE_DMA 0x00000800//DMA

3、注销申请的设备端的资源

函数返回值:成功返回 0 失败负数
函数功能:注销申请的设备端的资源
函数原型:void platform_device_unregister( struct platform_device *pdev)
函数头文件:#include<linux/platform_device.h>
函数参数:pdev:定义的核心结构体
函数返回值:无

4、驱动端的函数(全)

函数功能:注册驱动端的资源
函数原型:int platform_driver_register( struct platform_driver *drv)
函数头文件:#include<linux/platform_device.h>
函数参数:drv
函数返回值:成功返回 0 失败负数
在这里插入图片描述
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
struct device_driver driver;
};
probe:探测函数。一但驱动端和设备端匹配成功就会自动的去调用此函数,此函数里一般做字符设备的注册使用杂项/Linux2.6 以及设备资源获取。
remove:移除函数。 一但驱动端或者是设备端有一个卸载了,那么 此函数就会自动的调用,次函数里主要就是注销你在探测函数里申请的资源。
driver:你需要填写里边的名字 。注意这里的 name 要和设备端里的 name保持一致
函数功能:注销驱动端申请的资源
函数原型:void platform_driver_unregister(struct platform_driver *dev);
函数头文件:#include<linux/platform_device.h>
函数参数:dev:定义的核心结构体
函数返回值:无

5、从设备端获取资源

函数功能:从设备端获取资源
函数原型:struct resource *platform_get_resource(
struct platform_device *dev,
unsigned int type,
unsigned int num)
函数头文件:#include<linux/platform_device.h>
函数参数:dev:设备端的核心结构体
type:获取资源类型
num:你要获取资源的编号 — 第几个资源
函数返回值:成功返回指向 struct resource 指针 失败返回 NULL

6、设备树和驱动端匹配的时候需要用到的结构体

struct device_driver driver;
struct device dev{
struct device_driver *driver;
{
const struct of_device_id *of_match_table;
struct of_device_id {
charcompatible[128]; — 你这里的名字要和设备树里的
compatible
保持一致,否则无法从设备树里获取设备节点
};
}
在这里插入图片描述

7、设备树和驱动端设备树使用的节点信息

struct platform_device
{
struct device dev
{
struct device_node of_node; / 设备树使用的节点信息 */
}
}
这个成员是给设备树和驱动端匹配成功之后获取 gpio 口资源函数第一个参数
使用的。
在这里插入图片描述

三、代码与现象

一、只用平台设备总线

1、代码:

应用层函数 app.c

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd = 0;while(1){fd = open("/dev/myqxjled",O_RDWR); // --- 底层的open函数sleep(3);close(fd);//底层的closesleep(3);}return 0;
}

驱动层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
struct resource *led[2];
int i;
int all;
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;int myled_open (struct inode *inode, struct file *fp)
{gpio_set_value(led[0]->start,led[1]->end);gpio_set_value(led[0]->end,led[1]->end);printk("两个灯打开\n");printk("myled open 正确打开\n");return 0;
}int myled_close (struct inode *inode, struct file *fp)
{gpio_set_value(led[0]->start,led[1]->start);gpio_set_value(led[0]->end,led[1]->start);printk("两个灯关闭\n");printk("myled close 关闭正确\n");return 0;
}
struct file_operations myfops={.open = myled_open,.release = myled_close,};int myled_probe(struct platform_device *pdev)
{printk("探测函数:设备端和驱动端匹配成功\n");for(i=0;i<=1;i++){led[i]=platform_get_resource(pdev,IORESOURCE_MEM,i);}gpio_request(led[0]->start, "led1 pc5");gpio_request(led[0]->end, "led2 pc6");gpio_direction_output(led[0]->start, led[1]->start);gpio_direction_output(led[0]->end, led[1]->start);	all=alloc_chrdev_region(&dev,0, 1,"led");if(all<0){printk("alloc_chrdev_region error\n");printk("动态创建失败\n");return -1;}printk("主设备号:%d\n",MAJOR(dev));printk("次设备号:%d\n",MINOR(dev));cdev_init(&mydev,&myfops);cdev_add(&mydev,dev,1);myclass=class_create(THIS_MODULE,"class_led");//创建类if(myclass == NULL){printk("class_create error\n");printk("class_create 类创建失败\n");return -1;}device_create(myclass,NULL,dev,NULL,"myqxjled");return 0;
}int myled_remove (struct platform_device *pdev)
{printk("移除函数成功\n");device_destroy(myclass,dev);class_destroy(myclass);cdev_del(&mydev);unregister_chrdev_region(dev,1);gpio_free(led[0]->start);gpio_free(led[0]->end);return 0;
}struct platform_driver drv={.probe = myled_probe,.remove = myled_remove,.driver = {.name = "myxyd_leds",//与设备端必须保持一致},
};
static int __init myled_init(void)
{	platform_driver_register(&drv);return 0;
}
static void __exit myled_exit(void)
{platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

设备层:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
struct resource led_resou[2]={[0]={.start=21,.end=22,.name="xydleds",.flags=IORESOURCE_MEM,},[1]={.start=0,.end=1,.name="xydled", .flags=IORESOURCE_MEM,},
};
void xyd_release(struct device *dev)
{}
struct platform_device pdev={.name="myxyd_leds",.id=-1,.dev={.release=xyd_release,},.num_resources=ARRAY_SIZE(led_resou),.resource=led_resou,
};static int __init myled_init(void)
{	int plat = 0;plat = platform_device_register(&pdev);if(plat < 0){printk("platform_device_register error\n");printk("注册设备端的资源信息失败\n");return -1;}printk("注册设备端的资源信息成功\n");return 0;
}
static void __exit myled_exit(void)
{platform_device_unregister(&pdev);//注销设备资源
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

2、现象

在这里插入图片描述
在这里插入图片描述
代码运行现象:
在这里插入图片描述
灯现象:
在这里插入图片描述
在这里插入图片描述

二、平台设备总线与设备树

1、代码:

应用层:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{int fd = 0;while(1){fd = open("/dev/myqxjled",O_RDWR); // --- 底层的open函数sleep(1);close(fd);//底层的closesleep(1);}return 0;
}

驱动层:用的设备树

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/cdev.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
int led[2]={0};
int i;
int all;
dev_t dev;
struct cdev mydev;
struct class *myclass=NULL;int myled_open (struct inode *inode, struct file *fp)
{gpio_set_value(led[0],1);gpio_set_value(led[1],1);printk("两个灯打开\n");printk("myled open 正确打开\n");return 0;
}int myled_close (struct inode *inode, struct file *fp)
{gpio_set_value(led[0],0);gpio_set_value(led[1],0);printk("两个灯关闭\n");printk("myled close 关闭正确\n");return 0;
}
struct file_operations myfops={.open = myled_open,.release = myled_close,};int myled_probe(struct platform_device *pdev)
{printk("探测函数:设备端和驱动端匹配成功\n");//led[0] led[1]返回的是gpio编口号led[0]=of_get_named_gpio(pdev->dev.of_node,"led-gpios",0);led[1]=of_get_named_gpio(pdev->dev.of_node,"led-gpios",1);gpio_request(led[0], "led1 pc5");//21gpio_request(led[1], "led2 pc6");//22gpio_direction_output(led[0],0);gpio_direction_output(led[1],0);	all=alloc_chrdev_region(&dev,0, 1,"led");if(all<0){printk("alloc_chrdev_region error\n");printk("动态创建失败\n");return -1;}printk("主设备号:%d\n",MAJOR(dev));printk("次设备号:%d\n",MINOR(dev));cdev_init(&mydev,&myfops);cdev_add(&mydev,dev,1);myclass=class_create(THIS_MODULE,"class_led");//创建类if(myclass == NULL){printk("class_create error\n");printk("class_create 类创建失败\n");return -1;}device_create(myclass,NULL,dev,NULL,"myqxjled");return 0;
}int myled_remove (struct platform_device *pdev)
{printk("移除函数成功\n");device_destroy(myclass,dev);class_destroy(myclass);cdev_del(&mydev);unregister_chrdev_region(dev,1);gpio_free(led[0]);gpio_free(led[1]);return 0;
}
struct of_device_id	mydev_node={.compatible="myqxj_led",
};struct platform_driver drv={.probe = myled_probe,.remove = myled_remove,.driver = {.name = "myxyd_leds",//与设备端必须保持一致.of_match_table = &mydev_node,},
};
static int __init myled_init(void)
{	platform_driver_register(&drv);return 0;
}
static void __exit myled_exit(void)
{platform_driver_unregister(&drv);
}
module_init(myled_init);
module_exit(myled_exit);
MODULE_LICENSE("GPL");

不需要再写一个设备端了,而是由设备树上获取相应资源。

2、现象

在这里插入图片描述
代码运行状态:
在这里插入图片描述
灯现象:灯亮灭交替闪烁。
在这里插入图片描述
在这里插入图片描述


http://www.ppmy.cn/news/1522818.html

相关文章

逻辑表达式,最小项

目录 得到此图的逻辑电路 1.画出它的真值表 2.根据真值表写出逻辑式 3.画逻辑图 逻辑函数的表示 逻辑表达式 最小项 定义 基本性质 最小项编号 最小项表达式 得到此图的逻辑电路 1.画出它的真值表 这是同或的逻辑式。 2.根据真值表写出逻辑式 3.画逻辑图 有两种画法…

Android Fragment 学习备忘

1.fragment的动态添加与管理&#xff0c;fragment生命周期在后面小节&#xff1a;https://www.bilibili.com/video/BV1Ng411K7YP/?p37&share_sourcecopy_web&vd_source982a7a7c05972157e8972c41b546f9e4https://www.bilibili.com/video/BV1Ng411K7YP/?p37&share_…

Python 读取 Excel 数据|数据处理|Pandas|Excel操作

目录 1. 为什么选择 Python 读取 Excel 数据 2. Python 读取 Excel 数据的基本工具 2.1 Pandas 库 2.2 Openpyxl 库 2.3 xlrd 库 3. 读取 Excel 文件的高级操作 3.1 读取特定的工作表 3.2 读取特定的列和行 3.3 处理缺失数据 4. 实践应用示例 4.1 数据分析和可视化 …

ngrok | 内网穿透,支持 HTTPS、国内访问、静态域名

前言 当我们需要把本地开发的应用展示给外部用户时&#xff0c;常常会因为无法直接访问而陷入困境。 就为了展示一下&#xff0c;买服务、域名&#xff0c;搭环境&#xff0c;费钱又费事。 那有没有办法&#xff0c;让客户直接访问自己本机开发的应用呢&#xff1f; 这种需…

表格多列情况下,loading不显示问题

问题描述&#xff1a; 用element plus 做得表格&#xff0c;如下图&#xff0c;列数较多&#xff0c;且部分表格内容显示比较复杂&#xff0c;数据量中等的情况下&#xff0c;有一个switch 按钮&#xff0c;切换部分列的显示和隐藏&#xff0c;会发现&#xff0c;切换为显示的时…

逻辑运算基础知识

关系运算符 <:小于 <:小于等于 >:大于 >:大于等于 以上优先级相同&#xff1a;高 &#xff1a;等于 !&#xff1a;不等于 以上优先级相同&#xff1a;低 说明&#xff1a; 关系运算符的 优先级 低于 算数运算符 关系运算符的 优先级 大于 赋值运算符 逻辑运算&a…

前向渲染路径

1、前向渲染路径处理光照的方式 前向渲染路径中会将光源分为以下3种处理方式&#xff1a; 逐像素处理&#xff08;需要高等质量处理的光&#xff09;逐顶点处理&#xff08;需要中等质量处理的光&#xff09;球谐函数&#xff08;SH&#xff09;处理&#xff08;需要低等质量…

如何使用 PHP 函数与其他 Web 服务交互?

在 PHP 中&#xff0c;我们可以使用 cURL 或者 file_get_contents 函数与其他 Web 服务进行交互。 使用 cURL 函数 cURL 是一个库&#xff0c;它允许你使用各种类型的协议来发送数据&#xff0c;并从服务器获取数据。 $curl curl_init(‘http://example.com/api’); curl_s…

SprinBoot+Vue漫画天堂网的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

前端框架有哪些?以及每种框架的详细介绍

目录 前言1. React2. Vue.js3. Angular4. Bootstrap5. Foundation总结 前言 前端框架是Web开发中不可或缺的工具&#xff0c;它们为开发者提供了丰富的工具和抽象&#xff0c;使得构建复杂的Web应用变得更加容易。当前&#xff0c;前端框架种类繁多&#xff0c;其中一些最受欢…

【全网最全】2024年数学建模国赛A题30页完整建模文档+17页成品论文+保奖matla代码+可视化图表等(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片那是获取资料的入口&#xff01; 【全网最全】2024年数学建模国赛A题30页完整建模文档17页成品论文保奖matla代码可视化图表等&#xff08;后续会更新&#xff09;「首先来看看目前已有的资料&#xff0…

应用开发“取经路”,华为应用市场送出全周期服务“助攻”

最近大量国内外玩家被西游神话圈粉&#xff0c;化身游戏人物角色&#xff0c;踏上了充满冒险的取经路。如果让莘莘学子或创业者们&#xff0c;在自己的职业生涯中&#xff0c;也选一个机遇跟挑战并存的角色&#xff0c;“开发者”一定榜上有名。 智能手机和移动互联网的普及&am…

30天pandas挑战

大的国家 挑选出符合要求的行 def big_countries(world: pd.DataFrame) -> pd.DataFrame:df world[(world[area] > 3000000) | (world[population] > 25000000)]return df[[name,population,area]] 在Pandas中&#xff0c;当你使用条件过滤时&#xff0c;应该使用 …

记一次升级 Viper、ETCD V3操作Toml

前一阵子碰到Go写的一项目&#xff0c;使用viper和ETCD进行Toml文件的存储与写入。在当我安装新版本的ETCD和升级Go依赖包之后出现了不兼容的问题。旧版viper为1.10版本&#xff0c;使用github.com/coreos/go-etcd v2.0.0incompatible 作为请求包。看了源码之后发现新的版本中废…

HashMap 底层原理解析

HashMap 是 Java 中非常常用的一个数据结构&#xff0c;它基于哈希表实现&#xff0c;提供了快速的键值对存储和检索。本文将深入探讨 HashMap 的底层实现原理&#xff0c;包括其数据结构、哈希函数、冲突解决机制以及扩容机制。 1. 哈希表基础 哈希表是一种通过哈希函数将键…

【重学 MySQL】二十、运算符的优先级

【重学 MySQL】二十、运算符的优先级 MySQL 运算符的优先级&#xff08;由高到低&#xff09;注意事项示例 在 MySQL 中&#xff0c;运算符的优先级决定了在表达式中各个运算符被计算的先后顺序。了解运算符的优先级对于编写正确且高效的 SQL 语句至关重要。以下是根据高权威性…

C++学习笔记(13)

203、文件操作-写入二进制文件 二进制文件以数据块的形式组织数据&#xff0c;把内存中的数据直接写入文件。 包含头文件&#xff1a;#include <fstream> 类&#xff1a;ofstream&#xff08;output file stream&#xff09; ofstream 打开文件的模式&#xff08;方式&am…

代理模式(权限、远程调用、延迟加载、日志和缓存)

1、权限保护代理模式 使用 代理模式 实现一个“干饭村约会系统服务”的示例&#xff0c;能够通过代理控制对实际对象&#xff08;比如用户的约会资料&#xff09;访问、保护隐私、限制不正当操作等。 需求分析&#xff1a; 用户&#xff08;Person&#xff09;&#xff1a;干…

自我指导:提升语言模型自我生成指令的能力

人工智能咨询培训老师叶梓 转载标明出处 传统的语言模型&#xff0c;尤其是经过指令微调的大型模型&#xff0c;虽然在零样本&#xff08;zero-shot&#xff09;任务泛化上表现出色&#xff0c;但它们高度依赖于人类编写的指令数据。这些数据往往数量有限、多样性不足&#xf…

uniapp+vue+ts开发中使用signalR实现客户端和服务器通讯

SignalR SignalR 面向 ES6。 对于不支持 ES6 的浏览器&#xff0c;请将库转译为 ES5。 SignalR 支持以下用于处理实时通信的技术&#xff08;按正常回退的顺序&#xff09;&#xff1a; WebSocketsServer-Sent Events长轮询SignalR 自动选择服务器和客户端能力范围内的最佳传输…