ESP8266学习历程(5)——IIC

本文历程使用ESP8266使用IIC协议驱动OLED显示。
IIC 总线是一种串行数据总线,总共有4跟线,其中有二根信号线,一根是双向的数据线SDA,另一根是时钟线SCL,在IIC总线上可以挂载多个IIC设备。 IIC设备(绝大多数)里有个固化的地址,只有在两条线上传输的值等于IIC设备的固化地址时,其才会作出响应。通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。

IIC也是一种比较常用的协议,在ESP8266提供的SDK中也有IIC的官方接口,我们可以使用他的接口也可以自己写一个,用起来会比较顺手嘛;本文就官方提供的IIC编写思路,自行编写自己的IIC协议,官方提供的自己去看就行。

很多初学者会觉得这种协议相关的东西会学起来比较晦涩,其实不然,看着时序图,我们可以很容易的发现,IIC的时序动作其实就这几种:起始信号、停止信号、应答、非应答、就完了,然后进行动作的搭配,完整的通讯时序就出来了,根据前几篇博客我们学会了ESP8266的GPIO的操作,那么对于IIC只剩下理解时序了。

IIC协议网上有很多大佬介绍得特别详细,这里做个简单的说明:

IIC协议

个人认为,看懂时序就学会了IIC,对于IIC协议也就只有那个动作嘛,搞懂了就到了搭积木的撸代码过程;

数据的有效性规则

IIC总线进行数据传送时,时钟信号SCL为高电平期间,数据线SDA上的数据必须保持稳定(从机读取主机发送过来的数据,是读取在SCL为高电平时SDA的状态),只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。

即:数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定,这个期间的SDA数据才算有效。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZpqClPd-1585639245325)(E:\UtilityRoom\GraduationProject\ESP8266开发笔记\IIC01.png)]

空闲状态

I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。

此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

起始信号与停止信号

在这里插入图片描述

起始信号:当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。

停止信号:当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。

应答信号

在这里插入图片描述

主机向从机发送数据,从机每接收到一个字节的数据都会向主机回发一个应答,主机通过接收应答信号来判断当前字节是否成功发送与成功接收,但是有一点需要注意的是,从机发送的应答信号所需的始终依然由主机提供,所以应答信号紧跟在一个字节数据的8个时钟脉冲后的第9个时钟脉冲期间;

应答信号一般出现在:

  1. 发送端发送了一个字节数据后,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号ACK,接收失败则返回NACK。

  2. 如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信号

    应答信号为低电平时,规定为有效应答位(ACK简称应答位),表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功,如上图即为应答,非应答即为高电平。

IIC写数据

在这里插入图片描述
如图是AT24Cxx数据写入的时序图,图中是完整的一个字节数据的写入,包括了IIC从机设备的地址以及是写命令还是写数据,本问不讲具体实现,只讲最底层软件IIC的编写,只负责一个字节数据的写入部分,方便后续的移植啊通用啥的。

根据前面数据有效性的介绍,SDA在SCL为低电平时可变,在SCL为高到低跳变前保持稳定即可;

void mIIC_Send_Byte(uint8_t byte)
{
	int8_t i=0;

	IIC_SCL_OUT(0);							//拉低时钟线
    IIC_SDA_OUT(0);							//拉低数据线
	i2c_master_wait(i2c_delay_times);		 //延时5us
	
	for(i=7; i>=0; i--)
	{
		if(byte & (1<<i))					//根据需要传输的数据,改变数据线电平
			IIC_SDA_OUT(1);					
		else
			IIC_SDA_OUT(0);
		
		i2c_master_wait(i2c_delay_times);	  //稳定5us
		IIC_SCL_OUT(1);						//拉高时钟线
		i2c_master_wait(i2c_delay_times);	  //稳定5us
		IIC_SCL_OUT(0);						//时钟线高到低跳变,完成1bit的发送
		i2c_master_wait(i2c_delay_times);		
	}
}

IIC读数据

在这里插入图片描述

一样只需编写读取一个字节数据的代码即可,同时前面应答信号有说明读取完一个字节后,需要发送一个NACK,告诉发送端停止发送。

uint8_t mIIC_Read_Byte(uint8_t ask)			//参数可设置成是否需要发送应答信号
{
	int8_t i = 0;
	uint8_t byte = 0;
	
	mIIC_SDA_Mode_Change(SDA_MODE_READ);	//SDA切换到输入模式
	i2c_master_wait(i2c_delay_times);		
	
	for(i = 7; i >=0 ; i--)
	{
		IIC_SCL_OUT(1);                     //时钟线为高时,采集SDA数据电平
		i2c_master_wait(i2c_delay_times);		
		
		if(IIC_SDA_IN())
			byte |= 1<<i;

		IIC_SCL_OUT(0);
		i2c_master_wait(i2c_delay_times);		
	}
	
	mIIC_SDA_Mode_Change(SDA_MODE_WRITE);	//SDA切换到输出模式
	if(ask)
		mIIC_Send_Ack();				  //发送应答ACK
	else
		mIIC_Send_NAck();				  //发送非应答NACK
	
	return byte;
}

效果如图:(完成底层的IIC协议后,可以很方便的移植IIC协议的设备,具体代码见下文的连接)
在这里插入图片描述

这里有个小坑需要注意

就是SDK中的driver/gpio.h里面,gpio_config()这个函数中,带有日志打印的效果,代码如下:

ESP_LOGI(GPIO_TAG, "GPIO[%d]| InputEn: %d| OutputEn: %d| OpenDrain: %d| Pullup: %d| Pulldown: %d| Intr:%d ", io_num, input_en, output_en, od_en, pu_en, pd_en, gpio_cfg->intr_type);

个人猜测,这段代码执行周期太长,容易导致软件IIC的时序错乱,如果使用自己写的软件IIC,像本文的测试是在OLED,如果不注释会出现雪花,不能正常显示,注释之后就好了。

以上截图来源于AT24CXX的数据手册,用作讲解说明参考

具体完整代码见GITHUB


我的GITHUB

我的个人博客

CSDN

发布了32 篇原创文章 · 获赞 1 · 访问量 244

猜你喜欢

转载自blog.csdn.net/qq_41714908/article/details/105223561