Getting Data out of a PROGMEM struct - w/ pointer

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

EDIT Question clarified in separate post /EDIT
I'm still working with Dean's project Micromenu
https://www.avrfreaks.net/index.p...

I have the bones of the system working but I need to add an item to the menu struct like

// this change adds mode to the menus to relate to the eep parameters.
typedef struct  PROGMEM  {
	const uint8_t   mode;
	void       *Next;
	void       *Previous;
	void       *Parent;
	void       *Sibling;
	FuncPtr_t     SelectFunc;
	FuncPtr_t     EnterFunc;
	const char  Text[];
} Menu_Item_t ;

I modified the MAKE_MENU macro to add the mode

#define MAKE_MENU(Name, mode, Next, Previous, Parent, Sibling, SelectFunc, EnterFunc, Text) \
    extern Menu_Item_t Next;     \
	extern Menu_Item_t Previous; \
	extern Menu_Item_t Parent;   \
	extern Menu_Item_t Sibling;  \
	Menu_Item_t Name = { (uint8_t)mode, (void*)&Next, (void*)&Previous, (void*)&Parent, (void*)&Sibling, (FuncPtr_t)SelectFunc, (FuncPtr_t)EnterFunc, { Text }}

Some of my menus

//        Name      ,mode  ,Next      ,Previous   , Parent,      Sibling,     SelectFunc  , EnterFunc,    Text
MAKE_MENU(M_ROOT    ,0      ,M_1_1     ,NULL_ENTRY , NULL_ENTRY , NULL_ENTRY , click      , click        , "?f?y3?tWelcome"); 
MAKE_MENU(M_1_1     ,1      ,M_1_2     ,NULL_ENTRY , NULL_ENTRY ,NULL_ENTRY , click       , click        , "?y2?l?n?lITEM 1_1");
MAKE_MENU(M_1_2     ,2      ,M_1_3     ,NULL_ENTRY , NULL_ENTRY ,NULL_ENTRY , click    , eep_show      , "?y3?lITEM 1_2");

I dug through the hex file and confirmed that the correct uint8_t values are after the previous text.

How do I get them out to use them!

I have tried numerous things based on Dean's text extraction.

It seems to me that I need something like -

value_of the mode member of the struct.

but since it is progmem and everything is by address I am getting messed up.

Kirk

Last Edited: Wed. Jan 27, 2010 - 06:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

perhaps start at the tutorials forum and read about the PROGMEM attribute there. It was very enlighting for me whan I started to play with FLASH data readout.

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003

It should get you going.

regards

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

This ends up with the expected 0x37 in 'mode':

#include 
#include 
#include 

typedef void (*FuncPtr_t) (void);

typedef struct  PROGMEM  { 
   const uint8_t   mode; 
   void       *Next; 
   void       *Previous; 
   void       *Parent; 
   void       *Sibling; 
   FuncPtr_t     SelectFunc; 
   FuncPtr_t     EnterFunc; 
   const char  Text[]; 
} Menu_Item_t ; 

void foo(void) {}
void bar(void) {}

Menu_Item_t M_ROOT PROGMEM = {
 0x37, NULL, NULL, NULL, (void *)0x1234, foo, bar, "Hello" 
};

volatile uint8_t mode;

int main(void) {
	mode = pgm_read_byte(&M_ROOT.mode);
	while (1) {
		PORTB ^= 0xFF;
	}
}

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

A browser crash caused me to retype my post and I missed putting in some important info. Sorry.

meslomp is correct without Dean's PROGMEM tutorial, I would be totally lost.

   mode = pgm_read_byte(&M_ROOT.mode); 

does work. BUT I failed to make clear my question when I retyped it.
The MicroMenu system uses pointers to refer to the menus.
A SETMENU macro changes the pointer as needed. So instead of &M_ROOT.mode I need (I think) real code for the psuedo code.

x= ValueOf(CurrentMenu_Pntr_ToThePROGMEMStruct.mode)

This is a hard question to even ask in a way that makes sense. I hope this clarifies it.
Thanks
Kirk

Last Edited: Wed. Jan 27, 2010 - 06:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I gave in and looked at Dean's code. I notice for field access he uses:

#define PREVIOUS   *((Menu_Item*)pgm_read_word(&CurrMenuItem->Previous))
#define NEXT       *((Menu_Item*)pgm_read_word(&CurrMenuItem->Next))
#define PARENT     *((Menu_Item*)pgm_read_word(&CurrMenuItem->Parent))
#define SIBLING    *((Menu_Item*)pgm_read_word(&CurrMenuItem->Sibling))
#define ENTERFUNC  *((FuncPtr*)pgm_read_word(&CurrMenuItem->EnterFunc))
#define SELECTFUNC *((FuncPtr*)pgm_read_word(&CurrMenuItem->SelectFunc))

So wouldn't the equivalent for MODE be:

#define MODE   *((uint8_t*)pgm_read_word(&CurrMenuItem->Mode))

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

Quote:

So wouldn't the equivalent for MODE be:
Code:
#define MODE *((uint8_t*)pgm_read_word(&CurrMenuItem->Mode))

Not quite - you want to read out an 8-bit value, not a 16-bit pointer:

#define MODE pgm_read_byte(&CurrMenuItem->Mode)

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Dean, It works!
I thought that I had tried that. I was using the text extraction as an example. I must have messed up the syntax.
I have been parsing this code on paper and have been sure I had it right but the compiled code says otherwise until now.

Thanks to all and to all a good night!
Kirk

PS What will we all do when Dean gets a diploma, a job, a wife and 2.3 kids?

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

Great Kirk! I'm not surprised you messed the syntax - I *still* do it occasionally when using arrays as pointers (which isn't the problem here, but still).

An explanation of how it works. Using the MAKE_MENU macro, you are creating structs with the specified menu contents, which is placed into flash via the PROGMEM attribute. When you use the macro to select the menu, you are setting a global pointer to the current menu item.

When the PREVIOUS/NEXT/etc. macros are used, you are grabbing the address of the menu item to read (indirectly through the global pointer) which is then fed to the appropriate pgm_read_*() macro to retrieve it from flash. The typecast of the result is needed to convert the raw data into the original type - when you read the pointers from flash you get back the pointer as a raw integer, which needs to be cast into a menu pointer for the compiler to be happy.

Your MODE value is only an 8-bit integer, so by following the same process but using pgm_read_byte(), you can read it out directly as an integer for use.

Quote:

PS What will we all do when Dean gets a diploma, a job, a wife and 2.3 kids?

Supporting, maintaining and extending LUFA is already the workload of all the above ;). Still, I'm trying to remain active in the AVRFreaks community.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Since we all love pre-processor magic, take a look at this PROGMEM_GET macro I just whipped up. Given an address, it will retrieve the data from PROGMEM space and cast it to the appropriate type automatically, for 8 and 16 bit data objects. This includes both pointers and integer types, so as long as the destination is of the same type as the PROGMEM item, it will be read and cast correctly. Magic!

#include 
#include 

int main(void);




uint8_t  PROGMEM Test  = 3;
uint16_t PROGMEM Test2 = 6;
int (*Test3)(void) PROGMEM = main;

#define PROGMEM_GET(x)  __builtin_choose_expr(__builtin_types_compatible_p(typeof(*x), uint8_t), \
                         (typeof(*x))pgm_read_byte(x),  \
                         (typeof(*x))pgm_read_word(x))  \

int main(void)
{
	volatile uint8_t  a = PROGMEM_GET(&Test);
	volatile uint16_t b = PROGMEM_GET(&Test2);
	int (*c)(void)      = PROGMEM_GET(&Test3);
}

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:

Since we all love pre-processor magic

Speak for yourself - I abhor it - it can make code maintenance an utter nightmare. Presumably one of the reasons they chose not to allow a pre-procesor in Java?

Someone the other day posted a thread here about why they couldn't use AVR-LibCs bit_is_set() macro on a 16 bit variable (rather than just SFRs). There's about 4 or 5 levels of macro nesting involved in unwinding (manually) the reason for it (there's a * access off a uint8_t* cast pointer). I know you can generate the -E file but that doesn't always help identify where the bit that's giving you a problem is located.

Try working on a big project where the "clever" module authors have wrapped everything in multiple layers of macroisation and trying to understand/maintain it can become a nightmare.

'course I guess it does make life easy for the programmer who can ultimately just invoke a single macro to define a complete menu or whatever. But I'd argue that it does not make the code more "readable".

IMAO.

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

Quote:

Try working on a big project where the "clever" module authors have wrapped everything in multiple layers of macroisation and trying to understand/maintain it can become a nightmare.

Enough people complained about the LUFA (actually, MyUSB at the time) macro magic for events and callbacks that I ended up changing them all back to regular functions. Contrast the old code:

http://code.google.com/p/lufa-li...

With the new code:

http://code.google.com/p/lufa-li...

Or, if you will allow the use of the class drivers:

http://code.google.com/p/lufa-li...

I agree that they can make your life hard - but they can also make your life easy. It's all about how you write them, and what job you're trying to accomplish. Poo-pooing them totally is just as bad as over-using them; LUFA relies on them heavily to abstract out the actual bit-twiddles for each processor, for example.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Dean,

I'd agree that if you deliver a multi-layered macro solution and it simply works and people just use it then the user maybe doesn't need to delve in and work out WHY it works - just trust that it does (a lot of people use Pete Fleury and Pascal Stang library code in the same way).

But, like those libraries in fact, as soon as the user wants to modify, port or fix the code they are then left all at see with some complex plateful of spaghetti they cannot follow. If the code was written "simply" in the first place (God forbid that this might actually involved a bit more typing on the author's part) then the later user/maintainer has an easier job following it. So hats off to your MyUSB->LUFA changes!

This is why I have reservations about beginners even using libraries like Feury/Stang. If it's just a quick way to a solution (like the Arduino libs I guess) then fine but if they are building a maintainable product design on it without understanding the lib code completely they are digging a hole for themselves. In that case they really should be digging out the datasheet and programming the used devices directly themselves. They have more hope of understanding their code in 6 months when they now need to port from mega8 to mega88 or whatever.

Cliff

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

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=89356&start=0&postdays=0&postorder=asc&highlight=
I have a related question that I have posted at this link. It seems this thread has wandered a bit. :-)

[in which case I'll lock this one - Cliff]

Topic locked