赞助论坛
  • 4870阅读
  • 0回复

学习单片机之---i2c总线程序(asm) [复制链接]

楼层直达
jswr  
发帖
339
精华
1
金币
264
威望
8
贡献
7
好评
15
注册
2008-06-30
楼主    jswr 发表于: 2008-08-20 17:29:42 
i2c总线程序(asm)


    ORG 0000H
    SCL EQU P1.0
    SDA EQU P1.1
    W85 EQU 0A0H;8583器件地址
    W24 EQU 0A8H;24C04器件地址
    SLA EQU 70H;器件地址寄存器
    SUBA EQU 71H;片内地址寄存器
    MTD EQU 1EH;传送数据寄存器
    MRD EQU 4EH;读出数据寄存器
    NUMBYTE EQU 72H;传送字节数寄存器
    ACK BIT 73H;测试位
    AJMP MAIN

;*********************************************************************
;开机显示1-8,当有按键(P0。4)按下,然后向24C02写数据然后再读入显示
;*********************************************************************
MAIN:   MOV R0,#0F1H ;数码管显数
    MOV R1,#07H ;数码管位选指针,从左到右点亮
    MOV R2,#08H ;循环结束指针
LOOP:   MOV P0,R0
    MOV P2,R1   ;第八只数码管点亮,显0
    LCALL DELAY ;
    INC R0
    DEC R1
    DJNZ R2,LOOP
JNB P0.4,HH
    AJMP MAIN
HH:   LCALL DELAY3
    JNB P0.4,SETI
AJMP MAIN
;**************************************************************
;向8583时钟芯片送入出始值2003/07/14/01/18:55:00
;出始值放入缓存器30H-3EH内
;对24C04写入的数据
;****************************************************************


SETI: MOV 30H,#00H           ;秒的个位
    MOV 31H,#00H           ;秒的十位
    MOV 32H,#05H             ;分的个位
    MOV 33H,#05H             ;分的十位
    MOV 34H,#08H
    MOV 35H,#01H           ;时的值
    MOV 36H,#01H           ; 周的值
    MOV 37H,#04H
    MOV 38H,#01H               ;日期
    MOV 39H,#07H
    MOV 3AH,#00H               ;月份
    MOV 3BH,#03H
    MOV 3CH,#00H
    MOV 3DH,#00H
    MOV 3EH,#02H               ;年分

;********************************************************
;写入24C04
;********************************************************
WI2C: MOV P0,#06H
    MOV P2,#07H
    LCALL DELAY2   ;延时满足传输要求
    LCALL ZZ     ;字节转换,转换成8583字节格式
    MOV SLA,#W24
    MOV SUBA,#00H    
    MOV NUMBYTE,#07H     ;送入口参数
    LCALL IWRNBYTE ;写入24C04程序
   
    LCALL DELAY
    LCALL DELAY2     ;调用延时,满足要求
;**********************************************
;读24C04数据
;***************************************************
    MOV SLA,#W24
    MOV SUBA,#00H
    MOV NUMBYTE,#07H ;送入口参数
    LCALL IRDNBYTE   ;读出24C04程序
    LCALL DELAY2
   
K:   LCALL DISPLAY1;显示数据
    LCALL DELAY
    MOV A,P0
    ORL A,#0F0H
    MOV P0,A
    JNB P0.4,K2;是否有键按下
    AJMP K
   
K2:   LCALL DELAY3;去抖动
    JNB P0.4,QQ
    AJMP K
     
;**************************************************************
;对8583设置参数
;控制字为00H
;***********************************************************
QQ:     MOV P0,#0F2H
      MOV P2,#07H
LCALL DELAY2
LCALL DELAY
      LCALL ZZ           ;字节转换程序
MOV SLA,#W85
      MOV SUBA,#00H
      MOV 1EH,#00H
      MOV 1FH,#00H         ;初始化8583
      MOV NUMBYTE,#07H         ;送入口参数
      LCALL IWRNBYTE     ;送设置时间值给8583
LCALL DELAY2
  LCALL DELAY
;************************************************************
;读出8583时钟和日历数据
;***************************************************************
K1: MOV SLA,#W85
      MOV SUBA,#00H
      MOV NUMBYTE,#07H       ;送入口参数
LCALL IRDNBYTE     ;读8583的数值    
NOP
      LCALL DISPLAY1;显示时钟和周次
      LCALL DELAY
      MOV A,P0
      ORL A,#0F0H
      MOV P0,A
      JNB P0.7,K5;是否有翻页键按下,有则显示日历
      AJMP K1    
K5:   LCALL DELAY3
      JNB P0.7,K6
      AJMP K1
K6:   LCALL DISPLAY2;显示日历
      MOV A,P0
      ORL A,#0F0H
      MOV P0,A
      JNB P0.7,K7;是否又有翻页键按下,有则显示时钟
AJMP K6
K7:   LCALL DELAY3
      LCALL DELAY3
LCALL DELAY3;去抖动
      JNB P0.7,K8
AJMP K6
K8:   LJMP K1
   


;**************************************************************
;
;             以下程序为子程序
;
;**************************************************************

XX:   MOV 50H,20H
      MOV 51H,21H
      MOV 52H,22H
      MOV 53H,23H
      MOV 54H,24H
      MOV P0,#0F0H
      RET

;********************************************************
;字节转换程序
;入口参数-把30H-3EH的数据转换成8583的格式,
;出口参数-存放在20H-24H中
;********************************************************
ZZ:     MOV A,30H
      ANL A,#0FH
      MOV R0,A
      MOV A,31H
      ANL A,#0FH
      SWAP A
      ADD A,R0
      MOV 20H,A     ;将秒的字节给20H
     
MOV A,32H
      ANL A,#0FH
      MOV R0,A
      MOV A,33H
      ANL A,#0FH
      SWAP A
      ADD A,R0
      MOV 21H,A       ;将分的字节给21H
     
MOV A,34H
      ANL A,#0FH
      MOV R0,A
      MOV A,35H
      ANL A,#03H
      SWAP A
      ADD A,R0
      MOV 22H,A     ;将时的字节给22H
   
MOV A,37H
      ANL A,#0FH
      MOV R0,A
      MOV A,38H
      ANL A,#03H
      SWAP A
      ADD A,R0
      MOV R1,A
      MOV A,3BH
      ANL A,#03H
      SWAP A
      MOV R0,#02H
LOOP6:   RL A
      DJNZ R0,LOOP6
      ADD A,R1
      MOV 23H,A       ;将年,日的字节给23H
   
MOV A,39H
      ANL A,#0FH
      MOV R0,A
      MOV A,3AH
      ANL A,#01H
SWAP A
      ADD A,R0
      MOV R1,A
      MOV A,36H
      ANL A,#07H
SWAP A
RL A
      ADD A,R1
      MOV 24H,A       ;将周,月的字节给24H
RET
;**********************************************
;无翻页键按下时的显示子程序-DISPLAY1
;显示数据在50H-54H中
;只显示周,时,分,秒
;50H-显示秒
;51H-显示分
;52H-显示时
;53H-显示年,日
;54H-显示周,月
;**********************************************

DISPLAY1: MOV P0, 50H
      MOV P2,#00H       ;显示秒个位
      LCALL DELAY
 
  MOV A,50H
  SWAP A
      MOV P0,A
      MOV P2,#01H     ;显示秒十位
      LCALL DELAY
     
  MOV P0,51H
      MOV P2,#02H     ;显示分个位
      LCALL DELAY
 
  MOV A,51H
      SWAP A
      MOV P0,A
      MOV P2,#03H     ;显示分十位
      LCALL DELAY
     
  MOV P0,52H
      MOV P2,#04H     ;显示小时个位
      LCALL DELAY
 
  MOV A,52H
      SWAP A
      MOV P0,A
      MOV P2,#05H     ;显示小时十位
      LCALL DELAY
     
  MOV A,54H
      SWAP A
      RR A
      ANL A,#07H
  MOV P0, A
      MOV P2,#06H     ;显示周个位
      LCALL DELAY
     
  MOV P0,#00H
      MOV P2,#07H     ;显示周十位
  RET
;********************************************
;当有翻页键按下时的显示子程序-DISPLAY2
;用到了50H-54H
;只显示年,月,日
; 设置年时用到3CH,3DH,3EH
;********************************************

DISPLAY2: MOV P0,53H
      MOV P2,#00H       ;显示日的个位
      LCALL DELAY
      MOV A,53H
      SWAP A
      ANL A,#03H       ;屏蔽无用位
      MOV P0,A
      MOV P2,#01H       ;显示日的十位
      LCALL DELAY
      MOV P0,54H
      MOV P2,#02H       ;显示月的个位
      LCALL DELAY
      MOV A,54H
      SWAP A
      ANL A,#01H       ;屏蔽无用位      
      MOV P0,A
      MOV P2,#03H       ;显示月的十位
      LCALL DELAY
      MOV A,53H
      MOV R0,#02H
LOOP7:   RL A
      DJNZ R0,LOOP7     ;左移2位
      ANL A,#03H       ;屏蔽无用位
      MOV P0,A
      MOV P2,#04H       ;显示年的第一位
      LCALL DELAY
      MOV P0,3CH
      MOV P2,#05H       ;显示年的第二位
      LCALL DELAY
      MOV P0,3DH
      MOV P2,#06H       ;显示年的第三位
      LCALL DELAY
      MOV P0,3EH
      MOV P2,#07H       ;显示年的第四位
      LCALL DELAY
      RET
;*************************************************************
;使用前要定义好SCL和SDA。在标准80C51模式
;(12 Clock)下,对主频要求是不高于12MHz(1个机器周期1us);若Fosc>12MHz
;则要增加相应的NOP指令数。
;                 请注意
;占用内部资源:       R0,R1,R2,R3,ACC,Cy。
;         程序里要做以下定义:
;使用前须定义变量: SLA 器件从地址 SUBA器件子地址
;             NUMBYTE读/写的字节数 ,位变量ACK
;使用前须定义常量: SDA SCL 总线位 MTD 发送数据缓冲区首址
;            MRD 接收数据缓冲区首址
;(ACK为调试/测试位,ACK为0时表示无器件应答)

;*************************************************************
;************************************************************
;启动I2C总线子程序  
;***********************************************************
START:   SETB SDA
      NOP
      SETB SCL       ;起始条件建立时间大于4.7us
      NOP
      NOP
      NOP
      NOP
      NOP
      CLR SDA    
      NOP           ;起始条件锁定时大于4us
      NOP
      NOP
      NOP
      NOP
      CLR   SCL       ;钳住总线,准备发数据
      NOP
      RET
;****************************************************************
;结束总线子程序
;****************************************************************
STOP:   CLR   SDA
      NOP
      SETB   SCL     ;发送结束条件的时钟信号
      NOP         ;结束总线时间大于4us
      NOP        
      NOP
      NOP
      NOP
      SETB SDA     ;结束总线
      NOP         ;保证一个终止信号和起始信号的空闲时间大于4.7us
      NOP
      NOP
      NOP
      RET
;***********************************************************************
;发送应答信号子程序
;***********************************************************************
MACK:   CLR   SDA     ;将SDA置0
      NOP
      NOP
      SETB SCL
      NOP           ;保持数据时间,即SCL为高时间大于4.7us
      NOP
      NOP
      NOP
      NOP
      CLR   SCL
      NOP
      NOP
      RET
;**********************************************************************
;发送非应答信号
;************************************************************************
MNACK:   SETB SDA       ;将SDA置1
      NOP
      NOP
      SETB SCL
      NOP
      NOP           ;保持数据时间,即SCL为高时间大于4.7us
      NOP
      NOP
      NOP
      CLR SCL
      NOP
      NOP
      RET
;*****************************************************************************
; 检查应答位子程序
; 返回值,ACK=1时表示有应答
;**************************************************************************8
CACK: SETB SDA
    NOP
    NOP
    SETB SCL
    CLR   ACK
    NOP
    NOP
    MOV C,SDA
    JC   CEND
    SETB ACK         ;判断应答位
CEND: NOP
    CLR SCL
    NOP
    RET
;*************************************************************************
;发送字节子程序
;字节数据放入ACC    
;每发送一字节要调用一次CACK子程序,取应答位      
;**************************************************************************
WRBYTE: MOV R0,#08H
WLP: RLC   A           ;取数据位
    JC   WR1
    SJMP WR0         ;判断数据位
WLP1:   DJNZ R0,WLP
    NOP
    RET
WR1:   SETB SDA         ;发送1
    NOP
    SETB SCL
    NOP
    NOP
    NOP
    NOP
    NOP
    CLR   SCL
    SJMP WLP1
WR0:   CLR   SDA         ;发送0
    NOP
    SETB SCL
    NOP
    NOP
    NOP
    NOP
    NOP
    CLR SCL
    SJMP WLP1
;************************************************************************
;读取字节子程序            
;读出的值在ACC
;每取一字节要发送一个应答/非应答信号
;************************************************************************
RDBYTE: MOV R0,#08H
RLP:   SETB SDA
    NOP
    SETB SCL         ;时钟线为高,接收数据位
    NOP
    NOP
    MOV C,SDA       ;读取数据位
    MOV A,R2
    CLR   SCL         ;将SCL拉低,时间大于4.7us
    RLC   A           ;进行数据位的处理
    MOV R2,A
    NOP
    NOP
    NOP
    DJNZ R0,RLP       ;未够8位,再来一次
    RET
;*********************************************************************
; 无子地址器件写字节数据
; 入口参数: 数据为ACC、器件从地址SLA        
; 占用: A、R0、CY
;********************************************************************
IWRBYTE: PUSH   ACC
IWBLOOP: LCALL START     ;起动总线
      MOV   A,SLA
      LCALL WRBYTE     ;发送器件从地址
      LCALL CACK
      JNB   ACK,RETWRB     ;无应答则跳转
      POP   ACC       ;写数据
      LCALL WRBYTE
      LCALL CACK
      LCALL STOP
      RET
RETWRB: POP   ACC
      LCALL STOP
      RET
;****************************************************************
;无子地址器件读字节数据
;入口参数: 器件从地址SLA
;出口参数: 数据为ACC  
;占用 A 、R0、R2 、CY
;*******************************************************************
IRDBYTE: LCALL START
      MOV   A,SLA       ;发送器件从地址
      INC   A
      LCALL WRBYTE
      LCALL CACK
      JNB   ACK,RETRDB        
      LCALL RDBYTE     ;进行读字节操作
      LCALL MNACK       ;发送非应信号
RETRDB: LCALL STOP         ;结束总线
      RET
;***************************************************************************
;向器件指定子地址写N个数据
;入口参数: 器件从地址SLA、器件子地址SUBA 、发送数据缓冲区MTD、发送字节数NUMBYTE
; 占用: A 、R0 、R1 、R3 、CY
;****************************************************************************
IWRNBYTE: MOV   A,NUMBYTE
      MOV   R3,A
      LCALL START       ;起动总线
      MOV   A,SLA
      LCALL WRBYTE     ;发送器件从地址    
      LCALL CACK
      JNB   ACK,RETWRN ;无应答则退出
      MOV   A,SUBA       ;指定子地址
      LCALL WRBYTE
      LCALL CACK
      MOV   R1,#MTD
WRDA:   MOV   A,@R1
      LCALL WRBYTE     ;开始写入数据
      LCALL CACK
      JNB   ACK,IWRNBYTE
      INC   R1
      DJNZ   R3,WRDA     ;判断写完没有
RETWRN:   LCALL STOP
      RET
;*********************************************************************
;向器件指定子地址读取N个数据
;入口参数: 器件从地址SLA、器件子地址SUBA、接收字节数NUMBYTE
;出口参数: 接收数MRD
;占用:A、 R0、 R1、 R2、 R3、 CY          
;********************************************************************
IRDNBYTE: MOV   R3,NUMBYTE
      LCALL START
      MOV   A,SLA
      LCALL WRBYTE     ;发送器件从地址
      LCALL CACK
      JNB   ACK,RETRDN
 
      MOV   A,SUBA       ;指定子地址  
      LCALL WRBYTE
      LCALL CACK
      LCALL START       ;重新起动总线
      MOV   A,SLA
      INC   A         ;准备进行读操作
      LCALL WRBYTE
      LCALL CACK
      JNB   ACK,IRDNBYTE
     
      MOV   R1,#MRD
RDN1:     LCALL RDBYTE     ;读操作开始
      MOV   @R1,A
      DJNZ   R3,SACK
 
      LCALL MNACK     ;最后一字节发非应答位          
RETRDN:   LCALL STOP   ;并结束总线
      RET
SACK:     LCALL MACK
      INC   R1
      SJMP   RDN1

;**********************************************************
;延时程序——DELAY:16MS   显示用
;DELAY2:约1S;
;DELAY3:130MS左右   去抖动
;
;**********************************************************

DELAY:   MOV R7,#0FH
DL:   MOV R6,#0FH
DL1:   DJNZ R6,DL1
      DJNZ R7,DL
      RET
;*********************************************
DELAY2:   MOV R7,#05H
DL6:   MOV R5,#0FFH
DL4:   MOV R6,#0FFH
DL5:   DJNZ R6,DL5
        DJNZ R5,DL4
        DJNZ R7,DL6
        RET
;*********************************************
DELAY3:   MOV R7,#0FFH
DL2:   MOV R6,#0FFH
DL3:   DJNZ R6,DL3
        DJNZ R7,DL2
        RET
END      


;***********************************************************

;************************************************************