Commit 009c810b authored by deroo's avatar deroo
Browse files

updates for FLASH_TIGHT option

parent 51ae4eb8
......@@ -7,8 +7,9 @@ The Nano 33 BLE uses a nRF52840 microcontroller chip.
The Flash on the nRF52840 has 256 pages of 1024 words, each word is 4 bytes.
Each page is individually erasable; each word is individually writable.
This library defines 2 functions:
This library defines 3 functions:
int flashErasePage(int page);
int flashEraseAll(void);
void flashMode(_flashModeEnum mode);
where mode is one of
FLASH_READONLY
......@@ -21,10 +22,11 @@ flashBytesPerWord
flashNumberOfPages
with the values of 4096, 4 and 256, respectively.
Finally, it declares 3 macros:
Finally, it declares 4 macros:
NANO33BLE_DECLARE( \<datatype\>, \<arrayName\> )
NANO33BLE_PUT_ARRAY_IN_FLASH( \<datatype\>, \<arrayName\>, \<numberOfArrayElements\> )
NANO33BLE_FLASH_LOWEST_PAGE
NANO33BLE_FLASH_ERASE(\<arrayName\>)
which are described below.
This code is most useful for implementing non-volatile arrays,
......@@ -49,7 +51,7 @@ value = arrayInFlash[index];
or
Serial.print(arrayInFlash[index]);
Declare arrays in flash is a two-step process.
Declaring arrays in flash is a two-step process.
The first step is to declare the array data type, and specify its scope.
This is done with a statement with a format of
NANO33BLE_DECLARE(\<datatype\>,\<arrayName\>);
......@@ -57,7 +59,7 @@ such as
NANO33BLE_DECLARE(double,arrayInFlash);
to declare an array of doubles named "arrayInFlash".
The scope of the array is determined by where the declaration is made.
If outside of any function, the scope is global (all functions can see the array), while if it is inside a function, it will be seen only within that function , and others to which it is passed in function argument lists.
If outside of every function, the scope is global (all functions can see the array), while if it is inside a function, it will be seen only within that function, and others to which it is passed in function argument lists.
The second step is to specify where and how big the array is.
This is done using a macro:
......@@ -73,7 +75,7 @@ to make a same-sized array of doubles in RAM.
The array macro NANO33BLE_PUT_ARRAY_IN_FLASH must be inside a function, because it makes calculations as to where the array will fit in flash. It should be called only once for each array, and prior to using that array. It makes the most sense to put it into the function setup(). The declaration must preceed the macro call, but can be in the same function (for a local array) or before all the functions (for a global array).
The array macro NANO33BLE_PUT_ARRAY_IN_FLASH does not initialize or otherwise modify the contents of the array. It just makes the sketch look in the correct place in flash memory when it encounters future references to the array.
The array macro NANO33BLE_PUT_ARRAY_IN_FLASH does not initialize or otherwise modify the contents of the array. It just enables the sketch look in the correct place in flash memory when it encounters future references to the array.
The macro sequentially packs the arrays in from the top of the available flash memory, reaching down as far as it needs to go.
It does not check for any collisions with the sketch or bootloader, which start at the bottom of flash, but it'd have to be a pretty big array and/or sketch for a collision to occur. Simply locating an array over the sketch or in the bootloader is not damaging, fortunately. The flashErasePage() function does check if the page number is too low, to prevent bricking of your Arduino.
......@@ -87,7 +89,23 @@ for (page=NANO33BLE_FLASH_LOWEST_PAGE;page<flashNumberOfPages;page++){
&nbsp; flashMode(FLASH_READONLY);
}
You do not necessarily want to always erase the flash memory on start up,
as doing so defeats the non-volatility property of flash.
as doing so defeats the non-volatile property of flash.
The function flashEraseAll() executes the code snippet above.
Normally, pages in flash do not hold more than one array.
This facilitates the erasure of each array separately, by calling the macro
NANO33BLE_FLASH_ERASE(\<arrayName\>)
(bug/feature note: the macro has the same scope as the array.
ie. if the arrays are declared in setup(),
the sketch cannot call NANO33BLE_FLASH_ERASE()
from anywhere outside of setup(), too).
On the other hand, if space is really precious and you want to pack as much into flash as possible, do
#define FLASH_TIGHT
prior to the #include for this library.
Since with FLASH_TIGHT mulitple arrays might share parts of a flash page,
this disables the ability to erase individual arrays,
'tho erasing all of it with flashEraseAll() is still available.
Valid datatypes for arrays in flash using this library include structs and typedefs, in addition to the predefined C datatypes. Structs appear to default to multiples of flash words, so the programmer's attention to alignment issues does not appear to be neccessary.
......@@ -122,4 +140,5 @@ watch the colored text as it uploads (hit the Arduino reset button to abort).
Or, compile and take the number of bytes
in "Sketch uses NNNN bytes (X%) of program storage space..."
and divide by 4096, rounding up.
I have not yet figured out how to determine from within a sketch what is the last page in flash that the sketch occupies.
......@@ -2,6 +2,7 @@
* A sketch to demonstrate an array of double and of int in both RAM and flash.
*/
#define FLASH_TIGHT
#include <Nano33BLEflash.h>
#define SZ_ARRAY 5
......@@ -31,6 +32,23 @@ void setup() {
Serial.print("address of intArrayInFlash in setup() = 0x");
Serial.print((int)intArrayInFlash,HEX);
Serial.println();
Serial.print("The arrays occupy flash memory from page 255 down to ");
Serial.print(NANO33BLE_FLASH_LOWEST_PAGE);
Serial.println();
#ifndef FLASH_TIGHT
Serial.print("doubleArrayInFlash occupies pages ");
Serial.print(Nano33BLEflashFirstPageOf_doubleArrayInFlash);
Serial.print(" to ");
Serial.print(Nano33BLEflashLastPageOf_doubleArrayInFlash);
Serial.println();
Serial.print("intArrayInFlash occupies pages ");
Serial.print(Nano33BLEflashFirstPageOf_intArrayInFlash);
Serial.print(" to ");
Serial.print(Nano33BLEflashLastPageOf_intArrayInFlash);
Serial.println();
#endif
splash_help();
}
......@@ -130,6 +148,16 @@ void eraseArrays(){
flashErasePage(page);
}
flashMode(FLASH_READONLY);
// alternative way to erase all of flash:
flashEraseAll();
// yet another alternative, erasing the arrays separately.
// can be done this way only if FLASH_TIGHT isn't defined.
// can be done here only if the arrays are global.
#ifndef FLASH_TIGHT
NANO33BLE_FLASH_ERASE(doubleArrayInFlash);
NANO33BLE_FLASH_ERASE( intArrayInFlash);
#endif
Serial.println("Flash arrays erased");
}
......
......@@ -7,7 +7,11 @@ _flashModeEnum KEYWORD1
Nano33BLEflash KEYWORD2
flashMode KEYWORD2
flashErasePage KEYWORD2
flashEraseAll KEYWORD2
_wait4flash2Bready KEYWORD2
NANO33BLE_DECLARE KEYWORD2
NANO33BLE_PUT_ARRAY_IN_FLASH KEYWORD2
NANO33BLE_FLASH_ERASE KEYWORD2
# constants
FLASH_READONLY LITERAL1
......@@ -16,3 +20,4 @@ FLASH_ERASE LITERAL1
flashBytesPerPage LITERAL1
flashBytesPerWord LITERAL1
flashNumberOfPages LITERAL1
NANO33BLE_FLASH_LOWEST_PAGE LITERAL1
name=Nano33BLE-flash
version=0.6
version=0.7
author=Roger De Roo
maintainer=Roger De Roo
sentence=Direct flash memory access
......
......@@ -80,4 +80,15 @@ int flashErasePage(int page)
return page;
}
int flashEraseAll(void)
{
int page;
flashMode(FLASH_ERASE);
for (page=NANO33BLE_FLASH_LOWEST_PAGE;page<flashNumberOfPages;page++) {
flashErasePage(page);
}
flashMode(FLASH_READONLY);
return 0;
}
#endif
......@@ -12,12 +12,6 @@
// the lowest flash address
#define _NANO33BLE_FLASH_BASE (0x0)
/*
// set static unsigned int _flashAddr to one byte after the top of flash
#define NANO33BLE_FLASH_INIT() \
_flashAddr = (_NANO33BLE_FLASH_BASE + NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE);
*/
// private macro: move _flashAddr down by the size of the array being declared
// then make it start on a flash boundary
// boundary is a flash word if FLASH_TIGHT is defined; a flash page otherwise
......@@ -32,6 +26,7 @@ _flashAddr = (_NANO33BLE_FLASH_BASE + NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZ
_flashAddr &= ~(NRF_FICR->CODEPAGESIZE-1);
#endif
/*
// public macro: declare array in flash at highest flash addresses available
// first push _arrayAddr down by size of array in bytes,
// then use array - pointer equivalence to declare a pointer to that address
......@@ -39,23 +34,58 @@ _flashAddr = (_NANO33BLE_FLASH_BASE + NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZ
#define NANO33BLE_FLASH( _datatype, _arrayName, _numberOfArrayElements ) \
_NANO33BLE_FLASH_DECR_ADDR((sizeof(_datatype)) * (_numberOfArrayElements)) \
_datatype *_arrayName = (_datatype *)_flashAddr;
*/
// sets the type and scope of the array in Flash, depending on where done.
// set to a default address just beyond flash in case PUT_ARRAY_IN_FLASH isn't called
// public macro: declare an array to be in flash
// sets the type and scope of the array in flash, depending on where done.
// default address just beyond flash in case PUT_ARRAY_IN_FLASH isn't called
// and the sketch tries to write to it
// (otherwise the default would be zero, where the bootloader is)
#ifdef FLASH_TIGHT
#define NANO33BLE_DECLARE( _datatype, _arrayName) \
_datatype *_arrayName=(_datatype *)(_NANO33BLE_FLASH_BASE + NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE);
#else
#define NANO33BLE_DECLARE( _datatype, _arrayName) \
_datatype *_arrayName=(_datatype *)(_NANO33BLE_FLASH_BASE + NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE);\
unsigned int Nano33BLEflashFirstPageOf_##_arrayName=NRF_FICR->CODESIZE;\
unsigned int Nano33BLEflashLastPageOf_##_arrayName=NRF_FICR->CODESIZE;
#endif
// public macro: locate the array in unused flash memory
// move existing pointer _arrayName to next available spot in Flash
// macro includdes executable code, so call this in setup()
// if not doing tight packing, also make constants for the first and last pages
#ifdef FLASH_TIGHT
#define NANO33BLE_PUT_ARRAY_IN_FLASH( _datatype, _arrayName, _numberOfArrayElements ) \
_NANO33BLE_FLASH_DECR_ADDR((sizeof(_datatype)) * (_numberOfArrayElements)) \
_arrayName = (_datatype *)_flashAddr;
#else
#define NANO33BLE_PUT_ARRAY_IN_FLASH( _datatype, _arrayName, _numberOfArrayElements ) \
Nano33BLEflashLastPageOf_##_arrayName=((_flashAddr-1-_NANO33BLE_FLASH_BASE)/NRF_FICR->CODEPAGESIZE);\
_NANO33BLE_FLASH_DECR_ADDR((sizeof(_datatype)) * (_numberOfArrayElements)) \
_arrayName = (_datatype *)_flashAddr;\
Nano33BLEflashFirstPageOf_##_arrayName=((_flashAddr-_NANO33BLE_FLASH_BASE)/NRF_FICR->CODEPAGESIZE);
#endif
// public macro to be used after all the flash array movement
// useful for looping over flash pages to initialize/erase them.
#define NANO33BLE_FLASH_LOWEST_PAGE \
((_flashAddr - _NANO33BLE_FLASH_BASE) / NRF_FICR->CODEPAGESIZE)
/*
//for(int _page##_arrayName=Nano33BLEflashFirstPageOf_##_arrayName; _page##_arrayName<=Nano33BLEflashLastPageOf_##_arrayName;_page##_arrayName++) {\
*/
// public macro to erase a named array in flash, provided it's not packed tight
#ifndef FLASH_TIGHT
#define NANO33BLE_FLASH_ERASE( _arrayName) \
flashMode(FLASH_ERASE);\
for(unsigned int _page=Nano33BLEflashFirstPageOf_##_arrayName; _page<=Nano33BLEflashLastPageOf_##_arrayName;_page++) {\
flashErasePage(_page);\
}\
flashMode(FLASH_READONLY);
#endif
/*
// test. result: don't do sizeof(#_datatype)
#define NANO33BLE_FLASH_SIZEOF(_datatype) \
......@@ -68,8 +98,6 @@ _arrayName = (_datatype *)_flashAddr;
TYPE *_arrayName = (TYPE *)0x100000;
*/
//#undef _flashAddr
static unsigned int _flashAddr = (_NANO33BLE_FLASH_BASE + NRF_FICR->CODESIZE * NRF_FICR->CODEPAGESIZE);
// Caution: these enums are the register settings for the given mode.
......@@ -85,6 +113,7 @@ const int flashNumberOfPages = NRF_FICR->CODESIZE; // 256 pages
void flashMode(_flashModeEnum mode);
int flashErasePage(int page);
int flashEraseAll(void);
void _wait4flash2Bready(void);
/*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment