[...continued...]
7) DIFFERENCE BETWEEN FUNCTION USING RAM AND FLASH
The function that accepts something of datatype __FlashStringHelper will make use of:
byte b = pgm_read_byte (FLASH_16bitaddress);
within the function to gain access to FLASH data instead of RAM data (the default). This function works for all AVR processors PROVIDING THAT THE FLASH DATA IS IN THE FIRST 64KB. As already noted for the Mega2560 this may not be the case. This 64KB limit arises because __FlashStringHelper is a 16 bit address in effect.
8) FLASH USE ABOVE 64KB PROBLEMATIC
The use of FLASH memory above 64KB is problematic with many libraries. This arises because it is only the MEGA2560 processor that has provision to store data above 64KB. For this reason it is necessary to ensure that both compiled code and PROGMEM use does not cross the 64KB boundary as unexpected operation will occur with many libraries. This failure will not be flagged to the programmer in any way and will result in data in FLASH being accessed at the wrong FLASH address. How your program deals with this will be a can of worms depending on the use that is made of the data (it may be stuff other than text arrays).
9) MEGA2560 FLASH ISSUES
This unexpected operation with the Mega2560 arises because references to memory (both RAM and FLASH) have the compiler using 16 bit pointers by default. To operate with a Mega2560 a 18 bit pointer (address specifier) is needed. This has no practical datatype representation in the AVR so 32 bits get allocated to handle this. This 32 bit pointer to a FLASH address (above 64KB) is typically given the datatype:
uint_farptr_t
in practice this is just a redefine of uint32_t:
#typedef uint32_t uint_farptr_t; (in inttypes.h)
When using data stored in FLASH above 64KB (on the Mega2560) one needs to use the uint_farptr_t datatype in conjunction with the function:
pgm_get_far_address (16 bit address)
This essentially takes a 16 bit address and returns a 32bit value (of type uint_farptr_t). The pgm_get_far_address performs the necessary translation from 16 bit to 18 bit address for Mega2560. When working with the Mega2560 you need to essentially do two things:
a) You need to use pgm_get_far_address (16 bit address) in conjunction with
b) pgm_get_byte_far (32 bit address)
to have things work. So pgm_get_far_address and pgm_get_byte_far really go hand in hand and need to be used together.
10) LIBRARY SUPPORT FOR MEGA2560 ADDRESSES
These 32 bit address (18 bits effective) on the Mega2560 can be used with some of the string related functions like strlcpy (string copy) and strlcat etc. The trick here is to use the _PF variant so:
use strlcpy_PF (dest_string, source_string_in_flash_32_bits_in_size, max_size) instead of strlcpy (…)
use strlcat_PF (…) instead of strlcat.
11) LIBRARY _P VARIANTS
There are also _P variants of these functions (eg. Strlcat_P) and these only work with FLASH addresses located in the first 64KB (16 bit addresses). On a Mega2560 you wont be advised if you go over 64KB so in my view better to use the _PF variant to be safe - with pgm_get_far_address - to provide code that will work no matter where the FLASH data is located.
12) LIBRARY WORK AROUNDS ON MEGA2560
Because of the complexities associated with Mega2560 processors the following code would be typical of using strlcpy_PF where the source data could be located above 64KB:
//Global stuff...
const MaxStringSize = 100;
const char GJB_FlashData[] PROGMEM = “Yep, Im in FLASH and could be above 64KB depending on code size and other PROGMEM use.”;
//Later in in code...
loop () {
//Other stuff...
char tString[MaxStringSize];
strlcpy_PF (tString, pgm_get_far_address (GJB_FlashData), MaxStringSize);
...
}
In other words when using the _PF variants of library functions it is also necessary to use pgm_get_far_address() as well. The resultant tString (in RAM) could then be used with Serial.print () and all other library functions (strlcat, strtok etc). Serial.print and other functions, like many other library functions, silently fail when dealing with addresses above 64KB. The higher order bits (above the first 16) are silently thrown away so you will have fun if you don’t factor this into AVR operation. Using this technique everything is transferred from FLASH (whereever it resides) to RAM where life is easy.
13) F() AND FUNCTIONS WITH SUFFIX _PF
I initially thought that those library functions ending with _PF (eg. strlcpy_PF) were for use with the F() macro. For example:
char tString[MaxSize];
strlcpy_PF (tString, F(“I am in FLASH somewhere…”), MaxSize);
The use of F() has NOTHING to do with the _PF variants. The F in the _PF means FAR – ie. above 64KB. As an aside the F() macro I believe only works if located in the first 64KB for similar reasons already given – if located above 64KB the additional bits of the address are not factored in and FUN returns. For example:
Serial.print (F(“I will be displayed if located in first 64KB.”));
will only work if the chars are located below 64KB and this is because the Serial.print function cannot handle addresses above 16 bits.
14) FINAL COMMENT
There is a lot to learn to make use of the extra FLASH the Mega2560 offers. The traps are numerous and can occur in several places. One can expect a small performance loss when accessing FLASH above 64KB. Using the Mega2560 for the extra RAM it provides (8KB compared to 2KB for most other AVR processors) is easy. Using the extra FLASH is not easy.
I would like to thank earlier contributors to my understanding - particularly J-M-L and david_2018.
Geoffrey