IIC协议超详细解释

以下内容,将解释以下几个问题
1.iic协议是什么?
2.iic协议用来干什么?
3.iic协议的通信过程?
1.iic协议是什么?
iic,即i²c,全称 inter-integrated circuit,字面上的意思是集成电路之间,它其实是i²c bus简称,所以中文应该叫 集成电路总线 ,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。(百度百科)
2.iic协议用来干什么?
简单地说,iic就是一种通信协议,是为了能让主板,或嵌入式系统等与其他外设模块进行通信而进行开发的。玩过stm32开发板的同学都知道,对于一块stm32核心开发板而言,要想使用其他的外设模块,就肯定要经过接线,写代码,烧录运行的这个过程。
其实这个过程,就是一个stm32与外设模块通信的过程。接线,就是搭建通信的线路。写代码,就是制定通信的传输协议。烧录运行,就是正式的通信过程。只不过有的模块通信过程很简单,大家感觉不出来。
外设和芯片间的通信可以形象地比喻成两个人讲话:
你说的别人得能听懂:双方约定信号的协议
你的语速别人得能接受:双方满足时序要求
但是随着科技的发展,模块越来越多,总不可能,每个模块都要制定一种通信协议,这样不现实。所以,总要有一些代表性的协议能够适应大部分的模块的通信。iic这是这样一种协议,一个iic总线上,可以挂载多个外接设备。
常用的串行通信协议有:
①uart串口通信
②iic协议
③spi协议
④usb协议(很难)
常用的并行通信协议有:
①8080
②6800
3 .iic协议的通信过程( 此处重点 )
接线:要搭建iic的通信线路,出除去电源之外,还需要两条线,分别是sda和sclk
sda:数据信号线,用于传输数据
sclk:时钟信号线,用于产生时钟频率,控制时序,实现协议过程
由此可以看出,由于是单总线进行数据传输,所以iic协议是半双工的。
搭建好线路之后,就要进行具体的通信了。
要通信,总得先发个开始信号吧。就像你要和别人说话,总要先喊他一声一样。如下图所示,协议规定,当sclk时钟信号一直处于高电平状态时,sda线由高电平跳变到低电平这个动作,表示起始信号。注意此时就算sda数据线的电平跳变完,sclk线依然是高电平哦。当连接在iic总线上的外设模块检测到这个信号时,就知道数据要开始传输了。对于结束信号同理,协议规定,当sclk时钟信号一直处于高电平状态时,sda线由低电平跳变到高电平这个动作,表示结束信号。
在明白如何开始之后,就要开始进行数据的传输了。
协议规定,在数据的传输过程中,sclk为高电平时,外设模块开始采集sda数据线上的数据,此时要求sda数据线上的电平状态必须稳定(不然鬼知道这一位数据是0还是1),当sclk为低电平时才允许sda线上的数据跳变成另外一种状态。
以下以传输1个bit的数据为例,如下图所示:
现在,我想传输1bit数据,该位数据为1,从上文知道,我们在发完开始信号之后,此时sda数据线的电平状态为低电平,sclk信号依然是高电平。难道这个时候外设就要开始读取数据了吗?
这显然不是的,从发完开始信号到真正的数据传输之间,会有一段缓冲时间,让我们去准备数据,在准备数据阶段,先将sclk信号拉低一段时间,在这期间将sda数据线拉高一段时间(即数据1),然后再将sclk信号拉高,此时这个时钟信号的高电平被外设检测到的话,外设就知道要读取数据了,从而sda上的数据就会被外设读到了。依次类推,传输下一位数据。
一般,传输完1个字节(即8bit,高位先入)的数据,才算做一次完整的数据传输,因为对存储单元而言,最小的单位便是字节。那如何确定,每次都完好地传输了一个字节呢?
这种情况就需要外设来做出回应了,就像打电话一样,如果对方不在,或不想听,说再多也没用啊。那么外设如何做出回应呢?
协议规定,主机每传完一个字节的数据即外设每收到一个字节的数据,外设就要在第9个时钟脉冲到来的时候,将sda数据线拉低进行应答(ack),且必须是稳定的低电平,表示已经收到了一个字节的数据,拉高表示不进行应答(nack;注意这里是外设将sda数据线拉低,不是主机了哦。如下图所示:
所以在主机传完一个字节的数据之后,就应该释放总线(协议规定,当sda和sclk同时为高时,表示空闲状态)然后把sda数据线连接的io口从输出模式转换成输入模式,这样才能拿到sda数据线上的应答信号。这样,一个字节的数据就从主机到外设传输完毕了。
既然iic是双向通信的,那主机肯定也是需要从外设读取数据的,那这个读取的过程又是怎么实现的呢?毕竟外设对于我们而言是不能直接操作的,我们能操作的只有stm32。我们知道,一个iic总线上,可以挂载多个设备,那么stm32如何确定是哪个外设正在跟我进行通信呢。对于此,那些生产外设模块的厂商们就约定,要是这个设备使用iic协议进行通信,那么就要给这个设备指定一个器件地址,以供芯片访问。这个器件地址会在你购买其模块的时候在使用手册上注明。所以,要跟哪个模块通信,就一定要通过查阅其使用手册,找到它的器件地址。
所以,在上文所述的最开始的一个字节的数据传输过程中,这一个数据往往是器件地址。这样,对应的外设才知道,是要跟我进行通信。读取数据,也是同理,要想从外设中读取到数据,主机要明确三点:从哪个外设中的哪个地方读取数据,读取到的数据要存到哪里。
所以主机,在开始读数据之前,主机必须要先给外设发器件地址,数据所在的地址,外设才会知道你要从该地址读取数据,从而把数据通过sda线传出来。至于具体的每个字节的传输过程,和上面所讲的从主机到外设的过程差不多,只不过反了一个反向而已,并且主机的等待应答变成了主动应答。
/* 设置sda总线为输出模式 参数值:null 返回值:null*/void iic_setsdamode_out(){ gpio_inittypedef gpio_iic; rcc_ahb1periphclockcmd(rcc_ahb1periph_gpioe, enable); gpio_iic.gpio_mode = gpio_mode_out; //输出 gpio_iic.gpio_otype = gpio_otype_pp; //推挽 gpio_iic.gpio_pin = gpio_pin_15; //引脚 gpio_iic.gpio_pupd = gpio_pupd_up; //上拉 gpio_iic.gpio_speed = gpio_speed_25mhz; //输出 gpio_init(gpioe, &gpio_iic);}/* 设置sda总线为输入模式 参数值:null 返回值:null*/void iic_setsdamode_in(){ gpio_inittypedef gpio_iic; rcc_ahb1periphclockcmd(rcc_ahb1periph_gpioe, enable); gpio_iic.gpio_mode = gpio_mode_in; //输出 gpio_iic.gpio_pin = gpio_pin_15; //引脚 gpio_iic.gpio_pupd = gpio_pupd_up; //上拉 gpio_init(gpioe, &gpio_iic);}/* iic开始信号 参数值:null 返回值:null*/void iic_start(){ iic_setsdamode_out(); iic_sda_out(1); //总线释放状态 iic_scl_out(1); delay_us(5); iic_sda_out(0); //sda跳变为低电平 delay_us(5); iic_scl_out(0); delay_us(5);}/* iic停止信号 参数值:null 返回值:null*/void iic_stop(){ iic_setsdamode_out(); iic_sda_out(0); iic_scl_out(0); delay_us(5); iic_scl_out(1); //sda跳变为高电平 delay_us(5); iic_sda_out(1); delay_us(5);}/* 主机写入数据到外设中 参数值: data 要写入的一个字节 返回值:null*/void iic_writebyte(u8 data){ iic_setsdamode_out(); iic_scl_out(0); //只有时钟线拉低,sda上的数据才允许写入 delay_us(5); //将数据一位一位的发出去 for(int i =0;i<8;i++) { if(data&(0x1<<(7-i))) //高位先入 { iic_sda_out(1); } else { iic_sda_out(0); } iic_scl_out(1); //让外设读取数据 delay_us(5); iic_scl_out(0); //重新拉低,准备写入下一位数据 delay_us(5); }}/* 主机从外设中读取一个字节的数据 参数值:null 返回值:null*/u8 iic_readbyte(){ u8 data = 0; iic_setsdamode_in(); iic_scl_out(0); //先拉低,为读取数据做准备 delay_us(5); for(int i=0;i<8;i++) { iic_scl_out(1); // scl为高期间才可以读取数据 delay_us(5); if(iic_sda_in) { data|=(0x01<<(7-i)); }else{ data &= ~(0x1<<(7-i)); } iic_scl_out(0); delay_us(5); } return data;}/* 主机等待应答 参数值:null 返回值:ack 0 应答 1 不应答*/u8 iic_waitack(){ u8 ack =0; iic_setsdamode_in(); iic_scl_out(0); //准备时序 delay_us(5); iic_scl_out(1); delay_us(5); if(iic_sda_in) { ack =1; } else { ack =0; } iic_scl_out(0); //拉低,表示应答完成 delay_us(5); return ack;}/* 主机主动应答 参数值: ack 0 应答 1 不应答 返回值:null*/void iic_ack(u8 ack){ iic_setsdamode_out(); iic_scl_out(0); delay_us(5); if(ack) { iic_sda_out(1); } else { iic_scl_out(0); } iic_scl_out(1); delay_us(5); iic_scl_out(0); delay_us(5); }


玄学还是科学?摩尔定律了解一下
人事变动!微软中国区迎来换帅
蓝色光标虚拟人苏小妹舞台首秀完美收官,跨次元国风受追捧
实体关系联合抽取取得SOTA的三种方法
关于嵌入式STT-MRAM效应与流致反转的分析
IIC协议超详细解释
户外led屏安装步骤及注意事项
回顾英特尔物联网投入中国市场,加速产业合作升级的相关介绍
PLC在运行中出现故障的常见原因及处理方法
EDA探索之MOSFET收缩,Happy Scaling Era
用于低内存 IoT 设备的神经网络
美国商品期货交易委员会CFTC对加密货币采取了善待彼此的态度
android在手机上的文件系统框架的阐述
欧菲光成立二十周年 多次转换赛道成龙头
三元锂电池和磷酸铁锂电池不一定得你死我活
锅炉吹灰器是什么,有哪些作用
A游不错让你足不出户游天下
土壤成分检测仪器的工作原理
QS的固态电池技术如何 目前处于哪一阶段
整流、滤波电路—示波器的使用