本文适合有使用Air724UG模块基础的读者。
【啰嗦2句】
近期在试用合宙Air724UG模块,这个模块几年前就在用了,一直用的是AT,也知道片内系统LuatOS其实挺好用,但是一直没有应用场景,我的看法是这个品牌做的文档还是挺全面,只是细节仍然还不够。
这2天需要用I2C连接一片AT24C02的EEPROM,才发现官方竟然没用示例,并且全网都搜不到?不得已,只能自己研究,才发现官方资料估计也是哪几位大神随便写写,走了不少弯路才成功读写。
【硬件简介】
熟悉AT24C02的读者可跳过。
AT24C02是一颗EEPROM芯片,内部包含256Byte的存储空间,通过I2C两线制进行通信读写。
用的是A14版开发板(卖太贵了!),板上已经扩展4Pin的接口方便直接飞线接24C02,看我的实物图:
注意:724模块的IO电平大多数是1.8V,而不是熟悉的3.3V哦!
这里有个惊喜:官方开发板已经把3.3V电平做了转换,可以说非常方便,我刚开始直接就用这4个脚就够了,但是我后来发现其实生产时可以省掉这个电平转换电路,因为我发现AT24C02的手册写着电压范围1.8-5.5V,所以我又特意飞线到1.8V的引脚上,果然读写自如。又可以在批量时省下一笔成本。如果我帮您省了,请记得给我点赞。不过仍需注意:手册也写了当1.8V时,读写的速度是变慢的,请务必根据实际业务需要选择省还是不省,莫怪我没提醒。
【想象中的用法】
官方例程一:下载官方例程:Luat社区
解压后在其中\demo\i2c文件下有相应例程,这个例程是介绍Air724UG连接SSD1306主控LCD的,这个主控没接触过,应该是低分辨率的吧估计,暂时不去研究。但是可以通过相应demo了解到如何使用I2C。
官方例程二:在Luat社区直接搜索I2C,也可以看到一个连接SHT30温湿度传感器的例程。
页面地址:Luat社区
【I2C函数讲解】
函数还真没几个,看上去有6个,实际应该是4个:
i2c.setup( id, speed [,slaveaddr] [,isbaud] [,reg16bit])
i2c.write( id, reg, data )
i2c.read( id, reg, num )
i2c.send( id,slave, data )
i2c.recv( id, slave,size )
i2c.close( id )
为什么说是4个呢?首先setup和close就没什么难理解的,无非就是打开和关闭,等同于单片机时序里面的Start和ACK和NAK等等自动完成,这里面最让人莫名其妙的是send\rev其实跟write\read是同效果的才对,只是传递的参数有些差别。那为什么不能压缩成4个,害得我每一个都去研究,也不知道读写成功与否,走了弯路。
直接上代码:
--- 外部Flash,记录开关状态
-- @author CZY Application Team
-- @copyright CZY
-- @release 2024-03-24
module(..., package.seeall)
-- i2cid 1,2,3对应硬件的I2C1,I2C2,I2C3
local i2c_id = 2
--器件地址,根据24C02手册写是A0,读是A1,但是由于Air724要求7bit地址,所以在setup时务必右移1位
--根据Air724示例讲(但是官方教程未见到),执行write或send时,默认是左移1位,并上读写标记0或1
--例如读: (i2c_addr<<1)|0x01,例如写(i2c_addr<<1)|0x00
--最终得到的命令字节其实就是24C02的读写命令字节
local i2c_addr = 0xA0
-- 读取EEPROM
--addr:存储的位置地址,0开始
function AT24CXX_ReadOneByte(addr)
if i2c.setup(i2c_id, i2c.SLOW, bit.rshift(i2c_addr, 1)) ~= i2c.SLOW then
i2c.close(i2c_id)
log.warn("24C02", "open i2c error.")
return
end
--接收数据
local RevData = i2c.read(i2c_id, addr, 1)
i2c.close(i2c_id)
print("接收的数据:"..addr,string.toHex(RevData))
end
-- 写入EEPROM
--addr:存储的位置地址,0开始
--dat:写入的字节
function AT24CXX_WriteOneByte(addr,dat)
if i2c.setup(i2c_id, i2c.SLOW, bit.rshift(i2c_addr,1)) ~= i2c.SLOW then
i2c.close(i2c_id)
log.warn("24C02", "open i2c error.")
return
end
--写入数据
i2c.write(i2c_id,addr,dat)
--等同于下面的命令
--i2c.send(i2c_id,bit.rshift(i2c_addr,1),{addr,dat})
i2c.close(i2c_id)
print("写入的数据:"..addr,dat)
end
sys.taskInit( --起一个协程
function()
sys.wait(5000) --将协程挂起五秒,此函数只能在协程内使用
AT24CXX_WriteOneByte(0,0x30)
sys.wait(5) --坑啊,这里写周期要5ms,否则可能有些写入尚未完成不生效,如果批量写入会快些
AT24CXX_WriteOneByte(1,0x31)
sys.wait(5) --坑啊,这里写周期要5ms,否则可能有些写入尚未完成不生效,如果批量写入会快些
AT24CXX_WriteOneByte(2,0x32)
sys.wait(5) --坑啊,这里写周期要5ms,否则可能有些写入尚未完成不生效,如果批量写入会快些
AT24CXX_WriteOneByte(3,0x33)
end
)
sys.timerLoopStart(function ()
AT24CXX_ReadOneByte(0)
AT24CXX_ReadOneByte(1)
AT24CXX_ReadOneByte(2)
AT24CXX_ReadOneByte(3)
end, 3000)
【避坑】
1、这里最最最大的坑其实就是地址,官方手册没有提到地址是7bit,要求右移,所以一开始我一直用AT24C02手册里面的A0、A1分别写和读,始终得不到结果,好在吃完几个饺子看了一部电影后重新审查示例才发现官方SSD1306的注释里面写了7bit和I2C驱动会默认读写时左移的内容,才从坑里面爬出来。具体注释我已经写在上面的代码了。总结就是24C02的0xA0要先右移1位成为0x50,然后I2C的驱动会在读写时,自动左移并补0和1(写和读),其他信号时序都是驱动完成。
2、要注意控制每一个周期的时间间隔,手册写的是最大5ms,如果是连续写入则一个循环内没用时间限制,不同循环则尽量延时。
【扩展探讨】
可能有人会说,724内部有nvm,可以直接写入内部文件系统,为什么要额外用EEPOM存储呢?我试过nvm,效果很好,操作很简单,存储配置没有问题。问题就在我的应用场景是较为频繁写入操作,由于724内部用的是NorFlash,寿命每页一般是10万次,如果按“尽可能递增写入”算法把几十K的空间重复循环利用可能可以到100万次,但是程序的复杂度又高出不少,可靠性根据经验----总有些不按我想象的那样运行的情况,万一因为读写冲突导致程序跑飞,得不偿失啊,debug未必能复现这种随机的问题。另外flash如果更新或重新烧录,有概率会破坏现有数据,所以选择了EEPROM,一般有100万次写入,也都够了,操作好就好在能单字节操作,不用按页操作,用一点点成本换取稳定性和便捷性,可以接受。
原创不易,转载请注明本博客。