Go Down

Topic: "Load mapping" SRAM memory use in Arduino? (Read 3 times) previous topic - next topic

AWOL

Embedded is a strange discipline.
You can have von Neumann architecture, with ROM and RAM but in the same address space (old style microprocessors), or Harvard, with different spaces for both (classic microcontroller)
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

GoForSmoke

On the PROGMEM page it does say:

Quote
Using PROGMEM is also a two-step procedure. After getting the data into Flash memory, it requires special methods (functions), also defined in the pgmspace.h library, to read the data from program memory back into SRAM, so we can do something useful with it.


So far every post I've seen about having problems using PROGMEM did the setup but not the special methods.

I find it harder to express logic in English than in Code.
Sometimes an example says more than many times as many words.

gravelbar

Thanks very much wildbill.

Most of the other stuff ya'll are talking about is way above my pay grade.

And please remember, friends, we're sending a few dozen of these out.  Code needs to be bullet-proof.  All our hardware, too -- it really sucks when users have problems, and so far this is rare.

We're not like Microsoft, etc., we don't like users testing our gear; we try to do that, and let them just teach.

Thanks for you help, we need it, it's all for education, very worthy cause, I support six employees and haven't made a dime myself in five years, but almost there!

:-)


CraigKC

#18
Nov 14, 2011, 07:06 pm Last Edit: Nov 14, 2011, 07:12 pm by CraigKC Reason: 1
I recently implemented some code using a lookup table that should be exactly what you're looking for.  You're on the right track as PROGMEM stores the table to your micro's flash instead of SRAM which is exactly what you need to do and is almost certainly what your friend was trying to say.  I generated this table just using Excel to create the numeric columns and then a column between them with commas so that it pasted into code perfectly with no hand-writing of this really boring linear table.

To explain what I have below to help you make use of the example I'm doing some LED work where I need to quickly find a hue of the rainbow from a table instead of doing the expensive math formula or worse, depending on your other SRAM requirements, storing the table in SRAM.  You'll see my table is essentially just a linear formula but saved me lots of SRAM and in my case, improved speed dramatically instead of math formulas to calculate the values.

First, make sure you include the necessary header file:
Code: [Select]

#include <avr/pgmspace.h>  //used for PROGMEM


Then declare your table in your .pde:
Code: [Select]

//192 RGB values (576 bytes) in a linear fading formula in the form of R,G,B,R

prog_uchar tblRainbow[] PROGMEM = {

255 , 0 , 0 ,
251 , 4 , 0 ,
247 , 8 , 0 ,
243 , 12 , 0 ,
239 , 16 , 0 ,
235 , 20 , 0 ,
231 , 24 , 0 ,
227 , 28 , 0 ,
223 , 32 , 0 ,
219 , 36 , 0 ,
215 , 40 , 0 ,
211 , 44 , 0 ,
207 , 48 , 0 ,
203 , 52 , 0 ,
199 , 56 , 0 ,
195 , 60 , 0 ,
191 , 64 , 0 ,
187 , 68 , 0 ,
183 , 72 , 0 ,
179 , 76 , 0 ,
175 , 80 , 0 ,
171 , 84 , 0 ,
167 , 88 , 0 ,
163 , 92 , 0 ,
159 , 96 , 0 ,
155 , 100 , 0 ,
151 , 104 , 0 ,
147 , 108 , 0 ,
143 , 112 , 0 ,
139 , 116 , 0 ,
135 , 120 , 0 ,
131 , 124 , 0 ,
127 , 128 , 0 ,
123 , 132 , 0 ,
119 , 136 , 0 ,
115 , 140 , 0 ,
111 , 144 , 0 ,
107 , 148 , 0 ,
103 , 152 , 0 ,
99 , 156 , 0 ,
95 , 160 , 0 ,
91 , 164 , 0 ,
87 , 168 , 0 ,
83 , 172 , 0 ,
79 , 176 , 0 ,
75 , 180 , 0 ,
71 , 184 , 0 ,
67 , 188 , 0 ,
63 , 192 , 0 ,
59 , 196 , 0 ,
55 , 200 , 0 ,
51 , 204 , 0 ,
47 , 208 , 0 ,
43 , 212 , 0 ,
39 , 216 , 0 ,
35 , 220 , 0 ,
31 , 224 , 0 ,
27 , 228 , 0 ,
23 , 232 , 0 ,
19 , 236 , 0 ,
15 , 240 , 0 ,
11 , 244 , 0 ,
7 , 248 , 0 ,
3 , 252 , 0 ,
0 , 255 , 0 ,
0 , 251 , 4 ,
0 , 247 , 8 ,
0 , 243 , 12 ,
0 , 239 , 16 ,
0 , 235 , 20 ,
0 , 231 , 24 ,
0 , 227 , 28 ,
0 , 223 , 32 ,
0 , 219 , 36 ,
0 , 215 , 40 ,
0 , 211 , 44 ,
0 , 207 , 48 ,
0 , 203 , 52 ,
0 , 199 , 56 ,
0 , 195 , 60 ,
0 , 191 , 64 ,
0 , 187 , 68 ,
0 , 183 , 72 ,
0 , 179 , 76 ,
0 , 175 , 80 ,
0 , 171 , 84 ,
0 , 167 , 88 ,
0 , 163 , 92 ,
0 , 159 , 96 ,
0 , 155 , 100 ,
0 , 151 , 104 ,
0 , 147 , 108 ,
0 , 143 , 112 ,
0 , 139 , 116 ,
0 , 135 , 120 ,
0 , 131 , 124 ,
0 , 127 , 128 ,
0 , 123 , 132 ,
0 , 119 , 136 ,
0 , 115 , 140 ,
0 , 111 , 144 ,
0 , 107 , 148 ,
0 , 103 , 152 ,
0 , 99 , 156 ,
0 , 95 , 160 ,
0 , 91 , 164 ,
0 , 87 , 168 ,
0 , 83 , 172 ,
0 , 79 , 176 ,
0 , 75 , 180 ,
0 , 71 , 184 ,
0 , 67 , 188 ,
0 , 63 , 192 ,
0 , 59 , 196 ,
0 , 55 , 200 ,
0 , 51 , 204 ,
0 , 47 , 208 ,
0 , 43 , 212 ,
0 , 39 , 216 ,
0 , 35 , 220 ,
0 , 31 , 224 ,
0 , 27 , 228 ,
0 , 23 , 232 ,
0 , 19 , 236 ,
0 , 15 , 240 ,
0 , 11 , 244 ,
0 , 7 , 248 ,
0 , 3 , 252 ,
0 , 0 , 255 ,
4 , 0 , 251 ,
8 , 0 , 247 ,
12 , 0 , 243 ,
16 , 0 , 239 ,
20 , 0 , 235 ,
24 , 0 , 231 ,
28 , 0 , 227 ,
32 , 0 , 223 ,
36 , 0 , 219 ,
40 , 0 , 215 ,
44 , 0 , 211 ,
48 , 0 , 207 ,
52 , 0 , 203 ,
56 , 0 , 199 ,
60 , 0 , 195 ,
64 , 0 , 191 ,
68 , 0 , 187 ,
72 , 0 , 183 ,
76 , 0 , 179 ,
80 , 0 , 175 ,
84 , 0 , 171 ,
88 , 0 , 167 ,
92 , 0 , 163 ,
96 , 0 , 159 ,
100 , 0 , 155 ,
104 , 0 , 151 ,
108 , 0 , 147 ,
112 , 0 , 143 ,
116 , 0 , 139 ,
120 , 0 , 135 ,
124 , 0 , 131 ,
128 , 0 , 127 ,
132 , 0 , 123 ,
136 , 0 , 119 ,
140 , 0 , 115 ,
144 , 0 , 111 ,
148 , 0 , 107 ,
152 , 0 , 103 ,
156 , 0 , 99 ,
160 , 0 , 95 ,
164 , 0 , 91 ,
168 , 0 , 87 ,
172 , 0 , 83 ,
176 , 0 , 79 ,
180 , 0 , 75 ,
184 , 0 , 71 ,
188 , 0 , 67 ,
192 , 0 , 63 ,
196 , 0 , 59 ,
200 , 0 , 55 ,
204 , 0 , 51 ,
208 , 0 , 47 ,
212 , 0 , 43 ,
216 , 0 , 39 ,
220 , 0 , 35 ,
224 , 0 , 31 ,
228 , 0 , 27 ,
232 , 0 , 23 ,
236 , 0 , 19 ,
240 , 0 , 15 ,
244 , 0 , 11 ,
248 , 0 , 7 ,
252 , 0 , 3
};


Then write the small amount of extra code to retrieve the value from the table:
Code: [Select]

     byte colorNum = 96;
     pgm_read_byte_near(tblRainbow + (colorNum *3)),
     pgm_read_byte_near(tblRainbow + (colorNum *3) +1),
     pgm_read_byte_near(tblRainbow + (colorNum *3) +2)


When I want to pull out an RGB color (for example, color number 96, directly in the middle of the rainbow) I use the following code to pull bytes 288, 289, and 290 from the 576 byte table as shown above.

I hope that gives you a practical example of how to use a lookup table with PROGMEM.  If you need more specific help, PM me and I'll look over your exact usage in more detail and tailor a better example/solution for you.

CraigKC

I decided to take a small break and look over your code a little more and have some more specific advice for you.

First, absolutely implement the PROGMEM example I gave above for your hydrograph tables.  It will free up 328 bytes, or 20%, of your SRAM right off the top.  This might be enough for you right now but if you want to understand a little more or further optimize there's more you can do.

Second, you should also know that each of your literal strings also eats 1 byte of RAM for each character.  For example,. the hydrograph names ("8m flashy", "17m triangle", etc) can also be placed in PROGMEM and will free up 67 bytes on their own.  You also use 1 byte of SRAM for each character that's only used in a single Serial.print() or lcd.print() including spaces also.  For example, Serial.print("   pwmCal   ") eats 11 bytes.  Hunt down these and either eliminate, shorten, or move to PROGMEM.

Additionally it looks like static uint8_t Qvals[256] can also be moved to PROGMEM as it's read-only and essentially a lookup table as well.  Is that correct?  If so you're well on your way to cutting your application's SRAM usage by 50% if you can move this as well.

Last thing of mention is that you only want to store things in PROGMEM that are read-only.  Writing to flash does have a limited number of times it can be reliably performed (1,000 writes to any given address on the ATMega family I believe) so keep that in mind before you optimize too deeply.  Reads are unlimited.

James C4S

#20
Nov 14, 2011, 08:03 pm Last Edit: Nov 14, 2011, 08:05 pm by James C4S Reason: 1

Last thing of mention is that you only want to store things in PROGMEM that are read-only.  Writing to flash does have a limited number of times it can be reliably performed (1,000 writes to any given address on the ATMega family I believe) so keep that in mind before you optimize too deeply.  Reads are unlimited.

PROGMEM (aka FLASH) is rated at 10,000 write/erase cycles.  This is a pretty hard limit to hit in a reasonable amount of time since it is directly tied to the user uploading new code.

The EEPROM (256-1kbytes depending on the ATmega) is well over 100,000 cycles.
Capacitor Expert By Day, Enginerd by night.  ||  Personal Blog: www.baldengineer.com  || Electronics Tutorials for Beginners:  www.addohms.com

CraigKC


PROGMEM (aka FLASH) is rated at 10,000 write/erase cycles.  This is a pretty hard limit to hit in a reasonable amount of time since it is directly tied to the user uploading new code.


Just looked it up and you're right -- the flash supports 10,000 write/erase cycles and not 1,000.  I wanted to mention it since the application sounded a bit 'industrial' and he's mentioned requiring it to be bulletproof but I absolutely agree especially given your fact correction.  If new code were uploaded daily (or written via some hugely custom bootloader) to incorporate new water flow models or something it would still last 27 years.

wildbill

More playing around with progmem - adaptation of a suggestion in the Exhibition thread:
Code: [Select]

// Populate the lookup table for flow rate based on motor PWM setting.
PROGMEM prog_uchar Qvals[] = {3,4,7,10,13,15,18,21,23,26,29,31,33,36,38,40,43,45,47,49,51,53,55,57,59,61,63,64,66,68,69,
                          71,72,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,89,90,91,91,92,92,93,93,93,94,94,94,94,
                          95,95,95,95,96,96,96,97,98,98,99,100,100,101,100,101,102,102,103,104,105,105,106,107,107,
                          108,109,109,110,111,112,112,113,114,114,115,116,116,117,118,119,119,120,121,121,122,123,
                          123,124,125,126,126,127,128,128,129,130,130,131,132,133,133,134,135,135,136,137,137,138,
                          139,140,140,141,142,142,143,144,144,145,146,147,147,148,149,149,150,151,151,152,153,154,
                          154,155,156,156,157,158,158,159,160,161,161,162,163,163,164,165,165,166,167,168,168,169,
                          170,170,171,172,172,173,174,175,175,176,177,177,178,179,179,179}  ;   // fill with calibration curve numbers, 70 through 256 (?)

int Get_ml_per_sec_by_pwm(uint8_t pwm)
{
if(pwm<pwmCal)
  return 0;
else
  return pgm_read_byte_near(Qvals+pwm-pwmCal);
}

gravelbar


wildbill

One more suggestion - you might want to consider saving the pwmCal calibration in the eeprom - I assume it doesn't change much between each use of the system.

Go Up