Teensy 3 w/ large-ish array freezing

Status
Not open for further replies.

brietech

New member
I'm attempting to build a fairly simple data logger with a teensy 3.0. It polls an i2c gyro, magnetometer and accelerometer several times a second, and stores the x/y/z values as int16_t for all 3 sensors in an array in ram (with each entry = 3*3*2 = 18 bytes). I have a for-loop that polls the sensors and writes to an incrementing address in my array. For some reason, I am reliably seeing the program lock up after it writes to index 308 in the array.

I used the code from here: http://forum.pjrc.com/threads/23256-Get-Free-Memory-for-Teensy-3-0 to find the free SRAM while I'm running, and it's showing 6756 bytes, which should be plenty (if I make my array 308 entries, it works fine. If I make it anything greater than 308, it locks up). Here is the output at compile time:

Binary sketch size: 16,544 bytes (of a 131,072 byte maximum)
Estimated memory use: 9,248 bytes (of a 16,384 byte maximum)

How much free ram are we supposed to leave? Does anyone have any further suggestions for debugging? I should be able to get 500-600 total samples, given the free RAM I currently have.
 
RAM is used in 3 ways: static, stack and heap.

The memory estimate reports only static usage, which is all your global variables and any local static variables. These are the variables that exist for the entire life of your program. The compiler places all this stuff at the beginning of the RAM. That's why it's possible to know the size before your program runs.

Stack usage is mostly local variables. If you use things like "char buf[1024];" as local variables, you'll consume a lot of the RAM. Generally it's a good practice to limit the size of local variables. Function calls use the stack too, to save the return address and copies of registers which the called function overwrites. Interrupts also use the stack for their temporary data. The stack begins at the end of RAM and grows downwards.

Heap usage is memory allocated by malloc(), or C++ "new", which just uses malloc. Heap memory begins right after all the static variables and malloc keeps growing the usage upward towards the downward-growing stack. When you free memory, malloc tries to reuse the free holes left, but whether it can fulfill future requests depends on your usage patterns and requested allocation sizes. If you're using the String object in Arduino, it internally uses malloc() to get the memory which holds a copy of your text.

So 6756 free bytes is quite a lot if you don't use malloc(), new or String, and you don't have arrays as local variables.

Another memory hog is printf(). Investigating how much it really uses is still on my to-do list. But it can consume a surprising amount of stack and maybe heap memory. The Arduino print() and println() functions are fairly efficient, but of course it takes several of them to do what one printf call might.
 
So, I'm not using any local variables, or any printf() statements (only print, and println, to try to help in debugging). If I allocate a 600 entry array (of my 18-byte struct), I can start writing from the bottom, and I get up to 308 before it locks up. I can also start writing from the top, and I get down to 310 before it freezes. I can also start writing from the bottom, and explicitly exclude writes to entries 309 and 310, and it works fine (giving me 598 entries).

Reading back entries 309 and 310 after the fact shows that they both have all 0s in them. Is there some kind of weird magic address right in the middle of the static memory space?? This seems like a bug.
 
This is a simplified example that exhibits the same behavior. It runs until it gets to entry 312, and then it locks up.


Code:
#include <Arduino.h>

int led = 13;

int i, j, k;
struct measurement {
              int16_t ax;
              int16_t ay;
              int16_t az;
              int16_t gx;
              int16_t gy;
              int16_t gz;
              int16_t mx;
              int16_t my;
              int16_t mz;
};

measurement data_array[600];

measurement tmp_data;
long tmp_x, tmp_y, tmp_z;

uint32_t FreeRam(){ // for Teensy 3.0
    uint32_t stackTop;
    uint32_t heapTop;
    // current position of the stack.
    stackTop = (uint32_t) &stackTop;
    // current position of heap.
    void* hTop = malloc(1);
    heapTop = (uint32_t) hTop;
    free(hTop);
    // The difference is the free, available ram.
    return stackTop - heapTop;
}

void setup() {
  Serial.begin(9600);
  pinMode(led, OUTPUT);
}

void loop() { 
  for(i=0; i<600; i=i+1)
  { 
    uint32_t mem = FreeRam();
    Serial.print("SRAM:");
    Serial.println(mem);
    digitalWrite(led,HIGH);
    delay(25);
    digitalWrite(led,LOW);
    //Accelerometer Data
    tmp_x = 100;
    tmp_y = 100;
    tmp_z = 100;
    tmp_data.ax = (int16_t)(tmp_x);
    tmp_data.ay = (int16_t)(tmp_y);
    tmp_data.az = (int16_t)(tmp_z);
    //Magnetometer Data
    tmp_data.mx = (int16_t)(tmp_x);
    tmp_data.my = (int16_t)(tmp_y);
    tmp_data.mz = (int16_t)(tmp_z);
    //Gyroscope Data
    tmp_data.gx = (int16_t)(tmp_x);
    tmp_data.gy = (int16_t)(tmp_y);
    tmp_data.gz = (int16_t)(tmp_z);
    Serial.println(i);
    data_array[i] = tmp_data;
    delay(10);
  }
 
I don't know what is going on but the problem is obviously this statement:
Code:
    data_array[i] = tmp_data;

I changed it to use memcpy and it still blows up at the same place.
Then I changed it to explicitly copy each element of the structure using this:
Code:
    data_array[i].ax = tmp_data.ax;
    data_array[i].ay = tmp_data.ay;
    data_array[i].az = tmp_data.az;
    data_array[i].mx = tmp_data.mx;
    data_array[i].my = tmp_data.my;
    data_array[i].mz = tmp_data.mz;
    data_array[i].gx = tmp_data.gx;
    data_array[i].gy = tmp_data.gy;
    data_array[i].gz = tmp_data.gz;

and that works, or at least it doesn't crash :)

I removed one element (gz) of the structure, which changed the free memory to 2964, and that works when using memcpy. It appears to be a problem with free ram but I can't see what would be causing it.

Pete
 
Well that's interesting. Removing one element brings each array entry to 16 bytes, which might be a clue. Another clue is that it does seem to be associated with a specific address, since I deleted a few variables and saw the array index that blows up shift upward by a few entries. I can try writing each element individually and see if that fixes things. What a weird bug.
 
I've got it working.
Add a struct.h file to your project containing this:
Code:
#include <Arduino.h>
struct measurement {
  int16_t ax;
  int16_t ay;
  int16_t az;
  int16_t gx;
  int16_t gy;
  int16_t gz;
  int16_t mx;
  int16_t my;
  int16_t mz;
};

extern measurement data_array[];

extern measurement tmp_data;

add a struct.cpp file containing this:
Code:
#include "struct.h"

measurement data_array[600];

measurement tmp_data;

Remove the definition of the struct and data_array and tmp_data from your sketch and replace it with:
Code:
#include "struct.h"

I now remember that I have seen something like this on the Arduino forum (or perhaps here) but I can't remember the explanation of why this is necessary.

Pete
 
Status
Not open for further replies.
Back
Top