I2C通信协议

这块我们来说一下I2C通信协议。

 IIC有两根线,SCL时钟线,SDA数据线,主机可以挂载很多从机, 多个主机也可以挂载相同的从机,但是同一时刻只能有1个主机同一个从机进行通信。并且时钟线SCL是只能由主机控制,数据线SDA主机和从机都可以控制。

IIC时序

1.IIC空闲状态:SDA为高电平,SCL为高电平。

2.起始信号:当SCL为高电平时,SDA由高电平转变为低电平。

3.停止信号:当SCL为高电平时,SDA由低电平转变为高电平

4.应答或者不应答

当发送低电平时为应答,发送高电平为不应答

应答信号是在主机接收到数据后给从机的信号,一般选择不去应答,然后发送停止信号,如果应答从机可能还会继续发送数据。

 5.时序

平时正常使用是写入一个字节(8个bit位),此时是向所有的从机发送,但是从机会去比对发送的数据,这个数据是从机的地址,这个地址是7位bit,但是不知道是给从机发还是从从机收到数据,所以最后一位数据是确定去发送还是去接收数据。

 如图第1-7位是从机的地址位,最后第0位是控制向从机写还是从从机读数据,当第0位为0时表示主机向从机写数据,当为1时代表主机要接收从机的数据。

所以假设从机地址为1010000,此时要读数据,最后一位发送的就是1,最后的数据也就是0xA1,

当要向该从机写数据时就发送0xA0。

总体时序

在读数据和写数据的时候需要去同步传输的数据,IIC是通过SCL这根时钟线来同步传输的数据的,在写入1个bit位时,需要先将SCL拉低,然后改变此时你想传输的1位数据,再将SCL时钟线拉高,也就是从低电平转变为高电平。此时实现1位bit位的传输,再SCL为高电平时,SDA的数据需要是稳定的数据,也就是1或者0,不能一直再0和1之间跳变。 

使用:当我们再实际用IIC协议时一般是先传输从机地址写数据,然后将想要在从机地址的那个寄存器的地址上写数据,第三个就是写的数据。

写1个字节数据的步骤

假设从机地址是1010000,需要向他的0x10地址写入数据0x55

起始信号  0xA0(要向该从机写数据)  等待应答  0x10(要向0x10寄存器中写入数据) 等待应答     

0x55(数据) 等待应答  停止信号   

 读1个字节的步骤:

假设从机地址是1010000,需要向他的0x10地址读1个字节数据

起始信号  0xA0(向从机写入数据)   等待应答   0x10(要写入的地址)等待应答   起始信号  0xA1(要从从机读取数据) 等待应答  数据   主机非应答   停止信号

读取数据为什么又要写数据呢,因为此时前七位确定了从机的地址,但是不知道在从机的什么位置去读取数据,此时只能先发送需要读取的位置,再将写换成读,记住同一次不能即写又读,但是为了不去释放总线(怕被其他设备占用)所以再次在总线中使用起始信号去读取数据。

附带代码 .c文件

#include "stm32f10x.h"      
#include "hal_eeprom.h"

static void I2C_delay(unsigned short t);
static void hal_I2CConfig(void);
static void I2C_Stop(void);
static void I2C_Start(void);
static void hal_I2C_SCL(unsigned char bVal);
static void hal_I2C_SDA(unsigned char bVal);
static unsigned char hal_I2C_SDA_INPUT(void);
static void hal_I2C_SDA_IO_Set(unsigned char IOMode);
static void hal_I2C_SDA_IO_Set(unsigned char IOMode);
static void I2C_Ack(void);
static void I2C_NoAck(void);
static unsigned char I2C_WaitAck(void);
static void I2C_SendByte(unsigned char SendByte) ;
static unsigned char I2C_ReceiveByte(void) ;


 void hal_eepromInit(void)
{
	hal_I2CConfig();
}
 

//GPIO初始化
static void hal_I2CConfig(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure; 
  /* Configure I2C2 pins: PB8->SCL and PB9->SDA */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  GPIO_InitStructure.GPIO_Pin =  I2C_SCL_PIN | I2C_SDA_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  
  GPIO_Init(I2C_SCL_PORT, &GPIO_InitStructure);
  
  hal_I2C_SDA(1);
  hal_I2C_SCL(1);
}
static void hal_I2C_SDA(unsigned char bVal)
{
 if(bVal)
	 {
		 GPIO_SetBits(I2C_SDA_PORT,I2C_SDA_PIN);
	 }else
	 {
		 GPIO_ResetBits(I2C_SDA_PORT,I2C_SDA_PIN);
	 }
}

static void hal_I2C_SCL(unsigned char bVal)
{
 
	 if(bVal)
	 {
		 GPIO_SetBits(I2C_SCL_PORT,I2C_SCL_PIN);
	 }else
	 {
		 GPIO_ResetBits(I2C_SCL_PORT,I2C_SCL_PIN);
	 }

}
 
static unsigned char hal_I2C_SDA_INPUT(void)
{
	return GPIO_ReadInputDataBit(I2C_SDA_PORT, I2C_SDA_PIN);		
}


static void I2C_delay(unsigned short t)
{	

   unsigned short i=50,j,c; 
   c = t;
   for(j=0; j<c; j++)
   {
	   while(i) 
	   { 
				i--; 
	   } 
	}
}
static void I2C_Start(void)
{
	hal_I2C_SDA(1);
	I2C_delay(1);
	hal_I2C_SCL(1);
	I2C_delay(1);
	hal_I2C_SDA(0);
	I2C_delay(1);
}

 
static void I2C_Stop(void)
{
	hal_I2C_SDA(0);
	I2C_delay(1);
	hal_I2C_SCL(1);
	I2C_delay(1);
	hal_I2C_SDA(1);
	I2C_delay(1);
 
}
static void hal_I2C_SDA_IO_Set(unsigned char IOMode)
{
	if(IOMode == 0)					//输出
	{
		GPIO_InitTypeDef  GPIO_InitStructure; 
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
		GPIO_InitStructure.GPIO_Pin =   I2C_SDA_PIN;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  
		GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);
	}else if(IOMode == 1)			//输入	
	{
		GPIO_InitTypeDef  GPIO_InitStructure; 
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
		GPIO_InitStructure.GPIO_Pin =   I2C_SDA_PIN;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  
		GPIO_Init(I2C_SDA_PORT, &GPIO_InitStructure);

	}
	 
}


static void I2C_Ack(void)
{	
	hal_I2C_SCL(0);
	I2C_delay(1);
	hal_I2C_SDA(0);
	I2C_delay(1);
	hal_I2C_SCL(1);
	I2C_delay(1);
	hal_I2C_SCL(0);
	I2C_delay(1);
}

 
static void I2C_NoAck(void)
{	
	hal_I2C_SCL(0);
	I2C_delay(1);
	hal_I2C_SDA(1);
	I2C_delay(1);
	hal_I2C_SCL(1);
	I2C_delay(1);
	hal_I2C_SCL(0);
	I2C_delay(1);
}
static unsigned char I2C_WaitAck(void) 	
{
	hal_I2C_SDA(1);
	hal_I2C_SDA_IO_Set(1);		 
	hal_I2C_SCL(1);
	I2C_delay(1);
	if(hal_I2C_SDA_INPUT())
	{
		return 0;
	}
	hal_I2C_SCL(0);
	hal_I2C_SDA_IO_Set(0);		 
	I2C_delay(1); 
	return 1;
}
static void I2C_SendByte(unsigned char SendByte) 
{
  unsigned char i=0;
	unsigned char temp;
	temp = SendByte;
	for(i=0;i<8;i++)
	{
		hal_I2C_SCL(0);
		I2C_delay(1);
		if(temp&0x80)
		{
			hal_I2C_SDA(1);
		}else 
		{
			hal_I2C_SDA(0);
		}
		I2C_delay(1);
		hal_I2C_SCL(1);
		I2C_delay(1);
		temp<<=1;
	}
    
	hal_I2C_SCL(0);
	I2C_delay(1);
	hal_I2C_SDA(1);
	I2C_delay(1);
}

//通过I2C来写一个字节的步骤:首先开始信号,写入是发送还是写入的信号,等待应答,发送需要写入数据的地址,等待应答
//,最后在写入数据,等待应答,最后发送停止信号
void I2C_WriteByte(unsigned short address,unsigned char dat)
{
	I2C_Start();
	
	I2C_SendByte(0xA0);
	I2C_WaitAck();
	
	I2C_SendByte((address>>8)&0xFF);
	I2C_WaitAck();
	
	I2C_SendByte(address&0xFF);
	I2C_WaitAck();
	
	I2C_SendByte(dat);
	I2C_WaitAck();
	
	I2C_Stop();
	//I2C_delay(1000);			//延时10ms
}

//读操作同写操作差不多,但是
static unsigned char I2C_ReceiveByte(void)  
{ 
	unsigned char i;
	unsigned char ReceiveByte=0;
	
	hal_I2C_SCL(0);
	I2C_delay(1);
	hal_I2C_SDA(1);
	hal_I2C_SDA_IO_Set(1);		 //SDA设置成输入
	for(i=0; i<8; i++)
	{
		ReceiveByte <<= 1;
		I2C_delay(1);
		hal_I2C_SCL(1);
		I2C_delay(1);
		if(hal_I2C_SDA_INPUT())
		{
			ReceiveByte|=0x01;
		}
		hal_I2C_SCL(0);
		
	}
	hal_I2C_SDA_IO_Set(0);		//SDA设置成输出
	I2C_delay(1);
	return ReceiveByte;
}
//读一个字节,操作同写差不多,主要是参数和读函数
unsigned char I2C_ReadByte(unsigned short address)
{
	unsigned char dat;
	I2C_Start();
	I2C_SendByte(0xA0);
	I2C_WaitAck();

	I2C_SendByte((address>>8)&0xFF);
	I2C_WaitAck();

	I2C_SendByte(address&0xFF);
	I2C_WaitAck();

	I2C_Start();
	I2C_SendByte(0xA1);
	I2C_WaitAck();
	
	dat = I2C_ReceiveByte();
	I2C_NoAck();
	I2C_Stop();
	return dat;
}

.h文件

#ifndef  __HAL_EEPROM_H__
#define  __HAL_EEPROM_H__

#define I2C_SCL_PORT	GPIOB
#define I2C_SCL_PIN		GPIO_Pin_8

#define I2C_SDA_PORT	GPIOB
#define I2C_SDA_PIN		GPIO_Pin_9

#define EEPROM_PAGE_SIZE 32





void hal_eepromInit(void);
unsigned char I2C_ReadByte(unsigned short address);
void I2C_WriteByte(unsigned short address,unsigned char dat);
void I2C_PageWrite(unsigned short address,unsigned char *pDat, unsigned short num);
void I2C_Read(unsigned short address,unsigned char *pBuffer, unsigned short len);
#endif

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
I2C通信协议是一种串行总线协议,它允许多个设备在同一条总线上进行通信。在I2C总线上,每个设备都有一个唯一的地址,可以通过这个地址与其他设备进行通信。下面是I2C通信协议的详细讲解: 1. 总线结构 I2C总线结构包括两根信号线:SCL和SDA。SCL是时钟线,由主设备负责产生;SDA是数据线,用于传输数据。 2. 通信方式 I2C通信协议主要分为两种方式:传输数据和发送命令。在传输数据时,从设备首先向主设备发送一个应答信号(ACK),主设备接收到应答信号后,才会继续发送数据。在发送命令时,主设备向从设备发送命令并等待从设备的应答信号。 3. 传输数据 在I2C总线上传输数据时,每个字节都由8位二进制数字组成。在传输一个字节之前,主设备必须向从设备发送一个起始信号(Start Bit),表示一个新的传输过程开始了。然后主设备会先发送从设备的地址(包括读/写位),然后等待从设备的应答信号。如果从设备存在,并且它的地址与主设备发送的地址匹配,那么它会发送一个应答信号。接下来主设备会开始发送数据,每发送一个字节就等待从设备的应答信号。当主设备发送完最后一个字节后,它会发送一个停止信号(Stop Bit),表示这次传输已经结束了。 4. 发送命令 在I2C总线上发送命令时,主设备首先向从设备发送命令,并等待从设备的应答信号。如果从设备存在,并且它的地址与主设备发送的地址匹配,那么它会发送一个应答信号。接下来主设备可以向从设备发送一个或多个字节的数据,然后等待从设备的应答信号。当主设备完成数据传输后,它会向从设备发送停止信号。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值