实战iic协议

概述
I2C(Inter-Integrated Circuit BUS) 集成电路总线,该总线由NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。

经常IIC和SPI接口被认为指定是一种硬件设备,但其实这样的说法是不尽准确的,严格的说他们都是人们所定义的软硬结合体,分为物理层(四线结构)和协议层(主机,从机,时钟极性,时钟相位)。

iic的通信原理
起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件,结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件,要注意起始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态,对起始条件和结束条件的描述如下图所示。

(3)
应答:每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,如图所示。

在这里插入图片描述
实战代码驱动

/*
用GPIO的方式模拟IIC总线这种方法,和硬件连接的GPIO有关
我的设备接到不同的GPIO上,将来就需要配置响应的GPIO
因为我们做的实验有OLED EEPROM SHT30,而这些设备都是连接在了相同的管脚上
所以,在使用时从设备地址一定不要写错
*/

//以下代码是硬件相关的,移植时候需要修改
GPIO_InitTypeDef  GPIO_InitForSCL;//定义了GPIO初始化的结构体类型变量(时钟线)
GPIO_InitTypeDef  GPIO_InitForSDAOut;//定义了GPIO初始化的结构体类型变量(数据线<发送>)
GPIO_InitTypeDef  GPIO_InitForSDAIn;//定义了GPIO初始化的结构体类型变量(数据线<接收>)

#define IIC_SCL					PBOut(6)
#define IIC_SDA					PBOut(7)
#define IIC_SDA_STATUS 	PBIn(7)

void IIC_Init(void)
{			
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	//通过APB2总线使能GPIOB组的时钟
	
	//PB6 SCL	初始化时钟线,把时钟线配制成推挽的输出
  GPIO_InitForSCL.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitForSCL.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitForSCL.GPIO_Speed = GPIO_Speed_50MHz;
	
	//PB7 SDA OUT 初始化数据线,把数据线配制成推挽的输出
	GPIO_InitForSDAOut.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitForSDAOut.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitForSDAOut.GPIO_Speed = GPIO_Speed_50MHz;
	
  //PB7 SDA IN 初始化数据线,把数据线配制成上拉的输入
  GPIO_InitForSDAIn.GPIO_Mode = GPIO_Mode_IPU;
  GPIO_InitForSDAIn.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitForSDAIn.GPIO_Speed = GPIO_Speed_50MHz;
	
  GPIO_Init(GPIOB, &GPIO_InitForSDAOut);//先把数据线初始化成输出的模式
  GPIO_Init(GPIOB, &GPIO_InitForSCL);//初始化时钟线
	
	IIC_SCL = 1;//拉高时钟线
	IIC_SDA = 1;//拉高数据线
}

void IIC_Sda_In(void)//把数据线初始化成输入模式
{
	GPIO_Init(GPIOB, &GPIO_InitForSDAIn);
}

void IIC_Sda_Out(void)//把数据线初始化成输出模式
{
	GPIO_Init(GPIOB, &GPIO_InitForSDAOut);
}
//以上代码是硬件相关的,移植时候需要修改

//以下代码硬件无关,只跟iic协议有关
void IIC_Start(void)//产生开始信号
{
	IIC_Sda_Out();	//把数据线配制成输出模式
	IIC_SDA=1;			//把数据线拉高
	IIC_SCL=1;			//把时钟线拉高
	delay_us(2);		//延时2us
 	IIC_SDA=0;			//把数据线拉低
	delay_us(2);		//延时2us
	IIC_SCL=0;			//把时钟线拉低
}	  

void IIC_Stop(void)//产生结束信号
{
	IIC_Sda_Out();	//把数据线配制成输出模式
	IIC_SCL=0;			//把时钟线拉低
	IIC_SDA=0;			//把数据线拉低
 	delay_us(2);		//延时2us
	IIC_SCL=1;			//把时钟线拉高
	delay_us(1);		//延时1us
	IIC_SDA=1;			//把数据线拉高
	delay_us(2);		//延时2us
}

u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;	//定义了超时变量
	
	IIC_Sda_In();		//把数据线配制成输入模式
	IIC_SCL = 0;		//把时钟线拉低
	delay_us(1);		//延时1us
	IIC_SCL = 1;		//把时钟线拉高
	delay_us(1);		//延时1us
	while(IIC_SDA_STATUS)	//读取数据线的高低电平
	{
		ucErrTime++;				//让超时变量自增
		if(ucErrTime > 250)	//判断是否到了超时
		{
			IIC_Stop();				//产生结束信号
			return 1;					//返回1,代表没有等到ACK
		}
	}
	IIC_SCL = 0;					//把时钟线拉低
	return 0;							//返回0,代表等到了ACK
} 

void IIC_Ack(void)			//产生ACK
{
	IIC_SCL=0;						//把时钟线拉低
	IIC_Sda_Out();				//把数据线配制成输出模式
	IIC_SDA=0;						//把数据线拉低
	delay_us(2);					//延时2us
	IIC_SCL=1;						//把时钟线拉高
	delay_us(2);					//延时2us
	IIC_SCL=0;						//把时钟线拉低
}
    
void IIC_NAck(void)			//不产生ACK
{
	IIC_SCL=0;						//把时钟线拉低
	IIC_Sda_Out();				//把数据线配制成输出模式
	IIC_SDA=1;						//把数据线拉高
	delay_us(2);					//延时2us
	IIC_SCL=1;						//把时钟线拉高
	delay_us(2);					//延时2us
	IIC_SCL=0;						//把时钟线拉低
}					 				     

void IIC_Send_Byte(u8 txd)
{
  u8 t;//定义了循环变量t

	IIC_Sda_Out();//把数据线配制成输出模式

  for(t = 0;t < 8; t++)//进入8次循环
	{
		IIC_SCL = 0;					//把时钟线拉低
		delay_us(1);					//延时1us
    IIC_SDA = (txd & 0x80) >> 7;//把最高位的数据放到数据线上
    txd <<= 1;						//让txd保存的数据同步左移一位
		delay_us(1);					//延时1us
		IIC_SCL=1;						//把时钟线拉高
		delay_us(2);					//延时2us
   }	
	IIC_SCL = 0;						//把竖中线拉低
} 	    

u8 IIC_Recv_Byte(u8 ack)
{
	u8 i, receive = 0;//定义u8类型变量
	
	IIC_Sda_In();						//把数据线配制成输入模式
  
	for(i = 0; i < 8; i++)
	{
    IIC_SCL = 0;					//把时钟线拉低 
    delay_us(2);					//延时2us
		IIC_SCL=1;						//把时钟线拉高
		delay_us(2);					//延时2us
    receive <<= 1;				//把receive变量保存的数据同步左移一位
    if(IIC_SDA_STATUS)		//判断从数据线中读出的电平的高低
			receive |= 1;				//如果是高电平,把1存储到receive的最低位
  }					 
	IIC_SCL = 0;						//把时钟线拉低
  if(!ack)								//判断形参的值是否要产生ACK
		IIC_NAck();						//如果形参的值是0就不产生ACK
  else
		IIC_Ack();						//如果形参的值是1就产生ACK
  return receive;
}

总结:学习iic协议,写出相应的代码,提高自己的编程能力,不断学习驱动的编程方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值