stm32-软件iic 硬件iic

一,硬件层 

 原理:多个设备共用scl和sda信号线,支持多主机多从机。scl是单向串行时钟线,始终由主设备控制,sda是双向串行数据线,可以由主机和从机控制。每个设备在这个协议中都有固定的设备号。有俩个上拉电阻,当设备空闲时,由上拉电阻拉高。如果是多主机,那么会使用仲裁,避免数据冲突。有3种传输模式,100k,400k,3.4m。

为什么要使用俩条信号线?节省资源。为什么scl时钟线由主设备控制?进行通信的主动权是掌握在主机手中。为什么sda是双向串行数据线?因为设备和设备之间要通信,交换数据。为什么通过设备号区分?因为主设备发送数据是全部从设备都接收到了,就要通过一个特殊的id来区分,一般是厂家设置。上拉电阻的作用?避免数据冲突,如果主设备和从设备同时输出高电平,造成短路。空闲时,电平也会自动拉高。

二,协议层

 主机发送起始信号,然后发送需要通信的从设备地址,选择读写位,等待应答,然后发送数据等待应答。传输数据需要停止时,主机发送停止信号,接收数据需要停止时,给从机发送非应答。

  1. 起始信号 scl高电平时,sda从高电平向低电平跳变
  2. 停止信号 scl高电平时,sda从低电平向高电平跳变
  3. 只有scl为高电平时,sda的数据才有效
  4. 读数据时,sda会被主机释放掉,由从机控制sda,主机通过sda接收数据;写数据时,sda由主机控制,从机通过sda接收;iic的设备地址有 7位和11位。
  5. 响应,不管是主机和从机接收到数据都要发送应答,为了信号的完整性。有发送应答ack和发送非应答nack,接收到nack会停止数据发送。比如在主机发送完数据后,从机会立马得到sda的控制权,0是应答,1是非应答

三,软件模拟iic

好处:不用依赖硬件资源,灵活,操作简单。

坏处:速度比硬件慢。 

  1. iic起始信号
    //起始信号 sda先拉低在拉低scl
    void IIC_Start(void) 
    {
    	IIC_SDA_1();  
    	IIC_SCL_1();
    	IIC_Delay();
    	IIC_SDA_0();
    	IIC_Delay();
    	IIC_SCL_0();//方便发送数据中,不用在拉低时钟线
    	IIC_Delay();
    }

  2. iic停止信号:动SDA的时候一定要先保证SCL是低,如果进来的时候两个数据线都是高,你进来就把SDA拉低,变成了启始信号。
    //停止信号 sda先拉低,确保scl是1,在拉高sda
    void IIC_Stop(void)
    {
        IIC_SCL_0(); //放置信号混淆
    	IIC_SDA_0();
    	IIC_SCL_1(); //确保读到sda低电平
    	IIC_Delay(); 
    	IIC_SDA_1(); 
    }
  3. 发送应答信号:sda 0
    //主机scl拉高,读取从机sda的电平,为低电平为应答
    void IIC_Ack(void)
    {
        IIC_SCL_0();
    	IIC_SDA_0();
    	IIC_Delay();
    	IIC_SCL_1();
    	IIC_Delay();
    	IIC_SCL_0();
    }

  4. 发送非应答信号:sda 1 

    //主机scl拉高,读取从机sda的电平,高电平为非应答
    void IIC_Ack(void)
    {
        IIC_SCL_0();
    	IIC_SDA_1();
    	IIC_Delay();
    	IIC_SCL_1();
    	IIC_Delay();
    	IIC_SCL_0();
    }

  5. 等待应答信号
    uint8_t IIC_WaitAck(void)
    {
    	uint8_t re;
    	IIC_SDA_1(); //释放总线
    	IIC_Delay();
    	IIC_SCL_1();
    	//CPU读取sda端口线的状态,因为主机会释放sda,然后由从机接管,端口线的结果是主机与从机的线与结果
    	if(IIC_SDA_READ()) 
    	{
    		re=1;
    	}
    	else
    		re=0;
    	IIC_SCL_0();
    	return re;
    }

  6.  发送一个字节数据
    void IIC_SendByte(uint8_t _ucByte)
    {
    	uint8_t i;
    	for(i=0;i<8;i++)
    	{
    		//转换数据为信号 为1还是0 = 移位寄存器,先准备数据,在开时钟线,因为在scl为1时,sda不允许改变
    		if(_ucByte&0x80) 
    		{
    			IIC_SDA_1(); 
    		}
    		else 
    			IIC_SDA_0();
    		IIC_Delay();
    		IIC_SCL_1();
    		IIC_Delay();
    		IIC_SCL_0(); //确保下一次能之间写入数据
    		if(i==7)
    		{
    			IIC_SDA_1();
    		}
    		_ucByte<<=1;
    	}
    }

  7. 接收一个字节
    uint8_t IIC_ReadByte(u8 ack)
    {
    	uint8_t i;
    	uint8_t value;
    	value=0;
    	for(i=0;i<8;i++)
    	{
    		value<<=1;
    		IIC_SCL_1();
    		IIC_Delay();
    		if(IIC_SDA_READ())
    		{
    			value++;
    		}
    		IIC_SCL_0();
    		IIC_Delay();
    	}
    	if(ack==0)
    		IIC_NAck();
    	else
    		IIC_Ack();
    	return value;
    }
     

四,硬件iic

 

  1. 第一部分 是俩个通信引脚
  2. 第二部分 是控制iic的通信速率 ccr和iic外设输入时钟源共同构成iic时钟
  3. 第三部分 硬件的对数据进行移位,发送时把数据寄存器数据一位一位发送,接收时把移位寄存器的数据一位一位放进数据寄存器。自身地址寄存器,存放的是自身的地址,做从机时,接收到主机的设备信号,会进行比较。双地址,同时使用俩个iic设备。pec是和数据校验相关,存放校验结果。
  4. 第四部分 控制iic整体逻辑

iic的硬件代码

  1. 初始化结构体

    typedef struct
    {
      uint32_t I2C_ClockSpeed;          /*!< Specifies the clock frequency.
                                             This parameter must be set to a value lower than 400kHz */
    
      uint16_t I2C_Mode;                /*!< Specifies the I2C mode.
                                             This parameter can be a value of @ref I2C_mode */
    
      uint16_t I2C_DutyCycle;           /*!< Specifies the I2C fast mode duty cycle.
                                             This parameter can be a value of @ref I2C_duty_cycle_in_fast_mode */
    
      uint16_t I2C_OwnAddress1;         /*!< Specifies the first device own address.
                                             This parameter can be a 7-bit or 10-bit address. */
    
      uint16_t I2C_Ack;                 /*!< Enables or disables the acknowledgement.
                                             This parameter can be a value of @ref I2C_acknowledgement */
    
      uint16_t I2C_AcknowledgedAddress; /*!< Specifies if 7-bit or 10-bit address is acknowledged.
                                             This parameter can be a value of @ref I2C_acknowledged_address */
    }I2C_InitTypeDef;
    //iic的时钟频率 iic的工作模式 时钟占空比 配置自身地址 配置ack 配置寻址模式7或10

     

  2. 发送数据,要判断事件

    uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr) 
    {
      /* Send STRAT condition */
      I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
    
      I2CTimeout = I2CT_FLAG_TIMEOUT;  
      /* Test on EV5 and clear it */
      while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))  
      {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0);
      } 
      
      I2CTimeout = I2CT_FLAG_TIMEOUT;
      /* Send EEPROM address for write */
      I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);
      
      /* Test on EV6 and clear it */
      while(!I2C_CheckEvent(EEPROM_I2Cx, 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_I2Cx, WriteAddr);
      
      I2CTimeout = I2CT_FLAG_TIMEOUT;
      /* Test on EV8 and clear it */
      while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
      {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2);
      } 
      
      /* Send the byte to be written */
      I2C_SendData(EEPROM_I2Cx, *pBuffer); 
      
      I2CTimeout = I2CT_FLAG_TIMEOUT;  
      /* Test on EV8 and clear it */
      while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
      {
        if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
      } 
      
      /* Send STOP condition */
      I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
      
      return 1;
    }
  3. 接收数据,判断事件 

uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{  
  
  I2CTimeout = I2CT_LONG_TIMEOUT;
  
  //*((u8 *)0x4001080c) |=0x80; 
  while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9);
   }
  
  /* Send START condition */
  I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
  //*((u8 *)0x4001080c) &=~0x80;
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;
  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10);
   }
  
  /* Send EEPROM address for write */
  I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

  I2CTimeout = I2CT_FLAG_TIMEOUT;
  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, 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_I2Cx, ENABLE);

  /* Send the EEPROM's internal address to write to */
  I2C_SendData(EEPROM_I2Cx, ReadAddr);  

   
  I2CTimeout = I2CT_FLAG_TIMEOUT;
  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12);
   }
    
  /* Send STRAT condition a second time */  
  I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;
  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13);
   }
    
  /* Send EEPROM address for read */
  I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Receiver);
  
  I2CTimeout = I2CT_FLAG_TIMEOUT;
  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, 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_I2Cx, DISABLE);
      
      /* Send STOP Condition */
      I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
    }

    /* Test on EV7 and clear it */    
    I2CTimeout = I2CT_LONG_TIMEOUT;
    
		while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)==0)  
		{
			if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3);
		} 
    {      
      /* Read a byte from the EEPROM */
      *pBuffer = I2C_ReceiveData(EEPROM_I2Cx);

      /* 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_I2Cx, ENABLE);
  
    return 1;
}

 

  • 53
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值