在編寫AT89S8253片內EEPROM讀寫驅動程序時,要特別注意數據讀寫指令MOVX;
當EECON寄存器的EEMEN位置位時,MOVX訪問EEPROM;
當EECON寄存器的EEMEN位清零時,MOVX訪問外部RAM;
一般情況下,我們定義的用來寫入EEPROM的數據或保存EEPROM讀取的數據都用外部RAM;代碼具體分析如下:
C語言頁寫入代碼:
unsigned char W_EEPROM_PAGE(unsigned int paddr, unsigned char *pdat)
{
unsigned char temp;
EECON |= EEMEN;
EECON |= EEMWE;
...
For(temp = 0; temp < 32; temp++)
XBYTE[paddr+temp] = pdat[temp];
...
EECON &= ~EEMEN;
EECON &= ~EEMWE;
}
此代碼邏輯清晰,容易理解,可是編譯器沒有那麼智能;編譯器知道pdat指針指向的數據在外部RAM中,用MOVX指令可以讀取;編譯器也知道paddr表示的是要寫入EEPROM的地址,頁用MOVX指令訪問;於是,編譯器就將代碼翻譯成如下彙編指令:
ORL EECON,#08H
ORL EECON,#010H
...
?C0045:
; XBYTE[paddr+temp]= pdat[temp]; //將前31字節寫入頁寫緩衝區
; SOURCE LINE # 242
MOV DPTR,#pdat?663
MOVX A,@DPTR
MOV R3,A
INC DPTR
MOVX A,@DPTR
MOV R2,A
INC DPTR
MOVX A,@DPTR
MOV R1,A
MOV DPL,R7
MOV DPH,#00H
LCALL?C?CLDOPTR
MOV R6,A
MOV A,R7
MOV R5,A
MOV DPTR,#paddr?662+01H
MOVX A,@DPTR
ADD A,R5
MOV R5,A
MOV DPTR,#paddr?662
MOVX A,@DPTR
ADDC A,#00H
MOV DPL,R5
MOV DPH,A
MOV A,R6
MOVX @DPTR,A
INC R7
MOV A,R7
CJNE A,#01FH,?C0045
...
ANL EECON,#0E7H
紅色部分代碼是讀取pdat數組裏的數據,存入R6中,藍色部分代碼是將R6中的數據寫入paddr表示的EEPROM的地址中去;乍一看,完全符合邏輯,可是MOVX指令的訪問目標沒有考慮,在EEPROM使能情況下,MOVX訪問的一直是EEPROM,在上面的代碼中,EEPROM始終保持使能狀態,而我們的待寫入數據,是保存在外部RAM中的,始終沒有被讀取;這樣的代碼,其實是將EEPROM中的某頁數據複製另一頁,而沒有將按照我們的意願將外部RAM中的數據寫入EEPROM;因此,編譯器曲解了我們的C語言代碼。
到目前爲止,直接編寫彙編成了唯一可以解決此BUG的方法,於是乎,我們可以修改編譯器彙編好的代碼,在要讀取外部RAM數據時,關閉EEPROM使能,獲取數據,在準備寫入EEPROM時,再打開EEPROM使能;於是乎,我們在頻繁的打開關閉EEPROM使能;於是乎,EEPROM就不正常工作了,寫入失敗;
我們可以參考ATMEL官方給出的例程,編寫彙編代碼;例程中,寫入數據是從寶貴的DATA段獲取的,使用MOV指令就可以輕鬆得到,不用考慮MOVX訪問區域的限制。
至於此,我們只有拿出32字節(EEPROM一頁數據大小)寶貴的DATA段空間,用於緩存待寫入數據和讀出數據,才能實現正確的數據讀寫,實現代碼如下:
;定義全局共享數據讀寫緩衝區(32字節)
RSEG ?DT?AT89S8253_EEPROM_drv
buff: DS 32
RSEG ?C_INITSEG
DB020H
DB020H
DBbuff
DB000H
DB 000H,000H,000H,000H,000H,000H,000H,000H,000H,000H
DB 000H,000H,000H,000H,000H,000H,000H,000H,000H,000H
DB 000H,000H,000H,000H,000H,000H,000H,000H,000H,000H
DB 000H
...
; unsigned char W_EEPROM_PAGE(unsigned int paddr, unsigned char * pdat)
RSEG ?PR?_W_EEPROM_PAGE?AT89S8253_EEPROM_drv
_W_EEPROM_PAGE:
USING0
;將待寫入數據從XDATA段轉儲到DATA段緩衝區
MOV R4, #0H
MOV DPH, R2
MOV DPL, R1
w_p_dat_read:
MOV A,#LOW(buff)
ADD A, R4
MOV R0, A
MOVX A, @DPTR
MOV @R0, A
INC DPTR
INC R4
CJNE R4, #020H, w_p_dat_read
;使能EEPROM
ORL EECON,#018H
;檢查電壓是否達到寫EEPROM要求
MOV A,EECON
ANL A,#02H
MOV r0,A
jzw_p_ERROR
;等待EEPROM狀態轉爲閒
w_p_rdy1:
MOV A,EECON
ANL A,#02H
MOV r0,A
jzw_p_rdy1
;打開頁寫緩衝區
ORL EECON,#020H
;向頁寫緩衝區中寫入一頁前31字節
MOVR4, #0H
MOV DPL,R7
MOV DPH,R6
w_p_buff:
MOVA,#LOW(buff)
ADDA, R4
MOVR0, A
MOVA, @R0
MOVX @DPTR,A
INCDPTR
INCR4
CJNER4, #01FH,w_p_buff
;關閉頁寫緩衝區
ANL EECON,#0DFH
;寫入一頁的最後一個數據,啓動頁寫週期
MOVA,#LOW(buff)
ADDA, R4
MOVR0, A
MOVA, @R0
MOVX @DPTR,A
;等待硬件響應頁寫
w_p_busy:
MOV A,EECON
ANL A,#02H
MOV r5,A
jnzw_p_busy
;等待寫入完成
w_p_rdy2:
MOV A,EECON
ANL A,#02H
MOV r5,A
jzw_p_rdy2
;從DATA緩衝區讀取寫入的最後一個字節,存於R5中
MOVA, @R0
MOVR5, A
;將寫入EEPROM的最後一個字節從EEPROM中讀取出來,暫存於累加器A中
MOVX A, @DPTR
;用相減後零標誌位來判斷寫入是否正確
SUBBA, R5
JNZw_p_ERROR
;寫入成功,準備返回1,表示成功
MOV R7,#01H
JMPw_p_complete
;寫入失敗,準備返回0,表示失敗
w_p_ERROR:
MOVR7, #0H
;頁寫完成,關閉EEPROM使能
w_p_complete:
ANL EECON,#0E7H
; } ; SOURCE LINE # 66
?C0004:
RET
; END OF _W_EEPROM_PAGE
$NOMOD51
NAME AT89S8253_EEPROM_drv
EA BIT 0A8H.7
EECON DATA 96H ; watchdog and memory control register
EEMEN EQU 00001000B ; EEPROM access enable bit
EEMWE EQU 00010000B ; EEPROM write enable bit
EELD EQU 00100000B ; EEPROM page load enable bit
WRTINH EQU 00000001B ; EEPROM WRTINHbit
RDY EQU 00000010B ; EEPROM RDY/BSYbit
?PR?_R_EEPROM_BYTE?AT89S8253_EEPROM_drv SEGMENT CODE
?XD?_R_EEPROM_BYTE?AT89S8253_EEPROM_drv SEGMENT XDATA OVERLAYABLE
?PR?_R_EEPROM_PAGE?AT89S8253_EEPROM_drv SEGMENT CODE
?XD?_R_EEPROM_PAGE?AT89S8253_EEPROM_drv SEGMENT XDATA OVERLAYABLE
?PR?_W_EEPROM_BYTE?AT89S8253_EEPROM_drv SEGMENT CODE
?XD?_W_EEPROM_BYTE?AT89S8253_EEPROM_drv SEGMENT XDATA OVERLAYABLE
?PR?_W_EEPROM_PAGE?AT89S8253_EEPROM_drv SEGMENT CODE
?XD?_W_EEPROM_PAGE?AT89S8253_EEPROM_drv SEGMENT XDATA OVERLAYABLE
?C_INITSEG SEGMENT CODE
?DT?AT89S8253_EEPROM_drv SEGMENT DATA
PUBLIC buff
PUBLIC _W_EEPROM_PAGE
PUBLIC _W_EEPROM_BYTE
PUBLIC _R_EEPROM_PAGE
PUBLIC _R_EEPROM_BYTE
RSEG ?XD?_W_EEPROM_PAGE?AT89S8253_EEPROM_drv
?_W_EEPROM_PAGE?BYTE:
paddr?345: DS 2
ORG 2
pdat?346: DS 3
RSEG ?XD?_R_EEPROM_BYTE?AT89S8253_EEPROM_drv
?_R_EEPROM_BYTE?BYTE:
addr?040: DS 2
RSEG ?XD?_W_EEPROM_BYTE?AT89S8253_EEPROM_drv
?_W_EEPROM_BYTE?BYTE:
addr?243: DS 2
ORG 2
dat?244: DS 1
RSEG ?XD?_R_EEPROM_PAGE?AT89S8253_EEPROM_drv
?_R_EEPROM_PAGE?BYTE:
paddr?141: DS 2
ORG 2
pdat?142: DS 3
RSEG ?DT?AT89S8253_EEPROM_drv
buff: DS 32
RSEG ?C_INITSEG
DB 020H
DB 020H
DB buff
DB 000H
DB 000H,000H,000H,000H,000H,000H,000H,000H,000H,000H
DB 000H,000H,000H,000H,000H,000H,000H,000H,000H,000H
DB 000H,000H,000H,000H,000H,000H,000H,000H,000H,000H
DB 000H
; /********************************************************************
; * 文件名:AT89S8253_EEPROM_drv_V0.6.c
; * 文件內容:AT89S8253 EEPROM字節寫,字節讀代碼及相關測試代碼
; * 創作人: 付偉龍
; * 創作時間:2013年07月11日
; * 修改時間:2013年07月20日
; * 修改內容:修改頁寫頁讀代碼,修改測試代碼,此版本代碼能正確運行,讀寫EEPROM
; * 版本: v1.0
; ********************************************************************/
;
;
; unsigned char data buff[32] = {0};
;
; /*******************************************************************
; * 函數原型:unsigned char R_EEPROM_BYTE(unsigned int addr)
; * 函數功能:EEPROM字節讀函數
; * 輸入參數: unsigned int addr 讀基地址
; * 輸出參數: 無
; * 返回值: 讀取到的數據
; *******************************************************************/
; unsigned char R_EEPROM_BYTE(unsigned int addr)//讀一地址,返回所讀值
RSEG ?PR?_R_EEPROM_BYTE?AT89S8253_EEPROM_drv
_R_EEPROM_BYTE:
USING 0
; SOURCE LINE # 21
; MOV DPTR,#addr?040
; MOV A,R6
; MOVX @DPTR,A
; INC DPTR
; MOV A,R7
; MOVX @DPTR,A
; {
; SOURCE LINE # 22
orl EECON, #EEMEN ; enable EEPROM accesses ]
r_b_rdy:
MOV A,EECON
ANL A,#02H
MOV r0,A
jz r_b_rdy
mov DPH, R6
mov DPL, R7
movx A, @dptr ; read EEPROM
xrl EECON, #EEMEN ; disable EEPROM accesses
; SOURCE LINE # 23
MOV R7,A
; }
; SOURCE LINE # 24
?C0001:
RET
; END OF _R_EEPROM_BYTE
;
; /*******************************************************************
; * 函數原型:void R_EEPROM_PAGE(unsigned int paddr, unsigned char * pdat)
; * 函數功能:EEPROM頁讀函數
; * 輸入參數: unsigned int paddr 讀基地址
; * unsigned char * pdat數據保存指針
; * 輸出參數: pdat 讀取數據保存地址
; * 返回值: 無
; *******************************************************************/
; void R_EEPROM_PAGE(unsigned int paddr, unsigned char * pdat)
RSEG ?PR?_R_EEPROM_PAGE?AT89S8253_EEPROM_drv
_R_EEPROM_PAGE:
; SOURCE LINE # 34
; MOV DPTR,#paddr?141
; MOV A,R6
; MOVX @DPTR,A
; INC DPTR
; MOV A,R7
; MOVX @DPTR,A
; INC DPTR
; MOV A,R3
; MOVX @DPTR,A
; INC DPTR
; MOV A,R2
; MOVX @DPTR,A
; INC DPTR
; MOV A,R1
; MOVX @DPTR,A
; {
orl EECON, #EEMEN
r_p_rdy:
MOV A,EECON
ANL A,#02H
MOV r0,A
jz r_p_rdy
mov r4, #0h
mov DPH, R6
mov DPL, R7
r_p_next:
movx A, @dptr ; read EEPROM
mov r5, A
MOV A,#LOW (buff)
ADD A, R4
MOV R0, A
MOV @R0, AR5
INC DPTR
INC R4
CJNE R4, #020H, r_p_next
ANL EECON,#0F7H
MOV R4, #0H
MOV DPH, R2
MOV DPL, R1
r_p_save:
MOV A,#LOW(buff)
ADD A, R4
MOV R0, A
MOV A, @R0
MOVX @DPTR, A
INC DPTR
INC R4
CJNE R4, #020H, r_p_save
; }
; SOURCE LINE # 37
RET
; END OF _R_EEPROM_PAGE
;
; /*******************************************************************
; * 函數原型:unsigned char W_EEPROM_BYTE(unsigned int addr ,unsigned char dat)
; * 函數功能:EEPROM字節寫函數
; * 輸入參數: unsigned int addr 寫基地址
; * unsigned char dat待寫入數據
; * 輸出參數: 無
; * 返回值: 1 寫入成功
; * FALSE 寫入失敗
; *******************************************************************/
; unsigned char W_EEPROM_BYTE(unsigned int addr ,unsigned char dat)
RSEG ?PR?_W_EEPROM_BYTE?AT89S8253_EEPROM_drv
_W_EEPROM_BYTE:
USING 0
; SOURCE LINE # 48
; MOV DPTR,#addr?243
; MOV A,R6
; MOVX @DPTR,A
; INC DPTR
; MOV A,R7
; MOVX @DPTR,A
; INC DPTR
; MOV A,R5
; MOVX @DPTR,A
; {
ORL EECON,#018H
MOV A,EECON
ANL A,#02H
MOV r0,A
jz w_b_ERROR
w_b_rdy1:
MOV A,EECON
ANL A,#02H
MOV r0,A
jz w_b_rdy1
MOV DPL,R7
MOV DPH,R6
MOV A,R5
MOVX @DPTR,A
w_b_busy:
MOV A,EECON
ANL A,#02H
MOV r0,A
jnz w_b_busy
w_b_rdy2:
MOV A,EECON
ANL A,#02H
MOV r0,A
jz w_b_rdy2
MOVX A, @DPTR
SUBB A, R5
JNZ w_b_ERROR
MOV R7,#01H
JMP w_b_complete
w_b_ERROR:
MOV R7, #0H
w_b_complete:
ANL EECON,#0E7H
; }
; SOURCE LINE # 51
?C0003:
RET
; END OF _W_EEPROM_BYTE
;
; /*******************************************************************
; * 函數原型:W_EEPROM_PAGE(unsigned int paddr, unsigned char * pdat)
; * 函數功能:EEPROM頁寫函數
; * 輸入參數: unsigned int paddr 寫基地址(必須是頁大小的整數倍,
; * 若不是整數倍,寫入此地址所在頁)
; * unsigned char * pdat待寫入數據指針
; * 輸出參數: 無
; * 返回值: 1 寫入成功
; * FALSE 寫入失敗
; *******************************************************************/
; unsigned char W_EEPROM_PAGE(unsigned int paddr, unsigned char * pdat)
RSEG ?PR?_W_EEPROM_PAGE?AT89S8253_EEPROM_drv
_W_EEPROM_PAGE:
USING 0
; SOURCE LINE # 63
; MOV DPTR,#paddr?345
; MOV A,R6
; MOVX @DPTR,A
; INC DPTR
; MOV A,R7
; MOVX @DPTR,A
; INC DPTR
; MOV A,R3
; MOVX @DPTR,A
; INC DPTR
; MOV A,R2
; MOVX @DPTR,A
; INC DPTR
; MOV A,R1
; MOVX @DPTR,A
; {
MOV R4, #0H
MOV DPH, R2
MOV DPL, R1
w_p_dat_read:
MOV A,#LOW(buff)
ADD A, R4
MOV R0, A
MOVX A, @DPTR
MOV @R0, A
INC DPTR
INC R4
CJNE R4, #020H, w_p_dat_read
ORL EECON,#018H
MOV A,EECON
ANL A,#02H
MOV r0,A
jz w_p_ERROR
w_p_rdy1:
MOV A,EECON
ANL A,#02H
MOV r0,A
jz w_p_rdy1
ORL EECON,#020H
MOV R4, #0H
MOV DPL,R7
MOV DPH,R6
w_p_buff:
MOV A,#LOW(buff)
ADD A, R4
MOV R0, A
MOV A, @R0
MOVX @DPTR,A
INC DPTR
INC R4
CJNE R4, #01FH,w_p_buff
ANL EECON,#0DFH
MOV A,#LOW(buff)
ADD A, R4
MOV R0, A
MOV A, @R0
MOVX @DPTR,A
w_p_busy:
MOV A,EECON
ANL A,#02H
MOV r5,A
jnz w_p_busy
w_p_rdy2:
MOV A,EECON
ANL A,#02H
MOV r5,A
jz w_p_rdy2
MOV A, @R0
MOV R5, A
MOVX A, @DPTR
SUBB A, R5
JNZ w_p_ERROR
MOV R7,#01H
JMP w_p_complete
w_p_ERROR:
MOV R7, #0H
w_p_complete:
ANL EECON,#0E7H
; } ; SOURCE LINE # 66
?C0004:
RET
; END OF _W_EEPROM_PAGE
END