2
\$\begingroup\$

I am developing a hobby project using the Arduino IDE and an USBasp programmer to upload my code to the ATtiny85. After programming, I remove the chip from the programmer socket and put it into the target device (two buttons and an I2C 128x32 OLED display.)

When compiling in the Arduino IDE, the output says I have 89 bytes of RAM memory left for local variables. As I understand it, this memory shrinks when functions are called and local variables are declared. I can't say if a crash condition can occur.

Is there a simple way to emulate program execution and watch in real time how much memory is left?

EDIT 1

I tried using this approach with PRINT_STACK_SPACE changed to:

#define PRINT_STACK_SPACE do {   itoa(StackCount(), buffer, 10); oled.setFont(FONT6X8);  oled.setCursor(0, 0);  oled.print(buffer);  oled.setFont(FONT8X16); } while(0)

It seems that StackCount() always returns zero. I put PRINT_STACK_SPACE at the beginning of the execution in setup, after the OLED display is initialized, and in some places in loop(); it always shows zero.

I don't understand the symbols used in StackPaint - maybe something has to be adapted to the ATtiny85 and the Arduino IDE?

EDIT 2

OK, for testing I removed everything else except OLED initialization and stackpainting, the code:

#include <avr/sleep.h>
#include <avr/interrupt.h>
#include <avr/power.h>    
#include <avr/wdt.h> 
#include <EEPROM.h>
#include <TinyWireM.h>
#include <Tiny4kOLED.h>

#define POWER_PIN 1
#define BUTTON1_PIN 3
#define BUTTON2_PIN 4

char buffer[14];

extern uint8_t _end;
extern uint8_t __stack;

#define STACK_CANARY  0xfe
void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));

void StackPaint(void)
{
  #if 0
  uint8_t *p = &_end;

  while(p <= &__stack)
  {
  *p = STACK_CANARY;
  p++; 
  }
  #else
  __asm volatile ("    ldi r30,lo8(_end)n"
  "    ldi r31,hi8(_end)n"
  "    ldi r24,lo8(0xfe)n" /* STACK_CANARY = 0xfe */
  "    ldi r25,hi8(__stack)n"
  "    rjmp .cmpn"
  ".loop:n"
  "    st Z+,r24n"
  ".cmp:n"
  "    cpi r30,lo8(__stack)n"
  "    cpc r31,r25n"
  "    brlo .loopn"
  "    breq .loop"::);
  #endif
  }

uint16_t StackCount(void)
{
  const uint8_t *p = &_end;
  uint16_t       c = 0;

  while(*p == STACK_CANARY && p <= &__stack)
  {    
    p++;
    c++;
  }

  return c;
}

#define PRINT_STACK_SPACE do{   itoa(StackCount(), buffer, 10);    oled.setFont(FONT6X8); oled.setCursor(0, 0); oled.print(buffer); oled.setFont(FONT8X16); } while(0)


void setup() {
  pinMode(0, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(POWER_PIN, OUTPUT);
  pinMode(BUTTON1_PIN, INPUT_PULLUP);
  pinMode(BUTTON2_PIN, INPUT_PULLUP);

  digitalWrite(POWER_PIN, HIGH);
  delay(10);
  oled.begin(128, 32, sizeof(tiny4koled_init_128x32br), tiny4koled_init_128x32br);
  oled.setFont(FONT8X16);
  oled.clear();
  oled.on();
  delay(10);
  PRINT_STACK_SPACE;
}

void loop() {
  PRINT_STACK_SPACE;
  delay(10);

}

The compiler says "Global variables use 108 bytes (21%) of dynamic memory, leaving 404 bytes for local variables. Maximum is 512 bytes." but the only displayed number is still zero.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Simple is a relative term. One technique I have used in the past is stack painting. The basic idea is you fill RAM with a known / repeating pattern. You can, in this way, know the deepest level to which RAM has been written by looking for the first instance of the pattern, and assuming that it wasn't written by your code after initialization.

I wrote a pretty thorough blog post about this topic many years ago, and I don't think the state of the art has changed much since

https://shop.wickeddevice.com/2012/10/05/avr-stack-overflow/

\$\endgroup\$
7
  • \$\begingroup\$ Forgive my ignorance, but how to read RAM at runtime? Especially if the microcontroller is put in another device? Or are you talking about emulation? \$\endgroup\$
    – PanJanek
    Commented Feb 23 at 15:39
  • \$\begingroup\$ You just use a pointer and walk through the memory, C is very permissive in this regard. Take a look at the blog post, there is code shown there, see StackCount() and StackPaint() \$\endgroup\$
    – vicatcu
    Commented Feb 23 at 15:58
  • \$\begingroup\$ I used the code from the blog post, changed serial output to oled display. I try to display from time to time size of available stack on OLED since i don't have serial connected. Code compiles and I understand the principle, but it seems that StackCount() always returns zero... \$\endgroup\$
    – PanJanek
    Commented Feb 24 at 18:12
  • \$\begingroup\$ It is possible that the stack is full from the beginning, and some bytes are overwritten and the code works "by chance". But i put modified PRINT_STACK_SPACE macro in setup just after oled is initialized and it shows zero from the beginning. I don't uderstand why macro uses do while (0), what are extern _end and __stack symbols and why StackPaint containd #if 0 preprocessor that never enters. Could you explain? I'll update my question, mayby for attiny85 some code change is required? I use latest arduino IDE and uspASB to upload. \$\endgroup\$
    – PanJanek
    Commented Feb 24 at 19:52
  • \$\begingroup\$ Yes, that's not a great sign... Try it with an empty sketch and output to the serial port instead and see what it says \$\endgroup\$
    – vicatcu
    Commented Feb 26 at 23:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.