单片机-IIC

IIC

  • Inter IC Bus
  • 由数据线SDA和时钟线SCL构成,采用漏记开路结构和总线连接,因此SCL和SDA均需接上拉电阻,总线在空闲状态为高电平,任一器件输出低电平都可拉低总线。
  • IIC在数据传输时,时钟信号为高电平期间,数据线上的数据必须保持稳定。

信号

  • 发送启动信号,在SCL高电平期间,SDA出现下降沿。
  • 发送寻址信号,7位地址寻址时,高7位为地址位,最低位为方向位,0表示主机对从机进行写操作,1表示主机对从机进行读操作。主机发送地址时,总线上的从机都根据7位地址码与自己的地址进行比较,相同即认为自己正被主机寻址,根据R/W确定自己为发送器或者接收器。
位序号 7 6 5 4 3 2 1 0
:----: :----: :----: :----: :----: :----: :----: :----: :R/W:
  • 应答信号,应答信号由接收设备产生,在SCL信号为高电平期间,接收设备将SDA拉为低电平,表示数据传输正确,产生应答。
  • 停止信号,在SCL为高电平期间,SDA出现上升沿。

延时

void delay()  //延时4-5us
{;;}
void delay1ms(uint z)
{
	uint x,y;
	for(x=z;x>0;x--)
	{
		for(y=110;y>0;y--);
	}
}

初始化

//总线初始化
void init()  //总线在初始状态下保持高电平
{
	SCL=1;
	delay();
	SDA=1;
	delay();
}

启动信号

//启动信号
void start() //SCL在高电平期间,SDA出现下降沿
{
	SDA=1;
	delay();
	SCL=1;
	delay();
	SDA=0;
	delay();
}

应答信号

//应答信号
void respon()  //SCL为高电平期间,SDA被从设备拉低
{
	uchar i=0;
	SCL=1;
	delay();
	while((SDA==1)&&(i<255))
	{
		i++;
	}
	SCL=0;
	delay();
}

停止信号

//停止信号
void stop()  //SCL在高电平期间,SDA出现上升沿信号
{
	SDA=0;
	delay();
	SCL=1;
	delay();
	SDA=1;
	delay();
}

写一个字节

//写一个字节
void write_byte(uchar date)
{
	uchar i,temp;
	temp=date;
	for(i=0;i<8;i++)
	{
		temp=temp<<1;   //temp左移一位,最高位移入寄存器PSW的CY位,赋值给SDA
		SCL=0;  //IIC在进行数据传送时,SCL为高电平期间,SDA需保持稳定,因此拉低SCL
		delay();
		SDA=CY;
		delay();
		SCL=1;
		delay();
	}
	SCL=0;   
	delay();
	SDA=1;
	delay();
}

读一个字节

//读一个字节
uchar read_byte()
{
	uchar i,k;
	SCL=0;
	delay();
	SDA=1;
	for(i=0;i<8;i++)
	{
		SCL=1;
		delay();
		k=(k<<1)|SDA;
		SCL=0;
		delay();
	}
	delay();
	return k;
}

EEPROM-AT24C02

  • WP:写保护输入端,用于硬件数据保护,当其为低电平时,可以对整个存储器进行正常的读写操作,当其为高电平时,存储器具有写保护功能,读操作不受影响。
//例程:
#include <reg52.h>

#define uchar unsigned char
#define uint unsigned int
bit write=0;  //写24C02的标志
sbit SDA=P2^0;
sbit SCL=P2^1;
sbit dula=P2^6;
sbit wela=P2^7;
uchar sec,tcnt;

unsigned char code table[]={
	 0x3f,0x06,0x5b,0x4f,
     0x66,0x6d,0x7d,0x07,
   0x7f,0x6f,0x77,0x7c,
     0x39,0x5e,0x79,0x71,
};

void delay()  //延时4-5us
{;;}
	
void delay1ms(uint z)
{
	uint x,y;
	for(x=z;x>0;x--)
	{
		for(y=110;y>0;y--);
	}
}

//总线初始化
void init()  //总线在初始状态下保持高电平
{
	SCL=1;
	delay();
	SDA=1;
	delay();
}

//启动信号
void start() //SCL在高电平期间,SDA出现下降沿
{
	SDA=1;
	delay();
	SCL=1;
	delay();
	SDA=0;
	delay();
}

//应答信号
void respon()  //SCL为高电平期间,SDA被从设备拉低
{
	uchar i=0;
	SCL=1;
	delay();
	while((SDA==1)&&(i<255))
	{
		i++;
	}
	SCL=0;
	delay();
}

//停止信号
void stop()  //SCL在高电平期间,SDA出现上升沿信号
{
	SDA=0;
	delay();
	SCL=1;
	delay();
	SDA=1;
	delay();
}

//写一个字节
void write_byte(uchar date)
{
	uchar i,temp;
	temp=date;
	for(i=0;i<8;i++)
	{
		temp=temp<<1;   //temp左移一位,最高位移入寄存器PSW的CY位,赋值给SDA
		SCL=0;  //IIC在进行数据传送时,SCL为高电平期间,SDA需保持稳定,因此拉低SCL
		delay();
		SDA=CY;
		delay();
		SCL=1;
		delay();
	}
	SCL=0;   
	delay();
	SDA=1;
	delay();
}

//读一个字节
uchar read_byte()
{
	uchar i,k;
	SCL=0;
	delay();
	SDA=1;
	for(i=0;i<8;i++)
	{
		SCL=1;
		delay();
		k=(k<<1)|SDA;
		SCL=0;
		delay();
	}
	delay();
	return k;
}

void write_add(uchar address,uchar date)
{
	start();
	write_byte(0xa0);  //1010 0000  AT24C02的芯片地址1010 A2 A1 A0 R/W 
	respon();
	write_byte(address);  //器件内部地址
	respon();
	write_byte(date);  //数据
	respon();
	stop();
}

uchar read_add(uchar address)
{
	uchar date;
	start();
	write_byte(0xa0);
	respon();
	write_byte(address);
	respon();
	start();
	write_byte(0xa1);
	respon();
	date=read_byte();
	stop();
	return date;
}
	
void display(uchar bai_c,uchar sh_c)  //显示程序
{
	wela=0;
	dula=0;  //关闭段选锁存器
	wela=1;  //选中位选锁存器
  P0=0x7e;  //0111 1110 第一位数码管使能
	wela=0;  //关闭位选锁存
	P0=0xff;
	
	dula=1;  //打开段选锁存器
	P0=table[bai_c];  //显示第一位数据
	dula=0;  //关闭段选锁存器
	delay1ms(5);
	
	wela=1;  //选中位选锁存器
	P0=0x7d;  //0111 1101 第二位数码管使能
	wela=0;  //关闭位选锁存
	P0=0xff;
	
	dula=1;  //打开段选锁存器
	P0=table[sh_c]; //显示第二位数据
	dula=0;  //关闭段选锁存器
	delay1ms(5);
}

void main()
{
	init();  //初始化,拉高IIC总线
	sec=read_add(2);   //读出保存的数据赋值给sec
	if(sec>100)  //防止读出数据为3位造成两位数码管显示乱码
	{
		sec=0;
	}
	TMOD=0x01;  //0000 0001 定时器T0,工作方式1:16为定时器/计数器
	ET0=1;  //中断允许寄存器IE 定时器/计数器0中断允许位
	EA=1;  //中断允许寄存器IE 全局中断打开
	TH0=(65536-45872)/256;
	TL0=(65536-45872)%256;  //50ms中断一次
	TR0=1;  //定时器控制寄存器TCON 启动定时器1
	while(1)
	{
		display(sec/10,sec%10);
		if(write==1)  //判断计时器是否计时1s
		{
			write=0;  //清0
			write_add(2,sec);  //在24C02的地址2写入数据sec
		}
	}
}

void t0() interrupt 1
{
	TH0=(65536-45872)/256;
	TL0=(65536-45872)%256;
	tcnt++;  //0.05sx20=1s
	if(tcnt==20)  //1s写一次24C02
	{
		tcnt=0;  //清0
		sec++;  //每隔1s,sec加1,定时1s
		write=1;  //写24C02
		if(sec==100)  //到100s后归零
		{ 
			sec=0;
		}
	}
	
}
赞赏