News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Code going rouge and destroying HEF data

Started by TimB, Jun 04, 2025, 05:29 PM

Previous topic - Next topic

TimB


Hi all

Recently I have had an issue where my code went rouge and jumped into my HEF Ram write routines and wrote garbage and I'm trying to figure out why.

I wrote a prog that basically monitors an ADC input and compares it to the threshold worked out in a calibration routine.
The calibration routine once done uses an proc to write the cal data to the HEF ram (it's the alternative to eeprom).

I had an issue in the past with the calibration going out and put it down to the calibration sequence being started inadvertently on power up. So I beefed up the sequence needed to make it re cal.
Then it happened again so beefed it up more. Then within an hour it had done is again.
So I read the data back from the device to see what the calibrated values and found that rather than the it all being unwritten data $FF3F with just the data I had written. I found the whole section had been altered with garbage.

I have now rewritten the Flash write code so unless a couple of vars are preset it will not run. It now seems to be holding up but I am really worried that something is not right.

Aside from bad code ( I have checked it) I'm wondering if there are any other mechanisms that would make the code jump around randomly.

     
Basics
The device is a pic12f1572
I can post the code if needed but need to comment it more.

Thanks
Tim


JonW

#1
Did you relock the HEF bit after writing Tim (BCF PMCON1, WREN)?  I have used a few devices with the HEF and have not had any issues. The erase functions are a bit weird on some of the devices with pages etc.  I always do a verify read after the write to ensure the memory is written correctly.

trastikata

The only way that I can think of for the program counter to jump to a "random" place, other than HW defect, is if the stack is somehow affected and the program is not returning to the proper place. Can you post the code, if not proprietary?

TimB

Thanks chaps

Here is the current code read the comments to see the new safety changes







    Device 12f1572
    Declare xtal = 32


    Declare ADin_Tad = cFRC ' RC OSC chosen for the ADC
    Declare ADin_Delay = 40 ' Allow 40us sample time
'
' Global variables, constants and aliases can go here...
'

    Dim pSensorLedpin as porta.4
    Dim cOptoSensorPinNo as 0
    DIm pLineOut as Porta.2
    Dim pInfoLed as Porta.5
    Dim pStateChanged as PortA.3

    DIm cOptoSensorPin as 1                                                     ' ADC no for the sensor input pin

    Dim cTrue as 1
    Dim cFalse as 0

    Dim cCurrentThrsholdAddrs as 0x7E0
    Dim cCurrentDACThrsHoldAddrs as 0x7E1
    Dim cThresholdOffset as 120                                                  ' Should be about 0.39v
    DIm cSensorSetV as 620                                                      ' Should be about 2v

    Dim wOptoSensorRaw as word

    Dim bIndex1 as byte
    Dim wAveragingVar as word
    DIm bTemp1 as byte
    Dim wIndex2 as word
    Dim bPulsecount as byte
    Dim xLastState as bit
    DIm cBubble as 0
    Dim cWater as 1
    DIm cPulledLow as 0
    DIm cPulledHigh as 1



    Dim rPMAddress as Word at PMADRL
    Dim rPMData as word at PMdat
    Dim bSafeToFLash1 as byte
    Dim bSafeToFLash2 as Byte


    Dim wCurrentThreshold as word
    Dim wTemp1 as Word



    Dim cStartupLoopCount as 40000



    $Define WaterSignal() Input pLineOut
    $define AirSignal() Low pLineOut

    $define DisableOutSignal() Input pLineOut

    $Define LedWaterSignal() High pInfoLed
    $Define LedAirSignal() Low  pInfoLed

    $define StateChanged()


    ;Notes
    ;1st June 2025
    ;Reduced adc read averaging buffer from 8 to 4

'---------------------------------------------------------------------------------------------------
' The main program starts here
'
Main:
    Setup()                                         ' Setup the program and any peripherals

    CheckForReCal()



    Do

        wOptoSensorraw = GetSensorVal()


        If wOptoSensorraw > wCurrentThreshold then
            WaterSignal()
            LedWaterSignal()


        Else
            AirSignal()
            LedAirSignal()

        Endif

    Loop


'---------------------------------------------------------------------------------------------------
' Read Sensor
' Input     : None
' Output    : Word value for the sensor using an ADC
' Notes     : None
'
Proc GetAverageSensorVal(), word

    DIm bIndex as byte

    wAveragingVar = 0

    For Bindex = 0 to 3

        wAveragingVar = wAveragingVar + GetSensorVal()

    Next

    Result  = wAveragingVar / 4


EndProc


'---------------------------------------------------------------------------------------------------
' Read ADC for the Sensor
' Input     : None
' Output    : Word average value for the sensor using an ADC
' Notes     : None
'
Proc GetSensorVal(), word

    Delayus 5

    Result = ADCIN cOptoSensorPin

EndProc

'---------------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
    Proc Setup()

         Osc_Int32MHz()                                                         ' Setup the device to use the internal oscillator at 32MHz

        LATA = 0x00                                                             ' Clear Data Latch
        trisA = %00000111


        ' COnfig the ADC

        ANSELa = 0x02                                                           ' Enable PortA.1 as Analoge rest as digital drivers
        ADCON1.7 = 1 ' right shifted result

        DisableOutSignal()


        Clear

        DACCON0 = $A0                                                           ' Enable the DAC, DAC Out and DAC Positive Source = VDD

        DACCON1 = 0                                                             ' Set the DAC to 0V


        wCurrentThreshold = GetCurrentThreshold()                               ' Read the threshold from HEF data


        btemp1 = GetCurrentDac()                                                ' Read the DAC value from HEF data

        if Btemp1 > 28 then                                                     ' Keep it in working bounds to prevent to high a Current if HEF data Duff

            btemp1 = 28
        Endif

        DACCON1 = btemp1                                                        ' Load the DAC to set the right current to the LED of the Slot Opto


    EndProc


'---------------------------------------------------------------------------------------------------
' Called on power start up to see if a recalibration is needed
' Input     : None
' Output    : None
' Notes     : None

    Proc CheckForReCal()
        DisableOutSignal()                                                      ' Make sure outSignal is disabled
        delayms 500                                                             ' Wait 0.5 second

        if  pLineOut = cPulledLow then                                          ' If the line is low then do following code

            xLastState = cPulledLow                                             ' Record the state

            wIndex2 = 0
            bPulseCount = 0

            Repeat                                                              ' Make a loop and count the number of Low  to high changes

                if xLastState = cPulledLow then
                    if pLineOut = cPulledhigh Then
                        Inc bPulseCount
                    Endif
                Endif

                xLastState = pLineOut

                Inc wIndex2

                delayus 50


           Until wIndex2 > cStartupLoopCount                                    ' Once we have sampled for our set period


            If bPulseCount = 2 then
                                                                                ' If we saw the right no of Low - high pulses  then....

                LedAirSignal()                                                  ' Make the Led light


                bIndex1 = 0                                                     ' Make a loop

                repeat                                                          ' To run through the

                    DACCON1 = bIndex1                                           ' Dac levels to alter the LED brightness
                    delayms 500                                                 ' A delay to get the voltage to settle    !!500ms probably over kill

                    wtemp1 = GetAverageSensorVal()
                    if wTemp1 >= cSensorSetV then                               ' Until we see the right voltage on the output
                        break

                    endif

                    Inc bIndex1


                Until bindex1 >= 32                                             ' It should never reach here !!!! perhaps if there is no water?

                                                                                ' While commenting this the line above may be a problem


                wtemp1 = wtemp1 - cThresholdOffset                              ' Now take the ADC level for this brightness and minus the trigure offset

                bSafeToFLash1 = $55                                             ' Code added so HEF write works
                bSafeToFLash2 = $AA

                WriteThresholds(wtemp1,bIndex1)                                 ' And write this data to memory


                wCurrentThreshold = GetCurrentThreshold()                       ' read the threshold level back


                DACCON1 = GetCurrentDac()                                       ' And the DAC data and set the Dac to that level


                ' Now flash the led to say calibration finished

                bIndex1 = 11

                repeat

                    Toggle pInfoLed
                    delayms 200
                    dec bIndex1

                until bIndex1 = 0



                LedWaterSignal()





                Endif







        Endif


    EndProc



'---------------------------------------------------------------------------------------------------
' Proc to unlock the HE-Flash
' Input     : None
' Output    : None
' Notes     : None

    Proc Unlock_Flash()


        if bSafeToFLash1 = $55 then
            If bSafeToFLash2 = $AA then
                PMCON2 = 0x55
                PMCON2 = 0xAA
                PMCON1bits_WR = 1
                NOP
                NOP
            Endif
        Endif

    ENdProc


'---------------------------------------------------------------------------------------------------
' Proc to Erase a line in HE-Flash
' Input     : None
' Output    : None
' Notes     : None

    Proc EraseFlashRow(pAddress as word)

        if pAddress <> cCurrentThrsholdAddrs then                               ' New code to prevent an inadvertant address being used
            ExitProc
        Endif


        if bSafeToFLash1 = $55 then                                             ' New code to prevent inadvertant writes
            If bSafeToFLash2 = $AA then
                PMCON1bits_CFGS = 0
                PMCON1bits_FREE = 1
                PMCON1bits_WREN = 1
                rPMADDRESS = pAddress
                Unlock_Flash()
                PMCON1bits_WREN = 0
            Endif
        Endif

    EndProc


'---------------------------------------------------------------------------------------------------
' Proc to Write data to HE-Flash
' Input     : None
' Output    : None
' Notes     : None

    Proc WriteThresholds(pThesholdVal as word, pDACLevel as byte)

        if bSafeToFLash1 = $55 then                                             ' Safety code
            If bSafeToFLash2 = $AA then


                EraseFlashRow(cCurrentThrsholdAddrs)                            ' Erase the row the threshold is on

        ' Write the ADC thrshold level

                PMCON1bits_CFGS = 0
                PMCON1bits_FREE = 0                                                     '
                PMCON1bits_WREN = 1

                PMCON1bits_LWLO = 1

                rPMAddress = cCurrentThrsholdAddrs
                rPMData = pThesholdVal

                Unlock_Flash()

                rPMAddress = cCurrentDACThrsHoldAddrs
                rPMData = pDACLevel

                Unlock_Flash()

                PMCON1bits_LWLO = 0
                Unlock_Flash()
                PMCON1Bits_WREN = 0

            Endif

        Endif

        bSafeToFLash1 = 0                                                       ' Clear the safety vars
        bSafeToFLash2 = 0

    EndProc


'---------------------------------------------------------------------------------------------------
' Proc to read the threshold value from HE-Flash
' Input     : None
' Output    : None
' Notes     : None

    Proc GetCurrentThreshold(), word

        PMCON1Bits_CFGS = 0                                                     'set program memory space
        rPMAddress = cCurrentThrsholdAddrs
        PMCON1Bits_RD = 1                                                       'set read
        NOP
        NOP
        Result = rPMData


    endProc

'---------------------------------------------------------------------------------------------------
' Proc to read the required DAC value from HE-Flash
' Input     : None
' Output    : None
' Notes     : None

    Proc GetCurrentDac(), Byte
        PMCON1Bits_CFGS = 0                                                     'set program memory space
        rPMAddress = cCurrentDACThrsHoldAddrs
        PMCON1Bits_RD = 1                                                       'set read
        NOP
        NOP
        Result = rPMData
    EndProc

'---------------------------------------------------------------------------------------------------
' Setup the device to use the internal oscillator at 32MHz
' Input     : None
' Output    : None
' Notes     : May not need to be called when using the 'RSTOSC_HFINTOSC_32MHZ' config1 fuse
'
Proc Osc_Int32MHz()
    OSCCON = %11110000                                                          ' 8 Mhz oscillator with PLL to give 32Mhz.
EndProc



'-------------------------------------------------------------------------------
'**** Added by Fuse Configurator ****
' Use the Fuses Tab to change these settings


Config1 FOSC_INTOSC, WDTE_OFF, PWRTE_ON, MCLRE_ON, CP_OFF, BOREN_ON, CLKOUTEN_OFF
Config2 WRT_OFF, PLLEN_ON, STVREN_ON, BORV_LO, LPBOREN_OFF, LVP_OFF

'**** End of Fuse Configurator Settings ****
'-------------------------------------------------------------------------------

trastikata

Hi Tim,

can you try in your code, without the new safeguards, adding a delay of 3ms on the next line right after the HEF erasure is actually started and another 3ms delay on the next line right after the actual HEF write is started. I think this might be the reason for data corruption.

TimB


Thanks

I will add it to the code as it will do no harm. However the issue I have is that the write routine is being called when it should not with bad data and the address to be written. In real life the calibration/ HEflash routine should only be called once.

Tim

trastikata

#6
Quote from: TimB on Jun 04, 2025, 08:21 PMIn real life the calibration/ HEflash routine should only be called once.

I see it is called at start-up. Is it possible to monitor whether the device is rebooting for some reason during normal operation?

In your code I see you are using PORTA.3

QuoteDim pStateChanged As PORTA.3

which is also the MCLR pin, however the fuse settings does not disable the MCLR function:

QuoteConfig1 FOSC_INTOSC, WDTE_OFF, PWRTE_ON, MCLRE_ON, CP_OFF, BOREN_ON, CLKOUTEN_OFF

So is it possible this pin input causes your devices to restart and the apparent call of the HEF routines which are supposed to happen only at boot-up time?

TimB


Thanks trastikata

Dim pStateChanged As PORTA.3 is not actually used.

Yes to making a system to indicating how many times its been asked to calibrate.

Its gone again tonight, reading the data seems ok but the cal data is not the same as expected but not sure it's wrong though

I will add code to count the number of times it's been calibrated and flash it on start up. And once calibrated make a copy of the cal data by reading it back in after.

This is getting on my nerves!

Thanks again




trastikata

Hello Tim,

in any case you should disable the MCLR by declaring MCLRE OFF in the configuration settings or pull-up externally the MCLR pin.

Leaving the MCLRE ON and the MCLR pin floating is not advisable.

TimB

Hi Trastikata

the MCLR pin is pulled up on the board

I changed the code to flash the number of times it's been calibrated. I will check periodically on it.

Tim

JonW

#10
Have you got sufficient decoupling on the device?
If it is being corrupted through a glitch, you could try using the watchdog and increasing the brownout to ensure a reliable halt/brownout scenario, as mentioned above on the MCLR.

I did see that Trastikata mentioned delays and these are vitally important, however, in my experience, a factor that may change over time, temperature, or voltage is the delay for NVM access.  I didn't see any delays or monitoring of the access; I just saw consecutive writes.  I usually monitor the PMCON1,WR bit to ensure that writes and erases are always completed according to the specific device's operational conditions and wait for the bit to be cleared.

In your case, you can add this to the unlock sequence; it may be that you are writing sequentially too quickly, which can occasionally corrupt your data as conditions change. Adding the following WHILE PMCON1bits_WR = 1:WEND may fix the issue for you.

Proc Unlock_Flash()
    if bSafeToFLash1 = $55 then
        If bSafeToFLash2 = $AA then
            PMCON2 = 0x55
            PMCON2 = 0xAA
            PMCON1bits_WR = 1           ' Start the flash operation
            NOP
            NOP
           
            ' Wait for operation to complete
            WHILE PMCON1bits_WR = 1:WEND
           
        Endif
    Endif
ENdProc





JonW

You can test this by running your sequential write sequence and verifying afterwards. Use a rolling variable to write, then confirm. If corruption due to delays occurs, stop and illuminate the LED. This will stress the routine.  Then, repeat with the above monitoring of the WR bit.