Data in Program Space

From Nutwiki
Revision as of 17:02, 27 October 2016 by Harald (Talk | contribs) (1 revision imported)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

Test Environment

Hardware Comments Nut/OS
4.8.3
Example 1
Nut/OS
4.8.3
Example 2
Nut/OS
4.8.7
Example 1
Nut/OS
4.8.7
Example 2
Ethernut 1.3 H OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 2.1 B OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 3.0 E OK
Binaries
Compiler: ARM-GCC 4.3.3
NO OK
Binaries
Compiler: ARM-GCC 4.3.3
NO

Overview

These examples show the difference between storing data in RAM and program space. The first program declares a string filled with some text and outputs it, then displays the available memory. The second example does almost the same but with the string stored in program space.

This example will only show different results on Harvard architecture boards (AVR) because those boards have separate RAM and program space. Von Neumann architecture boards (ARM, x86) do not have this distinction, they use RAM for the program and for data.

The advantage of storing data in program is that Harvard architecture boards generally don't have too much RAM but comparably huge amounts of flash. That flash is intended for storing the program but can be used for static data as well. Most programs won't come close to using all the flash that is available anyway.

Source Code

Example 1: Data in RAM <source lang="c">

  1. include <dev/board.h>
  2. include <sys/heap.h>
  1. include <stdio.h>
  2. include <io.h>
  3. include <string.h>

char li_text[] =

   "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.";

int main(void) {

   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("Data NOT in program space\n");
   puts(li_text);
   printf("\nAvailable memory: %d Bytes", NutHeapAvailable());
   for (;;);
   return 0;

} </source>

Example 2: Data in program space <source lang="c">

  1. include <dev/board.h>
  2. include <sys/heap.h>
  1. include <stdio.h>
  2. include <io.h>
  3. include <string.h>

char li_text[] PROGMEM =

   "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.";

int main(void) {

   unsigned long baud = 115200;
   NutRegisterDevice(&DEV_DEBUG, 0, 0);
   freopen(DEV_DEBUG_NAME, "w", stdout);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("Data in program space\n");
   puts_P(li_text);
   printf("\nAvailable memory: %d Bytes", NutHeapAvailable());
   for (;;);
   return 0;

} </source>

Details

<source lang="c"> char li_text[] = </source>

This is a normal variable declaration which goes into RAM.

<source lang="c"> char li_text[] PROGMEM = </source>

Although this declaration looks almost the same, the PROGMEM macro tells the compiler to store this variable in program space. Please note that you will have to use special routines to access program space data. Those routines look almost like normal routines just with an appended _P, for example puts_P, memcpy_P, strcpy_P, printf_P and so on.

Using this data is actually pretty easy now.


<source lang="c">

   puts(li_text);

</source>

As you probably already know this line outputs a string literal. The source of the string is RAM here as li_text is a pointer into normal RAM in the first example.

<source lang="c">

   puts_P(li_text);

</source>

Although this looks almost like the line from the first example, it does not pull the string from RAM but program space. Since li_text is a pointer into program memory in the second example, there's no need for conversions. Just make sure you use the correct routine as you will not get errors when using the wrong one but you will get garbage data (whatever happens to be at the point you're reading from) when doing this.

Now if you compare the output of the final <source lang="c">

   printf("\nAvailable memory: %d Bytes", NutHeapAvailable());

</source> from both examples you'll see that the second example uses noticeably less RAM. That's because it pulls the string from flash (program space) and as such doesn't have to use any RAM for it.

You may notice that even in the second example some RAM gets used for something. That's true. A pointer needs RAM. We use a pointer, namely li_text which is NOT located in program space. The string it points to is, though. If you want to, feel free to upgrade the example so it can even retrieve its own pointer from program space. You'll still have to save the pointer value somewhere in RAM but you can make dynamic program space pointers this way.


Output

<source lang="text"> Data NOT in program space

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

Available memory: 30532 Bytes </source>

<source lang="text"> Data in program space

Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.

Available memory: 30688 Bytes </source>

See also