I²C通信
1.1简介
IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送,高速 IIC 总线一般可达 400kbps 以上。
当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生。
1.2硬件层
IIC一共有且仅有两条总线: 一条是数据线SDA,一条是时钟线SCL。
注:IIC采用漏极开路即高阻状态,适用于输入/输出,其可独立输入/输出低电平和高阻状态,若需要产生高电平,则需使用外部上拉电阻
1.3通信
1.3.1总时序图
1.3.2起始信号
SCL保持高电平,SDA完成一个下降沿。
1.3.3传送地址码
SCL和SDA均为高电平表示逻辑1,SCL为高电平SDA为低电平表示逻辑0。
需要注意的是在每传送一个字节数据后,需要从机返回一个应答信号(SDA下降沿)。
1.3.3.1如何更改发送数据
拉低SCL,此时允许发送器更改SDA信号,更改结束重新拉高SCL信号。
1.3.4结束
SCL为高电平时SDA完成上升沿,这与起始信号相反。表示一次通信结束。
1.3.5收发数据
注:若两个设备之间已建立连接,想改变数据传送方向,可不发送停止信号而重新发送一次起始信号(即不交出总线的使用权)。
2、应用实例(与AT24C02结合)
#include <reg51.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
unsigned char sec; //定义计数值,每过 1 秒,sec 加 1
unsigned int tcnt; //定时中断次数
bit write = 0; //写 24C08 的标志;
sbit sda = P2 ^ 0;
sbit scl = P2 ^ 1;
sbit dula = P2 ^ 6;
sbit wela = P2 ^ 7;
unsigned char j, k;
void delay(unsigned char i) { //延时程序
for (j = i; j > 0; j--)
for (k = 125; k > 0; k--);
}
uchar code table[] = {
//数码管编码
0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};
void display(uchar bai_c, uchar sh_c) {
dula = 0;
P0 = table[bai_c];
dula = 1;
dula = 0;
wela = 0;
P0 = 0x7e;
wela = 1;
wela = 0;
delay(5);
dula = 0;
P0 = table[sh_c];
dula = 1;
dula = 0;
wela = 0;
P0 = 0x7d;
wela = 1;
wela = 0;
delay(5);
}
/24C02 读写驱动程序
void delay1(unsigned char x) { //延时子函数
unsigned int i;
for (i = 0; i < x; i++);
}
void flash() //延时子函数
{ ; ; }
void x24c08_init() { //24c02 初始化子程序
scl = 1;
flash();
sda = 1;
flash();
}
void start() { //启动 I2C 总线
sda = 1;
flash();
scl = 1;
flash();
sda = 0;
flash();
}
void stop() { //停止 I2C 总线
sda = 0;
flash();
scl = 1;
flash();
sda = 1;
flash();
}
void writex(unsigned char j) { //写一个字节
unsigned char i, temp;
temp = j;
for (i = 0; i < 8; i++) {
temp = temp << 1;
scl = 0;
flash();
sda = CY;
flash();
scl = 1;
flash();
}
scl = 0;//释放
flash();//
sda = 1;//为了读取应答信号(低电平)
flash();//
}
unsigned char readx() { //读一个字节
unsigned char i, j, k = 0;
scl = 0;
flash();
sda = 1;//释放
flash();
for (i = 0; i < 8; i++) {
scl = 1;//读总线
flash();
j = sda;
k = (k << 1) | j;
scl = 0;
flash();
}
flash();
return (k);
}
void clock() { //I2C 总线应答子函数
uchar i = 0;
scl = 1;//高电平可以读取信号
flash();
while ((sda == 1) && (i < 255))//没有收到应答信号
i++;
scl = 0;//第九个时钟拉低
flash();
}
从 24c02 的地址 address 中读取一个字节数据/
unsigned char x24c08_read(unsigned char address) {
unsigned char i;
start();
writex(0xa0);//高四位1010 低三位000 最后一位为0
clock();
writex(address);//寄存器地址
clock();
start();
writex(0xa1);//最后一位的1为读数据信号
clock();
i = readx();
stop();
delay1(10);
return (i);
}
//向 24c02 的 address 地址中写入一字节数据 info/
void x24c08_write(unsigned char address, unsigned char info) {
start();
writex(0xa0);
clock();
writex(address);
clock();
writex(info);
clock();
stop();
}
void t0(void) interrupt 1 using 0 { //定时中断服务函数
TH0 = (65536 - 50000) / 256; //0.5s
TL0 = (65536 - 50000) % 256;
tcnt++;
if (tcnt == 20) { //计满 20 次(1 秒)时
tcnt = 0;
sec++;//读秒
write = 1; //1 秒写一次 24C02
if (sec == 100) { //定时 100 秒,在从零开始计时
sec = 0;
}
}
}
void main() {
unsigned char i;
TMOD = 0x01;
ET0 = 1;
EA = 1;
x24c08_init();
sec = x24c08_read(2); //读出保存的数据赋于 sec
TH0 = (65536 - 50000) / 256;
TL0 = (65536 - 50000) % 256;
TR0 = 1; //启动定时器
while (1) {
i = 10;
while (i--) {
display(sec / 10, sec % 10);
}
if (write == 1) { //判断计时器是否计时一秒
write = 0; //清零
x24c08_write(2, sec); //在 24c02 的地址 2 中写入数据 sec
}
}
}