基于 rt-thread的I2C操作EEPROM(AT24C02)
一、AT24C02
The AT24C01A/02/04/08A/16A provides 1024/2048/4096/8192/16384 bits of serial electrically erasable and programmable read-only memory (EEPROM) organized as 128/256/512/1024/2048 words of 8 bits each.
AT24C01A/02/04/08A/16A提供1024/2048/4096/8192/16384位串行电可擦除和可编程只读存储器(EEPROM),以128/256/512/1024/2048 个字节的8位数据组成。
设备操作
1、数据有效性
2、开始停止
3、响应
4、软件复位
5、bus Timing
6、Write Cycle Timing
7、设备地址
8、
9、
10、
11、
12、
二、I2C 简介
来源 RT-Thread 标准文档 中 I2C 总线设备
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c?id=i2c-%e7%ae%80%e4%bb%8b
I2C(Inter Integrated Circuit)总线是 PHILIPS 公司开发的一种半双工、双向二线制同步串行总线。I2C 总线传输数据时只需两根信号线,一根是双向数据线 SDA(serial data),另一根是双向时钟线 SCL(serial clock)。SPI 总线有两根线分别用于主从设备之间接收数据和发送数据,而 I2C 总线只使用一根线进行数据收发。
I2C 和 SPI 一样以主从的方式工作,不同于 SPI 一主多从的结构,它允许同时有多个主设备存在,每个连接到总线上的器件都有唯一的地址,主设备启动数据传输并产生时钟信号,从设备被主设备寻址,同一时刻只允许有一个主设备。
I2C 总线主要的数据传输格式:
当总线空闲时,SDA 和 SCL 都处于高电平状态,当主机要和某个从机通讯时,会先发送一个开始条件,然后发送从机地址和读写控制位,接下来传输数据(主机发送或者接收数据),数据传输结束时主机会发送停止条件。传输的每个字节为8位,高位在前,低位在后。数据传输过程中的不同名词详解如下所示:
1、开始条件: SCL 为高电平时,主机将 SDA 拉低,表示数据传输即将开始。
2、从机地址: 主机发送的第一个字节为从机地址,高 7 位为地址,最低位为 R/W 读写控制位,1 表示读操作,0 表示写操作。一般从机地址有 7 位地址模式和 10 位地址模式两种,如果是 10 位地址模式,第一个字节的头 7 位 是 11110XX 的组合,其中最后两位(XX)是 10 位地址的两个最高位,第二个字节为 10 位从机地址的剩下8位,如下图所示:
3、应答信号: 每传输完成一个字节的数据,接收方就需要回复一个 ACK(acknowledge)。写数据时由从机发送 ACK,读数据时由主机发送 ACK。当主机读到最后一个字节数据时,可发送 NACK(Not acknowledge)然后跟停止条件。
4、数据: 从机地址发送完后可能会发送一些指令,依从机而定,然后开始传输数据,由主机或者从机发送,每个数据为 8 位,数据的字节数没有限制。
5、重复开始条件: 在一次通信过程中,主机可能需要和不同的从机传输数据或者需要切换读写操作时,主机可以再发送一个开始条件。
6、停止条件: 在 SDA 为低电平时,主机将 SCL 拉高并保持高电平,然后在将 SDA 拉高,表示传输结束。
三、代码
51代码来源:
https://blog.csdn.net/weixin_43772810/article/details/122149151
//24C02的 A0、A1、A2 都接地,所以它的I2C设备地址为1010000,写地址为10100000(0xA0),读地址为10100001(0xA1)
#include <reg52.h> //此文件中定义了单片机的一些特殊功能寄存器
sbit scl = P2^1;
sbit sda = P2^0;
unsigned char code coding[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//显示0-F的值
#define LED_TUBE P3 //P3的8个IO端口对应数码管的8个信号引脚
/******************************************************************************
* @ 函数名 : Delay_10us
* @ 功 能 : 10us粗略延时
* @ 参 数 : 延时时间--单位10us
* @ 返回值 : 无
******************************************************************************/
void Delay_10us(unsigned int time)
{
while(time--);
}
/******************************************************************************
* @ 函数名 : I2c_Delay
* @ 功 能 : I2C延时函数
* @ 参 数 : 无
* @ 返回值 : 无
******************************************************************************/
void I2c_Delay()
{
Delay_10us(1);
}
/******************************************************************************
* @ 函数名 : I2c_Start
* @ 功 能 : I2C起始信号
* @ 参 数 : 无
* @ 返回值 : 无
******************************************************************************/
void I2c_Start()
{
sda = 1;
scl = 1;
I2c_Delay(); //起始信号建立时间
sda = 0; //SDA拉低,下降沿
I2c_Delay(); //起始信号保持时间
scl = 0;
}
/******************************************************************************
* @ 函数名 : I2c_Stop
* @ 功 能 : I2C停止信号
* @ 参 数 : 无
* @ 返回值 : 无
******************************************************************************/
void I2c_Stop()
{
scl = 0;
I2c_Delay(); //上一个时钟周期的低电平
sda = 0;
scl = 1;
I2c_Delay(); //停止信号建立时间
sda = 1; //SDA拉高,上升沿
I2c_Delay(); //总线空闲时间保持
}
/******************************************************************************
* @ 函数名 : I2c_Ack
* @ 功 能 : I2C应答信号
* @ 参 数 : 无
* @ 返回值 : 无
******************************************************************************/
void I2c_Ack()
{
scl = 0;
sda = 0; //SDA拉低,发出应答信号
I2c_Delay();
scl = 1;
I2c_Delay();
scl = 0;
}
/******************************************************************************
* @ 函数名 : I2c_No_Ack
* @ 功 能 : I2C非应答信号
* @ 参 数 : 无
* @ 返回值 : 无
******************************************************************************/
void I2c_No_Ack()
{
scl = 0;
sda = 1; //SDA拉高,发出非应答信号
I2c_Delay();
scl = 1;
I2c_Delay();
scl = 0;
}
/******************************************************************************
* @ 函数名 : I2c_Wait_Ack
* @ 功 能 : I2C等待应答信号
* @ 参 数 : 无
* @ 返回值 : 1:接收到应答信号,0:接收到非应答信号
******************************************************************************/
unsigned char I2c_Wait_Ack()
{
unsigned char ack = 0;
sda = 1;
scl = 0;
I2c_Delay();
scl = 1;
I2c_Delay();
if(sda == 0) //检测数据线SDA是否被拉低
ack = 1;
else
ack = 0;
scl = 0;
return ack;
}
/******************************************************************************
* @ 函数名 : I2c_Write_Byte
* @ 功 能 : I2C写字节
* @ 参 数 : dat 要写入的字节数据
* @ 返回值 : 无
******************************************************************************/
void I2c_Write_Byte(unsigned char dat)
{
unsigned char i = 0;
for(i = 0; i < 8; i++) //读取8位
{
scl = 0;
I2c_Delay();
if(dat & 0x80) //发送最高位
sda = 1;
else
sda = 0;
scl = 1;
I2c_Delay();
dat <<= 1; //左移1位
}
scl = 0;
}
/******************************************************************************
* @ 函数名 : I2c_Read_Byte
* @ 功 能 : I2C读字节
* @ 参 数 : 无
* @ 返回值 : 读取的字节数据
******************************************************************************/
unsigned char I2c_Read_Byte()
{
unsigned char dat = 0, i = 0;
for(i = 0; i < 8; i++) //读取8位
{
dat <<= 1; //左移1位
scl = 0;
I2c_Delay();
scl = 1; //SCL高电平
I2c_Delay();
if(sda) //读取SDA状态
dat |= 0x1;
}
scl = 0;
return dat;
}
/******************************************************************************
* @ 函数名 : At24c02_Write
* @ 功 能 : AT24C02写字节
* @ 参 数 :
* addr 要写数据的地址(存储空间)
* dat 要写入的字节数据
* @ 返回值 : 无
******************************************************************************/
void At24c02_Write(unsigned char addr, unsigned char dat)
{
I2c_Start();
I2c_Write_Byte(0xA0); //发送I2C设备地址
I2c_Wait_Ack(); //等待从机响应
I2c_Write_Byte(addr); //发送要写入的内存地址
I2c_Wait_Ack();
I2c_Write_Byte(dat); //写入数据
I2c_Wait_Ack();
I2c_Stop();
Delay_10us(1000); //写周期
}
/******************************************************************************
* @ 函数名 : At24c02_Read
* @ 功 能 : AT24C02读字节
* @ 参 数 : addr 要读数据的地址(存储空间)
* @ 返回值 : 读取的字节数据
******************************************************************************/
unsigned char At24c02_Read(unsigned char addr)
{
unsigned char dat = 0, i = 0;
I2c_Start();
I2c_Write_Byte(0xA0); //发送I2C设备地址
I2c_Wait_Ack(); //等待从机响应
I2c_Write_Byte(addr); //发送要写入的内存地址
I2c_Wait_Ack();
I2c_Start();
I2c_Write_Byte(0xA1); //发送I2C设备地址(读数据)
I2c_Wait_Ack(); //等待从机响应
dat = I2c_Read_Byte(); //读取数据
I2c_Wait_Ack();
I2c_Stop();
return dat;
}
/******************************************************************************
* @ 函数名 : main
* @ 功 能 : 主函数
* @ 参 数 : 无
* @ 返回值 : 无
******************************************************************************/
int main()
{
unsigned char i = 0;
Delay_10us(100); //AT24C02上电时序
while(1)
{
for(i = 0; i < 8; i++)
{
//向AT24C02前8个字节空间写8-i
At24c02_Write(i, 8 - i);
//读取该地址的数据,并显示到数码管
LED_TUBE = coding[At24c02_Read(i)];
//粗略延时500ms
Delay_10us(50000);
}
for(i = 0; i < 8; i++)
{
//向AT24C02前8个字节空间写i+1
At24c02_Write(i, i + 1);
//读取该地址的数据,并显示到数码管
LED_TUBE = coding[At24c02_Read(i)];
//粗略延时500ms
Delay_10us(50000);
}
}
}
四、RT-Thread 操作配置
电路如下:
使用rt-thread studio IDE。
1、按照要求配置软件模拟I2C
2、添加软件包
3、直接编译,通过指令测试
代码测试1
/*
* 程序清单:这是一个iic设备使用例程
* 例程导出了i2c_test3命令到控制终端
* 命令调用格式:i2c_test3
* 实现功能:向eeprom写入16进制的HelloRTT,再重读eeprom并以字符输出。
*/
#include <rtthread.h>
#include <rtdevice.h>
static const char * i2c_bus_device_name = "i2c1";
static const rt_uint8_t eeprom_addr = 0x50; // 1010A2A1A0 - R/W she bei di zhi
static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *data);
static rt_err_t read_regs(struct rt_i2c_bus_device *bus, rt_uint8_t len,rt_uint8_t *buf);
rt_err_t i2c_test3(void)
{
struct rt_i2c_bus_device * i2c_device;
i2c_device = rt_i2c_bus_device_find(i2c_bus_device_name);
if(i2c_device == RT_NULL)
{
rt_kprintf("i2c bus device %s not found!\n", i2c_bus_device_name);
return -RT_ENOSYS;
}
rt_size_t i;
rt_uint8_t send[8] = {0x48,0x65,0x6C,0x6C,0x6F,0x52,0x54,0x54};//HelloRTT
rt_uint8_t recv[8] = {0};
//step 1: write to eeprom.
write_reg(i2c_device,8,send);
//step 2: read out from eeprom.
read_regs(i2c_device,8,recv);
rt_kprintf("read eeprom at: 0x%02X\n", eeprom_addr);
rt_kprintf("your data is: \n");
for(i=0; i<sizeof(recv); i++)
{
rt_kprintf("%c", recv[i]);
}
rt_kprintf("\n");
return RT_EOK;
}
MSH_CMD_EXPORT(i2c_test3, i2c_test3);
/* 写eeprom */
static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint8_t len, rt_uint8_t *data)
{
struct rt_i2c_msg msgs[2];
rt_uint8_t buff[1] = {0x00}; //数据在EEPROM上的存储地址
msgs[0].addr = eeprom_addr;
msgs[0].flags = RT_I2C_WR;
msgs[0].buf = buff;
msgs[0].len = 1;
msgs[1].addr = eeprom_addr;
msgs[1].flags = RT_I2C_WR | RT_I2C_NO_START;
msgs[1].buf = data;
msgs[1].len = len;
if (rt_i2c_transfer(bus, msgs, 2) == 2)
{
rt_thread_delay(rt_tick_from_millisecond(10));
return RT_EOK;
}else{
rt_kprintf("write eeprom fail!\n");
return -RT_ERROR;
}
}
/* 读eeprom数据 */
static rt_err_t read_regs(struct rt_i2c_bus_device *bus,rt_uint8_t len,rt_uint8_t *buf)
{
struct rt_i2c_msg msgs[2];
rt_uint8_t buff[1] = {0x00}; //数据在EEPROM上的存储地址
msgs[0].addr = eeprom_addr;
msgs[0].flags = RT_I2C_WR;
msgs[0].buf = buff;
msgs[0].len = 1;
msgs[1].addr = eeprom_addr;
msgs[1].flags = RT_I2C_RD;
msgs[1].buf = buf;
msgs[1].len = len;
if (rt_i2c_transfer(bus, msgs, 2) == 2)// 调用I2C设备接口传输数据
{
return RT_EOK;
}else{
rt_kprintf("read eeprom fail!\n");
return -RT_ERROR;
}
}
//FINSH_FUNCTION_EXPORT(i2c_test3, i2c_test3);
代码测试2
#include <rtthread.h>
#include <rtdevice.h>
#include "at24cxx.h"
static const char * i2c_bus_device_name = "i2c1";
static const rt_uint8_t eeprom_addr = 0x50; // 1010A2A1A0 - R/W she bei di zhi
rt_err_t i2c_test3(void)
{
at24cxx_device_t dev = RT_NULL;
dev = at24cxx_init(i2c_bus_device_name, 0);
if(dev == RT_NULL)
{
rt_kprintf("i2c bus device %s not found!\n", i2c_bus_device_name);
return -RT_ENOSYS;
}
rt_size_t i;
uint8_t send[8] = {0x48,0x65,0x6C,0x6C,0x6F,0x52,0x54,0x54};//HelloRTT
uint8_t recv[16] = {0};
//step 1: write to eeprom.
at24cxx_write(dev, 8, send, 8);
rt_kprintf("write ok\n");
//step 2: read out from eeprom.
at24cxx_read(dev, 0, recv, 16);
rt_kprintf("read eeprom at: 0x%02X\n", eeprom_addr);
rt_kprintf("your data is: \n");
for(i=0; i<sizeof(recv); i++)
{
rt_kprintf("%c", recv[i]);
}
rt_kprintf("\n");
return RT_EOK;
}
MSH_CMD_EXPORT(i2c_test3, i2c_test3);
参考:
https://blog.csdn.net/weixin_43772810/article/details/122149151
https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/i2c/i2c?id=i2c-%e7%ae%80%e4%bb%8b