IIC通讯程序
1、I2C总线简介
I2C总线是一种由PHILIPS公司开发的两线串行通讯总线,用于连接为控制器及其外围设备。
I2C串行通讯总线由两条线组成:
时钟线SCL。
数据线SDA。
时钟线SCL用来同步数据的传输,数据线SDA用来传输或读取数据。
I2C总线通信设备之间常用连接方式如下:
总线一般是指多个设备共用的信号线,比如上图中的SCL总线和SDA总线,这两个总线组成了I2C通讯总线,可以在这两个总线上挂载多个设备。每一个连接到I2C总线上的设备都有一个独立设备地址,主机通过设备地址识别响应的设备并与之通信。
IC2总线一般需要使用上拉电阻接到电源端,这是因为I2C的设备的内部结构一般是开漏的,在平常状态时为高组态,状态时不确定的,通过上拉电阻将其上拉到高电平,使用这种方式也可以提高I2C总线的驱动能力。
I2C总线有三种传输模式:
标准模式:通讯速率为100Kbit/s。
快速模式:通讯速率为400Kbit/s。
高速模式:通讯速率为3.4Mbit/s。
一般使用标准或快速这两个模式进行通讯,这是应该目前大多数I2C设备并不支持高速模式,具体使用哪种模式可以根据I2C的设备的规格资料进行查询。
2、I2C通讯协议
I2C通讯协议包含起始信号、设备地址和读写信号、读或写数据信息、响应或非响应信号、结束信号。
起始信号:
起始信号由主机产生,主机通过在SCL为高电平的时候将SDA从输出高电平变为输出低电平来触发一个I2C的起始信号,如下图:
主机在产生完起始信号之后,通过拉低SCL时钟线使总线处于空闲状态。主机产生起始信号的程序如下:
void I2C_START(void)
{
I2C_SDA();
I2C_SCL();
I2C_DELAY();
I2C_SDA();
I2C_DELAY();
I2C_SCL();
}
程序首先将SDA和SCL输出高电平,然后通过给SDA输出低电平触发起始信号,最后拉低SCL使总线处于空闲状态(也有一种说法是钳住总线)。
停止信号:
当不需要再进行I2C通讯时,主机产生一个停止信号来结束I2C通讯。主机通过在SCL为高电平时将SDA从输出低电平变为输出高电平来触发一个I2C结束信号,如下图:
主机产生停止信号的程序如下:
void I2C_STOP(void)
{
I2C_SCL();
I2C_SDA();
I2C_DELAY();
I2C_SCL();
I2C_DELAY();
I2C_SDA();
}
程序首先将SCL和SDA输出低电平使总线处于空闲状态,然后使SCL输出高电平,最后再将SDA从输出低电平变为输出高电平,这样不仅产生了一个停止信号,并且还释放了总线(SCL和SDA都为高电平)。
数据采样:
主机发送启动信号之后,就可以向从机设备传输数据了。在传输数据时,每经过一个SCL时钟周期传输一个bit的数据,如果是主机写数据到从机,那么主机通过SDA在一个SCL时钟周期内传输一个bit给从机;如果是主机从从机中读取数据,那么主机通过SDA在一个SCL时钟周期内读取一个bit的数据。
从I2C的起始信号和结束信号的介绍中可以看到,在SCL为高电平时,SDA电平的变换会触发起始信号或停止信号,所以在数据的传输过程中,不能在SCL为高电平时改变SDA的电平状态,也就是说SDA线上的数据应该在SCL为高电平的时候保持稳定。如下图:
I2C总线通讯时数据的采样发生在SCL时钟周期的高电平时间段,数据的改变发生在SCL时钟周期的低电平时间段。也就是说数据是在SCL为高电平的时候传输或读取,在SCL为低电平的时候根据传输的数据改变SDA的电平状态。
I2C总线在传输数据时,一般是先发送高位字节的数据,最后发送低位字节的数据。
主机向从机发送一个字节的程序如下:
uint8_t I2C_WRITE_BYTE(uint8_t dat)
{
uint8_t i; I2C_SCL();
I2C_SDA();//释放总线
I2C_DELAY(); for(i = ;i < ;i ++)
{
if(dat & 0x80)
{
I2C_SDA();
}
else
{
I2C_SDA();
}
dat <<= ;
I2C_DELAY();
I2C_SCL();
I2C_DELAY();
I2C_SCL();
} i = I2C_WAIT_ACK(); return i;
}
发送程序第11~18行是根据要传输的数据dat来决定SDA的电平状态,这是在SCL为低电平的时候处理的,在SCL为高电平的时候保持SDA的输出状态不变。第26行是等待从机反馈应答信号,在后面会讲到应答信号的作用。第28行是返回的参数用来判断是否收到应答信号,如果没有收到应答信号则退出。
主机从从机读取一个字节的程序如下:
uint8_t I2C_READ_BYTE(uint8_t ack)
{
uint8_t i;
uint8_t dat; I2C_SCL();
I2C_SDA_IN();
I2C_SDA();
I2C_DELAY(); for(i = ;i < ;i ++)
{
I2C_SCL();
I2C_DELAY();
dat <<= ;
if(RD_I2C_SDA != )
{
dat |= 0x01;
}
I2C_SCL();
I2C_DELAY();
} I2C_SDA_OUT(); if(ack != )
{
I2C_ACK();
}
else
{
I2C_NOT_ACK();
} return dat;
}
接收数据程序的第6~8行是将SDA数据线转换为输入状态。第15~19行是在SCL为高电平的时候读取SDA的电平状态,即接收到一个bit的数据。在接收完8个字节的数据后,第24行将SDA数据线转换为输出状态。第26~33行是根据函数输入的参数来决定接收到一个字节的数据之后反馈给从机的是应答信号还是非应答信号。最后一行是返回接收到的数据。
IC2设备地址和数据方向:
I2C总线上的每个从机设备都有一个属于自己的独立的设备地址,主机在发送起始信号之后,需要发送从机的设备地址来查找需要通讯的从机设备,因为总线上的I2C从机设备的地址是独立的,所以不会造成多个总线上的设备在传输数据时相互干扰。
主机在发送设备地址的同时,还需要发送一个数据读写方向位,用来决定接下来的操作是向从机传输数据还是从从机读取数据。数据读写方向位一般包含在设备地址中发送给从机设备,如下图。
bit7~bit1位是从机的设备地址,最低位bit0是数据读写方向位。
当bit0位为0时,表示后面的操作是主机向从机发送数据。
当bit0位为1时,表示后面的操作是主机从从机读取数据。
关于I2C设备的地址需要注意的是,一般I2C设备在规格资料说明里给出的设备地址都是不包含数据读写方向位的,需要自己根据设备地址进行转换。比如说SHTC3温湿度传感器,在它的数据手册中给出的设备地址是0x70,在发送设备地址和读写方向位时,并不是发送0x70(最低位为0表示写)或0x71(最低位为1表示读),而是要经过以下变换得出的,首先将0x70左移一个bit,得到0xE0,再用0xE0的最低位来表示数据的读写方向位,即在给SHTC3发送写操作时应该发送0xE0(最低位为0表示写);给SHTC3发送读操作时应该发送0xE1(最低位为1表示读)。
响应信号:
在I2C总线通讯中,在传输完一个字节之后,接收数据的设备(主机和从机都有可能接收数据)会在第9个SCL时钟周期反馈一个应答信号或非应答信号给发送数据的设备,也就是说在I2C通讯中,数据的传输由8bit的数据和1bit的响应信号组成,即每一次传输会产生9个SCL时钟周期信号。如下图:
响应信号包括应答信号(ACK)、非应答信号(NACK):
应答信号(ACK):在第9个SCL时钟周期,数据发送端将SDA的控制权释放转换为输入状态,数据接收端控制SDA的输出,通过SDA数据线输出一个低电平来产生一个应答信号,数据发送端根据SDA数据线的电平状态来识别响应信号。
非应答信号(NACK):在第9个SCL时钟周期,数据发送端将SDA的控制权释放转换为输入状态,数据接收端控制SDA的输出,通过SDA数据线输出一个高电平来产生一个非应答信号,数据发送端根据SDA数据的电平状态来识别响应信号。
所以说应答信号和非应答信号是根据SDA数据在第9个SCL时钟周期时的电平状态来决定的,如果在第9个SCL时钟周期时SDA为低电平,那么就是应答信号;如果SDA是高电平,那么就是非应答信号。
应答信号和非应答信号的作用如下:
当主机向从机发送数据时:
在正常的I2C通讯中,主机每发送一个字节的数据给从机后,从机都会反馈一个应答信号给主机,主机可以通过从机反馈的响应信号来判断与从机的通讯是否正常。如果发送完一个数据之后,从机反馈的是一个应答信号则说明数据传输成功,通讯正常;如果发送完一个数据之后,从机反馈的是一个非应道信号说明数据没有传输成功,通讯异常,可能是从机正在进行其它操作或异常,这时候主机应当发送一个停止信号来停止发送数据。
一般主机在传输完一个数据给从机后,都会等待从机反馈应答信号,如果超时没有识别到应答信号,则说明收到的是非应答信号,程序如下:
uint8_t I2C_WAIT_ACK(void)
{
uint8_t Wait_cnt; I2C_SCL();
I2C_SDA_IN();
I2C_SDA();
I2C_DELAY();
I2C_SCL(); Wait_cnt = ; while(RD_I2C_SDA != )
{
Wait_cnt--;
if(Wait_cnt == )
{
break;
}
} I2C_SCL();
I2C_SDA_OUT(); if(Wait_cnt == )
{
I2C_STOP();
} return Wait_cnt;
}
以上是主机等待从机应答信号的程序,第5~9行首先在SCL为低电平的时候将SDA数据线切换为输入状态,然后将SCL输出高电平产生第9个SCL时钟周期。第13~20行是等待应答信号,如果SDA位低电平,说明识别到了应答信号,直接通过break退出循环;如果一直识别不到应答信号,当Wait_cnt的值被自减到0后退出循环。第22~23行是将SDA数据线切换为输出状态。第25~28行是根据Wait_cnt来决定是否要产生停止信号,如果Wait_cnt等于0说明收到的是非应答信号,这时候通讯已经异常,所以产生一个停止信号来结束通讯;如果Wait_cnt不等于0说明收到的应答信号,通讯正常。函数通过返回Wait_cnt的值来指示通讯是否正常。
当主机从从机读取数据时:
当主机读取从机的数据时,每读取完一个字节的数据时,主机通过反馈一个应答信号或非应答信号给从机,用来通知从机是继续传输数据还是停止传输数据。
如果主机在读取完一个从机发送的数据之后,产生一个应答信号给从机,那么从机会继续发送下一个数据给主机。
如果主机在读取完一个从机发送的数据之后,产生一个非应答信号给从机,那么从机则会停止发送数据。
也就是说,在主机读取从机的过程中,从机时根据主机反馈的响应信号来决定是否要传输下一个数据的。
在主机读取从机数据时,主机产生的应答信号程序如下:
void I2C_ACK(void)
{
I2C_SCL();
I2C_SDA();//发送低电平,响应从机
I2C_DELAY();
I2C_SCL();
I2C_DELAY();
I2C_SCL();
}
在SCL为高电平的时候,SDA输出一个低电平来表示应答信号。
在主机读取从机数据时,主机产生的非应答信号程序如下:
void I2C_NOT_ACK(void)
{
I2C_SCL();
I2C_SDA();//发送低电平,响应从机
I2C_DELAY();
I2C_SCL();
I2C_DELAY();
I2C_SCL();
}
在SCL为高电平的时候,SDA输出一个高电平来表示非应答信号。
等待应答信号的程序在主机发送一个字节的函数I2C_WRITE_BYTE中用到;主机产生应答信号或非应答信号的程序在主机读取从机数据函数I2C_READ_BYTE中用到。
3、I2C通讯的读写过程
主机写数据到从机的通讯过程如下图:
图中灰色部分是主机产生的,白色部分是从机产生的。
主机产生一个起始信号S,然后发送一个字节的从机设备地址SLAVE ADDRESS和写数据方向位R/W=0给从机,从机收到设备地址和R/W数据之后产生一个应答信号给主机,然后主机再发送一个字节的数据给从机,从机每收到一个数据都会产生一个应答信号给主机,当主机不需要再发送数据给从机或收到从机的非应答信号的时候产生一个停止信号P结束通讯。
主机由从机中读取数据的通讯过程如下图:
图中灰色部分是主机产生的,白色部分是从机产生的。
主机产生一个起始信号S,然后发送一个字节的从机设备地址SLAVE ADDRESS和读数据方向位R/W=1给从机,从机收到设备地址和R/W数据之后产生一个应答信号给主机,然后主机开始读取从机发送过来的数据,主机每收到一个数据都会产生一个应答信号或非应答信号给从机,如果希望从机继续发送数据则反馈应答信号给从机;如果希望从机停止发送数据则反馈一个非应答信号给从机。当主机不需要再读取从机的数据时产生一个停止信号P结束通讯。
I2C通讯的复合格式如下图:
在I2C通讯过程中,可能需要先发送一个地址或命令给从机后再读取从机的数据,比如读取AT24C02的数据时,需要先发送需要读取的地址,然后再读取数据。主机需要先发送一个地址信息给从机,然后再读取数据,由于发送地址信息是一个写的过程,而读取数据是一个读的过程,这样就组成了复合通讯格式。
主机首先发送一个起始信号S,然后发送一个字节的从机设备地址SLAVE ADDRESS和写数据方向位R/W=0给从机,等待从机反馈应答信号,再发送一个地址数据给从机后等待应答,然后再发送一个重复起始信号Sr,Sr信号之后发送一个字节的从机设备地址SLAVE ADDRESS和读数据方向位R/W=1给从机,以读取从机的数据,当读取完数据之后,主机产生一个结束信号P来停止通讯。
IIC通讯程序的更多相关文章
- 用Verilog实现IIC通讯
注意,此代码是错误代码,并不能实现想要的结果. 之所以留着,因为里面的enable 是独立开来的思想值得借鉴.就是控制单元和运算单元分开(我也是借鉴别人的实现思想).具体用verilogHDL实现II ...
- LPC1768的iic通讯
LPC1768有三路IIC,其中IIC0支持高速模式和plus模式,另外两路是普通IIC,使用IIC的过程如下 首先依然是打开IIC时钟,同时打开GPIO时钟 然后配置引脚为IIC功能 另外,因为ii ...
- STM32作为主设备,Arduino作为从设备进行IIC通讯的注意要点
近日公司的项目重心要往米思齐的Arduino图形化编程上转移了,需要我将STM32和Arduino的IIC通讯调通.之前Arduino并没怎么使用过,仅仅是将超声波的代码移植成TOF激光测距而已.网上 ...
- 基于esp32的IIC通讯
本文源码地址在:http://download.csdn.net/download/noticeable/9962029 IIC 通讯应该是当代比较常用的几种通讯方式之一,其无需特殊的IO接口,连线方 ...
- HALCON串口通讯程序
串口通讯程序 * Note: This example is meant to demonstrate the use of the serial interface * of HALCON. ...
- Verilog实现IIC通讯第二版
HMC5883三轴磁力传感器IIC通讯模块的VerilogHDL的实现 上一版并没有实现我想要的功能 0.0.1版 正在修订中 2013/9/2 //date :2013/7/7 //desi ...
- iic通讯 FPGA实现 mpu6050为例
IIC最常用的通讯协议,但普遍用于单片机.arm这些,用FPGA实现大材小用,但对于菜鸡水平练练手很不错,考验串并转换和时序的控制.今天我就以mpu6050陀螺仪为例,实现FPGA的iic通信. 1. ...
- 「STM32 」IIC通讯原理及其实验
I2C两线式串行总线通讯协议,它是由飞利浦开发的,主要用于连接微控制器及其外围设备之间,它是由数据线SDA和信号线SCL构成的,可发送和接收数据即在MUC和I2C设备之间,I2C和I2C之间进行全双工 ...
- 填坑-关于IIC通讯
01.概述 在之前的文章中<STM32IIC详解>中详细讲解了IIC协议,并且使用是NXP的官方手册,demo示例使用IIC读取RTC芯片,运行正常,没有任何问题.并且更新了<II ...
随机推荐
- Kali-linux使用OpenVAS
OpenVAS(开放式漏洞评估系统)是一个客户端/服务器架构,它常用来评估目标主机上的漏洞.OpenVAS是Nessus项目的一个分支,它提供的产品是完全地免费.OpenVAS默认安装在标准的Kali ...
- Multiplication in PyTorch
1. Element-wise Multiplication * torch.Tensor.mul() torch.mul() 2. Matrix Multiplication torch.Tenso ...
- Redis(三)内存模型
本文转载自编程迷思,原文链接 深入学习Redis(1):Redis内存模型 前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可 ...
- 修改linux系统的默认语言
修改linux系统的默认语言: 1.全局修改: 所有用户都是同一种统一的语言设置 修改/etc/sysconfig/i18n文件 vi /etc/s ...
- PAT——1055. 集体照
拍集体照时队形很重要,这里对给定的N个人K排的队形设计排队规则如下: 每排人数为N/K(向下取整),多出来的人全部站在最后一排: 后排所有人的个子都不比前排任何人矮: 每排中最高者站中间(中间位置为m ...
- 字符型设备驱动程序-first-printf以及点亮LED灯(二)
编译这几个函数之前要学一下:Linux 的几个操作命令. 学习地址:http://edu.51cto.com/lesson/id-101824.html 重要的命令 有4个 :分别是 1.lsmod, ...
- Linux下gdb调试(tui)
1 处于TUI模式的GDB 为了以TUI模式运行GDB,可以在调用GDB时在命令行上指定-tui选项,或者处于非TUI模式时在GDB中使用Ctrl+X+A组合键.如果当前处于TUI模式,后一种命令方式 ...
- iOS文本文件的编码检测
windows上很多文本未必是用UTF8,所以在iOS上读取的时候,如何得到文件的编码是个问题.网上有很多读取中文的例子,但是那些不够通用.比如说要读取日文,韩文,阿拉伯文等等的时候,就不行了(虽然一 ...
- MySQL学习之路(二)——数据类型和操作数据表
数据类型和操作数据表 2.1 MySQL类型之整型 2.2 MySQL数据类型之浮点型 2.3 日期时间型 DATE 1支持时间:1000年1月1日~9999年12月31日 DATETIME 3 支持 ...
- 前端基础-jQuery的事件的用法
阅读目录 常用事件 事件绑定 移除事件 页面载入 一.常用事件 1.鼠标事件之click事件 用交互操作中,最简单直接的操作就是点击操作.jQuery提供了两个方法一个是click方法用于监听用户单击 ...