General question about Menu programming and ROM size.

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

Hi all,

I'm a bit doubtful, if my menu programming is going the right way.
Everything is working fine, but after implementing 10 different screens, I already use ~8500 bytes of Flash.

I have a state machine for the menus and for every menu special button handling which does the right thing for the menu (e.g. scrolling through the settings; switching through the hours minutes, day, month, year position; change password; having a nice startup screen; blablabla yadayadayada...)
The thing is, I'm not finding a "more Flash saving" way to do this, because using an LCD, every screen needs specific button handling and specific position calculation to write to the correct position on the LCD and do the correct value changes to my variables.

Is the amount of Flash which is allready used normal?

Is there a better way to do this?

Which saves a lot of Flash space?

Thanks in advance.
Regards,
Baldrian

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

How are you measuring your flash usage in the first place?

(please don't say "by .hex file size"!)

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

It is my observation too that user interface code is the most biggest part of a project that takes a lot to think, a lot time to implement and a lot of available resources.

Can you post some code to look at?

- Jani

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

Baldrin, we would need to see your code, or at least how you are storing your menus in flash to comment on how you might be able to save space. But as stated, UI stuff can get large very quickly. Also are you sure it is your menus that is bloating your code?

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:
How are you measuring your flash usage in the first place?
(please don't say "by .hex file size"!)

Of course not :D
Program: 10430 bytes (64.0% Full)
(.text + .data + .bootloader)

Data: 249 bytes (24.3% Full)
(.data + .bss + .noinit)

Quote:
It is my observation too that user interface code is the most biggest part of a project that takes a lot to think, a lot time to implement and a lot of available resources.

OK. That calms me down a bit :-)

Quote:
Can you post some code to look at?

Not really. It's not a private project.

Quote:
...or at least how you are storing your menus in flash to comment on how you might be able to save space.

I'm storing strings in flash for each line of the LCD display. I put them in a structure with an array, so that for each menu I have a field with 2-5 strings á 20 characters.

I'm using a state machine to go through the menues.

Pseudocode
void statemachine()
switch(state)
  case: 1
    build menu1()
    handle menu1()
  case: 2
    build menu2()
    handle menu2()
  case: 3
    .
    .
    .

Then in build_menu(), the specific menu strings are copied from flash to the LCD buffer. build_menu() is only executed once, so that a button up/down not leads to build everything again. Only when switching from one menu to antother the specific build_menu() function is called again.

In handle_menu(), the button handling for each menu is implemented. Each one needs it's own, because most menues are different. Some are just scrolling up/down, some are switching through single characters on specific LCD position (like date/time setting screen).

Quote:
Also are you sure it is your menus that is bloating your code?

Yes. I started with 2000something bytes and I'm only implementing the menu. Now I'm at 10430 bytes.

Regards,
Baldrian

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

I would have a closer look at the build_menuX() and handle_menuX() functions. At the moment you apparently have Menu items * 2 functions. I would be surprised if you couldn't use generic versions for most of your menu items, in particular for the build_menuX() functions. I am not saying that you can use generic versions for all menu items, but there should be some way to unify them, in particular for static menus, where the display is not depending on the state of your application. E.g. consider providing the display strings as parameters to the function.

Oh, and you could consider using a lookup table with pointers to functions instead of the long case statement.

Stealing Proteus doesn't make you an engineer.

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

I agree... in all likelyhood the build_menuX() functions are identical, except for the data being displayed. As such I would just make a build_menu() function that takes a pointer to the structure in flash that contains the data to be displayed. [perhaps that is what you're already doing, it is jsut unclear from your pseudocode]

As for space for your data, if you have 20 characters per string & 5 per menu, that's 105 bytes per menu (20 + 1(null terminator) * 5) just for the strings. Add a few more for any pointers and other data you're probably sitting aroung 120 bytes per menu structure. Not sure how many menus you have, but a modest 10 would apready exceed 1K of 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

Quote:
I put them in a structure with an array, so that for each menu I have a field with 2-5 strings á 20 characters.

Do you have a font library?
From the qoute above I gather that you keep using up new bytes in Flash each time you display i.e character "A" instead of having a pointer to "A" in your font library.

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

I've been following this post and now I have a question. I don't want to change the focus of this post, so you can pm me if you know the answer.

Lennart wrote:

Do you have a font library?
From the qoute above I gather that you keep using up new bytes in Flash each time you display i.e character "A" instead of having a pointer to "A" in your font library.

Although I'm not new at working with AVR's, I'm new at anything involving a UI. I don't understand how a font library like this could help. If you're storing 8bit characters or using an 8bit pointer in the place of the character, how are you saving room?

Thanks,
Nathan

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

Nathan,
You made me read OP's post closer.
I was (wrongfully?) assuming that it was a graphic LCD where characters are made up of more than one byte(ASCII-char) and the font library needs to be created by the programmer.

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

Do your menu strings have lots of blanks? How about storing the number of blanks with the 0x80 bit set? I suppose you dont have dozens of strings with the same couple of words over and over so they get saved over and over? Then it would be better to concatenate words from flash to make strings in ram.

Imagecraft compiler user

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

Along the lines of what Bob just suggested, it might be possible to do a variety of packing techniques to save space. If you posted just your menu strings we could suggest some methods that might save considerable space.

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Also do you use floating point (float, double) variables anywhere? That will also bump up the flash usage by a largish amount.

And I assume that you have a 20x4 LCD display, and you seem to be storing full 20 character lines. If there are lot of spaces, it would be beneficial to store only the part of the string that is needed, not just arrays of 20 characters for every line. So basically this means that if you have things like linetext[20] in some struct or array of menus, you have a char* which points to the actual null-terminated string somewhere in flash. This way short strings like "Hello" would take 6 bytes instead of 20 bytes, plus the pointer to flash, 2 bytes.

Can you post an example menu building routine (change the texts if you like)?

- Jani

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

Huffman, LZW (or other dictionary compressor)?

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

I seem to forget the LZW patent has expired - I would have said it can't be used because of the patent.

Anyway, if you need compression, real difference between Huffman and LZW is that you can build the dictionary on the fly with LZW while with Huffman you need to store the dictionary separately. LZW might also be a bit easier to implement, as it can be done in one pass, while best results with Huffman is first to analyze the data for building the best dictionary and then doing the actual encoding.

As we are compressing ASCII text which has 7-bit codes, you could have 8-bit dictionary only. I think LZW in GIF images had 12 bit dictionary. See how it works out. If you need some special symbols with MSB on, you could make remapping tables to map those to unused 7-bit characters and back.

- Jani

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

"st00n01cIF YOU /CAN/CAN'T/WILL/n02CALL\ JOE=
"st01n02cr01THEN YOU CAN REST/EASY/ALL DAY/FULLY="
"st02n02cr02/300/1200/2400n03/4800n03/kABORTn04\BAUD="
"st03d01 IS REALLY FAST!!n02"
"st04cn80ABORTING!"
;=============================
ANOTHER EXAMPLE: button0=up, 1=down, 2=left, 3=right,4=enter

"st00cn00SETUP:/FREQn401/DUTYn402=
"st01cn01FREQ IS:v04n080n181n282n383n402"
"st02cn00FREQ WAS ENTERED"

stxx=present state
nxx =next state, unless something else happens
nbxx =next state when button B pressed
/ =option separator
c=clear display (may be placed in an option field
rxx=recall present option selection from storage location xx, new setting stored in rxx when leaving state
k=keep old option value (don't overwrite rxx)
vxx=display the numerical value in storage location xx
dxx=delay for time interval xx (for pausing, blinking loops, etc)
pyxx move cursor to row y position xx
states >=80 call code routines that do some code function (beep, buzz, turn on motors, etc) In this example, state 80 increases the frequency value

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

"st00n01cIF YOU /CAN/CAN'T/WILL/n02CALL\ JOE=
"st01n02cr01THEN YOU CAN REST/EASY/ALL DAY/FULLY="
"st02n02cr02/300/1200/2400n03/4800n03/kABORTn04\BAUD="
"st03d01 IS REALLY FAST!!n02"
"st04cn80ABORTING!"
;=============================
ANOTHER EXAMPLE: button0=up, 1=down, 2=left, 3=right,4=enter

"st00cn00SETUP:/FREQn401/DUTYn402=
"st01cn01FREQ IS:v04n080n181n282n383n402"
"st02cn00FREQ WAS ENTERED"

stxx=present state
nxx =next state, unless something else happens
nbxx =next state when button B pressed
/ =option separator
c=clear display (may be placed in an option field
rxx=recall present option selection from storage location xx, new setting stored in rxx when leaving state
k=keep old option value (don't overwrite rxx)
vxx=display the numerical value in storage location xx
dxx=delay for time interval xx (for pausing, blinking loops, etc)
pyxx move cursor to row y position xx
states >=80 call code routines that do some code function (beep, buzz, turn on motors, etc) In this example, state 80 increases the frequency value

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

well, that's clear :?

Maybe add a general commentary, text-wise?

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

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

Interesting, that is like a text based script language which the menu engine parses and executes.

You could shave off a few bytes if some of that stuff was in binary, i.e. bytecode instead of text. Like instead of "st01" which takes four bytes, you could use escape codes to insert hex or decimal numbers. Like "st" would be replaced with one byte, say 0x80, and the "01" would be another byte, 0x01, etc..

It would be most irritating to insert/concatenate both single character escape codes and text strings, I have not yet found a reasonable way to do it, if the escape codes are first #defined to be something like 0x80 or \x80. Preprocessor won't replace stuff inside quotes, and in C, I don't know a good way to build/concatenate stuff like I have done in x86 asm or pascal languages:

; asm
message1 db "Hello World",0x0d,0x0a,0x80,0

lf equ 0x0a
cr equ 0x0d
dingdong equ 0x80
null equ 0x00

message2 db "Hello", cr, lf, dingdong, null

(* pascal *)

string s;

s="hello"+' '+"world"+#13+#10+#128#0; 

/* in C */

// all in quotes with string, can't use defines for hex codes
message1 char* ="hello world!\x0d\x0a\x80\x00";
// all characters are separate, very awkward way to build strings like this.. but hex bytes can be defines.
message2 char[] = {'H','e', (..) ,'!',0x0d, 0x0a, 0x80, 0x00};

So any way to combine good sides of both C ways? Maybe with string building macros, which builds the string and finally quotes it?

Quote:

#define PUTINQUOTES(x) #x

- Jani

Edit:

Ah forget it, it was not the OPs code - sorry I was not paying attention.

While at it, I solved my string problems. The C compiler concatenates string literals by itself, so basically the following things are equal:

char *message0="Hello_World!";
char *message1="Hello"   "_"     "World!";
#define SEPARATOR1 "_"
char *message2="Hello" SEPARATOR1 "World!";
#define SEPARATOR2 "\x5F"
char *message3="Hello" SEPARATOR2 "World!";
Last Edited: Fri. Aug 8, 2008 - 09:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Wow, so many answers.

Quote:
would have a closer look at the build_menuX() and handle_menuX() functions. At the moment you apparently have Menu items * 2 functions. I would be surprised if you couldn't use generic versions for most of your menu items, in particular for the build_menuX() functions.

Quote:
I agree... in all likelyhood the build_menuX() functions are identical, except for the data being displayed. As such I would just make a build_menu() function that takes a pointer to the structure in flash that contains the data to be displayed.

Sounds good to me. In the moment I have for each menu a build_menuX() function. I will change this to use pointers to the strings and use one build_menu() function.

Quote:
As for space for your data, if you have 20 characters per string & 5 per menu, that's 105 bytes per menu (20 + 1(null terminator) * 5) just for the strings. Add a few more for any pointers and other data you're probably sitting aroung 120 bytes per menu structure. Not sure how many menus you have, but a modest 10 would apready exceed 1K of Flash.

Holy crap. Now I see my calculation error. Didn't know what I thought when calculating my 2,5k flash for text. With an average of 3 Lines per menu, I get 63 bytes. On 20 menues that would be 1260 bytes. And with ~ 20 languages supported, that will be 25200 bytes just for the strings. :shock:

Quote:

Lennart wrote:
Do you have a font library?

Although I'm not new at working with AVR's, I'm new at anything involving a UI. I don't understand how a font library like this could help. If you're storing 8bit characters or using an 8bit pointer in the place of the character, how are you saving room?


That's what I'm also thinking. Pointers to font library isn't saving Flash :(

Quote:
I was (wrongfully?) assuming that it was a graphic LCD where characters are made up of more than one byte(ASCII-char) and the font library needs to be created by the programmer.

It is a 2line standard LCD display with S6A0069 compatible driver.

Quote:
How about storing the number of blanks with the 0x80 bit set?

What does the 0x80 bit mean?

Quote:
Also do you use floating point (float, double) variables anywhere? That will also bump up the flash usage by a largish amount.

Nope :D

Quote:
And I assume that you have a 20x4 LCD display, and you seem to be storing full 20 character lines. If there are lot of spaces, it would be beneficial to store only the part of the string that is needed, not just arrays of 20 characters for every line. So basically this means that if you have things like linetext[20] in some struct or array of menus, you have a char* which points to the actual null-terminated string somewhere in flash. This way short strings like "Hello" would take 6 bytes instead of 20 bytes, plus the pointer to flash, 2 bytes.

Sounds good. I will try it out.

Quote:
Huffman, LZW (or other dictionary compressor)?

Hmm. I will read about it and see what I can do with it.

@avrcandies
Looks interesting, but what is it?

So many answers and good ideas around here. I love this forum :D

Regards,
Baldrian

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

There has to be a way of using one generic set of menu functions. You seem to have a fairly involved script system - so why can you not implement one script processing function to show and handle each menu?

Personally I'd make a generic menu structure system. Each structure would store the strings (only) in each language, a pointer to the next state, and some sore of encoding (enum?) to indicate what to do when certain actions are taken. That way you aren't storing the same state control data in every single language, instead only using it once.

"0x80" refers to the most significant bit of each character, which is left unused in standard ASCII. The others are suggesting you use that bit to flag special control sequences rather than encoding them in true ASCII.

- Dean :twisted:

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

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

abcminiuser wrote:

Personally I'd make a generic menu structure system. Each structure would store the strings (only) in each language, a pointer to the next state, and some sore of encoding (enum?) to indicate what to do when certain actions are taken. That way you aren't storing the same state control data in every single language, instead only using it once.

For language change, I just need to set a variable to the language and then, the menu functions are handling the specific language. In the moment, there is one handler per menu. Not per language.

abcminiuser wrote:

"0x80" refers to the most significant bit of each character, which is left unused in standard ASCII. The others are suggesting you use that bit to flag special control sequences rather than encoding them in true ASCII.
- Dean :twisted:

Ok. Undertsand.

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

If you were intersted in the possibility of compressing the text then various techniques (and useable source) is given at http://bcl.comli.eu/

But these techniques (often) benefit from compressing a large chunk of text in one go - not a lot of short, individual strings. However the "use 0x80+N for N spaces" idea given by Bob above is effectively a simple Run Length Encoding (RLE)

You could, of course, "fix this in hardware" simply by trading up from the 16K to the 32K part or adding an external storage device of some sort.

Cliff

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

"st00n01cTHIS IS:/A FUN/COMPACT/EASY/\ METHOD="
"st01n01cMAKE ME A/STEAKn03/SALADn04/DRINKn05\ FOR LUNCH=
"st03n08r00COOK IT/MEDIUM/WELL/RARE\ PLEASE="
"st04n08r01WITH /ITALIAN/RANCH/FRENCH\ DRESSING="
"st05n08r02USE PLENTY OF /RUM/VODKAn06/VERMOUTH/WHISKEY/JUICE\IN MY DRINK="
"st06n05VODKA SHORTAGE, TRY AGAIN="

This script is run through a parser that extracts the next state & different nextstates based on button readings. Selections start with the 1st option field, unless the recall (rxx) option is used to show the present setting. When leaving the state, rxx is updated with the latest seletion. k=keep old setting, used for abort/no change selections.
In order to actually do something (other than just show menus) higher numbered action (code) states are called by the parser: n80=beep, n82=open valve, n83=close valve, etc. The rxx values are used to control the action, such as setting the UART cointrol register:
"st23n85r12USE /300/1200/2400/9600/ckABORTn20 \BAUD="
In this example state 23 is maintained until the enter button is pressed, then control is passed to action state 85 (sets up the uart based on value in r12). Since an r reg is used this menu pops up showing the present setting (based on r12). Since most menus have few selections (<16) these can actually refer to a table of storage nibbles. Scroll buttons scroll through thte options. If abort is scrolled the "c" clears the display showing only the word ABORT. If abort is selected "k" keeps the old (premenu) selection & the next state goes back to state 20 instead.

Once you've defined your parser syntax & have it running, you can define & edit your menus easily just by typing them up. You can even get all you menus set up & then add the individual action states later.

"st35n36MAKE ME A /HAM/PEANUT BUTTER/TURKEY\ AND /CHEESE/JELLY/SWISS\ SANDWICH /TODAY/NOW/TUES\="

MAKE ME A HAM AND CHEESE SANDWICH TODAY
MAKE ME A PEANUT BUTTER AND JELLY SANDWICH NOW
MAKE ME A TURKEY AND SWISS SANDWICH TUES

TIME FOR LUNCH
HOYT

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!