I2C读写EEPROM"/>
STM32F4—I2C读写EEPROM
本文参考了野火stm32官网资料。
硬件IIC读写EEPROM
- 一. I2C协议简介
- I2C物理层的特点
- I2C的协议层
- 1. I2C基本读写过程
- 主机由从机中读数据:
- 通讯复合格式:
- 2.通讯的起始和停止信号
- 3.数据有效性
- 4.地址及数据方向
- 5.响应
- 二 .STM32的I2C特性及架构
- STM32的I2C架构剖析
- 1.通讯引脚
- 2.时钟控制逻辑
- 3.数据控制逻辑
- 4.整体控制逻辑
- STM32的I2C通讯过程
- 1.主发送器
- 主发送器通讯过程
- 2.主接收器
- 三.I2C初始化结构体
- 1. I2C_ClockSpeed
- 2.I2C_Mode
- 3.I2C_DutyCycle
- 4.I2C_OwnAddress1
- 5.I2C_Ack_Enable
- 6.I2C_AcknowledgeAddress
- 四. 硬件IIC示例代码:
- 1.头文件:
- 2.源文件:
- 3.主函数:
一. I2C协议简介
I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
I2C物理层的特点
它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
I2C的协议层
I2C的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
1. I2C基本读写过程
主机由从机中读数据:
通讯复合格式:
2.通讯的起始和停止信号
- 当 SCL 线是高电平时 SDA 线从高电平向低电平切换,这个情况表示通讯的起始。
- 当 SCL 是高电平时 SDA 线由低电平向高电平切换,表示通讯的停止。
- 起始和停止信号一般由主机产生。
3.数据有效性
- I2C使用SDA信号线来传输数据,使用SCL信号线进行数据同步。 SDA数据线在SCL的每个时钟周期传输一位数据。
- SCL为高电平的时候SDA表示的数据有效,即此时的SDA为高电平时表示数据“1”,为低电平时表示数据“0”。
- 当SCL为低电平时,SDA的数据无效,一般在这个时候SDA进行电平切换,为下一次表示数据做好准备。
4.地址及数据方向
I
- 2C总线上的每个设备都有自己的独立地址,主机发起通讯时,通过SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。设备地址可以是7位或10位。
- 紧跟设备地址的一个数据位R/W用来表示数据传输方向,数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
5.响应
- I2C的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。
- 传输时主机产生时钟,在第9个时钟时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。
二 .STM32的I2C特性及架构
- 软件模拟协议:使用CPU直接控制通讯引脚的电平,产生出符合通讯 协议标准的逻辑。
- 硬件实现协议:由STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。
- STM32的I2C外设可用作通讯的主机及从机,支持100Kbit/s和400Kbit/s的速率,支持7位、10位设备地址,支持DMA数据传输,并具有数据校验功能。
STM32的I2C架构剖析
1.通讯引脚
- STM32芯片有多个I2C外设,它们的I2C通讯信号引出到不同的GPIO引脚上, 使用时必须配置到这些指定的引脚,以《STM32F4xx规格书》为准。
2.时钟控制逻辑
SCL线的时钟信号,由I2C接口根据时钟控制寄存器(CCR)控制,控制的参数主要为时钟频率。
- 可选择I2C通讯的“标准/快速”模式,这两个模式分别I2C对应 100/400Kbit/s的通讯速率。
- 在快速模式下可选择SCL时钟的占空比,可选Tlow/Thigh=2或 Tlow/Thigh=16/9模式。
- CCR寄存器中12位的配置因子CCR,它与I2C外设的输入时钟源共同 作用,产生SCL时钟。STM32的I2C外设输入时钟源为PCLK1
计算时钟频率:
标准模式:
Thigh=CCR*TPCKL1 Tlow = CCR*TPCLK1
快速模式中Tlow/Thigh=2时:
Thigh = CCR*TPCKL1 Tlow = 2*CCR*TPCKL1
快速模式中Tlow/Thigh=16/9时:
Thigh = 9*CCR*TPCKL1 Tlow = 16*CCR*TPCKL1
例如,我们的PCLK1=42MHz,想要配置400Kbit/s的速率,计算方式如下:
PCLK时钟周期: TPCLK1 = 1/42000000
目标SCL时钟周期: TSCL = 1/400000
SCL时钟周期内的高电平时间: THIGH = TSCL/3
SCL时钟周期内的低电平时间: TLOW = 2*TSCL/3
计算CCR的值: CCR = THIGH/TPCLK1 = 35
该结果刚好为整数,所以我们可直接把CCR取值为35,这样I2C的SCL实际频率即为400KHz。
3.数据控制逻辑
I2C的SDA信号主要连接到数据移位寄存器上,数据移位寄存器的数据来源及目标是数据寄存器(DR)、地址寄存器(OAR)、PEC寄存器以及SDA数据线。
- 当向外发送数据的时候,数据移位寄存器以“数据寄存器”为数据源,把数据一位一位地通过SDA信号线发送出去;
- 当从外部接收数据的时候,数据移位寄存器把SDA信号线采样到的数据一位一位地存储到“数据寄存器”中。
4.整体控制逻辑
- 整体控制逻辑负责协调整个I2C外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。
- 在外设工作时,控制逻辑会根据外设的工作状态修改“状态寄存器(SR1和SR2)”,只要读取这些寄存器相关的寄存器位,就可以了解I2C的工作状态。
STM32的I2C通讯过程
使用I2C外设通讯时,在通讯的不同阶段它会对“状态寄存器(SR1及SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。
1.主发送器
可使用STM32标准库函数来直接检测这些事件的复合标志,降低编程难度。
主发送器通讯过程
- 控制产生起始信号(S),当发生起始信号后,它产生事件“EV5”,并会对SR1寄存器的“SB”位置1,表示起始信号已经发送;
- 发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”及“EV8”,这时SR1寄存器的“ADDR”位及“TXE”位被置1,ADDR 为1表示地址已经发送,TXE为1表示数据寄存器为空;
- 往I2C的“数据寄存器DR”写入要发送的数据,这时TXE位会被重置0,表示数据寄存器非空,I2C外设通过SDA信号线一位位把数据发送出去后,又会产生“EV8”事件,即TXE位被置1,重复这个过程,可以发送多个字节数据;
- 发送数据完成后,控制I2C设备产生一个停止信号§,这个时候会产生EV2事件,SR1的TXE位及BTF位都被置1,表示通讯结束。
2.主接收器
- 起始信号(S)是由主机端产生的,控制发生起始信号后,它产生事件“EV5”,并会对SR1寄存器的“SB”位置1,表示起始信号已经发送。
- 发送设备地址并等待应答信号,若有从机应答,则产生事件“EV6”这时SR1寄存器的“ADDR”位被置1,表示地址已经发送。
- 从机端接收到地址后,开始向主机端发送数据。当主机接收到这些数据后,会产生“EV7”事件,SR1寄存器的RXNE被置1,表示接收数据寄存器非空,读取该寄存器后,可对数据寄存器清空,以便接收下一次数据。此时可以控制I2C发送应答信号(ACK)或非应答信号(NACK),若应答,则重复以上步骤接收数据,若非应答,则停止传输;
- 发送非应答信号后,产生停止信号§,结束传输。
三.I2C初始化结构体
1. I2C_ClockSpeed
- 设置I2C的传输速率,在调用初始化函数时,函数会根据我们输入的数值经过运算后把时钟因子写入到I2C的时钟控制寄存器CCR。而我们写入的这个参数值不得高于400KHz。
- 实际上由于CCR寄存器不能写入小数类型的时钟因子,影响到SCL的实际频率可能会低于本成员设置的参数值,这时除了通讯稍慢一点以外,不会对I2C的标准通讯造成其它影响。
2.I2C_Mode
- 选择I2C的使用方式,有I2C模式(I2C_Mode_I2C )和SMBus主、从模式(I2C_Mode_SMBusHost、
I2C_Mode_SMBusDevice ) 。I2C不需要在此处区分主从模式,直接设置I2C_Mode_I2C即可。
3.I2C_DutyCycle
- 设置I2C的SCL线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平时间为2:1 ( I2C_DutyCycle_2)和16:9(I2C_DutyCycle_16_9)。
- 其实这两个模式的比例差别并不大,一般要求都不会如此严格, 这里随便选就可以了。
4.I2C_OwnAddress1
- 配置STM32的I2C设备自己的地址,每个连接到I2C总线上的设备都要有一个自己的地址,作为主机也不例外。地址可设置为7位或10位(受下面I2C_AcknowledgeAddress成员决定),只要该地址是I2C总线上唯一的即可。
- STM32的I2C外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员I2C_OwnAddress1配置的是默认的、OAR1寄存器存储的地址,若需要设置第二个地址寄存器OAR2,可使用I2C_OwnAddress2Config函数来配置,OAR2不支持10位地址。
5.I2C_Ack_Enable
- 配置I2C应答是否使能,设置为使能则可以发送响应信号。一般配置为允许应答(I2C_Ack_Enable),这是绝大多数遵循I2C标准的设备的通讯要求,改为禁止应答(I2C_Ack_Disable)往往会导致通讯错误。
6.I2C_AcknowledgeAddress
- 选择I2C的寻址模式是7位还是10位地址。这需要根据实际连接到I2C总线上设备的地址进行选择,这个成员的配置也影响到I2C_OwnAddress1成员,只有这里设置成10位模式时,I2C_OwnAddress1才支持10位地址。
配置完这些结构体成员值,调用库函数I2C_Init即可把结构体的配置写入到寄存器中。
四. 硬件IIC示例代码:
1.头文件:
#ifndef __I2C_EE_H
#define __I2C_EE_H#include "stm32f4xx.h"/* AT24C01/02每页有8个字节 */
#define I2C_PageSize 8/* AT24C04/08A/16A每页有16个字节 */
//#define I2C_PageSize 16 /* STM32 I2C 快速模式 */
#define I2C_Speed 400000/* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */
#define I2C_OWN_ADDRESS7 0X0A /*I2C接口*/
#define EEPROM_I2C I2C1
#define EEPROM_I2C_CLK RCC_APB1Periph_I2C1
#define EEPROM_I2C_CLK_INIT RCC_APB1PeriphClockCmd#define EEPROM_I2C_SCL_PIN GPIO_Pin_8
#define EEPROM_I2C_SCL_GPIO_PORT GPIOB
#define EEPROM_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB
#define EEPROM_I2C_SCL_SOURCE GPIO_PinSource8
#define EEPROM_I2C_SCL_AF GPIO_AF_I2C1#define EEPROM_I2C_SDA_PIN GPIO_Pin_9
#define EEPROM_I2C_SDA_GPIO_PORT GPIOB
#define EEPROM_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB
#define EEPROM_I2C_SDA_SOURCE GPIO_PinSource9
#define EEPROM_I2C_SDA_AF GPIO_AF_I2C1/*等待超时时间*/
#define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))/*信息输出*/
#define EEPROM_DEBUG_ON 0#define EEPROM_INFO(fmt,arg...) printf("<<-EEPROM-INFO->> "fmt"\n",##arg)
#define EEPROM_ERROR(fmt,arg...) printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...) do{\if(EEPROM_DEBUG_ON)\printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\}while(0)/* * 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 *//* EEPROM Addresses defines */
#define EEPROM_Block0_ADDRESS 0xA0 /* E2 = 0 */
//#define EEPROM_Block1_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_Block2_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_Block3_ADDRESS 0xA6 /* E2 = 0 */void I2C_EE_Init(void);
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite);
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr);
uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite);
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead);
void I2C_EE_WaitEepromStandbyState(void);#endif /* __I2C_EE_H */
2.源文件:
/********************************************************************************* @file bsp_i2c_ee.c* @author STMicroelectronics* @version V1.0* @date 2015-xx-xx* @brief i2c EEPROM(AT24C02)应用函数bsp******************************************************************************* @attention*********************************************************************************/ #include "./i2c/bsp_i2c_ee.h"
#include "./usart/bsp_debug_usart.h"uint16_t EEPROM_ADDRESS;static __IO uint32_t I2CTimeout = I2CT_LONG_TIMEOUT; static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);/*** @brief I2C1 I/O配置* @param 无* @retval 无*/
static void I2C_GPIO_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure; /*!< EEPROM_I2C Periph clock enable */EEPROM_I2C_CLK_INIT(EEPROM_I2C_CLK, ENABLE);/*!< EEPROM_I2C_SCL_GPIO_CLK and EEPROM_I2C_SDA_GPIO_CLK Periph clock enable */RCC_AHB1PeriphClockCmd(EEPROM_I2C_SCL_GPIO_CLK | EEPROM_I2C_SDA_GPIO_CLK, ENABLE);/*!< GPIO configuration *//* Connect PXx to I2C_SCL*/GPIO_PinAFConfig(EEPROM_I2C_SCL_GPIO_PORT, EEPROM_I2C_SCL_SOURCE, EEPROM_I2C_SCL_AF);/* Connect PXx to I2C_SDA*/GPIO_PinAFConfig(EEPROM_I2C_SDA_GPIO_PORT, EEPROM_I2C_SDA_SOURCE, EEPROM_I2C_SDA_AF); /*!< Configure EEPROM_I2C pins: SCL */ GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(EEPROM_I2C_SCL_GPIO_PORT, &GPIO_InitStructure);/*!< Configure EEPROM_I2C pins: SDA */GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN;GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStructure);}/*** @brief I2C 工作模式配置* @param 无* @retval 无*/
static void I2C_Mode_Configu(void)
{I2C_InitTypeDef I2C_InitStructure; /* I2C 配置 */I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; /* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /* I2C的寻址模式 */I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /* 通信速率 */I2C_Init(EEPROM_I2C, &I2C_InitStructure); /* I2C1 初始化 */I2C_Cmd(EEPROM_I2C, ENABLE); /* 使能 I2C1 */I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
}/*** @brief I2C 外设(EEPROM)初始化* @param 无* @retval 无*/
void I2C_EE_Init(void)
{I2C_GPIO_Config(); I2C_Mode_Configu();/* 根据头文件i2c_ee.h中的定义来选择EEPROM要写入的地址 */
#ifdef EEPROM_Block0_ADDRESS/* 选择 EEPROM Block0 来写入 */EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;
#endif#ifdef EEPROM_Block1_ADDRESS /* 选择 EEPROM Block1 来写入 */EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;
#endif#ifdef EEPROM_Block2_ADDRESS /* 选择 EEPROM Block2 来写入 */EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;
#endif#ifdef EEPROM_Block3_ADDRESS /* 选择 EEPROM Block3 来写入 */EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;
#endif
}/*** @brief 将缓冲区中的数据写到I2C EEPROM中* @param * @arg pBuffer:缓冲区指针* @arg WriteAddr:写地址* @arg NumByteToWrite:写的字节数* @retval 无*/
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;Addr = WriteAddr % I2C_PageSize;count = I2C_PageSize - Addr;NumOfPage = NumByteToWrite / I2C_PageSize;NumOfSingle = NumByteToWrite % I2C_PageSize;/* If WriteAddr is I2C_PageSize aligned */if(Addr == 0) {/* If NumByteToWrite < I2C_PageSize */if(NumOfPage == 0) {I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);I2C_EE_WaitEepromStandbyState();}/* If NumByteToWrite > I2C_PageSize */else {while(NumOfPage--){I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); I2C_EE_WaitEepromStandbyState();WriteAddr += I2C_PageSize;pBuffer += I2C_PageSize;}if(NumOfSingle!=0){I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);I2C_EE_WaitEepromStandbyState();}}}/* If WriteAddr is not I2C_PageSize aligned */else {/* If NumByteToWrite < I2C_PageSize */if(NumOfPage== 0) {//从下一单元格开始存的数据有几个NumByteToWrite -= count;//最后落单的数据的个数NumOfSingle = NumByteToWrite % I2C_PageSize; if(count != 0){ I2C_EE_PageWrite(pBuffer, WriteAddr, count);I2C_EE_WaitEepromStandbyState();WriteAddr += count; //存储单元地址增加pBuffer += count; //数组地址增加} //将最后落单的几个数据存起来if(NumOfSingle != 0){I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState();}}/* If NumByteToWrite > I2C_PageSize */else{NumByteToWrite -= count;NumOfPage = NumByteToWrite / I2C_PageSize;NumOfSingle = NumByteToWrite % I2C_PageSize; if(count != 0){ I2C_EE_PageWrite(pBuffer, WriteAddr, count);I2C_EE_WaitEepromStandbyState();WriteAddr += count;pBuffer += count;} while(NumOfPage--){I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);I2C_EE_WaitEepromStandbyState();WriteAddr += I2C_PageSize;pBuffer += I2C_PageSize; }if(NumOfSingle != 0){I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState();}}}
}/*** @brief 写一个字节到I2C EEPROM中* @param * @arg pBuffer:缓冲区指针* @arg WriteAddr:写地址 * @retval 无*/
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)
{/* Send STRAT condition */I2C_GenerateSTART(EEPROM_I2C, ENABLE);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);} /* Send EEPROM address for write */I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1);} /* Send the EEPROM's internal address to write to */I2C_SendData(EEPROM_I2C, WriteAddr);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);} /* Send the byte to be written */I2C_SendData(EEPROM_I2C, *pBuffer); I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);} /* Send STOP condition */I2C_GenerateSTOP(EEPROM_I2C, ENABLE);return 1;
}/*** @brief 在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数* 不能超过EEPROM页的大小,AT24C02每页有8个字节* @param * @arg pBuffer:缓冲区指针* @arg WriteAddr:写地址* @arg NumByteToWrite:写的字节数* @retval 无*/
uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{I2CTimeout = I2CT_LONG_TIMEOUT;while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);} /* Send START condition */I2C_GenerateSTART(EEPROM_I2C, ENABLE);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);} /* Send EEPROM address for write */I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);} /* Send the EEPROM's internal address to write to */ I2C_SendData(EEPROM_I2C, WriteAddr); I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(! I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);} /* While there is data to be written */while(NumByteToWrite--) {/* Send the current byte */I2C_SendData(EEPROM_I2C, *pBuffer); /* Point to the next byte to be written */pBuffer++; I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);} }/* Send STOP condition */I2C_GenerateSTOP(EEPROM_I2C, ENABLE);return 1;
}/*** @brief 从EEPROM里面读取一块数据 * @param * @arg pBuffer:存放从EEPROM读取的数据的缓冲区指针* @arg WriteAddr:接收数据的EEPROM的地址* @arg NumByteToWrite:要从EEPROM读取的字节数* @retval 无*/
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{ I2CTimeout = I2CT_LONG_TIMEOUT;//*((u8 *)0x4001080c) |=0x80; while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);}/* Send START condition */I2C_GenerateSTART(EEPROM_I2C, ENABLE);//*((u8 *)0x4001080c) &=~0x80;I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);}/* Send EEPROM address for write */I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);}/* Clear EV6 by setting again the PE bit */I2C_Cmd(EEPROM_I2C, ENABLE);/* Send the EEPROM's internal address to write to */I2C_SendData(EEPROM_I2C, ReadAddr); I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV8 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);}/* Send STRAT condition a second time */ I2C_GenerateSTART(EEPROM_I2C, ENABLE);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV5 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);}/* Send EEPROM address for read */I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver);I2CTimeout = I2CT_FLAG_TIMEOUT;/* Test on EV6 and clear it */while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);}/* While there is data to be read */while(NumByteToRead) {if(NumByteToRead == 1){/* Disable Acknowledgement */I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE);/* Send STOP Condition */I2C_GenerateSTOP(EEPROM_I2C, ENABLE);}I2CTimeout = I2CT_LONG_TIMEOUT;while(I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0) {if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);} {/* Read a byte from the device */*pBuffer = I2C_ReceiveData(EEPROM_I2C);/* Point to the next location where the byte read will be saved */pBuffer++; /* Decrement the read bytes counter */NumByteToRead--;} }/* Enable Acknowledgement to be ready for another reception */I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);return 1;
}/*** @brief Wait for EEPROM Standby state * @param 无* @retval 无*/
void I2C_EE_WaitEepromStandbyState(void)
{vu16 SR1_Tmp = 0;do{/* Send START condition */I2C_GenerateSTART(EEPROM_I2C, ENABLE);/* Read EEPROM_I2C SR1 register */SR1_Tmp = I2C_ReadRegister(EEPROM_I2C, I2C_Register_SR1);/* Send EEPROM address for write */I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);}while(!(I2C_ReadRegister(EEPROM_I2C, I2C_Register_SR1) & 0x0002));/* Clear AF flag */I2C_ClearFlag(EEPROM_I2C, I2C_FLAG_AF);/* STOP condition */ I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
}/*** @brief Basic management of the timeout situation.* @param errorCode:错误代码,可以用来定位是哪个环节出错.* @retval 返回0,表示IIC读取失败.*/
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode)
{/* Block communication and all processes */EEPROM_ERROR("I2C 等待超时!errorCode = %d",errorCode);return 0;
}
/*********************************************END OF FILE**********************/
3.主函数:
/********************************************************************************* @file main.c* @author fire* @version V1.0* @date 2015-xx-xx* @brief 读写EEPROM******************************************************************************* @attention********************************************************************************/#include "stm32f4xx.h"
#include "./usart/bsp_debug_usart.h"
#include "./i2c/bsP_i2c_ee.h"
#include "./led/bsp_led.h"#define EEP_Firstpage 0x00
uint8_t I2c_Buf_Write[256];
uint8_t I2c_Buf_Read[256];
uint8_t I2C_Test(void);/*** @brief 主函数* @param 无* @retval 无*/
int main(void)
{LED_GPIO_Config();LED_BLUE;/*初始化USART1*/Debug_USART_Config(); printf("\r\n 欢迎使用野火 STM32 F407 开发板。\r\n"); printf("\r\n 这是一个I2C外设(AT24C02)读写测试例程 \r\n");/* I2C 外设初(AT24C02)始化 */I2C_EE_Init();if(I2C_Test() ==1){LED_GREEN;}else{LED_RED;}while (1){ } }/*** @brief I2C(AT24C02)读写测试* @param 无* @retval 正常返回1 ,不正常返回0*/
uint8_t I2C_Test(void)
{u16 i;EEPROM_INFO("写入的数据");for ( i=0; i<=255; i++ ) //填充缓冲{ I2c_Buf_Write[i] = i;printf("0x%02X ", I2c_Buf_Write[i]);if(i%16 == 15) printf("\n\r"); }//将I2c_Buf_Write中顺序递增的数据写入EERPOM中 I2C_EE_BufferWrite( I2c_Buf_Write, EEP_Firstpage, 256);EEPROM_INFO("写成功");EEPROM_INFO("读出的数据");//将EEPROM读出数据顺序保持到I2c_Buf_Read中I2C_EE_BufferRead(I2c_Buf_Read, EEP_Firstpage, 256); //将I2c_Buf_Read中的数据通过串口打印for (i=0; i<256; i++){ if(I2c_Buf_Read[i] != I2c_Buf_Write[i]){printf("0x%02X ", I2c_Buf_Read[i]);EEPROM_ERROR("错误:I2C EEPROM写入与读出的数据不一致");return 0;}printf("0x%02X ", I2c_Buf_Read[i]);if(i%16 == 15) printf("\n\r");}EEPROM_INFO("I2C(AT24C02)读写测试成功");return 1;
}/*********************************************END OF FILE**********************/
更多推荐
STM32F4—I2C读写EEPROM
发布评论