先大概分解IIC协议.找出其变化和不变的东西... */
- /* */
- /* IIC总线总是以下面这种结构出现 */
- /* <开始信号>[通信过程(读/写)<ACK>|<NACK>]<停止信号> */
- /* */
- /* 当然了,这是我自己总结的,有不同意见我们再进行讨论.嘿嘿 */
- /* */
- /* 也就有了 */
- /* Start(...);发开始信号 */
- /* Stop(...);发停止信号 */
- /* SendByte(...);发送一个字节 */
- /* ReadByte(...);读取一个字节 */
- /* */
- /* 以及一系列的ACK机制的东西 */
- /******************************************************************************/
- /* 上面的都是不变的,下面我们来看什么是可变的. */
- /* */
- /* 时间间格. */
- /* 也就是各个脉冲的宽度间格, */
- /* 由于单片机间的速度参差不齐(从几百KHz到几十MHz). */
- /* 因此,有Delay(); */
- /* 这个函数,供用户自行根据自己的单片机速度进行调整. */
- /* */
- /* IO定义. */
- /* 具体的应用,不同的资源.设计的理念不一样,用的IO不一样. */
- /* 这里以8051上可以位寻址的IO作例子: */
- /* sbit SDA = P1 ^ 2; */
- /* sbit SCL = P1 ^ 3; */
- /* */
- /* 协议组合方式. */
- /* 不同的功能芯片,要求的具体协议不一致.功能分块后,可以任意组合 */
- /* 以应对不同的需求.具体的可以从功能芯片的手册里得出... */
- /******************************************************************************/
#include "I2C.h"
/* 延时约5微秒,对于12M时钟 */
void delay5us()
{
uint i;
for (i=0;i<5;i++)
_nop_();
}
/* 起始条件子函数 */
void start(void)
{
SDA = 1; // 启动I2C总线
SCL = 1;
delay5us();
SDA = 0;
delay5us();
SCL = 0;
}
/* 停止条件子函数 */
void stop(void)
{
SDA = 0; // 停止I2C总线数据传送
SCL = 1;
delay5us();
SDA = 1;
delay5us();
SCL = 0;
}
/* 发送应答子函数 */
void ack(void)
{
SDA = 0; // 发送应答位
SCL = 1;
delay5us();
SCL = 0;
SDA = 1;
}
/* 发送非应答子函数 */
void n_ack(void)
{
SDA = 1; // 发送非应答位
SCL = 1;
delay5us();
SCL = 0;
SDA = 0;
}
/* 应答位检查子函数 */
void checkack(void)
{
SDA = 1; // 应答位检查(将p1.0设置成输入,必须先向端口写1)
SCL = 1;
nackFlag = 0;
if(SDA == 1) // 若SDA=1表明非应答,置位非应答标志F0
nackFlag = 1;
SCL = 0;
}
/* 发送一个字节数据子函数 */
void sendbyte(uchar idata *ch)
{
uchar idata n = 8;
uchar idata temp;
temp = *ch;
while(n--)
{
if((temp&0x80) == 0x80) // 若要发送的数据最高位为1则发送位1
{
SDA = 1; // 传送位1
SCL = 1;
delay5us();
SCL = 0;
SDA = 0;
}
else
{
SDA = 0; // 否则传送位0
SCL = 1;
delay5us();
SCL = 0;
}
temp = temp<<1; // 数据左移一位
}
}
/* 接收一字节子程序 */
void recbyte(uchar idata *ch)
{
uchar idata n=8; // 从SDA线上读取一位数据字节,共8位
uchar idata temp = 0;
while(n--)
{
SDA = 1;
SCL = 1;
temp = temp<<1; // 左移一位
if(SDA == 1)
temp = temp|0x01; // 若接收到的位为1,则数据的最后一位置1
else
temp = temp&0xfe; // 否则数据的最后一位置0
SCL=0;
}
*ch = temp;
}
/* 发送n字节数据子程序 */
void sendnbyte(uchar idata *sla, uchar n)
{
uchar idata *p;
start();
sendbyte(sla);
checkack();
if(F0 == 1)
{
NACK = 1;
return;
}
p = sbuf;
while(n--)
{
sendbyte(p);
checkack(); // 检查应答位
if (nackFlag == 1)
{
NACK=1;
return; // 若非应答表明器件错误或已坏,置错误标志位NACK
}
p++;
}
stop(); // 全部发完则停止
}
/* 接收n字节数据子程序 */
void recnbyte(uchar idata *sla, uchar n)
{
uchar idata *p;
start(); // 发送启动信号
sendbyte(sla); // 发送从器件地址字节
checkack(); // 检查应答位
if(nackFlag == 1)
{
NACK = 1;
return;
}
p = rbuf; // 接收字节存放在rbuf中
while(n--)
{
recbyte (p);
ack(); // 收到一个字节后发送一个应答位
p++;
}
n_ack(); // 收到最后一个字节后发送一个非应答位
stop();
}
/* 主函数,模拟实现I2C总线的数据收发 */
void main(void)
{
uchar i,numbyte;
numbyte = 8;
/* 需发送的8字节数据 */
for (i=0;i<numbyte;i++)
sbuf[i] = i+0x11;
SLAdd = 0x58; // 从器件地址
sendnbyte(&SLAdd,numbyte); // 向从器件发送存放在sbuf中的8字节数据
for (i=0;i<10000;i++)
delay5us();
recnbyte(&SLAdd,numbyte); // 由从器件接收8字节数据,存放在rbuf中