1. 如何写程序操作AP3216C?
对于应用程序开发,大公司一般提供示例代码/驱动程序,可以直接调用函数,不需要关心硬件细节也不需要写驱动程序;对于中小公司可能从上到下都需要管,虽然不需要写驱动程序和硬件开发,但是需要懂,要知道怎么找到接口。
如下图所示,如果有专用库,优先使用①专用库
;如果没有专用库,则使用②通用库
;如果没有库,则使用③ open/read/write(底层接口)
;如果以上都没有,则需要自己④编写驱动程序
。
如果内核里面没有这款芯片的驱动程序的话,是没办法直接写这个程序的,还需要搞懂芯片的一些细节。 首先需要了解I2C传输协议。
通过接口访问I2C设备1和2的具体步骤:
- (1)如何分辨?设备地址
- (2)读写设备:
- 读:发出地址/方向——对方回应——读数据
- 写:发出地址/方向——对方回应——写数据
怎么发出地址?方向?对方怎么响应?怎么读/写数据? 使用库。
在i2c-tool-4.2源码中,以eeprog为例子来分析库函数, 下图是eeprog.c和24cXX.c中的示例代码
I2C设备可能有多个,且连接的I2C控制器也不一样,因此需要知道是哪个I2C控制器的哪个设备。
③写数据:用接口函数
④读数据:用接口函数
:读的时候需要知道是读哪个存储地址(内存的地址)
读数据时需要读和写两步操作:
①将需要访问的设备的存储地址写入从设备(告诉从设备)
②读设备存储地址里的数据
- 写:发出设备地址/方向——对方回应——写数据(存储地址)给设备
- 读:发出设备地址/方向——对方回应——读数据
2. 基于i2c编写AP3216C程序
AP3216C封装好的专用库函数:
四个步骤:
1.打开I2C控制器总线(主设备) fd = open_i2c_dev(I2C_BUS, buf, sizeof(buf), 0);
2.设置I2C设备地址(从设备) err = set_slave_addr(fd, AP3216C_ADDR, 1);
构造i2c消息,使用ioctl发起i2c消息,i2c消息里面有具体的读/写信息。
3.写入设备存储地址
msgs[0].addr = AP3216C_ADDR;
msgs[0].flags = 0; /* 写:0, 读:I2C_M_RD */
msgs[0].len = 1; //写入一个长度数据
msgs[0].buf = buf_tx;
buf_tx[0] = 0xc;
4.读数据
msgs[1].addr = AP3216C_ADDR;
msgs[1].flags = I2C_M_RD; /* 写:0, 读:I2C_M_RD */
msgs[1].len = 2;
msgs[1].buf = buf_rx;
Makefire文件:
程序文件:
实验结果:
上面四个库文件和头文件是怎么来的?
对i2c-tools-4.2.tar.xz进行解压,然后修改Makefire(使用vi编辑器基本操作),再make交叉编译i2c-tool,得到头文件、库文件,然后可以将头文件和库文件(include和lib文件夹)放入程序文件中,最后在上传到PC端程序文件中。步骤如下:
3. 基于smbus编写AP3216C程序
3.1 使用源码smbus.c
根据SMBus读写源码流程,在基于i2c编写AP3216C程序上进行修改并添加smbus.c文件:
在源码smbuc.c文件里有具体的读写函数,只需要把构造i2c消息方法改成具体的读/写函数。
Makefire文件:
编译结果:
如果出现警告:
运行结果:
3.2 使用例li2c库
1. 源码(
smbus.c
)与库(libi2c.a
/libi2c.so
)的区别?
特性 源码( smbus.c
)库( libi2c.a
/libi2c.so
)内容形式 人类可读的 C 语言源文件( .c
/.h
)。编译后的二进制文件(机器码),不可直接阅读。 编译参与方式 直接参与编译(需 #include
头文件)。不参与编译,仅需在链接阶段与目标文件合>并。 可修改性 可自由修改代码逻辑或调试。 无法修改,只能通过头文件接口调用。 依赖管理 需手动维护源码更新和兼容性。 依赖库的版本,需确保库与系统环境兼容。 2. 动态库(
.so
)与静态库(.a
)的区别?
特性 静态库( .a
)动态库( .so
)链接方式 编译时链接: 库代码被复制到最终可执行文件中。
运行时链接: 可执行文件仅记录库的引用路径,运行时加载。
文件大小 可执行文件较大(包含所有依赖的库代码)。 可执行文件较小(仅含引用,库文件独立存在)。 内存占用 每个进程独立加载库代码,内存占用高。 库代码在内存中共享,多个进程共用,内存占用低。 部署复杂度 简单(单文件发布,无需额外库文件)。 需确保目标系统安装正确版本的动态库。 更新维护 需重新编译整个程序以更新库功能。 替换动态库文件即可更新功能(需接口兼容)。 兼容性风险 无版本冲突(库代码已固化在程序中)。 需处理动态库版本兼容性问题(如 libi2c.so.1
vslibi2c.so.2
)。
- 源码 vs 库:源码灵活但需维护;库封装性好但依赖管理复杂。
- 静态库 vs 动态库:
- 静态库:独立性强、文件大、更新需重编译。
- 动态库:共享性强、文件小、更新方便但需版本管理。
不使用smbus.c,因为在工作中一般看不到源码(比如smbus.c),但是又必须要使用源码里的函数,那就使用库,在Makefire文件中修改代码,如下:
3.2.1 使用动态库
Makefire文件:
编译结果:
运行结果:
直接运行会出错误,因为动态库需要在运行时指定库的路径
或者把三个库文件全部拷贝
到
3.2.2 使用静态库
编译三个.c文件,然后和静态库一起链接