当前位置:文档之家› An464

An464

P87LPC76x 单片机作为I2C总线主控器

(AN464Using the P87LPC76x microcontroller as an I2C bus master)

摘要

本文首先回顾了I2C总线的基本概念和功能包括主控器被控器数据传送地址设置传送格式以及被控器地址的使用本文接着描述了P87LPC76x的I2C接口的硬件特性

包括总线控制寄存器数据寄存器和配置寄存器以及定时器I最后本文还给出了支持单个主控器的ASM程序范例包括数据传送和接收以及总线错误恢复的编程规范

概述

P87LPC76x是一种小封装低成本高性能的单片机它采用80C51加速处理器结构而且片内带有支持I2C总线的硬件接口

PHILIPS公司发展的I2C已成为总线标准并且由PHILIPS公司申请了专利I2C总线可使芯片通过一条简单的双向两线总线直接进行通信工程设计者可以很方便的使用众多的CMOS器件和片内带有I2C接口的双极型芯片进行工业数字控制家电产品和通信设备等方面的设计图1给出了一个典型的系统配置

图1 典型I2C总线的配置

I2C基本系统的器件接口是非常简单的因为这些器件可以直接用两条线相连接一条串行数据线SDA和一条串行时钟线SCL因为设计者不需要设计总线接口而且方

块草图上的功能模块对应于实际的芯片所以设计者可以很快地完成从方块草图到最后方案的设计修改和升级所设计的系统也很方便只需从总线上卸下芯片或把芯片加到总线上

I2C总线的简单性没有影响它的实用性I2C总线是一种可靠的支持多主控器的总线片

内有地址定义功能和数据传送协议参见图2其它带有I2C总线的器件也是低成本和小

封装的芯片

图2 I2C总线的连接

系统设计者利用P87LPC76x单片机设计系统将会是很有效率的P87LPC76x内部集成

的I2C总线协议使系统实现完全软件化定义运用一些有用的软件模块库可以提高编程效率另外P87LPC76x 所支持的多主控器功能可以实现系统的快速测试以及通过外接器

件进行系统的校正

P87LPC76x可以加密EPROM程序P87LPC76x可以在I2C系统上作为主控器或被控

器除了有I2C接口外P87LPC76x还可以实现I/O和RAM的扩展以及访问EEPROM 和进行单片机之间的通信

P87LPC76x I2C总线多主控器功能也非常有用但多数设计场合下并不需要运用这个功能多数系统都运用单个主控器初始化器件之间的通信信息本文详细介绍了如何使用

P87LPC76x作为I2C总线的主控器本文还介绍了I2C总线以及P87LPC76x单片机的I2C硬件接口最后本文还给出了一个关于I2C单个主控器通信的演示软件规范这个软件规范十分通用可以方便地把它移用到其它系统设计中

本文中有关I2C总线的介绍不十分完整有关I2C总线和P87LPC76x单片机的详细介绍可以参考相关资料

I2C总线

I2C总线只有两条线串行数据线SDA和串行时钟线SCL这两条线都通过一个上拉电阻与正电源连接而且在总线不忙时保持高电平总线上每个器件都有唯一的地址

无论它是单片机LCD驱动器存储器或者是键盘接口根据器件的功能这些器件可

以在总线上作为发送器或接收器产生信号或数据的器件为发送器接收信号或数据的器件为接收器显而易见外围器件例如LCD驱动器只能作为接收器而单片机或存储器可以发送和接收数据

主控器和被控器

总线上的主控器和被控器都可以发送数据主控器初始化数据传送格式和产生与传送数据相应的时钟信号而被控器将被主控器寻址注意主控器可以作为发送器或接收器在发送数据时作为发送器在接收数据时作为接收器在这两种情况下主控器都要初始化数

据传送格式另一方面被控器也可以作为发送器或接收器

I2C总线是一个多主控器的总线I2C总线系统中可以有多个主控器这些主控器都可

以初始化数据传送格式和控制总线参见图2一个单片机在一次数据传送过程中可以作

为主控器也可以在另一次数据传送过程中作为被控器这时由总线上另一个单片机初始化数据传送格式总线上主控器/被控器的关系不是固定的在每次数据传送时都可能会改变

当多个主控器连接到I2C总线上时也许会同时占据总线为了避免总线冲突和通信混淆需要有总线仲裁系统中各个器件以线与方式连接在总线上这样I2C总线可以实现总线仲裁和时钟同步在典型多主控器系统中相应的单片机软件应该可以方便地使单片

机器件在主控器和被控器模式之间变换以及避免总线仲裁过程中的数据丢失

数据传送

在每个时钟脉冲出现时总线传送一个数据位参见图3在时钟信号高电平期间SDA

线上的数据位应保持稳定如果此时改变SDA线数据则被认为是总线的控制信号SCL线为高电平时SDA线电平由高至低的变化作为总线的起动条件SCL线为高电平时SDA 线电平由低至高的变化作为总线的停止条件参见图4在起动条件和停止条件后的一小

段时间之间总线将处于忙状态起动条件和停止条件由主控器产生

图3 I2C总线上的位传送

图4 起动条件和停止条件

在起动和停止条件之间所传送的数据数量不受限制传送数据的每个字节为8位字节

最高位数据先被传送再依次传送其它位最后传送一个应答位参见图5应答位的时

钟脉冲由主控器产生在出现与应答位对应的时钟脉冲时产生应答位的器件将拉低SDA 线发送器则释放SDA线参见图6被控接收器在接收到每一个字节数据之后必须发送

一个应答信号而主控器在接收到被控发送器发送的数据后也必须发送一个应答信号如果接收器不能立即接收数据它将拉低SCL线迫使发送器进入等待状态设计I2C系统时还需要考虑没有接收到应答信号的情况例如被寻址的器件正处于忙状态而没有发送应答信号这时主控器在定时器溢出后应产生停止条件使总线进行其它的数据传送

这里的其它的数据传送是由同一个主控器或者多主控器系统中其它的主控器产生的

图5 I2C总线上的数据传送

图6 I2C总线上的应答位

关于每个字节数据后产生应答位这里有两个例外情况第一个例外是当主控器

作为接收器时它必须在被控器发送完最后一个字节数据后产生非应答信号与非应答信号相对应的时钟脉冲依然由主控器产生但主控器并不拉低SDA线我们把这种情况下的非

应答信号称之为负应答位第二个例外是被控器不再接收多余的字节数据时它应该

发送一个负应答位器件发送数据而对方不接收时产生这种情况

I2C总线包含特殊的接口可用于与那些完全由软件模拟实现I2C功能的单片机以I2C总线方式通信这称之为低模式系统中所有的器件都内置I2C硬件接口时可以避免低

模式

寻址和数据传送格式

总线上的每一个器件都有自己唯一的地址在发送数据之前主控器必须先发送被控器的地址系统中与这个地址相匹配的被控器将产生一个应答信号主控器在产生起动条件之后完成寻址

总线上的器件地址为7位地址字节中最后一位是方向位R/W位R/W为0表

示主控器作为发送器写操作R/W为1表示主控器作为接收器读操作图7中给

出了一个完整的数据传送格式由一个地址字节写操作和两个数据字节组成

图7 I2C总线上的完整数据传送

起动条件产生后主控器发送地址字节时总线上每个器件将自己的地址与总线上的地址字节进行比较如果某个器件的地址匹配这个器件将传送一个应答信号并可根据R/W 位的值确定出它是被控接收器还是被控发送器

I2C总线上的每个器件节点都有唯一的7位地址单片机的地址完全是可编程的而外围器件的地址分为固定和可编程两部分除了这里所讨论的标准地址外I2C总线协议允许使用广播地址和CBUS设备地址

当主控器只与一个器件通信时数据传送格式参见图7图中R/W位可以是任一数据

传送方向数据传送结束后主控器将产生停止条件如果主控器需要寻址系统中其它的器件它可以产生一个新的起动条件开始另一次数据发送或接收

在主控器与几个不同的器件通信的情况下将采用重复起动方式在传送完最后一

个字节数据包括应答位或负应答位之后主控器产生另一个起动条件接着发送地址字节和数据而不用产生停止条件主控器与多个器件通信时可以使用读操作和写操作在最后一个数据传送结束时主控器产生停止条件并释放总线相关的数据传送格式参见图8注意产生重复起动条件时可以变换所寻址的器件以及数据传送方向而且不用释放总线

我们将从下文中了解到即使在主控器只与一个器件通信时变换数据传送方向也是很方便的在单个主控器系统中利用重复起动条件结束每次数据传送比用一个停止条件和一个起动条件更有效率在多主控器情况下想确定出使用哪种数据格式最有效率将是很复杂的事情因为当主控器使用重复起动模式时它会长时间占用总线这样将妨碍其它器件使用总

线传送数据

图8 I2C总线数据格式

单元地址的使用

对于I2C总线上的某些芯片其唯一的总线地址是不足以解决有效的通信这时需要使用芯片内部寻址机制典型例子是当我们想访问被寻址存储器内部所指定的数据或者连续的存储器地址数据时就需要使用存储器的单元地址

典型的I2C存储器芯片例如PCF8570片内有一个地址寄存器这个寄存器在每一个字节数据被读或写后自动增益当主控器与PCF8570连接通信时主控器必须在发送被控器地址字节后接着发送单元地址这个单元地址是主控器传送单个字节数据的存储器内部地

址或者是传送多个字节数据的连续存储器区域的起始地址单元地址是为8位字节与器件地址不同单元地址不包含方向位R/W与总线上所有字节数据传送格式一样单元

地址后必须跟随一个应答位

存储器写数据操作格式参见图9a产生起动条件之后主控器发送被控器地址和方向位这里为写操作模式R/W=0,接着发送单元地址和一些字节数据以及产生停止条件

每次发送一个字节同时存储器的地址寄存器自动产生增益

存储器读数据操作格式参见图9b开始时与写数据操作格式一样主控器发送被

控器地址和方向位R/W=0为写模式接着发送单元地址为了改变数据传送方向主

控器产生一个重复起动条件接着发送被控器地址和方向位这时R/W=1为读模式存储器将发送从指定单元地址开始的字节数据到总线上而且每个字节数据后跟随一个由主控器

产生的应答位但是最后一个字节数据后跟随一个负应答位表明数据传送结束最后由主控器产生的停止条件将结束读数据操作

图9 I2C总线单元地址的使用

P87LPC76x I2C硬件接口

P87LPC76x系列单片机的I2C硬件接口可以高速运行也可以简化系统通信的软件编

程I2C硬件接口激活和监视SDA线和SCL线执行总线仲裁和检测总线状态错误以及

处理时钟延时和时钟同步P87LPC76x I2C硬件接口包括一个总线溢出定时器即定时器I P87LPC76x I2C硬件接口与软件同步运行甚至在进入循环状态或产生中断时也同步运行

当激活I2C 总线时P87LPC76x端口0中P0.0与P0.1可以分别作为SCL和SDA线模拟行使I2C总线功能端口0的管脚都有开漏输出

P87LPC76x五个中断源中的两个可用于I2C总线设置中断使能寄存器中标志位E12

使能I2C中断而且在地址033H处开始运行I2C服务程序在SCL线出现上升沿表明总线上有新的数据产生起动条件产生停止条件或者总线仲裁失败时系统就会发生一个I2C中断请求事先已使能中断标志位ATN的置位将导致I2C总线中断的发生参见下文关于标志位ATN被置位的条件设置标志位ETI可以使能定时器I产生溢出中断其服

务程序在地址73H处开始执行

I2C总线由3个特殊功能寄存器控制这3个寄存器为I2C控制寄存器I2CON I2C

配置寄存器I2CFG和I2C数据寄存器I2DAT它们的地址参见表1

表1 I2C总线的特殊功能寄存器地址

寄存器位地址

名称符号地址最高位最低位

I2C控制寄存器I2CON D8 DF DE DD DC DB DA D9 D8

I2C数据寄存器I2DAT D9 - - - - - - - -

I2C配置寄存器I2CFG C8 CF CE CD CC CB CA C9 C8

定时器I

在I2C总线应用中定时器I用于产生I2C总线时序和监视总线在非I2C总线应用中定时器I通常作为一个固定的时基

定时器I用于产生I2C总线时序时可以产生I2C时钟(SCL)信号定时器I的时钟周期为一个机器周期osc/12,SCL线的跳变速率是定时器I时钟频率的几倍由于P87LPC76x 单片机可以在宽范围振荡器频率下运行因此需要调整SCL线频率为振荡器的某一分频

这样I2C总线依据系统振荡器频率可获得最佳的数据传输速率设置特殊功能寄存器I2CFG 中的两个标志位CT0和CT1可以调整SCL线的时序周期参见表2在定时器I中第4位

翻转时CT0和CT1的数值将被装入定时器I的最后两位当CT0和CT1被设置为11

时定时器I的第3位为1其它情况下第3位为0当定时器I的第4位翻转时

SCL线也跟着翻转例如CT1=1且CT0=0时定时器I最后3位被预置为010即数

值2这时定时器I的时间计数为345678即6个时间计数或机器周期在计

数为8时定时器I的第四位将翻转SCL线也跟着翻转而定时器I的最后3位又重新被预置为010

表2 CT1CT0数值定义

CT1,CT0最小时间计数

机器周期

CPU时钟最大值

I2C的频率为100KHz

溢出周期

机器周期

1 0 7 8.4 MHz1023

0 1 6 7.2 MHz1022

0 0 5 6.0 MHz1021

11 4 4.8 MHz1020

定时器I可以作为看门狗定时器用于监视总线的挂起在总线激活过程中如

果SCL线长时间停留在某一状态时定时器I将产生溢出中断当SCL线被钳制为低电平时表明器件为无效的主控器或被控器当SCL线被钳制为高电平时表明器件无效或者是因为外界对I2C总线的干扰导致所有的主控器退出总线仲裁

定时器I产生溢出的时间是固定不变的大约为1024个机器周期其准确的数值由CT0和CT1的数值确定激活I2C总线以及使能定时器I之后在SCL线的下降沿产生时将复位定时器I如果SCL线在1024个机器周期中没有由高电平翻转为低电平定时器I将计时溢出并产生中断

定时器I溢出后I2C总线将复位这对于多主控器系统的软件设计很有用因为当发生总线错误时总线将被挂起软件做不出响应而定时器I的溢出将释放SCL线使

多主控器系统中的其它器件可以继续使用I2C总线

I2CON寄存器

对I2C控制寄存器I2CON可以进行写操作参见图10编程者应该用下面程序范例中

的位屏蔽方式对I2CON进行写操作如果用位寻址方式去清除或设置I2CON寄存器将可能会导致严重的后果因为某些程序命令例如CLR先是读寄存器再设置I2CON寄存器中的位然后再写回这时的写操作可能会影响寄存器的其它位

图10 I2CON寄存器

读I2CON

RDAT ATN DRDY ARL STR STP MASTER

RDAT 数据接收位在SCL线上升沿时获取SDA线的值与I2DAT中RDAT的数值相同

从I2CON中RDA T读数据的同时不清除DRDY和不释放SCL线

ATN 注意标志位当DRDY ARL STR或STP中任意一个为1时A TN被置为1检测A TN用于软件判断是否结束等待循环状态和是否总线上发生了某类

事件A TN也可以激活I2C中断请求

DRDY 数据准备好标志位激活I2C后在SCL线上升沿时被设置但对于空闲的被控器不设置DRDY读/写I2DA T或写CDR为1时清除DRDY

ARL 总线仲裁失败标志位表明本器件试图控制I2C总线时的仲裁失败

STR 起动标志位当检测到起动条件时被设置但对于空闲被控器除外

STP 停止标志位当检测到停止条件时被设置但对于空闲被控器除外

MASTER 当本器件成为主控器时被设置

写I2CON

CXA IDLE CDR CARL CSTR CSTP XSTR XSTP

CXA 清除数据传送状态写CXA为1清除数据传送状态

IDLE 设置IDLE使被控器进入空闲模式被控器在检测到下一个起动条件

之前将忽略I2C总线如果软件设置MASTRQ空闲的被控器将变成

主控器

CDR 清除DRDY

CARL 清除ARL

CSTR 清除STR

CSTP 清除STP

XSTR 发送重复起动条件写XSTR为1可使总线产生重复起动条件

只有主控器可以写XSTR

XSTP 发送停止条件写XSTP为1时可使总线产生停止条件

图 11 I2CFG寄存器

SLA VEN MASTRQ CLRTI TIRUN CT1CT0

SLA VEN 向此位写入1使器件成为I2C总线被控器

MASTRQ 器件申请成为主控器时向此位写入1

CLRTI 向此位写入1将清除定时器I中断标志当激活I2C总线时起动

条件和停止条件的发送以及数据的发送将清除TIRUN向此位写入

0将清除和停止运行定时器I

CT1CT0 根据系统振荡器频率设置这两位的值CT1和CT0的值决定SCL线

高电平和低电平的最小时间在不同的系统振荡器频率时CT1和

CT0用于优化系统

图12 I2DAT寄存器

读I2DAT

RDAT

RDA T 接收数据位在每个SCL线的上升沿时读取SDA线上的数据

读I2DA T将清除DRDY和发送激活状态如果不想清除标志位

应该从I2CON中的RDA T读取数据

写I2DAT

XDA T

XDA T 发送数据位下一个要传送的数据写入此位写XDA T时将

清除DRDY和设置发送激活状态

I2CFG寄存器

I2C配置寄存器I2CFG是一个可读/写的寄存器参见图11

I2DAT寄存器

I2C数据寄存器I2DAT是一个可读/写的寄存器其中最高位的数据是总线所传送的数据其它7位被读时总为0参见图12

数据传送激活状态

数据传送激活状态是I2C总线内部的一种状态这个状态受上文所描述的I2C特殊寄存器的影响当设置数据发送激活标志位时I2C接口将拉低SDA线对I2DAT写操作或设

置I2CON中的XSTR=1或XSTP=1都可以设置数据发送激活状态数据发送激活状态被设置时标志位ARL将被设置为1这样在总线仲裁失败时数据发送激活状态将自行

复位向I2CON中标志位CXA写入1或读I2DAT可清除数据发送激活状态

编程范例

下面的程序可使P87LPC76x用于单主控器系统中作为I2C总线的主控器

单主控器系统的软件设计比多主控器系统简单些因为软件设计者不用涉及变换主控器和被控器也不用处理总线仲裁失败问题

在单个主控器系统中不需要用I2C的状态起动中断因此不需要使能I2C中断在相应程序被调用时主控器初始化所有的数据传送格式而在多主控器系统中需要产生状态起动中断在每一个I2C状态时程序中将用一个循环等待操作检测标志位ATN以及监视总线活动状态因为I2C总线可以在高速模式下工作软件中循环等待操作只消耗一小段时间而在软件中使用I2C状态中断将会消耗较多的时间

P87LPC76x有一个字节寄存器硬件接口可以直接影响总线上器件的级别软件与硬件

寄存器一起构成I2C总线协议硬件和处理寄存器的服务程序是密不可分的编程者在修改服务程序时一定要额外小心

硬件系统复位后主程序在地址0处转到复位程序Reset开始执行

主程序简单演示了I2C总线的编程规范主程序首先使能定时器I中断再设置一些参数这些参数用于读取被控器的数据在本例子中PCF8574A 8位I/O口器件用作被控器

其中03口与按键相连接47口与LED相连接RcvData子程序用于读I/O口的数据

当读取PCF8574A的数据字节时按键数值也被保存并映象到相应的LED位调用SendData 子程序可以把数据写到PCF8574A I/O口SendData和RcvData子程序可以相应地用于发送和接收多个字节数据其数据数目由变量ByteCnt确定

从SendData和RcvData子程序返回时程序将测试标志Retry确定数据传送是否正确完成如果数据传送没有正确完成程序将重复数据传送操作

在数据传送失败的情况下程序将转回MainLoop处执行

在复位程序之后是定时器I中断服务程序如果定时器I溢出系统将执行73H处指令这时程序将使定时器I停止工作清除定时器I中断和返回中断开放其它中断接着返

回堆栈指针再跳转到Recover子程序试图恢复总线

定时器I中断服务程序之后是I2C总线数据传送服务程序其中SendData DcvData SendSub和RcvSub子程序由主程序调用参量XmtDat的值作为数据传送缓冲区地址用于保存所发送的数据在本程序中XmtDat缓冲区长度为4个字节可以根据系统需要确定数据缓冲区的大小所接收的数据保存在RcvDat缓冲区中RcvDat缓冲区长度也为4个字

节以上4个程序都使用参量SlvAdr和ByteCnt相应地确定被控器地址和所传送的数据字

节数SendSub和RcvSub子程序都使用参量SubAdr作为被控器的单元地址

接下来的程序是一些子程序由主程序调用处理I2C总线接口状态在SendAddr子程序中调用XmitAddr子程序发送被控器地址XmitByte子程序用于发送数据到I2C总线上也可用于发送单元地址在发送完每一个字节数据后这两个程序都将检测由被控器产生的

应答信号

RDAck子程序调用RcvByte子程序读一个字节数据然后发送一个应答信号给被控器

RDAck子程序用于接收除最后一个字节数据外的所有数据主控器将忽略最后一个字节的

应答位RcvByte子程序用于接收最后一个字节数据

SendStop子程序将产生一个I2C总线停止条件RepStart子程序用于发送一个重复起动条件使主控器不用发送停止条件而直接开始下一个新的I2C总线数据传送状态

地址发送

发送地址子程序用于发送地址字节地址字节中最高位先被写入I2DAT中接着其它7位依次被写入I2DAT中使用SendAd2子程序SendAd2子程序还清除起动条件以便

释放SCL线当释放SCL线时地址字节的最高位已经在I2DAT中

数据接收

当接收数据时程序应循环等待检测ATN在ATN等于1后接着检测DRDY如果DRDY等于1则SCL线产生一个上升沿这时可以从I2CON中的标志位RDAT或I2DAT 中读取数据

应该从I2CON中读取一个字节数据的最后一位而不是I2DAT参见RcvByte程序的

末尾这样标志位DRDY不会被清除SCL的低电平周期将被延长因为在接收最后一个

数据位时主控器必须响应一个应答信号为了做到这一点程序必须等待产生应答信号时钟释放SCL线时SDA出现高电平SCL线将保持低电平直到程序把应答信号写入

I2DAT为止

总线错误和非法状态

定时器I用于检测总线错误非法状态由程序检测定时器I溢出后由Recover子程序恢复总线Recover子程序首先调用FixBus子程序检测是否只有SDA线被钳制为高电平

如果是这种情况将发送额外时钟信号强制产生总线停止条件如果这种方法无效将调

用BusReset子程序处理严重的总线错误例如在时钟线被钳制为低电平的情况下BusReset 子程序将置位TIRUN使定时器I运行定时器I溢出后产生中断这样就可以恢复严重的总线错误

***************************************************************

P87LPC764 I2C总线单主控器编程规范

本程序演示了用单元地址接收I2C被控器的数据以及发送数据给被控器

***************************************************************

$INCLUDE(MOD76x.ASM) P87LPC76x寄存器定义

I2C演示板的器件地址

LCD EQU72H PCF8576 LCD 驱动控制器

LED7EQU 76H SAA1064 LED 驱动控制器

RTCLK EQU0A2H PCF8563 时钟/日历芯片

RAM EQU0AEH PCF8570 256 byte RAM

EEPROM EQU0A6H24WC02 256 byte EEPROM

DTMF EQU4AH PCD3312 DTMF 发生器

PIO EQU4EH PCF8574 8位 I/O 口器件

KEYLED EQU7EH PCF8574A I/O 口扩展器件

ADDAC EQU9EH PCF8591 ADC和DAC器件

数值定义

CTVAL EQU02H设置I2C寄存器标志位CT1CT0

屏蔽I2CFG标志位

BTIR EQU10H屏蔽TIRUN

BMRQ EQU40H屏蔽MASTRQ

屏蔽I2CON标志位

BCXA EQU80H屏蔽CXA

BIDLE EQU40H屏蔽IDLE

BCDR EQU20H屏蔽CDR

BCARL EQU10H屏蔽CARL

BCSTR EQU08H屏蔽CSTR

BCSTP EQU04H屏蔽CSTP

BXSTR EQU02H屏蔽XSTR

BXSTP EQU01H屏蔽XSTP

由I2C软件分配RAM

BitCnt DATA21H I2C 数据位计数器

ByteCnt DATA22H

SlvAdr DATA23h被激活的被控器地址

SubAdr DATA24H

RcvDat DATA25H接收数据缓冲区4字节地址为25H28H

XmtDat DATA29H发送数据缓冲区4字节地址为29H2CH

StkSave DATA2DH保存堆栈地址

Flags DATA20H I2C软件状态标志位

NoAck BIT Flags.0指明没有应答信号

Fault BIT Flags.1指明总线出错

retry BIT Flags.2指明I2C最后的数据传送失败应该重复操作

****************************************************************

程序开始

****************************************************************复位和中断向量

ORG0000H

AJMP Reset复位向量

ORG0073H定时器I 中断地址

TimerI:SETB CLRTI清除定时器I中断

CLR TIRUN

ACALL ClrInt中断返回

MOV SP,StkSave恢复堆栈指针返回主程序

AJMP Recover试图恢复总线

ClrInt:RETI

ORG0100H

****************************************************************

发送和接收数据程序

****************************************************************

发送数据字节给被控器

被控器地址在SlvAdr,数据在XmtDat缓冲区中

被发送的数据字节数在Bytecnt中

Senddata:

CLR NoAck清除错误标志位

CLR Fault

CLR retry

MOV StkSave,SP为处理总线错误保存堆栈指针

MOV A,SlvAdr取被控器地址

ACALL SendAddr占据总线和发送被控器地址

JB NoAck,SDEX检测未送的应答信号

JB Fault,SDatErr 检测总线错误

MOV R0,#XmtDat设置数据传送缓冲区的头地址

SDLoop:MOV A,@R0取数据

INC R0

ACALL XmitByte发送数据给被控器

JB NoAck,SDEX检测未送的应答信号

JB Fault,SDatErr 检测总线错误

DJNZ ByteCnt,SDLoop 这里ByteCnt为1

SDEX:ACALL SendStop发送I2C停止信号

RET

SDatErr:

AJMP Recover试图恢复总线

从被控器接收数据字节

被控器地址在SlvAdr中数据字节数在ByteCnt中

数据返回到RcvDat缓冲区

Rcvdata:

CLR NoAck清除错误标志位

CLR Fault

CLR Retry

MOV StkSave,SP为处理总线错误保存堆栈指针

MOV A,SlvAdr取被控器地址

SETB ACC,0取总线读操作位

ACALL SendAddr发送被控器地址

JB NoAck,RDEX检测未送的应答信号

JB Fault,RDatErr 检测总线错误

MOV R0,#RcvDat设置数据接收缓冲区头地址

DJNZ ByteCnt,RDLoop 这里ByteCnt为1

SJMP RDLast

RDLoop:ACALL RDAck取数据和发送应答信号

JB Fault,RDatErr 检测总线错误

MOV@R0,A保存数据

INC R0

DJNZ ByteCnt,RDLoop 重复操作直到接收最后一个字节数据

RDLast:ACALL RcvByte从被控器取最后一个数据

JB Fault,RDatErr 检测总线错误

MOV@R0,A保存数据

MOV I2DAT,#80H发送非应答信号

JNB ATN,$等待非应答信号发送

JNB DRDY,RDatErr 检测总线错误

RDEX:ACALL SendStop发送I2C总线停止信号

RET

RDatErr:

AJMP Recover试图恢复总线

用单元地址发送数据给被控器

被控器地址在ACC中单元地址在SubAdr中

所发送数据的字节数在ByteCnt中发送的数据在XmtDat缓冲区中

SendSub:

CLR NoAck清除错误标志位

CLR Fault

CLR Retry

MOV StkSave,SP为处理总线错误保存堆栈指针

MOV A,SlvAdr取被控器地址

ACALL SendAddr占据总线和发送被控器地址

JB NoAck,SSEX检测未发送的应答信号

JB Fault,SSubErr 检测总线错误

MOV A,SubAdr取被控器单元地址

ACALL XmitByte发送单元地址

JB NoAck,SSEX检测未送的应答信号

JB Fault,SSubErr 检测总线错误

MOV R0,#XmtDat设置数据传送缓冲区头地址

SSLoop:MOV A,@R0取数据

INC R0

ACALL XmitByte发送数据给被控器

JB NoAck,SSEX检测未送的应答信号

JB Fault,SSubErr 检测总线错误

DJNZ ByteCnt,SSLoop

SSEX:ACALL SendStop发送I2C停止条件

RET

SSubErr:

AJMP Recover试图恢复总线

用单元地址从被控器接收数据

被控器地址在SlvAdr中单元地址在SubAdr中

所接收的数据字节数在ByteCnt中数据返回到RcvDat缓冲区RcvSub:CLR NoAck清除错误标志位

CLR Fault

CLR Retry

MOV StkSave,SP为处理总线错误保存堆栈指针

MOV A,SlvAdr取被控器地址

ACALL SendAddr发送被控器地址

JB NoAck,RSEX检测未送的应答信号

JB Fault,RSubErr 检测总线错误

MOV A,SubAdr取被控器单元地址

ACALL XmitByte发送单元地址

JB NoAck,RSEX检测未送的应答信号

JB Fault,RSubErr 检测总线错误

ACALL RepStart发送重复起始条件

JB Fault,RSubErr 检测总线错误

MOV A,SlvAdr取被控器地址

SETB ACC.0取总线读操作位

ACALL SendAd2发送被控器地址

JB NoAck,RSEX检测未送的应答信号

JB Fault,RSubErr 检测总线错误

MOV R0,#RcvDat设置数据接收缓冲区头地址

DJNZ ByteCnt,RSLoop 这里ByteCnt为1

SJMP RSLast

RSLOOP:

ACALL RDAck取数据和发送应答信号

JB Fault,RSubErr 检测总线错误

MOV@R0,A保存数据

INC R0

DJNZ ByteCnt,RSLoop 重复操作直到接收最后一个字节数据RSLast:ACALL RcvByte从被控器取最后一个字节数据

JB Fault,RSubErr 检测总线错误

MOV@R0,A保存数据

MOV I2DAT,#80H发送非应答信号

JNB ATN,$等待

JNB DRDY,RSubErr 检测总线错误

RSEX:ACALL SendStop发送I2C停止条件

RET

处理总线数据接收错误

RSubErr:

AJMP Recover试图恢复总线

****************************************************************

子程序

****************************************************************

送地址字节

入口地址在ACC

SendAddr:

MOV I2CFG,#BMRQ+BTIR+CTVAL 请求I2C总线

JNB ATN,$等待

JNB Master,SAErr 应成为总线上的主控器

SendAd2:

MOV I2DAT,A发送第一个数据位清除DRDY

MOV I2CON,#BCARL+BCSTR+BCSTP 清除起动条件释放SCL线

ACALL XmitAddr结束传送地址

RET

SAErr:SETB Fault返回总线错误状态

RET

字节传送程序

数据在ACC中

XmitByte传送8位数据

XmitAddr传送7位地址

XmitAddr:

MOV BitCnt,#8设置地址数值格式为8位

SJMP Xmbit2

XmitByte:

MOV BitCnt,#8设置数据格式为8位

Xmbit:MOV I2DAT,A发送数据位

Xmbit2:RL A取下一个数据位

JNB ATN,$等待

JNB DRDY,XMErr数据准备好

DJNZ BitCnt,Xmbit 重复操作直到发送完所有的数据位

MOV I2CON,#BCDR+BCXA 转换为接收模式

JNB ATN,$等待应答位

JNB RDAT,XMBX是应答位

SETB NoAck返回无应答信号状态

XMBX:RET

XMErr:SETB Fault返回总线错误状态

RET

字节数据接收程序

RDAck接收一个字节数据然后发送应答位

RcvByte接收一个字节数据数据返回到ACC

RDAck:ACALL RcvByte接收一个字节数据

MOV I2DAT,#0发送应答位

JNB ATN,$等待应答位的传送

JNB DRDY,RdErr 检测总线错误

RET

RcvByte:

MOV bitCnt,#8设置数据位计数器

CLR A把数据初始化为0

Rbit:ORL A,I2DAT取数据位清除ATN

RL A左移数据

JNB ATN,$等待下一个数据位

JNB DRDY,RdErr数据准备好

DJNZ BitCnt,Rbit 重复操作直到接收了7个数据位

MOV C,RDAT取最后一个数据位但不清除ATN

RLC A形成完整字节数据

RET

RdErr:SETB Fault返回总线错误状态

RET

产生I2C停止条件程序

SendStop:

CLR MASTRQ取消主控器地位

MOV I2CON,#BCDR+BXSTP产生总线停止条件

JNB ATN,$等待ATN

MOV I2CON,#BCDR清除数据准备好标志位

JNB ATN,$等待传送停止条件

MOV I2CON,#BCARL+BCSTP+BCXA释放I2C总线

CLR TIRUN停止定时器I的运行

RET

I2C总线重复起动程序

入口地址在ACC中

RepStart:

MOV I2CON,#BCDR+BXSTR发送重复的起动条件

JNB ATN,$等待ATN

MOV I2CON,#BCDR清除数据准备好标志位

JNB ATN,$等待重复起动条件的传送

MOV I2CON,#BCARL+BCSTR清除起动标志位

RET

总线错误恢复程序

Recover:

ACALL FixBus核查是否可以恢复总线

JC BusReset如果不能恢复使用强制方式恢复总线

SETB Retry如果总线恢复完毕返回到主程序

CLR Fault

CLR NoAck

SETB CLRTI

SETB TIRUN使能定时器I

SETB ETI开放定时器I中断

RET

下面的程序用于强制恢复总线当SCL或SDA线被钳制时

可以使用以下程序释放总线当定时器I溢出时返回到Recover程序

BusReset:

CLR MASTRQ释放总线

MOV I2CON,#0BCH清除所有I2C标志位

SETB TIRUN

SJMP$等待定时器I溢出这将使I2C硬件接口复位

下面的程序试图在发生总线错误后重新控制I2C总线

如果成功将返回进位标志位为0

如果失败将返回进位标志位为1

FixBus:

CLR MASTRQ关闭I2C功能

SETB C

SETB SCL确保I/O 口没有钳制住I2C总线

SETB SDA

JNB SCL,FixBusEx 如果SCL线为低电平将不能恢复总线

JB SDA,RStop如果SCL和SDA线都为高电平强制产生停止条件

MOV BitCnt,#9设置BitCnt为9试图释放总线

ChekLoop:

CLR SCL强制产生一个I2C时钟

ACALL SDelay

JB SDA,RStop SDA线为低电平

SETB SCL

ACALL SDelay

DJNZ BitCnt,ChekLoop 重复产生时钟直到SDA线为低电平

SJMP0FixBusEx用这种方法失败则返回

RStop:CLR SDA由于SCL和SDA线都为高电平用本程序强制

ACALL SDelay产生停止条件

SETB SCL

ACALL SDelay

SETB SDA

ACALL SDelay

JNB SCL,FixBusEx SCL和SDA线都还为高电平若是则返回

JNB SDA,FixBusEx 进位标志位为0表明总线已经恢复

CLR C

FixBusEx:

RET

短时间延时程序10个机器周期

SDelay:NOP

NOP

NOP

NOP

NOP

NOP

NOP

RET

****************************************************************

主程序

****************************************************************

Reset:SETB ETI开放定时器I中断

SETB EA 开放系统所有中断

以下程序使用I2C演示板进行测试

MainLoop:

MOV SlvAdr,#KEYLED 设置被控器地址8位I/O口器件

MOV ByteCnt,#1H设置字节计数器

MOV SubAdr,#0H设置被控器单元地址

ACALL RcvSub从被控器处读数据

JB Retry,MainLoop如果出现问题则重复操作

MOV A,RcvDat取所要接收的数据字节

ANL A,#0FH屏蔽按键数据位

SWAP A把按键数据位映象到LED

ORL A,#0FH不锁住按键数据位

MOV XmtDat,A返回数据

ML2:MOV SlvAdr,#KEYLED 设置被控器地址8位I/O口器件

MOV ByteCnt,#1H 设置字节计数器

MOV SubAdr,#0H设置被控器单元地址

ACALL SendSub发送数据给被控器

JB Retry,ML2如果出现问题则重复操作

SJMP MainLoop重复操作把按键数据位映象到LED

===============================================================

ORG0FD00H EPROM配置字节UCFG1

DB038H关闭看门狗WDT系统复位后端口线置为高电平

掉电复位电压等于2.5V,CLK=1时钟频率为外部

晶振频率的两倍振荡器类型为高频晶振

;

END

相关主题
文本预览
相关文档 最新文档