Writing in program space in c++ mode

Hi guys,
If I try to write in the program space using the c++ compiler with something like this:

#include <pololu/OrangutanLCD.h>
#include <pololu/OrangutanPushbuttons.h>
#include <pololu/OrangutanTime.h>
#include <pololu/OrangutanSerial.h>
#include <pololu/Uico.h>
#include <string.h>

// This include file allows data to be stored in program space.  The
// ATmega168 has 16k of program space compared to 1k of RAM, so large
// pieces of static data should be stored in program space.
#include <avr/pgmspace.h>

// Introductory messages.  The "PROGMEM" identifier causes the data to
// go into program space.
const char welcome_line1[] PROGMEM = " Pololu";
const char welcome_line2[] PROGMEM = "3\xf7 Robot"; // \xf7 is a greek
													// pi character
const char demo_name_line1[] PROGMEM = "Demo";
const char demo_name_line2[] PROGMEM = "Program";

.....

int main()
{
	// set up the 3pi
	initialize();
	Uico controller;
	// This is the "main loop" - it will run forever.
	while(1)
	{
		menu_select();
	}
}

It gives me a warning:

ico.cpp:17: warning: only initialized variables can be placed into program memory area

I’m kind of scared in trying to program it with this error. Is it normal?

EDIT:
On this forum http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=57011&highlight=warning+initialized the users are saying is a bug, but I’m a beginner with AVR.

Looks like that’s a bug with the C++ compiler. You could try rewriting those lines as:

const prog_char welcome_line1[] = " Pololu";

The forum you referred to said that this way of writing it works without warnings.

-Paul

Perfect is working, but now I get the last warning message:

Should I fix something on my makefile?

CFLAGS=-g -Wall -mcall-prologues -mmcu=atmega168 -Os
CPP=/usr/bin/avr-g++
CXX=/usr/bin/avr-g++
CC=/usr/bin/avr-gcc
OBJ2HEX=avr-objcopy 
LDFLAGS=-Wl,-gc-sections -lpololu -lm

PORT=/dev/ttyUSB0
AVRDUDE=avrdude
TARGET=ico
OBJECT_FILES=ico.o

all: $(TARGET).hex

clean:
	rm -f *.o *.hex *.obj *.hex

%.hex: %.obj
	$(OBJ2HEX) -R .eeprom -O ihex $< $@

test.o: test.cpp

%.obj: $(OBJECT_FILES)
	$(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@

program: $(TARGET).hex
	$(AVRDUDE) -p m168 -c avrisp2 -P $(PORT) -U flash:w:$(TARGET).hex

When you post the warning messages, you should also post the g++ command line that generated the error. In this case, it looks like you have defined CFLAGS but not CXXFLAGS, so the compiler is not getting the -mmcu option when compiling your c++ files. Can you try adding a CXXFLAGS declaration to your Makefile?

Yes corrected in this way:

CFLAGS=-g -Wall -mcall-prologues -mmcu=atmega168 -Os
CXXFLAGS=-g -Wall -mcall-prologues -mmcu=atmega168 -Os
CPP=/usr/bin/avr-g++
CXX=/usr/bin/avr-g++
CC=/usr/bin/avr-gcc
OBJ2HEX=avr-objcopy 
LDFLAGS=-Wl,-gc-sections -lpololu -lm

PORT=/dev/ttyUSB0
AVRDUDE=avrdude
TARGET=ico
OBJECT_FILES=ico.o

all: $(TARGET).hex

clean:
	rm -f *.o *.hex *.obj *.hex

%.hex: %.obj
	$(OBJ2HEX) -R .eeprom -O ihex $< $@

test.o: test.cpp

%.obj: $(OBJECT_FILES)
	$(CC) $(CFLAGS) $(OBJECT_FILES) $(LDFLAGS) -o $@

program: $(TARGET).hex
	$(AVRDUDE) -p m168 -c avrisp2 -P $(PORT) -U flash:w:$(TARGET).hex

and is compiling properly:

Very good!

Great! So you’re able to load your C++ code onto the board now? Is this the neural network library that you were talking about before? I hope you’ll tell us how that project goes.

-Paul

Yes is a simple controller based on a temporal hebbian learning rule called ICO-ISO learning http://www.berndporr.me.uk/isolearn/. But I’m planning to develop also traditional neural networks and digital filters. So it will be useful for everybody.

Argh there’s something wrong in the function:
OrangutanLCD::printFromProgramSpace
For instance even a simple program like this:

#include <pololu/OrangutanLCD.h>
#include <pololu/OrangutanPushbuttons.h>
#include <pololu/OrangutanTime.h>
#include <pololu/OrangutanSerial.h>
#include <pololu/OrangutanBuzzer.h>
#include <string.h>

// This include file allows data to be stored in program space.  The
// ATmega168 has 16k of program space compared to 1k of RAM, so large
// pieces of static data should be stored in program space.
#include <avr/pgmspace.h>


const prog_char demo_name_line1[]  = "Demo";
const prog_char demo_name_line2[]  = "Program";

OrangutanLCD lcd;
OrangutanPushbuttons buttons;

void print_two_lines_delay_1s(const prog_char *line1, const prog_char *line2)
{
	// Play welcome music and display a message
	lcd.clear();
	lcd.printFromProgramSpace(line1);
	lcd.gotoXY(0,1);
	lcd.printFromProgramSpace(line2);
	OrangutanTime::delayMilliseconds(1000);
}

void print_two_string_delay_1s(const char *line1, const char *line2)
{
	// Play welcome music and display a message
	lcd.clear();
	lcd.print(line1);
	lcd.gotoXY(0,1);
	lcd.print(line2);
	OrangutanTime::delayMilliseconds(1000);
}


void menu_select()
{
	static int menu_index = 0;

	//This code is not working
	print_two_lines_delay_1s(main_menu_intro_line1,main_menu_intro_line2);
	//But using embedded strings is working
	//print_two_string_delay_1s("Main","Menu");

}

// This is the main function, where the code starts.  All C programs
// must have a main() function defined somewhere.
int main()
{

	// This is the "main loop" - it will run forever.
	while(1)
	{
		menu_select();
	}
}

When you run it on the LCD there are some garbage characters, I think because is loading the ASCII strings from some other memory locations.

I’m not sure how that example compiles for you - you use:

print_two_lines_delay_1s(main_menu_intro_line1,main_menu_intro_line2);

instead of

print_two_lines_delay_1s(demo_name_line1,demo_name_line2);

When I change that single line, it works fine for me, with no garbage characters. What version of avr-g++ are you using now? A new WinAVR was just released a couple of days ago; you might want to give it a try.
-Paul

Yes that code works but this simple reduced one, still gives me garbage characters:

#include <pololu/OrangutanLCD.h>
#include <pololu/OrangutanPushbuttons.h>
#include <pololu/OrangutanTime.h>
#include <pololu/OrangutanSerial.h>
#include <pololu/OrangutanBuzzer.h>
#include <string.h>

// This include file allows data to be stored in program space.  The
// ATmega168 has 16k of program space compared to 1k of RAM, so large
// pieces of static data should be stored in program space.
#include <avr/pgmspace.h>

// Introductory messages.  The "" identifier causes the data to
// go into program space.

// pi prog_character
const prog_char demo_name_line1[]  = "Demo";
const prog_char demo_name_line2[]  = "Program";


const prog_char main_menu_intro_line1[]  = "  Main";
const prog_char main_menu_intro_line2[]  = "  Menu";

OrangutanLCD lcd;
OrangutanPushbuttons buttons;

void print_two_lines_delay_1s(const prog_char *line1, const prog_char *line2)
{
	// Play welcome music and display a message
	lcd.clear();
	lcd.printFromProgramSpace(line1);
	lcd.gotoXY(0,1);
	lcd.printFromProgramSpace(line2);
	OrangutanTime::delayMilliseconds(1000);
}

void print_two_string_delay_1s(const char *line1, const char *line2)
{
	// Play welcome music and display a message
	lcd.clear();
	lcd.print(line1);
	lcd.gotoXY(0,1);
	lcd.print(line2);
	OrangutanTime::delayMilliseconds(1000);
}


void menu_select()
{
	//static int menu_index = 0;

	//This code is not working
	print_two_lines_delay_1s(demo_name_line1,demo_name_line2);
	print_two_lines_delay_1s(main_menu_intro_line1,main_menu_intro_line2);

}

// This is the main function, where the code starts.  All C programs
// must have a main() function defined somewhere.
int main()
{

	// This is the "main loop" - it will run forever.
	while(1)
	{
		menu_select();
	}
}

I can’t understand why because is so simple!
I’m using the last version from Debian repository, but oky I can try on windows too and see what happen tomorrow.
Thanks for the help again.

Okay, I finally got around to trying this on my 3pi and it works fine, displaying your text endlessly. I’m using Ubuntu with avr-gcc 4.2.2. What version do you have?

Mistery solved: the avr-gcc on my kubuntu 8.10 is the one that produces the lcd error

epokh@epokh-laptop:~$ avr-gcc --v
Using built-in specs.
Target: avr
Configured with: ../src/configure -v --enable-languages=c,c++ --prefix=/usr --infodir=/usr/share/info --mandir=/usr/share/man --libexecdir=/usr/lib --enable-shared --with-system-zlib --enable-long-long --enable-nls --without-included-gettext --disable-checking --disable-libssp --build=i486-linux-gnu --host=i486-linux-gnu --target=avr
Thread model: single
gcc version 4.3.0 (GCC)

but on my ubuntu 8.6 with avr-gcc 4.2.2 is working properly on the lcd.
So it’s fine I will use the 4.2.2 for both.
Thanks again

It looks like attaching the progmem attribute to a typedef no longer works in gcc 4.3.0 and above, and thus “prog_char” is now acting just as “char” would. So, to make your program work with those compilers, instead of using “prog_char”, just use “PROGMEM char”. But then you will have to tolerate some warning messages that say “only initialized variables can be placed into program memory area”, which are silly because your variables are initialized. Moreover, for parameters to functions, just use “char”, because the progmem attribute is advice to the compiler only about where to allocate the data when initializing it, not advice about how to use pointers to it (unfortunately).

Hi dan,
i forgot to reply long time ago.
Yes you are right I got warning but is working good.
Here the fix for my skeleton code:

 

#include <pololu/OrangutanLCD.h>
#include <pololu/OrangutanPushbuttons.h>
#include <pololu/OrangutanTime.h>
#include <pololu/OrangutanSerial.h>
#include <pololu/OrangutanBuzzer.h>
#include <pololu/Uico.h>
#include <string.h>

// This include file allows data to be stored in program space.  The
// ATmega168 has 16k of program space compared to 1k of RAM, so large
// pieces of static data should be stored in program space.
#include <avr/pgmspace.h>

	// set up the neural controller
	Uico controller(0.01,0.501);



OrangutanLCD lcd;
OrangutanPushbuttons buttons;

// Introductory messages.  The "" identifier causes the data to
// go into program space.

													// pi PROGMEM character
const PROGMEM char demo_name_line1[]  = "ICO";
const PROGMEM char demo_name_line2[]  = "learning";


const PROGMEM char main_menu_intro_line1[]  = "  Main";
const PROGMEM char main_menu_intro_line2[]  = "  Menu";

const PROGMEM char menu_start[]  = "Run";
const PROGMEM char menu_calibrate[]  = "Calib";
const PROGMEM char menu_reset[]  = "Reset";
const PROGMEM char menu_save[]  = "Save";
const PROGMEM char menu_view_weights[]  = "View";
const PROGMEM char menu_edit_weights[]  = "Edit";

const PROGMEM char menu_line2[]  = "\x7f" "A \xa5" "B C\x7e";
const PROGMEM char back_line2[]  = "\6B";


void start();
void calibrate();
void reset();
void save();
void view_weights();
void edit_weights();

typedef void (*function)();
const function main_menu_functions[] = { start, calibrate, reset, save,view_weights, edit_weights };
const char *main_menu_options[] = { menu_start, menu_calibrate, menu_reset, menu_save, menu_view_weights, menu_edit_weights};
const char main_menu_length = sizeof(main_menu_options)/sizeof(main_menu_options[0]);

// A couple of simple tunes, stored in program space.
//const PROGMEM char welcome[]  = ">g32>>c32";
//const PROGMEM char thank_you_music[]  = ">>c32>g32";
const PROGMEM char beep_button_a[]  = "!c32";
const PROGMEM char beep_button_b[]  = "!e32";
const PROGMEM char beep_button_c[]  = "!g32";
//const PROGMEM char timer_tick[]  = "!v8>>c32";


void start()
{

// wait for all buttons to be released, then a press
while(!buttons.isPressed(ALL_BUTTONS));
{
//  controller.update();
  lcd.print("Updating");
}
  lcd.print("Stopped");
		OrangutanTime::delayMilliseconds(100);
}

void calibrate()
{
		OrangutanTime::delayMilliseconds(100);
}

void reset()
{
		OrangutanTime::delayMilliseconds(100);
}

void save()
{
		OrangutanTime::delayMilliseconds(100);
}

void view_weights()
{
		OrangutanTime::delayMilliseconds(100);
}

void save_weights()
{
		OrangutanTime::delayMilliseconds(100);
}

void edit_weights()
{
		OrangutanTime::delayMilliseconds(100);
}

void print_two_lines_delay_1s(const PROGMEM char *line1, const PROGMEM char *line2)
{
	// Play welcome music and display a message
	lcd.clear();
	lcd.printFromProgramSpace(line1);
	lcd.gotoXY(0,1);
	lcd.printFromProgramSpace(line2);
	OrangutanTime::delayMilliseconds(1000);
}

void menu_select()
{
	static int menu_index = 0;

	print_two_lines_delay_1s(main_menu_intro_line1,main_menu_intro_line2);

	while(1)
	{
		lcd.clear();
		lcd.gotoXY(0,1);
		lcd.printFromProgramSpace(menu_line2);
		lcd.gotoXY(0,0);
		lcd.printFromProgramSpace(main_menu_options[menu_index]);
		lcd.showCursor(CURSOR_BLINKING);
		// the cursor will be blinking at the end of the option name
	
		// wait for all buttons to be released, then a press
		while(buttons.isPressed(ALL_BUTTONS));
		char button = buttons.waitForButton(ALL_BUTTONS);
		if(button & BUTTON_A)
		{
			OrangutanBuzzer::playFromProgramSpace(beep_button_a);
			menu_index --;
		}
		else if(button & BUTTON_B)
		{
			lcd.hideCursor();
			lcd.clear();

			OrangutanBuzzer::playFromProgramSpace(beep_button_b);
			buttons.waitForRelease(button);

			while(!buttons.isPressed(BUTTON_B))
			{
				lcd.gotoXY(0,1);
				lcd.printFromProgramSpace(back_line2);
				lcd.gotoXY(0,0);
				main_menu_functions[menu_index]();
			}

			//do something here
			OrangutanBuzzer::playFromProgramSpace(beep_button_b);

			return;
		}
		else if(button & BUTTON_C)
		{
			OrangutanBuzzer::playFromProgramSpace(beep_button_c);
			menu_index ++;
		}

		if(menu_index < 0)
			menu_index = main_menu_length-1;
		if(menu_index >= main_menu_length)
			menu_index = 0;
	}
}

// This is the main function, where the code starts.  All C programs
// must have a main() function defined somewhere.
int main()
{

	// This is the "main loop" - it will run forever.
	while(1)
	{
		menu_select();
	}
}