Solved: Use of freeRAM()

Hi,
for checking how much RAM an object occupies I made a small test with following sketch. But I don't understand the result.
The test setup uses a touch display which all the lcd. calls are made for.

guiTest.ino:

#include "GSM_GP.h"  // only for freeRam()
#include "guiFkt.h"

#define RST_PIN   (8)     // fuer Display

int16_t x=0,y=0;

boolean checkButton(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char* txt) {
  // malt einen Button "txt" bei (x,y) mit der Breite w und Hoehe h wartet auf
  //  einen Touch und gibt true zurueck, wenn er beruehrt wurde
  Button ButtWeiter(x,y, w,h, txt);

  Serial.print(F(", in checkButton: freeRam="));
  Serial.print(gsm.freeRam());

  lcd.lcd_z = 0;  // damit wird auf ein neues Touch gewartet
  while (!lcd.touchRead()) ;  // warten auf Touch
  while (lcd.touchRead()) ;   // warten bis kein Touch mehr
  if ( ButtWeiter.isHit(lcd.lcd_x,lcd.lcd_y)) {
    ButtWeiter.~Button();
    return( 1);
  }
  else  {
    ButtWeiter.~Button();
    return( 0);
  }
}

void setup() {
  Serial.begin(9600);
  while (!Serial); // needed for Leonardo only

  Serial.println("guiTest  neu ---------");
  
  //init display
  lcd.begin(SPI_CLOCK_DIV4, RST_PIN);
  lcd.fillScreen(WHITE);  // bereits in lcd.begin()

  while(1) {
    Serial.print(F("vor checkButton: freeRam="));
    Serial.print(gsm.freeRam());
  
    checkButton(100, 50, 50, 12, (char*)("weiter"));
  
    Serial.print(F(", nach checkButton: freeRam="));
    Serial.println(gsm.freeRam());

    delay(2000);
    Serial.println(F("nochmal"));
  }
}

void loop() {
  
}

guiFkt.cpp:

#include "guiFkt.h"

DisplaySPI lcd; //SPI (GLCD-Shield or MI0283QT Adapter v2)

Button::Button(uint16_t px, uint16_t py, uint16_t wd, uint16_t ht, char* title)
{
  uint16_t tw;
  lcd.drawRect(px, py, wd, ht, BLACK);
  tw = strlen(title) * FONT_WIDTH;
  if (tw > wd - 2) {
    tw = w / FONT_WIDTH;
    title[tw] = 0; // String kuerzen
    tw *= FONT_WIDTH;
  }
  lcd.drawText(px + (wd - tw) / 2, py + (ht - FONT_WIDTH) / 2, title, BLACK, WHITE, 1);

  x=px; y=py;
  w=wd; h=ht;
}

Button::~Button()
{
  lcd.fillRect(x,y, w,h, WHITE);
}

boolean Button::isHit(uint16_t hx, uint16_t hy)
{
  if ((hx>=x) && (hx<=x+w-1) && (hy>=y) && (hy<=y+h-1))
    return(true);
  else return(false);
}

guiFkt.h:

#ifndef GUI_FKT_H
#define GUI_FKT_H

#include <Wire.h>
#include <SPI.h>
#include <digitalWriteFast.h>
#include <DisplaySPI.h>
#include <fonts.h>

// Farben in RGB565:
#define BLACK 0
#define WHITE  RGB(255,255,255)     // 0xFFFF
#define RED    RGB(255,  0,  0)     // 0xF800
#define GREEN  RGB(  0,255,  0)     // 0x07E0
#define BLUE   RGB(  0,  0,255)     // 0x001F
#define YELLOW RGB(200,200,  0)     // 0xCE40
#define NAVY   RGB(  0,  0,123)     // 0x000F
#define ORANGE RGB(255,165,  0)     // 0xFD20
#define MAROON RGB(123, 0,   0)     // 0x7800
#define PURPLE RGB(123, 0, 123)     // 0x780F
#define OLIVE  RGB(123,125, 0)      // 0x7BE0
#define CYAN   RGB(  0,255,255)     // 0x07FF 
#define MAGENTA   RGB(255,  0,255)  // 0xF81F
#define DARKGREEN RGB(0, 125,   0)  // 0x03E0
#define DARKCYAN  RGB(0, 125, 123)  // 0x03EF
#define LIGHTGREY RGB(198,195,198)  // 0xC618
#define DARKGREY  RGB(123,125,123)  // 0x7BEF

extern DisplaySPI lcd; //SPI (GLCD-Shield or MI0283QT Adapter v2)

class Button
{
  public:
    Button(uint16_t px, uint16_t py, uint16_t wd, uint16_t ht, char* title);
    ~Button();
    boolean isHit(uint16_t hx, uint16_t hy);
  private:
    uint16_t x,y;
    uint16_t w,h;
};

#endif

The function freeRam() is contained in a separate file, GSM_GP.h. The code is from this forum:

int GSM_GP::freeRam (void) {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

The output from the sketch is:

vor checkButton: freeRam=7197, in checkButton: freeRam=7205, nach checkButton: freeRam=7197

I expected less free RAM within the function checkButton() because a function call takes some bytes from RAM and the object ButtWeiter also needs some RAM. But it is vice versa.

I don't understand why, or is something wrong with freeRam()?

The compiler can do many things to optimize the code. Arduino sets the optimization for minimal code size. A function call might be replaced by putting the function inline. Even if the compiler does a excellent job, then there is also the magic "LTO" optimization, which optimizes it even more.
What you see is normal. If you want to debug it or see normal behavior, then you should turn off all optimizations.

There is something called: the "high water mark". That is how high the RAM usage goes during runtime without calling freeRAM(). I did some tests with it: HighWaterMark.ino. I have it in a project, so I can see after a year how much RAM was never used.

freeRam() returns the number of bytes between heap end and stack start. The "ButtWeiter" variable is placed on the stack and the memory is reserved during compile time. That is why "freeRam()" is useless for what you are trying to do.

The likely reason why the amount of free memory is larger inside "checkButton" is because memory is reserved in "setup()" to hold the string "weiter". In the constructor of Button, this variable is drawn to the screen and then released because it is no longer used.

Btw., you should not explicitly call "ButtWeiter.~Button()", this is done automatically when the function "checkButton()" exits.

Offtopic: I use this function:

// ---------------------------------------------
// rgb565     version 2
// ---------------------------------------------
// Convert a hexadecimal rgb value into a rgb565 format.
// To be able to use HTML RGB color format in a Arduino sketch.
//
// When a text is needed, then the result can converted into 
// a decimal number as text with a sprintf with %u, or with utoa().
// Great HTML color picker tool: 
//   https://www.w3schools.com/colors/colors_picker.asp
uint16_t rgb565( const unsigned long rgb)
{
  uint16_t R = (rgb >> 16) & 0xFF;
  uint16_t G = (rgb >>  8) & 0xFF;
  uint16_t B = (rgb      ) & 0xFF;

  uint16_t ret  = (R & 0xF8) << 8;  // 5 bits
           ret |= (G & 0xFC) << 3;  // 6 bits
           ret |= (B & 0xF8) >> 3;  // 5 bits
       
  return( ret);
}

Thank you all, this gives a deeper understanding to me.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.