News:

Let's find out together what makes a PIC Tick!

Main Menu

Losing Eeprom information

Started by Amateurtje, May 17, 2024, 07:10 AM

Previous topic - Next topic

Amateurtje

Hello,

I have a strange thing happening. I have now got a couple of items back that loose completely partly their eeprom when the power was taken off (for a while)... Not all  do that, but already a few.

I thought it might be bad chips but I found out that they did not come from the same batch. They were bought from Farnell, which sells only original chips..

Anybody any expierience with this?

The program writes and reads to the eeprom normally. When setting it back, it works again... but, it is not work-able to do this every time..

John Lawton

If your unit lost power whilst in the middle of writing to EEPROM then you could expect problems.

You may need to protect against this happening by sensing an imminent power loss event before the PIC power goes too low and inhibiting any EEPROM writes.

Ensure any PIC power loss takes at least as long as an EEPROM write time by adding sufficient supply line reservoir capacitance and cut the PIC's power consumption to a minimum when a power loss is detected to increase it's survival time during this time.

Amateurtje

Hi John,

 Oke, never knew this. Good thing. I have a kind of standby script running but I do not think there is a write instruction inside, I can not imagine why that would be.. But I will check.. And I can instruct the people to power down before cutting the power (it goes to that standby script).  But there are not a lot write instructions in the program. Only in a special (setup) menu and 1 other thtat occurs occasionally.

Detecting the power down is a good idea but will cost me a pin.. I do however, not have a lot of pins left.. (in 1 project I have all pins occupied).

So, I think this is not the problem but it is very good information.  for sure I am going to  re-check all write instructions andadd this to the manual..

PS. I just had contact with Microchip. They will escalate the issue and probably investigate this issue. He also thought it might have something to do with the chip.. I probably have to send pictures/good desription and the code.... We will see what comes form that and I will test further.. What I know, I will share here.

trastikata

If you google it, it seems the issue occurs more frequently than we'd expect.

Google: "pic EEPROM corrupted by power surge"

There are plenty of posts facing the same issue, good to know this might be a problem, thanks.

In any case where information is to be protected I usually write in EEPROM at three or more positions. Each position has a marker place, where I first  indicate that write has been initiated and after taht read it back and if correct then I write in the marker place that the write was successful.  Repeat that in 3 or more EEPROM places.

Next time when you need to read back the EEPROM, first check the marker and if OK, then read it, if not read the next place and if OK, update the first data space.

The disadvantage is that it takes longer to update the data, but if it is just few coefficients or parameters then it is acceptable. The code looks something like:

Dim bMyData As Byte

Symbol WriteStart = 123
Symbol WriteDone = 231

Main:
    'Read parameter from EEPROM
    bMyData = ReadEepromParameter()

    'Place new parameter to EEPROM
    bMyData = 99
    UpdateEeprom(bMyData)
End


Proc ReadEepromParameter(), Byte
    'Read first instance
     If ERead 0 = WriteDone Then
        Result = ERead 1
        ExitProc
     EndIf
     
     'Read second instance and correct corruption if OK
     If ERead (0 + 32) = WriteDone Then
        Result = ERead (1 + 32)
        UpdateEeprom(Result)
        ExitProc
     EndIf
     
     'Read third instance and correct corruption if OK
     If ERead (0 + 64) = WriteDone Then
        Result = ERead (1 + 64)
        UpdateEeprom(Result)
        ExitProc
     EndIf
     
     'If you reached here, then all instances of this file are corrupted, do something else
EndProc

Proc UpdateEeprom(bData As Byte)
    'Write first instance
    'Indicate write has been initiated
    EWrite 0, [WriteStart] : DelayMS 5
    'Write data in EEPROM
    EWrite 1, [bData] : DelayMS 5
    'Indicate write has finished
    EWrite 0, [WriteDone] : DelayMS 5
   
    'Write second instance
    'Indicate write has been initiated
    EWrite 0 + 32, [WriteStart] : DelayMS 5
    'Write data in EEPROM
    EWrite 1 + 32, [bData] : DelayMS 5
    'Indicate write has finished
    EWrite 0 + 32, [WriteDone] : DelayMS 5
   
    'Write Third instance
    'Indicate write has been initiated
    EWrite 0 + 64, [WriteStart] : DelayMS 5
    'Write data in EEPROM
    EWrite 1 + 64, [bData] : DelayMS 5
    'Indicate write has finished
    EWrite 0 + 64, [WriteDone] : DelayMS 5
EndProc   


Frizie

Amateurtje, try a DELAYMS 30 after the EWRITE instruction, see if this helps.
Ohm sweet Ohm | www.picbasic.nl

JonW

"Detecting the power down is a good idea but will cost me a pin.. I do however, not have a lot of pins left.. (in 1 project I have all pins occupied)."

What device is it?  Many Pics have low voltage detection modules or the ability to read the VCC against a lower voltage reference; some of these modules are hardware-based with interrupts, so you can quickly detect if the VDD is changing.  This way you may not need to sacrifice a pin and just slow the VDD and power like John suggested.

top204

I've used the HLVD (High/Low-Voltage Detect) peripheral a few times in projects and it works well. It also seems to follow the same mechanism with different devices, so below is a demo program showing it working on a PIC18F26K40 device, using both the HLVD flag, or an interrupt trip.

For EEPROM access, it would be better to check the HLVD flag before the write to EEPROM takes place and not bother with the interrupt mechanism. Also, it would be better to create a procedure for the EEPROM write instead of the EWrite command, so there is more control over it before the write actually takes place.

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A demonstration of the HLVD (High/Low-Voltage Detect) peripheral on a PIC18F26K40 device.
' The demo detects a falling voltage and transmits to a serial terminal if the voltage falls to and below the threshold set.
' The demo also uses two methods of detection, interrupt and HLVD flag, but in reality, only one method is usually enough.
'
' Written for the Positron8 BASIC compiler by Les Johnson
'
    Device = 18F26K40                                   ' Tell the compiler what device to compile for
    Declare Xtal = 64                                   ' Tell the compiler what frequency the device is operating at (in MHz)
    On_Hardware_Interrupt Goto ISR_Handler              ' Point to the interrupt handler
'
' Setup USART1
'
    Declare HSerial1_Baud = 9600
    Declare HRsout1_Pin = PORTC.6

'------------------------------------------------------------------------------
$define IntGlobal_Enable() INTCONbits_GIE = 1           ' Enable global interrupts
$define IntGlobal_Disable() INTCONbits_GIE = 0          ' Disable global interrupts

'------------------------------------------------------------------------------
$define IntPeriph_Enable()  INTCONbits_GIEL = 1         ' Enable peripheral interrupts
$define IntPeriph_Disable() INTCONbits_GIEL = 0         ' Disable peripheral interrupts

'------------------------------------------------------------------------------
' The voltages to set a trip for the HLVD peripheral on a PIC18F26K40 device
'
$define cHLVD_TRIP_POINT_1_85V 0                        ' The value for the HLVD peripheral to trip at 1.85 Volts
$define cHLVD_TRIP_POINT_2_06V 1                        ' The value for the HLVD peripheral to trip at 2_06 Volts
$define cHLVD_TRIP_POINT_2_26V 2                        ' The value for the HLVD peripheral to trip at 2_26 Volts
$define cHLVD_TRIP_POINT_2_47V 3                        ' The value for the HLVD peripheral to trip at 2_47 Volts
$define cHLVD_TRIP_POINT_2_57V 4                        ' The value for the HLVD peripheral to trip at 2_57 Volts
$define cHLVD_TRIP_POINT_2_78V 5                        ' The value for the HLVD peripheral to trip at 2_78 Volts
$define cHLVD_TRIP_POINT_2_88V 6                        ' The value for the HLVD peripheral to trip at 2_88 Volts
$define cHLVD_TRIP_POINT_3_09V 7                        ' The value for the HLVD peripheral to trip at 3_09 Volts
$define cHLVD_TRIP_POINT_3_40V 8                        ' The value for the HLVD peripheral to trip at 3_40 Volts
$define cHLVD_TRIP_POINT_3_60V 9                        ' The value for the HLVD peripheral to trip at 3_60 Volts
$define cHLVD_TRIP_POINT_3_71V 10                       ' The value for the HLVD peripheral to trip at 3_71 Volts
$define cHLVD_TRIP_POINT_3_91V 11                       ' The value for the HLVD peripheral to trip at 3_91 Volts
$define cHLVD_TRIP_POINT_4_12V 12                       ' The value for the HLVD peripheral to trip at 4_12 Volts
$define cHLVD_TRIP_POINT_4_32V 13                       ' The value for the HLVD peripheral to trip at 4_32 Volts
$define cHLVD_TRIP_POINT_4_63V 14                       ' The value for the HLVD peripheral to trip at 4_63 Volts
'
' Create any global variables here
'
    Dim Global_tVoltageLow As Bit                       ' Is 1 if the HLVD triggered the interrupt (must be reset in the main program)

'------------------------------------------------------------------------------
' The main program starts here
' A demonstratio to check for a falling voltage on the PIC18F26K40 microcontroller
' If the voltage falls, transmit to a serial terminal if it was the HLVD flag or an interrupt that detected it
'
Main:
    Setup()                                             ' Setup the program and peripherals

    Do                                                  ' Create a loop
        If HLVD_OutputStatus() = 1 Then                 ' Is the HLVDCON0bits_HLVDOUT bit high?
            HRsoutLn "Voltage Low via HLVDCON0bits_HLVDOUT flag"    ' Yes. So transmit to a serial terminal that the voltage is low
        Else                                            ' Otherwise...
            HRsoutLn "Voltage OK"                       ' Transmit to a serial terminal that the voltage is OK
        EndIf
        '
        ' Detect a low voltage via an interrupt
        '
        If Global_tVoltageLow = 1 Then                  ' Did the interrupt set the bit flag "Global_tVoltageLow"?
            HRsoutLn "Voltage Low via Interrupt"        ' Yes. So transmit to a serial terminal that the voltage is low
            Global_tVoltageLow = 0                      ' Reset the bit flag "Global_tVoltageLow"
        EndIf
        DelayMs 500                                     ' A general delay between loop iterations
    Loop                                                ' Do it forever

'------------------------------------------------------------------------------
' Setup the program and peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    Oscillator_64MHz()                                  ' Set the microcontroller to internal 64MHz operation with an HFINTOSC_1MHZ fuse
    HLVD_Init_3_91()                                    ' Setup the HLVD peripheral for 3.91 volts
    HLVD_Int_Falling()                                  ' Set the HLVD peripheral to interrupt on a falling voltage

    Global_tVoltageLow = 0                              ' Clear the bit flag for the voltage falling
    IntPeriph_Enable()                                  ' Enable peripheral interrupts
    IntGlobal_Enable()                                  ' Enable global interrupts
EndProc

'------------------------------------------------------------------------------
' Setup the HLVD peripheral for 3.91 volts
' Input     : None
' Output    : None
' Notes     : Waits for it to stabilise
'
Proc HLVD_Init_3_91()
    HLVDCON1 = cHLVD_TRIP_POINT_3_91V                   ' Select 3.91 volts
    HLVDCON0 = $80                                      ' HLVDEN enabled
    PIR2bits_HLVDIF = 0
    Clrwdt
    PRODL = 100                                         ' \
    Repeat                                              ' |
        If HLVDCON0bits_HLVDRDY =  1 Then Break         ' | Wait for it to become stable
        DelayMS 1                                       ' |
        Dec PRODL                                       ' |
    Until STATUSbits_Z = 1                              ' /
EndProc

'------------------------------------------------------------------------------
' Disable the HLVD module
' Input     : None
' Output    : None
' Notes     : None
'
Proc HLVD_Disable()
    HLVDCON0bits_HLVDEN = 0
EndProc

'------------------------------------------------------------------------------
' Set the HLVD peripheral to interrupt on a falling voltage
' Input     : None
' Output    : None
' Notes     : HLVDCON1 must be previously set with the voltage threshold
'
Proc HLVD_Int_Falling()
    HLVDCON0bits_HLVDINTL = 1                           ' Set Negative trip
    HLVDCON0bits_HLVDINTH = 0                           ' Clear Rising trip
    PIR2bits_HLVDIF = 0                                 ' Clear the HLVD interrupt flag
    PIE2bits_HLVDIE = 1                                 ' Enable HLVD interrupt
EndProc

'------------------------------------------------------------------------------
' Set the HLVD peripheral to interrupt on a rising voltage
' Input     : None
' Output    : None
' Notes     : HLVDCON1 must be previously set with the voltage threshold
'
Proc HLVD_Int_Rising()
    HLVDCON0bits_HLVDINTL = 0                           ' Clear Negative trip
    HLVDCON0bits_HLVDINTH = 1                           ' Set Rising trip
    PIR2bits_HLVDIF = 0                                 ' Clear the HLVD interrupt flag
    PIE2bits_HLVDIE = 1                                 ' Enable HLVD interrupt
EndProc

'------------------------------------------------------------------------------
' Return HLVD voltage status
' Input     : None
' Output    : Returns 1 if the voltage level has tripped
' Notes     : None
'
Proc HLVD_OutputStatus(), Bit
    Result = HLVDCON0bits_HLVDOUT
EndProc

'------------------------------------------------------------------------------------------------
' Set the PIC18F26K40 to internal 64MHz operation with an HFINTOSC_1MHZ fuse
' Input     : None
' Output    : None
' Notes     : None
'
Proc Oscillator_64MHz()
    OSCCON1 = %01100000
    OSCCON3 = %00000000
    OSCEN   = %00000000
    OSCFRQ  = %00001000                                 ' 64MHz
    OSCTUNE = %00000000
    Clrwdt
    Repeat : Until OSCSTATbits_HFOR = 1
EndProc

'------------------------------------------------------------------------------------------------
' Interrupt Handler
' Input     : None
' Output    : Global_tVoltageLow is set to 1 if the HLVD peripheral is triggered
' Notes     : None
'
ISR_Handler:
    Context Save                                        ' Save any important SFRs and compiler system variables before the interrupt code runs

    If PIR2bits_HLVDIF = 1 Then                         ' Was it an HLVD peripheral that triggered the interrupt?
        Global_tVoltageLow = 1                          ' Yes. So set the bit flag Global_tVoltageLow (must be reset in the main program)
        PIR2bits_HLVDIF = 0                             ' Clear the HLVD interrupt flag
    EndIf

    Context Restore                                     ' Restore any SFRs and compiler system variables, then exit the interrupt

'------------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator on a PIC18F26K40
'
Config_Start
    RSTOSC = HFINTOSC_1MHZ                              ' With HFFRQ = 4MHz and CDIV = 4:1
    FEXTOSC = Off                                       ' External Oscillator not enabled
    CLKOUTEN = Off                                      ' CLKOUT function is disabled
    CSWEN = On                                          ' Writing to NOSC and NDIV is allowed
    FCMEN = Off                                         ' Fail-Safe Clock Monitor disabled
    MCLRE = EXTMCLR                                     ' MCLR pin is MCLR
    PWRTE = On                                          ' Power up timer enabled
    LPBOREN = off                                       ' LPBOREN disabled
    BOREN = On                                          ' Brown-out Reset enabled and controlled by software (BORCONbits_SBOREN is enabled)
    BORV = VBOR_285                                     ' Brown-out Reset Voltage set to 2.85V
    ZCD = Off                                           ' ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
    PPS1WAY = Off                                       ' PPSLOCK bit can be set and cleared repeatedly (subject to the unlock sequence)
    STVREN = Off                                        ' Stack full/underflow will not cause Reset
    Debug = Off                                         ' Background debugger disabled
    XINST = Off                                         ' Extended Instruction Set and Indexed Addressing Mode disabled
    SCANE = Off                                         ' Scanner module is Not available for use. SCANMD bit is ignored
    LVP = Off                                           ' Low Voltage programming disabled
    WDTE = Off                                          ' WDT disabled
    WDTCPS = WDTCPS_17                                  ' Watchdog Divider ratio 1:4194304 (128 seconds)
    WDTCWS = WDTCWS_7                                   ' Window always open (100%). Software control. Keyed access not required
    WDTCCS = LFINTOSC                                   ' WDT input clock selector->WDT reference clock is the 31.2kHz HFINTOSC output
    WRT0 = Off                                          ' Block 0 (000800-001FFFh) not write-protected
    WRT1 = Off                                          ' Block 1 (002000-003FFFh) not write-protected
    WRTC = Off                                          ' Configuration registers (300000-30000Bh) not write-protected
    WRTB = Off                                          ' Boot Block (000000-0007FFh) write-protected
    WRTD = Off                                          ' Data EEPROM not write-protected
    Cp = Off                                            ' UserNVM code protection disabled
    CPD = Off                                           ' DataNVM code protection disabled
    EBTR0 = Off                                         ' Block 0 (000800-001FFFh) not protected from table reads executed in other blocks
    EBTR1 = Off                                         ' Block 1 (002000-003FFFh) not protected from table reads executed in other blocks
    EBTRB = Off                                         ' Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

Amateurtje

#7
@ Frizie,

thanks. ON most eqwrite instructions tere are delays. Most places even longer.


@JonW I use the 18F46k22. would be nice if it can be done internally...

@ trastika,
this is a good and save idea but it seems that when it happens, it happens in the complete EEprom... So using a backup does not give benefits for this problem..

@Everybody,
It looks like when it happens, all (or most) bytes are resetted alays to decimal 99.. Strange...

I suspected that another programm that uses the same pcb does not have the problem.. It also seemd that it has less to do withe the power down time but more with the fact that it is powered-up.

There are running two pic simultaniously the same script, both reading a GPS receiver. One of them is also sending some instructions to the receiver to configure it during start-up. I tought that had something to do with it..While it sometimes happens, it is a bit difficult to test. I am still looking for the exact part of code...
PS, the programming lines of the chips are connected while they need to read the same buttons.. I ca not imagine this is the problem while it also does not influence the other (almost the same, except for the GPS) program.

So I still think it is a kind of hardware defect, but not to do with a batch problem but a structural problem while It seems I can influence it with code but  Ireally do not see a thing wrong in te code ...

I keep on starting, power cutting and reducing the code and trying.... I will keep you posted.

The problem seems to happen, even before any Ewrite instruction is met.....So, while it seems to me that it is good to be carefull with Ewrite, it does not wsem to be the cause of my problem, I think)

PS, all the suggested issues are ofcourse, still valid and good ideas for a stable program, So I need to do them also.



JonW

That part has the HVLVD so you should be able to use Les code above

keytapper

In my experiments I found that some MCUs doe's read the data from the EEPROM during the power up time.
One of this attempt it was to control the triac for a motor, in order to have the startup same as it was powered down.
Another case was also seen for a light dimmer.
Ignorance comes with a cost

tumbleweed

It's likely not a problem with the 46K22 itself, but odd things can happen as devices power up/power down.

You haven't mentioned your config settings, but I would suggest enabling the powerup timer (PWRTEN) and enabling the brownout reset (BOREN) set for the highest voltage (BORV = 2.85V).

Also, adding a delay at the very start of you program (say 100ms or so) before any EEPROM accesses can help with powerup issues.

Amateurtje


Hi everyone,

Thanks, I will come back with more info. I have to run more tests. Last week, I thoguht I isolated it, but after a week, I still have problems.

I think it has something to do with:
- GIE
-WDT
- Sending/Receving on uart.

Is there a way before Activating the GIE to clear the hardware receive buffer??



@ tumbleweed, Thanks for the info but it is already there.

top204

#12
In critical applications it is always better to create user procedures or subroutines that perform a specific task, so you have more control over the mechanism. For example, to write a single 8-bit value to on-board EEPROM on a PIC18Fx6K22 device that contains up to 1024 bytes of EEPROM (PIC18F26K22 and PIC18F46K22), the procedure below can be used:

'-------------------------------------------------------------------------------------------
' Write an 8-bit value to on-board EEPROM
' Input     : pOffset holds the address offset within EEPROM memory to write
'           : pValue holds the 8-bit value to write to EEPROM
' Output    : None
' Notes     : Disables then re-enables global interrupts if they were enabled.
'           : The procedure's SFRs are for PIC18Fx6K22 devices with up to 1024 bytes of EEPROM.
'
Proc EEPROM_Write8(pOffset As Word, pValue As Byte)
    Dim wEEADR       As EEADR.Word                      ' Create a 16-bit SFR from EEADR and EEADRH
    Dim bINTCON_Save As Byte                            ' Holds a copy of the INTCON SFR

    bINTCON_Save = INTCON                               ' Make a copy of the INTCON SFR into bINTCON_Save
    INTCONbits_GIE = 0                                  ' Clear the GIE bit to disable global interrupts

    wEEADR = pOffset                                    ' Load the 16-bit offset address within EEPROM memory to write
    EEDATA = pValue                                     ' Load the 8-bit data to write to EEPROM
    EECON1 = %00000100                                  ' \
    EECON2 = $55                                        ' | Setup to write to EEPROM
    EECON2 = $AA                                        ' /
    EECON1bits_WR = 1                                   ' Perform the write to EEPROM
    DelayCs 1                                           ' A delay of 1 clock cycle before waiting for the write to finish, and reset the watchdog timer (if enabled)
    Repeat: Until EECON1bits_WR = 0                     ' Wait for the EEPROM write to complete
    DelayCs 2                                           ' A delay of 2 clocks cycle just to allow some stability
    EECON1bits_WREN = 0                                 ' Disable writes to EEPROM
    INTCON = bINTCON_Save                               ' Place the saved INTCON copy back into the INTCON SFR to re-enable interrupts if they were enabled
    EECON1bits_EEPGD = 1                                ' Setup for reads from flash memory before exiting the procedure (important to do this before exit)
    DelayMs 5                                           ' Give a slight delay before exiting the procedure, to make sure things are stable
EndProc

Notice that it makes a copy of the INTCON SFR before anything happens, then disables global interrupts. Then when the EEPROM write is finished, it places the copy back into the INTCON SFR, so if interrupts were enabled they will be re-enabled before the procedure exits. It also has a watchdog clear before the waiting loop, and a small delay to allow for extra stability before exiting, which is not normally required, but better safe than sorry when issues are being cleared up. :-)

Because it is all in a high level language, it can be altered for any device and extras added if required.

To use the procedure use something like:

EEPROM_Write8(10, 123)    ' Write the value 123 to EEPROM offset address 10

Where the two parameters can be variables or constants.