En primer lugar, una buena manera de verificar si la EEPROM se ha escrito correctamente es volver a leerla a través de la ventana de Visibilidad del Espacio del Programa (PSV). En muchos casos no necesito la ventana de PSV para nada más, así que la dejo configurada permanentemente para el acceso a la EEPROM.
Segundo, no miré su código C, pero aquí hay un fragmento de un proyecto 30F4013 que configura la ventana de PSV para leer y escribe un byte en la EEPROM:
;*******************************************************************************
;
; Subroutine IEE_INIT
;
; Initialize the hardware and software state managed by this module.
;
glbsub iee_init, regf0
;
; Set up the program space visibility window to map the internal data EEPROM
; to data space.
;
mov #psvpage(ieestart), w0
mov w0, Psvpag ;which region of prog memory to map to data adr 8000-FFFF
bset Corcon, #Psv ;enable program space visibility window
leaverest
;*******************************************************************************
;
; Macro WAIT_EEPROM
;
; Wait for any previous EEPROM operation to complete. TASK_YIELD is called in
; a loop for the duration of the wait. TASK_YIELD is not called if the EEPROM
; is not active on entry.
;
.macro wait_eeprom
1: ;back here to check EEPROM busy again
btss Nvmcon, #Wr ;EEPROM is busy ?
jump 2f ;no, all done
gcall task_yield ;give other tasks a chance to run
jump 1b ;back to check EEPROM busy again
2: ;the EEPROM is now idle
.endm
;*******************************************************************************
;
; Subroutine IEE_WRITE
;
; Write the value in W0 to the EEPROM word at the offset in W1. W1 must
; contain the low 16 bits of the EEPROM location as mapped into program
; memory. This can be found, for example, by using the TBLOFFSET function on
; any of the address symbols defined in the .EEPROM section above. Since there
; are two addresses per EEPROM word and a whole word is always written, the
; low bit of W1 is irrelevant.
;
; W1 is returned incremented by 2, which is the address of the next EEPROM
; word. Successive calls to this routine without modifying W1 therefore
; set sequential EEPROM words.
;
; This routine returns after the write has completed. This may take several
; milliseconds. TASK_YIELD is called in a loop during any waits.
;
; This processor has 512 words of EEPROM.
;
glbsub iee_write, regf2
wait_eeprom ;wait for any previous operation to complete
;
; Get the current EEPROM target word value into W2.
;
mov #tblpage(ieestart), w2
mov w2, Tblpag ;select the program memory page to access
tblrdl [w1], w2 ;read the target EEPROM word
cp w0, w2 ;compare the new value to the existing
bra z, iew_leave ;no change, nothing to do ?
;
; Erase the EEPROM word if any 0 bits are being changed to 1 bits. The
; existing value of the word is in W2. TBLPAG is already set approriately for
; the EEPROM address.
;
com w2, w2 ;make mask of bits currently set to 0
and w2, w0, w2 ;make mask of 0 bits to change to 1
bra z, iew_derase ;no 1 bits changing to 0, skip the erase
;
; Perform the erase.
;
; According to the family reference manual, to perform a erase you
; need to set the address and then do a word erase. However, we have
; found this doesn't work for the first write attempt since startup.
; After some experimentation it was found that loading a write latch
; once seems to be what allows erases to work. We therefore load the
; write latch before doing a erase, even though this is not necessary
; according to the documentation. Since loading the write latch for
; the word to erase also sets the address, we don't need to set
; NVMADR explicitly. The data written to the write latch probably
; doesn't matter, but we write the erase value since that makes the
; most sense and has the best chance of working around any additional
; undocumented behavior.
;
mov #0xFFFF, w2
tblwtl w2, [w1] ;load latch with the erase value, set address
mov #0b0100000001000100, w2
; 0--------------- don't initiate operation now
; -1-------------- enable erase operation
; --0------------- clear any previous error condition
; ---XXXXX-------- unused
; --------0100---- select erase operation
; ------------0100 one data word
mov w2, Nvmcon ;configure for the erase operation
disi #5 ;disable interrupts around unlock sequence
mov #0x55, w2 ;perform unlock sequence
mov w2, Nvmkey
mov #0xAA, w2
mov w2, Nvmkey
bset Nvmcon, #Wr ;start the erase
wait_eeprom ;wait for the erase to complete
iew_derase: ;done with erasing the word
;
; Write the word.
;
tblwtl w0, [w1] ;write the word, increment address
mov #0b0100000000000100, w2
; 0--------------- don't initiate operation now
; -1-------------- enable erase operation
; --0------------- clear any previous error condition
; ---XXXXX-------- unused
; --------0000---- select write operation
; ------------0100 one data word
mov w2, Nvmcon ;configure for the erase operation
disi #5 ;disable interrupts around unlock sequence
mov #0x55, w2 ;perform unlock sequence
mov w2, Nvmkey
mov #0xAA, w2
mov w2, Nvmkey
bset Nvmcon, #Wr ;start the erase
wait_eeprom ;wait for the write to complete
iew_leave: ;common exit point
add w1, #2, w1 ;increment address to the next EEPROM word
leaverest
Tenga en cuenta que esta rutina hace algunas cosas agradables más allá de solo escribir ciegamente los nuevos datos en la EEPROM. No hace nada si la palabra EEPROM ya está configurada como se desea, y también omite el borrado si solo 1 bit se invierte a 0. Esto desgastará menos la EEPROM, especialmente si algún otro código ingresa accidentalmente en un bucle al escribir en la EEPROM .