NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Array limitations

October 15, 2012
by jmuthe
jmuthe's Avatar

Hello, I was trying to write some test programs with the arrays and I notice that there is a limit to the amount of variables that could be stored and retrieved from an array. I wrote a program that stores variables into an array and then displays them one by one on an LCD. I wanted to see how many numbers I could put in the array and it was 440 variables. When I tried to store 441 variables the program didn't work. Is the reason for this limitation a programming error or is it simply because of the limited memory space on the ATmega168 microcontroller that came with the Nerdkit. If so then how many variables could I store in the array of the Atmel ATmega328P (Microcontroller).

October 16, 2012
by JimFrederickson
JimFrederickson's Avatar

I don't think anyone will be able to give you an answer if there is no code?

Without seeing what is defined, and how it was defined the question cannot really be answered. (As well as surrounding code, as to it's influences as well...)

In Regards to the "proportionality" of the two Microcontrollers...

The 328 has twice the available RAM. (Which is likely where "your variables" are being stored.)

Microcontrollers are by definition "Systems on a Chip" with "Limited Resources".

It is likely the "program didn't work" result you are seeing is not simply a "limitation", but is the result of the data section and the stack section colliding. (The "data section" grows low to high, and the "stack section" grows from high to low within the same RAM Memory.)

Remember that "memory sections colliding, nearly doomed the pair of Mars Rovers a few years ago.

The C Compiler relies you, the programmer, to really make sure that these two sections NEVER REALLY MEET!

October 16, 2012
by jmuthe
jmuthe's Avatar
#define F_CPU 14745600

#include <stdio.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"

int main ()
{
lcd_init();
lcd_home();
uint16_t   a[441] = { 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, 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,
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,
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,
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,

};
uint16_t  i;
for (i=0; i<441; i++)
{
lcd_line_one(); 
lcd_write_int16(i);
lcd_write_string(PSTR("  "));
lcd_line_two(); 
lcd_write_int16(a[i]);
lcd_write_string(PSTR("  "));
delay_ms(50);
}
return 0;
}
October 16, 2012
by jmuthe
jmuthe's Avatar

That last post was the code I used> it works fine as it is but if I add one more number in the array, it won't work.

October 16, 2012
by pcbolt
pcbolt's Avatar

jmuthe -

Since all your array values are less than 255, you should be able to increase your array size by changing line 17 to:

uint8_t a[880] = ....

The initial array had 2-byte (16-bit) elements so a 1-btye (8-bit) array should double your array size. This would mean out of the 1024 bytes of RAM, 880 is used for the array and the rest is being used by the code execution stack (144 bytes) like Jim said. If you switch to the 328p with 2048 bytes of RAM and the same stack size is used, you should get more than double the array size (1904 bytes) instead of 1760. For 16-bit variables, this would mean an array of 952 elements.

October 16, 2012
by JimFrederickson
JimFrederickson's Avatar
    ATMega168 has 1KB of RAM.
    ATMega328 has 2KB of RAM.
    ATMega644 which has 4KB of RAM.

(you could also add RAM is necessary too.)

Understand though that "by definition" a Microcontroller is not a general purpose computer.
(Although the ATMega328 does have more Flash/ROM and RAM than my first "General Purpose Computer"!)

So the int_16/word array that you have created with 441 entries is using 882 bytes of the 1KB of RAM.
So there is, as most, there is 142 bytes of RAM that are not already in use directly by your program.

The C compiler has some RAM overhead, for the Compiler Libraries that are being used there is some additional RAM overhead for temporary variables, buffers, and other uses.

RAM in the Microntroller is a precious commodity and has to be used very judiciously.

You can, as mentioned, reduce the size of the memory footprint of the array by changing it to an int_8/byte array which should essentially double your maximum array size.

Another option, used quite often and effectively, is to store CONSTANT ARRAYS/DATA into the Program Memory/Flash on the Microcontroller.

Using this wisely you can get significant increases in capacity.

You can even use Flash or EEPROM to store values that don't change that often.

A log of temperature measurements for instance.) Although this is NOT a process that can be done directly, it is a process that can work well.

    ATMega168 has 16KB of Flash
    ATMega328 has 32KB of Flash
    ATMega644 has 64KB of Flash

    AtMega168 has 512 Bytes of EEPROM
    ATMega328 has 1KB of EEPROM
    ATMega644 has 2KB of EEPROM

There have been many posts here regarding how to do this. (I had posted a pretty indepth post some time ago on this.)

    Let's say you have some global data:

    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}
    };

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

    byte = mydata[i][j];

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

    #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}
    };

    That's it! Now your data is in the Program Space. You can compile, link, and check the map file to verify that 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 mydata array, plus offsets indexed by the i and 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.

This was taken from "http://www.nongnu.org" as an example.

You can use the following:

avr-size -A myuart.o

To get and idea of how space is being used in your code.

    myuart.o  :
    section           size   addr
    .text             1068      0
    .data                0      0
    .bss                 0      0
    .debug_abbrev      448      0
    .debug_info       1485      0
    .debug_line       1279      0
    .progmem.data       17      0
    .debug_frame       288      0
    .debug_loc        1030      0
    .debug_pubnames    417      0
    .debug_aranges      32      0
    .debug_str         708      0
    Total             6772

I just used an "example" from my code.

The code is myuart.o.

"*.o" are the object files that the Compiler Creates.

You can do that command with ANY of the Object File in your Project to get a better understanding as well.

Hopefully this helps, at little.

Post a Reply

Please log in to post a reply.

Did you know that two resistors can be used to make a voltage divider? Learn more...