15 玩转STM32之IIC通信(芯片硬件篇)

15.1 IIC控制器说明(硬件部分)

14 玩转STM32之IIC通信(软件模拟篇)我们讲了软件模拟的方式,本章讲解芯片自带的硬件部分(并非所有的芯片都含有IIC,像51系列的是没有的,但是绝大多数是有的,根据我用过的S32、STM32、HC32、CH32、GD32都是有的,目前我只见过51的没有、STM8也有这个可能分系列吧,我用的有,其他的不清楚)

15.1.1 STM32 IIC通信主要特性

STM32F429微控制器继承了3个IIC(内部集成的)软件模拟和硬件模拟的功能是一样的。

具备多主模式功能,同一接口可以用作主从模式。
7 位/10 位寻址以及广播呼叫的生成和检测。
支持不同的通信速度:
— 标准速度(高达 100 kHz)
— 快速速度(高达 400 kHz)
状态标志:
— 发送/接收模式标志
— 字节传输结束标志
— I2C 忙碌标志
错误标志:
— 主模式下的仲裁丢失情况
— 地址/数据传输完成后的应答失败
— 检测误放的起始位和停止位
— 禁止时钟延长后出现的上溢/下溢
带 DMA 功能的 1 字节缓冲(减轻MCU工作)
兼容SMBus2.0和PMBus。
具体的功能作用可参看数据手册(648页中文版)
总结:
STM32的I2C外设可用作通讯的主机及从机,支持100Kbit/s和400Kbit/s的速率,支持7位、10位设备地址,支持DMA数据传输,并具有数据校验功能。

15.1.2 STM32 IIC结构

IIC通过SCL和SDA两个引脚完成与其他器件进行通信,如下图IIC内部结构图:

IIC引脚对照表:

IIC有四种工作模式:从发送器、从接收器、主发送器、主接收器模式四种。
默认情况下,IIC在从模式下工作。接口在生成起始位后会自动由从模式切换为主模式,并在出现重在丢失或生成停止位的时候,从模式切换为从模式,进而实现多主模式功能。

15.1.3 STM32 IIC主模式

在主模式下,I2C接口会启动数据传输(发送起始信号和器件地址,地址始终在主模式下发送),并生成时钟信号(SCL),将需要发送的数据写入数据寄存器,并通过数据移位寄存器和数据控制逻辑(输出),将数据一位位发送到SDA数据线。通信的时钟由主机的时钟控制逻理生成,可以在标准速度或快速速度模式下工作。通信的工作模式由控制寄存器(CR1和CR2)的不同配置控制,状态寄存器体现通信过程中产生的一系列状态,根据不同的工作模式和通信状态,控制逻辑负责实现完整的通信过程。串行数据传输始终在出现起始位时开始,在出现停止位时结束。起始位和停止位均在主模式下由软件生成。
12C控制器自动检测从机发送回来的ACK信号,并置位状态寄存器相应的状态位,程序通过检测状态寄存器(SR1和SR2)的状态位,判断数据是否发送成功。

1-主发送器模式

I2C控制器产生开始信号(S),然后通过检测EV5事件,判断是否启动成功。
在满足EV5事件后,主机发送器件地址+W,然后通过检测EV6事件、判断是否发送器件地址成功。
在满足EV6事件后,主机发送数据,然后通过检测EV8事件,判断是否发送数据成功。在发送完最后一个数据后,主机发送结束信号§结束通信过程。
如图:主发送器模式下IIC通信示意图:

EV5:总线正在进行通信(BUSY=1),主/从模式(MSL=1)及起始位是否己经发送(SB=1)。
EV6:处于发送器或接收器状态(TRA=1),正在进行通信(BUSY=1),主/从模式(MSL=1).
数据寄存器是否为空(TXE=1),地址是否已发送(ADDR=1,主模式)。
EV8:处于发送器或接收器状态(TRA=1),正在进行通信(BUSY=1),主/从模式(MSL=1).
数据寄存器是否为空(TXE=l),字节是否传输完成(BTF=1,主模式)。

2-主接收器模式

I2C控制器产生开始信号,然后通过检测EV5事件,判断是否启动成功。
在满足EV5事件后,主机发送器件地址,然后通过检测EV6事件,判断是否发送器件地址成功。
在满足EV6事件后,主机准备接收从机发送过来的数据,然后通过检测EV7事件,判断是否接收数据成功。如果接收的不是最后一个数据的话,则主机发送ACK信号给从机。
如果接收的是最后一个数据的话,则主机发送一个NACK信号,并发送结束信号,结束通信。
EV7:正在进行通信(BUSY=1),主/从模式(MSL=1),数据寄存器非空(RXNE=1)。
主接收器模式下I2C通信示意图如图:

15.1.4 IIC 从模式

在从模式下,根据写入自身地址寄存器的地址(从模式下的器件地址),l2C控制器通过比较器能够识主机发送过来的地址是否和其自身地址(7位或10位)一致。在地址匹配的情况下,根据读写控制状态,通过数据控制逻辑可以接收(写)或发送(读)数据。

1-从发送器模式

在检测到开始信号后,I2C控制器通过检测EV1事件,判断主机发送过来的器件地址是否和本机地址一致。
在满足EV1事件后,从机发送一个ACK信号给主机,将数据发送给主机,并通过检测EV3事件,判断是否发送数据成功。在发送完最后一个数据后,从机检测到NACK信号和结束信号,结束通信。
EV1:正在进行通信(BUSY=1),接收到的地址匹配(ADDR=1,从模式)。
EV3:处于发送器或接收器状态(TRA=1),正在进行通信(BUSY=l),数据寄存器是否为
空(TXE=1)。
从发送器模式下I2C通信示意图如图:

15.1.5 12C控制碍中断

I2C控制器有2个中断向量:一个中新由成功的地址/数期字管传输事件发另一个中新由银误状态触发。
I2C控制器支持多种中断事件的请求,便于实时响应一些紧急事务,为了提高CPU利用率。选常在I2C控制器处于从模式时,使用中新方式来响应一系列事务的处理。例如,从模式下数西的接收、发送、停止及错误等,I2C控制器中斯事件如图所示:

15.2 IIC应用步骤和常用库函数

15.2.1 IIC应用步骤

以IIC1为例这里,其他的与这是一样的.

(1)使能IIC1时钟和通信线复用引脚端口GPIOB的时钟。

使能IIC通信时钟

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); //I2C2时钟使能

GPIO时钟使能

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //GPIO时钟使能

(2)初始化引脚

/*GPIO引脚复用*/
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); //将PB6复用给I2C1
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); //将PB7复用给I2C1
    /*初始化GPIO */   
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
  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_UP;//使能上拉
  GPIO_Init(GPIOB, &GPIO_InitStructure);// 初始化PB6:SCL
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
  GPIO_Init(GPIOB, &GPIO_InitStructure);// 初始化PB7:SDA

(3)初始化IIC工作模式

	/* I2C 配置 */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;	//I2C模式 */
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	// SCL 时钟线的占空比 
  I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7; // 从机时,自身器件地址
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;	// 使能ACK相应
   //7bit的寻址模式 
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = 400000;	     // 通信速率<=400K
  I2C_Init(I2C1, &I2C_InitStructure);	                 // I2C2 初始化 

(4)使能IIC1控制器

I2C_Cmd(I2C1, ENABLE);  	                     // 使能 I2C2 

(5)使能IIC ACK应答

I2C_AcknowledgeConfig(I2C1, ENABLE);  //使能IIC ACK功能

(6)中断使能

如果需要使用中断,则需要配置NVIC和使能IIC中断。

15.3.2 常用库函数

与I2C相关的函数和宏都被定义在以下两个文件中。
头文件:stm32f4xx_i2c.h.
源文件:stm32f4xx i2c.c。

1-I2C初始化函数

void 12C_Init(12C_TypeDef* I2Cx,I2C_InitTypeDef* 12C_InitStruct);
参数1:I2C_TypeDef*I2Cx,I2C应用对象,一个结构体指针,表示形式是I2C1、I2C2和I2C3,以宏定义形式定义在stm32f4xx_.h文件中。例如:

#define I2C1 		((12C_TypeDef *) I2C1_BASE)
#define I2C2		((12C_TypeDef *) 12C2_BASE)
#define I2C3		((I2C_TypeDef *) I2C3_BASE)

参数2:I2C_InitTypeDef* I2C_InitStruct,I2C应用对象初始化结构体指针,以自定义的结
构体形式定义在stm32f4xx i2c.h文件中。

typedef struct
{
uint32_t	I2C_ClockSpeed;//时钟速度
uint16_t	I2C_Mode;//工作模式
uint16 t	I2C_DutyCycle;//时钟信号低电平/高电平的占空比
uint16_t I2C_OwnAddressl;//自身器件地址,从机时使用
uintl6_t 12C_Ack;//ACK应答使能
uint16_t I2C_AcknowledgedAddress;/12C寻址模式
}12C_InitTypeDef;

成员1:uint32_tI2C_ClockSpced,时钟速度,根据自定义的通信速度,库程序会将I2C配置为标准模式(≤100kHz)或快速模式。
成员2: uintl6_tI2C_Mode,工作模式,可以是12C模式或SMBus模式,有如下定义:

#define I2C_Mode_12C  ((uintl6_t)0x0000) //I2C模式
#define 12C_Mode_SMBusDevice ((uint16_t)0x0002)//SMBus设备模式
#define I2C_Mode_SMBusHost ((uint16_t)0x000A) //SMBus 主机模式

成员3:12C DutyCycle,定义时钟信号低电平/高电平的占空比,有如下定义:

#define I2C_DutyCycle_16_9 ((uintl6_t)0x4000)//时钟信号低电干/高电干=16/9
#define I2C_DutyCycle_2 ((uintl6_t)0xBFFF)//时钟信号低电平/高电平=2

成员4:I2C_OwnAddress1,定义从机通信时的自身器件地址。
成员5:I2C_ Ack,定义ACK应答使能,有如下定义。

#define l2C_Ack_Enable ((uint16_t)0x0400)//使能ACK应答使能
#define l2C Ack Disable ((uintl6_t1)0x0000)//禁止使能ACK应答使能

成员6:I2C_AcknowledgedAddress,定义I2C寻址模式,有如下定义。

#define l2C_AcknowledgedAddress_7bi t ((uint16_t)0x4000)//7 (位地址寻址模式
#define I2C_AcknowledgedAddress_10bit ((uint16_1)0xC000)//10位地址寻址模式

2-IIC 使能函数

void 12C_Cmd(I2C_TypeDef* 12Cx,FunctionalState NewState);

参数2:FunctionalState NewState,使能或禁止12CACK应答功能。
ENABLE:使能I2C ACK应答功能。
DISABLE:禁止I2CACK应答功能。
例如,使能I2C1 ACK应答功能:

I2C_Cmd (12C1,ENABLE);

参数1:I2C应用对象,同I2C初始化函数参数1。

4-I2C检测通信事件函数

ErrorStatus I2C_CheckEvent(12C_TypeDef* 12Cx,uint32_t I2C EVENT);

参数1:I2C应用对象,同12C初始化参数1。
参数2:uint32t I2C EVENT,定义通信事件,有EVI~EV9事件,它D分国定 不同的I2C通信状态。例如,EV8为主机接收到字节数据事件。

#deline I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint)2_1Cx00070084

其他的定义参见头文件stm32f4xx_i2c.h文件中的定义。
例如,等待I2C2主机发送完一个字节(EV8)。

while(!12C_CheckEvent(I2C2,12C_EVENT_MASTER_BVTE_ TRANSMITTED)

返回:成功或失败、ErorSiuatus是一个枚举类型,定义如下:

typedef enum {ERROR=0,SUCCESS=!ERROR}ErrorStatus;

5-I2C开始值号

/**
  * @brief  Generates I2Cx communication START condition.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @param  NewState: new state of the I2C START condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState)

6-I2C控制器产生结束信号函数

/**
  * @brief  Generates I2Cx communication STOP condition.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @param  NewState: new state of the I2C STOP condition generation.
  *          This parameter can be: ENABLE or DISABLE.
  * @retval None.
  */
void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)

7-I2C控制器发送7位寻址地址函数

/**
  * @brief  Transmits the address byte to select the slave device.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @param  Address: specifies the slave address which will be transmitted
  * @param  I2C_Direction: specifies whether the I2C device will be a Transmitter
  *         or a Receiver. 
  *          This parameter can be one of the following values
  *            @arg I2C_Direction_Transmitter: Transmitter mode
  *            @arg I2C_Direction_Receiver: Receiver mode
  * @retval None.
  */
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)

8-I2C控制器发送一个字节的数据函数

/**
  * @brief  Sends a data byte through the I2Cx peripheral.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @param  Data: Byte to be transmitted..
  * @retval None
  */
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)

9-I2C控制器接收一个字节的数据函数

/**
  * @brief  Returns the most recent received data by the I2Cx peripheral.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  * @retval The value of the received data.
  */
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)

10-I2C控制器获取最新的通信事件涵数

/**
  * @brief  Returns the last I2Cx Event.
  * @param  I2Cx: where x can be 1, 2 or 3 to select the I2C peripheral.
  *     
  * @note   For detailed description of Events, please refer to section I2C_Events
  *         in stm32f4xx_i2c.h file.
  *    
  * @retval The last event
  */
uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx)

以上就是这些部分,具体的其他部分请查看相关.c以及.h文件和数据手册。

15.3 应用



#include "bsp_i2c_ee.h"
#include "bsp_usart.h"

uint16_t EEPROM_ADDRESS;

static __IO uint32_t  I2CTimeout = I2CT_LONG_TIMEOUT;   


static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);



/**
  * @brief  I2C 工作模式配置
  * @param  无
  * @retval 无
  */
void I2C_Config(void)
{
 I2C_InitTypeDef  	I2C_InitStructure; 
	GPIO_InitTypeDef  GPIO_InitStructure; 
	/*-------------------第1步--------------------*/  
	/*时钟使能*/
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE); //I2C2时钟使能
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOH, ENABLE); //GPIO时钟使能

	/*-------------------第2步--------------------*/	
	/*GPIO引脚复用*/
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource4, GPIO_AF_I2C2); //将PH4复用给I2C2
  GPIO_PinAFConfig(GPIOH, GPIO_PinSource5, GPIO_AF_I2C2); //将PH5复用给I2C2
  /*初始化GPIO */   
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
  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_UP;//使能上拉
  GPIO_Init(GPIOH, &GPIO_InitStructure);// 初始化PH4:SCL
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_Init(GPIOH, &GPIO_InitStructure);// 初始化PH5:SDA


	/*-------------------第3步--------------------*/
	/* I2C 配置 */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;	//I2C模式 */
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;	// SCL 时钟线的占空比 
  I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7; // 从机时,自身器件地址
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;	// 使能ACK相应
   //7bit的寻址模式 
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = 400000;	     // 通信速率<=400K
  I2C_Init(I2C2, &I2C_InitStructure);	                 // I2C2 初始化 


	/*-------------------第4步--------------------*/
	I2C_Cmd(I2C2, ENABLE);  	                     // 使能 I2C2 

	/*-------------------第5步--------------------*/
	I2C_AcknowledgeConfig(I2C2, ENABLE);  //使能IIC ACK功能
	
	EEPROM_ADDRESS = 0xA0;//EEPROM器件地址

}


/**
  * @brief   将缓冲区中的数据写到I2C EEPROM中
  * @param   
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址
  *     @arg NumByteToWrite:写的字节数
  * @retval  无
  */
void I2C_Buffer_Write(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_Page_Write(pBuffer, WriteAddr, NumOfSingle);
      I2C_WaitEepromStandbyState();
    }
    /* If NumByteToWrite > I2C_PageSize */
    else  
    {
      while(NumOfPage--)
      {
        I2C_Page_Write(pBuffer, WriteAddr, I2C_PageSize); 
        I2C_WaitEepromStandbyState();
        WriteAddr +=  I2C_PageSize;
        pBuffer += I2C_PageSize;
      }

      if(NumOfSingle!=0)
      {
        I2C_Page_Write(pBuffer, WriteAddr, NumOfSingle);
        I2C_WaitEepromStandbyState();
      }
    }
  }
  /* If WriteAddr is not I2C_PageSize aligned  */
  else 
  {
    /* If NumByteToWrite < I2C_PageSize */
    if(NumOfPage== 0) 
    {
      I2C_Page_Write(pBuffer, WriteAddr, NumOfSingle);
      I2C_WaitEepromStandbyState();
    }
    /* If NumByteToWrite > I2C_PageSize */
    else
    {
      NumByteToWrite -= count;
      NumOfPage =  NumByteToWrite / I2C_PageSize;
      NumOfSingle = NumByteToWrite % I2C_PageSize;	
      
      if(count != 0)
      {  
        I2C_Page_Write(pBuffer, WriteAddr, count);
        I2C_WaitEepromStandbyState();
        WriteAddr += count;
        pBuffer += count;
      } 
      
      while(NumOfPage--)
      {
        I2C_Page_Write(pBuffer, WriteAddr, I2C_PageSize);
        I2C_WaitEepromStandbyState();
        WriteAddr +=  I2C_PageSize;
        pBuffer += I2C_PageSize;  
      }
      if(NumOfSingle != 0)
      {
        I2C_Page_Write(pBuffer, WriteAddr, NumOfSingle); 
        I2C_WaitEepromStandbyState();
      }
    }
  }  
}

/**
  * @brief   写一个字节到I2C EEPROM中
  * @param   
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址 
  * @retval  无
  */
uint32_t I2C_Byte_Write(u8* pBuffer, u8 WriteAddr)
{
  /* Send STRAT condition   启动操作*/
  I2C_GenerateSTART(I2C2, ENABLE);

  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV5 and clear it 检测总线是否忙*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
  }    

  /* Send EEPROM address for write 发送器件地址 写操作 */
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
  
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;
  /* Test on EV6 and clear it   等待ACK*/
  while(!I2C_CheckEvent(I2C2, 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(I2C2, WriteAddr);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV8 and clear it   等待ACK*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED))  
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
  } 
  /* Send the byte to be written   写数据*/
  I2C_SendData(I2C2, *pBuffer); 
   
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV8 and clear it 等待ACK*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
  } 
  
  /* Send STOP condition 停止操作*/
  I2C_GenerateSTOP(I2C2, ENABLE);
  
  return 1;
}

/**
  * @brief   在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数
  *          不能超过EEPROM页的大小,AT24C02每页有8个字节
  * @param   
  *		@arg pBuffer:缓冲区指针
  *		@arg WriteAddr:写地址
  *     @arg NumByteToWrite:写的字节数
  * @retval  无
  */
uint32_t I2C_Page_Write(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
  I2CTimeout = I2CT_LONG_TIMEOUT;

  while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY))  //检测IIC控制器是否忙 确认没有总线上没有通信
   {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
  } 
  
  /* Send START condition 启动操作*/
  I2C_GenerateSTART(I2C2, ENABLE);
  
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV5 ((uint32_t)0x00030001)    and clear it   确认以下信息: BUSY-总线正在进行通信, MSL-主/从模式, and SB-起始位已经发送*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
  } 
  
  /* Send EEPROM address for write 写器件地址 写操作*/
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV6 ((uint32_t)0x00070082) and clear it 等待ACK		 确认以下信息:  TRA-发送器/接收器, BUSY-总线正在进行通信, MSL-主/从模式,  TXE-数据寄存器为空, ADDR-地址已发送(主模式)*/
  while(!I2C_CheckEvent(I2C2, 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(I2C2, WriteAddr);  

  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV8 and clear it 等待ACK    ((uint32_t)0x00070084)   确认以下信息: TRA-发送器/接收器, BUSY-总线正在进行通信, MSL-主/从模式, TXE-数据寄存器为空 and BTF-字节传输完成 */
  while(! I2C_CheckEvent(I2C2, 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(I2C2, *pBuffer); 

    /* Point to the next byte to be written */
    pBuffer++; 
  
    I2CTimeout = I2CT_FLAG_TIMEOUT;

    /* Test on EV8 and clear it 等待ACK		   ((uint32_t)0x00070084)   确认以下信息: TRA-发送器/接收器, BUSY-总线正在进行通信, MSL-主/从模式, TXE-数据寄存器为空 and BTF-字节传输完成*/
    while (!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
    } 
  }

  /* Send STOP condition 停止操作*/
  I2C_GenerateSTOP(I2C2, ENABLE);
  
  return 1;
}

/**
  * @brief   从EEPROM里面读取一块数据 
  * @param   
  *		@arg pBuffer:存放从EEPROM读取的数据的缓冲区指针
  *		@arg WriteAddr:接收数据的EEPROM的地址
  *     @arg NumByteToWrite:要从EEPROM读取的字节数
  * @retval  无
  */
uint32_t I2C_Buffer_Read(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{  
    I2CTimeout = I2CT_LONG_TIMEOUT;

  //*((u8 *)0x4001080c) |=0x80; 
    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY))    //检测IIC控制器是否忙
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
    }
  /* Send START condition 启动操作*/
  I2C_GenerateSTART(I2C2, ENABLE);
  //*((u8 *)0x4001080c) &=~0x80;
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV5 ((uint32_t)0x00030001)    and clear it   确认以下信息: BUSY-总线正在进行通信, MSL-主/从模式, and SB-起始位已经发送*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
   }

  /* Send EEPROM address for write  写器件地址 写操作*/
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);

  I2CTimeout = I2CT_FLAG_TIMEOUT;
 
  /* Test on EV6 ((uint32_t)0x00070082) and clear it 等待ACK		 确认以下信息:  TRA-发送器, BUSY-总线正在进行通信, MSL-主/从模式,  TXE-数据寄存器为空, ADDR-地址已发送(主模式)*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11);
   }
//  /* Clear EV6 by setting again the PE bit 清除事件6   实际测试去掉也没有出错*/
//  I2C_Cmd(I2C2, ENABLE);

  /* Send the EEPROM's internal address to write to 发送器件内读地址*/
  I2C_SendData(I2C2, ReadAddr);  

     I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV8 and clear it 等待ACK    ((uint32_t)0x00070084)   确认以下信息: TRA-发送器, BUSY-总线正在进行通信, MSL-主/从模式, TXE-数据寄存器为空 and BTF-字节传输完成 */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
   }
  /* Send STRAT condition a second time 重亲启动操作*/  
  I2C_GenerateSTART(I2C2, ENABLE);
  
     I2CTimeout = I2CT_FLAG_TIMEOUT;

   /* Test on EV5 ((uint32_t)0x00030001)    and clear it   确认以下信息: BUSY-总线正在进行通信, MSL-主/从模式, and SB-起始位已经发送*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
   }
  /* Send EEPROM address for read 发送器件地址+1  读操作*/
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Receiver);
  
     I2CTimeout = I2CT_FLAG_TIMEOUT;

  /* Test on EV6 ((uint32_t)0x00030002) and clear it 等待ACK		 确认以下信息:  TRA-接收器, BUSY-总线正在进行通信, MSL-主/从模式,  TXE-数据寄存器为空, ADDR-地址已发送(主模式)*/
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14);
   }
  /* While there is data to be read */
  while(NumByteToRead)  
  {		
		I2CTimeout = I2CT_LONG_TIMEOUT;
		
		if(NumByteToRead == 1)//最后一个数据的话
    {
      /* Disable Acknowledgement 禁止ACK 结束数据接收*/
      I2C_AcknowledgeConfig(I2C2, DISABLE);
      
      /* Send STOP Condition 停止操作*/
      I2C_GenerateSTOP(I2C2, ENABLE);
    }
		else
			/* Enable Acknowledgement to be ready for another reception 使能ACK  重复数据接收*/
			I2C_AcknowledgeConfig(I2C2, ENABLE);		
		
		while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)  //等待数据接收结束  ((uint32_t)0x00030040)   BUSY-总线正在进行通信, MSL-主/从模式, and RXNE-数据寄存器非空 flags
		{
			if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
		} 	
		{
		  /* Read a byte from the device 读取数据*/
      *pBuffer = I2C_ReceiveData(I2C2);

      /* Point to the next location where the byte read will be saved */
      pBuffer++; 
      
      /* Decrement the read bytes counter */
      NumByteToRead--;
		}	 
  }
  
  return 1;
}

/**
  * @brief  Wait for EEPROM Standby state 
  * @param  无
  * @retval 无
  */
void I2C_WaitEepromStandbyState(void)      
{
  vu16 SR1_Tmp = 0;

  do
  {
    /* Send START condition */
    I2C_GenerateSTART(I2C2, ENABLE);
    /* Read I2C2 SR1 register */
    SR1_Tmp = I2C_ReadRegister(I2C2, I2C_Register_SR1);
    /* Send EEPROM address for write */
    I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
  }while(!(I2C_ReadRegister(I2C2, I2C_Register_SR1) & 0x0002));
  
  /* Clear AF flag */
  I2C_ClearFlag(I2C2, I2C_FLAG_AF);
  /* STOP condition */    
  I2C_GenerateSTOP(I2C2, 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 */
  return 0;
}
/*********************************************END OF FILE**********************/
#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                          I2C2
#define EEPROM_I2C_CLK                      RCC_APB1Periph_I2C2
#define EEPROM_I2C_CLK_INIT								  RCC_APB1PeriphClockCmd

#define EEPROM_I2C_SCL_PIN                  GPIO_Pin_4                 
#define EEPROM_I2C_SCL_GPIO_PORT            GPIOH                       
#define EEPROM_I2C_SCL_GPIO_CLK             RCC_AHB1Periph_GPIOH
#define EEPROM_I2C_SCL_SOURCE               GPIO_PinSource4
#define EEPROM_I2C_SCL_AF                   GPIO_AF_I2C2

#define EEPROM_I2C_SDA_PIN                  GPIO_Pin_5                  
#define EEPROM_I2C_SDA_GPIO_PORT            GPIOH                       
#define EEPROM_I2C_SDA_GPIO_CLK             RCC_AHB1Periph_GPIOH
#define EEPROM_I2C_SDA_SOURCE               GPIO_PinSource5
#define EEPROM_I2C_SDA_AF                   GPIO_AF_I2C2

/*等待超时时间*/
#define I2CT_FLAG_TIMEOUT         ((uint32_t)0x1000)
#define I2CT_LONG_TIMEOUT         ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))

/* 
 * AT24C02      2kb   = 256 B ,    1个块  32 页, 8 字节/页,Block0	
 * AT24C04      4kb   = 512 B ,    2个块2*32 页,16 字节/页,Block0~1
 * AT24C08      8kb   =  1K B ,    4个块4*32 页,16 字节/页,Block0~3
 * AT24C16      16kb  =  2K B ,    8个块8*32 页,16 字节/页,Block0~7

 *
 * 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  /* 块0 AT24C02 AT24C04  AT24C08  AT24C16*/
#define EEPROM_Block1_ADDRESS 0xA2  /*块1  AT24C04  AT24C08  AT24C16*/

#define EEPROM_Block2_ADDRESS 0xA4  /*块2  AT24C08  AT24C16*/
#define EEPROM_Block3_ADDRESS 0xA6  /*块3  AT24C08 AT24C16*/

#define EEPROM_Block4_ADDRESS 0xA8  /* 块4 AT24C16*/ 
#define EEPROM_Block5_ADDRESS 0xAA  /*块5  AT24C16*/
#define EEPROM_Block6_ADDRESS 0xAC  /*块6  AT24C16*/
#define EEPROM_Block7_ADDRESS 0xAE  /*块7  AT24C16*/



void I2C_Config(void);
void I2C_Buffer_Write(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite);
uint32_t I2C_Byte_Write(u8* pBuffer, u8 WriteAddr);
uint32_t I2C_Page_Write(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite);
uint32_t I2C_Buffer_Read(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead);
void I2C_WaitEepromStandbyState(void);

#endif /* __I2C_EE_H */

  • 4
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32F103是一款基于ARM Cortex-M3内核的微控制器,具备硬件IIC(Inter-Integrated Circuit)功能,可以实现主从设备之间的通信硬件IIC通信是一种串行通信协议,通常用于短距离的设备之间的通信。它需要两根信号线:SDA(数据线)和SCL(时钟线)。在IIC通信中,一个设备充当主设备,控制整个通信的进行,而其他设备则充当从设备,等待主设备的命令。 在STM32F103中,可以通过配置相关寄存器和使用相应的库函数来实现IIC通信。作为主设备,我们需要首先初始化IIC总线,并设置相应的通信速率。然后,我们可以通过发送起始信号、选择从设备地址和读写操作来与从设备进行通信通信过程中,主设备可以通过发送或接收数据来完成数据的传输。 作为从设备,我们首先需要等待主设备选择我们的地址。当地址匹配时,我们可以通过拉低SDA线来发送应答信号,表示已经准备好进行通信。在收到数据后,我们可以发送应答信号,告知主设备数据已经接收完毕。 硬件IIC通信是一种简单、高效的通信方式,适用于需要短距离、低速率的设备之间的通信。在使用STM32F103进行硬件IIC主从通信时,我们需要合理配置主设备和从设备的相关参数,并遵循IIC通信的协议来进行通信。 总之,STM32F103通过硬件IIC功能,可以实现主从设备之间的通信。通过合适的配置和使用相应的库函数,我们可以轻松地实现IIC通信,并实现数据的传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南山府嵌入式

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值