模拟IIC

  • IIC协议
  • GPIO配置
  • 根据时序图完成代码
  • 起始与停止信号
  • 等待应答
  • 读写字节
  • 检查设备是否连接

IIC协议

I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、 CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

STM32 CUBEMX 读模拟电压 stm32 模拟iic_引脚

  • 它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯
  • 一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
  • 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。主机及多个通讯从机。

特点:

  • 总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
  • 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
  • 具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。
  • 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。

GPIO配置

STM32的8种引脚模式

void IIC_Init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
 	
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOx, ENABLE);

	GPIO_InitStructure.GPIO_Pin = IIC_SCL_Pin|IIC_SDA_Pin; 
 	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 	GPIO_Init(IIC_GPIO, &GPIO_InitStructure);	  
 	GPIO_SetBits(IIC_GPIO,IIC_SCL_Pin|IIC_SDA_Pin);	
}

IIC的两个引脚模式配置为开漏输出GPIO_Mode_Out_OD,要得到高电平状态需要上拉电阻才行。将多个开漏输出的引脚连接到一条线上时,通过一个上拉电阻,在不增加任何器件的情况下,形成”线与“关系,这就是IIC、SMBus等总线判断总线占用状态的原理。

所谓的"线与"指的是多个信号线直接连接在一起,只有当所有信号全部为高电平时,合在一起的总线为高电平;只要有任意一个或者多个信号为低电平,则总线为低电平。而推挽输出就不行,如果高电平和低电平连在一起,会出现电流倒灌,损坏器件。

根据时序图完成代码

STM32 CUBEMX 读模拟电压 stm32 模拟iic_数据_02


I2C通信本是连贯的,在编程时把它拆分,怎么拆就见仁见智了。在I2C尚未完成通信时,将SCL拉低是为了钳住IIC总线,准备收发数据。不拉为低,你的接下来操作都可能被误认为是起始或终止。

当SCL 为1时,SDA的电平变换会被认为是起始和结束信号,所以通讯未结束一定要拉低SCL!!!!

本项目IIC通讯未结束时,SCL线统一为0。
延时函数

*********************************************************************************************************
*	函 数 名: i2c_Delay
*	功能说明: I2C总线位延迟,最快400KHz
*	形    参:无
*	返 回 值: 无
*********************************************************************************************************
*/
static void i2c_Delay(void)
{
	uint8_t i;

	/* 
	 	下面的时间是通过逻辑分析仪测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
		循环次数为10时,SCL频率 = 205KHz 
		循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
	 	循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
	*/
	for (i = 0; i < 10; i++);
}

起始与停止信号

STM32 CUBEMX 读模拟电压 stm32 模拟iic_数据_03

/*
 * @brief IIC起始信号
 */
void IIC_Start(void)
{
	IIC_SDA_1();//先拉高SDA再拉高SCL,防止出现错误信号
	IIC_SCL_1();
	i2c_Delay();//延时保证SCL高电平时,SDA下降沿
	IIC_SDA_0();
	IIC_SCL_0();//如果不拉低SCL线,那么数据传输过程中的SDA的电平变换就会被认为是起始和结束信号
}

/*
 * @brief IIC停止信号
 */
void IIC_Stop(void)
{
	IIC_SDA_0();//先拉低SDA再拉高SCL,防止出现错误信号
	IIC_SCL_1();
	i2c_Delay();//延时保证SCL高电平时,SDA上升沿沿
	IIC_SDA_1();
	/*这里就不用拉低SCL了,因为IIC通讯已经结束*/
}

等待应答

STM32 CUBEMX 读模拟电压 stm32 模拟iic_引脚_04

在第9个时钟(SCL高电平)时,数据发送端会释放SDA的控制权,由数据接收端控制SDA,若SDA为高电平,表示非应答信号(NACK),低电平表示应答信号(ACK)。
一定要先拉高SDA,再拉高SCL。否者就可能变成起始信号和结束信号了。

void IIC_Wait_Ack(void)
{
	//一定要先拉高SDA,再拉高SCL。否者就可能变成起始信号和结束信号了
	IIC_SDA_1();
	IIC_SCL_1();
	while(IIC_Read_SDA());
	IIC_SCL_0();//IIC通讯没结束,拉低SCL线
}

IIC主机不应答:

void IIC_No_Ack(void)
{
	IIC_SDA_1();
	IIC_SCL_1();
	IIC_SCL(GPIO_PIN_RESET);//通讯未结束,拉低SCL线
}

IIC主机应答:
主机在发出应答信号后一定要释放SDA线(拉高SDA线),否者SDA线处于占用状态无法进行数据传输,线与性质

void IIC_Ack(void)
{
	IIC_SDA_0();
	IIC_SCL_1();
	IIC_SCL_0();//通讯未结束,拉低SCL线
	IIC_SDA_1();//主机释放SDA线
}

读写字节

STM32 CUBEMX 读模拟电压 stm32 模拟iic_数据_05


由时序图可知,字节数据MSB先行,SCL低电平进行数据变换,高电平写入(如果在SCL高电平时进行数据变换,从机会理解成起始或结束信号)。

/*
 * @brief IIC写字节
 * @para  待写入的字节数据
 */
void Write_IIC_Byte(u8 IIC_Byte)
{
	u8 i,data;
	data = IIC_Byte;
	for(i=0;i<8;i++)		
	{
		IIC_SCL_0();
		if(data&0x80)
			IIC_SDA_1();
		else 
			IIC_SDA_0();
		data=data<<1;
		IIC_SCL_1();
	}
	IIC_SCL_0();//通讯未结束,拉低SCL
}

/*
 * @brief IIC读字节
 * @return 返回读取到的一个字节  
 */
u8 Read_IIC_Byte(void)
{
	u8 i,value=0;
	for(i=0;i<8;i++)
	{
		value <<= 1;
		//数据变化
		IIC_SCL_0();
		i2c_Delay();
		//读取数据
		IIC_SCL_1();
		if(IIC_Read_SDA())
			value++;
	}
	IIC_SCL_0();//通讯未结束,拉低SCL
	return value;
}

检查设备是否连接

发送地址呼叫从机,如果有应答就代表连接成功

/*
 * @brief 检测设备是否连接
 */
void Check_IIC_Device(u8 slave_addr)
{
	IIC_Start();
	Write_IIC_Byte((slave_addr<<1)|write_flag);
	IIC_Wait_Ack();
	IIC_Stop();
}

STM32 CUBEMX 读模拟电压 stm32 模拟iic_引脚_06