Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C Programming for microcontrollers (Joe Pardue, 2005).pdf
Скачиваний:
272
Добавлен:
12.08.2013
Размер:
4.55 Mб
Скачать

Chapter 8: C Pointers and Arrays

Projects

Messenger

Arrays in RAM and ROM

Microcontrollers have limited memory, especially RAM, Random Access Memory. ROM, Read Only Memory, is much cheaper to make, so there is usually a lot more ROM than RAM. The AVR microcontrollers have a type of memory that is somewhat intermediate between RAM and ROM called Flash ROM. It functions like ROM, but can be rewritten using special hardware and software functions.

RAM is like money and beauty, you can never have too much of it, but in microcontrollers you can often have too little: alas, microcontrollers are ugly and poor. C programs require RAM. You can write assembly programs that can be burned into ROM, and run on microcontrollers that have no RAM, but C requires RAM to keep a stack for parameters and return addresses and to store arrays, among other things. [The AVR is a special case in that it has 32 general purpose registers that can be used as RAM for very tiny and carefully written C programs, so it is possible to write C programs for AVR devices with no ‘RAM’, but that’s another story.]

When we define an array of constants we should be able leave it in ROM, since the elements are constants and we won’t need to change them. But our C compiler puts arrays in the data section of RAM. If we have lots of constant data in arrays, say strings, or conversion factor tables, or timing and tone data for songs, we are going to needlessly lose a lot of RAM.

The following is an example of how to store a string and an array in flash ROM, and keep it there:

const char ERROR_YOUFOOBARED[] PROGMEM = "You fouled up beyond repair.\r\0";

The PROGMEM modifier is not C and is specific to the WinAVR compiler. The AVR has special Load Program Memory, LPM, instructions to get data from the

171

Chapter 8: C Pointers and Arrays

flash ROM, but this is not C and needs to be wrapped with some code to make its use C-like, which is what the PROGMEM does. The details get more complex than we want right now so just thank the guys who figured this out for you by sending them some money at: http://www.sourceforge.net/donate.

We send this string to the PC by defining a sendFString function:

// Send a string located in Flash ROM void sendFString(const char *pFlashStr)

{

uint8_t i;

//The 'for' logic terminates if the byte is '\0' or if i = 60.

//'\0' is 'null' and terminates C strings

//The 60 prevents too much overrun if we get a bad pointer

//and it limits the string size

for (i = 0; pgm_read_byte(&pFlashStr[i]) && i < 60; i++)

{

sendChar(pgm_read_byte(&pFlashStr[i]));

}

}

The function takes a constant character pointer as an argument. We can send a string as follows:

sendFString(&ERROR_YOUFOOBARED[0]);

which explicitly shows that we are sending the address of the first element in the array. Or we could use the simpler:

sendFString(ERROR_YOUFOOBARED);

which is exactly the same thing since the ERROR_YOUFOOBARED is a constant character pointer to the so-named array. (Aren’t you glad I didn’t say ‘eponymous array’?).

Using an array of pointers to arrays.

The first time I saw an array of pointers to arrays I thought somebody was either putting me on or trying to obfuscate the code for job security. But I’ve learned that, complex as it sounds, it’s actually a useful programming technique.

172

Chapter 8: C Pointers and Arrays

Let’s define a set of arrays:

const char ERROR_YOUFOOBARED[] PROGMEM = "You fouled up beyond repair.\r\0";

const char ERROR_SNAFUED[] PROGMEM = "Situation normal, all fouled up.\r\0";

const char ERROR_STOPTHEMADNESS[] PROGMEM = "Stop the madness!\r\0"; const char ERROR_WHERE[] PROGMEM = "Where did you learn to program?\r\0"; const char ERROR_RTFM[] PROGMEM = "Read the freaking manual!\r\0";

And then we define an array of pointers to arrays, initializing it with… pointers to the arrays:

const char *ERROR_TBL[] = { ERROR_YOUFOOBARED, ERROR_SNAFUED, \ ERROR_STOPTHEMADNESS, ERROR_WHERE, ERROR_RTFM };

Now Let’s specify that we write a program that begins by outputting the following to HyperTerminal;

Enter a 0 for error message: You fouled up beyond repair. Enter a 1 for error message: Situation normal, all fouled up. Enter a 2 for error message: Stop the madness!

Enter a 3 for error message: Where did you learn to program? Enter a 4 for error message: Read the freaking manual!

In the software we store these string arrays in addition to the error arrays:

const char ENTER[] PROGMEM = "Enter a ";

const char FOR[] PROGMEM = " for error message: "; char c = '0';

Then we send the lot to HyperTerminal with the following loop:

char c = '0';

for(int i = 0; i < 5; i++)

{

sendFString(ENTER); sendChar(c + i); sendFString(FOR); sendFString(ERROR_TBL[i]);

}

HyperTerminal first receives the ENTER array: ‘Enter a ‘. Next we sent the character ‘0’ + i, (this allows us to sequentially send 0 to 4 in this loop). Then we

173

Chapter 8: C Pointers and Arrays

send the FOR message. And finally we send the pointer to the string array stored in the ERROR_TBL in the i position. Okay, this is a bit complex so Let’s write some code to show what we’re talking about.

The messenger software.

We are going to upgrade the PC_Comm software so we can use this messenger stuff in later code. This will save us the RAM that has up to now been wasted on constant strings. (Note – don’t use the old PC_Comm code after this).

Create a new directory, Messenger, and copy from the PC_Comm directory the .c and .h files and the makefile.

In Programmers Notepad create a new file Messages.h:

//Messages.h

//identify yourself specifically

const char TALKING_TO[] PROGMEM = "\r\rYou are talking to the \0"; const char WHO_DEMO[] PROGMEM = "'Messenger' demo.\r\r\0";

// bad command

const char BAD_COMMAND1[] PROGMEM = "\rYou sent: '\0";

const char BAD_COMMAND2[] PROGMEM = "' - I don't understand.\r\0";

const char ENTER[] PROGMEM = "Enter a ";

const char FOR[] PROGMEM = " for error message: ";

const char ERROR_YOUFOOBARED[] PROGMEM = "You fouled up beyond repair.\r\0";

const char ERROR_SNAFUED[] PROGMEM = "Situation normal, all fouled up.\r\0";

const char ERROR_STOPTHEMADNESS[] PROGMEM = "Stop the madness!\r\0"; const char ERROR_WHERE[] PROGMEM = "Where did you learn to program?\r\0"; const char ERROR_RTFM[] PROGMEM = "Read the freaking manual!\r\0";

const char *ERROR_TBL[] = { ERROR_YOUFOOBARED, ERROR_SNAFUED, ERROR_STOPTHEMADNESS, ERROR_WHERE, ERROR_RTFM };

and save it in the Messenger directory.

Add to the PC_Comm.h file:

#include <avr/pgmspace.h>

174

Chapter 8: C Pointers and Arrays

void sendFString(const char *);

Add to the PC_Comm.c file:

// Send a string located in Flash ROM void sendFString(const char *pFlashStr)

{

uint8_t i;

//The 'for' logic terminates if the byte is '\0' or if i = 60.

//'\0' is 'null' and terminates C strings

//The 60 prevents too much overrun if we get a bad pointer

//and it limits the string size

for (i = 0; pgm_read_byte(&pFlashStr[i]) && i < 60; i++)

{

sendChar(pgm_read_byte(&pFlashStr[i]));

}

}

Change Demonstrator.h to:

// Demonstrator.h Messenger version

void initializer(void); void parseInput(char *); void showMessage(char);

Change Demonstrator.c to:

// Demonstrator.c Messenger version

#include "PC_Comm.h" #include "Messages.h"

void initializer()

{

//Calibrate the oscillator: OSCCAL_calibration();

//Initialize the USART USARTinit();

//Display instructions on PC sendFString(TALKING_TO); sendFString(WHO_DEMO);

char c = '0';

175

Chapter 8: C Pointers and Arrays

for(int i = 0; i < 5; i++)

{

sendFString(ENTER); sendChar(c + i); sendFString(FOR); sendFString(ERROR_TBL[i]);

}

}

void parseInput(char s[])

{

if( (s[0] <= '4') && ( s[0] >= '0') ) // 5 error messages

{

showMessage(s[0]);

}

else

{

// parse first character switch (s[0])

{

case 'd': if((s[1=='e')&&(s[2]=='m')&&(s[3]=='o')&&(s[4]=='?') )

sendFString(TALKING_TO); sendFString(WHO_DEMO); break;

default: sendFString(BAD_COMMAND1); sendChar(s[0]); sendFString(BAD_COMMAND2);

break;

}

s[0] = '\0';

}

}

void showMessage(char mess)

{

int num = atoi(&mess);

sendFString(ERROR_TBL[num]);

// Send the song title to the PC

}

Compile, load to the Butterfly, and in HyperTerminal you will see:

You are talking to the 'Messenger' demo.

176

Chapter 8: C Pointers and Arrays

Enter a 0 for error message: You fouled up beyond repair. Enter a 1 for error message: Situation normal, all fouled up. Enter a 2 for error message: Stop the madness!

Enter a 3 for error message: Where did you learn to program? Enter a 4 for error message: Read the freaking manual!

Test it as follows:

2

Stop the madness! 0

You fouled up beyond repair. 1

Situation normal, all fouled up. 3

Where did you learn to program? 4

Read the freaking manual! 5

You sent: '5' - I don't understand.

Let me add a postscript to this by saying again that this memory use is not about C, it is about the AVR microcontroller and how to conserve limited RAM by using Flash ROM. It is important to keep C and the microcontroller specific ‘fixes’ separate in your head, because what we just learned works great on the AVR using the WinAVR compiler, but won’t work using other compilers for the AVR, and is completely useless for other microcontrollers.

177