⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hi2c_asm.inc

📁 周立功试验板DP51+程序
💻 INC
字号:
;================================
;        LPC76X作主控器的硬件I2C软件包(V1.1)
; 1  使用I2C总线软件包时P1.2,P1.3切勿写为0。
; 2  当定时器I溢出时,表明I2C总线出错,不开定时器I中断,程序
;    可能因为总线出错而进入死循环等待ATN.
; 3  定时中断返回出口是调用此次I2C操作指令的下一条指令,以便
;    检查出错原因,避免出现死循环。
; 4  MCU的最大时钟8MHz
;    本版本是在V1.0的基础上增加了两个用户接口子程序:IRDNBYTEEXT
;    和IWRNBYTEEXT。
;                                  更新时间:2002.06.03
;================================

;/* 定义I2CFG的位掩码  */
CTVAL  EQU   02H   ;/*为I2CFG寄存器的参数定义,置位 ,CT1=1,CT0=0*/
BTIR   EQU   10H   ;/*使用时只要用或运算即可取相应状态,TIRUN=1*/
BMRQ   EQU   40H   ;/*MASTRQ=1*/

;/* 定义I2CON的位掩码  */
BCXA   EQU   80H   ;/*为I2CON寄存器的参数定义,置位CXA */ 
BIDLE  EQU   40H   ;/*置位IDLE*/
BCDR   EQU   20H   ;/*清DRDY*/
BCARL  EQU   10H   ;/*清ARL*/
BCSTR  EQU   08H   ;/*清STR*/
BCSTP  EQU   04H   ;/*清STP*/
BXSTR  EQU   02H   ;/*置位XSTR*/
BXSTP  EQU   01H   ;/*置位XSTP*/

 
;请求总线,申请成功ACK返回1(MCU成为主控器)
;注意:申请成功后总线已启动,STR=1,DRDY=0。
;      在I2C器件没有接收完正确的操作时,MCU会不能申请总线或结束总线的,
;      也就是说它会死循环等待。
GETBUS:              
           MOV    I2CFG,#BMRQ+BTIR+CTVAL       ;申请总线,设置CT1,CT0,启动I定时器            
           MOV    R0,#30                       ;等待20ms无ATN,表明总线有误
GETBUS_L1: MOV    R1,#0F0H
GETBUS_L2: JB     ATN,GETBUS_OK                ;等待总线认可(STR置1或ARL置1)
           DJNZ   R1,GETBUS_L2
           DJNZ   R0,GETBUS_L1
           MOV    R2,#80H                      ;总线失败,置R2为01H
           RET
GETBUS_OK: JB     MASTER,GETB_END              ;总线申请成功 
           MOV    R2,#01H                      ;总线仲裁失败,置R2为01H       
GETB_END:  RET
   

;结束总线,结束此次主控操作
ENDBUS:
           CLR    MASTRQ                  ;先取消主控器
           MOV    I2CON,#BXSTP+BCDR       ;允许CLK上升沿,发结束信号
           JNB    ATN,$                   ;等待CLK上升沿,CLK=1
           MOV    I2CON,#BCDR             ;清DRDY,以便接收结束信号
           JNB    ATN,$                   ;等待结束信号,SDA=1
           MOV    I2CON,#BCXA+BCSTP+BCARL ;总线已结束,将各标志位清零
           CLR    TIRUN                   ;关定时器
           SETB   CLRTI                   
           RET


;重新启动总线
RRST:      
           MOV    I2CON,#BXSTR+BCDR       ;设置XSTR,重启
           JNB    ATN,$                   ;等待CLK上升沿,CLK=1
           MOV    I2CON,#BCDR
           JNB    ATN,$                   ;等待起动信号,SDA=0
           RET                            ;若总线有误,会在收发数据时/定时器I溢出时发现


;发送应答信号子程序
;返回ACK
SACK:           
           MOV    I2DAT,#00H              ;发送应答信号0。同时DYDY=0,释放SCL
           JNB    ATN,$
           JB     DRDY,SACK_END
           MOV    R2,#04H                 ;发关非应答信号时出错,置R2为04H
SACK_END:  RET


;发送非应答信号子程序
;返回ACK
SNACK:           
           MOV    I2DAT,#80H              ;发送非应答信号1
           JNB    ATN,$                   ;等待CLK上升
           JB     DRDY,SNACK_END          ;CLK上升沿时,DRDY置1
           MOV    R2,#08H                 ;发关非应答信号时出错,置R2为08H
SNACK_END: RET



;发送字节数据子程序
;用了R3,A,CY
;入口参数:发送的数据在ACC;出口参数:返回ACK位
;注:启动总线后,STR=1,DRDY=0,发第1位数据应
;  MOV    I2DAT,A    再  MOV  ICON,#BCSTR
SENDBYTE:  
           MOV   R3,#7                    
           MOV   I2DAT,A                   ;写I2DAT时DRDY清零,并设置发送状态 
           MOV   I2CON,#BCARL+BCSTR+BCSTP  ;清ARL,STR,STP
SENDB_L1:  JNB   ATN,$                    
           JNB   DRDY,SENDB_ERR
           RL    A
           MOV   I2DAT,A
           DJNZ  R3,SENDB_L1
           JNB   ATN,$                     ;1字节数据发送完毕
CHKACK:    MOV   I2CON,#BCDR+BCXA          ;转为接收,接收应答信号 
           JNB   ATN,$                     ;等待CLK上升沿
           MOV   C,RDAT                    ;读入应答位
           CPL   C
           MOV   ACK,C
SENDB_END: RET
             
SENDB_ERR: MOV   R2,#10H                   ;发送数据过程序中出错,置R2=10H
           RET           

           
;接收字节数据子程序
;占用R3,CY,A
;出口参数:接收到的值在ACC里
RCVBYTE:  
           CLR    A
           MOV    R3,#8  
           MOV    I2CON,#BCARL+BCSTR+BCSTP;清ARL,STR,STP
RCVB_L1:   ORL    A,I2DAT                 ;读I2DAT时,清DRDY,清发送状态
           RL     A                       ;清完ARL,STR,STP,DRDY位后ATN为0
           JNB    ATN,$
           JNB    DRDY,RCVB_ERR  
           DJNZ   R3,RCVB_L1
           MOV    C,RDAT                  ;将最后一位读入,因为开头读入的第1位无用
           RLC    A                       ;开头第1位的作用让DRDY=0,以便后而产生CLK上升沿
           RET
           
RCVB_ERR:  MOV   R2,#40H                  ;读取数据过程序中出错,置R2=10H
           RET           


;定时器I中断
;若在主程序中不用此中断,可能会出现死机现象。
; 功能:进行I2C总线的纠正工作
; 注意:其它高优先级中断的处理时间要短(小于1020个机器周期),
;       以保证此中断是在软件包调用时产生的。

TIMERI_INT: 
           CLR     TIRUN
           SETB    CLRTI             ;清除定时器I溢出标志      
           CLR     MASTRQ            ;定时器I溢出中断
           MOV     I2CON,#0BCH       ;强制结束I2C
           CLR     SCL               ;模拟产生I2C总线的结束信号
           CLR     SDA    
           ACALL   SDELAY
           SETB    SCL
           ACALL   SDELAY
           SETB    SDA
           DEC     SP                 ;出栈4字节,以便能正确返回
           DEC     SP
           DEC     SP
           DEC     SP         
           RETI   
SDELAY:    DB 0,0,0,0,0,0,0
           RET    

           
;无子地址单字节读
;出口参数:读出值在ACC
;          返回ACK=1操作正确
IRDBYTE:  
          CLR   ACK
          MOV   R2,#00H          
          LCALL GETBUS                     ;启动总线
          CJNE  R2,#00H,IRDB_RET
          MOV   A,SLA
          INC   A
          LCALL SENDBYTE                   ;器件寻址
          JNB   ACK,IRDB_END
          LCALL RCVBYTE                    ;读出立即地址数据
          LCALL SNACK                      ;发送非应答位以结束总线        
IRDB_END: LCALL ENDBUS                     ;结束总线
IRDB_RET: RET
                    

;有子地址多字节读
;占用R0,R1,R3,ACC
;入口参数:器件从地址SLA 子地址SUBA  接收数据区MRD  读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IRDNBYTE:    
          CLR   ACK
          MOV   R2,#00H
          LCALL GETBUS                          ;取得总线
          CJNE  R2,#00H,IRDNB_RET
          MOV   R0,#MRD
          MOV   R1,NUMBYTE 
          MOV   A,SLA
          LCALL SENDBYTE
          JNB   ACK,IRDNB_END
          MOV   A,SUBA
          LCALL SENDBYTE
          JNB   ACK,IRDNB_END
          LCALL RRST                           ;发送器件子地址后即重新启动总线
          MOV   A,SLA
          INC   A
          LCALL SENDBYTE
          JNB   ACK,IRDNB_END
IRDNB_L1: LCALL RCVBYTE                        ;读出多个字节数据
          MOV   @R0,A
          INC   R0
          DJNZ  R1,IRDNB_L2
          LCALL SNACK        
IRDNB_END:LCALL ENDBUS                    
IRDNB_RET:RET
          
IRDNB_L2: LCALL SACK
          SJMP  IRDNB_L1
        
;无子地址写单字节
;入口参数:写入值在ACC
;出口参数:ACK为1时表明操作正确 
IWRBYTE:          
          CLR   ACK
          MOV   R2,#00H                  
          LCALL GETBUS
          CJNE  R2,#00H,IWRB_RET
          MOV   R1,A                        ;将A暂存在R1
          MOV   A,SLA
          LCALL SENDBYTE
          JNB   ACK,IWRB_END
          MOV   A,R1                    
          LCALL SENDBYTE                    ;器件寻址完后写入数据
IWRB_END: LCALL ENDBUS         
IWRB_RET: RET
 
          
;有子址址多字节写
;占用R0,R1,R3,ACC
;入口参数:器件从地址SLA 子地址SUBA  发送数据区MTD  读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IWRNBYTE:  
          CLR   ACK
          MOV   R2,#00H            
          LCALL GETBUS
          CJNE  R2,#00H,IWRNB_RET
          MOV   R0,#MTD
          MOV   R1,NUMBYTE
          MOV   A,SLA
          LCALL SENDBYTE                          ;器件寻址
          JNB   ACK,IWRNB_END                     ;器件无应答则结束
          MOV   A,SUBA
          LCALL SENDBYTE                          ;发送子地址
          JNB   ACK,IRDNB_END
IWRNB_L1: MOV   A,@R0                             ;取出入数据
          LCALL SENDBYTE                          ;写入数据
          JNB   ACK,IRDNB_END
          INC   R0
          DJNZ  R1,IWRNB_L1                       ;写完NUMBYTE个字节?
IWRNB_END:LCALL ENDBUS         
IWRNB_RET:RET



;无子地址多字节读
;占用R0,R1,R3,ACC
;入口参数:器件从地址SLA 接收数据区MRD  读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IRDNBYTEEXT:   
           CLR   ACK
           MOV   R2,#00H
           LCALL GETBUS                          ;取得总线
           CJNE  R2,#00H,IRDNBEE_RET
           MOV   R0,#MRD
           MOV   R1,NUMBYTE 
           MOV   A,SLA
           INC   A
           LCALL SENDBYTE
           JNB   ACK,IRDNBEE_END
IRDNBEE_L1:LCALL RCVBYTE                        ;读出多个字节数据
           MOV   @R0,A
           INC   R0
           DJNZ  R1,IRDNBEE_L2
           LCALL SNACK        
IRDNBEE_END:LCALL ENDBUS                    
IRDNBEE_RET:RET
          
IRDNBEE_L2:LCALL SACK
           SJMP  IRDNBEE_L1

;无子地址多字节写
;占用R0,R1,R3,ACC
;入口参数:器件从地址SLA  发送数据区MTD  读入字节数NUMBYTE
;出口参数:ACK,ACK=1时操作正确。
IWRNBYTEEXT:
           CLR   ACK
           MOV   R2,#00H            
           LCALL GETBUS
           CJNE  R2,#00H,IWRNBEE_RET
           MOV   R0,#MTD
           MOV   R1,NUMBYTE
           MOV   A,SLA
           LCALL SENDBYTE                          ;器件寻址
           JNB   ACK,IWRNBEE_END                   ;器件无应答则结束
IWRNBEE_L1:MOV   A,@R0                             ;取出入数据
           LCALL SENDBYTE                          ;写入数据
           JNB   ACK,IRDNBEE_END
           INC   R0
           DJNZ  R1,IWRNBEE_L1                     ;写完NUMBYTE个字节?
IWRNBEE_END:LCALL ENDBUS         
IWRNBEE_RET:RET




;                         请注意
;
;占用内部资源:         R0,R1,R2,R3,ACC,Cy。
;    在你的程序里要做以下定义:
;使用前须定义变量:  SLA 器件从地址  SUBA器件子地址  NUMBYTE读/写的字节数 ,位变量ACK 
;使用前须定义常量:  SDA SCL 总线位  MTD 发送数据缓冲区首址    MRD 接收数据缓冲区首址
;(ACK为调试/测试位,ACK为0时表示无器件应答;R2为总线出错字节,不为00H则是总线出错)
;软件包调用返回后,I2CON,I2CFG寄存器值可供诊断。

;(1)IRDBYTE  无子地址读单字节子程序
;       入口:器件从地址SLA
;       出口:读出数据ACC,应答位ACK,总线出错字R2
;       
;(2)IRDNBYTE 有子址地址读多字节子程序
;       入口:器件从地址SLA,器件子地址SUBA,读出字节数NUMBYTE
;       出口:读出数据依次放在MRD区,应答位ACK,总线出错字R2
;
;(3)IWRBYTE  无子地址写单字节子程序
;      入口: 器件从地址SLA,写入的数据ACC
;      出口: 应答位ACK,总线出错字R2
;
;(4)IWRNBYTE 有子地址写多字节子程序
;      入口: 器件从地址SLA,写入的数据依次放在MTD区,写入字节数NUMBYTE
;      出口: 应答位ACK,总线出错字R2

;(5)IRDNBYTEEXT 无子地址读多字节子程序
;       入口:器件从地址SLA,读出字节数NUMBYTE
;       出口:读出数据依次放在MRD区,应答位ACK,总线出错字R2

;(6)IWRNBYTEEXT 无子地址写多字节子程序
;      入口: 器件从地址SLA,写入的数据依次放在MTD区,写入字节数NUMBYTE
;      出口: 应答位ACK,总线出错字R2

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -