Problem with PROGMEM

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so I'm designing a finite state machine in order to display a menu system on an lcd. I'm using a ATmega8515 at internal 8Mhz. There are many data structures that need to be stored for this FSM, and also two arrays for the lcd: a bitmap, and a font table. The code has worked, but I just added a new data structure, and I think I have overwritten some of the data in my data structures.

All of these structures and arrays are defined as static and PROGMEM, and I have verified in AVR STUDIO that there is sufficient program memory left. The menu works perfectly even with the new data structure, but some of the strings that are supposed to be displayed are gibberish, or bits and pieces of the wrong strings.

OK enough of my gibberish here is the code:

These are the FSM data structures

//fsmtest.h

//////////////Includes////////////////

#include	"glcd.c"
#include	"titlefont.h"
#include	"movies.h"

///////////Global Variables///////////

char STATE;
char *STATE_TEXT;
unsigned char PARENT;
unsigned char CURRENT_MOVIE;

/////////////Functions////////////////

void Initialization(void);
char mainList(void);
char allList(void);
char genreList(void);
char ratingList(void);
char yearList(void);
char comedyList(void);
char dramaList(void);
char romanceList(void);
char kidsList(void);
char actionList(void);
char gList(void);
char pgList(void);
char pg13List(void);
char rList(void);
char recentList(void);
char y90sList(void);
char y80sList(void);
char y70sList(void);
char olderList(void);
char movieDisplay(void);
char retrieveMovie(void);
char movieList(char *, unsigned char, unsigned char, unsigned int);

////////////Defines///////////////////

#define		F_CPU 				8000000UL 	 // 8 MHz
#define		NULL				0
#define		BUTTON_DIR			DDRB
#define		BUTTON_IN			PINB
#define		BUTTON_PORT			PORTB
#define		NUM_MOVIES			14

///////////STATE DEFINES//////////////

#define		MAIN_LIST			10	
#define		ALL_LIST			11
#define		GENRE_LIST			12
#define		RATING_LIST			13
#define		YEAR_LIST			14
#define		COMEDY_LIST			15
#define		DRAMA_LIST			16
#define		ROMANCE_LIST		17
#define		KIDS_LIST			18
#define		ACTION_LIST			19
#define		G_LIST				20
#define		PG_LIST				21
#define		PG13_LIST			22
#define		R_LIST				23
#define		RECENT_LIST			24
#define		y90s_LIST			25
#define		y80s_LIST			26
#define		y70s_LIST			27
#define		OLDER_LIST			28
#define		MOVIE_DISPLAY		29
#define		RETRIEVE_MOVIE		30

///////////Data Structures/////////////

//OPTIONS
static struct options {
	char 	*name;
	char	state;
};

#define		NUM_MAIN	4
static struct options Main_Options[] PROGMEM = {
		//TEXT			STATE
		{"ALL", 		ALL_LIST},
		{"GENRE", 		GENRE_LIST},
		{"RATING",		RATING_LIST},
		{"YEAR",		YEAR_LIST}
	};

#define		NUM_GENRE	5	
static struct options Genre_Options[] PROGMEM = {
		//TEXT			STATE
		{"COMEDY",		COMEDY_LIST},
		{"DRAMA",		DRAMA_LIST},
		{"ROMANCE", 	ROMANCE_LIST},
		{"KIDS",		KIDS_LIST},
		{"ACTION",		ACTION_LIST}
	};
	
#define		NUM_RATING	4	
static struct options Rating_Options[] PROGMEM = {
		//TEXT			STATE
		{"G RATED",			G_LIST},
		{"PG RATED",			PG_LIST},
		{"PG-13 RATED",		PG13_LIST},
		{"R RATED",			R_LIST}
	};
	
#define		NUM_YEAR	5	
static struct options Year_Options[] PROGMEM = {
		//TEXT			STATE
		{"RECENT",		RECENT_LIST},
		{"90's",		y90s_LIST},
		{"80's",		y80s_LIST},
		{"70's",		y70s_LIST},
		{"OLDER",		OLDER_LIST}
	};
	
//MENU STATE

static struct MENU_STATE {
	char state;
	char *text;
	char (*pFunc)(void);
};

static struct MENU_STATE menu_state[] PROGMEM = {
		//STATE				STATE TEXT		POINTER TO FUNCTION
		{MAIN_LIST,			NULL,			mainList},
		{ALL_LIST,			"ALL",			allList},
		{GENRE_LIST,		"GENRE",		genreList},
		{RATING_LIST,		"RATING",		ratingList},
		{YEAR_LIST,			"YEAR",			yearList},
		{COMEDY_LIST,		"COMEDY",		comedyList},
		{DRAMA_LIST,		"DRAMA",		dramaList},
		{ROMANCE_LIST,		"ROMANCE",		romanceList},
		{KIDS_LIST,			"KIDS",			kidsList},
		{ACTION_LIST,		"ACTION",		actionList},
		{G_LIST,			"G RATED",		gList},
		{PG_LIST,			"PG RATED",		pgList},
		{PG13_LIST,			"PG-13 RATED",	pg13List},
		{R_LIST,			"R RATED",		rList},
		{RECENT_LIST,		"RECENT",		recentList},
		{y90s_LIST,			"90's",			y90sList},
		{y80s_LIST,			"80's",			y80sList},
		{y70s_LIST,			"70's",			y70sList},
		{OLDER_LIST,		"OLDER",		olderList},
		{MOVIE_DISPLAY,		NULL,			movieDisplay},
		{RETRIEVE_MOVIE,	NULL,			retrieveMovie},
		{0,					NULL,			NULL}
	};
	

This is the Main funtion

//fsmtest.c

#include "fsmtest.h"




////////Main Function/////////
   /*Infinite Loop of FSM*/
   
int main(void)
{
	Initialization();
	
	STATE = MAIN_LIST;
	STATE_TEXT = NULL;
	char (*pStateFunc)(void) = mainList;
	
	for(;;)
	{
		STATE = pStateFunc();			//Run state function until it returns the next state
		
		for (unsigned char i=0; pgm_read_byte(&menu_state[i].state); i++)			//Finds corresponding text and function pointers
        {
            if (pgm_read_byte(&menu_state[i].state) == STATE)
            {
						
				STATE_TEXT =  (PGM_P)pgm_read_word(&menu_state[i].text);	
						
				pStateFunc = (PGM_VOID_P)pgm_read_word(&menu_state[i].pFunc);
						
                break;
            }
        }
	}
	
	return 0;
}

////////Initialization////////
/*Initialization of Lcd, Stepper, Inputs, and Servo*/

void Initialization(void)
{
	lcdInit();
	BUTTON_DIR = 0x00;		//Buttons as input
	BUTTON_PORT = 0x00;
}



////////////STATE FUNCTIONS////////////////
////////////MAINLIST///////////////////////
char mainList(void)
{
	lcdHome();
	
	unsigned int i;
	
	for(i=0; i<256; i++)						//Display Splash Screen
		lcdDataWrite(pgm_read_byte(&Fontsplash[i]));
	
	for(i=0; i0)&&(BUTTON_IN == 0b11111101))
		{
			lcdInvertPage(select+3, 0, 127);		//deselect and erase arrow
			goToChar(20, select+3);
			lcdWriteChar(' ');
			
			select--;
			
			goToChar(20, select+3);					//draw arrow and select
			lcdWriteChar('}'+1);
			lcdInvertPage(select+3, 0, 127);
			
			while(BUTTON_IN==0b11111101);
		}
		
		if(BUTTON_IN == 0b11111011)
		{
			while(BUTTON_IN == 0b11111011);
			return pgm_read_byte(&Main_Options[select].state);
		}
	}
	
			
	return MAIN_LIST;			//Failsafe
}

//////////////ALL_LIST/////////////////////

char allList(void)
{
	PARENT = ALL_LIST;
		
	return movieList("ALL", 0, 0, 0);
}

/////////////GENRE_LIST///////////////////////

char genreList(void)
{
	lcdHome();
	
	unsigned char i;
	
	setAddr(49,0);				//center text
	
	lcdPutStr("GENRE");
	
	setAddr(0,1);				//draw division
	for(i=0; i<128; i++)
		lcdDataWrite(0x01);
	
	for(i=0; i0)&&(BUTTON_IN == 0b11111101))
		{
			lcdInvertPage(select+2, 0, 127);		//deselect and erase arrow
			goToChar(20, select+2);
			lcdWriteChar(' ');
			
			select--;
			
			goToChar(20, select+2);					//draw arrow and select
			lcdWriteChar('}'+1);
			lcdInvertPage(select+2, 0, 127);
			
			while(BUTTON_IN==0b11111101);
		}
		
		if(BUTTON_IN == 0b11111011)
		{
			while(BUTTON_IN == 0b11111011);
			return pgm_read_byte(&Genre_Options[select].state);
		}
		
		if(BUTTON_IN == 0b11110111)
		{
			while(BUTTON_IN == 0b11110111);
			return MAIN_LIST;
		}
	}
	
	return GENRE_LIST;
}

////////////////RATING_LIST//////////////

char ratingList(void)
{
	lcdHome();
	
	unsigned char i;
	
	setAddr(43,0);				//center text
	
	lcdPutStr("RATING");
	
	setAddr(0,1);				//draw division
	for(i=0; i<128; i++)
		lcdDataWrite(0x01);
	
	for(i=0; i0)&&(BUTTON_IN == 0b11111101))
		{
			lcdInvertPage(select+2, 0, 127);		//deselect and erase arrow
			goToChar(20, select+2);
			lcdWriteChar(' ');
			
			select--;
			
			goToChar(20, select+2);					//draw arrow and select
			lcdWriteChar('}'+1);
			lcdInvertPage(select+2, 0, 127);
			
			while(BUTTON_IN==0b11111101);
		}
		
		if(BUTTON_IN == 0b11111011)
		{
			while(BUTTON_IN == 0b11111011);
			return pgm_read_byte(&Rating_Options[select].state);
		}
		
		if(BUTTON_IN == 0b11110111)
		{
			while(BUTTON_IN == 0b11110111);
			return MAIN_LIST;
		}
	}
	return RATING_LIST;
}

////////////////YEAR_LIST////////////////

char yearList(void)
{
	lcdHome();
	
	unsigned char i;
	
	setAddr(52,0);				//center text
	
	lcdPutStr("YEAR");
	
	setAddr(0,1);				//draw division
	for(i=0; i<128; i++)
		lcdDataWrite(0x01);
	
	for(i=0; i0)&&(BUTTON_IN == 0b11111101))
		{
			lcdInvertPage(select+2, 0, 127);		//deselect and erase arrow
			goToChar(20, select+2);
			lcdWriteChar(' ');
			
			select--;
			
			goToChar(20, select+2);					//draw arrow and select
			lcdWriteChar('}'+1);
			lcdInvertPage(select+2, 0, 127);
			
			while(BUTTON_IN==0b11111101);
		}
		
		if(BUTTON_IN == 0b11111011)
		{
			while(BUTTON_IN == 0b11111011);
			return pgm_read_byte(&Year_Options[select].state);
		}
		
		if(BUTTON_IN == 0b11110111)
		{
			while(BUTTON_IN == 0b11110111);
			return MAIN_LIST;
		}
	}
	return YEAR_LIST;
}
char comedyList(void){return NULL;}
char dramaList(void){return NULL;}
char romanceList(void){return NULL;}
char kidsList(void){return NULL;}
char actionList(void){return NULL;}
char gList(void){return NULL;}
char pgList(void){return NULL;}
char pg13List(void){return NULL;}
char rList(void){return NULL;}
char recentList(void){return NULL;}
char y90sList(void){return NULL;}
char y80sList(void){return NULL;}
char y70sList(void){return NULL;}
char olderList(void){return NULL;}
char movieDisplay(void){return NULL;}
char retrieveMovie(void){return NULL;}


char movieList(char *title, unsigned char genre, unsigned char rating, unsigned int year) // only send one parameter, rest are 0
{
	unsigned char temp_num_movies=0, i;
	struct movie temp_list[NUM_MOVIES];
	
	if(genre)								//If genre selected sort by genre
	{
		for(i=0; i= year)&&((pgm_read_byte(&movies[i].year) - year) < 10))
											||((pgm_read_byte(&movies[i].year) - year) < 1970))
					     			
			{
				temp_list[i].title = pgm_read_byte(&movies[i].title);
				temp_list[i].rating = pgm_read_byte(&movies[i].rating);				
				temp_list[i].year = pgm_read_word(&movies[i].year);			
				temp_list[i].genre = pgm_read_byte(&movies[i].genre);
				temp_list[i].slot = pgm_read_byte(&movies[i].slot);
				
				temp_num_movies++;
			}
			
		}
	}
	
	else
	{
		for(i=0; i

This is the character set:

/*! \file font5x7.h \brief Graphic LCD Font (Ascii Characters). */
//*****************************************************************************
//
// File Name	: 'font5x7.h'
// Title		: Graphic LCD Font (Ascii Charaters)
// Author		: Pascal Stang
// Date			: 10/19/2001
// Revised		: 10/19/2001
// Version		: 0.1
// Target MCU	: Atmel AVR
// Editor Tabs	: 4
//
//*****************************************************************************

#ifndef FONT5X7_H
#define FONT5X7_H

// standard ascii 5x7 font
// defines ascii characters 0x20-0x7F (32-127)
static unsigned char __attribute__ ((progmem)) Font5x7[] = {
	0x00, 0x00, 0x00, 0x00, 0x00,// (space)
	0x00, 0x00, 0x5F, 0x00, 0x00,// !
	0x00, 0x07, 0x00, 0x07, 0x00,// "
	0x14, 0x7F, 0x14, 0x7F, 0x14,// #
	0x24, 0x2A, 0x7F, 0x2A, 0x12,// $
	0x23, 0x13, 0x08, 0x64, 0x62,// %
	0x36, 0x49, 0x55, 0x22, 0x50,// &
	0x00, 0x05, 0x03, 0x00, 0x00,// '
	0x00, 0x1C, 0x22, 0x41, 0x00,// (
	0x00, 0x41, 0x22, 0x1C, 0x00,// )
	0x08, 0x2A, 0x1C, 0x2A, 0x08,// *
	0x08, 0x08, 0x3E, 0x08, 0x08,// +
	0x00, 0x50, 0x30, 0x00, 0x00,// ,
	0x08, 0x08, 0x08, 0x08, 0x08,// -
	0x00, 0x60, 0x60, 0x00, 0x00,// .
	0x20, 0x10, 0x08, 0x04, 0x02,// /
	0x3E, 0x51, 0x49, 0x45, 0x3E,// 0
	0x00, 0x42, 0x7F, 0x40, 0x00,// 1
	0x42, 0x61, 0x51, 0x49, 0x46,// 2
	0x21, 0x41, 0x45, 0x4B, 0x31,// 3
	0x18, 0x14, 0x12, 0x7F, 0x10,// 4
	0x27, 0x45, 0x45, 0x45, 0x39,// 5
	0x3C, 0x4A, 0x49, 0x49, 0x30,// 6
	0x01, 0x71, 0x09, 0x05, 0x03,// 7
	0x36, 0x49, 0x49, 0x49, 0x36,// 8
	0x06, 0x49, 0x49, 0x29, 0x1E,// 9
	0x00, 0x36, 0x36, 0x00, 0x00,// :
	0x00, 0x56, 0x36, 0x00, 0x00,// ;
	0x00, 0x08, 0x14, 0x22, 0x41,// <
	0x14, 0x14, 0x14, 0x14, 0x14,// =
	0x41, 0x22, 0x14, 0x08, 0x00,// >
	0x02, 0x01, 0x51, 0x09, 0x06,// ?
	0x32, 0x49, 0x79, 0x41, 0x3E,// @
	0x7E, 0x11, 0x11, 0x11, 0x7E,// A
	0x7F, 0x49, 0x49, 0x49, 0x36,// B
	0x3E, 0x41, 0x41, 0x41, 0x22,// C
	0x7F, 0x41, 0x41, 0x22, 0x1C,// D
	0x7F, 0x49, 0x49, 0x49, 0x41,// E
	0x7F, 0x09, 0x09, 0x01, 0x01,// F
	0x3E, 0x41, 0x41, 0x51, 0x32,// G
	0x7F, 0x08, 0x08, 0x08, 0x7F,// H
	0x00, 0x41, 0x7F, 0x41, 0x00,// I
	0x20, 0x40, 0x41, 0x3F, 0x01,// J
	0x7F, 0x08, 0x14, 0x22, 0x41,// K
	0x7F, 0x40, 0x40, 0x40, 0x40,// L
	0x7F, 0x02, 0x04, 0x02, 0x7F,// M
	0x7F, 0x04, 0x08, 0x10, 0x7F,// N
	0x3E, 0x41, 0x41, 0x41, 0x3E,// O
	0x7F, 0x09, 0x09, 0x09, 0x06,// P
	0x3E, 0x41, 0x51, 0x21, 0x5E,// Q
	0x7F, 0x09, 0x19, 0x29, 0x46,// R
	0x46, 0x49, 0x49, 0x49, 0x31,// S
	0x01, 0x01, 0x7F, 0x01, 0x01,// T
	0x3F, 0x40, 0x40, 0x40, 0x3F,// U
	0x1F, 0x20, 0x40, 0x20, 0x1F,// V
	0x7F, 0x20, 0x18, 0x20, 0x7F,// W
	0x63, 0x14, 0x08, 0x14, 0x63,// X
	0x03, 0x04, 0x78, 0x04, 0x03,// Y
	0x61, 0x51, 0x49, 0x45, 0x43,// Z
	0x00, 0x00, 0x7F, 0x41, 0x41,// [
	0x02, 0x04, 0x08, 0x10, 0x20,// "\"
	0x41, 0x41, 0x7F, 0x00, 0x00,// ]
	0x04, 0x02, 0x01, 0x02, 0x04,// ^
	0x40, 0x40, 0x40, 0x40, 0x40,// _
	0x00, 0x01, 0x02, 0x04, 0x00,// `
	0x20, 0x54, 0x54, 0x54, 0x78,// a
	0x7F, 0x48, 0x44, 0x44, 0x38,// b
	0x38, 0x44, 0x44, 0x44, 0x20,// c
	0x38, 0x44, 0x44, 0x48, 0x7F,// d
	0x38, 0x54, 0x54, 0x54, 0x18,// e
	0x08, 0x7E, 0x09, 0x01, 0x02,// f
	0x08, 0x14, 0x54, 0x54, 0x3C,// g
	0x7F, 0x08, 0x04, 0x04, 0x78,// h
	0x00, 0x44, 0x7D, 0x40, 0x00,// i
	0x20, 0x40, 0x44, 0x3D, 0x00,// j
	0x00, 0x7F, 0x10, 0x28, 0x44,// k
	0x00, 0x41, 0x7F, 0x40, 0x00,// l
	0x7C, 0x04, 0x18, 0x04, 0x78,// m
	0x7C, 0x08, 0x04, 0x04, 0x78,// n
	0x38, 0x44, 0x44, 0x44, 0x38,// o
	0x7C, 0x14, 0x14, 0x14, 0x08,// p
	0x08, 0x14, 0x14, 0x18, 0x7C,// q
	0x7C, 0x08, 0x04, 0x04, 0x08,// r
	0x48, 0x54, 0x54, 0x54, 0x20,// s
	0x04, 0x3F, 0x44, 0x40, 0x20,// t
	0x3C, 0x40, 0x40, 0x20, 0x7C,// u
	0x1C, 0x20, 0x40, 0x20, 0x1C,// v
	0x3C, 0x40, 0x30, 0x40, 0x3C,// w
	0x44, 0x28, 0x10, 0x28, 0x44,// x
	0x0C, 0x50, 0x50, 0x50, 0x3C,// y
	0x44, 0x64, 0x54, 0x4C, 0x44,// z
	0x00, 0x08, 0x36, 0x41, 0x00,// {
	0x00, 0x00, 0x7F, 0x00, 0x00,// |
	0x00, 0x41, 0x36, 0x08, 0x00,// }
	0x08, 0x08, 0x2A, 0x1C, 0x08,// ->
	0x08, 0x1C, 0x2A, 0x08, 0x08 // <-
};

#endif

This is the bitmap:

//titlefont.h
//Graphic for Main Menu


static unsigned char Fontsplash[] PROGMEM = {
0,255,255,3,3,3,3,3,
6,14,252,240,0,3,15,124,
240,128,0,0,0,128,240,124,
15,3,0,255,255,3,3,3,
3,3,6,14,252,240,0,0,
0,0,0,0,7,63,248,192,
0,0,192,248,63,63,248,192,
0,0,192,248,63,7,0,255,
255,192,192,192,192,192,192,192,
192,255,255,0,0,243,243,0,
0,3,3,3,131,195,227,115,
59,31,15,7,0,0,0,0,
0,0,24,30,6,195,195,195,
230,190,24,0,248,254,6,3,
3,3,6,254,248,0,248,254,
6,3,3,3,6,254,248,0,

128,191,191,176,176,176,176,176,
152,156,143,131,128,128,128,128,
129,143,190,184,190,143,129,128,
128,128,128,191,191,176,176,176,
176,176,152,156,143,131,128,128,
128,128,128,128,128,128,129,143,
190,190,143,129,128,128,129,143,
190,190,143,129,128,128,128,191,
191,128,128,128,128,128,128,128,
128,191,191,128,128,191,191,128,
184,188,190,183,179,177,176,176,
176,176,176,176,128,128,128,128,
128,128,134,158,152,176,176,176,
153,159,134,128,135,159,152,176,
176,176,152,159,135,128,135,159,
152,176,176,176,152,159,135,128
};

and finally this is the bit that when added, screws everything up.

//movies.h

//Defines

#define	COMEDY		100
#define	DRAMA		101
#define	ROMANCE		102
#define	KIDS		103
#define	ACTION		104

#define G			50
#define PG			51
#define	PG_13		52
#define	R			54

//Structures

static struct movie {
	unsigned char *title;
	unsigned char rating;
	unsigned int year;
	unsigned char genre;
	unsigned char slot;
};

static struct movie movies[] PROGMEM = {
		//Title					Rating		YEAR	GENRE		SLOT
		{"Hard Ball",			PG_13,		2001,	COMEDY,		0},
		{"Juno",				PG_13,		2007,	DRAMA,		1},
		{"Anchor Man",			PG_13,		2004,	COMEDY,		2},
		{"DodgeBall",			PG_13,		2004,	COMEDY,		3},
		{"Into the Wild",		R,			2007,	DRAMA,		4},
		{"Apocalypse Now",		R,			1979,	ACTION,		5},
		{"Blow",				R,			2001,	ACTION,		6},
		{"Forrest Gump",		PG_13,		1994,	COMEDY,		7},
		{"The Matrix",			R,			1999,	ACTION,		8},
		{"Platoon",				R,			1986,	ACTION,		9},
		{"Miracle",				PG,			2004,	DRAMA,		10},
		{"Star Wars",			PG,			1977,	ACTION,		11},
		{"Blade Runner",		R,			1982,	ACTION,		12},
		{"Baseketball",			R,			1998,	COMEDY,		13}
	};

I am pretty sure this has something to do with how I am implementing the PROGMEM attribute, but I have no clue what the problem is. Any help would be greatly appreciated

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

it's your strings. You have declared them as pointers to char, so what is stored in the structure itself is only the 16 bit pointer. The string literal will be stored in RAM (because you have not declared it to be in flash) But of course, you are decoding them as being in flash, thus you are reading random data from Flash, instead of the string.

There are a couple of ways to resolve this, but the easiest in this case is probably the PSTR macro.


static struct movie {
   const PROGMEM char *title; // const & PROGMEM added to remove discarded qualifier warnings
   unsigned char rating;
   unsigned int year;
   unsigned char genre;
   unsigned char slot;
};


static struct movie movies[] PROGMEM = {
      //Title               Rating      YEAR   GENRE      SLOT
      {PSTR("Hard Ball"),         PG_13,      2001,   COMEDY,      0},
      {PSTR("Juno"),            PG_13,      2007,   DRAMA,      1},
      {PSTR("Anchor Man"),         PG_13,      2004,   COMEDY,      2},
      {PSTR("DodgeBall"),         PG_13,      2004,   COMEDY,      3},
      {PSTR("Into the Wild"),      R,         2007,   DRAMA,      4},
      {PSTR("Apocalypse Now"),      R,         1979,   ACTION,      5},
      {PSTR("Blow"),            R,         2001,   ACTION,      6},
      {PSTR("Forrest Gump"),      PG_13,      1994,   COMEDY,      7},
      {PSTR("The Matrix"),         R,         1999,   ACTION,      8},
      {PSTR("Platoon"),            R,         1986,   ACTION,      9},
      {PSTR("Miracle"),            PG,         2004,   DRAMA,      10},
      {PSTR("Star Wars"),         PG,         1977,   ACTION,      11},
      {PSTR("Blade Runner"),      R,         1982,   ACTION,      12},
      {PSTR("Baseketball"),         R,         1998,   COMEDY,      13}
   };

Note that the above applies to anywhere you have declared a "char *" pointer in your structures, and then initialized it with a simple string literal. The string literal will actually be stored in RAM.

I suggest you read the avr-libc documentation to get a better understanding/

http://www.nongnu.org/avr-libc/u...

http://www.nongnu.org/avr-libc/u...

http://www.nongnu.org/avr-libc/u...

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I miss some includes in your source e.g.

#include 
#include 

in every file you want to use PROGMEM specific attribute / defines / functions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Glitch thanks for your insight, that makes sense, however when I try to compile my code with the PSTR() encapsulating all of my strings, I get these compiler errors at every instance:

fsmtest.h:86: error: braced-group within expression allowed only inside a function
fsmtest.h:86: error: initializer element is not constant
fsmtest.h:86: error: (near initialization for 'Main_Options[0].name')

I also worked a little bit and found a "work around" although I'm hesitant to call it a solution. It is that everytime the functions in my program dealt with any of the data that was a string, I cast it to a (PGM_P) and read a word, in this fashion:

lcdPutStr((PGM_P)pgm_read_word(&Rating_Options[i].name));

Like I said, I believe this is a work around and could come back to haunt me later, but I'm unsure.

BTW kschwiRG the includes are defined in a file that did not need to be shown for my problem, but thanks.

Thanks for all your help!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

agoessling wrote:
fsmtest.h:86: error: braced-group within expression allowed only inside a function
fsmtest.h:86: error: initializer element is not constant
fsmtest.h:86: error: (near initialization for 'Main_Options[0].name')

I read a discussion about flash strings somewhere the last days. Outside of functions it was not necessary to define a string as "static", but inside a function the string has to be definied "static" - which was the difference between a error/warning or not (if I remember correctly).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Clearly your lcdPutString expects a string from SRAM or this would not work:

   lcdPutStr("RATING"); 

Your fix (excluding the PGM_P cast) makes sense, you can not read out a byte and expect that to be an ok string pointer so:

lcdPutStr((char *)pgm_read_word(&Rating_Options[i].name)); 

Now, to save SRAM you should probably make everything end up in flash, the PROGMEM tutorial shows how this is done:
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003
For starters you will need a lcdPutStr_P() or similar function that can work with a string from flash.
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

agoessling wrote:

lcdPutStr((PGM_P)pgm_read_word(&Rating_Options[i].name));

Well here you have another problem. Chances are that lcdPutStr is designed for a pointer to a string in RAM. (this is why your menus were working before you added the extra stuff, adding the extra stuff caused you to run out of RAM, so the stack was overflowing into your data)

What that line is doing is reading an integer from flash, and then casting it as a pointer to flash. lcdPutStr will still interpret the pointer as a pointer to RAM, and decode it that way, regardless of the cast. (the address space info is not passed along with the pointer, so the function has no way to tell if it is a pointer to ram, or a pointer to flash on its own... you as the programmer have to write the function to interpret it one way or the other)

I'll have to look into those errors. It may be that you can't use an anonymous flash string literal in the initialization like that. (sorry that was off the top of my head last night)

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I'll have to look into those errors. It may be that you can't use an anonymous flash string literal in the initialization like that. (sorry that was off the top of my head last night)
Yes, that is it, PSTR is implemented using the gcc extension "Statements and Declarations in Expressions" which can not be used in constant expressions (e.g., the initial value of a global variable).
/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yeah, I see that now. Keep forgetting that.

So sadly the solution is one of the following.

1) declare the strings separately, and then use their symbolic name in the structure initialization

2) declare a fixed length array inside the structure to hold the string contents.

Option 1 is harder to maintain, but remains compact in terms of memory usage. Option 2 is easier to maintain, but will consume more memory.

Option 1:

static struct movie {
   const char *title; 
   unsigned char rating;
   unsigned int year;
   unsigned char genre;
   unsigned char slot;
};

const char HardBall[] PROGMEM = "Hard Ball";
const char Juno[] PROGMEM = "Juno";
.
.
.
const char Baseketball[] PROGMEM = "Baseketball";

static struct movie movies[] PROGMEM = {
      //Title               Rating      YEAR   GENRE      SLOT
      {HardBall,         PG_13,      2001,   COMEDY,      0},
      {Juno,            PG_13,      2007,   DRAMA,      1},
      {AnchorMan,         PG_13,      2004,   COMEDY,      2},
      {DodgeBall,         PG_13,      2004,   COMEDY,      3},
      {IntoTheWild,      R,         2007,   DRAMA,      4},
      {ApocalypseNow,      R,         1979,   ACTION,      5},
      {Blow,            R,         2001,   ACTION,      6},
      {ForrestGump,      PG_13,      1994,   COMEDY,      7},
      {TheMatrix,         R,         1999,   ACTION,      8},
      {Platoon,            R,         1986,   ACTION,      9},
      {Miracle,            PG,         2004,   DRAMA,      10},
      {StarWars,         PG,         1977,   ACTION,      11},
      {BladeRunner,      R,         1982,   ACTION,      12},
      {Baseketball,         R,         1998,   COMEDY,      13}
   };

Option 2:

static struct movie {
   char title[15]; // length set to longest string +1 for the null terminator
   unsigned char rating;
   unsigned int year;
   unsigned char genre;
   unsigned char slot;
};


static struct movie movies[] PROGMEM = {
      //Title               Rating      YEAR   GENRE      SLOT
      {"Hard Ball",         PG_13,      2001,   COMEDY,      0},
      {"Juno",            PG_13,      2007,   DRAMA,      1},
      {"Anchor Man",         PG_13,      2004,   COMEDY,      2},
      {"DodgeBall",         PG_13,      2004,   COMEDY,      3},
      {"Into the Wild",      R,         2007,   DRAMA,      4},
      {"Apocalypse Now",      R,         1979,   ACTION,      5},
      {"Blow",            R,         2001,   ACTION,      6},
      {"Forrest Gump",      PG_13,      1994,   COMEDY,      7},
      {"The Matrix",         R,         1999,   ACTION,      8},
      {"Platoon",            R,         1986,   ACTION,      9},
      {"Miracle",            PG,         2004,   DRAMA,      10},
      {"Star Wars",         PG,         1977,   ACTION,      11},
      {"Blade Runner",      R,         1982,   ACTION,      12},
      {"Baseketball",         R,         1998,   COMEDY,      13}
   };

What I would do, since you may want to update the listings form time to time, is create a plaintext resource file that has all this information, and then write a generator program that creates the progmem arrays for you. This will give you the ease of maintenance of option #2 while retaining the compactness of option 1.

[edit]
Just for completeness... this is an example of what a generator might output.

static struct movie {
   const char *title;
   unsigned char rating;
   unsigned int year;
   unsigned char genre;
   unsigned char slot;
};

/*
 * The compiler will concatenate all the strings below into one long string,
 * that is terminated by a null. In order to maintain each string 
 * individually, we have to add a null (\0) after 
 * each string (except the last one)
 */

char movieTitles[] PROGMEM = 
      "Hard Ball\0"      // 0  
      "Juno\0"           // 10
      "Anchor Man\0"     // 15
      "DodgeBall\0"      // 26
      "Into the Wild\0"  // 36
      "Apocalypse Now\0" // 50
      "Blow\0"           // 65
      "Forrest Gump\0"   // 70
      "The Matrix\0"     // 83
      "Platoon\0"        // 94
      "Miracle\0"        // 102
      "Star Wars\0"      // 110
      "Blade Runner\0"   // 120
      "Baseketball"      // 133	-- null added by compiler

/*
 * The title pointer is set to the address of the index of 
 * the associated title in the movieTitle array generated above
 */
static struct movie movies[] PROGMEM = {
      //Title               Rating      YEAR   GENRE      SLOT
      {&movieTitles[0],     PG_13,      2001,   COMEDY,     0},
      {&movieTitles[10],    PG_13,      2007,   DRAMA,      1},
      {&movieTitles[15],    PG_13,      2004,   COMEDY,     2},
      {&movieTitles[26],    PG_13,      2004,   COMEDY,     3},
      {&movieTitles[36],     R,         2007,   DRAMA,      4},
      {&movieTitles[50],     R,         1979,   ACTION,     5},
      {&movieTitles[65],     R,         2001,   ACTION,     6},
      {&movieTitles[70],    PG_13,      1994,   COMEDY,     7},
      {&movieTitles[83],     R,         1999,   ACTION,     8},
      {&movieTitles[94],     R,         1986,   ACTION,     9},
      {&movieTitles[102],   PG,         2004,   DRAMA,     10},
      {&movieTitles[110],   PG,         1977,   ACTION,    11},
      {&movieTitles[120],    R,         1982,   ACTION,    12},
      {&movieTitles[133],    R,         1998,   COMEDY,    13}
   };

It's important to note that for all 3 options the lcd_puts function needs to be written to uses pointers to strings in flash.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What you all are saying makes sense, and I will try to impelemnt a lcdPutStr() that takes a pointer to flash, but that then raises the question, of what is happening when I use the current version of lcdPutStr(), as in the case:

lcdPutStr("Hello World");

Does that create a array of characters in RAM never to be used again? Basically what I am asking is, given enough statements like that would that eventually fill up my RAM with random strings?

THANKS A LOT

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

agoessling wrote:

lcdPutStr("Hello World");

Does that create a array of characters in RAM never to be used again? Basically what I am asking is, given enough statements like that would that eventually fill up my RAM with random strings?

yes and yes

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So in order to save ram I need to first make it a PROGMEM char array and then pass it to the pointer version of lcdPutStr()?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lcdPutStr always takes a pointer... you need to make a version of lcdPutStr that interprets the pointer as being to data in flash instead of RAM. Let's call it lcdPutStr_P to differentiate it from the one you already have. (this follows the naming convention already used in avr-libc for the flash version of the string functions)

So in the "new" version you would simply do:

lcdPutStr_P(PSTR("Hello World")); 

or you could pass it a pointer to a string in one of your menu arrays.

lcdPutStr_P((const PROGMEM char *)pgm_read_word(&movies[i].title));

lcdPutStr_P would simply look something like

void lcdPutStr_P(const PROGMEM char *str)
{
  char ch;

  ch = pgm_read_byte(str++); // read the first character in the string
  while(ch) // repeat until character is NULL
  {
    lcdPutChar(ch); // print it
    ch = pgm_read_byte(str++); // get next character & advance
  }
}

Just for comparison, the normal lcdPutStr would look something like this:

void lcdPutStr(const char *str)
{
  while(*str) // repeat until character is NULL
  {
    lcdPutChar(*str++); // print it & advance
  }
}

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I was trying to implement the code you so graciously provided glitch, and I came up with,

void lcdPutStr_P(const PROGMEM char *str)
{
	unsigned char c;
	
	c = pgm_read_byte(str++);
	
	while(c)
	{
		lcdWriteChar(c);
		c = pgm_read_byte(str++);
	}
}

except the compiler gives a warning saying it is ignoring PROGMEM in the function:

glcd.c:355: warning: '__progmem__' attribute ignored

From what I understand about PROGMEM, I believe you can only specify something as PROGMEM when it is being initialized, not when it is just defined. This makes me believe that as long as the parameter of lcdPutStr_P() is a pointer to flash, the function will work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

yeah that's fine... progmem is just an attribute. I just like to include it, so that it is clear that it is a pointer to flash that the function is expecting. So you can either remove PROGMEM to get rid of the warning. Or if you prefer the informative approach you can do something like

#define FLASHDATA

void lcdPutStr_P(const FLASHDATA char *str)
{
   unsigned char c;
   
   c = pgm_read_byte(str++);
   
   while(c)
   {
      lcdWriteChar(c);
      c = pgm_read_byte(str++);
   }
}

The "FLASHDATA" macro expands to nothing, so it does not affect anything, but it does add the documentation that the function expects a pointer to flash.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If I remember correctly, there are defines in AVR Libc to show it's a char pointer in flash.

Found it:

#define 	PGM_P   const prog_char *
...
typedef char PROGMEM 	prog_char
...

here: http://www.nongnu.org/avr-libc/u...

I personally use these typedefs, so it's clear what happens. For instance:

void lcdPutStr_P(const prog_char *str) 
{
   unsigned char c;
   
   c = pgm_read_byte(str++);
   
   while(c)
   {
      lcdWriteChar(c);
      c = pgm_read_byte(str++);
   }
}

void foo(void)
{
  const prog_char *pChar;
  pChar = &Flashstring;
  lcdPutStr_P(pChar);
  // or the same arguments glitch gave:
  lcdPutStr_P((const PROGMEM char *)pgm_read_word(&movies[i].title)); 
}

Doing it this way, you still can see it's a pointer to flash and you don't get the warning. I don't think one way is better than the other, this is just the way I like to do it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I moved away from the defines & typedefs because they also use PROGMEM in the definition (I assumed that the warning would remain). Honestly I don't remember ever having the warning in my code... but I'm on a slightly older version of GCC, so thought it might be something new, or just that my memory is failing (more likely). Either way, I just tested and indeed using prog_char does not generate any warnings. So that is definitely the way to go. [I should have tested before posting]

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Everything seems to be working properly, and I finally feel like I have a grasp on FLASH data. Thanks Everyone!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello to everybody, I have a similar problem, and eventhough I have read the forum and this topic I could not find a solution.

I have the following array in RAM that would like to move to flash, it's a font:

const unsigned char FONT6x8[97][8] = {
0x06,0x08,0x08,0x00,0x00,0x00,0x00,0x00, // columns, rows, num_bytes_per_char
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // space 0x20
0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, // !
0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00, // "...and so on

And then I have a rutine that takes a char and draw it:

void lcdWriteChar (char c, unsigned short x, unsigned short y, int size,
	unsigned short fColor, unsigned short bColor) {
	extern const unsigned char FONT6x8[97][8];
	extern const unsigned char FONT8x8[97][8];
	extern const unsigned char FONT8x16[97][16];

	int i, j;

	unsigned int nCols;
	unsigned int nRows;
	unsigned int nBytes;
	unsigned char PixelRow;

	unsigned char Mask;


	unsigned short color;
	unsigned char *pFont;
	unsigned char *pChar;

	unsigned char *FontTable[] = { (unsigned char *) FONT6x8,
			(unsigned char *) FONT8x8, (unsigned char *) FONT8x16 };

	// get pointer to the beginning of the selected font table
	pFont = (unsigned char *) FontTable[size];
	// get the nColumns, nRows and nBytes
	nCols = *pFont;
	nRows = *(pFont + 1);
	nBytes = *(pFont + 2);
	// get pointer to the last byte of the desired character
	pChar = pFont + (nBytes * (c - 0x1F)) + nBytes - 1;

	for (i = nRows - 1; i >= 0; i--) {
		lcdMoveTo(x, y + i);

		// copy pixel row from font table and then decrement row
		PixelRow = *pChar--;

		Mask = 0x80;

		for (j = 0; j < nCols; j += 1) {
			if ((PixelRow & Mask) == 0)
				color = bColor;
			else
				color = fColor;
			Mask = Mask >> 1;

			cDisp_CS_LO();
			cDispWrDat((INT8U) (color >> 8), (INT8U) color);
			cDisp_CS_HI();

		}
	}
}

I don't know how to modify the function in order to make it work using Flash memory for the font array. Because the function use several pointers.

Hope somebody can give me a hand.

Thanks!

Ezequiel

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try something like:

#include 

const unsigned char FONT6x8[97][8] PROGMEM = { 
0x06,0x08,0x08,0x00,0x00,0x00,0x00,0x00, // columns, rows, num_bytes_per_char 
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // space 0x20 
0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, // ! 
0x50,0x50,0x50,0x00,0x00,0x00,0x00,0x00, // "...and so on 

   extern const unsigned char FONT6x8[97][8]; 
   extern const unsigned char FONT8x8[97][8]; 
   extern const unsigned char FONT8x16[97][16]; 


unsigned char *FontTable[] PROGMEM = { (unsigned char *) FONT6x8, 
         (unsigned char *) FONT8x8, (unsigned char *) FONT8x16 }; 

void lcdWriteChar (char c, unsigned short x, unsigned short y, int size, 
   unsigned short fColor, unsigned short bColor) { 

   int i, j; 

   unsigned int nCols; 
   unsigned int nRows; 
   unsigned int nBytes; 
   unsigned char PixelRow; 

   unsigned char Mask; 

   unsigned short color; 
   unsigned char *pFont; 
   unsigned char *pChar; 

   // get pointer to the beginning of the selected font table 
   pFont = (unsigned char *) pgm_read_word(&FontTable[size]); 
   // get the nColumns, nRows and nBytes 
   nCols = pgm_read_wrod(pFont); 
   nRows = pgm_read_word(pFont + 2); 
   nBytes = pgm_read_word(pFont + 4); 
   // get pointer to the last byte of the desired character 
   pChar = pFont + (nBytes * (c - 0x1F)) + nBytes - 1; 

   for (i = nRows - 1; i >= 0; i--) { 
      lcdMoveTo(x, y + i); 

      // copy pixel row from font table and then decrement row 
      PixelRow = pgm_read_byte(pChar--); 

      Mask = 0x80; 

      for (j = 0; j < nCols; j += 1) { 
         if ((PixelRow & Mask) == 0) 
            color = bColor; 
         else 
            color = fColor; 
         Mask = Mask >> 1; 

         cDisp_CS_LO(); 
         cDispWrDat((INT8U) (color >> 8), (INT8U) color); 
         cDisp_CS_HI(); 

      } 
   } 
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To match the original this

   nCols = pgm_read_wrod(pFont);
   nRows = pgm_read_word(pFont + 2);
   nBytes = pgm_read_word(pFont + 4);

must be

   nCols = pgm_read_char(pFont);
   nRows = pgm_read_char(pFont + 1);
   nBytes = pgm_read_char(pFont + 2);

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry, I read the type (and hence width) of the target and took those to be 16 bits - hence using the word functions and stepping the pointer in 2 byte increments. Lars is clearly right.

This kind of raises the question of why you are using 16 bit destinations for 8 bit reads?