File: pgmspace.dox

package info (click to toggle)
avr-libc 1%3A1.6.2.cvs20080610-2
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 14,848 kB
  • ctags: 55,619
  • sloc: ansic: 92,267; asm: 6,692; sh: 4,131; makefile: 2,481; python: 976; pascal: 426; perl: 116
file content (314 lines) | stat: -rw-r--r-- 12,402 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/* Copyright (c) 2007  Eric B. Weddington
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:

   * Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in
     the documentation and/or other materials provided with the
     distribution.
   * Neither the name of the copyright holders nor the names of
     contributors may be used to endorse or promote products derived
     from this software without specific prior written permission.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  POSSIBILITY OF SUCH DAMAGE. */

/* $Id: pgmspace.dox,v 1.3 2007/10/02 13:48:37 arcanum Exp $ */

/** 

\page pgmspace Data in Program Space

\section pgmspace_introduction Introduction

So you have some constant data and you're running out of room to store it?
Many AVRs have limited amount of RAM in which to store data, but may have
more Flash space available. The AVR is a Harvard architecture processor,
where Flash is used for the program, RAM is used for data, and they each
have separate address spaces. It is a challenge to get constant data to be
stored in the Program Space, and to retrieve that data to use it in the
AVR application.

The problem is exacerbated by the fact that the C Language was not designed
for Harvard architectures, it was designed for Von Neumann architectures where
code and data exist in the same address space. This means that any compiler
for a Harvard architecture processor, like the AVR, has to use other means to 
operate with separate address spaces.

Some compilers use non-standard C language keywords, or they extend the standard
syntax in ways that are non-standard. The AVR toolset takes a different 
approach. 

GCC has a special keyword, \c __attribute__ that is used to attach
different attributes to things such as function declarations, variables, and
types. This keyword is followed by an attribute specification in double
parentheses. In AVR GCC, there is a special attribute called \c progmem. This
attribute is use on data declarations, and tells the compiler to place
the data in the Program Memory (Flash).

AVR-Libc provides a simple macro \c PROGMEM that is defined as the attribute
syntax of GCC with the \c progmem attribute. This macro was created as a
convenience to the end user, as we will see below. The \c PROGMEM macro is
defined in the \c <avr/pgmspace.h> system header file.

It is difficult to modify GCC to create new extensions to the C language syntax, 
so instead, avr-libc has created macros to retrieve the data from the Program
Space. These macros are also found in the \c <avr/pgmspace.h> system header 
file.


\section pgmspace_const A Note On const

Many users bring up the idea of using C's keyword \c const as a means of
declaring data to be in Program Space. Doing this would be an abuse of
the intended meaning of the \c const keyword.

\c const is used to tell the compiler that the data is to be "read-only". It
is used to help make it easier for the compiler to make certain transformations,
or to help the compiler check for incorrect usage of those variables.

For example, the const keyword is commonly used in many functions as a modifier 
on the parameter type. This tells the compiler that the function will only use 
the parameter as read-only and will not modify the contents of the parameter 
variable.

\c const was intended for uses such as this, not as a means to identify where 
the data should be stored. If it were used as a means to define data storage, 
then it loses its correct meaning (changes its semantics) in other situations 
such as in the function parameter example.


\section pgmspace_data Storing and Retrieving Data in the Program Space


Let's say you have some global data:

\code
unsigned char mydata[11][10] =
{
	{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
	{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13},
	{0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D},
	{0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27},
	{0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31},
	{0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B},
	{0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45},
	{0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F},
	{0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59},
	{0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63},
	{0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D}
};
\endcode

and later in your code you access this data in a function and store a single
byte into a variable like so:

\code
byte = mydata[i][j];
\endcode

Now you want to store your data in Program Memory. Use the \c PROGMEM macro 
found in \c <avr/pgmspace.h> and put it after the declaration of the variable, 
but before the initializer, like so:

\code
#include <avr/pgmspace.h>
.
.
.
unsigned char mydata[11][10] PROGMEM =
{
	{0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09},
	{0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13},
	{0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D},
	{0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27},
	{0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31},
	{0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B},
	{0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,0x43,0x44,0x45},
	{0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F},
	{0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59},
	{0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,0x60,0x61,0x62,0x63},
	{0x64,0x65,0x66,0x67,0x68,0x69,0x6A,0x6B,0x6C,0x6D}
};
\endcode

That's it! Now your data is in the Program Space. You can compile, link, and 
check the map file to verify that \c mydata is placed in the correct section.

Now that your data resides in the Program Space, your code to access (read)
the data will no longer work. The code that gets generated will retrieve the
data that is located at the address of the \c mydata array, plus offsets 
indexed by the \c i and \c j variables. However, the final address that is
calculated where to the retrieve the data points to the Data Space! Not the
Program Space where the data is actually located. It is likely that you will 
be retrieving some garbage. The problem is that AVR GCC does not intrinsically
know that the data resides in the Program Space.

The solution is fairly simple. The "rule of thumb" for accessing data stored
in the Program Space is to access the data as you normally would (as if the
variable is stored in Data Space), like so:

\code
byte = mydata[i][j];
\endcode

then take the address of the data:

\code
byte = &(mydata[i][j]);
\endcode

then use the appropriate \c pgm_read_* macro, and the address of your data
becomes the parameter to that macro:

\code
byte = pgm_read_byte(&(mydata[i][j]));
\endcode

The \c pgm_read_* macros take an address that points to the Program Space, and
retrieves the data that is stored at that address. This is why you take the 
address of the offset into the array. This address becomes the parameter to the 
macro so it can generate the correct code to retrieve the data from the Program
Space. There are different \c pgm_read_* macros to read different sizes of data 
at the address given. 


\section pgmspace_strings Storing and Retrieving Strings in the Program Space

Now that you can successfully store and retrieve simple data from Program Space
you want to store and retrive strings from Program Space. And specifically
you want to store and array of strings to Program Space. So you start off
with your array, like so:

\code
char *string_table[] = 
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5"
};
\endcode

and then you add your PROGMEM macro to the end of the declaration:

\code
char *string_table[] PROGMEM = 
{
    "String 1",
    "String 2",
    "String 3",
    "String 4",
    "String 5"
};
\endcode

Right? WRONG!

Unfortunately, with GCC attributes, they affect only the declaration that they
are attached to. So in this case, we successfully put the \c string_table 
variable, the array itself, in the Program Space. This DOES NOT put the actual
strings themselves into Program Space. At this point, the strings are still
in the Data Space, which is probably not what you want.

In order to put the strings in Program Space, you have to have explicit 
declarations for each string, and put each string in Program Space:

\code
char string_1[] PROGMEM = "String 1";
char string_2[] PROGMEM = "String 2";
char string_3[] PROGMEM = "String 3";
char string_4[] PROGMEM = "String 4";
char string_5[] PROGMEM = "String 5";
\endcode

Then use the new symbols in your table, like so:

\code
PGM_P string_table[] PROGMEM = 
{
    string_1,
    string_2,
    string_3,
    string_4,
    string_5
};
\endcode

Now this has the effect of putting \c string_table in Program Space, where
\c string_table is an array of pointers to characters (strings), where each
pointer is a pointer to the Program Space, where each string is also stored.

The \c PGM_P type above is also a macro that defined as a pointer to a 
character in the Program Space.

Retrieving the strings are a different matter. You probably don't want to pull
the string out of Program Space, byte by byte, using the \c pgm_read_byte() 
macro. There are other functions declared in the <avr/pgmspace.h> header file
that work with strings that are stored in the Program Space.

For example if you want to copy the string from Program Space to a buffer in
RAM (like an automatic variable inside a function, that is allocated on the 
stack), you can do this:

\code
void foo(void)
{
    char buffer[10];
    
    for (unsigned char i = 0; i < 5; i++)
    {
        strcpy_P(buffer, (PGM_P)pgm_read_word(&(string_table[i])));
        
        // Display buffer on LCD.
    }
    return;
}
\endcode

Here, the \c string_table array is stored in Program Space, so
we access it normally, as if were stored in Data Space, then take the address
of the location we want to access, and use the address as a parameter to
\c pgm_read_word. We use the \c pgm_read_word macro to read the string pointer 
out of the \c string_table array. Remember that a pointer is 16-bits, or word 
size. The \c pgm_read_word macro will return a 16-bit unsigned integer. We then
have to typecast it as a true pointer to program memory, \c PGM_P. This pointer 
is an address in Program Space pointing to the string that we want to 
copy. This pointer is then used as a parameter to the function \c strcpy_P. The 
function \c strcpy_P is just like the regular \c strcpy function, except that 
it copies a string from Program Space (the second parameter) to a buffer in the 
Data Space (the first parameter).

There are many string functions available that work with strings located in
Program Space. All of these special string functions have a suffix of \c _P in
the function name, and are declared in the <avr/pgmspace.h> header file.


\section pgmspace_caveats Caveats

The macros and functions used to retrieve data from the Program Space have
to generate some extra code in order to actually load the data from the
Program Space. This incurs some extra overhead in terms of code space (extra 
opcodes) and execution time. Usually, both the space and time overhead is 
minimal compared to the space savings of putting data in Program Space. But you 
should be aware of this so you can minimize the number of calls within a single 
function that gets the same piece of data from Program Space. It is always
instructive to look at the resulting disassembly from the compiler.



*/