1、第6章 单片机单总线、I2C和SPI应用 总线就是一个公共的连接线,所有外围设备都可以通过它与单片机相连接,是信息传递的通道。总线的种类很多,常用的有单总线(1-wire)、集成电路总线I2C(也称IC、IIC,Inter-Integrated Circuit)和串行外设接口SPI(Serial Peripheral Interface),分别需要1、2、3条线的连接。(1)单总线需要1条线:一条数据线通信。(2)I2C需要2条线:一条时钟线,一条数据线。(3)SPI需要3条线:一条时钟线,一条数据接收线,一条数据发送线。这些总线传输,结构简单、传输速度快、传输距离远,被越来越多的系统应用。6
2、.1 单总线单总线DS18B20测温项目设计测温项目设计项目单总线单总线概述单总线概述 近年来,美国的达拉斯半导体公司(近年来,美国的达拉斯半导体公司(DALLAS SEMICONDUCTOR)推出了一项特有的单总线()推出了一项特有的单总线(1Wire Bus)技术。它采用单根信号线,既可传输时钟,又能传输)技术。它采用单根信号线,既可传输时钟,又能传输数据,而且数据传输是双向的,因而这种单总线技术具有线数据,而且数据传输是双向的,因而这种单总线技术具有线路简单,硬件开销少,成本低廉,便于总线扩展和维护等优路简单,硬件开销少,成本低廉,便于总线扩展和维护等优点。点。序列号序列号 产品型号:产
3、品类别(例如产品型号:产品类别(例如18B20为为0 x28)产品序列号:产品序列号:48位的身份证,可寻址。位的身份证,可寻址。CRC校验码:校验码:CRC(Cyclic Redundancy Check)在)在使用时使用时,将前将前56位按位按CRC多项式计算出多项式计算出CRC值值,然后与然后与ROM中高中高8位的位的CRC值比较值比较,若相同则表明数据传送正确若相同则表明数据传送正确,否则要否则要求重传。求重传。DS-18B20 数字温度传感器 DS18B20是美国DALLAS公司生产的1-wire式单总线的数字温度传感器。具有耐磨耐碰,体积小,线路简单,使用方便,大大提高了系统的抗干
4、扰性。适合于恶劣环境的现场温度测量。例如:冷冻库,粮仓,储罐,电讯机房,电力机房,电缆线槽等测温,汽车空调、冰箱、冷柜、以及中低温干燥箱等。27.0DQ2VCC3GND1U3DS18B20一、主要特点 1.1.1-wire1-wire式式 单总线有单总线有4.7K10K 4.7K10K 的上拉电阻,这样,当的上拉电阻,这样,当总线闲置时其状态为高电平。总线闲置时其状态为高电平。2.2.分辨率可以在分辨率可以在9-129-12位位选择(温度转换后的数字信号是选择(温度转换后的数字信号是9-9-1212位位:0.5 0.0625 0.5 0.0625 )3.3.测温范围测温范围 5555125,1
5、25,在在 -10+85-10+85C C 范围内,范围内,精度为精度为 0.5 0.5C C。4.4.每个器件都有每个器件都有唯一的序列号。唯一的序列号。5.5.内部有内部有温度上限、下限的报警温度上限、下限的报警设置,有报警功能设置,有报警功能 6.6.支持支持多点组网多点组网功能,多个功能,多个DS18B20DS18B20可以并联在唯一的总可以并联在唯一的总线上,实现多点测温线上,实现多点测温 7.7.供电方式灵活供电方式灵活 ,可以外接电源,也可以通过内部寄生电可以外接电源,也可以通过内部寄生电路从数据线上获取电源路从数据线上获取电源二、DS18B20管脚排列DS18B20DS18B2
6、0的管脚排列的管脚排列1.GND1.GND为电源为电源 地;地;2.DQ2.DQ(I/OI/O)为数字信号输入输出端;)为数字信号输入输出端;3.VDD3.VDD为外接供电电源输入端为外接供电电源输入端(在寄生电源接线方式时接地)(在寄生电源接线方式时接地)两种供电方式:两种供电方式:通常为了给通常为了给 1-WIRE 设备提供足够的电源,设备提供足够的电源,1.外部电源:需要一个上拉电阻,将外部电源:需要一个上拉电阻,将 1-WIRE 总线上拉至总线上拉至+5V 电电源。源。2.寄生电源:再需要一个寄生电源:再需要一个 MOSFET 管将管将 1-WIRE 总线上拉至总线上拉至+5V 电源。
7、电源。三、三、DS18B20内部结构内部结构 如图所示,主要由如图所示,主要由4部分组成:部分组成:64 64 位光刻位光刻 ROMROM、温度传感器、温度报警触发器、温度传感器、温度报警触发器TH和和TL、配、配置寄存器。置寄存器。温度传感器温度传感器 DS18B20DS18B20中的温度传感器完成对温度的测量,中的温度传感器完成对温度的测量,把温度转换为对应的数字量(把温度转换为对应的数字量(9 9位或位或1212位,出位,出厂时默认厂时默认1212位,最高位是符号位)。位,最高位是符号位)。该数字量和温度之间的对应转换格式,见下图,该数字量和温度之间的对应转换格式,见下图,其中其中S S
8、为符号位,即数据格式以为符号位,即数据格式以0.0625/LSB0.0625/LSB形式表达。高形式表达。高5 5位为符号位:位为符号位:0 0为正,为正,1 1为负。为负。正:正:*0.06250.0625;负:(取反加;负:(取反加1 1)*0.06250.0625 配置寄存器配置寄存器最高位用于设置最高位用于设置DS18B20DS18B20在工作模式还是在测试模式。在在工作模式还是在测试模式。在DS18B20DS18B20出厂时该位被设置为出厂时该位被设置为0 0,用户不要去改动。,用户不要去改动。R1R1和和R0R0用来设置分辨率,(用来设置分辨率,(DS18B20DS18B20出厂时
9、被设置为出厂时被设置为1212位)位)低五位一直都是低五位一直都是“1”1”高速暂存存储器高速暂存存储器RAMRAM 高速暂存存储器由9个字节组成,见下图.四、DS18B20的工作时序(单线接口访问(单线接口访问DS18B20DS18B20的协议的协议 )单片机对单片机对DS18B20DS18B20操作:温度转换、读取温度、存操作:温度转换、读取温度、存取报警上下限等。取报警上下限等。根据根据DS18B20DS18B20的通讯协议,单片机每次访问的通讯协议,单片机每次访问DS18B20DS18B20都必须遵循以下顺序:都必须遵循以下顺序:初始化初始化18B20 18B20;发发ROMROM操作
10、命令;操作命令;发发RAMRAM操作命令:操作命令:相应操作。相应操作。1.1.ROM操作命令操作命令2.RAMRAM操作命令操作命令五、DS18B20的基本操作 在以上的工作流程中,首先要初始化18B20,然后发各种命令以及数据传输都涉及到对DS18B20的读写操作,所以共涉及到以下3个工作时序:1.初始化操作2.写操作 (发命令)3.读操作 (读温度)1.初始化时序对应时序图,实现初始化的程序设计如下。对应时序图,实现初始化的程序设计如下。uchar DS18B20_init(void)/DS18B20初始化初始化uchar i,s=0;DQ=0;i=250;while(i0)i-;/50
11、0us,范围范围:480-960DQ=1;i=20;while(i0)i-;/40us,范围范围:15-60s=DQ;/读存在脉冲,如果读存在脉冲,如果DQ为为0,说明初始化成功;说明初始化成功;为为1,说明损坏或不存在,说明损坏或不存在while(!DQ);/DQ恢复恢复1return s;/函数值为函数值为0或或12.2.写时序写时序主机要产生一个写0时隙,就必须把数据线拉低并保持60us。主机要产生一个写1时隙,就必须把数据线拉低,在写时隙开始后的15us内允许数据线拉高。实现的程序代码如下所示。uchar i;DQ=1;i=2;while(i0)i-;/6us,间隔至少1usDQ=0;
12、i=2;while(i0)i-;/6us,间隔至少1usDQ=x;i=45;while(i0)i-;/90us,写一位时间范围:60-120usDQ=1;3.3.读时序读时序完成一个读时序过程,至少需要60us才能完成DQ=1;i=2;while(i0)i-;/6us,间隔至少间隔至少1usDQ=0;i=2;while(i0)i-;/6us,间隔至少间隔至少1usDQ=1;i=2;while(i0)i-;/6us,间隔至少间隔至少1usx=DQ;i=40;while(i0)i-;/80us,读一位时间范围:读一位时间范围:60-90usDQ=1;动手做动手做画出硬件电路图画出硬件电路图 启动温
13、度转换、读取温度值并显示在LCD的完整程序如下:#include#define uchar unsigned char#define uint unsigned intuint DS18B20(void);/读DS18B20双字节函数声明uchar DS18B20_init(void);/DS18B20初始化void Write(uchar);/写DS18B20一个字节函数声明uchar Read(void);/读DS18B20一个字节函数声明 void start(void);/启动DS18B20函数声明bit LCD_Busy();/忙函数声明void LCD_write_command(
14、uchar);/写命令函数声明void LCD_write_data(uchar);/写数据函数声明void LCD_init();/初始化函数声明void LCD_1_line(uchar pos1,uchar*LCDline1);/第一行显示函数声明void LCD_2_line(uchar pos2,uchar*LCDline2);/第二行显示函数声明void delay(uchar);sbit DQ=P22;/单总线sbit LCD_RS=P30;/将P3.0引脚取名为LCD_RS,控制寄存器选择sbit LCD_E=P32;/将P3.2引脚取名为LCD_E,使能端sbit LCD_R
15、W=P31;/将P3.1引脚取名为LCD_RW,控制读写选择bit ERROR=0;/*主程序*/void main(void)uchar LCDline1=TEMP:;/定义第一行要显示的字符串uchar LCDline216=;/定义第二行要显示的字符串unsigned int temp12,temp;LCD_init();LCD_1_line(0 x00,LCDline1);while(1)temp=DS18B20();if(temp0 xF800)/高5位为11111,温度为负temp=temp+1;/负数要有补码:取反加1LCDline20=;/负百位不显示LCDline21=-;/
16、显示温度的负temp12=(long int)temp*625/1000;/十进制数温度,计小数点一位,放大10倍LCDline22=(temp12%1000)/100+0 x30;/十位if(LCDline22=0 x30)LCDline21=;/首位为0,不显示LCDline22=-;/显示负elseLCDline20=;temp12=(long int)temp*625/1000;/十进制数温度,计小数点一位,放大10倍 LCDline21=(temp12%10000)/1000+0 x30;/温度百位,并+0 x30,形成LCD字符码if(LCDline21=0 x30)LCDline
17、21=;/首位为0,不显示LCDline22=(temp12%1000)/100+0 x30;/温度十位if(LCDline21=&LCDline22=0 x30)LCDline22=;/首位为0,不显示temp=(long int)temp*625/1000;/十进制数温度,计小数点一位,放大10倍 LCDline23=(temp%100)/10+0 x30;/温度个位LCDline24=.;/温度小数点LCDline25=(temp%10)/1+0 x30;/温度小数位LCDline26=0 xdf;/6位显示温度符号的oLCDline27=0 x43;/7位显示温度符号的CLCD_2_l
18、ine(0 x00,LCDline2);/*DS18B20初始化*/uchar DS18B20_init(void)uchar i,s=0;DQ=0;i=250;while(i0)i-;/500us,范围:480-960DQ=1;i=20;while(i0)i-;/40us,范围:15-60s=DQ;/存在脉冲为0,初始化成功;为1,损坏或不存在while(!DQ);/DQ恢复1return s;/函数值为0或1/*温度传感器写一字节*/void Write(uchar date)uchar i,j;for(j=0;j0)i-;/6us,间隔至少1usDQ=0;i=2;while(i0)i-;
19、/6us,间隔至少1usDQ=date&0 x01;/先写最低位i=45;while(i0)i-;/90us,写一位时间范围:60-120usdate=date1;/右移一位,再写,循环8次DQ=1;/*温度传感器读出一字节数据*/uchar Read(void)uchar i,j,date=0;for(j=0;j0)i-;/6us,间隔至少1usdate=1;/右移1位DQ=0;i=2;while(i0)i-;/6us,间隔至少1usDQ=1;i=2;while(i0)i-;/6us,间隔至少1usif(DQ=1)date=date|0 x80;/若DQ=1,最高位置1i=40;while(
20、i0)i-;/80us,读一位时间范围:60-90usDQ=1;return(date);/返回一字节数据/*启动DS18B20的一次温度转换*/void start(void)if(DS18B20_init()=1)ERROR=1;/无法初始化,出错elseWrite(0 xcc);/单个传感器,可以跳过ROM,即跳过多传感器识别Write(0 x44);/启动温度转换while(!DQ);/DQ由0转为1,则温度转换结束/*温度传感器读温度双字节*/uint DS18B20(void)unsigned int Temp=0;start();if(DS18B20_init()=1)ERROR
21、=1;elseWrite(0 xcc);/跳过ROM,即跳过多传感器识别Write(0 xbe);/读取DS18B20寄存器指令Temp=Read();/读取温度值低位字节Temp=(Read()8)|Temp;/读取温度值高位字节,并合成16位字节return(Temp);/返回温度值/*LCD显示程序 */*忙检测函数*/bit LCD_Busy()bit LCD_Busy;LCD_RS=0;LCD_RW=1;LCD_E=1;delay(1);LCD_Busy=(bit)(P0&0 x80);/取LCD数据首位,即忙信号位LCD_E=0;return LCD_Busy;/*写命令函数*/v
22、oid LCD_write_command(uchar cmd)while(LCD_Busy();/等待显示器忙检测完毕delay(1);LCD_RS=0;LCD_RW=0;P0=cmd;delay(1);LCD_E=1;delay(1);LCD_E=0;/*写数据函数*/void LCD_write_data(uchar dat)while(LCD_Busy();delay(1);LCD_RS=1;LCD_RW=0;P0=dat;delay(1);LCD_E=1;delay(1);LCD_E=0;/*初始化函数*/void LCD_init()LCD_write_command(0 x38)
23、;/162显示,57点阵,8位数据接口 delay(1);LCD_write_command(0 x01);/清屏delay(1);LCD_write_command(0 x06);/光标右移,字符不移 delay(1);LCD_write_command(0 x0C);/显示开,无光标,光标不闪烁delay(1);/*显示第一行字符*/void LCD_1_line(uchar pos1,uchar*LCDline1)uchar i=0;LCD_write_command(0 x80+pos1);/在第一行pos1位显示while(LCDline1i!=0)/显示首行字符串LCD_write
24、_data(LCDline1i);i+;delay(1);/*显示第二行字符*/void LCD_2_line(uchar pos2,uchar*LCDline2)uchar i=0;LCD_write_command(0 x80+0 x40+pos2);/在第二行pos2位显示while(LCDline2i!=0)/显示第二行字符串LCD_write_data(LCDline2i);i+;delay(1);/*延时函数t(ms)*/void delay(uchar t)uchar j,k;for(j=0;jt;j+)for(k=0;k255;k+)仿真运行图 6.2 I2C总线总线AT24C
25、02 项目项目设计设计项目I2C串行总线概述串行总线概述 I2C总线是总线是PHLIPS公司推出的一种串行总线,是高性能公司推出的一种串行总线,是高性能串行总线。串行总线。I2C总线只有两根双向信号线。总线只有两根双向信号线。一根是数据线一根是数据线SDA(Serial Data),另一根是时钟线另一根是时钟线SCL(Serial Clock)。)。I2C总线通过上拉电阻总线通过上拉电阻(4.7K或或10K)接正电源。当总线空闲接正电源。当总线空闲时,两根线均为高电平。连到总线上的任一器件输出的低电时,两根线均为高电平。连到总线上的任一器件输出的低电平,都将使总线的信号变低,即各器件的平,都将
26、使总线的信号变低,即各器件的SDA及及SCL都是线都是线“与与”关系关系。种类种类型号型号寻址地址寻址地址(设备号设备号+引脚配置引脚配置+读读写位写位)256x8/128x8静态静态RAM PCF8570/711010 A2A1A0 R/W 256x8静态静态RAM PCF8570C1011 A2A1A0 R/W 256B E2PROM PCF8582 1010 A2A1A0 R/W 256B E2PROM AT24C02 1010 A2A1A0 R/W 512B E2PROMAT24C041010 A2A1P0 R/W 1024B E2PROMAT24C081010 A2P1P0 R/W
27、2048B E2PROMAT24C161010 P2P1P0 R/W 日历时钟日历时钟PCF85911010 0 0 A0 R/W8位位I/O口口PCF85740100 A2A1A0 R/W 4位位LED驱动控制器驱动控制器SAA10640111 0 A1A0 R/W 4通道通道8位位A/D、1路路D/A转换转换器器PCF85911001 A2A1A0 R/W 每个接到每个接到I2C总线上的器件都有唯一的地总线上的器件都有唯一的地址。(例如:址。(例如:1010A2A1A0)由总线上传送数据的器件为发送器。由总线上传送数据的器件为发送器。由总线上接收数据的器件则为接收器由总线上接收数据的器件则
28、为接收器。在在80C51单片机应用系统的串行总线扩单片机应用系统的串行总线扩展中,我们经常遇到的是以展中,我们经常遇到的是以80C51单片机单片机为主机,其它接口器件为从机的单主机情为主机,其它接口器件为从机的单主机情况。况。一、起始和终止信号一、起始和终止信号 SCL线为高电平期间,线为高电平期间,SDA线由高电平向低电线由高电平向低电平的变化表示起始信号;平的变化表示起始信号;SCL线为高电平期间,线为高电平期间,SDA线由低电平向高电线由低电平向高电平的变化表示终止信号。平的变化表示终止信号。I2C总线的数据传送总线的数据传送起始信号为:当起始信号为:当SCL为高期间,为高期间,SDA由
29、高到低的跳变。该信号是一种电平由高到低的跳变。该信号是一种电平跳变时序信号。跳变时序信号。起始信号程序模拟如下。起始信号程序模拟如下。void delay_6us(void)/6微秒延时微秒延时 _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();void start()/开始数据传送,开始位开始数据传送,开始位 SDA=1;/SDA初始化为高电平初始化为高电平“1”SCL=1;/开始数据传送时,开始数据传送时,SCL为高电平为高电平“1”delay_6us();/按时序大于按时序大于4.7微秒微秒 SDA=0;/SDA的下降沿的下降沿:开始信号开始
30、信号 delay_6us();/按时序大于按时序大于4微秒微秒 SCL=0;/SCL为低电平时,为低电平时,SDA上数据才允许变化上数据才允许变化 停止信号为:当停止信号为:当SCL为高期间,为高期间,SDA由低到高的跳变;停止信号也是一种由低到高的跳变;停止信号也是一种电平跳变时序信号电平跳变时序信号(边沿边沿),而不是一个电平信号。,而不是一个电平信号。停止信号程序模拟如下。停止信号程序模拟如下。void stop()/结束数据传递结束数据传递 SDA=0;/SDA初始化为低电平初始化为低电平“0”SCL=1;/结束数据传送时,结束数据传送时,SCL为高电平为高电平“1”delay_6us
31、();/按时序大于按时序大于4微秒微秒 SDA=1;/SDA的上升沿的上升沿:结束信号结束信号 delay_6us();/按时序大于按时序大于4微秒微秒 SCL=0;/SCL为低电平时,为低电平时,SDA上数据才允许变化上数据才允许变化数据传送的应答与非应答应答信号应答信号ACK:主机读写,主机读写,接收设备接收设备产生,数产生,数据传输正确,产生应答。据传输正确,产生应答。非应答信号非应答信号NO ACK:主机读,:主机读,主机主机接收后接收后不产生应答,产生非应答,表示数据接收结不产生应答,产生非应答,表示数据接收结束。束。发送应答信号程序模拟如下。发送应答信号程序模拟如下。void ac
32、k()/应答应答 SDA=0;SCL=1;delay_6us();/按时序大于按时序大于4微秒微秒 SCL=0;SDA=1;发送非应答信号程序模拟如下。发送非应答信号程序模拟如下。void noack()/非应答非应答 SDA=1;SCL=1;delay_6us();/按时序大于按时序大于4微秒微秒 SCL=0;SDA=0;检测检测I2C设备应答信号程序模拟如下。设备应答信号程序模拟如下。bit detect_ack()/检测应答检测应答 bit detect_ack;SDA=1;SCL=1;delay_6us();/按时序大于按时序大于4微秒微秒 detect_ack=SDA;SCL=0;r
33、eturn detect_ack;在传输期间,如果从设备没有应答意味着将没有更多的数据要传送在传输期间,如果从设备没有应答意味着将没有更多的数据要传送或者设备没有准备好传送。这时,主设备要么产生停止信号,要么重新或者设备没有准备好传送。这时,主设备要么产生停止信号,要么重新发出起始信号。发出起始信号。二、数据位的有效性规定二、数据位的有效性规定 I2C总线进行数据传送时,总线进行数据传送时,(1)时钟信号为高电平期间)时钟信号为高电平期间,数据线上的数据,数据线上的数据必须保持稳定,必须保持稳定,(2)时钟线上的信号为低电平期间)时钟线上的信号为低电平期间,数据线上,数据线上的高电平或低电平状
34、态才允许变化。的高电平或低电平状态才允许变化。三、总线的寻址三、总线的寻址 I2C总线协议有明确的规定:采用总线协议有明确的规定:采用7位的寻址字位的寻址字节(寻址字节是起始信号后的第一个字节)。节(寻址字节是起始信号后的第一个字节)。D7D1位组成从机的地址。位组成从机的地址。D0位是数据位是数据传送方向位,为传送方向位,为“0”时表示主机向从机写数时表示主机向从机写数据,为据,为“1”时表示主机由从机读数据。时表示主机由从机读数据。主机发送地址主机发送地址时,总线上的每个从机都将这时,总线上的每个从机都将这7位地址码与自己的地址进行比较,如果相同,位地址码与自己的地址进行比较,如果相同,则
35、认为自己正被主机寻址,根据则认为自己正被主机寻址,根据R/W位将自己位将自己确定为发送器或接收器确定为发送器或接收器。从机的地址从机的地址由固定部分和可编程部分组成。由固定部分和可编程部分组成。在一个系统中可能希望接入多个相同的从机,在一个系统中可能希望接入多个相同的从机,从机地址中可编程部分决定了可接入总线该类从机地址中可编程部分决定了可接入总线该类器件的最大数目。如一个从机的器件的最大数目。如一个从机的7位寻址位有位寻址位有4位是固定位,位是固定位,3位是可编程位,这时仅能寻位是可编程位,这时仅能寻址址8个同样的器件,即可以有个同样的器件,即可以有8个同样的器件个同样的器件接入到该接入到该
36、I2C总线系统中总线系统中。四、数据传送格式四、数据传送格式(1)字节传送)字节传送 每一个字节必须保证是每一个字节必须保证是8位长度。数据传送时,先传位长度。数据传送时,先传送最高位(送最高位(MSB)。)。(2)数据帧格式)数据帧格式 I2C总线上传送的数据信号是广义的,既包括总线上传送的数据信号是广义的,既包括地址地址信号信号,又包括真正的,又包括真正的数据信号数据信号。在起始信号后必须传送一个从机的地址(在起始信号后必须传送一个从机的地址(7位),位),第第8位是数据的传送方向位(位是数据的传送方向位(R/W),),用用“0”表示主机发送数据(表示主机发送数据(W),),“1”表示主机
37、接收数据(表示主机接收数据(R)。)。每次数据传送总是由主机产生的终止信号结束。每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。另一从机进行寻址。数据传输格式起始起始-地址地址-读写读写-应答应答-数据数据-应答应答-停止停止I2C器件:AT24C02芯片AT24C02是一个2K位串行CMOS E2PROM程序存储器,内部含有256个8位字节,掉电保护,并可以多次电擦写管脚名称管脚名称功能功能A2、A1、
38、A0设备地址选择SDAI2C数据线SCLI2C时钟线WP写保护VCC+1.8V5.5V工作电压VSS地存储结构与寻址芯片寻址:1010(固定)A2A1A0(三位)R/W(读1写0)片内寻址:256(8位)动手做动手做画出硬件电路图画出硬件电路图 项目使用了二个按钮开关BUTTON1和BUTTON2,作为计件,分别将计件数存放到二个AT24C02中,随后,单片机分别取出二个AT24C02中的存放数,显示在液晶显示器LCD的第一行和第二行项目使用了二个AT24C02,U2器件的地址为0 xa0即为10100000B,其中1010为AT24C02,000为U2选择器件,0为写。U3器件的地址为0 x
39、a2即为10100010B,其中1010为AT24C02,001为U3选择器件,0为写。程序设计如下。#include#include /包含_nop_()函数定义的头文件#define uchar unsigned charsbit LCD_RS=P30;/将P3.0引脚取名为LCD_RS,控制寄存器选择sbit LCD_E=P32;/将P3.2引脚取名为LCD_E,使能端sbit LCD_RW=P31;/将P3.1引脚取名为LCD_RW,控制读写选择sbit button1=P10;/将第一按钮开关位定义为P1.0引脚sbit button2=P11;/将第二按钮开关位定义为P1.1引脚s
40、bit SDA=P34;/将串行数据总线SDA位定义在为P3.4引脚sbit SCL=P33;/将串行时钟总线SCL位定义在为P3.3引脚uchar num=;bit LCD_Busy();/忙函数声明void LCD_write_command(uchar);/写命令函数声明void LCD_write_data(uchar);/写数据函数声明void LCD_init();/初始化函数声明void delay(uchar);/延时函数声明void delay_6us();/延时6微秒函数声明 void LCD(uchar,uchar);/LCD显示函数声明void start();/开始函
41、数声明void stop();/停止函数声明void ack();/应答函数声明void no_ack();/非应答函数声明bit detect_ack();/检测应答函数声明uchar Read24c02_byte();/读24C02函数声明void Write24c02_byte(uchar);/写数据函数声明void Write24c02_addr_byte(uchar,uchar,uchar);/写地址函数声明uchar Read24c02_addr_byte(uchar,uchar);/读地址函数声明/*主程序*/void main(void)uchar num1,num2;ucha
42、r DATA_24C02;/存储从AT24C02读出的值LCD_init();/调用LCD初始化函数while(1)/无限循环if(button1=0)/如果该键被按下delay(10);/软件消抖,延时10msif(button1=0)/确实该键被按下 num1+;/计数值加1if(num1=100)/如果计满99 num1=0;/清0,重新开始计数while(button1=0);/等待按键松开Write24c02_addr_byte(0 xa0,0 x01,num1);/0 xa0即为1010 0000B:1010为AT24C02,000为选择器件,0为写.将计件值写入第一片AT24C0
43、2中的指定地址“0 x01”DATA_24C02=Read24c02_addr_byte(0 xa0,0 x01);/从AT24C02中0 x01地址读出计件值LCD(0 x5,DATA_24C02);/将计件值用1602LCD显示在第一行第五位if(button2=0)/如果该键被按下delay(10);/软件消抖,延时10msif(button2=0)/确实该键被按下 num2+;/计数值加1if(num2=100)/如果计满99 num2=0;/清0,重新开始计数while(button2=0);/等待按键松开Write24c02_addr_byte(0 xa2,0 x01,num2);
44、/0 xa2即为1010 0010B:1010为AT24C02,001为选择器件,0为写,将计件值写入第二片AT24C02中的指定地址“0 x01”DATA_24C02=Read24c02_addr_byte(0 xa2,0 x01);/从AT24C02中0 x01地址读出计件值LCD(0 x5+0 x40,DATA_24C02);/将计件值用1602LCD显示在第一行第五位/*开始数据传送,开始位*/void start()SDA=1;/SDA初始化为高电平“1”SCL=1;/开始数据传送时,SCL为高电平“1”delay_6us();/按时序大于4.7微秒SDA=0;/SDA的下降沿:开始
45、信号delay_6us();/按时序大于4微秒SCL=0;/SCL为低电平时,SDA上数据才允许变化/*结束数据传递*/void stop()SDA=0;/SDA初始化为低电平“0”SCL=1;/结束数据传送时,SCL为高电平“1”delay_6us();/按时序大于4微秒SDA=1;/SDA的上升沿:结束信号delay_6us();/按时序大于4微秒SCL=0;/SCL为低电平时,SDA上数据才允许变化/*应答*/void ack()SDA=0;SCL=1;delay_6us();SCL=0;SDA=1;/*非应答*/void no_ack()SDA=1;SCL=1;delay_6us();
46、SCL=0;SDA=0;/*检测应答*/bit detect_ack()bit detect_ack;SDA=1;SCL=1;delay_6us();detect_ack=SDA;SCL=0;return detect_ack;/*从AT24C02读取数据字节到单片机*/uchar Read24c02_byte()uchar i;uchar read_byte;SDA=1;/读方式:数据线为输入方式for(i=0;i8;i+)read_byte=1;/左移一位,补0read_byte|=(uchar)SDA;/将SDA上的数据通过按位“或”运算存入字节SCL=1;delay_6us();/按时
47、序大于4.7微秒 SCL=0;return(read_byte);/返还读取的AT24C02字节数据/*向AT24C02的当前地址写入数据*/void Write24c02_byte(uchar write_byte)uchar i;for(i=0;i8;i+)/循环移入8个位SDA=(bit)(write_byte&0 x80);/传送时高位在前,低位在后SCL=1;delay_6us();/按时序大于4.7微秒SCL=0;delay_6us();/按时序大于4.7微秒write_byte=1;/左移一位,传送下一位/*向AT24C02中的指定单个地址add写入数据dat*/void Wri
48、te24c02_addr_byte(uchar I2C,uchar addr,uchar byte)start();/开始送数据Write24c02_byte(I2C);while(detect_ack();/等待应答Write24c02_byte(addr);/写入指定地址while(detect_ack();/等待应答Write24c02_byte(byte);/向这个地址写入数据while(detect_ack();/等待应答stop();/停止数据传递delay(4);/一字节的写入周期为1ms,延时1ms以上/*从指定AT24C02中的指定地址读取数据*/uchar Read24c0
49、2_addr_byte(uchar I2C,uchar addr)uchar read_addr_byte;start();/开始数据传递Write24c02_byte(I2C);while(detect_ack();/等待应答Write24c02_byte(addr);/写入指定地址while(detect_ack();/等待应答start();/开始数据传递Write24c02_byte(I2C+0 x01);/+0 x01为读while(detect_ack();/等待应答read_addr_byte=Read24c02_byte();no_ack();stop();/停止数据传递ret
50、urn read_addr_byte;/返回读取的数据/*LCD显示程序 */*忙检测函数*/bit LCD_Busy()bit LCD_Busy;LCD_RS=0;LCD_RW=1;LCD_E=1;delay(1);LCD_Busy=(bit)(P0&0 x80);/取LCD数据首位,即忙信号位LCD_E=0;return LCD_Busy;/*写命令函数*/void LCD_write_command(uchar cmd)while(LCD_Busy();delay(1);LCD_RS=0;LCD_RW=0;P0=cmd;delay(1);LCD_E=1;delay(1);LCD_E=0;