实验八I2C通信协议一、实验目的:1、培养学生阅读资料的能力;2、加深学生对I2C总线通信协议的理解;3、加强学生对模块化编程的理解;二、实验环境:1、硬件环境:PC机一台、单片机实验板一块、母头串口交叉线、USB电源线;2、软件环境:keiluVision2集成开发环境;STC-ISP下载上位机软件;三、实验原理:要学会I2C通信协议的编程,关键是要看懂并掌握其时序图,理解对I2C通信协议相关子程序的实验编写。I2C通信协议的总线时序图如下所示:I2C总线时序图I2C相关子程序的详细介绍1、起始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。2、结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。起始信号和结束信号的时序图如下所示:起始信号和结束信号的时序图起始信号的流程如下:1、SCL和SDA拉高,保持时间约为0.6us-4us;2、拉低SDA,保持时间为约为0.6us-4us;3、拉低时钟线结束信号的流程如下:1、SCL置高电平,SDA置低电平,保持时间约为0.6us-4us2、SDA拉高,保持时间约为1.2-4us;应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。应答信号的时序图如下所示:应答时序图发送时的应答信号;**********应答信号**********ACK:SETBSDA;数据线置高SETBSCL;时钟线置高ACALLDELAYJBSDA,$;等待数据线变低ACALLDELAYCLRSCL;时钟线置低RET注意:这里如果数据线一直为高将进入死循环,所以一般我们都会在这做一个容错的处理。具体的程序如下:ACK:MOVR4,#00HSETBSDASETBSCLLOP0:JNBSDA,LOPDJNZR4,LOP0;循环255次LOP:ACALLDELCLRSCLRET接收时的应答信号ACK1:CLRSDA;数据线置低SETBSCL;时钟线置高NOPNOPCLRSCL;时钟线置低SETBSDA;数据线置高RET3、字节的发送和接收写周期时序图一字节数据发送子程序,流程如下:图6-22发送子程序流程图开始时钟线置低累加器左移一位数据传送时钟线置高一个应答信号周期结束8位信号完否?YN(2)一字节数据接收子程序,流程如下:图6-23接收子程序流程图7、写操作(1)字节写图6-24字节写时序图流程如下:开始数据线置高时钟线置高数据传送累加器左移一位时钟线置低结束8位信号完否YN图6-25字节写的流程图(2)页写图6-26页写时序图页写流程如下:写器件命令应答信号器件地址应答信号数据地址应答信号停止信号延时结束开始信号78、读操作(1)选择读图6-28选择读时序图写器件命令应答信号器件地址应答信号数据地址应答信号停止信号开始信号写数据字节初值数据地址-1数据写完否?YN图6-27页写流程图图6-29NOACK时序图(2)连续读图6-30连续读时序图四、实验原理图:I2C总线电路图五、实验例题:例题一编写一程序,实现I2C的指定字节读写,用24C08来记录单片机复位或者开机的次数,并将复位或者开机的次数显示在数码管上。程序分析本程序利用单片机复位时程序总是从0000H开始执行的特性。用24C08的一个单元来存储开机的次数,程序一开始就将次数读出来,加上本次的开机,显示在数码管上。再将加1之后的数据存入24c08的对应单元。程序代码;**************************************************************************;****程序功能:实现对24C08的字节读写,用24C08记录开机次数,显示在数码管上;****程序编写:李代勇;****编写日期:2007/2/04;**************************************************************************;**************************************************************************;初始化数据设置;**************************************************************************SDAEQUP1.7;定义数据线为P1.7(实验板上已固定,不可更改)SCLEQUP1.6;定义始终线为P1.6I2C_AddrEQU10H;定义地址缓冲区,存储要读写24C08的单元地址I2C_DataEQU12H;用于缓冲要读写的数据D_BUF0EQU55H;数码管显示缓冲区(个位)D_BUF1EQU56H;数码管显示缓冲区(十位)D_BUF2EQU57H;数码管显示缓冲区(百位)FLAGBIT00H;定义标志位,确定百位是否等于0,不等于0置1;**************************************************************************;程序段:主程序;**************************************************************************ORG0000HLJMPSTARTORG0030HSTART:MOVSP,#70HMOVI2C_Data,#00HMOVI2C_Addr,#0FHACALLI2C_READMOVI2C_Data,A;读出数据INCI2C_DataMOVI2C_Addr,#0FH;更新数据ACALLI2C_WRITELCALLDATAEDIT;数据加工WAIT:LCALLDISPLAY;显示数据AJMPWAIT;**************************************************************************;函数名称:DATAEDIT;输入参数:I2C_Data;输出参数:D_BUF0,D_BUF1,D_BUF2;函数功能:处理数据,确定每一位数码管要显示的数字,;因为01,只要求显示1,10只要求显示10(而不是显示010);101要求显示101;**************************************************************************DATAEDIT:MOVA,I2C_DataMOVB,#100DIVABJZL1;根据百位是否等于0确定百位的显示MOVD_BUF2,A;百位不等于0,直接显示百位的数字SETBFLAG;百位不等于0,标志位置1AJMPNEXL1:MOVD_BUF2,#25;百位等于0,对百位赋一个超过段码个数的值NEX:MOVA,B;对十位数的处理MOVB,#10DIVAB;根据十位是否等于0确定十位的显示JZL2L3:MOVD_BUF1,A;十位等于0,百位不等于0,显示十位的数字AJMPNEX1L2:JBFLAG,L3;十位等于0,判断百位是否也为0MOVD_BUF1,#22;百位、十位等于0,十位赋超过段码个数的值NEX1:MOVD_BUF0,B;个位的赋值RET;**************************************************************************;函数名称:DISPLAY;输入参数:D_BUF0,D_BUF1,D_BUF2;输出参数:无;占用资源:R6,R7,R0;函数功能:将D_BUF0,D_BUF1,D_BUF2显示在数码管上;**************************************************************************DISPLAY:MOVR6,#3MOVR7,#0FEHMOVDPTR,#TABMOVR0,#D_BUF0LOPP1:MOVA,@R0MOVCA,@A+DPTRMOVP0,AMOVP2,R7LCALLDELAY5MS;动态扫描延时,消除余辉效应INCR0MOVA,R7RLAMOVR7,ADJNZR6,LOPP1NOPRET;**************************************************************************;函数名称:DELAY5MS;输入参数:无;输出参数:无;占用资源:60H,61H,62H;函数功能:延时5MS(24MHz晶振);**************************************************************************DELAY5MS:MOV60H,#1DDD2:MOV61H,#20DDD1:MOV62H,#248DJNZ62H,$DJNZ61H,DDD1DJNZ60H,DDD2RET;**************************************************************************;函数名称:I2C_WRITE;输入参数:I2C_Addr,I2C_data;输出参数:无;函数功能:24C08字节写,将I2Cdata写入24C08的Address地址单元;**************************************************************************I2C_WRITE:I2C_WRITE_A:ACALLI2C_STARTMOVA,#10100000BLCALLI2C_SEND8BITLCALLI2C_ACKJCI2C_WRITE_ARS_ADDR:MOVA,I2C_AddrLCALLI2C_SEND8BITLCALLI2C_ACKJCRS_ADDRRS_DATA:MOVA,I2C_DataLCALLI2C_SEND8BITLCALLI2C_ACKJCRS_DATALCALLI2C_STOPLCALLDELAY10MSRET;**************************************************************************;函数名称:I2C_READ;输入参数:I2C_Addr;输出参数:I2C_Data;函数功能:24C08指定字节读,将24C08的Address地址单元的内容读到I2Cdata;**************************************************************************I2C_READ:I2C_READ_A:LCALLI2C_STARTMOVA,#10100000BLCALLI2C_SEND8BITLCALLI2C_ACKJCI2C_READ_ARS_ADDR2:MOVA,I2C_AddrLCALLI2C_SEND8BITLCALLI2C_ACKJCRS_ADDR2LCALLI2C_STARTREAD_ADDR:MOVA,#10100001BLCALLI2C_SEND8BITLCALLI2C_ACKJCREAD_ADDRACALLI2C_RECEIVE8BITMOVI2C_Data,AACALLI2C_ACKLCALLI2C_STOPRET;**************************************************************************;函数名称:I2C_START;输入参数:无;输出参数:无;函数功能:实现24C08启动时序的编写;********************************************************************