Loading...
Pages: 1 [2]   Go Down
Author Topic: Arduino pointer-to-function broken? Compiler Issue?  (Read 856 times)
0 Members and 1 Guest are viewing this topic.
Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Silicon puppeteer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am not quite sure on how to use PROGMEM in something like
Code:
Serial << "message" << endl;

Moving
Code:
Serial << "mess"
to
Code:
Serial.println("mess")
doesn't seem to affect available RAM.
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 137
Posts: 19003
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I am not quite sure on how to use PROGMEM in something like
Code:
Serial << "message" << endl;
Me neither.
That's one of the reasons I avoid streaming, and prefer the serial print methods.

Quote
Moving
Code:
Serial << "mess" to
Code:
Serial.println("mess") doesn't seem to affect available RAM.
I can't imagine why you thought it could.
Logged

Pete, it's a fool looks for logic in the chambers of the human heart.

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Silicon puppeteer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
[quote]I am not quite sure on how to use PROGMEM in something like
Code:
Serial << "message" << endl;
[/quote]
Me neither.
That's one of the reasons I avoid streaming, and prefer the serial print methods.

Well, I guess I'll have to give up streaming for memory in most cases if I can't find a way (and I'll let you guys know if I do)

Quote
Quote
Moving
Code:
Serial << "mess" to
Code:
Serial.println("mess") doesn't seem to affect available RAM.
I can't imagine why you thought it could.

Me neither, I was thinking about your comment of literals that should "finding their way to flash", I guess you meant "find their way with a bit of a push" smiley

Thanks for all the help!
Logged

Offline Offline
God Member
*****
Karma: 14
Posts: 999
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not sure if you founds this already, but here's the information about program memory: http://arduino.cc/playground/Main/PROGMEM
Logged

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Silicon puppeteer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yeah, I saw that some time ago, it doesn't seem super user-friendly though. I wonder why the compiler doesn't do it automatically (controlled with a compiler option or a #define or a #pragma or something)
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 137
Posts: 19003
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

do what automatically?
Logged

Pete, it's a fool looks for logic in the chambers of the human heart.

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Silicon puppeteer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am thinking in the lines of function inlining or C++ this* preprocessing, where the compiler can get a hint (that being PROGMEM) and try to place the variable in flash and wrap accesses to that memory location with the right copying-to-RAM. Even post-link code patching might do the job.
Not that I am saying it should be done, there might be a million reasons why it shouldn't. But it would be convenient.
Is there any reason from-the-top-of-your-head to not want the feature?
Logged

Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 137
Posts: 19003
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

No reason to think that it is not desireable, though how the implementation would be made transparent (and lightweight) enough not to further confuse noobs who have never encountered Harvard architecture or embedded apps, I reallly can't imagine.
Logged

Pete, it's a fool looks for logic in the chambers of the human heart.

Central MN, USA
Offline Offline
Faraday Member
**
Karma: 35
Posts: 5915
Phi_prompt, phi_interfaces, phi-2 shields, phi-panels
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I modified this from outputting to LCD into serial. You can then do:

Code:
PROGMEM prog_char phi_menu_style_00[]="Classic menu";

void msg_serial(char* msg_line)
{
  char msg_buffer[17];
  strcpy_P(msg_buffer,msg_line);
  Serial.print(msg_buffer);
}

Here is an example:

Code:
msg_serialphi_menu_style_00);(

Should be clear enough what the code is doing, right? If you have enough of these constant strings, you will save enough SRAM. Notice the string is copied to msg_buffer so you need large enough buffer to hold the string. You can also make a version where you read one byte at a time but I don't know if strcpy_P has optimized for copying FLASH page at a time.
Logged


Global Moderator
UK
Offline Offline
Brattain Member
*****
Karma: 137
Posts: 19003
I don't think you connected the grounds, Dave.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

You don't need any more than a single byte buffer.
Logged

Pete, it's a fool looks for logic in the chambers of the human heart.

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Silicon puppeteer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is there any way to detect the variable was declared with PROGMEM?
Something in the lines of parameter matching, so it would be possible to do something like:

Code:
    void msg_serial(char* msg_line)
    void msg_serial(PROGMEM char* msg_line)

Of course it would be possible to just name the variables, but I would rather have the compiler doing the job for me (and not only out of lazyness:)
Logged

United Kingdom
Offline Offline
Faraday Member
**
Karma: 130
Posts: 4643
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Unfortunately, AFAIK there is no way of detecting whether a string was declared with PROGMEM. I guess you could declare a class to encapulate such strings, but instances of that class would themselves occupy RAM.

I use the following to print strings in flash memory:

1. Include this in the sketch:

Code:
#include <avr/pgmspace.h>

void print_P(Print& device, const PROGMEM char* s)
{
  for (size_t i = 0; i < strlen_P(s); ++i)
  {
    device.print(pgm_read_byte_near(s + i));
  }
}

void println_P(Print& device, const PROGMEM char* s)
{
  print_P(device, s);
  device.println();
}

2. Instead of using "Serial.print("some string")", use "print_P(Serial, PSTR("some string"))". Similarly for Serial.println, lcd.print etc.

It's a real shame that the Print class doesn't already includes methods for printing strings held in flash memory.
« Last Edit: September 14, 2011, 03:45:01 pm by dc42 » Logged

Formal verification of safety-critical software, software development, and electronic design and prototyping. http://www.eschertech.com

0
Offline Offline
Full Member
***
Karma: 1
Posts: 222
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Have you tried Mikal Hart's Flash library?

This lets you stream program strings as:
Code:
Serial << F("A very long string") << endl;
or
Code:
Serial << aFlashString << endl;
or (with modification) the regular print syntax.
Logged

Offline Offline
God Member
*****
Karma: 3
Posts: 812
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I am thinking in the lines of function inlining or C++ this* preprocessing, where the compiler can get a hint (that being PROGMEM) and try to place the variable in flash and wrap accesses to that memory location with the right copying-to-RAM.

Sadly, the language specification doesn't allow you to do that. The "char *" that you pass to another function may be passed to some function that stores the string, and uses it some arbitrary time later. Thus, the "copy" in SRAM needs to be permanent. Thus, the compiler might as well put it there in the first place.
Regarding needing only a single byte when calling Serial.print(), that is true -- but many other standard functions that use strings need more of the space.
If the declaration of PROGMEM was detectable at compile time the way "const" or "volatile" is, then you could write overloaded functions for the cases where you're OK with a progmem variable. That would be cool. Unfortunately, the GCC compiler was not blessed with the ability to declare arbitrary custom "cv-quals" :-(

You can wrap these things into a class, and use that class as a char*, and have that class read the data into a scratchpad space when used automatically. Unfortunately, you then get into trouble when the program uses more than one (or two, or however much your max scratch space is).

The class would look something like:
Code:
class ProgMemString {
public:
  ProgMemString(prog_char const *str) : str_(str) {}
  prog_char const *str_;
  operator char const *() {
    strncpy_P(scratch, str_, 20);
    scratch[20] = 0;
    return scratch;
  }
  static char scratch[21];
};

You'd use it something like:
Code:
PROGMEM proc_char pm_myMsg[] = "Hello, World!";
ProgMemString myMsg(pm_myMsg);

void setup() {
  ...
  Serial.print(myMsg);
}

Unfortunately, when more than one is used at the same time, they will fight for the scratch space. Each individual string can be at most 20 characters (in this case), too. And each ProgMemString in turn is 2 additional bytes or SRAM (sizeof(char *)) -- as long as you only need one at a time, that's probably a good trade-off, but long term, it's probably no simpler than the standard library form:

Code:
PROGMEM proc_char pm_myMsg[] = "Hello, World!";
char scratch[21];

void setup() {
  ...
  Serial.println(strcpy_P(scratch, pm_myMsg));
}
Logged

Stockholm
Offline Offline
Newbie
*
Karma: 0
Posts: 21
Silicon puppeteer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Mikal Hart's Flash library does a fantastic job, and has a pretty simple implementation, there are a couple of things to keep in mind though.

Flash.h  has to be included after Streaming.h , otherwise the defines collide and, for instance, endl becomes a missing symbol.
Flash.h doesn't have include guards, if you need to include it from different files it'll complain.
Logged

Pages: 1 [2]   Go Up
Print
 
Jump to: