基于 rt-thread的I2C操作EEPROM(AT24C02)

2024-09-07 11:36
文章标签 操作 i2c thread rt eeprom at24c02

本文主要是介绍基于 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-iAt24c02_Write(i, 8 - i);//读取该地址的数据,并显示到数码管LED_TUBE = coding[At24c02_Read(i)];//粗略延时500msDelay_10us(50000);	 	}for(i = 0; i < 8; i++){//向AT24C02前8个字节空间写i+1At24c02_Write(i, i + 1);//读取该地址的数据,并显示到数码管LED_TUBE = coding[At24c02_Read(i)];//粗略延时500msDelay_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 zhistatic  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};//HelloRTTrt_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 zhirt_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};//HelloRTTuint8_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

这篇关于基于 rt-thread的I2C操作EEPROM(AT24C02)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1144943

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

Python利用自带模块实现屏幕像素高效操作

《Python利用自带模块实现屏幕像素高效操作》这篇文章主要为大家详细介绍了Python如何利用自带模块实现屏幕像素高效操作,文中的示例代码讲解详,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、获取屏幕放缩比例2、获取屏幕指定坐标处像素颜色3、一个简单的使用案例4、总结1、获取屏幕放缩比例from

通过prometheus监控Tomcat运行状态的操作流程

《通过prometheus监控Tomcat运行状态的操作流程》文章介绍了如何安装和配置Tomcat,并使用Prometheus和TomcatExporter来监控Tomcat的运行状态,文章详细讲解了... 目录Tomcat安装配置以及prometheus监控Tomcat一. 安装并配置tomcat1、安装

Python中操作Redis的常用方法小结

《Python中操作Redis的常用方法小结》这篇文章主要为大家详细介绍了Python中操作Redis的常用方法,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解一下... 目录安装Redis开启、关闭Redisredis数据结构redis-cli操作安装redis-py数据库连接和释放增

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言