?? 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
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 + -