1
\$\begingroup\$

As I mentioned here, I've been having issues with EEPROM on dsPIC30F6012A. Since my old EEPROM library was difficult to analyze and support, I went back to square one and rewrote it using the new (newer than my library!) C30 compiler built-in EEPROM routines. I took the Microchip demo code and tried to run it. Compiled, programmed, no problems, but it didn't seem to operate correctly. I made a modified version of their demo, also without success.

I run this program, read the chip's contents back into MPLAB X, and look at the contents of EEPROM. Erase operations work correctly, any addresses I erase comes back as 0xFFFF. But the write operations do nothing. I've tried several combinations, repeated writes, different addresses, nothing appears to be written.

#include <p30fxxxx.h>
#include <libpic30.h>

 _FOSC(CSW_FSCM_OFF & FRC_PLL16); 
_FWDT(WDT_OFF);                 /* Turn off the Watch-Dog Timer.  */
_FBORPOR(MCLR_EN & PWRT_OFF);   /* Enable MCLR reset pin and turn off the power-up timers. */
_FGS(CODE_PROT_OFF);            /* Disable Code Protection */

 typedef struct _eestruct {
    unsigned char testdata[10];
} eestruct;
 eestruct eedata __attribute__((space(eedata)));
 eestruct backup_eedata __attribute__((space(eedata)));

 int main(){

    _prog_addressT EE_addr;
    //_init_prog_address(EE_addr, eedata);
    EE_addr = 0x7FF000;

    int temp_word = 0x0102;
    _erase_eedata(EE_addr, _EE_WORD);
    _wait_eedata();
    _write_eedata_word(EE_addr, temp_word);
    _wait_eedata();

    while(1){
    ClrWdt();
    };
    return 0;
}

I've got four theories:

  1. Writes are taking place, and I'm not reading the data back correctly.
  2. Writes are not taking place, because something is wrong with my code.
  3. Writes are not taking place, because something is wrong with my project/build options.
  4. Writes are not taking place, because something is wrong with my hardware.

I've posted to the Microchip forums without result. I've opened a ticket with Microchip, and all they did was tell me to use/write an assembly call. They seem uninterested in whether/why these built-in function calls don't work.

Does anyone have any suggestions?

\$\endgroup\$
5
  • \$\begingroup\$ What is the device? \$\endgroup\$ – Leon Heller Aug 16 '12 at 13:23
  • \$\begingroup\$ @LeonHeller From the previously mentioned question, it is a dsPIC30F6012a \$\endgroup\$ – W5VO Aug 16 '12 at 13:35
  • \$\begingroup\$ Correct, adding to the question. \$\endgroup\$ – Stephen Collings Aug 16 '12 at 14:15
  • \$\begingroup\$ Does MPLAB X read the EEPROM as expected? I have some theories of my own. (1) Not supported. (2) Doesn't read back until you tell it to in the menu. (3) Doesn't read back correctly. This is all original MPLAB experience. It's possible MPLAB X has fixed all of this. When in doubt, I have the PIC tell me about its EEPROM contents (some kind of dump routine). \$\endgroup\$ – gbarry Aug 16 '12 at 14:54
  • \$\begingroup\$ The data I initialize EEPROM with does read back properly into MPLAB X, and erased values read back as 0xFFFF. Everything seems to work EXCEPT the write. \$\endgroup\$ – Stephen Collings Aug 16 '12 at 15:11
2
\$\begingroup\$

When in doubt, refactor.

It's not likely a hardware issue, but to rule it out, I would try replacing the C calls with the exact inline assembly instructions described in section 7 of the datasheet, following all of its recommendations (disabling interrupts, polling WR to test for doneness, etc.)

If you get it working, make your own function/library from the inline assembly and carry on.

\$\endgroup\$
5
  • \$\begingroup\$ Unfortunately, that's what I had that I'm trying to refactor out! \$\endgroup\$ – Stephen Collings Aug 19 '12 at 16:41
  • \$\begingroup\$ You should post something on the Microchip forums, or get in contact with an FAE if the datasheet-published code does not work. \$\endgroup\$ – Adam Lawrence Aug 22 '12 at 2:11
  • \$\begingroup\$ Already posted on the forums, and opened a ticket. No response to either. \$\endgroup\$ – Stephen Collings Aug 22 '12 at 12:40
  • \$\begingroup\$ Could you post your disassembly / listing file as well? \$\endgroup\$ – jacobq Aug 28 '12 at 2:36
  • \$\begingroup\$ Amusingly, Microchip tech support's answer was also to replace the compiler function calls with assembly code. When I asked why the functions didn't work, they had no answer, and refused to even test my code. Apparently, even though the functions I'm using are listed in the compiler documentation, and even though Microchip publishes demo code using them on their site, the functions are not actually supported by Microchip. \$\endgroup\$ – Stephen Collings Aug 28 '12 at 13:04
0
\$\begingroup\$

First, a good way to check whether EEPROM has been successfully written is to read it back thru the PSV (Program Space Visibility) window. In a lot of cases I don't need the PSV window for anything else, so I leave it permanently configured for access to the EEPROM.

Second, I didn't look at your C code but here is a snippet from a 30F4013 project that sets up the PSV window for reading and writes a byte to the 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

Note that this routine does some nice things beyond just blindly writing the new data to the EEPROM. It does nothing if the EEPROM word is already set as desired, and it also skips the erase if only 1 bits are being flipped to 0. This will wear out the EEPROM less, especially if some other code accidentally goes into a loop writing to EEPROM.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.