STM32F407单片机通用24CXXX读写程序(KEIL),兼容24C系列存储器(24C01到24C512),支持存储器任意地址跨页连续读写多个页

一、AT24CXXX容量

  AT24C01,AT24C02,AT24C04,AT24C08,AT24C16,AT24C32,AT24C64,AT24C128,AT24C256…不同的xxx代表不同的容量。

AT24CXXX bit容量 Byte容量
AT24C01 1Kbit 128Byte
AT24C02 2Kbit 256Byte
AT24C04 4Kbit 512Byte
AT24C08 8Kbit 1024Byte
AT24C16 16Kbit 2048Byte
AT24C32 32Kbit 4096Byte
AT24C64 64Kbit 8192Byte
AT24C128 128Kbit 16384Byte
AT24C256 256Kbit 32768Byte
AT24C512 512Kbit 65536Byte

二、AT24CXXX页与页内单元

  总容量(Byte容量) = 页数 × 页内字节单元数。

AT24CXXX Byte容量 页数 页内字节单元数
AT24C01 128Byte 16页 8Byte
AT24C02 256Byte 32页 8Byte
AT24C04 512Byte 32页 16Byte
AT24C08 1024Byte 64页 16Byte
AT24C16 2048Byte 128页 16Byte
AT24C32 4096Byte 128页 32Byte
AT24C64 8192Byte 256页 32Byte
AT24C128 16384Byte 256页 64Byte
AT24C256 32768Byte 512页 64Byte
AT24C512 65536Byte 512页 128Byte

三、AT24CXXXX寻址方式(不是IIC地址,是存储器内部寻址)

  对AT24CXXX进行读写操作时,都得先访问存储地址、比如AT24C01写一个字节的IIC时序:
在这里插入图片描述
  先发送设备地址,收到应答后再发送需要写数据的地址(WORD ADDRESS)。AT24C01容量为128Byte则WORD ADDRESS只需要7bit就可以覆盖128Byte的数据地址。通俗的讲就是128Byte就占用了128个地址,一个7bit的数据范围为(0-127)刚好128,所以128Byte的字节地址需要一个7bit的数据来表示。
AT24CXXX 字节地址如下(*表示无效位):

AT24CXXX 容量(Byte) WORD ADDRESS(占用bit数) WORD ADDRESS
AT24C01 128Byte 7bit 在这里插入图片描述
AT24C02 256Byte 8bit 在这里插入图片描述
AT24C04 512Byte 9bit 在这里插入图片描述
AT24C08 1024Byte 10bit 在这里插入图片描述
AT24C16 2048Byte 11bit 在这里插入图片描述
AT24C32 4096Byte 12bit 在这里插入图片描述
AT24C64 8192Byte 13bit 在这里插入图片描述
AT24C128 16384Byte 14bit 在这里插入图片描述
AT24C256 32768Byte 15bit 在这里插入图片描述
AT24C512 65536Byte 16bit 在这里插入图片描述

四、AT24CXXX页地址与页内单元地址

  比如AT24C256有512页每页64个字节,15bit的地址数据对其寻址,低6bit(D5-D0)为页内字节单元地址,高9bit(D14-D6)为页地址。
在这里插入图片描述
如第16页开始写,则WORD ADDRESS = 0x0400(0000 0100 0000 0000)
0:地址无效位
000 0100 00:9位页地址
00 0000:6位页内字节单元地址
下表如AT24C01
16页:需要4bit寻址(2^4=16)
8Byte:需要3bit寻址(2^3=8)

AT24CXXX Byte容量 页数 页内字节单元数 页地址 页内偏移地址
AT24C01 128Byte 16页 8Byte 在这里插入图片描述
AT24C02 256Byte 32页 8Byte 在这里插入图片描述
AT24C04 512Byte 32页 16Byte 在这里插入图片描述
AT24C08 1024Byte 64页 16Byte 在这里插入图片描述
AT24C16 2048Byte 128页 16Byte 在这里插入图片描述
AT24C32 4096Byte 128页 32Byte 在这里插入图片描述
AT24C64 8192Byte 256页 32Byte 在这里插入图片描述
AT24C128 16384Byte 256页 64Byte 在这里插入图片描述
AT24C256 32768Byte 512页 64Byte 在这里插入图片描述
AT24C512 65536Byte 512页 128Byte 在这里插入图片描述

查看手册
AT24C01字节寻址需一个7bit地址:
在这里插入图片描述
AT24C128字节寻址需一个14bit地址:
在这里插入图片描述
以此类推,其实就是上面总结的那张表。

五、AT24CXXX IIC地址

  IIC通信需要先向从设备发送设备地址,AT24CXXX芯片上有A2、A1、A0引脚,通过这三个引脚我们就可以自定义AT24CXXX芯片的通信地址。
在这里插入图片描述
地址构成如下(手册上都会有写),比如A2、A1、A0接地,则IIC写地址为1010 0000(0xA0),读地址为1010 0001(0xA1),有关IIC地址详情请看IIC协议详解
在这里插入图片描述

六、AT24CXXX 数据的读写

AT24C256为例

1、字节写

在这里插入图片描述

2、按页写

在这里插入图片描述
★★★注意:
  往AT24CXXX中写数据时,每写一个Byte的数据页内地址+1,当前页写满后会重新覆盖掉这一页前面的数据,而不会自动跳转到下一页,但是读会自动翻页。
具体看手册:
在这里插入图片描述

3、如何翻页写

  按页写其实就是执行一次下面的时序,也就是发送一次从机设备和字节地址最大就可以写入64字节的数据,如果要连写多页,就重新按照以下时序发送从机地址和字节地址等。
在这里插入图片描述

4、读

有以下模式,和写差不多
在这里插入图片描述

 

在这里插入图片描述

七、源程序

 1、i2c_gpio.h

#ifndef _BSP_I2C_GPIO_H
#define _BSP_I2C_GPIO_H#include "stm32f4xx.h"#define I2C_WR	0		// 写控制bit
#define I2C_RD	1		// 读控制bitvoid BSP_AT24CXX_InitI2C(void);
void i2c_Start(void);
void i2c_Stop(void);
void i2c_SendByte(uint8_t _ucByte);
uint8_t i2c_ReadByte(void);
uint8_t i2c_WaitAck(void);
void i2c_Ack(void);
void i2c_NAck(void);
uint8_t i2c_CheckDevice(uint8_t _Address);#endif

2、i2c_ee.h

#ifndef __I2C_EE_H
#define	__I2C_EE_H#include "stm32f4xx.h"/* * AT24C02 2kb = 2048bit = 2048/8 B = 256 B* 32 pages of 8 bytes each** Device Address* 1 0 1 0 A2 A1 A0 R/W* 1 0 1 0 0  0  0  0 = 0xA0* 1 0 1 0 0  0  0  1 = 0xA1 *//* AT24C01/02每页有8个字节 * AT24C04/08A/16A每页有16个字节 、*/#define AT24C512#ifdef AT24C01#define EE_MODEL_NAME		"AT24C01"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		8			/* 页面大小(字节) */#define EE_SIZE				128			/* 总容量(字节) */#define EE_ADDR_BYTES		1			/* 地址字节个数 */#define EE_ADDR_A8			0			/* 地址字节的高8bit不在首字节 */
#endif#ifdef AT24C02#define EE_MODEL_NAME		"AT24C02"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		8			/* 页面大小(字节) */#define EE_SIZE				256			/* 总容量(字节) */#define EE_ADDR_BYTES		1			/* 地址字节个数 */#define EE_ADDR_A8			0			/* 地址字节的高8bit不在首字节 */
#endif#ifdef AT24C04#define EE_MODEL_NAME		"AT24C04"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		8			/* 页面大小(字节) */#define EE_SIZE				512			/* 总容量(字节) */#define EE_ADDR_BYTES		1			/* 地址字节个数 */#define EE_ADDR_A8			1			/* 地址字节的高8bit在首字节 */
#endif#ifdef AT24C08#define EE_MODEL_NAME		"AT24C08"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		16			/* 页面大小(字节) */#define EE_SIZE				(16*64)		/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			1			/* 地址字节的高8bit在首字节 */
#endif#ifdef AT24C16#define EE_MODEL_NAME		"AT24C16"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		16			/* 页面大小(字节) */#define EE_SIZE				(128*16)	/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			1			/* 地址字节的高8bit在首字节 */
#endif#ifdef AT24C32#define EE_MODEL_NAME		"AT24C32"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		32			/* 页面大小(字节) */#define EE_SIZE				(128*32)	/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			1			/* 地址字节的高8bit在首字节 */
#endif#ifdef AT24C64#define EE_MODEL_NAME		"AT24C64"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		32			/* 页面大小(字节) */#define EE_SIZE				(256*32)	/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			1			/* 地址字节的高8bit在首字节 */
#endif#ifdef AT24C128#define EE_MODEL_NAME		"AT24C128"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		64			/* 页面大小(字节) */#define EE_SIZE				(256*64)	/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			0			/* 地址字节的高8bit不在首字节 */
#endif#ifdef AT24C256#define EE_MODEL_NAME		"AT24C256"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		64			/* 页面大小(字节) */#define EE_SIZE				(512*64)	/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			0			/* 地址字节的高8bit不在首字节 */
#endif#ifdef AT24C512#define EE_MODEL_NAME		"AT24C512"#define EE_DEV_ADDR			0xA0		/* 设备地址 */#define EE_PAGE_SIZE		128			/* 页面大小(字节) */#define EE_SIZE				(512*128)	/* 总容量(字节) */#define EE_ADDR_BYTES		2			/* 地址字节个数 */#define EE_ADDR_A8			0			/* 地址字节的高8bit不在首字节 */
#endif#endif /* __I2C_EE_H */

3、i2c_gpio.c

*
*********************************************************************************************************
*
*	模块名称 : I2C总线驱动模块
*	文件名称 : bsp_i2c_gpio.c
*	版    本 : V1.0
*	说    明 : 用gpio模拟i2c总线, 适用于STM32F4系列CPU。该模块不包括应用层命令帧,仅包括I2C总线基本操作函数。
*
*	修改记录 :
*		版本号  日期        作者     说明
*		V1.0    2013-02-01 armfly  正式发布
*
*	Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*//*应用说明:在访问I2C设备前,请先调用 i2c_CheckDevice() 检测I2C设备是否正常,该函数会配置GPIO
*/#include "stm32f4xx.h"
#include "i2c_gpio.h"#define RCC_AT24CXX_I2C_PORT 			RCC_AHB1Periph_GPIOB		// GPIO端口时钟
#define GPIO_AT24CXX_I2C_PORT			GPIOB						// GPIO端口
#define GPIO_AT24CXX_I2C_SCL_Pin		GPIO_Pin_6					// 连接到SCL时钟线的GPIO
#define GPIO_AT24CXX_I2C_SDA_Pin		GPIO_Pin_7					// 连接到SDA数据线的GPIO#define I2C_SCL_1()  	GPIO_SetBits(GPIO_AT24CXX_I2C_PORT, GPIO_AT24CXX_I2C_SCL_Pin)			// SCL = 1
#define I2C_SCL_0()  	GPIO_ResetBits(GPIO_AT24CXX_I2C_PORT, GPIO_AT24CXX_I2C_SCL_Pin)			// SCL = 0
#define I2C_SDA_1()  	GPIO_SetBits(GPIO_AT24CXX_I2C_PORT, GPIO_AT24CXX_I2C_SDA_Pin)			// SDA = 1
#define I2C_SDA_0()  	GPIO_ResetBits(GPIO_AT24CXX_I2C_PORT, GPIO_AT24CXX_I2C_SDA_Pin)			// SDA = 0
#define I2C_SDA_READ()  GPIO_ReadInputDataBit(GPIO_AT24CXX_I2C_PORT, GPIO_AT24CXX_I2C_SDA_Pin)				// 读SDA口线状态
#define I2C_SCL_READ()  GPIO_ReadInputDataBit(GPIO_AT24CXX_I2C_PORT, GPIO_AT24CXX_I2C_SCL_Pin)				// 读SCL口线状态/*
*********************************************************************************************************
*	函 数 名: bsp_InitI2C
*	功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void BSP_AT24CXX_InitI2C(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AT24CXX_I2C_PORT , ENABLE);	/* 打开GPIO时钟 */GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;		/* 设为输出口 */GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;		/* 设为开漏模式 */GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	/* 上下拉电阻不使能 */GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;	/* IO口最大速度 */GPIO_InitStructure.GPIO_Pin = GPIO_AT24CXX_I2C_SCL_Pin | GPIO_AT24CXX_I2C_SDA_Pin;GPIO_Init(GPIO_AT24CXX_I2C_PORT, &GPIO_InitStructure);/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */i2c_Stop();
}/*
*********************************************************************************************************
*	函 数 名: i2c_Delay
*	功能说明: I2C总线位延迟,最快400KHz
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{uint8_t i;/* CPU主频168MHz时,在内部Flash运行, MDK工程不优化。用台式示波器观测波形。循环次数为5时,SCL频率 = 1.78MHz (读耗时: 92ms, 读写正常,但是用示波器探头碰上就读写失败。时序接近临界)循环次数为10时,SCL频率 = 1.1MHz (读耗时: 138ms, 读速度: 118724B/s)循环次数为30时,SCL频率 = 440KHz, SCL高电平时间1.0us,SCL低电平时间1.2us上拉电阻选择2.2K欧时,SCL上升沿时间约0.5us,如果选4.7K欧,则上升沿约1us实际应用选择400KHz左右的速率即可*/for (i = 0; i < 30; i++){__NOP();__NOP();}
}/*
*********************************************************************************************************
*	函 数 名: i2c_Start
*	功能说明: CPU发起I2C总线启动信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start(void)
{/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */I2C_SDA_1();I2C_SCL_1();i2c_Delay();I2C_SDA_0();i2c_Delay();I2C_SCL_0();i2c_Delay();
}/*
*********************************************************************************************************
*	函 数 名: i2c_Start
*	功能说明: CPU发起I2C总线停止信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop(void)
{/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */I2C_SDA_0();I2C_SCL_1();i2c_Delay();I2C_SDA_1();i2c_Delay();
}/*
*********************************************************************************************************
*	函 数 名: i2c_SendByte
*	功能说明: CPU向I2C总线设备发送8bit数据
*	形    参:  _ucByte : 等待发送的字节
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte(uint8_t _ucByte)
{uint8_t i;/* 先发送字节的高位bit7 */for (i = 0; i < 8; i++){if (_ucByte & 0x80){I2C_SDA_1();}else{I2C_SDA_0();}i2c_Delay();I2C_SCL_1();i2c_Delay();I2C_SCL_0();if (i == 7){I2C_SDA_1(); // 释放总线}_ucByte <<= 1;	/* 左移一个bit */i2c_Delay();}
}/*
*********************************************************************************************************
*	函 数 名: i2c_ReadByte
*	功能说明: CPU从I2C总线设备读取8bit数据
*	形    参:  无
*	返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte(void)
{uint8_t i;uint8_t value;/* 读到第1个bit为数据的bit7 */value = 0;for (i = 0; i < 8; i++){value <<= 1;I2C_SCL_1();i2c_Delay();if (I2C_SDA_READ()){value++;}I2C_SCL_0();i2c_Delay();}return value;
}/*
*********************************************************************************************************
*	函 数 名: i2c_WaitAck
*	功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*	形    参:  无
*	返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck(void)
{uint8_t re;I2C_SDA_1();	/* CPU释放SDA总线 */i2c_Delay();I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */i2c_Delay();if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */{re = 1;}else{re = 0;}I2C_SCL_0();i2c_Delay();return re;
}/*
*********************************************************************************************************
*	函 数 名: i2c_Ack
*	功能说明: CPU产生一个ACK信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack(void)
{I2C_SDA_0();	/* CPU驱动SDA = 0 */i2c_Delay();I2C_SCL_1();	/* CPU产生1个时钟 */i2c_Delay();I2C_SCL_0();i2c_Delay();I2C_SDA_1();	/* CPU释放SDA总线 */
}/*
*********************************************************************************************************
*	函 数 名: i2c_NAck
*	功能说明: CPU产生1个NACK信号
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck(void)
{I2C_SDA_1();	/* CPU驱动SDA = 1 */i2c_Delay();I2C_SCL_1();	/* CPU产生1个时钟 */i2c_Delay();I2C_SCL_0();i2c_Delay();
}/*
*********************************************************************************************************
*	函 数 名: i2c_CheckDevice
*	功能说明: 检测I2C总线设备,CPU向发送设备地址,然后读取设备应答来判断该设备是否存在
*	形    参:  _Address:设备的I2C总线地址
*	返 回 值: 返回值 0 表示正确, 返回1表示未探测到
*********************************************************************************************************
*/
uint8_t i2c_CheckDevice(uint8_t _Address)
{uint8_t ucAck;if (I2C_SDA_READ() && I2C_SCL_READ()){i2c_Start();		/* 发送启动信号 *//* 发送设备地址+读写控制bit(0 = w, 1 = r) bit7 先传 */i2c_SendByte(_Address | I2C_WR);ucAck = i2c_WaitAck();	/* 检测设备的ACK应答 */i2c_Stop();			/* 发送停止信号 */return ucAck;}return 1;	/* I2C总线异常 */
}

4、i2c_ee.c

/*
*********************************************************************************************************
*
*	模块名称 : 串行EEPROM 24xx驱动模块
*	文件名称 : bsp_eeprom_24xx.c
*	版    本 : V1.0
*	说    明 : 实现24xx系列EEPROM的读写操作。写操作采用页写模式提高写入效率。
*
*	修改记录 :
*		版本号  日期        作者     说明
*		V1.0    2013-02-01 armfly  正式发布
*
*	Copyright (C), 2013-2014, 安富莱电子 www.armfly.com
*
*********************************************************************************************************
*//*应用说明:访问串行EEPROM前,请先调用一次 bsp_InitI2C()函数配置好I2C相关的GPIO.
*/#include "i2c_gpio.h"
#include "i2c_ee.h"/*
*********************************************************************************************************
*	函 数 名: ee_CheckOk
*	功能说明: 判断串行EERPOM是否正常
*	形    参:  无
*	返 回 值: 1 表示正常, 0 表示不正常
*********************************************************************************************************
*/
uint8_t ee_CheckOk(void)
{if (i2c_CheckDevice(EE_DEV_ADDR) == 0){return 1;}else{/* 失败后,切记发送I2C总线停止信号 */i2c_Stop();return 0;}
}/*
*********************************************************************************************************
*	函 数 名: ee_ReadBytes
*	功能说明: 从串行EEPROM指定地址处开始读取若干数据
*	形    参:  _usAddress : 起始地址
*			 _usSize : 数据长度,单位为字节
*			 _pReadBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{uint16_t i;/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 *//* 第1步:发起I2C总线启动信号 */i2c_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */#if EE_ADDR_A8 == 1i2c_SendByte(EE_DEV_ADDR | I2C_WR | ((_usAddress >> 7) & 0x0E));	/* 此处是写指令 */#elsei2c_SendByte(EE_DEV_ADDR | I2C_WR);	/* 此处是写指令 */#endif/* 第3步:发送ACK */if (i2c_WaitAck() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */if (EE_ADDR_BYTES == 1){i2c_SendByte((uint8_t)_usAddress);if (i2c_WaitAck() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}}else{i2c_SendByte(_usAddress >> 8);if (i2c_WaitAck() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}i2c_SendByte(_usAddress);if (i2c_WaitAck() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}}/* 第6步:重新启动I2C总线。下面开始读取数据 */i2c_Start();/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */#if EE_ADDR_A8 == 1i2c_SendByte(EE_DEV_ADDR | I2C_RD | ((_usAddress >> 7) & 0x0E));	/* 此处是写指令 */#else		i2c_SendByte(EE_DEV_ADDR | I2C_RD);	/* 此处是写指令 */#endif	/* 第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();return 0;
}/*
*********************************************************************************************************
*	函 数 名: ee_WriteBytes
*	功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
*	形    参:  _usAddress : 起始地址
*			 _usSize : 数据长度,单位为字节
*			 _pWriteBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t ee_WriteBytes(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{uint16_t i,m;uint16_t usAddr;/*写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。对于24xx02,page size = 8简单的处理方法为:按字节写操作模式,每写1个字节,都发送地址为了提高连续写的效率: 本函数采用page wirte操作。*/usAddr = _usAddress;for (i = 0; i < _usSize; i++){/* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */if ((i == 0) || (usAddr & (EE_PAGE_SIZE - 1)) == 0){/* 第0步:发停止信号,启动内部写操作 */i2c_Stop();/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10msCLK频率为200KHz时,查询次数为30次左右*/for (m = 0; m < 1000; m++){/* 第1步:发起I2C总线启动信号 */i2c_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */#if EE_ADDR_A8 == 1i2c_SendByte(EE_DEV_ADDR | I2C_WR | ((_usAddress >> 7) & 0x0E));	/* 此处是写指令 */#else				i2c_SendByte(EE_DEV_ADDR | I2C_WR);#endif/* 第3步:发送一个时钟,判断器件是否正确应答 */if (i2c_WaitAck() == 0){break;}}if (m  == 1000){goto cmd_fail;	/* EEPROM器件写超时 */}/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */if (EE_ADDR_BYTES == 1){i2c_SendByte((uint8_t)usAddr);if (i2c_WaitAck() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}}else{i2c_SendByte(usAddr >> 8);if (i2c_WaitAck() != 0){goto cmd_fail;	/* EEPROM器件无应答 */}i2c_SendByte(usAddr);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();/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10msCLK频率为200KHz时,查询次数为30次左右*/for (m = 0; m < 1000; m++){/* 第1步:发起I2C总线启动信号 */i2c_Start();/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */	#if EE_ADDR_A8 == 1i2c_SendByte(EE_DEV_ADDR | I2C_WR | ((_usAddress >> 7) & 0x0E));	/* 此处是写指令 */#else		i2c_SendByte(EE_DEV_ADDR | I2C_WR);	/* 此处是写指令 */#endif/* 第3步:发送一个时钟,判断器件是否正确应答 */if (i2c_WaitAck() == 0){break;}}if (m  == 1000){goto cmd_fail;	/* EEPROM器件写超时 */}/* 命令执行成功,发送I2C总线停止信号 */i2c_Stop();	return 1;cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 *//* 发送I2C总线停止信号 */i2c_Stop();return 0;
}

 

八、测试

下面以AT24C512为例,进行测试,测试以下功能:

        1、任意地址连续跨页读多页数据

        2、任意地址连续跨页写多页数据

注意:如果需要测试AT24C512,需要在i2c_ee.h中定义宏定义 AT24C512,告诉单片机目前的芯片是AT24C512芯片。

#define AT24C512

注意:如果需要测试AT24C128,需要在i2c_ee.h中定义宏定义 AT24C128,告诉单片机目前的芯片是AT24C128芯片.

#define AT24C128

为便于观察数据读写的每一个字节都是否正确,初始化数组时,将test_array1[0—127] 初始化数值 = 1—128

                                                  test_array1[128—255] 初始化数值 = 1—128

                                                  test_array1[256—383] 初始化数值 = 1—128

下面的测试程序先将存储在数组test_array1中的连续3页数据写到起始地址为80的芯片中。然后将起始地址为80的芯片中的数据读到数组test_array2.

 

main.c

    #include "i2c_gpio.h"#include "i2c_ee.h"uint8_t test_array1[3*EE_PAGE_SIZE];   //注:AT24C512时,EE_PAGE_SIZE=128uint8_t test_array2[3*EE_PAGE_SIZE];   //    AT24C512时,一个页面有128个字节 void DEBUG_test_AT24C512(void){uint16_t i;uint16_t j;for (i=0;i<3*EE_PAGE_SIZE;i++){if (i>=256)j=i-256;            //test_array1[256---383] 单元初始化数值 = 1---128else if (i>=128)j=i-128;            //test_array1[128---255] 单元初始化数值 = 1---128elsej=i;              //test_array1[0---127] 单元初始化数值 = 1---128test_array1[i]=j+1;}memset(test_array2,0x00,3*EE_PAGE_SIZE);  if (ee_CheckOk() == 1)   //如果检测到I2C器件存在{ee_WriteBytes(test_array1,80,3*EE_PAGE_SIZE);  //从I2C的地址80处开始写3页字节(测试跨页连续写)ee_ReadBytes(test_array2,80,3*EE_PAGE_SIZE); //从I2C的地址80处开始读3页字节(测试跨页连续读)}}int main(void)
{BSP_AT24CXX_InitI2C();DEBUG_test_AT24C512();while (1){}
}

九、测试结果

 

 

可以观察到先将3页数据(共计128*3=384个字节) 写到AT24C512起始地址80处,然后再次读出,数据完全正确。

Published by

风君子

独自遨游何稽首 揭天掀地慰生平