目录

前言

辅助函数

相关信号函数

起始信号

停止信号

接收应答函数

发送应答信号

应答信号

非应答信号

发送/接收一个字节数据

发送一字节数据

接收一字节数据

应用

IIC级联

最后

前言

   IIC为两线式串行总线,其用于主设备与从设备之间的通信,且任一时刻只能有一台主设备。网上关于IIC的详细说明很多,故本文不打算从介绍IIC入手,直接进行程序说明。

        此外,由于不同的从设备的控制方法不同,鄙人不才,想不到一个比较完美的方法将不同的设备在程序上进行统一,因此,本文章仅介绍IIC的基本信号产生的函数,还望读者谅解。如对设备在程序上的统一,有好办法的读者,还请赐教!

        文章中程序使用的单片机为STC15系列的单片机,所用的时钟频率为24MHz。但是它是适用51系列的单片机的,因其程序编写过程大同小异,故读者可在此基础上适当修改,以适用读者所用的芯片。

辅助函数

   在IIC中,当SCL为高电平时,SDA上电平的变化将代表着相关的操作,为防止误触发,每个函数都以SCL为低电平收尾。

/************************************************************************
	本文件为IIC的基本信号集合(作为主机),就目前测试,适合在各个频率下工作,
	针对不同的频率,仅需适当调整IIC延时的循环次数即可
	每个信号中都以SCL低电平为结尾,这样一来可防止信号发送之后在SDA上的电
	平变化引起误触发
*************************************************************************/
 
sbit SDA = P0^0;
sbit SCL = P0^1;

在IIC适用过程中会涉及到相应信号的延时,在此吾建立了一个专用于IIC延时的函数,如下:

/*仅作为IIC延时,故使其仅作为该文件可以调用的函数,可根据不同的时钟频率调节相应的延时时间*/
static void IIC_Delay()
{
	unsigned char i;
	for(i = 0;i < 2;i++)	//根据不同的时钟频率调整不同的循环次数
	{
		_nop_();
	}
}

接下来的函数是配置所使用到的引脚的模式以及相关的初始化,如下:

/*IIC初始化,将端口设为准双向输入输出(不设置为推挽模式是因为还需要读取数据,推挽下输入的电平几乎是不变的)*/
void IIC_Init()
{
	P0M0 &= 0xfc;	//根据需要调整参数
	P0M1 &= 0xfc;	//根据需要调整参数
 
	SCL = 0;		//将时钟线拉低后,数据线上的变化不会产生影响,防止误触发
	SDA = 1;		//释放数据线
}

相关信号函数

起始信号

起始信号即在SCL为高电平情况下,SDA由高电平变为低电平

/****IIC起始信号(SCL高电平下SDA的一个下降沿,后以SCL变为低电平收尾)****/
/*	     				   ____
						 /     \4.7us
					___/       \___									 */
void IIC_Start()
{
	SDA = 1;
	SCL = 1;
	SDA = 0;
	SCL = 0;
}

停止信号

停止信号即在SCL为高电平情况下,SDA由低电平变为高电平

/****IIC停止信号(SCL高电平下SDA一个上升沿,后以SCL变为低电平收尾)****/
/*							  _____________
					SCL ____/					
							 4us_____
					SDA ______/	     \____						   */
void IIC_Stop()
{
	SDA = 0;
	SCL = 1;
	SDA = 1;
	SCL = 0;
}

接收应答函数

主设备释放SDA总线,将SCL总线置为高电平,接收SDA上的应答信号

/*接收应答信号,若接收到非应答信号,则发送停止信号(SDA低电平表示应答,高电平表示非应答)*/
bit IIC_ReceiveACK()
{
	bit state;
	SDA = 1;
	SCL = 1;
	state = SDA;
	SCL = 0;
	if(state)			//非应答
	{
		IIC_Stop();
	}
	return state;
}

发送应答信号

应答信号

SDA保持低电平,然后将SCL由低电平转为高电平

/*************************发送应答信号函数*************************/
/****应答(SDA保持为低电平)****/
/*							 	 ____
					SCL _______/	 \_____				
						_____
					SDA	   	 \____________						   */
void IIC_SendACK()	
{
	SDA = 0;
	SCL = 1;
	IIC_Delay();		//必须延时
	SCL = 0;
}

非应答信号

SDA保持高电平,然后将SCL由低电平转为高电平

/****非应答(SDA保持为高电平)****/
/*							 	 ____
					SCL _______/	 \_____				
							  _____________
					SDA	____/		 	 							*/
void IIC_SendNACK()
{
	SDA = 1;
	SCL = 1;
	IIC_Delay();
	SCL = 0;
}

发送/接收一个字节数据

发送一字节数据

/****IIC发送一个字节数据(每发送一个字节接收一次应答)****/
void IIC_SendByte(unsigned char dat)
{
	unsigned char i;
	for(i = 0;i < 8;i++)
	{
		SDA = (dat >> 7);
		SCL = 1;
		IIC_Delay();
		SCL = 0;
		dat <<= 1;
		IIC_Delay();		//必须延时
	}
}

接收一字节数据

/****IIC接收一个字节(每接收一个字节发送一次应答/非应答)****/
unsigned char IIC_ReadByte()
{
	unsigned char i,receive;
 
	SDA = 1;				//释放数据线
	
	for(i = 0;i < 8;i++)
	{
		SCL = 1;
		receive <<= 1;
		receive |= SDA;
		SCL = 0;
		IIC_Delay();		//必须延时
	}
	return receive;
}

应用

  在使用IIC对设备进行读写操作时,需要先往IIC总线上发送7位从机地址和一位读写标志位,7位从机地址由模块厂商生产时确定,发送后与该地址一致的从机根据最后一位进入读或者写状态。当最后一位为0时,往从机写入数据;当最后一位为1时,往从机读出数据。以下假设从机的7位地址为0x68,为读者演示其读写操作:

写入数据:

IIC_SendByte((0x68 << 1) & 0xfe);		//direction is write

读出数据:

IIC_SendByte((0x68 << 1) | 0x01);		//direction is read

IIC级联

    IIC级联即将不同模块的IIC时钟引脚和数据引脚分别连接在一起,然后连接到单片机的IO口上,可实现单片机的一对IIC的IO口控制多个模块。例如模块1的SCL引脚与模块2的SCL引脚连接在一起然后接到单片机定义的SCL引脚上;再将模块1的SDA引脚与模块2的SDA引脚连接在一起然后接到单片机的SDA引脚上,即级联。

        在级联中如何区分不同的模块呢?此时前面提到的从机地址发挥了关键作用。由于每次对模块进行控制时都需要先发送从机地址和读写位,其中读写位说明了数据的传输方向,而从机地址则说明了此时所控制的模块。

        在从机地址由7位数据组成的模块中,理论上可级联的IIC模块个数为127个。

        在实际的级联操作中,由于每个模块多少会有漏电流,所以还要根据实际情况决定是否需要为单片机的IO口添加上拉电阻,增强其驱动能力。

        假设此时有两个IIC模块进行级联,模块1的从机地址为0x68,模块2的从机地址为0x38。当对模块1进行读写时,操作如下:

IIC_SendByte((0x68 << 1) & 0xfe);		//direction is write
IIC_SendByte((0x68 << 1) | 0x01);		//direction is read

当对模块2进行读写时,操作如下:

IIC_SendByte((0x38 << 1) & 0xfe);		//direction is write
IIC_SendByte((0x38 << 1) | 0x01);		//direction is read

   级联更多模块的操作与此类似,不作过多赘述。

最后

 以上即IIC的基本函数,程序中有些地方必须有一定的延时,否则将造成通信异常,这些地方我在后面都有注释。这些延时仅适用于24MHz及一下的频率使用模拟IIC,其他频率下的其他地方是否需要延时鄙人未知;还有,在其他频率下,延时函数也需要进行一定的调整,这些问题本身难度不大,留给读者探索。