嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型

news/2025/1/15 22:55:31/

嵌入式Linux应用开发-面向对象-分层-分离及总线驱动模型

  • 第八章 驱动设计的思想:面向对象/分层/分离
    • 8.1 面向对象
    • 8.2 分层
    • 8.3 分离
    • 8.4 写示例代码
    • 8.5 课后作业
  • 第九章 驱动进化之路:总线设备驱动模型
    • 9.1 驱动编写的 3种方法
      • 9.1.1 传统写法
      • 9.1.2 总线设备驱动模型
      • 9.1.3 设备树
    • 9.2 在 Linux中实现“分离”:Bus/Dev/Drv模型
    • 9.3 匹配规则
      • 9.3.1 最先比较:platform_device. driver_override和 platform_driver.driver.name
      • 9.3.2 然后比较:platform_device. name和 platform_driver.id_table[i].name
      • 9.3.3 最后比较:platform_device.name和 platform_driver.driver.name
      • 9.3.4 函数调用关系
    • 9.4 常用函数
      • 9.4.1 注册/反注册
      • 9.4.2 获得资源
    • 9.5 怎么写程序
      • 9.5.1 分配/设置/注册 platform_device结构体
      • 9.5.2 分配/设置/注册 platform_driver结构体
    • 9.6 课后作业

第八章 驱动设计的思想:面向对象/分层/分离

在这里插入图片描述

8.1 面向对象

字符设备驱动程序抽象出一个 file_operations结构体; 我们写的程序针对硬件部分抽象出 led_operations结构体。

8.2 分层

上下分层,比如我们前面写的 LED驱动程序就分为 2层:
① 上层实现硬件无关的操作,比如注册字符设备驱动:leddrv.c
② 下层实现硬件相关的操作,比如 board_A.c实现单板 A的 LED操作
在这里插入图片描述

8.3 分离

还能不能改进?分离。
在 board_A.c中,实现了一个 led_operations,为 LED引脚实现了初始化函数、控制函数:

static struct led_operations board_demo_led_opr = {  
.num  = 1,  
.init = board_demo_led_init,  
.ctl  = board_demo_led_ctl, 
}; 

如果硬件上更换一个引脚来控制 LED怎么办?你要去修改上面结构体中的 init、ctl函数。
实际情况是,每一款芯片它的 GPIO操作都是类似的。比如:GPIO1_3、GPIO5_4这 2个引脚接到 LED:
① GPIO1_3属于第 1组,即 GPIO1。
有方向寄存器 DIR、数据寄存器 DR等,基础地址是 addr_base_addr_gpio1。
设置为 output引脚:修改 GPIO1的 DIR寄存器的 bit3。
设置输出电平:修改 GPIO1的 DR寄存器的 bit3。

② GPIO5_4属于第 5组,即 GPIO5。
有方向寄存器 DIR、数据寄存器 DR等,基础地址是 addr_base_addr_gpio5。
设置为 output引脚:修改 GPIO5的 DIR寄存器的 bit4。
设置输出电平:修改 GPIO5的 DR寄存器的 bit4。

既然引脚操作那么有规律,并且这是跟主芯片相关的,那可以针对该芯片写出比较通用的硬件操作代码。
比如 board_A.c使用芯片 chipY,那就可以写出:chipY_gpio.c,它实现芯片 Y的 GPIO操作,适用于芯片 Y的所有 GPIO引脚。
使用时,我们只需要在 board_A_led.c中指定使用哪一个引脚即可。
程序结构如下:
在这里插入图片描述
以面向对象的思想,在 board_A_led.c中实现 led_resouce结构体,它定义“资源”──要用哪一个引脚。 在 chipY_gpio.c中仍是实现 led_operations结构体,它要写得更完善,支持所有 GPIO。

8.4 写示例代码

使用 GIT下载所有源码后,本节源码位于如下目录:

01_all_series_quickstart\ 
05_嵌入式 Linux驱动开发基础知识\source\02_led_drv\03_led_drv_template_seperate 

程序仍分为上下结构:上层 leddrv.c向内核注册 file_operations结构体;下层 chip_demo_gpio.c提供 led_operations结构体来操作硬件。

下层的代码分为 2个:chip_demo_gpio.c实现通用的 GPIO操作,board_A_led.c指定使用哪个 GPIO,即“资源”。

led_resource.h中定义了 led_resource结构体,用来描述 GPIO:

04 /* GPIO3_0 */ 
05 /* bit[31:16] = group */ 
06 /* bit[15:0]  = which pin */ 
07 #define GROUP(x) (x>>16) 
08 #define PIN(x)   (x&0xFFFF) 
09 #define GROUP_PIN(g,p) ((g<<16) | (p)) 
10 
11 struct led_resource { 
12      int pin; 
13 }; 
14 
15 struct led_resource *get_led_resouce(void); 
16 

board_A_led.c指定使用哪个 GPIO,它实现一个 led_resource结构体,并提供访问函数:

02 #include "led_resource.h" 
03 
04 static struct led_resource board_A_led = { 
05      .pin = GROUP_PIN(3,1), 
06 }; 
07 
08 struct led_resource *get_led_resouce(void) 
09 { 
10      return &board_A_led; 
11 } 
12 

chip_demo_gpio.c中,首先获得 board_A_led.c实现的 led_resource结构体,然后再进行其他操作,请看下面第 26行:

20 static struct led_resource *led_rsc; 
21 static int board_demo_led_init (int which) /* 初始化 LED, which-哪个 LED */ 
22 { 
23      //printk("%s %s line %d, led %d\n", __FILE__, __FUNCTION__, __LINE__, which); 
24      if (!led_rsc) 
25      { 
26              led_rsc = get_led_resouce(); 
27      } 
28 ...
29}

8.5 课后作业

使用“分离”的思想,去改造前面写的 LED驱动程序:实现 led_resouce,在里面可以指定要使用哪一个 LED;改造 led_operations,让它能支持更多 GPIO。
注意:作为练习,led_operations结构体不需要写得很完善,不需要支持所有 GPIO,你可以只支持若干个 GPIO即可。

第九章 驱动进化之路:总线设备驱动模型

在这里插入图片描述

示例:
在这里插入图片描述

9.1 驱动编写的 3种方法

以 LED驱动为例:

9.1.1 传统写法

在这里插入图片描述
使用哪个引脚,怎么操作引脚,都写死在代码中。
最简单,不考虑扩展性,可以快速实现功能。 修改引脚时,需要重新编译。

9.1.2 总线设备驱动模型

在这里插入图片描述
引入 platform_device/platform_driver,将“资源”与“驱动”分离开来。
代码稍微复杂,但是易于扩展。
冗余代码太多,修改引脚时设备端的代码需要重新编译。 更换引脚时,上图中的 led_drv.c基本不用改,但是需要修改 led_dev.c

9.1.3 设备树

在这里插入图片描述
通过配置文件──设备树来定义“资源”。
代码稍微复杂,但是易于扩展。
无冗余代码,修改引脚时只需要修改 dts文件并编译得到 dtb文件,把它传给内核。
无需重新编译内核/驱动。

9.2 在 Linux中实现“分离”:Bus/Dev/Drv模型

在这里插入图片描述

9.3 匹配规则

9.3.1 最先比较:platform_device. driver_override和 platform_driver.driver.name

可以设置 platform_device的 driver_override,强制选择某个 platform_driver。

9.3.2 然后比较:platform_device. name和 platform_driver.id_table[i].name

Platform_driver.id_table是“platform_device_id”指针,表示该 drv支持若干个 device,它里面列出了各个 device的{.name, .driver_data},其中的“name”表示该 drv支持的设备的名字,driver_data是些提供给该 device的私有数据。

9.3.3 最后比较:platform_device.name和 platform_driver.driver.name

platform_driver.id_table可能为空, 这时可以根据 platform_driver.driver.name来寻找同名的 platform_device。

9.3.4 函数调用关系

platform_device_register 
platform_device_add     device_add         bus_add_device // 放入链表         bus_probe_device  // probe枚举设备,即找到匹配的(dev, drv)            device_initial_probe                 __device_attach                     bus_for_each_drv(...,__device_attach_driver,...)                         __device_attach_driver                             driver_match_device(drv, dev) // 是否匹配                             driver_probe_device         // 调用 drv的 probe 
platform_driver_register 
__platform_driver_register     driver_register         bus_add_driver // 放入链表             driver_attach(drv)                     bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);                         __driver_attach                             driver_match_device(drv, dev) // 是否匹配                             driver_probe_device         // 调用 drv的 probe 

9.4 常用函数

这些函数可查看内核源码:drivers/base/platform.c,根据函数名即可知道其含义。 下面摘取常用的几个函数。

9.4.1 注册/反注册

platform_device_register/ platform_device_unregister 
platform_driver_register/ platform_driver_unregister 
platform_add_devices // 注册多个 device 

9.4.2 获得资源

返回该 dev中某类型(type)资源中的第几个(num):

struct resource *platform_get_resource(struct platform_device *dev,unsigned int type,unsigned int num)

返回该 dev所用的第几个(num)中断:

int platform_get_irq(struct platform_device *dev,unsigned int num)

通过名字(name)返回该 dev的某类型(type)资源:

struct resource *platform_get_resource byname(struct platform_device *dev,unsigned int type,const char *name)

通过名字(name)返回该 dev的中断号:

int platform_get_irq_byname(struct platform_device *dev,unsigned char *name)

9.5 怎么写程序

9.5.1 分配/设置/注册 platform_device结构体

在里面定义所用资源,指定设备名字。

9.5.2 分配/设置/注册 platform_driver结构体

在其中的 probe函数里,分配/设置/注册 file_operations结构体, 并从 platform_device中确实所用硬件资源。 指定 platform_driver的名字。

9.6 课后作业

在内核源码中搜索 platform_device_register可以得到很多驱动,选择一个作为例子:
① 确定它的名字
② 根据它的名字找到对应的 platform_driver
③ 进入 platform_device_register/platform_driver_register内部,分析 dev和 drv的匹配过程


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

相关文章

全面横扫:dlib Python API在Linux和Windows的配置方案

前言 在计算机视觉和人工智能领域&#xff0c;dlib是一个备受推崇的工具库。它为开发者提供了强大的图像处理、机器学习和深度学习功能。在计算机视觉项目中&#xff0c;配置dlib Python API是一个重要的初始步骤。本文将引导读者详细了解在Linux和Windows系统上安装和配置dli…

YOLOV8-DET转ONNX和RKNN

目录 1. 前言 2.环境配置 (1) RK3588开发板Python环境 (2) PC转onnx和rknn的环境 3.PT模型转onnx 4. ONNX模型转RKNN 6.测试结果 1. 前言 yolov8就不介绍了&#xff0c;详细的请见YOLOV8详细对比&#xff0c;本文章注重实际的使用&#xff0c;从拿到yolov8的pt检测模型&…

优化 Node.js 性能:检测内存泄漏和高 CPU 使用率

优化 Node.js 性能&#xff1a;检测内存泄漏和高 CPU 使用率 Node.js 是一种流行的 JavaScript 运行时&#xff0c;以其速度、性能和可扩展性而闻名。然而&#xff0c;即使是优化和编写得非常好的 Node.js 应用程序也可能会遇到性能问题&#xff0c;例如内存泄漏和 CPU 使用率…

SW免安装的toolbox只读问题

把SOLIDWORKSDATA 整体复制到另外的目录&#xff0c;然后这里设置目录位置。不然原始位置有只读属性

【问题思考总结】CPU怎么访问磁盘?CPU只有32位,最多只能访问4GB的空间吗?

问题 在学习操作系统的时候发现了这样一个问题&#xff0c;32位的CPU寻址空间只有4GB&#xff0c;难道只有4GB的空间可以使用吗&#xff1f;以此为始&#xff0c;我开始了一些思考。 思考 Q1&#xff1a;首先&#xff0c;我似乎混淆了一个概念&#xff0c;内存和外存&#x…

【面试题精讲】Java移位运算符

“ 有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top ” 首发博客地址[1] 面试题手册[2] 系列文章地址[3] 1. 什么是移位运算符? 在 Java 中&#xff0c;移位运算符用于对二进制数进行位移操作。它们…

查找排序部分习题 242. 有效的字母异位词 74. 搜索二维矩阵 1. 两数之和 167.两数之和 II

242. 有效的字母异位词 给定两个字符串 s 和 t &#xff0c;编写一个函数来判断 t 是否是 s 的字母异位词。 注意&#xff1a;若 s 和 t 中每个字符出现的次数都相同&#xff0c;则称 s 和 t 互为字母异位词。 class Solution(object):def isAnagram(self, s, t):""…

快速幂算法-python

看了大神讲解&#xff0c;理论在这里&#xff1a;快速幂算法&#xff08;全网最详细地带你从零开始一步一步优化&#xff09;-CSDN博客 例题&#xff1a;求整数 base 的 整数 power 次方&#xff0c;对整数 num_mod 取幂。 python 代码如下&#xff1a; import timedef norm…