I2C
一、简介
I2C 总线(Inter-Integrated Circuit)是由 Philips 公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
Air724UG 模块提供 2 个 I2C 接口,速率支持 FAST(400KHz)、SLOW(100KHz)、3500KHz。外设地址支持 0x00-0x7f。
1.1 特性
- 支持 Fast mode (400Kbps)和 Slow mode(100Kbps)
- 只支持 I2C master 模式
- 可通过软件来配置内部的上拉电阻,1.8K 或者 20K
- 模块最多支持 2 路独立的 I2C 接口
- 理论上最多可以支持 127 个从设备
1.2 模块 I2C 功能列表
接口 | 名称 | 复用 | Air724UG引脚序号 | 作用 |
---|---|---|---|---|
I2C1 | CAMI2C_SCL1 | I2C1_SCL | 51 | I2C1 时钟信号 |
I2C1 | CAMI2C_SDA1 | I2C1_SDA | 50 | I2C1 数据信号 |
I2C2 | I2C2_SCL | I2C2_SCL | 32 | I2C2 时钟信号 |
I2C2 | I2C2_SDA | I2C2_SDA | 31 | I2C2 数据信号 |
注意: 请优先使用I2C2,I2C1预留给摄像头使用。
二、演示功能概述
本教程教你如何用 Air724 开发板,演示 I2C 功能,AHT10 数字型温湿度传感器读取温湿度并通过日志观察实验结果。
三、准备硬件环境
3.1 开发板准备
使用 EVB_Air724 开发板,如下图所示:
淘宝购买链接:Air724UG-NFM 开发板淘宝购买链接 ;
此开发板的详细使用说明参考:Air724UG 产品手册 中的《EVB_Air724UG_AXX 开发板使用说明》,写这篇文章时最新版本的使用说明为:《EVB_Air724UG_A14 开发板使用说明》;开发板使用过程中遇到任何问题,可以直接参考这份使用说明文档。
api:https://doc.openluat.com/wiki/21?wiki_page_id=2068
3.2 AHT10 模块
AHT10 数字型温湿度传感器模块购买链接:点击此处
3.3 数据通信线
USB 数据线一根(micro USB)。
3.4 PC 电脑
WIN7 以及以上版本的 WINDOWS 系统。
3.5 SIM 卡
中国大陆环境下,可以上网的 SIM 卡。一般来说,使用移动,电信,联通的物联网卡或者手机卡都行。
3.6 组装硬件环境
USB 数据线插入 USB 口,另一端与电脑相连,拨码开关全部拨到 ON,串口切换开关选择 UART1,USB 供电的 4V 对应开关拨至 ON 档,SIM 卡放到 SIM 卡槽中锁紧,如下图所示。
3.7 主板和传感器连接
确保 SDA、SCL、GND、VIN 分别连接主板和传感器,如下图所示。
四、准备软件环境
4.1 下载调试工具
使用说明参考:Luatools 下载和详细使用
4.2 源码及固件
1.底层 core 下载
下载底层固件,并解压
链接:https://docs.openluat.com/air724ug/luatos/firmware/
如下图所示,红框的是我们要使用到的。
2.本教程使用的 demo:
4.3 下载固件和脚本到开发板中
打开 Luatools,开发板上电开机,如开机成功 Luatools 会打印如下信息。
点击项目管理测试选项。
进入管理界面,如下图所示。
- 点击选择文件,选择底层固件,我的文件放在 D:\luatOS\Air724 路径中
- 点击增加脚本或资源文件,选择 之前下载的程序源码,如下图所示。
- 点击下载底层和脚本,下载完成如下图所示。
五、代码示例介绍
5.1 API 说明
5.1.1 i2c.setup( id, speed [,slaveaddr] [,isbaud] [,reg16bit])
打开 i2c 接口
参数
参数 | 类型 | 释义 | 取值 |
---|---|---|---|
id | number | i2c 接口 id | core 0025 版本之前,0、1、2 都表示 i2c2core 0025 以及之后的版本,1、2、3 分别表示 i2c 1、2、3 |
speed | number | 时钟频率 | 0~3500000 |
[,slaveaddr] | number | 可选,i2c 外设地址 | 0x00-0x7f |
[,isbaud] | number | 可选自定义波特率开关 0/1-关闭/开启 | 0/1 |
[,reg16bit] | number | 双字节寄存器配置开关 0/1-关闭/开启 | 0/1 |
返回值
返回值 | 类型 | 释义 | 取值 |
---|---|---|---|
result | number | 可以根据返回的频率值判断是否成功打开 i2c | 0~3500000 |
例子
_--使用i2c.send和i2c.recv的setup_
if i2c.setup(i2cid,100000) ~= 100000 then
print("init fail")
return
end
_--自定义i2c波特率(不想指定地址可以将第三个参数置为-1),波特率自定义开关 第四个参数:1 打开 0 关闭_
if i2c.setup(i2cid,2400,-1,1) ~= 2400 then
print("init fail")
return
end
_--使用i2c.write和i2c.read的setup_
if i2c.setup(i2cid,100000,i2cslaveaddr) ~= 100000 then
print("init1 fail")
return
end
5.1.2 i2c.write( id, reg, data )
往指定的寄存器地址 reg 传输数据
参数
参数 | 类型 | 释义 | 取值 |
---|---|---|---|
id | number | i2c 接口 id | core 0025 版本之前,0、1、2 都表示 i2c2core 0025 以及之后的版本,1、2、3 分别表示 i2c 1、2、3 |
reg | number | 写入 i2c 从设备的寄存器起始地址 | |
data | number | number / string / table,自动根据参数类型写数据,num 只写 1 个字节,string/table |
返回值
返回值 | 类型 | 释义 | 取值 |
---|---|---|---|
result | number | 传输成功的字节数 |
例子
local cmd,i = {0x1B,0x00,0x6A,0x01,0x1E,0x20,0x21,0x04,0x1B,0x00,0x1B,0xDA,0x1B,0xDA}
for i=1,#cmd,2 do_--向从设备的寄存器地址cmd[i]中写1字节的数据cmd[i+1]_
i2c.write(i2cid,cmd[i],cmd[i+1])
end
5.1.3 i2c.read( id, reg, num )
读取指定寄存器地址 reg 的数据内容
参数
参数 | 类型 | 释义 | 取值 |
---|---|---|---|
id | number | i2c 接口 id | core 0025 版本之前,0、1、2 都表示 i2c2core 0025 以及之后的版本,1、2、3 分别表示 i2c 1、2、3 |
reg | number | 读取 i2c 从设备的寄存器起始地址 | |
num | number | 读取数据字节数 |
返回值
返回值 | 类型 | 释义 | 取值 |
---|---|---|---|
返回读取的数据 | string | 二进制数据会包含非可见字符,请使用 string.byte 打印数据流 |
例子
_--从从设备的寄存器地址cmd[i]中读1字节的数据,并且打印出来_
local cmd,i = {0x1B,0x00,0x6A,0x01,0x1E,0x20,0x21,0x04,0x1B,0x00,0x1B,0xDA,0x1B,0xDA}
for i=1,#cmd,2 do
_--向从设备的寄存器地址cmd[i]中写1字节的数据cmd[i+1]_
i2c.write(i2cid,cmd[i],cmd[i+1])
_--从从设备的寄存器地址cmd[i]中读1字节的数据,并且打印出来_
print("testI2c.init1",string.format("%02X",cmd[i]),string.toHex(i2c.read(i2cid,cmd[i],1)))
end
5.1.4 i2c.send( id,slave, data )
向从设备写数据
参数
参数 | 类型 | 释义 | 取值 |
---|---|---|---|
id | number | i2c 接口 id | core 0025 版本之前,0、1、2 都表示 i2c2core 0025 以及之后的版本,1、2、3 分别表示 i2c 1、2、3 |
slave | number | i2c 外设地址 | 0x00-0x7f |
data | number/string/table | 自动根据参数类型写数据,num 只写 1 个字节,string/table 自动 |
返回值
返回值 | 类型 | 释义 | 取值 |
---|---|---|---|
result | string | 返回读取的数据,二进制数据会包含非可见字符,请使用 string.byte 打印数据流 |
例子
_--从从设备的寄存器地址cmd[i]中读1字节的数据,并且打印出来_
local cmd,i = {0x1B,0x00,0x6A,0x01,0x1E,0x20,0x21,0x04,0x1B,0x00,0x1B,0xDA,0x1B,0xDA}
for i=1,#cmd,2 do
_--向从设备的寄存器地址cmd[i]中写1字节的数据cmd[i+1]_
i2c.write(i2cid,cmd[i],cmd[i+1])
_--从从设备的寄存器地址cmd[i]中读1字节的数据,并且打印出来_
print("testI2c.init1",string.format("%02X",cmd[i]),string.toHex(i2c.read(i2cid,cmd[i],1)))
end
5.1.5 i2c.recv( id, slave,size )
向从设备读取数据
参数
参数 | 类型 | 释义 | 取值 |
---|---|---|---|
id | number | i2c 接口 id,core 0025 版本之前,0、1、2 都表示 i2c2core 0025 以及之后的版本,1、2、3 分别表示 i2c 1、2、3 | |
slave | number | i2c 外设地址 | 0x00-0x7f |
size | number | 读取数据字节数 |
返回值
返回值 | 类型 | 释义 | 取值 |
---|---|---|---|
result | string | 返回读取的数据,二进制数据会包含非可见字符,请使用 string.byte 打印数据流 |
例子
_--向从设备i2cslaveaddr发送寄存器地址cmd[i]_
i2c.send(i2cid,i2cslaveaddr,cmd[i])
_--读取从设备i2cslaveaddr寄存器内的1个字节的数据,并且打印出来_
print("testI2c.init",string.format("%02X",cmd[i]),string.toHex(i2c.recv(i2cid,i2cslaveaddr,1)))
5.1.6 i2c.close( id )
关闭 I2C 接口
参数
参数 | 类型 | 释义 | 取值 |
---|---|---|---|
id | number | i2c 接口 id | core 0025 版本之前,0、1、2 都表示 i2c2core 0025 以及之后的版本,1、2、3 分别表示 i2c 1、2、3 |
返回值
无
5.2 AHT10.lua 代码
打开 AHT10 传感器,读取温湿度寄存器,解释温湿度数据,打印日志。
--- 模块功能:AHT10功能测试.
-- @module i2c
-- @author openLuat
-- @license MIT
-- @copyright OpenLuat.com
-- @release 2021.8.10
local function i2c_open(id)
if i2c.setup(id, i2c.SLOW) ~= i2c.SLOW then
log.error("I2C.init is: ", "fail")
end
end
function readAHT10()
local id = 2
i2c_open(id)
--数值查询,发送指令0xAC, 0x22, 0x00,通过I2C发送完毕之后,AHT10返回的数值是6个字节的数组
i2c.send(id, 0x38, {0xAC, 0x22, 0x00})
--等待75毫秒以上
--rtos.sleep(80)
--1[状态位],2[湿度第一段],3[湿度第二段],4前四位[湿度第三段],4后四位[温度第一段],5[温度第二段],6[温度第三段]
local data = i2c.recv(id, 0x38, 6)
log.info("i2cdata", #data, data:toHex())
i2c.close(id)
if #data == 6 then
local _, _, data2, data3, data4, data5, data6 = pack.unpack(data, "b6")
local hum = bit.bor(bit.bor(bit.lshift(data2, 12), bit.lshift(data3, 4)), bit.rshift(data4, 4))/ 1048576 * 10000
log.info("hum", hum/100 )
local tmp = bit.bor(bit.bor(bit.lshift(bit.band(data4, 0x0f), 16), bit.lshift(data5, 8)), data6) / 1048576 * 20000 - 5000
log.info("tmp", tmp/100)
--前面将数据放大了100倍,方便没有float的固件保留精度,在使用float固件时直接缩小100倍还原
--return tmp, hum
return tmp/100, hum/100
else
return 0, 0
end
end
sys.timerLoopStart(readAHT10,2000)
5.3 main.lua 代码
本代码为主程序脚本,系统启动后首先会对 4G 网络进行配置,等待网络连接成功,然后加载测试模块。
六、开机调试
6.1 开发板开机
连接好硬件并下载固件后,启动 Luatools 软件,系统运行信息将显示在界面中。红框中为开发板连接到 PC 机后正常打印的信息,如下图所示。
6.2 功能调试
系统循环打印温湿度数据。
七、常见问题
7.1 i2c 每次读写数据前都需要 setup 一次吗?
不需要
正常的业务逻辑为:
i2c.setup
…此处可以多次读写数据
i2c.close
setup 之后,只要没有 close,就可以反复读写数据
一旦执行了 close,下次读写数据前,必须再次 setup
7.2 i2c 通信时模块可以做从设备吗?
注意:模块 I2C 只能做主设备,外部需要上拉,在配置 FAST 速率时,上拉电阻阻值不要大于 4.7K。
7.3 i2c 通信时主从机电平不一样,要做电平转换吗?
如果主从设备的参考电平不一致,请注意做电平转换。如下图:
7.4 i2c 设备 7 位的地址为 0x38 ,需要我手动转换到 8 位地址吗?
这里 i2c_addr 地址是 7bit 地址
如果 i2c 外设手册中给的是 8bit 地址,需要把 8bit 地址右移 1 位,赋值给 i2c_addr 变量
如果 i2c 外设手册中给的是 7bit 地址,直接把 7bit 地址赋值给 i2c_addr 变量即可
给读者的话
本篇文章由
杨超
开发;本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题;
请登录合宙技术交流论坛,点击文档找错赢奖金-Air724UG-LuatOS-软件指南-外设驱动实现-I2C;
用截图标注+文字描述的方式跟帖回复,记录清楚您发现的问题;
我们会迅速核实并且修改文档;
同时也会为您累计找错积分,您还可能赢取月度找错奖金!