掌上实验室V8系列教程(六)I2C总线入门

目录

1 I2C总线简介

2 I2C总线的工作原理

2.1 位传输和字节传输

2.2 常见数据传输格式 

 2.3 几个相关定义

3 软件模拟I2C主机程序


1 I2C总线简介

        总线(Bus)是计算机各种功能部件之间传送信息的公共通信干线,一般总线用于多个模块之间的通信。

        I2C(Inter-Integrated Circuit)总线是由Philips公司开发的一种简单、双向二线制同步串行总线,只需要两根线即可在连接于总线上的器件之间传送信息。I2C总线上分成主机和从机两种设备,主机用于启动总线传送数据并产生时钟以同步从机,此时任何被寻址的器件均被认为是从器件。

        I2C总线规范现在已经发展到了2.0版,总线速率最高为3.4Mbits/s。通常分为三种工作模式:标准模式(最高100kbits/s)、快速模式(最高400kbits/s)、高速模式(最高3.4Mbits/s)、超快速模式(5Mbits/s),常用的器件都支持标准和快速模式。

        I2C总线只有两根通信线,SDA(串行数据线)和SCL(串行时钟线),这两根线都是双向I/O线。接口电路为开漏输出,需通过上拉电阻接电源VCC。当总线空闲时.两根线都是高电平,连接总线的外同器件都是CMOS器件,输出级也是开漏电路.各器件的SDA和SCL都是线“与”关系。

MOS器件采用OD(Open Drain)开漏模式,TTL器件采用OC(Open Colletor)集电极开路模式。当器件输出“0”时,将总线和地接通,输出“1”时,断开输出,所以总线必须采用外部上拉电阻把总线上拉到电源。由于所有器件采用OD或OC,输出“1”其实不对总线有影响,只是由于外部有上拉电阻,所以总线表线出来是高电平;当输出“0”时,器件接通MOS管,直当于把总线接地了,总线被强制拉到地,总线变成了低电平。

当所有器件输出“1”,总线表现出高电平;当所有器件输出“0”,总线表现出低电平。如果一部分输出“1”,一部分输出“0”,总线电平就会变成“0”,这就是线“与”。线“与”就是总线最终的表现出来的是所器件输出信号相“与”的结果。

典型的I2C总线应用示例如下图所示:

 I2C总线支持多主机和多从机,总线上连接的设备只受到总线负载400pF的限制。这么多设备全部连接在总线上,为方便区分各个器件,每个器件必须有一个唯一的地址。这个地址一般是7位,也可以是10位,10位地址也叫扩展地址。

2 I2C总线的工作原理

2.1 位传输和字节传输

数据的有效性规定:

时钟信号(SCL)为高期间,数据总线(SDA)上的数据必须保持稳定,只有在SCL为低电平期间,才允许改变SDA上的数据,如下图:

 起始(Start)和终止信号(Stop):

SCL为高电平期间,SDA由高变低表示起始信号;SCL高电平期间,SDA由低变高表示终止信号。标准模式要求图中t必须大于4微秒。Start一般简写为S,Stop简写为P。

 应答信号:

I2C总线上的数据都是以8位字节为单位传送的,发送器每发送一个字节(首先传输的是数据的最高位 MSB),就在第9个脉冲期间释放数据线,由接收器反馈一个应答信号。应答信号为低电平时,规定为有效应答位(ACK);应答信号为高电平时,规定为非应答位(NACK)。

重新起始信号(Start Repeated):

一般通信过程中都是起始信号开始,数据传输完成,以终止信号结束。但有些情况下,传输部分数据后,还需要再进行另外一次通信(一般都有写切换到读的过程),就会发送一个重新起始信号。

Start Repeated一般简写为Sr。

主机发送起始信号、终止信号和应答信号必须遵守的最小时间规定如下图:

2.2 常见数据传输格式 

写传输格式

MCU作为主设备向从设备发送两个字节数据,首先发送起始信号(S),接着发送7位地址(MSB高位在前),再发送“0”(代表写),等从机确认(ACK)后,接着发送第一个字节数据(同时发送SCL和SDA信号),等从机确认后,再发送第二个数据,等从机确认后,发送终止信号,结束本次通信。

读传输格式

 MCU作为主设备向从设备读取两个字节数据,首先发送起始信号(S),接着发送7位地址(MSB高位在前),再发送“1”(代表读),等从机确认(ACK)后,开始接收第一个字节数据(只发送SCL信号,从SDA上读返回的数据),收到8位数据后,主机发送(ACK);再开始接收第二个数据,收到8位数据后(如果不是最一个字节,主机发ACK;如果是最后一个字节,主机发NACK),然后发送终止信号,结束本次通信。

复合传输格式

写传输格式和读传输格式,在整个数据通信过程中,数据传输方向都不会改变。如果需要传输中间改变传输方向,就要在传输中间发送Sr(重新起始信号)。相当于写传输格式和读传输格式的组合。

 2.3 几个相关定义

理解上述基本概念后,一般的I2C总线相关编程就没有问题了。为不干扰初学者,下列几个概念这里就不作解释了,想深入了解的可以自行找相关资料。

插入等待时间

总线封锁状态

总线竞争的仲裁

时钟信号的同步

3 软件模拟I2C主机程序

/*
PA8 - SCL
PC9 - SDA

Util_DelayUs 函数需另外定义
例程采用标准库函数版本,如需采用其它库函数,可自行修改相关函数

例程只是定义了I2C基本操作函数,具体应用还需参考相关器件的手册
*/
static void SCL_GPIO_Init(void)
{
	GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8; //
	GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_OD;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA, GPIO_Pins_8);
	GPIO_InitStructure.GPIO_Pins = GPIO_Pins_9; //
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_SetBits(GPIOC, GPIO_Pins_9);
}
static void SCL_OPEN(void)
{
	GPIO_SetBits(GPIOA, GPIO_Pins_8);
}
static void SCL_LOW(void)
{
	GPIO_ResetBits(GPIOA, GPIO_Pins_8);
}
static void SDA_OPEN(void)
{
	GPIO_SetBits(GPIOC, GPIO_Pins_9);
}
static void SDA_LOW(void)
{
	GPIO_ResetBits(GPIOC, GPIO_Pins_9);
}
static uint8_t SCL_READ()
{
	return GPIO_ReadInputDataBit(GPIOA, GPIO_Pins_8);
}
static uint8_t SDA_READ()
{
	return GPIO_ReadInputDataBit(GPIOC, GPIO_Pins_9);
}

static void I2c_GenStart()
{
	SDA_OPEN();
	Util_DelayUs(1);
	SCL_OPEN();
	Util_DelayUs(1);
	SDA_LOW();
	Util_DelayUs(4);  // hold time start condition (t_HD;STA)
	SCL_LOW();
	Util_DelayUs(4);
}
static void I2c_GenStop()
{
	SCL_LOW();
	Util_DelayUs(1);
	SDA_LOW();
	Util_DelayUs(1);
	SCL_OPEN();
	Util_DelayUs(4); // set-up time stop condition (t_SU;STO)
	SDA_OPEN();
	Util_DelayUs(4);
}

static int I2c_WriteByte(uint8_t txByte)
{
	uint8_t mask;
	int Acked = 1;
	for(mask = 0x80; mask > 0; mask >>= 1) { // shift bit for masking (8 times)
		if((mask & txByte) == 0) 
			SDA_LOW();    // masking txByte & write to SDA-Line
		else                     
			SDA_OPEN();   
		Util_DelayUs(1);                  // data set-up time (t_SU;DAT)
		SCL_OPEN();                            // generate clock pulse on SCL
		Util_DelayUs(5);                  // SCL high time (t_HIGH)
		SCL_LOW();                             
		Util_DelayUs(1);                  // data hold time(t_HD;DAT)
	}                       
	
	SDA_OPEN();                              // release SDA-line
	SCL_OPEN();                              // clk #9 for ack
	
	Util_DelayUs(5);                    // data set-up time (t_SU;DAT)
	if(SDA_READ()) 
		Acked = 0;          // check ack from i2c slave
	SCL_LOW();                               
	Util_DelayUs(20);                   // wait to see byte package on scope
	return Acked;                            // return error code
}

static uint8_t I2c_ReadByte(int GenAck)
{
	uint8_t mask;
	uint8_t rxByte = 0;
	SDA_OPEN();                              // release SDA-line
	for(mask = 0x80; mask > 0; mask >>= 1) { // shift bit for masking (8 times)
		SCL_OPEN();                            // generate clock pulse on SCL
		Util_DelayUs(1);                  // data set-up time (t_SU;DAT)
		while(SCL_READ() == 0){}                 // wait while hold master
		Util_DelayUs(5);                  // SCL high time (t_HIGH)
		if(SDA_READ()) rxByte = rxByte | mask;   // read bit
		SCL_LOW();                             
		Util_DelayUs(1);                  // data hold time(t_HD;DAT)
	}                                        
	if(GenAck) 
	  SDA_LOW();                // send acknowledge if necessary
	else           
	  SDA_OPEN();
	
	Util_DelayUs(1);                    // data set-up time (t_SU;DAT)
	SCL_OPEN();                              // clk #9 for ack
	Util_DelayUs(5);                    // SCL high time (t_HIGH)
	SCL_LOW();                               
	SDA_OPEN();                              // release SDA-line
	Util_DelayUs(20);                   // wait to see byte package on scope
	return rxByte;                           // return error code
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值