STM32学习笔记---IIC协议

IIC介绍

IIC即Inter-IntegratedCircuit(集成电路总线),是一种多向控制总线,由飞利浦半导体公司在八十年代初设计,主要是用来连接整体电路(ICS)。在IIC中,多个设备可以连接到同一总线结构下,同时每个设备都可以作为实施数据传输的控制源,这种方式简化了信号传输总线。

I2C串行总线一般有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上。IIC时钟频率: 不高于400K
STM32学习笔记---IIC协议_第1张图片
上拉电阻一般在4.7k~10k之间

通信方式简介

1、主机发送开始信号
2、主机发送地址数据
3、从机接收后与自身地址匹配,若符合则发送ACK响应表示建立连接,否则不响应
4、主机收到ACK后可以发送数据
5、数据发送完成后,从机(接收方)发送ACK表示接收成功
6、数据发送完成,主机发送结束信号

信号介绍

总线信号 :
  SDA :串行数据线
  SCL :串行时钟

总线空闲状态 :
  SDA :高电平
  SCL :高电平
  
起始位: SCL为高电平期间 SDA出现下降沿
终止位: SCL为高电平期间 SDA出现上升沿
应答ACK: 当IIC主机(不一定是发送端还是接受端)将8位数据或命令传出后,会将SDA信号设置为输入,等待从机应答(等待SDA由高电平拉为低电平)若从机正确应答,表明数据或者命令传输成功,否则传输失败,注意,应答信号是数据接收方发送给数据发送方的。
STM32学习笔记---IIC协议_第2张图片
数据传输 : SDA的数据在SCL高电平期间被写入从机。所以SDA的数据变化要发生在SCL低电平期间。
STM32学习笔记---IIC协议_第3张图片
IIC器件地址: 每一个IIC器件都有一个器件地址,有的器件地址在出厂时地址就设定好了,用户不可以更改,比如OV7670的地址为0x42。有的器件例如EEPROM,前四个地址已经确定为1010,后三个地址是由硬件链接确定的,所以一个IIC总线最多能连8个EEPROM芯片。

数据传输介绍

STM32学习笔记---IIC协议_第4张图片
开始信号传输之后,七位地址代表器件地址,第八位代表读或者写,0为写,1代表读

写入过程:
进行写操作时,首先发送该器件的7位地址码和写方向位”0”(共8位,即一个字节),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为响应,单片机收到应答后就可以传送数据了。传送数据时,单片机首先发送一个字节的被写入存储器的首地址,收到存储器器件的应答后,单片机就逐个发送数据字节,但每发送一个字节后都要等待应答。从机在接收到每一个数据字节地址后自动加1。装载字节数超过从机的“一次装载字节数”时,数据地址将“上卷”,前面的数据将被覆盖。
两次写之间要有一个10ms的间隔

字节写:
STM32学习笔记---IIC协议_第5张图片
页写:
在这里插入图片描述

读入过程:
单片机先发送该器件的7位地址码和写方向位“0”(“伪写”),发送完后释放SDA线并在SCL线上产生第9个时钟信号。被选中的存储器器件在确认是自己的地址后,在SDA线上产生一个应答信号作为回应。

然后,再发一个字节的要读出器件的存储区的首地址,收到应答后,单片机要重复一次起始信号并发出器件地址和读方向位(“1”),收到器件应答后就可以读出数据字节,每读出一个字节,单片机都要回复应答信号。当最后一个字节数据读完后,单片机应返回以“非应答”(高电平),并发出终止信号以结束读出操作。
  
当前地址读:
STM32学习笔记---IIC协议_第6张图片
字节读:
 STM32学习笔记---IIC协议_第7张图片
 页读:
 STM32学习笔记---IIC协议_第8张图片
以上时序的寄存器地址都是8位的,下面的是8位设备地址,16位寄存器地址,16位数据的传输时序

STM32学习笔记---IIC协议_第9张图片
STM32学习笔记---IIC协议_第10张图片
16位设备地址的读写时序没找到,但是有一个读的程序可以参看,其实就是发送设备地址时,先发高位地址,再发低位地址,之后再进行其他操作

u8 IIC_Read(u8 addr,u16 iaddr)
{
	u8 temp;
	IIC_Start();
	IIC_SendByte(addr);//写模式
	IIC_Wait_Ack();
	IIC_SendByte((u8)(iaddr>> 8));//高位地址
	IIC_Wait_Ack();
	IIC_SendByte((u8)aiaddrdr);//低位地址
	IIC_Wait_Ack();
	
	IIC_Start();
	IIC_SendByte(addr+1);//进入读模式
	IIC_Wait_Ack();
	
	temp = IIC_ReadByte();
	IIC_NAck();//发送nACK
	
	IIC_Stop();
	return temp;
	}

IIC程序


//初始化IIC接口
void IIC_Init(void)
{					     
 	RCC->APB2ENR|=1<<3;		//使能外设IO时钟							 
	GPIOB->CRL&=0X00FFFFFF;	//PB6/7 推挽输出
	GPIOB->CRL|=0X33000000;	   
	GPIOB->ODR|=3<<6;     	//PB6,7 输出高
}
//产生起始信号
void IIC_Start(void)
{
	SDA_OUT();//SDA引脚设置为输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//钳住IIC总线,准备发送或接收数据
}	  
//产生停止信号
void IIC_Stop(void)
{
	SDA_OUT();////SDA引脚设置为输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//发送IIC总线结束信号
	delay_us(4);							   	
}
//等待应答信号
//返回值:1,接收应答失败
//       0,接收应答成功
u8 IIC_Wait_Ack(void)
{
	u8 ucErrTime=0;
	SDA_IN();//SDA引脚设置为输入
	IIC_SDA=1;delay_us(1);	   
	IIC_SCL=1;delay_us(1);	 
	while(READ_SDA)
	{
		ucErrTime++;
		if(ucErrTime>250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL=0;//时钟输出0
	return 0;  
} 
//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();//SDA引脚设置为输入
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//产生NACK应答  
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();//SDA引脚设置为输入
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}					 				     
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答	  
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT();//SDA引脚设置为输出  
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	
		delay_us(2);
    }	 
} 	    
//读一个字节
//ack=1时发送ACK信号
//ack=0时发送NACK信号
u8 IIC_Read_Byte(unsigned char ack)
{
	unsigned char i,receive=0;
	SDA_IN();//SDA引脚设置为输入
    for(i=0;i<8;i++ )
	{
        IIC_SCL=0; 
        delay_us(2);
		IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
		delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送NACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}
//读目标器件的寄存器数据,可单读可连续读
//addr:器件地址
//regist:寄存器地址
//len:读取数据长度,单位 字节
void IIC_ReadOneByte(u8 addr,u16 regist,u8 len)
{				  
		u8 i=0;		  	    																 
    IIC_Start();  

		IIC_Send_Byte(addr);//发送器件地址+写命令0
		IIC_Wait_Ack();
		IIC_Send_Byte(regist);    
		IIC_Wait_Ack(); 
   
		IIC_Start();  	 	   
		IIC_Send_Byte(addr+1);//发送器件地址+读命令1
		IIC_Wait_Ack();	 
		while(len)
	{
		if(len==1)buf[i]=IIC_Read_Byte(0);//读数据,发送NACK
		else buf[i]=IIC_Read_Byte(1);//读数据,发送ACK,可以连读
		len--;
		i++; 
	}    	   
    IIC_Stop();
}
//写一个字节
//addr:器件地址
//regist:寄存器地址
//data:数据
void IIC_WriteOneByte(u8 addr,u8 regist,u8 data)
{ 
	IIC_Start();  
	IIC_Send_Byte(addr);//发送器件地址+写命令0
	IIC_Wait_Ack();
	
	IIC_Send_Byte(regist);   
	IIC_Wait_Ack(); 
	
	IIC_Send_Byte(data);
	IIC_Wait_Ack(); 
	
	IIC_Stop(); 	 
}
//写多个字节
//addr:器件地址
//regist:寄存器地址
//len:写数据长度,单位 字节
//buf:数据区
//返回值:0,正常
//      其他,错误
u8 IIC_WritelenByte(u8 addr,u8 regist,u8 len,u8 *buf)
{
	u8 i; 
 	IIC_Start();  
	IIC_Send_Byte(addr);//发送器件地址+写命令0
	IIC_Wait_Ack();
	
	IIC_Send_Byte(regist);
	IIC_Wait_Ack(); 
	
	for(i=0;i<len;i++)
	{
			IIC_Send_Byte(buf[i]);//发送数据
	
		if(IIC_Wait_Ack())		//等待ACK
		{
			IIC_Stop();	 
			return 1;		 
		}		
	}    
  	IIC_Stop();	 
	return 0;	
} 

你可能感兴趣的:(STM32笔记)