嵌入式linux/鸿蒙开发板(IMX6ULL)开发(十九)I2C应用编程(下)

简介: 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(十九)I2C应用编程

1.4.5. 内核里怎么传输数据


使用一句话概括I2C传输:

APP通过I2C Controller与I2C Device传输数据

APP通过i2c_adapter与i2c_client传输i2c_msg

内核函数i2c_transfer

i2c_msg里含有addr,所以这个函数里不需要i2c_client

1670915589467.jpg


1.5. 无需编写驱动直接访问设备_I2C-Tools介绍


参考资料:

Linux驱动程序: drivers/i2c/i2c-dev.c

I2C-Tools-4.2: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/

AP3216C:

git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git

该GIT仓库中的文件《嵌入式Linux应用开发完全手册_韦东山全系列视频文档全集.pdf》第10.1篇,第十六章 I2C编程


1.5.1 I2C硬件连接

1670915658942.jpg


1.5.2. 无需编写驱动程序即可访问I2C设备


APP访问硬件肯定是需要驱动程序的,

对于I2C设备,内核提供了驱动程序 drivers/i2c/i2c-dev.c ,通过它可以直接使用下面的I2C控制器

驱动程序来访问I2C设备。

框架如下:

1670915667094.jpg

i2c-tools是一套好用的工具,也是一套示例代码。


1.5.3. 体验I2C-Tools


使用一句话概括I2C传输:APP通过I2C Controller与I2C Device传输数据。

所以使用I2C-Tools时也需要指定:

哪个I2C控制器(或称为I2C BUS、I2C Adapter)

哪个I2C设备(设备地址)

数据:读还是写、数据本身


1.5.3.1 交叉编译


在Ubuntu设置交叉编译工具链

IMX6ULL

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihfexport PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin


修改I2C-Tools的Makefile指定交叉编译工具链

CC ?= gcc
AR ?= ar
STRIP ?= strip
改为(指定交叉编译工具链前缀, 去掉问号):
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
STRIP = $(CROSS_COMPILE)strip


在Makefile中,“?=”在第一次设置变量时才会起效果,如果之前设置过该变量,则不会起效果。

执行make即可

执行make时,是动态链接,需要把libi2c.so也放到单板上

想静态链接的话,执行: make USE_STATIC_LIB=1


1.5.3.2 用法


i2cdetect:I2C检测

// 列出当前的I2C Adapter(或称为I2C Bus、I2C Controller)
i2cdetect -l
// 打印某个I2C Adapter的Functionalities, I2CBUS为0、1、2等整数
i2cdetect -F I2CBUS
// 看看有哪些I2C设备, I2CBUS为0、1、2等整数
i2cdetect -y -a I2CBUS
// 效果如下
# i2cdetect -l
i2c-1 i2c STM32F7 I2C(0x40013000) I2C adapter
i2c-2 i2c STM32F7 I2C(0x5c002000) I2C adapter
i2c-0 i2c STM32F7 I2C(0x40012000) I2C adapter
# i2cdetect -F 0
Functionalities implemented by /dev/i2c-0:
I2C yes
SMBus Quick Command yes
SMBus Send Byte yes
SMBus Receive Byte yes
SMBus Write Byte yes
SMBus Read Byte yes
SMBus Write Word yes
SMBus Read Word yes
SMBus Process Call yes
SMBus Block Write yes
SMBus Block Read yes
SMBus Block Process Call yes
SMBus PEC yes
I2C Block Write yes
I2C Block Read yes
// --表示没有该地址对应的设备, UU表示有该设备并且它已经有驱动程序,
// 数值表示有该设备但是没有对应的设备驱动
# i2cdetect -y -a 0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: 00 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- UU -- -- -- 1e --
20: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --


i2cget:I2C读

使用说明如下:

# i2cget
Usage: i2cget [-f] [-y] [-a] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77, or 0x00 - 0x7f if -a is given)
MODE is one of:
b (read byte data, default)
w (read word data)
c (write byte/read byte)
Append p for SMBus PEC


使用示例:

// 读一个字节: I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
i2cget -f -y I2CBUS CHIP-ADDRESS
// 读某个地址上的一个字节:
// I2CBUS为0、1、2等整数, 表示I2C Bus
// CHIP-ADDRESS表示设备地址
// DATA-ADDRESS: 芯片上寄存器地址
// MODE:有2个取值, b-使用`SMBus Read Byte`先发出DATA-ADDRESS, 再读一个字节,
中间无P信号
// c-先write byte, 在read byte,中间有P信号
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE
i2cset:I2C写
使用说明如下:
使用示例:
// 读某个地址上的2个字节:
// I2CBUS为0、1、2等整数, 表示I2C Bus
// CHIP-ADDRESS表示设备地址
// DATA-ADDRESS: 芯片上寄存器地址
// MODE:w-表示先发出DATA-ADDRESS,再读2个字节
i2cget -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS MODE


i2cset:I2C写

使用说明如下:

# i2cset
Usage: i2cset [-f] [-y] [-m MASK] [-r] [-a] I2CBUS CHIP-ADDRESS DATA-ADDRESS
[VALUE] ... [MODE]
I2CBUS is an integer or an I2C bus name
ADDRESS is an integer (0x03 - 0x77, or 0x00 - 0x7f if -a is given)
MODE is one of:
c (byte, no value)
b (byte data, default)
w (word data)
i (I2C block data)
s (SMBus block data)
Append p for SMBus PEC


使用示例:

/ 写一个字节: I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
// DATA-ADDRESS就是要写的数据
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS
// 给address写1个字节(address, value):
// I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
// DATA-ADDRESS: 8位芯片寄存器地址;
// VALUE: 8位数值
// MODE: 可以省略,也可以写为b
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE [b]
// 给address写2个字节(address, value):
// I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
// DATA-ADDRESS: 8位芯片寄存器地址;
// VALUE: 16位数值
// MODE: w
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE w
// SMBus Block Write:给address写N个字节的数据
// 发送的数据有:address, N, value1, value2, ..., valueN
// 跟`I2C Block Write`相比, 需要发送长度N
// I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
// DATA-ADDRESS: 8位芯片寄存器地址;
// VALUE1~N: N个8位数值
// MODE: s
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE1 ... VALUEN s
// I2C Block Write:给address写N个字节的数据
// 发送的数据有:address, value1, value2, ..., valueN
// 跟`SMBus Block Write`相比, 不需要发送长度N
// I2CBUS为0、1、2等整数, 表示I2C Bus; CHIP-ADDRESS表示设备地址
// DATA-ADDRESS: 8位芯片寄存器地址;
// VALUE1~N: N个8位数值
// MODE: i
i2cset -f -y I2CBUS CHIP-ADDRESS DATA-ADDRESS VALUE1 ... VALUEN i


i2ctransfer:I2C传输(不是基于SMBus)

使用说明如下:

# i2ctransfer
Usage: i2ctransfer [-f] [-y] [-v] [-V] [-a] I2CBUS DESC [DATA] [DESC
[DATA]]...
I2CBUS is an integer or an I2C bus name
DESC describes the transfer in the form: {r|w}LENGTH[@address]
1) read/write-flag 2) LENGTH (range 0-65535) 3) I2C address (use last
one if omitted)
DATA are LENGTH bytes for a write message. They can be shortened by a
suffix:
= (keep value constant until LENGTH)
+ (increase value by 1 until LENGTH)
- (decrease value by 1 until LENGTH)
p (use pseudo random generator until LENGTH with value as seed)
Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):
# i2ctransfer 0 w1@0x50 0x64 r8
Example (same EEPROM, at offset 0x42 write 0xff 0xfe ... 0xf0):
# i2ctransfer 0 w17@0x50 0x42 0xff-


使用举例:

// Example (bus 0, read 8 byte at offset 0x64 from EEPROM at 0x50):
# i2ctransfer -f -y 0 w1@0x50 0x64 r8
// Example (bus 0, write 3 byte at offset 0x64 from EEPROM at 0x50):
# i2ctransfer -f -y 0 w9@0x50 0x64 val1 val2 val3
// Example
// first: (bus 0, write 3 byte at offset 0x64 from EEPROM at 0x50)
// and then: (bus 0, read 3 byte at offset 0x64 from EEPROM at 0x50)
# i2ctransfer -f -y 0 w9@0x50 0x64 val1 val2 val3 r3@0x50
# i2ctransfer -f -y 0 w9@0x50 0x64 val1 val2 val3 r3 //如果设备地址不变,后面的设
备地址可省略


1.5.3.3 使用I2C-Tools操作传感器AP3216C

1670915807488.jpg

AP3216C是红外、光强、距离三合一的传感器,以读出光强、距离值为例,步骤如下:

复位:往寄存器0写入0x4

使能:往寄存器0写入0x3

读光强:读寄存器0xC、0xD得到2字节的光强

读距离:读寄存器0xE、0xF得到2字节的距离值

AP3216C的设备地址是0x1E,假设节在I2C BUS0上,操作命令如下:

使用SMBus协议

i2cset -f -y 0 0x1e 0 0x4
i2cset -f -y 0 0x1e 0 0x3
i2cget -f -y 0 0x1e 0xc w
i2cget -f -y 0 0x1e 0xe w


使用I2C协议

i2ctransfer -f -y 0 w2@0x1e 0 0x4
i2ctransfer -f -y 0 w2@0x1e 0 0x3
i2ctransfer -f -y 0 w1@0x1e 0xc r2
i2ctransfer -f -y 0 w1@0x1e 0xe r2


1.5.4. I2C-Tools的访问I2C设备的2种方式


I2C-Tools可以通过SMBus来访问I2C设备,也可以使用一般的I2C协议来访问I2C设备。

使用一句话概括I2C传输:APP通过I2C Controller与I2C Device传输数据。

在APP里,有这几个问题:

怎么指定I2C控制器?

i2c-dev.c提供为每个I2C控制器(I2C Bus、I2C Adapter)都生成一个设备节点:/dev/i2c0、/dev/i2c-1等待

open某个/dev/i2c-X节点,就是去访问该I2C控制器下的设备

怎么指定I2C设备?

通过ioctl指定I2C设备的地址

ioctl(file, I2C_SLAVE, address)

如果该设备已经有了对应的设备驱动程序,则返回失败

ioctl(file, I2C_SLAVE_FORCE, address)

如果该设备已经有了对应的设备驱动程序

但是还是想通过i2c-dev驱动来访问它

则使用这个ioctl来指定I2C设备地址

怎么传输数据?

两种方式

一般的I2C方式:ioctl(file, I2C_RDWR, &rdwr)

SMBus方式:ioctl(file, I2C_SMBUS, &args)


1.5.5. 源码分析


1.5.5.1 使用I2C方式


示例代码:i2ctransfer.c

1670915849066.jpg


1.5.5.2 使用SMBus方式


示例代码:i2cget.c、i2cset.c

1670915861056.jpg


1.6. 编写APP直接访问EEPROM


参考资料:

Linux驱动程序: drivers/i2c/i2c-dev.c

I2C-Tools-4.2: https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/

AT24cxx.pdf

本节源码:GIT仓库中

doc_and_source_for_drivers\IMX6ULL\source\04_I2C\01_at24c02_test

doc_and_source_for_drivers\STM32MP157\source\A7\04_I2C\01_at24c02_test


1.6.1 硬件连接


STM32MP157的I2C模块连接方法

1670915876880.jpg

IMX6ULL的I2C模块连接方法

1670915884716.jpg


1.6.2. AT24C02访问方法


1.6.2.1 设备地址


从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:

1670915898758.jpg

打开I2C模块的原理图(这2个文件是一样的):

STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模
块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf
IMX6ULL\开发板配套资料\原理图
\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf


如下:

1670915930669.jpg

从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。


1.6.2.2 写数据

1670915945625.jpg


1.6.2.3 读数据

可以读1个字节,也可以连续读出多个字节。

连续读多个字节时,芯片内部的地址会自动累加。

当地址到达存储空间最后一个地址时,会从0开始。


1670915952156.jpg

1670915958172.jpg

1.6.3. 使用I2C-Tools的函数编程


在这里插入代码片



1.6.4. 编译


1.6.4.1 在Ubuntu设置交叉编译工具链

IMX6ULL

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihfexport PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-
2016.11-x86_64_arm-linux-gnueabihf/bin


1.6.4.2 使用I2C-Tools的源码

1670915989948.jpg


1.6.4.3 编译


为IMX6ULL编译时,有如下错误:

1670916000791.jpg

这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。

需要我们自己提供这个头文件,解决方法:

1670916014702.jpg


1.6.4.4 上机测试


以下命令在开发板中执行。

挂载NFS

vmware使用NAT(假设windowsIP为192.168.1.100)

[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999
192.168.1.100:/home/book/nfs_rootfs /mnt


vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为

192.168.1.137

[root@100ask:~]# mount -t nfs -o nolock,vers=3
192.168.1.137:/home/book/nfs_rootfs /mnt


复制、执行程序

[root@100ask:~]# cp /mnt/at24c02_test /bin
[root@100ask:~]# at24c02_test 0 w www.100ask.net
[root@100ask:~]# at24c02_test 0 r
get data: www.100ask.net
相关文章
|
2月前
|
Ubuntu Linux 开发者
Ubuntu20.04搭建嵌入式linux网络加载内核、设备树和根文件系统
使用上述U-Boot命令配置并启动嵌入式设备。如果配置正确,设备将通过TFTP加载内核和设备树,并通过NFS挂载根文件系统。
138 15
|
3月前
|
存储 监控 Linux
嵌入式Linux系统编程 — 5.3 times、clock函数获取进程时间
在嵌入式Linux系统编程中,`times`和 `clock`函数是获取进程时间的两个重要工具。`times`函数提供了更详细的进程和子进程时间信息,而 `clock`函数则提供了更简单的处理器时间获取方法。根据具体需求选择合适的函数,可以更有效地进行性能分析和资源管理。通过本文的介绍,希望能帮助您更好地理解和使用这两个函数,提高嵌入式系统编程的效率和效果。
140 13
|
7月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
293 3
|
11天前
鸿蒙开发:V2版本装饰器@Once
@Once装饰器只能与@Param搭配使用,仅此一个组合,无其他使用方式,还有就是,必须在V2版本,也就是@ComponentV2装饰的自定义组件中,否则会报异常。
鸿蒙开发:V2版本装饰器@Once
|
4天前
|
API 开发者 Windows
uniapp 极速上手鸿蒙开发
uniapp 自版本 `4.28.2024092502` 起支持鸿蒙应用开发,现版本 `4.36.2024112817` 同时支持鸿蒙应用和元服务开发。本文介绍使用 HBuilderX 4.24+ 和 DevEco Studio 进行环境配置、项目创建及运行的详细步骤,涵盖从 AGC 平台新建项目、配置证书到最终运行项目的全流程,帮助开发者快速上手鸿蒙开发。注意:HBuilderX 4.31+ 构建的鸿蒙运行包不支持 x86_64 平台,需使用真机调试。
112 85
uniapp 极速上手鸿蒙开发
|
5天前
鸿蒙开发:wrapBuilder传递参数
本文,主要简单了介绍了一下,非UI使用的情况下,wrapBuilder传递数据问题,除了以上的方式之外,还有其它的方式可以实现,在实际的开发中,还是具体问题具体分析。
77 61
鸿蒙开发:wrapBuilder传递参数
|
3天前
|
存储
鸿蒙开发:自定义一个搜索模版
这样的一个模版,可以简单的分为,三个部分,分别是上边的搜索框,中间的历史搜索和下边的热门搜索,搜索框,我们直接可以使用系统的组件Search,历史搜索,由于是内容不一的搜索的内容,这里使用弹性布局Flex,下边的热门搜索,条目规格一致,这里我们直接使用Grid网格组件。
41 23
鸿蒙开发:自定义一个搜索模版
|
2天前
|
IDE 程序员 编译器
鸿蒙开发:ArkTs语言注释
关于注释,有一点需要注意,那就是,注释,不会被编译器或解释器执行,而本小节的重点并不是简单的教大家注释如何去写,而是在实际的项目中,我们能够真正的把注释投入到实际的开发中。
36 18
鸿蒙开发:ArkTs语言注释
|
2天前
|
传感器 存储 安全
鸿蒙开发:权限管理之权限声明
本文,主要简单概述了为什么要有权限管理,以及权限管理的声明原则,这些都是基本的概念内容,大家做为了解即可,重要的是怎么声明权限,在什么位置声明权限,这一点需要掌握。
36 16
鸿蒙开发:权限管理之权限声明
|
1天前
|
前端开发 JavaScript 程序员
鸿蒙开发:console日志输出
针对初学者而言,大家只需要掌握住日志打印即可,等到了鸿蒙应用开发的时候,还有一个鸿蒙原生的打印工具HiLog,到时,我们也会详细的去讲述,也会针对HiLog,封装一个通用的工具类。
26 11
鸿蒙开发:console日志输出