Linux学习第38天:Linux I2C 驱动实验(二):哥俩好

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


       本节笔记主要学习I2C设备驱动编写及硬件原理图分析。

        先把整个本节的思维导图贴出来:

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

        适配器驱动一般都是由SOC厂商提供的,不作为本节学习的重点内容。做一般了解即可。在 imx6ull.dtsi 文件中找到 I.MX6U 的 I2C1 控制器节点---i2c1 节点。i2c1节点的 compatible属性值有两个:“ fsl,imx6ul-i2c”和“ fsl,imx21-i2c”。

        I.MX6U 的 I2C 适配器驱动是个标准的 platform 驱动。当设备和驱动匹配成功以后i2c_imx_probe 函数就会执行, i2c_imx_probe 函数就会完成 I2C 适配器初始化工作。

        NXP 使用 imx_i2c_struct 结构体来表示 I.MX 系列 SOC 的 I2C 控制器,这里使用 devm_kzalloc 函数来申请内存。

        i2c_imx_probe 函数主要的工作就是一下两点:
①、初始化 i2c_adapter,设置 i2c_algorithm 为 i2c_imx_algo,最后向 Linux 内核注册
i2c_adapter。
②、初始化 I2C1 控制器的相关寄存器。

        i2c_imx_algo 包含 I2C1 适配器与 I2C 设备的通信函数 master_xfer。

        最终就是通过i2c_imx_xfer函数来完成与 I2C 设备通信的。

三、I2C设备驱动编写流程

1、I2C设备信息描述

1)、未使用设备树的时候

        在未使用设备树的时候需要在 BSP 里面使用 i2c_board_info 结构体来描述一个具体的 I2C 设备。

295 struct i2c_board_info {
296 char type[I2C_NAME_SIZE]; /* I2C 设备名字 */
297 unsigned short flags; /* 标志 */
298 unsigned short addr; /* I2C 器件地址 */
299 void *platform_data;
300 struct dev_archdata *archdata;
301 struct device_node *of_node;
302 struct fwnode_handle *fwnode;
303 int irq;
304 };

关于 OV2640 的 I2C 设备信息描述如下:【主要工作就是设置 I2C 设备名字为 ov2640, ov2640 的器件地址为 0X30。】

392 static struct i2c_board_info mx27_3ds_i2c_camera = {
393 I2C_BOARD_INFO("ov2640", 0x30),
394 };

        使用 I2C_BOARD_INFO 来完成 mx27_3ds_i2c_camera 的初始化工作,I2C_BOARD_INFO 是一个宏,定义如下:

316 #define I2C_BOARD_INFO(dev_type, dev_addr) \
317 .type = dev_type, .addr = (dev_addr)

2)、使用设备树的时候

        使用设备树的时候 I2C 设备信息通过创建相应的节点就行了。

1 &i2c1 {
2 clock-frequency = <100000>;
3 pinctrl-names = "default";
4 pinctrl-0 = <&pinctrl_i2c1>;
5 status = "okay";
6
7 mag3110@0e {/*第 7~11 行,向 i2c1 添加 mag3110 子节点   第 7 行“ mag3110@0e”是子节点名字,“ @”后面的“ 0e”就是 mag3110 的 I2C 器件地址。*/
8 compatible = "fsl,mag3110";/* 第 8 行设置 compatible 属性值为“ fsl,mag3110”。*/
9 reg = <0x0e>;/*第 9 行的 reg 属性也是设置 mag3110 的器件地址的,因此值为 0x0e。 */
10 position = <2>;
11 };
......
20 };

        I2C 设备节点的创建重点是 compatible 属性和 reg 属性的设置,一个用于匹配驱动,一个用于设置器件地址。

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

         对 I2C 设备寄存器进行读写操作,这里就要用到 i2c_transfer 函数了。 i2c_transfer 函数
最终会调用 I2C 适配器中 i2c_algorithm 里面的 master_xfer 函数,对于 I.MX6U 而言就是
i2c_imx_xfer 这个函数。 i2c_transfer 函数原型如下:

int i2c_transfer(struct i2c_adapter *adap,
struct i2c_msg *msgs,
int num)

adap: 所使用的 I2C 适配器, i2c_client 会保存其对应的 i2c_adapter。
msgs: I2C 要发送的一个或多个消息。
num: 消息数量,也就是 msgs 的数量。
返回值: 负值,失败,其他非负值,发送的 msgs 数量。
        msgs是一个i2c_msg类型的指针参数。Linux内核使用i2c_msg结构体来描述一个消息,结构体定义如下:

68 struct i2c_msg {
69 __u16 addr; /* 从机地址 */
70 __u16 flags; /* 标志 */
71 #define I2C_M_TEN 0x0010
72 #define I2C_M_RD 0x0001
73 #define I2C_M_STOP 0x8000
74 #define I2C_M_NOSTART 0x4000
75 #define I2C_M_REV_DIR_ADDR 0x2000
76 #define I2C_M_IGNORE_NAK 0x1000
77 #define I2C_M_NO_RD_ACK 0x0800
78 #define I2C_M_RECV_LEN 0x0400
79 __u16 len; /* 消息(本 msg)长度 */
80 __u8 *buf; /* 消息数据 */
81 };

        使用 i2c_transfer 函数发送数据之前要先构建好 i2c_msg,使用 i2c_transfer 进行 I2C 数据收
发的示例代码如下:

1 /* 设备结构体 */
2 struct xxx_dev {/*第 2~5行,设备结构体,在设备结构体里面添加一个执行 void的指针成员变量 private_data,
此成员变量用于保存设备的私有数据。在 I2C 设备驱动中我们一般将其指向 I2C 设备对应的
i2c_client。*/
3 ......
4 void *private_data; /* 私有数据,一般会设置为 i2c_client */
5 };
6
7 /*
8 * @description : 读取 I2C 设备多个寄存器数据
9 * @param – dev : I2C 设备
10 * @param – reg : 要读取的寄存器首地址
11 * @param – val : 读取到的数据
12 * @param – len : 要读取的数据长度
13 * @return : 操作结果
14 */
15 static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val,/*第 15~40 行, xxx_read_regs 函数用于读取 I2C 设备多个寄存器数据。第 18 行定义了一个
i2c_msg 数组, 2 个数组元素,因为 I2C 读取数据的时候要先发送要读取的寄存器地址,然后再
读取数据,所以需要准备两个 i2c_msg。一个用于发送寄存器地址,一个用于读取寄存器值。对于 msg[0],将 flags 设置为 0,表示写数据。 msg[0]的 addr 是 I2C 设备的器件地址, msg[0]的 buf
成员变量就是要读取的寄存器地址。对于 msg[1],将 flags 设置为 I2C_M_RD,表示读取数据。
msg[1]的 buf 成员变量用于保存读取到的数据, len 成员变量就是要读取的数据长度。调用
i2c_transfer 函数完成 I2C 数据读操作。*/
int len)
16 {
17 int ret;
18 struct i2c_msg msg[2];
19 struct i2c_client *client = (struct i2c_client *)
dev->private_data;
20
21 /* msg[0],第一条写消息,发送要读取的寄存器首地址 */
22 msg[0].addr = client->addr; /* I2C 器件地址 */
23 msg[0].flags = 0; /* 标记为发送数据 */
24 msg[0].buf = &reg; /* 读取的首地址 */
25 msg[0].len = 1; /* reg 长度 */
26
27 /* msg[1],第二条读消息,读取寄存器数据 */
28 msg[1].addr = client->addr; /* I2C 器件地址 */
29 msg[1].flags = I2C_M_RD; /* 标记为读取数据 */
30 msg[1].buf = val; /* 读取数据缓冲区 */
31 msg[1].len = len; /* 要读取的数据长度 */
32
33 ret = i2c_transfer(client->adapter, msg, 2);
34 if(ret == 2) {
35 ret = 0;
36 } else {
37 ret = -EREMOTEIO;
38 }
39 return ret;
40 }
41
42 /*
43 * @description : 向 I2C 设备多个寄存器写入数据
44 * @param – dev : 要写入的设备结构体
45 * @param – reg : 要写入的寄存器首地址
46 * @param – val : 要写入的数据缓冲区
47 * @param – len : 要写入的数据长度
48 * @return : 操作结果
49 */
50 static s32 xxx_write_regs(struct xxx_dev *dev, u8 reg, u8 *buf,/*第 50~66 行, xxx_write_regs 函数用于向 I2C 设备多个寄存器写数据, I2C 写操作要比读操
作简单一点,因此一个 i2c_msg 即可。数组 b 用于存放寄存器首地址和要发送的数据,第 59 行
设置 msg 的 addr 为 I2C 器件地址。第 60 行设置 msg 的 flags 为 0,也就是写数据。第 62 行设
置要发送的数据,也就是数组 b。第 63 行设置 msg 的 len 为 len+1,因为要加上一个字节的寄
存器地址。最后通过 i2c_transfer 函数完成向 I2C 设备的写操作。*/
u8 len)
51 {
52 u8 b[256];
53 struct i2c_msg msg;
54 struct i2c_client *client = (struct i2c_client *)
dev->private_data;
55
56 b[0] = reg; /* 寄存器首地址 */
57 memcpy(&b[1],buf,len); /* 将要发送的数据拷贝到数组 b 里面 */
58
59 msg.addr = client->addr; /* I2C 器件地址 */
60 msg.flags = 0; /* 标记为写数据 */
61
62 msg.buf = b; /* 要发送的数据缓冲区 */
63 msg.len = len + 1; /* 要发送的数据长度 */
64
65 return i2c_transfer(client->adapter, &msg, 1);
66 }

        I2C 数据发送函数 i2c_master_send,函数原型如下:

int i2c_master_send(const struct i2c_client *client,
const char *buf,
int count)

client: I2C 设备对应的 i2c_client。
buf:要发送的数据。
count: 要发送的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无
符号 16 位)类型的数据。
返回值: 负值,失败,其他非负值,发送的字节数。
I2C 数据接收函数为 i2c_master_recv,函数原型如下:

int i2c_master_recv(const struct i2c_client *client,
char *buf,
int count)

client: I2C 设备对应的 i2c_client。
buf:要接收的数据。
count: 要接收的数据字节数,要小于 64KB,以为 i2c_msg 的 len 成员变量是一个 u16(无
符号 16 位)类型的数据。
返回值: 负值,失败,其他非负值,发送的字节数。
 

四、硬件原理图分析

        AP3216C 使用的是 I2C1,其中 I2C1_SCL 使用的 UART4_RXD 这个IO、 I2C1_SDA 使用的是 UART4_TXD 这个 IO。

由于本节需要学习的内容较多,以下内容将在下节进行学习:

五、试验程序编写

1、修改设备树

2、AP3216驱动编写

3、编写测试APP

六、运行测试

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

2、运行测试


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

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

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

相关文章

乌镇峰会十年之约,为什么IBM是那颗最亮的星?

&#xff08;IBM董事长兼首席执行官Arvind Krishna&#xff09; 2023年处于世界互联网下一个50年开端的头五年。54年前的1969年&#xff0c;世界互联网诞生&#xff1b;同年&#xff0c;IBM助力“阿波罗”号登月成功、首次将人类宇航员送上了月球&#xff1b;当时&#xff0c;…

React Native适配Xcode 15 iOS 17.0+

iOS 17.0 Simulator(21A328)下载失败 App Store 更新到 Xcode15 后&#xff0c;无法运行模拟器和真机。需要下载iOS 17对应的模拟器。Xcode中更新非常容易中断失败&#xff0c;可以在官网单独下载iOS 17模拟器文件&#xff0c;例如&#xff1a;iOS_17.0.1_Simulator_Runtime.d…

WPS表格无法粘贴信息,原因是复制区域与粘贴区域形状不同

WPS表格无法粘贴信息&#xff0c;原因是复制区域与粘贴区域形状不同 问题描述 我是选中了一整列&#xff0c;复制&#xff0c;但是无法粘贴到另一个EXCEL表格中 原因 首先我的数据量很大&#xff0c;有20万行&#xff0c;然后需要复制的EXCEL是.xls格式的&#xff0c;.xls格…

ros1 基础学习07 - 模拟客户端生成小乌龟服务请求生成小乌龟

模拟客户端生成小乌龟服务请求生成小乌龟 一、话题模型二、创建功能包三 创建客户端Client代码四 配置CMakeLists.txt编译规则&#xff1a;五 测试启动ros 主服务启动小乌龟的服务启动模型客户端服务 一、话题模型 Sever端是海龟仿真器/turtlesim&#xff0c;Client端是待实现…

leetcode:2485. 找出中枢整数(python3解法)

难度&#xff1a;简单 给你一个正整数 n &#xff0c;找出满足下述条件的 中枢整数 x &#xff1a; 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数&#xff0c;则返回 -1 。题目保证对于给定的输入&#xff0c;至多存在一个中…

ChatGPT和API发生重大中断!

11月9日凌晨&#xff0c;OpenAI在官网发布&#xff0c;ChatGPT和API发生重大中断&#xff0c;导致全球所有用户无法正常使用&#xff0c;宕机时间超过2小时。 目前&#xff0c;OpenAI已经找到问题所在并进行了修复&#xff0c;但仍然不稳定&#xff0c;会继续进行安全监控。 …

3.前端调式

1. Elements 先来看这张图最上头的一行是一个功能菜单&#xff0c;每一个菜单都有它相应的功能和使用方法&#xff0c;依次从左往右来看 箭头按钮 用于在页面选择一个元素来审查和查看它的相关信息&#xff0c;当我们在Elements这个按钮页面下点击某个Dom元素时&#xff0c;箭…

[量化投资-学习笔记008]Python+TDengine从零开始搭建量化分析平台-CCI和ATR

目录 1. 指标简介CCIATR 2. 程序编写题外话 1. 指标简介 将这两个指标放在一起&#xff0c;一方面是因为这两个指标都属于摆动指数&#xff0c;可以反应市场的活跃度。 另一方面是因为CCI和ATR与之前提到的EMA,MACD,布林带的三个指标的计算基础不同。之前的三个指标都是以收盘…

Oracle迁移(RAC变单机模式)

1.升级内核 systemctl stop firewalld systemctl disable firewalldrpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm yum --enablerepo"elrepo-kernel" list --showduplic…

【MySQL】手把手教你centos7下载MySQL

centos7下载MySQL 前言正式开始卸载不需要的环境&#xff08;如果你之前没有安装过数据库相关的东西可以跳过&#xff09;下载mysql登录mysql登陆⽅法⼀【不⾏就下⼀个】登陆⽅法⼆【不⾏就下⼀个】登录方式三 前言 安装和卸载MySQL都用系统的root权限&#xff0c;更方便一点&…

SQL优化之MySQL执行计划(Explain)及索引失效详解

1、执行计划基础 1.1、执行计划&#xff08;Explain&#xff09;定义 在 MySQL 中可以通过 explain 关键字模拟优化器执行 SQL语句&#xff0c;从而解析MySQL 是如何处理 SQL 语句的。 1.2、MySQL查询过程 客户端向 MySQL 服务器发送一条查询请求服务器首先检查查询缓存&am…

SecoClient接收返回码超时解决

第一步&#xff0c;Windows键进入"设置" 第二步&#xff0c;进入"更新与安全" 第三步&#xff0c;找到恢复&#xff0c;点击“高级启动”下的“立即重新启动”&#xff0c;重启电脑&#xff1a;(此时要记住以下步骤) 第四步&#xff0c;重启后选择“疑难解…

下定决心做增长了吗 ——《增长黑客》读后感1

关注我的公众号&#xff0c;并回复【增长黑客】可以获取我整理的《增长黑客》书籍大纲 前言 年中就看完《增长黑客》这本书了&#xff0c;收获还是蛮多的&#xff0c;但因为各种原因&#xff0c;导致我都没有好好做下总结。 前阵子做完总结之后&#xff0c;我就第一时间给组员…

真是性价比之王,腾讯云这款88元云服务器已经圈粉无数!

你是否曾经想过拥有一台属于自己的云服务器&#xff0c;但是却被高昂的价格和复杂的配置吓到了&#xff1f;现在&#xff0c;腾讯云推出了一款价格亲民、简单易用的88元云服务器&#xff0c;让你的梦想成为现实。腾讯云88元/年云服务器配置见下图&#xff1a; 腾讯云88元服务器…

第十六章 反射与注解

通过java的反射机制&#xff0c;程序员可以更深入的控制程序的运行过程。例如&#xff0c;可在程序运行时对象用户输入的信息进行验证&#xff0c;还可以逆向控制程序的执行过程&#xff0c;讲解了反射&#xff0c;另外java还提供了Annotation注解功能&#xff0c;该功能建立在…

Unreal Engine 学习笔记 (3)—— 导入资源

1.导入FBX文件 打开系统文件管理器按下鼠标左键拖动fbx文件到UE编辑器中松开鼠标左键在弹出对话框FBX导入选项页面中&#xff0c;选择对应的骨骼 重定向骨骼 拖动UE4的walk_strafe_back.fbx文件到UE5编辑器中 在弹出的FBX导入选项对话框中选择UE4对应的骨骼 使用重定向资产…

阿里云AIGC小说生成【必得京东卡】

任务步骤 此文真实可靠不做虚假宣传&#xff0c;绝对真实&#xff0c;可截图为证。 领取任务 链接&#xff08;复制到wx打开&#xff09;&#xff1a;#小程序://ITKOL/1jw4TX4ZEhykWJd 教程实践 打开函数计算控制台 应用->创建应用->人工智能->通义千问 AI 助手-…

面试字节、美团、阿里等公司后,才知道软件测试面试题就这些...

一、Linux系统应用和环境配置 1、Linux系统的操作命令给我说10个&#xff0c;一般用什么工具远程连接Linux服务器&#xff1f; 2、Linux中的日志存储在哪里&#xff1f;怎么查看日志内容&#xff1f; 3、Linux中top和ps命令的区别&#xff1f; 4、Linux命令运行的结果如何写…

基于 Flink CDC 高效构建入湖通道

01Flink CDC 核心技术解析 Flink CDC 是基于数据库日志的 CDC 技术&#xff0c;实现了全增量一体化读取的数据集成框架。配合 Flink 优秀的管道能力和丰富的上下游生态&#xff0c;Flink CDC 可以高效实现海量数据的实时集成。 如上图所示&#xff0c;数据库表里有历史的全量数…

互联网按摩预约小程序开发;

随着移动互联网的普及&#xff0c;越来越多的人开始通过手机预约按摩服务。按摩预约小程序是一种方便快捷的预约方式&#xff0c;可以让用户随时随地预约按摩服务。那么&#xff0c;按摩预约小程序的开发周期要多久&#xff1f;按摩预约小程序的功能有哪些呢&#xff1f;本文将…