当前位置: 首页 > article >正文

STM32单片机芯片与内部53 AT24C02读写原理 模拟IIC 标准库 HAL库

目录

一、AT24C02读写原理

1、设备地址

2、单字节写入

3、页写入

4、单字节读取

5、随机连续字节读取

6、顺序读取

二、模拟IIC-I2C的时序控制

1、GPIO配置

2、I2C开始

3、I2C结束

4、I2C响应

5、I2C不响应

6、I2C等待响应

7、I2C发送字节

8、I2C接收字节

三、模拟IIC-AT24C02相关控制

1、设备检测

2、写入数据

3、读取数据


一、AT24C02读写原理

1、设备地址

1010A2A1A0R/W

        可以看到其地址用了MSB7位标识,其中1010固定,不固定的由A2 A1 A0决定。因此一个总线最多挂载8个AT24C02。

2、单字节写入

        可以看到,如果每次写入数据都需要给地址那速度太慢了,因为一般不会只用8bits,而是连续较长的数据,因此AT24C02支持页写入。

3、页写入

        可以看到每次页写入支持连续写入多个字节数据,但是超出了仍需要再次写入新的地址。

4、单字节读取

5、随机连续字节读取

        同理,读取也支持连续,但是支持无穷连续读取。该随机并不代表随机的地址,而是用户给定的任意地址。

6、顺序读取

        支持不给地址的情况下,从头读取。

二、模拟IIC-I2C的时序控制

        模拟IIC只写时序,不考虑IIC的相关配置。

1、GPIO配置

        之前聊过,I2C为了保证挂载,一般都是ID开漏模式,外部通过电阻上拉。

	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(EEPROM_RCC_I2C_PORT, ENABLE);	/* 打开GPIO时钟 */

	GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN | EEPROM_I2C_SDA_PIN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  	/* 开漏输出 */
	GPIO_Init(EEPROM_GPIO_PORT_I2C, &GPIO_InitStructure);

2、I2C开始

        可以看到,就是直接写时序的形式。

void i2c_Start(void)
{
	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
	EEPROM_I2C_SDA_1();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	EEPROM_I2C_SDA_0();
	i2c_Delay();
	EEPROM_I2C_SCL_0();
	i2c_Delay();
}

3、I2C结束

void i2c_Stop(void)
{
	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
	EEPROM_I2C_SDA_0();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	EEPROM_I2C_SDA_1();
}

4、I2C响应

void i2c_Ack(void)
{
	EEPROM_I2C_SDA_0();	/* CPU驱动SDA = 0 */
	i2c_Delay();
	EEPROM_I2C_SCL_1();	/* CPU产生1个时钟 */
	i2c_Delay();
	EEPROM_I2C_SCL_0();
	i2c_Delay();
	EEPROM_I2C_SDA_1();	/* CPU释放SDA总线 */
}

5、I2C不响应

void i2c_NAck(void)
{
	EEPROM_I2C_SDA_1();	/* CPU驱动SDA = 1 */
	i2c_Delay();
	EEPROM_I2C_SCL_1();	/* CPU产生1个时钟 */
	i2c_Delay();
	EEPROM_I2C_SCL_0();
	i2c_Delay();	
}

6、I2C等待响应

uint8_t i2c_WaitAck(void)
{
	uint8_t re;

	EEPROM_I2C_SDA_1();	/* CPU释放SDA总线 */
	i2c_Delay();
	EEPROM_I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	i2c_Delay();
	if (EEPROM_I2C_SDA_READ())	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else
	{
		re = 0;
	}
	EEPROM_I2C_SCL_0();
	i2c_Delay();
	return re;
}

7、I2C发送字节

void i2c_SendByte(uint8_t _ucByte)
{
	uint8_t i;

	/* 先发送字节的高位bit7 */
	for (i = 0; i < 8; i++)
	{		
		if (_ucByte & 0x80)
		{
			EEPROM_I2C_SDA_1();
		}
		else
		{
			EEPROM_I2C_SDA_0();
		}
		i2c_Delay();
		EEPROM_I2C_SCL_1();
		i2c_Delay();	
		EEPROM_I2C_SCL_0();
		if (i == 7)
		{
			 EEPROM_I2C_SDA_1(); // 释放总线
		}
		_ucByte <<= 1;	/* 左移一个bit */
		i2c_Delay();
	}
}

8、I2C接收字节

uint8_t i2c_ReadByte(void)
{
	uint8_t i;
	uint8_t value;

	/* 读到第1个bit为数据的bit7 */
	value = 0;
	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		EEPROM_I2C_SCL_1();
		i2c_Delay();
		if (EEPROM_I2C_SDA_READ())
		{
			value++;
		}
		EEPROM_I2C_SCL_0();
		i2c_Delay();
	}
	return value;
}

三、模拟IIC-AT24C02相关控制

1、设备检测

        CPU向发送设备地址,然后读取设备应答来判断该设备是否存在。

uint8_t i2c_CheckDevice(uint8_t _Address)
{
	uint8_t ucAck;

	i2c_CfgGpio();		/* 配置GPIO */

	
	i2c_Start();		/* 发送启动信号 */

	/* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */
	i2c_SendByte(_Address | EEPROM_I2C_WR);
	ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */

	i2c_Stop();			/* 发送停止信号 */

	return ucAck;
}

2、写入数据

        首先是开始。

/* 第1步:发起I2C总线启动信号 */
i2c_Start();
				
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);	/* 此处是写指令 */
				
/* 第3步:发送一个时钟,判断器件是否正确应答 */
if (i2c_WaitAck() == 0)
{
	break;
}

        其次发送地址和数据,后续循环每次地址+1,向串行EEPROM指定地址写入若干数据,写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page,即8次写入地址。

/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
i2c_SendByte((uint8_t)usAddr);
			
/* 第5步:等待ACK */
if (i2c_WaitAck() != 0)
{
	goto cmd_fail;	/* EEPROM器件无应答 */
}
/* 第6步:开始写入数据 */
i2c_SendByte(_pWriteBuf[i]);
	
/* 第7步:发送ACK */
if (i2c_WaitAck() != 0)
{
    goto cmd_fail;	/* EEPROM器件无应答 */
}
usAddr++;	/* 地址增1 */	

        最终结束。

/* 命令执行成功,发送I2C总线停止信号 */
i2c_Stop();
return 1;
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();
return 0;

3、读取数据

        首先启动与控制。

/* 第1步:发起I2C总线启动信号 */
i2c_Start();
	
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_WR);	/* 此处是写指令 */
/* 第3步:等待ACK */
if (i2c_WaitAck() != 0)
{
	goto cmd_fail;	/* EEPROM器件无应答 */
}

        其次发送地址与等待数据。可以发现和写入不同,我们只传输了一次地址,这是因为读取支持连续读取字节,只需要给一次地址即可。

/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
i2c_SendByte((uint8_t)_usAddress);
/* 第5步:等待ACK */
if (i2c_WaitAck() != 0)
{
	goto cmd_fail;	/* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
i2c_Start();
	
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte(EEPROM_DEV_ADDR | EEPROM_I2C_RD);	/* 此处是读指令 */
/* 第8步:发送ACK */
if (i2c_WaitAck() != 0)
{
	goto cmd_fail;	/* EEPROM器件无应答 */
}	
	
/* 第9步:循环读取数据 */
for (i = 0; i < _usSize; i++)
{
	_pReadBuf[i] = i2c_ReadByte();	/* 读1个字节 */
		
	/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
	if (i != _usSize - 1)
	{
		i2c_Ack();	/* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
	}
	else
	{
		i2c_NAck();	/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
	}
}

        最终结束。

/* 发送I2C总线停止信号 */
i2c_Stop();
return 1;	/* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
i2c_Stop();

        可以看到上面所有的代码都是被封装的,且相关代码几乎就是将IO置1或者0,所以不管是切换了芯片还是IO,甚至换了标准与HAL库都非常好解决。


http://www.kler.cn/a/461411.html

相关文章:

  • 原子类及原理和ABA问题解决
  • 蓝桥杯JAVA--003
  • JVM实战—9.线上FGC的几种案例
  • API多并发识别、C#文字识别
  • 单片机串口控制
  • android studio android sdk下载地址
  • el-input输入框需要支持多输入,最后传输给后台的字段值以逗号分割
  • 智慧社区养老服务平台(源码+文档+部署+讲解)
  • C语言自定义类型
  • 字节跳动Java开发面试题及参考答案(数据结构算法-手撕面试题)
  • 【网络云SRE运维开发】2024第52周-每日【2024/12/31】小测-计算机网络参考模型和通信协议的理论和实操考题-简要解析
  • AI替换:FaceFusion4.1.0 更新内容和软件
  • STM32 IAP技术 bootloader设计
  • 好用的随机生成图片的网站
  • Ae:项目设置 - 音频
  • π₀:基于VLM的多任务具身操作基础模型
  • View Shadcn UI 正式版本 v2024.5.4 发布
  • C++【内存管理】
  • golang中的错误处理机制
  • Fetch处理大模型流式数据请求与解析
  • OpenLinkSaas使用手册-项目外部资源管理
  • HarmonyOS:@Require装饰器:校验构造传参
  • 深入解析 Android MediaHTTPConnection JNI 实现
  • 2024广东省职业技能大赛云计算——私有云(OpenStack)平台搭建
  • Java Web学生自习管理系统
  • 课程设计项目之基于Python实现围棋游戏代码