STM32F4—I2C读写EEPROM

编程入门 行业动态 更新时间:2024-10-08 10:56:42

STM32F4—<a href=https://www.elefans.com/category/jswz/34/1769217.html style=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

本文发布于:2024-03-12 02:33:32,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1730492.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:I2C   EEPROM

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!