前言
本章是基于RT-Thread studio实现软件模拟I2C,开发板是正点原子的STM32F4探索者,使用的RT-Thread驱动是5.1.0,使用的I2C通讯芯片是存储芯片AT24C02,本章和前面文章不同在于I2C是软件模拟,与设备无关,所以驱动文件不会有问题,但是对于代码开发官方文档有些重要内容没有提到,本文将进行补充。
一、RT-Thread工程创建
这里只需要按前文得到一个不报错的基础工程,因为是软件模拟I2C,所以不需要去cubemx进行硬件配置。
然后去board.h找到I2C的定义区间,使能一个I2C型号,将SCL和SDA按你的使用的开发板引脚定义。我这里使能了I2C1,SCL接入的引脚是PB8,SDA接入的引脚是PB9,如下。
然后去设置中打开软件模拟I2C开关,Use GPIO to soft simulate I2C感觉重复了,开不开对工程没有影响,对于debug message开启的话可以看到I2C通讯的工作流程。
到此,I2C工程配置完成
二、AT24C02
AT24C02是接下来用到的I2C通讯芯片,我简单说明一下。
AT24C02 是一款由 低功耗串行电可擦除只读存储器(EEPROM),通过 I²C 总线进行通信。支持标准模式(100kHz)和快速模式(400kHz)。
2K 位(256 字节)存储空间,分为 32 页,每页 8 字节。地址范围:0x00~0xFF(地址低3位为页内地址,高5位为页面地址)。一个页面
只能写入8字节,超过的数据将写入下一个页面。
三、函数编写
1.I2C_soft.c
先看代码,再看讲解
#include "I2C_soft.h"
#define DBG_TAG "I2C_soft"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#define I2C1_NAME "i2c1"
#define SLAVE_ADDR 0xa0>>1
//data[0]为数据地址,后面开始是写入数据
int write_i2c1reg(rt_uint8_t *data,rt_uint8_t data_byte)
{
rt_uint8_t i2c1_flag = 0;
struct rt_i2c_bus_device *i2c1_bus;
i2c1_bus = (struct rt_i2c_bus_device *)rt_device_find(I2C1_NAME);
if(i2c1_bus == RT_NULL){
LOG_D("failed to i2c1 bus find");
return -1;
}
i2c1_flag = (rt_uint8_t)rt_i2c_master_send(i2c1_bus, SLAVE_ADDR, RT_NULL, data, data_byte);
if(i2c1_flag != data_byte){
LOG_D("failed to i2c1 send");
return -1;
}
return 0;
}
//data_add为读数据地址
int read_i2c1reg(rt_uint8_t data_add,rt_uint8_t *data,rt_uint8_t data_byte)
{
rt_uint8_t i2c1_flag = 0;
struct rt_i2c_bus_device *i2c1_bus;
i2c1_bus = (struct rt_i2c_bus_device *)rt_device_find(I2C1_NAME);
if(i2c1_bus == RT_NULL){
LOG_D("failed to i2c1 bus find");
return -1;
}
rt_i2c_master_send(i2c1_bus, SLAVE_ADDR, RT_NULL, &data_add, 1);
i2c1_flag = (rt_uint8_t)rt_i2c_master_recv(i2c1_bus, SLAVE_ADDR, RT_NULL, data, data_byte);
if(i2c1_flag != data_byte){
LOG_D("failed to i2c1 recv");
return -1;
}
return 0;
}
按照图1,器件地址应该是0xa0(W)或0xa1®,最后一位为读写位,但是官方手册明确说明了不包含读写位。如下
所以,后面的函数默认传入的地址是不带最后一位的7位地址,然后根据flags标志形成8位地址作为器件地址,具体操作是将输入的地址左移1位,然后或上读写位成为最终地址,那么为了保证最终地址不出错,所以我们传入的地址就要先右移一位。
我这里使用rt_i2c_master_send和rt_i2c_master_recv写入和读出数据,代码上更简介,这两个函数就是把rt_i2c_transfer和传输的消息数组指针封装了一下。
另外对于rt_i2c_transfer函数第三个参数我觉得官网解释有误,num只能是1,否则就会出错。要符合官方文档的定义,你每次用rt_i2c_transfer只能写入一个字节,那就效率太低了。所以最好不要用这个函数,用上面两个函数,如果实在要用,那就记住num只能是1,和你数组元素个数无关。
当我们使用rt_i2c_master_send传输数据时,如果标志位flag不写入RT_I2C_NO_START /* 无开始条件 */
,那么自动一开始发送一个起始位和一个器件地址字节和应答,后面就是一个数据字节一个应答,达到发送的数据个数后一个停止位,刚好符合图2中AT24C02的写入格式,所以我的写入函数没有问题。就是注意数组第一位是数据字节地址,后面才是写入的数据内容。
提示
如果分开操作,把数据地址独立出来应该如下操作。
//data_add为数据地址,data为写入数据数组
rt_i2c_master_send(i2c1_bus, SLAVE_ADDR, RT_I2C_NO_STOP, &data_add, 1);
rt_i2c_master_send(i2c1_bus, SLAVE_ADDR, RT_I2C_NO_START, data, data_byte);
可见RT_I2C_NO_STOP会去除停止位,RT_I2C_NO_START会去除起始位和器件地址字节
对于接收函数,按照图3的数据格式,先采用rt_i2c_master_send(i2c1_bus, SLAVE_ADDR, RT_NULL, &data_add, 1);
发送一个读数据地址,然后使用rt_i2c_master_recv(i2c1_bus, SLAVE_ADDR, RT_NULL, data, data_byte);
将读到的数据存入数组data中,data_byte是读取数据字节数,rt_i2c_master_recv的flag同理。
弄清了以上这些,遇见了其他的I2C通讯芯片,只要按照它的读写格式,就能利用rt_i2c_master_send和rt_i2c_master_recv配置出需要的格式。
2.I2C_soft.h
#ifndef APP_I2C_SOFT_H_
#define APP_I2C_SOFT_H_
#include <board.h>
//data[0]为数据地址,后面开始是写入数据
int write_i2c1reg(rt_uint8_t *data,rt_uint8_t data_byte);
int read_i2c1reg(rt_uint8_t data_add,rt_uint8_t *data,rt_uint8_t data_byte);
#endif /* APP_I2C_SOFT_H_ */
3.main.h
#include <rtthread.h>
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>
#include "I2C_soft.h"
int main(void)
{
rt_uint8_t i2c1_wd[] = {0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10};
rt_uint8_t i2c1_rd[8] = {0};
write_i2c1reg(i2c1_wd, 10);
rt_thread_mdelay(100);
read_i2c1reg(0x00,i2c1_rd, 9);
rt_thread_mdelay(100);
for(int i = 0;i<9;i++)
{
rt_kprintf("i2c1 read data id 0x%x\n",i2c1_rd[i]);
}
read_i2c1reg(0x08,i2c1_rd, 9);
rt_thread_mdelay(100);
for(int i = 0;i<9;i++)
{
rt_kprintf("i2c1 read data id 0x%x\n",i2c1_rd[i]);
}
while (1)
{
rt_thread_mdelay(1000);
}
return RT_EOK;
}
四、效果展示
为什么是这样效果我就不解释了,如果你看懂了这篇文章你就知道为什么是这样了(AT24C02默认无数据写入空间值为0xff)
五、资源分享
通过网盘分享的文件:I2C_soft.zip
链接: https://pan.baidu.com/s/1k1P0mUaXDR7jJ6dwgJJ7QQ?pwd=iims 提取码: iims
总结
对于软件模拟的I2C好处是兼容性好,移植容易,对硬件设计要求不高,缺点就是传输速率很低,效率不高,占用系统资源过大。我看了一下,好像RT-Thread并没有设计硬件I2C驱动。另外RT-Thread可以安装AT24C02的软件包,可以尝试使用一下。