Linux学习第37天:Linux I2C 驱动实验:哥俩好

Linux版本号4.1.15   芯片I.MX6ULL                                    大叔学Linux    品人间百味  思文短情长 


        世界上的很多事物都是成双成对出现的。也包括在驱动开发的过程中,比如I2C中其实就是数据线和时钟线的相互配合才能完成的。

        I2C常用于连接各种外设、传感器等器件。本节笔记学习如何在Linux下开发I2C接口器件驱动。重点是学习Linux下的I2C驱动框架。以正点原子I.MX6U-ALPHA开发板上的AP3216这个三合一环境光传感器为例学习如何编写Linux下的I2C设备驱动程序。

        本节的思维导图如下:

一、Linux I2C驱动框架简介

Linux内核也将 I2C 驱动分为两部分:
①、 I2C 总线驱动, I2C 总线驱动就是 SOC 的 I2C 控制器驱动,也叫做 I2C 适配器驱动。
②、 I2C 设备驱动, I2C 设备驱动就是针对具体的 I2C 设备而编写的驱动。


1、I2C总线驱动

        Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter:

        i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。 i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。其中master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。 smbus_xfer 就是 SMBUS 总线的传输函数。

        I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter,这两个函数的区别在于 i2c_add_adapter 使用动态的总线号,而 i2c_add_numbered_adapter使用静态总线号。如果要删除 I2C 适配器的话使用i2c_del_adapter 函数即可。

        一般 SOC 的 I2C 总线驱动都是由半导体厂商编写的,不作为本次笔记学习的重点内容。
 

2、I2C设备驱动

        I2C 设备驱动重点关注两个数据结构: i2c_client 和 i2c_driver。i2c_client 就是描述设备信息的, i2c_driver 描述驱动内容,类似于 platform_driver。

1)、i2c_client 结构体

217 struct i2c_client {
218 unsigned short flags; /* 标志 */
219 unsigned short addr; /* 芯片地址, 7 位,存在低 7 位*/
......
222 char name[I2C_NAME_SIZE]; /* 名字 */
223 struct i2c_adapter *adapter; /* 对应的 I2C 适配器 */
224 struct device dev; /* 设备结构体 */
225 int irq; /* 中断 */
226 struct list_head detected;
......
230 };

        一个设备对应一个 i2c_client,每检测到一个 I2C 设备就会给这个 I2C 设备分配一个
i2c_client。

2)、i2c_driver 结构体

        i2c_driver 类似 platform_driver,是我们编写 I2C 设备驱动重点要处理的内容:

161 struct i2c_driver {
162 unsigned int class;
163
164 /* Notifies the driver that a new bus has appeared. You should
165 * avoid using this, it will be removed in a near future.
166 */
167 int (*attach_adapter)(struct i2c_adapter *) __deprecated;
168
169 /* Standard driver model interfaces */
170 int (*probe)(struct i2c_client *, const struct i2c_device_id *);/*当 I2C 设备和驱动匹配成功以后 probe 函数就会执行,和 platform 驱动一样。*/
171 int (*remove)(struct i2c_client *);
172
173 /* driver model interfaces that don't relate to enumeration */
174 void (*shutdown)(struct i2c_client *);
175
176 /* Alert callback, for example for the SMBus alert protocol.
177 * The format and meaning of the data value depends on the
178 * protocol.For the SMBus alert protocol, there is a single bit
179 * of data passed as the alert response's low bit ("event
180 flag"). */
181 void (*alert)(struct i2c_client *, unsigned int data);
182
183 /* a ioctl like command that can be used to perform specific
184 * functions with the device.
185 */
186 int (*command)(struct i2c_client *client, unsigned int cmd,
void *arg);
187
188 struct device_driver driver;/*device_driver 驱动结构体,如果使用设备树的话,需要设置 device_driver 的of_match_table 成员变量,也就是驱动的兼容(compatible)属性。*/
189 const struct i2c_device_id *id_table;/*id_table 是传统的、未使用设备树的设备匹配 ID 表。*/
190
191 /* Device detection callback for automatic device creation */
192 int (*detect)(struct i2c_client *, struct i2c_board_info *);
193 const unsigned short *address_list;
194 struct list_head clients;
195 };

        I2C 设备驱动编写人来说,重点工作就是构建 i2c_driver,构建完成以后需要向Linux 内核注册这个 i2c_driver。 i2c_driver 注册函数为 int i2c_register_driver:

int i2c_register_driver(struct module *owner,/*owner: 一般为 THIS_MODULE。*/
struct i2c_driver *driver)/*driver:要注册的 i2c_driver。*/

        i2c_add_driver 也常常用于注册 i2c_driver:

587 #define i2c_add_driver(driver) \
588 i2c_register_driver(THIS_MODULE, driver)

/*i2c_add_driver 就是对 i2c_register_driver 做了一个简单的封装,只有一个参数,就是要注册
的 i2c_driver。*/

i2c_del_driver 函数将前面注册的 i2c_driver 从 Linux 内核中注销掉.

void i2c_del_driver(struct i2c_driver *driver)
     /*i2c_driver 注册流程*/
1 /* i2c 驱动的 probe 函数 */
2 static int xxx_probe(struct i2c_client *client,
const struct i2c_device_id *id)
3 {
4 /* 函数具体程序 */
5 return 0;
6 }
7
8 /* i2c 驱动的 remove 函数 */
9 static int ap3216c_remove(struct i2c_client *client)
10 {
11 /* 函数具体程序 */
12 return 0;
13 }
14
15 /* 传统匹配方式 ID 列表   第 16~19 行, i2c_device_id,无设备树的时候匹配 ID 表。*/
16 static const struct i2c_device_id xxx_id[] = {
17 {"xxx", 0},
18 {}
19 };
20
21 /* 设备树匹配列表   第 22~25 行,of_device_id,设备树所使用的匹配表。*/
22 static const struct of_device_id xxx_of_match[] = {
23 { .compatible = "xxx" },
24 { /* Sentinel */ }
25 };
26
27 /* i2c 驱动结构体   第 28~37 行, i2c_driver,当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行*/
28 static struct i2c_driver xxx_driver = {
29 .probe = xxx_probe,
30 .remove = xxx_remove,
31 .driver = {
32 .owner = THIS_MODULE,
33 .name = "xxx",
34 .of_match_table = xxx_of_match,
35 },
36 .id_table = xxx_id,
37 };
38
39 /* 驱动入口函数 */
40 static int __init xxx_init(void)
41 {
42 int ret = 0;
43
44 ret = i2c_add_driver(&xxx_driver);
45 return ret;
46 }
47
48 /* 驱动出口函数 */
49 static void __exit xxx_exit(void)
50 {
51 i2c_del_driver(&xxx_driver);
52 }
53
54 module_init(xxx_init);
55 module_exit(xxx_exit);


3、I2C设备和驱动匹配过程

        I2C 设备和驱动的匹配过程是由 I2C 核心来完成的, drivers/i2c/i2c-core.c 就是 I2C 的核心
部分。I2C 核心提供了一些与具体硬件无关的 API 函数,例如:

1i2c_adapter 注册/注销函数
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
void i2c_del_adapter(struct i2c_adapter * adap)
2i2c_driver 注册/注销函数
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_add_driver (struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)

        设备和驱动的匹配过程也是由 I2C 总线完成的, I2C 总线的数据结构为 i2c_bus_type,其定义如下:

736 struct bus_type i2c_bus_type = {
737 .name = "i2c",
738 .match = i2c_device_match,/*.match 就是 I2C 总线的设备和驱动匹配函数,在这里就是 i2c_device_match 这个函数*/
739 .probe = i2c_device_probe,
740 .remove = i2c_device_remove,
741 .shutdown = i2c_device_shutdown,
742 };

i2c_device_match的定义如下:

457 static int i2c_device_match(struct device *dev, struct
device_driver *drv)
458 {
459 struct i2c_client *client = i2c_verify_client(dev);
460 struct i2c_driver *driver;
461
462 if (!client)
463 return 0;
464
465 /* Attempt an OF style match */
466 if (of_driver_match_device(dev, drv))/*of_driver_match_device 函数用于完成设备树设备和驱动匹配。比较 I2C 设备节
点的 compatible 属性和 of_device_id 中的 compatible 属性是否相等,如果相当的话就表示 I2C
设备和驱动匹配。*/
467 return 1;
468
469 /* Then ACPI style match */
470 if (acpi_driver_match_device(dev, drv))/*acpi_driver_match_device 函数用于 ACPI 形式的匹配。*/
471 return 1;
472
473 driver = to_i2c_driver(drv);
474 /* match on an id table if there is one */
475 if (driver->id_table)
476 return i2c_match_id(driver->id_table, client) != NULL;/*i2c_match_id 函数用于传统的、无设备树的 I2C 设备和驱动匹配过程。比较 I2C
设备名字和 i2c_device_id 的 name 字段是否相等,相等的话就说明 I2C 设备和驱动匹配。*/
477
478 return 0;
479 }

        由于本节内容太多,以下的内容将在下节笔记中学习:

二、I.MX6U的I2C适配器驱动分析

三、I2C设备驱动编写流程

1、I2C设备信息描述

2、I2C设备数据收发处理流程

四、硬件原理图分析

五、试验程序编写

1、修改设备树

2、AP3216驱动编写

3、编写测试APP

六、运行测试

1、编译驱动程序和测试APP

2、运行测试


本文为参考正点原子开发板配套教程整理而得,仅用于学习交流使用,不得用于商业用途。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

debian/ubuntu/windows配置wiregurad内网服务器(包含掉线自启动)

文章目录 前言一、服务器配置安装wireguard软件生成私钥公钥配置服务器参数配置服务器sysctl参数启动、停止服务端 二、用户端配置安装wireguard软件生成私钥公钥配置客户端参数启动、停止客户端配置服务开机启动 三、服务器添加、删除客户四、配置掉线自启动配置掉线自启动脚本…

【不正经操作】百度深度学习框架paddlepaddle本地运行-Python环境配置笔记

百度深度学习框架PaddlePaddle 百度深度学习框架PaddlePaddle是一个支持深度学习和机器学习的开源框架。它由百度公司于2016年开发并发布,现在已经成为中国最受欢迎的深度学习框架之一,并且在国际上也获得了不少关注。 特点与功能 易于使用 PaddlePa…

二分查找(二分法)

核心代码&#xff08;循环&#xff09;; int f -1;while(left<right){mid(leftright)/2;if(a[mid]key){fmid;break;}if(key<a[mid]) rmid-1;if(key>a[mid]) lmid1;}if(f-1) cout<<"没找到";else cout <<f<<endl;核心代码(递归): int bins…

Unity之NetCode多人网络游戏联机对战教程(6)--NetworkTransform组件

文章目录 前言NetworkTransform是什么玩家移动脚本NetworkTransform字段讲解Synchronizing ("Syncing")ThresholdsLocal spaceInterpolationSlerp PositionUse Quaternion SynchronizationUse Quaternion CompressionUse Half Float PrecisionAuthority modesServer …

0基础学习VR全景平台篇第118篇:利用动作录制器功能避免重复操作 - PS教程

上课&#xff01;全体起立~ 大家好&#xff0c;欢迎观看蛙色官方系列全景摄影课程&#xff01; 嗨&#xff0c;大家好。欢迎收看蛙色VR系列教程之PS利用动作记录器节约补地时间。 大家拍摄在补地的时候&#xff0c;利用插件选择输入输出选项的时候&#xff0c;每次重复操作…

Transformer的最简洁pytorch实现

目录 前言 1. 数据预处理 2. 模型参数 3. Positional Encoding 4. Pad Mask 5. Subsequence Mask 6. ScaledDotProductAttention 7. MultiHeadAttention 8. FeedForward Networks 9. Encoder Layer 10. Encoder 11. Decoder Layer 12. Decoder 13. Transformer 1…

什么是 HwameiStor?

HwameiStor 是一款 Kubernetes 原生的容器附加存储 (CAS) 解决方案&#xff0c;将 HDD、SSD 和 NVMe 磁盘形成本地存储资源池进行统一管理&#xff0c; 使用 CSI 架构提供分布式的本地数据卷服务&#xff0c;为有状态的云原生应用或组件提供数据持久化能力。 具体的功能特性如下…

GoLong的学习之路(二十)进阶,语法之反射(reflect包)

这个是为了接上之前的语法篇的。按照我的学习计划&#xff0c;这里此时应该有一个小项目来做一个统合。但是吧&#xff0c;突然觉得&#xff0c;似乎也没必要。能学go的大部分肯定都是有其他语言的基础的。 接下来说反射 文章目录 反射介绍reflect包TypeOftype name和type kin…

基于springboot的二次元商品销售网站的设计与开发

大家好我是玥沐春风&#xff0c;今天分享一个基于springboot的二次元商品销售网站的设计与开发&#xff0c;项目源码以及部署相关请联系我&#xff0c;文末附上联系信息 。 开发工具及技术 2.3.1 Spring Boot框架 SpringBoot是一个全新的开源的轻量级框架。简化了Spring应用的…

一张数学地图带你尽览数学分支

我们在学校学习的数学可能也只是数学领域的冰山一角&#xff0c;作为庞大而多样的学科&#xff0c;我今天将通过一张数学地图带你尽览数学分支。 本数学地图对应的视频讲解地址如下&#xff1a; https://www.youtube.com/watch?vOmJ-4B-mS-Y 另外&#xff0c;由于图片较大&a…

React进阶之路(一)-- JSX基础、组件基础

文章目录 React介绍React开发环境搭建项目目录说明以及相关调整 JSX基础JSX介绍JSX中使用js表达式JSX列表渲染JSX条件渲染JSX样式处理JSX注意事项 组件基础组件的概念函数组件类组件事件绑定如何绑定事件获取事件对象传递额外参数 组件状态状态不可变表单处理受控表单组件非受控…

Triples of Cows

题目传送门 引 模拟赛 T 4 T4 T4 , 变换挺妙的, 而且感觉转换后问题就迎刃而解了 解法 强行模拟拆点重连边显然不行,会让图的边数达到 n 2 n^2 n2 级别的 —————————————————————————————————————————————————— 考虑转…

python爬虫怎么翻页 ?

首先&#xff0c;你需要安装相关的库。在你的命令行窗口中&#xff0c;输入以下命令来安装所需的库&#xff1a; pip install requests beautifulsoup4然后&#xff0c;你可以使用以下代码来爬取网页内容并翻页&#xff1a; package mainimport ("fmt""net/htt…

米软科技 | 推进医院智慧管理分级评估体系建立、提升评级

国家卫生健康委办公厅于2021年3月15日发布了“关于印发医院智慧管理分级评估标准体系&#xff08;试行&#xff09;的通知”&#xff08;国卫办医函〔2021〕86 号&#xff09;&#xff0c;该评估体系用于指导医疗机构科学、规范开展智慧医院建设&#xff0c;提升医院管理精细化…

[黑马程序员Pandas教程]——Pandas缺失值处理

目录&#xff1a; 学习目标空值和缺失值查看缺失值 加载数据并通过info函数初步查看缺失值情况df.isnull().sum()空值数量统计Missingno库对缺失值的情况进行可视化探查 安装missingno库missingno.bar(df)缺失值数量可视化missingno.matrix(df)缺失值位置的可视化missingno.he…

【23-24 秋学期】NNDL 作业7 基于CNN的XO识别

一、用自己的语言解释以下概念 局部感知、权值共享池化&#xff08;子采样、降采样、汇聚&#xff09;。会带来那些好处和坏处&#xff1f;全卷积网络&#xff08;课上讲的这个概念不准确&#xff0c;同学们查资料纠正一下&#xff09;低级特征、中级特征、高级特征多通道。N输…

Java提高与实践

IO流 IO流概述 文件字节输入流&#xff1a;每次读取一个字节 package fileStream;import java.io.*;public class HelloFileInputStream {public static void main(String[] args) throws IOException {//创建文件字节输入流 管道&#xff0c;与源文件接通//写法一//InputStr…

getid3 获取视频时长

1、首先&#xff0c;我们需要先下载一份PHP类—getid3https://codeload.github.com/JamesHeinrich/getID3/zip/master 2.我在laravel6.0 中使用 需要在composer.json 自动加载 否则系统访问不到 在命令行 执行 composer dump-autoload $getID3 new \getID3();//视频文件需要放…

『 C++类与对象 』多继承与虚继承

文章目录 ⌨️多继承的概念语法 &#x1f5b1;️ ⌨️棱形继承⌨️虚继承虚继承是如何解决数据冗余和二义性的(不谈虚表概念)?&#x1f5b1;️ ⌨️多继承的概念 多继承指的是一个派生类是由多个基类继承而来的; 而在生活当中也有类似的例子:番茄既可以是水果,也可以是蔬菜;…

内核移植笔记 Cortex-M移植

常用寄存器 PRIMASK寄存器 为1位宽的中断屏蔽寄存器。在置位时&#xff0c;它会阻止不可屏蔽中断&#xff08;NMI&#xff09;和HardFault异常之外的所有异常&#xff08;包括中断&#xff09;。 实际上&#xff0c;它是将当前异常优先级提升为0&#xff0c;这也是可编程异常/…