"); //-->
说说虚拟I2C总线程序的设计
------------深 冬
随着计算机技术的发展,数字电路已越来显得举足轻重。最为突出的是处理器。ARM、DSP、MPU、MCU……,已是当今世界继计算机系统的另一分支,嵌入式系统,对于较复杂嵌入式应用系统,元件与芯片之间短距离通信的电路走线一般比较多,从而增加硬件系统设计难度。于是系统也变得不太稳定。因此众多公司提出不同解决方案。20世纪末的1992年philips公司提出的I2C总线协议,有效解决了此问题。它是一种简单的双向两条总线来连接系统中的各个器件。但每个器件都需加一个符合I2C总线协议的片上接口,以保正常通讯。
因平时较繁忙,只能根据自己经历的那一段时期所感受到的进行介绍。90年代初,彩电渐渐进入千家万户,中学时期的我,常到同学家修彩电,见过不少品牌、时期不同的电视。从技术一面看,可分为三个阶段。最早的模拟彩电,95年推出的数码彩电,以及数字式的等离子电视。其中数码彩电是最为典型的I2C应应用,在整个彩电控制系统中的微处理器为中心,微处理接收用户遥控器发出的指令,处理后经I2C总线向各单位发出控制住处和接收反回的住处进行处理。I2C总线上的单位部件有:主控器件微处理,及从部件I2C EEPROM,行场扫描,色解码,AV/IO,亮色控制等。时致今日,I2C总线仍然得到热门的应用,特别是嵌入式系统。但也有很多处理器中不带有I2C总线硬件接口,因此我建议大家可以用软件模拟的方式实现。以充分发挥自己的应用系统。
标准I2C总线特性:
只有两根总线:串行时钟线,串行数据线。
主器件即可作为发送器也可作为接收器。
总线上的器件都有各自唯一的一个地址,通过这个地址主机可对分机进行寻址。
I2C总线是一个真正的多主总线,有竞争检测和件裁电路,能使多个主机任意同时发送数据而不破坏总线上的数据。
时钟同步线上的同步时钟允许器件通过总线的不同的波特率进行通信。
数据传输速率可达到100Kbit/s,快速模式下可以达到400 Kbit/s,在高速模式下可达到3.4MKbit/s。
采用漏极开路工艺,总线上须接上拉电阻。
同步时钟可作为停止和重新启动串行接口发送的握手方式。
总线上的集成电路器件数只受400PF的最大总线电容的限制。
片上滤波器可以滤去总线上的毛刺。以保证数据传输的稳定性和完整性。由于I2C具有上述特性,可更多地方便设计,并且总线上的设备在系统中的增加或移出对别的器件没有影响;简单的结构便于产品改型和升级。
I2C总线可实现多双向同步数据信息传输,每项个主器件都可发同步时钟,因为SCL接口的“线与”结构,如果其中一个主器件时钟跳变为低电平SCL线将保持低电平直到时钟达到高电平。所以SCL线上的时钟低电平时间由各器件中的时钟最长的低电平时间决定,而时钟高电平是由高电平时间最短的器件决定。总线竞争的仲裁及处理通常由硬件电路完成。如果两个主器件发送数据相同时不可能出现总线竞争。如下图1示:
当某一时刻主器件A发送高电平而主器件B发送低电平,这时,由于SDA的“线与”作用,A主器件发送的高电平在SDA线上反映的是B主器件的低电平状态,这低电平状态通过硬件系统反馈到数据寄存器中和原有状态比较不同而退出竞争。图2示:
图1:
图2:
I2C总线规定的中启动和停止条件规定如下:
启动条件:
在SCL为高电平时,SDA出现一个下降沿测启动I2C总线
停止条件:
在SCL为高电平时,SDA出现一个上升沿测停止使用I2C总线
有关I2C总线的详细操作请参阅相关资料,代码如下:
/* 虚拟I2C总线程序*/
/* --- XXXXXXXX 深 冬 设计 2006/1/29 V1.0 --- */
/* --- 虚拟I2C总线程序 ---------------- */
/* --- E-MAIL: EAST_SHEN@126.COM --------------- */
/* --- Mobile: 13798381465 ----------------- */
/* --- QQ: 907153057----------------- */
//-----------------------函数声明,变量定义--------------------------------------------------------
#include <reg51.h>
#include <intrins.h>
#define HIGH 1
#define LOW 0
#define length 10 // 收和发缓存区的长度
sbit SDA=P3^2; // 用p3.2口模拟数据口
sbit SCL=P3^3; // 用p3.3口模拟时钟口
unsigned char idata send_buf[length]; // 数据发送缓冲区
unsigned char idata receive_buf[length]; // 数据接收缓冲区
bit bdata Slave_Error; // 从机出错标志
//-------------------------------------------------------------------------------------------------
// 函数名称: delay4nop()
// 函数功能: 延时
void delay4nop()
{ _nop_();
_nop_();
_nop_();
_nop_();
}
//--------------------------------------------------------------------------------------------------
// 函数名称: I2C_start()
// 函数功能: 启动I2C总线
// 说 明: 时钟线置高,数据线从高到低一次跳变,停止I2C通信
//--------------------------------------------------------------------------------------------------
void I2C_start(void)
{ EA=0; //关闭中断
SDA = HIGH; //数据线置高
SCL = HIGH; //时钟线置高
delay4nop(); // 延时5us
SDA =LOW; //数据线从高到低跳变时I2C开始通信
delay4nop();// 延时5us
SCL =LOW;//时钟线置低
}
//--------------------------------------------------------------------------------------------------
// 函数名称: I2C_stop()
// 函数功能: I2C停止总线数据传送
// 说 明: 时钟线置高,数据线从低到高一次跳变,停止I2C通信
//--------------------------------------------------------------------------------------------------
void I2C_stop(void)
{
SDA =LOW;
SCL =HIGH;
delay4nop();
SDA =HIGH;
delay4nop();
SCL =LOW;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: Slave_ACK
// 函数功能: 分 机发送应答位
// 说 明:
//--------------------------------------------------------------------------------------------------
void Slave_ACK(void)
{
SDA =LOW;
SCL =HIGH;
delay4nop();
SDA =HIGH;
SCL =LOW;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: Slave_NO_ACK
// 函数功能: 分机发送非应答位,迫使数据传输过程结束
//--------------------------------------------------------------------------------------------------
void Slave_NO_ACK(void)
{
SDA =HIGH;
SCL =HIGH;
delay4nop();
SDA =LOW;
SCL =LOW;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: check_ACK
// 函数功能: 主机应答位检查,迫使数据传输过程结束
//--------------------------------------------------------------------------------------------------
void check_ACK(void)
{
SDA =HIGH; // 设置SDA为输入,必须先向SDA写1
SCL =HIGH;
F0 =LOW;
if(SDA ==HIGH) // 若SDA=HIGH不应答,应答标志F0置位
F0 = 1;
SCL =LOW;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: I2CSendByte
// 入口参数: input_data
// 函数功能: 发送一个字节
//--------------------------------------------------------------------------------------------------
void I2C_Send_Byte(unsigned char input_data)
{
unsigned char idata n=8; // 向SDA上发送一位数据字节,共八位
while(n--)
{
if((input_data&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1
{
SDA =HIGH; // 传送位1
SCL =HIGH;
delay4nop();
SDA =LOW;
SCL =LOW;
}
else
{
SDA =LOW; // 否则传送位0
SCL =HIGH;
delay4nop();
SCL =LOW;
}
input_data = input_data<<1; // 数据左移一位
}
}
//--------------------------------------------------------------------------------------------------
// 函数名称: I2C_receive_Byte
// 返回接收的数据
// 函数功能: 接收一字节
//--------------------------------------------------------------------------------------------------
unsigned char I2C_receive_Byte(void)
{
unsigned char idata n=8; // 从SDA线上读取一上数据字节,共八位
unsigned char temp;
while(n--)
{
SDA =HIGH;
SCL = HIGH;
temp = temp<<1; // 左移一位
if(SDA ==HIGH)
temp = temp|0x01; // 若接收到的位为1,则数据的最后一位置1
else
temp = temp&0xfe; // 否则数据的最后一位置0
SCL=LOW;
}
return(temp);
}
//--------------------------------------------------------------------------------------------------
// 函数名称: write_Nbyte
// 入口参数: Slave_add从机地址,n要发送的数据个数
// 函数功能: 发送n位数据
//--------------------------------------------------------------------------------------------------
void write_Nbyte(unsigned char slave_add, unsigned char n)
{
unsigned char idata send_da,i=0;
I2C_start(); // 启动I2C
I2C_Send_Byte(slave_add); // 发送地址位
check_ACK(); // 检查应答位
if(F0 == 1)
{
Slave_Error = 1;
return; // 若非应答表明器件错误或损坏,置错误标志位Slave_Error
}
while(n--)
{
send_da = send_buf[i++];
I2C_Send_Byte(send_da);
check_ACK(); // 检查应答位
if (F0 == 1)
{
Slave_Error=1;
return; // 若非应答表明器件错误或损坏,置错误标志位Slave_Error
}
}
I2C_stop(); // 全部发完则停止
}
//--------------------------------------------------------------------------------------------------
// 函数名称: receive_Nbyte
// 入口参数: slave_add从机地址,n要接收的数据个数
// 函数功能: 接收n位数据
//--------------------------------------------------------------------------------------------------
void receive_Nbyte(unsigned char idata Slave_add, unsigned char n)
{
unsigned char idata receive_data,i=0;
I2C_start();
I2C_Send_Byte(Slave_add);
check_ACK();
if(F0 == 1)
{
Slave_Error = 1;
return;
}
while(n--)
{
receive_data=I2C_receive_Byte();
receive_buf[i++]=receive_data;
Slave_ACK(); // 收到一个字节后发送一个应答位
}
Slave_NO_ACK(); // 收到最后一个字节后发送一个非应答位
I2C_stop();
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。