stm32 - IIC

STM32-IIC

1. 基本概念

半双工同步通信的串行通信接口


2. 引脚说明

SDA

SCK/SCL


IIC 总线只需要两根引脚就可以实现通信,一根是数据线(SDA Serial Data),
另一根是时钟线(SCL Serial Clock)

在空闲状态下所有器件的时钟线(SCL)和数据线(SDA)都被总线的上拉电阻拉高,这样就可以把SDA引脚和SCL引脚设置为开漏模式即可,好处是防止短路。

思考:如果IIC总线上挂载了多个外围器件,如何与某一个器件进行单独通信?
器件地址

3. 传输方向

1. 在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。


2. 如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送。


3. 如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下,主机负责产生定时时钟和终止数据传送。

4. 通信过程


image

1. 空闲状态

image

点击查看代码
//3.空闲状态下SDA和SCL都为高电平
	IIC_SCL_WRITE(1);
	IIC_SDA_WRITE(1);

指的是不传输任何数据的时候就被称为空闲状态,IIC总线规定SDA数据线和SCL时钟线在不传输数据的时候都应该设置高电平,表示空闲。

2. 开始信号

image

点击查看代码
//IIC总线开始信号
void IIC_StartSignal(void)
{
	//1.设置SDA引脚为输出模式
	IIC_SDAOutputMode();
	
	//2.确保SDA和SCL为高电平
	IIC_SCL_WRITE(1);
	IIC_SDA_WRITE(1);
	Delay_us(5);            //提高程序可靠性
    
	//3.把SDA引脚拉低
	IIC_SDA_WRITE(0);
	Delay_us(5);   // IIC总线的通信速率为100KHZ  1000000us = 100000HZ  10us = 1HZ 
	
	//4.把SCL引脚拉低,表示准备通信
	IIC_SCL_WRITE(0);
}

开始信号由主机发出,表示打算和所有的从器件进行通信,IIC总线规定在SCL时钟线保持高电平期间,把SDA数据线拉低,表示开始信号。

3. 数据发送


在主机发送开始信号后,就可以发送数据或者地址,IIC总线规定数据的收发都是MSB(高位先出),由于只有一个数据线,所以IIC采用串行方式把数据的每个bit位发出去。


image
image

点击查看代码
//主机发送数据(从机读取数据)
//IIC的发送字节
void IIC_SendByte(uint8_t Byte)   
{
	uint8_t i = 0;
	//1.设置SDA引脚为输出模式
	IIC_SDAConfig(GPIO_Mode_OUT);

	//2.把SCL引脚电平拉低,此时主机准备数据
	SCL_SET(0);
	delay_us(5);

	//3.循环发送8bit,遵循MSB高位先出
	for(i=0;i<8;i++)
	{
		//4.判断待发送的字节的最高位
		if( Byte & 0x80 )
		{
			SDA_SET(1);
		}
		else
			SDA_SET(0);

		Byte <<= 1;
		delay_us(5);

		//5.把SCL电平拉高,此时从机读取bit
		SCL_SET(1);
		delay_us(5);

		//6.把SCL引脚电平拉低,此时主机准备下一个bit
		SCL_SET(0);
		delay_us(5);
	}
}


由于SCL提供的脉冲周期是有规律的,所以IIC总线规定只能在SCL脉冲周期的高电平期间进行数据的读取或者写入,在SCL脉冲周期的低电平期间可以进行数据的修改。

4. 应答信号


IIC总线增加了应答机制,在主机发送一个字节数据之后,从机在第9个脉冲周期进行应答,如果SDA为0,则表示应答,如果SDA=1,则表示无应答,如果从机没有应答,则主机应该发送停止信号,表示停止通信。

image

点击查看代码
//主机等待从机应答  返回0 说明从机应答   返回1 说明从机没应答
//判断从机是否应答
bool  IIC_IsSlaveACK(void)
{
	bool ack;
	
	//1.设置SDA引脚为输入模式
	IIC_SDAConfig(GPIO_Mode_IN);

	//2.把SCL引脚电平拉低,此时为第9个脉冲的低电平,从机准备bit
	SCL_SET(0);
	delay_us(5);

	//3.把SCL引脚电平拉高,此时为第9个脉冲的高电平,主机读取状态
	SCL_SET(1);
	delay_us(5);

	//4.主机读取状态  1 表示未应答  0  表示已应答
	if(SDA_READ)
		ack = false;
	else
		ack = true;
	
	//5.把SCL引脚电平拉低
	SCL_SET(0);
	delay_us(5);
	
	return ack;
}


//从机发送数据,主机接收数据并发送应答信号
//ack=1 表示不应答  ack=0 表示要应答
void IIC_MasterACK(uint8_t ack)
{
	//1.设置SDA引脚为输出模式
	IIC_SDAConfig(GPIO_Mode_OUT);

	//2.把SCL引脚电平拉低,此时主机准备
	SCL_SET(0);
	delay_us(5);

	//3.判断ack的状态,从而对SDA操作
	if(ack)
		SDA_SET(1);
	else
		SDA_SET(0);

	delay_us(5);

	//4.把SCL电平拉高,此时从机读取bit
	SCL_SET(1);
	delay_us(5);
	
	//5.把SCL引脚电平拉低
	SCL_SET(0);
	delay_us(5);
}

5. 数据接收


在主机发送开始信号后,就可以发送数据或者地址,IIC总线规定数据的收发都是MSB(高位先出),由于只有一个数据线,所以IIC采用串行方式把数据的每个bit位发出去。
image

点击查看代码
//主机读取数据(从机发送数据)
//IIC读取字节
uint8_t IIC_ReadByte(void)
{
	uint8_t i = 0,data = 0;

	//1.设置SDA引脚为输入模式
	IIC_SDAConfig(GPIO_Mode_IN);

	//2.把SCL引脚电平拉低,此时从机准备数据
	SCL_SET(0);
	delay_us(5);

	//3.循环读取8bit,遵循MSB高位先出
	for (i = 0; i < 8; ++i)
	{
		//4.把SCL电平拉高,此时主机读取bit
		SCL_SET(1);
		delay_us(5);

		//5.主机读取bit
		data <<= 1;
		data |= SDA_READ;
		//delay_us(5);

		//6.把SCL引脚电平拉低,此时从机准备下一个bit数据
		SCL_SET(0);
		delay_us(5);
	}

	//7.返回结果
	return data;
}

由于SCL提供的脉冲周期是有规律的,所以IIC总线规定只能在SCL脉冲周期的高电平期间进行数据的读取或者写入,在SCL脉冲周期的低电平期间可以进行数据的修改。

6. 停止信号


停止信号由主机发出,表示不打算和从器件继续通信,IIC总线规定在SCL时钟线保持高电平期间,把SDA数据线拉高,表示停止信号。
image

点击查看代码
//IIC总线停止信号
void IIC_StopSignal(void)
{
	//1.设置SDA引脚为输出模式
	IIC_SDAOutputMode();

	//2.确保SDA和SCL为低电平
	IIC_SCL_WRITE(0);
	IIC_SDA_WRITE(0);
	
	//4.把SCL引脚拉高
	IIC_SCL_WRITE(1);
	Delay_us(5);
	
	//5.把SDA引脚拉高
	IIC_SDA_WRITE(1);
	Delay_us(5);  //确保SDA的电平状态可以被其他从器件检测到
}

5. 集合应用举例

点击查看往AT24C02字节写入代码

//AT24C02字节写入的指令
bool AT24C02_ByteWrite(uint8_t Addr,uint8_t Byte)
{
	//1.开始信号
	IIC_Start();
	
	//2.器件地址
	IIC_SendByte(0xA0);
	
	//3.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
		  return false;
	}
	
	//4.存储地址
	IIC_SendByte(Addr);
	
	//5.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Memory Address Error\r\n");
		  return false;
	}
	//6.发送数据
	IIC_SendByte(Byte);
	
	//7.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Data Error\r\n");
		  return false;
	}
	//8.停止信号
	IIC_Stop();
	
	//9.延时5ms
	delay_ms(5);
	
	return true;
}

点击查看AT24C02的当前地址读代码

//AT24C02的当前地址读指令
uint8_t  AT24C02_CurrentAddressRead(void)
{
	uint8_t recv = 0;
	
	//1.开始信号
	IIC_Start();
	
	//2.器件地址
	IIC_SendByte(0xA1);
	
	//3.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
		  return false;
	}
	
	//4.读取字节
	recv = IIC_ReadByte();
	
	//5.发送应答
	IIC_MasterACK(0x01);
	
	//6.停止信号
	IIC_Stop();
	
	return recv;
}

posted @ 2024-07-18 19:14  shishusheng  阅读(27)  评论(0编辑  收藏  举报