NVRAM

Question about power
basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

NVRAM

Post by basicchip »

>from the help line
>I have a need to store a value for boot detection upon power failure.

>So I'm wondering if you use the NVRAM in the RTC at 0x40024044 up to 0x40024054
>I don't want to step on any critical values, and I'm afraid to write to that area until you tell me it's unused... could crash my board)

>LPC1756, ARMbasic 9.34e, BASICtools 5.37

BASIC does not use any of these registers, so have at it.

As long as the battery is connected they will be saved through a power cycle



basicchip
Posts: 1090
Joined: Fri Oct 19, 2012 2:39 am
Location: Weeki Watchee, FL
Contact:

Re: NVRAM

Post by basicchip »

PS the LPC1756/1 (SuperPRO/PROplus) also have a brown out detect that can be used for power glitches

AMDlloydsp
Posts: 99
Joined: Mon Apr 15, 2013 3:51 pm
Location: NE Central FL

Re: NVRAM

Post by AMDlloydsp »

Yep... and with three stages of falling/rising supply detection, it provides a lot of detail on what's happening in the few tens of milliseconds as power is failing.

In my scenario, I must both detect the falling supply, and read a couple of GPIO inputs to see if the problem was caused by a use hitting one of two 'crash' buttons, or because power just failed. There's enough code in my PF interrupt so that flash was too slow a solution. The NVRAM locations are as fast as regular RAM.... plenty of time to do the saving of states.

Lloyd

AMDlloydsp
Posts: 99
Joined: Mon Apr 15, 2013 3:51 pm
Location: NE Central FL

Re: NVRAM

Post by AMDlloydsp »

As part of this quest to save states when power fails, I needed to conserve memory, since there are only 20 bytes (5 integer's worth) of NVRAM in the RTC backup registers. "BOD" stands for "Brownout Detect". I'll document the interrupt method to employ it on the LPC17xx devices as time permits.

Below is a quick and dirty snippet that will pack the entire date/time (down to seconds) in one integer, along with one bit of 'flag' for whatever you want.
The whole packing thing takes about 9uS on the SuperPro, so it can be incorporated into a systick interrupt routine, so long as the systick rate is slow enough. This came from a machine with systick running at 10ms per tick, so the time to pack is tiny per tick.

#define NVRAM_BODTIME *&H40024044 ' pointer to base address of the RTC NVRAM backup registers.
' only works if you have an RTC battery installed.


dim packed_date as integer
dim [the up_xxxxx things in unpack_date] as integers

sub pack_date ' pack the current date/time into one integer

' date is base-0 from year 2014, giving us 31 years past that to 2045...
' the machine isn't going to run for 31 years. If it does, well...
'OR in your flag to the last bit of the last byte. Apparently, BASIC uses little-endian storage, because this worked the first time.

packed_date=(YEAR(0)-2014) << 27 ' 5-bit year: Base 0 for 2014 shift to left-most 5 bits of the integer
packed_date=packed_date or (MONTH(0) << 23)' four-bit month
packed_date=packed_date or (DAY(0) << 18) ' five bit day
packed_date=packed_date or (HOUR(-1) << 13) ' five bit hour
packed_date=packed_date or (MINUTE(-1) << 7) ' six bit minute
packed_date=packed_date or (SECOND(-1) << 1) ' six bit seconds, leaving one bit empty
packed_date=packed_date or (flag_state AND 1) ' fill that last bit with your flag state

endsub

sub unpack_date

' get the packed date from the RTC backup registers, as well as the flag bit

up_year=((NVRAM_BODTIME and &HF8000000) >> 27)+2014 ' five bits
up_month=((NVRAM_BODTIME and &H07800000) >> 23) ' four bits
up_day=((NVRAM_BODTIME and &H007C0000) >> 18) ' five bits
up_hour=((NVRAM_BODTIME and &H0003E000) >> 13) ' five bits
up_minute=((NVRAM_BODTIME and &H00001F80) >> 7) ' six bits
up_second=((NVRAM_BODTIME and &H0000007E) >> 1) ' six bits
up_crash_bit=(NVRAM_BODTIME and 1) ' gets the flag in an integer, 1 or 0

if NVRAM_BODTIME=0
up_year=0
up_month=0 ' if no bod ever happened, don't give any bogus info; make it ALL zeros
up_crash_bit=0
endif

endsub


Hope this helps someone.
Lloyd

olzeke51
Posts: 414
Joined: Sat May 17, 2014 4:22 pm
Location: South Carolina

Re: NVRAM

Post by olzeke51 »

Thanks for the routine. I've been trying to 'assemble' a way to store time data for my 'event monitor'. It uses an I2C RTC.
now I can get back to work on it
'
I am a little curious -- I do the Basic, this looks like basic and you referenced Basic as being little-endian.
Is Basic fast-enough to capture your time info and store it ??? Or do you have the variable packed_date constantly
being updated and then when a powerfail happens you quickly store just the already processed packed_date integer??
[via an interrupt routine]?
'
I also got wind of some 'debug' memory locations that are available- but that was a special 'flash' section,
GOTTA research that - I don't remember if it was JTAG or part of the CMSIS type spec.
'Course if you use Jtag - then its not available.
'
again - thanks for the code snippet. it is BASIC enough for me !!! - Gary

AMDlloydsp
Posts: 99
Joined: Mon Apr 15, 2013 3:51 pm
Location: NE Central FL

Re: NVRAM

Post by AMDlloydsp »

The BASIC on the LPC1756 isn't as fast as tight C code, but in the scheme of things, I'd calling "Blindingly Fast!!!".

There's plenty enough time to pack that date AND do probably another 50 lines of code after it in the short time between a brown-out interrupt and the final stoppage of the CPU by the low-voltage test circuitry (built-in).

I have challenged it with the intent of seeing if I had enough time, not by killing AC power, but by ripping out the +5V power connection to the board while it was running. That, then, relies only on the charge on the few capacitors on the board for its holdup time. No problems.

As soon as I get home from this install job, I'll document the super-simple interrupt code that allows you to save states on power fail. The manual for the NXP devices is cumbersome and hard to wade through, but the actual code is dead-simple and very short.

FWIW, flash was WAY too slow to do what I wanted, which is why I started hunting for NVRAM in the RTC. It's a natural adjunct of just having a battery-backed-up RTC that there will usually be a few available cells of memory the clock doesn't use. It comes in 'chunks', but the clock seldom uses it all. This is true for most systems, not only the Coridium/LPC17xx parts.

I'm actually a little embarrassed by my "Little Endian" comment. It just seemed obvious when I said it, but in mental review, this would work regardless of the Endian nature of the variable. Duh!

I don't know about how quickly you can read a serial device, though. There's a lot of bit-banging going on in the I2C routines. Testing will tell.

Lloyd

olzeke51
Posts: 414
Joined: Sat May 17, 2014 4:22 pm
Location: South Carolina

Re: NVRAM

Post by olzeke51 »

It will be interesting to see the INT code; since you have 3 bits/flags to record.
BTW - I would have thought you would have done the 'flag' bit first so that it ended up in bit 31, after all the shifting.
that way you could do a 'negative' (number) check on it.
Well, with a 'mask' I guess it doesn't matter -- [ I have too much 8051 running through my mind, lots of bitwise instructions]
'
'endian' would come into play if you were to mix Basic with C, or approach the task with a 'C frame of mind'.
'
the I2C reading process would be slow in my mind also - that kindof flavored my comment about doing a time-stamp loop,
so that you could grab the already processed time and just "add" the flag bit , then store the result into the NVRAM in the INT routine.
[there's that 8051 12 clocks/machine cycle thinking - all at 11.059 Mhz clocking.] - so yes the 1756 is 'blazingly fast'.
'
Keep us posted. Gary

AMDlloydsp
Posts: 99
Joined: Mon Apr 15, 2013 3:51 pm
Location: NE Central FL

Re: NVRAM

Post by AMDlloydsp »

Actually, bit 0 is the position the flag occupies in the hardware from which I read it, so it seemed natural to place it there.

With the bit-shifting nature of that packing algorithm, one isn't limited to where the flag can go -- any bit; although it would require a couple more statements to pack the flag into an arbitrary bit inside one of the day/date values.

I don't think I made myself clear about where the packing is going on. I originally had it in systick, thinking the same as you, that I wouldn't have time to do it in the BOD interrupt. Later, it became clear that I didn't need to clutter and obfuscate purposes in my systick IRQ routine with that, since it is only needed after the BOD interrupt occurs.


Lloyd

AMDlloydsp
Posts: 99
Joined: Mon Apr 15, 2013 3:51 pm
Location: NE Central FL

Re: NVRAM

Post by AMDlloydsp »

Here is the one interrupt routine, two subs for enable and disable of the IRQ, and a tiny pseudo-code
example that sets up to use the pack_date discussed above.

I'm good at forgetting the purpose of various statements I write, so there are MANY more comments
than code... comments are 'free', even in BASIC, if it's compiled, or is line-linked interpreted code.

Remember that the board must have the RTC battery installed in order for this RAM to survive power-failure.

Lloyd
-----------------------------------------------------------------------------------------------------------------------------------------

' defines, dims, and IRQ code for a very simple version of brownout detect on the LPC17xx parts

#define NV_RAM0 *&H40024044 ' Memory Location of the five 32-bit words of backup
#define NV_RAM1 *&H40024048 ' NVRAM in the RTC
#define NV_RAM2 *&H4002404C
#define NV_RAM3 *&H40024050
#define NV_RAM4 *&H40024054

#define NVRAM_BODTIME NV_RAM0 ' save location for the packed date/time
#define NVRAM_BODUSER NV_RAM1 ' save location for the integer user ID
#define NVRAM_BODBITS NV_RAM2 ' save location for GPIO 0 bits


' note GPIO0 bits 9,8,7,6 are inputs connected to my setup switches and the power lock switch
' auxiliary contacts.
'
' detecting and saving those and the state of the panic crash button leaves evidence of just HOW
' the power failed. If the user turns the machine off with the key, or smacks the panic button
' and then resets the button before power-on, we want to record those conditions in the log file.
' If we get a BOD and nothing's been manually switched, then we can assume it's a legitimate
' line power failure, and not a user action.

dim packed_date as integer
dim Current_user as integer ' used elsewhere in the system to record who last logged in)


sub pack_date()
' stub routine describe earlier in this thread
'do.... stuff to pack and save the date and the crash mushroom contact state.
' That last could have gone in the BODBITS cell, but we had the one empty bit left, anyway,
' so why not use it?
'
' Make this a sub or function rather than putting the code in-line
' in the IRQ, so if we change the packing algorithm, we don't have
' to muck around with a working (and thus sacred) IRQ routine.
NVRAM_BODTIME=packed_date ' log the time we got the BOD
endsub

sub record_states
NVRAM_BODTIME OR= (((IN(PANIC) and 1)) ' flag to say we got a mushroom switch hit

NVRAM_BODUSER=Current_user ' preserve the user ID

NVRAM_BODBITS=(IN(6) AND 1) '------------- ' the power-switch aux contacts indicating it was shut off
NVRAM_BODBITS OR= (((IN(7) AND 1) << 1))'\
NVRAM_BODBITS OR= (((IN(8) AND 1) << 2)) ' record some setup switch settings at power-down
NVRAM_BODBITS OR= (((IN(9) AND 1) << 3))'/ in case they're changed while power is off (tampering)
endsub

'=========================================================================================
' the B.rown O.ut D.etect IRQ routine
'
' No reason to clear any registers, because we assume that power is failing, period.
'
' IF one suspected that power might go down to the BOD level, but not fail completely, AND
' then come back up, that situation can be detected and acted upon. There are both falling
' voltage and rising voltage BOD detectors in the LPC17xx parts.
'
' Also, there's a second stage of brownout-detect that occurs at a lower voltage than the
' first stage we're acting upon. That can create another interrupt just before the CPU halts.
'
' Coridium's LPC17xx.bas only defines the BOD_ISR, but not the other BOD registers necessary
' to read/write to determine which condition caused an interrupt. You will NOT get the rising
' voltage interrupt upon power-up, because everything comes up disabled. That can only occur
' if the power 'partially' fails to the first falling voltage interrupt, then rises again before the final
' falling voltage interrupt. There is hysteresis on all the states, so you won't get 'chattering'
' interrupts.
'
' See the NXP LPC17xx manual for details.

' We don't really have to tell what got us here, nor clear the interrupt
' The interrupt comes up cleared and disabled on power-up. Must explicitly
' enable it.

'|---------------------IRQ----------------|
' When the power fails, we don't care about anything but recording
' the event.

INTERRUPT SUB Do_Brownout_ISR ' enter here when power is failing
gosub pack_date() ' note that there is NO interrupt code here, only local tasks
gosub record_states() ' to preserve our application state.
endsub
'|--------------------end IRQ--------------|


' BOD always comes up inactive on Power-on-reset
' so nothing else to do but enable the interrupt.
'
' BOD_ISR defined in LPC17xx.bas of the Coridium include files.
' SOMEWHERE, I read that the INTERRUPT SUB above should fall on an even
' byte boundary (bit 0=0), or else the address stored in BOD_ISR
' can be incorrectly set. For some odd reason, I've never had
' an IRQ routine require to be adjusted by byte-filling, but I believe NXP
' warns to watch out for the condition. Maybe BASIC automatically
' aligns INTERRUPT SUBs on even-byte boundaries (???).

'|-----------enable/disable IRQ subs--------|
sub BOD_Enable
BOD_ISR = ADDRESSOF Do_Brownout_ISR or 1 ' enable the BOD interrupt
VICIntEnable OR= (1<<23) ' interrupt number of the BOD
endsub

sub BOD_Disable
BOD_ISR = ADDRESSOF Do_Brownout_ISR ' DISable the BOD interrupt. This only works if it's an even address,
' if not, you'd have to XOR the address with a 1... but I think BASIC puts the INTERRUPT Subs at even addresses
' automatically, because I've never had to deal with it.

VICIntEnable XOR= (1<<23) ' interrupt number of the BOD
endsub
'|--------------------end subs-------------|


MAIN:
' enter here at power-up
' first check to see if we recorded anything on the last PF
' do stuff.....
' ....... HERE ....
' to read the data from the NVRAM, and if a PF was recorded there,
' to log it to disk before proceeding to clear and re-enable BOD.

' Now clear out the evidence, and re-enable for another BOD.
gosub BOD_Disable ' comes up disabled, but always assume it's not
NVRAM_BODTIME=0 ' initialize the cells for the next BOD
NVRAM_BODUSER=0 ' so as to make them appear "not used"
NVRAM_BODBITS=0
gosub BOD_Enable ' enable brown-out detection.

' go run the rest of the program. A BOD will leave a trail of crumbs, if
' it happens.
Last edited by AMDlloydsp on Mon Nov 10, 2014 12:34 pm, edited 5 times in total.

AMDlloydsp
Posts: 99
Joined: Mon Apr 15, 2013 3:51 pm
Location: NE Central FL

Re: NVRAM

Post by AMDlloydsp »

PS to Gary.

If you have much to do to create your 'saved state', it would be reasonable to do it in a systick-driven or event-driven sub, rather than within the BOD IRQ, but with one limitation:

Because it might be possible to get a BOD interrupt during your construction of the saved value, it would be best to generate it and save it in a temporary store (up to five words, integer or single). Then, before putting it in the 'permanent' cells that the BOD would actually tuck into NVRAM: 1) disable the BOD interrupt, 2) quickly move the temporary store into the BOD-recognized cells, 3) re-enable BOD interrupts.

This will ensure that a BOD interrupt always sees a 'clean' (completed) version of your saved data, and not some intermediate result that got interrupted.

The permanent store could even _be_ the NVRAM locations, but then there would be no need to have a BOD interrupt routine. Its whole purpose in my code is to also save the states of the switches the user could have actuated to actually cause the BOD.

I will admit, though, that I do not know what will happen if you get a brown-out detect while the interrupt is disabled, then enable it. I would hope that the interrupt occurs, but I know of no reasonable way to challenge that. That's all the more reason to keep the time it's disabled to a minimum. That wouldn't completely eliminate the possibility of an interrupt while disabled, but would certainly minimize the likelihood of it.

Lloyd

Post Reply