📄 hi2c_asm.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 + -