Closed migry closed 2 years ago
Hi!
If you don't want to mess around with 32-bit pointers, just use the F() macro.
Support code in main ".ino" for writing a string to the Serial port...
void SerialPrint (const char *s) { Serial.print (s); } void SerialPrintln(const char *s) { Serial.println(s); }
Why would you do this? I see no benefit here except that it won't work with the F() macro. You can use Serial.print/println in the cpp file by including Arduino.h at the very top of the file: #include <Arduino.h>
Here's some example code you can use as a reference, that does work
const char str_in_progmem[] PROGMEM = {"This is a string stored in PROGMEM the old fasion way!\n"};
void setup()
{
delay(1000);
Serial.begin(9600);
Serial.println(F("Example using the F() macro!"));
// Print str_in_progmem array
for(uint8_t i = 0; i < sizeof(str_in_progmem); i++)
{
char c = pgm_read_byte(str_in_progmem + i);
Serial.write(c);
}
}
void loop()
{
Serial.printf(F("Milliseconds since start: %ld ms\n"), millis());
delay(1000);
}
Apologies for not replying sooner. I just saw the issue closed email.
Firstly thank you for your reply and the information which you gave. It was invaluable and was of great help.
The problem was NOT with Megacore, but a result of my lack of knowledge of C++ and the AVR architecture. Apologies for raising an issue which is unrelated to this project.
Your suggestion of including "Arduino.h" and not individual dot h files was the solution to allow me to access the Serial port from the dot cpp module.
Just in case anyone arrives here from google, here is some things I understand better since having raised the issue...
If I understand correctly, fixed strings used in the source code are stored in ROM, but are copied to RAM during the C startup, and take up valuable RAM. It is easy for the unaware programmer (i.e. me) to eat away at the valuable RAM resource, without being aware. Prints of various flavours are the main reason, and most of the time these fixed strings can be "forced" into ROM. In my case the lack of RAM (unnecessarily consumed by my fixed strings) led to the program crashing due to lack of stack and/or heap space (or likely the stack and heap overlapped). So be aware and use a number of different ways to avoid this unnecessary use.
I found this information on this web page invaluable...
https://www.e-tinkers.com/2020/05/do-you-know-arduino-progmem-demystified
The Teensy has a linear address range, and the issue of strings in RAM/ROM gets taken care of without needing to use the PROGMEM keyword. Also it has lots of RAM, which sort of hides the issue of string RAM usage from you.
The PROGMEM keyword is needed on the AVR (e.g. Atmega128) to force strings into ROM, e.g.
const char Serial1[] PROGMEM = "* SERIAL BUFFER OVERFLOW **\n";
Warning...
const char Serial2[] = "* OPERATION COMPLETE **\n"; // gets put in RAM on the AVR but (only) ROM on the Teensy
The issue arises since the AVR has a Harvard architecture which means that there is 64k of addresses of both ROM and RAM. On the Atmega128 I noted in the machine code listing that when an address/pointer was passed there was nothing to distinguish a ROM address from a RAM address, so it was the responsibility of the called routine to know what is was receiving. So I now use "pgm_read_byte(ptr)" when I know "ptr" holds a ROM address.
I could be wrong here, but any function which expects a pointer to char, will either get a RAM address or a ROM address, depending upon what "thing" you pass as the parameter (e.g. Serial1 or Serial2 above). If you pass a string which is in ROM (thanks to PROGMEM) then you need to use pgm_read_byte() to force the compiler to read from ROM (byte by byte), otherwise the read will be asumed to be from RAM. There are some useful functions for string manipulation such as strcpy_P() - please google for information as to how and when to use them.
My less than perfect fix is to have two print functions, one which expects a ROM pointer and the other which expects a RAM pointer. C++ experts may well have a solution to allow you to pass either type of pointer, but I am only "C" grade :-) .
Note, fixed strings can be wrapped in the F() macro, and this works with a lot of standard Arduino library functions such as Serial.print(). This macro was incorrectly used in my own code, which led to the error message. Again please google for more information.
I hope this helps anyone in a similar situation as to me, or anyone else unknowingly eating up precious RAM. Apologies in advance for any mistakes or misunderstandings in my explaination.
I am helping a friend who is porting "arduino" code to an existing board which uses an ATMEGA128 CPU. The "Megacore" seems perfect for this task and compiles without issues, although we need more work to move away from some custom libraries. FWIW: The original code was compiled on a Teensy.
The application did not run. We established that it was a lack of run time RAM (determined by commenting out one of the modules). So I am working to try to optimise RAM usage. In the "serial GUI" modules I have lots of writes of fixed strings to the serial port (via a helper function - see below). I figured out that these strings are not only stored in ROM, but are in RAM and must get initialised from ROM by the C runtime. So these fixed strings consume valuable RAM.
On other Arduino projects I have used the "F()" macro to wrap fixed strings, but this does not compile. Just BTW there is the main ino, but the GUI is in it's own ".cpp" module, and so explicit #defines are needed for library functions. In the ".ino" this is all done for you, so was another discovery.
So the question is, how do I put fixed text strings into ROM and then pass a pointer to the Serial print wrapper function?
In order to print to the serial port, I have the following functions. A pointer to the string is passed to the main ".ino" where Serial.print(s) can be used.
Support code in main ".ino" for writing a string to the Serial port...
Thank you for any help.