Help understanding memory/binutils

In my quest to understand memory usage and lower it as much as possible, I ran avr-nm -Crtd --size-sort to see what portions of the code are the most taxing on the RAM.

The problem:
Memory is being allocated to irrelevant things (e.x. digitalwrite using 152 bytes even though I do not use it at all in my sketch) and a bunch of memory is being used by Serial this and Serial that for a reason I do not understand.

What is going on here???
How can I interpret these results???
Is there a method to trace these large memory blocks even further?

For reference if needed:

The binutil output:

00001272 T main
00000574 T __vector_24
00000480 t TwoWire::endTransmission(unsigned char) [clone .constprop.23]
00000414 t twi_readFrom.constprop.15
00000410 t I2Cdev::readBytes(unsigned char, unsigned char, unsigned char, unsigned char*, unsigned int, void*)
00000178 t I2Cdev::writeBits(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, void*)
00000157 b Serial
00000154 t HardwareSerial::write(unsigned char)
00000152 t digitalWrite
00000148 T __vector_16
00000142 t global constructors keyed to 65535_0_verywildforthenight.ino.cpp.o.2679
00000112 t twi_stop
00000100 T __vector_18
00000094 t TwoWire::write(unsigned char)
00000094 t I2Cdev::writeBytes(unsigned char, unsigned char, unsigned char, unsigned char*, void*) [clone .constprop.17]
00000094 t writeByte(unsigned char, unsigned char, unsigned char) [clone .constprop.18]
00000090 t TwoWire::write(unsigned char const*, unsigned int)
00000090 t Print::write(unsigned char const*, unsigned int)
00000078 t twi_transmit
00000076 T __vector_19
00000074 t micros
00000068 t HardwareSerial::_tx_udr_empty_irq()
00000068 T __udivmodsi4
00000066 t twi_handleTimeout
00000064 t HardwareSerial::flush()
00000060 t twi_init
00000046 T __vector_6
00000046 T __divmodsi4
00000042 t WDT_INIT()
00000040 t HardwareSerial::read()
00000038 t TwoWire::read()
00000033 b mpu
00000032 b twi_txBuffer
00000032 b twi_rxBuffer
00000032 b twi_masterBuffer
00000032 b TwoWire::txBuffer
00000032 b TwoWire::rxBuffer
00000030 t TwoWire::peek()
00000030 t HardwareSerial::availableForWrite()
00000030 T __umulhisi3
00000028 t HardwareSerial::peek()
00000024 t millis
00000024 t HardwareSerial::available()
00000022 T __muluhisi3
00000022 T __do_global_ctors
00000022 T __do_copy_data
00000020 t digital_pin_to_timer_PGM
00000020 t digital_pin_to_port_PGM
00000020 t digital_pin_to_bit_mask_PGM
00000020 t Serial0_available()
00000020 t serialEventRun()
00000020 T __vector_1
00000018 d vtable for TwoWire

The code

#include <avr/wdt.h>
#include "Wire.h"       
#include "I2Cdev.h"     
#include "MPU6050.h" 
#include <avr/sleep.h>

#define SIGNAL_PATH_RESET 0x68
#define INT_PIN_CFG 0x37
#define ACCEL_CONFIG 0x1C
#define MOT_THR 0x1F // Motion detection threshold bits [7:0]
#define MOT_DUR 0x20 // Duration counter threshold for motion interrupt generation, 1 kHz rate, LSB = 1 ms
#define MOT_DETECT_CTRL 0x69
#define INT_ENABLE 0x38
#define WHO_AM_I_MPU6050 0x75 // Should return 0x68
#define INT_STATUS 0x3A
#define ADO 0
#if ADO
#define MPU6050_ADDRESS 0x69 // Device address when ADO = 1
#else
#define MPU6050_ADDRESS 0x68 // Device address when ADO = 0
#endif   

MPU6050 mpu;
int16_t ax, ay, az;
int16_t gx, gy, gz;

struct MyData {
  byte X;
  byte Y;
  byte Z;
};

MyData data;

enum state_t {
  START,
  IN_WARNING_1,
  IN_WARNING_2,
  SEAT_BELT_OK,
  SEAT_BELT_NOK_ACCEPTED,
} ;

state_t state = state_t::SEAT_BELT_OK;
uint32_t inCurrentStateAtMs ;

const uint8_t wakePin = 2; // pin used for waking up  
const uint8_t seatPin = 5 ;   // active low
const uint8_t buzzer = 4;

void setState( state_t newState ) {
  state = newState ;
  inCurrentStateAtMs = millis() ;
}

void writeByte(uint8_t address, uint8_t subAddress, uint8_t data) {
  Wire.begin();
  Wire.beginTransmission(address); // Initialize the Tx buffer
  Wire.write(subAddress); // Put slave register address in Tx buffer
  Wire.write(data); // Put data in Tx buffer
  Wire.endTransmission(); // Send the Tx buffer
}

uint8_t readByte(uint8_t address, uint8_t subAddress) {
  uint8_t data; // `data` will store the register data   
  Wire.beginTransmission(address); // Initialize the Tx buffer
  Wire.write(subAddress); // Put slave register address in Tx buffer
  Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
  Wire.requestFrom(address, (uint8_t) 1); // Read one byte from slave register address 
  data = Wire.read(); // Fill Rx buffer with result
  return data; // Return data read from slave register
}

void setup() {
  wdt_disable ();
  Wire.begin();
  mpu.initialize();
  writeByte(MPU6050_ADDRESS, 0x6B, 0x00);
  writeByte(MPU6050_ADDRESS, SIGNAL_PATH_RESET, 0x07); //Reset all internal signal paths in the MPU-6050 by writing 0x07 to register 0x68;
  writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0x01); //Write register 28 (==0x1C) to set the Digital High Pass Filter, bits 3:0. For example set it to 0x01 for 5Hz. (These 3 bits are grey in the data sheet, but they are used! Leaving them 0 means the filter always outputs 0.)
  writeByte(MPU6050_ADDRESS, MOT_THR, 10); //Write the desired Motion threshold to register 0x1F (For example, write decimal 20).  
  writeByte(MPU6050_ADDRESS, MOT_DUR, 40); //Set motion detect duration to 1  ms; LSB is 1 ms @ 1 kHz rate  
  writeByte(MPU6050_ADDRESS, MOT_DETECT_CTRL, 0x15); //to register 0x69, write the motion detection decrement and a few other settings (for example write 0x15 to set both free-fall and motion decrements to 1 and accelerometer start-up delay to 5ms total by adding 1ms. )   
  writeByte(MPU6050_ADDRESS, INT_ENABLE, 0x40); //write register 0x38, bit 6 (0x40), to enable motion detection interrupt.     
  writeByte(MPU6050_ADDRESS, INT_PIN_CFG, 160); // now INT pin is active low
  //pinMode( seatPin, INPUT_PULLUP ) ;
  //pinMode(wakePin, INPUT_PULLUP); // wakePin is pin no. 2
  //pinMode(buzzer, OUTPUT);
  //these do not include the led since it will be deleted evenutally
  //unsure of how to classify pins used by MPU
  DDRD = B11011011; //defaults at output to save power. buzzer is output
  PORTD = B00100100; //enable the pull up resistors
  setState( state_t::START ) ;
}


//creating alternative to attachinterrupt
// EICRA = 0; //low generates interrupt
//EIMSK |= (1<<INT0); //extenal pin interrupt is enabled


uint16_t readdata;

void Sleep_CPU(void){
  SMCR |= (1 << 2); //power down mode
  SMCR |= 1;//enable sleep
  EIMSK |= (1<<INT0); //extenal pin interrupt is enabled
    __asm__  __volatile__("sleep");//in line assembler to go to sleep
  
  SMCR = 0;
  EIMSK &= ~(1<<INT0); //extenal pin interrupt is enabled
  readdata = readByte(MPU6050_ADDRESS, INT_STATUS);
  //I think I should be detach interrupts to some capacity here
}
 
 void WDT_INIT (void){
  cli();
  MCUSR &= ~(1<<WDRF); //clearing the WD reset flag.
  WDTCSR = (1<<WDCE) | (1<<WDE);
  WDTCSR = (1<<WDIE ) | (1<< WDP2 ) | (1<<WDP0 ); // set WDIE ( Interrupt only, no Reset ) and 1 second TimeOut
  wdt_reset();
  //digitalWrite(LED_BUILTIN, HIGH); // turn off LED to show sleep mode
  SMCR |= (1 << 2); //power down mode
  SMCR |= 1;//enable sleep
    sei();
    __asm__  __volatile__("sleep");//in line assembler to go to sleep
   SMCR = 0;
  //digitalWrite(LED_BUILTIN, LOW); // turn off LED to show sleep mode
    //I think I should be detach interrupts to some capacity here
}

ISR (INT0_vect) {
}

ISR(WDT_vect) {
  wdt_disable ();
}

void loop() {
  static boolean outputTone = false;
  static bool movementInCurrentWindow = false ;
  static bool carDeemedStopped = true ;
  static uint32_t lastMovAtMs = millis() ;
  uint32_t ms = millis() ;

static uint8_t xlast = 0; //tradeoff dynamic memory vs program memory. this is better for program space but increase global variables by 2 bytes
static uint8_t ylast = 0;
static uint8_t zlast = 0;

  xlast = data.X;
  ylast = data.Y;
  zlast = data.Z;
  
  mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
  data.X = map(ax, -17000, 17000, 0, 255 ); // X axis data
  data.Y = map(ay, -17000, 17000, 0, 255); 
  data.Z = map(az, -17000, 17000, 0, 255);  // Y axis data

  if((abs(xlast) - abs(data.X)) > 3 || (abs(ylast) - abs(data.Y)) > 3){
  movementInCurrentWindow = true;
  carDeemedStopped =false;
  lastMovAtMs = ms;
}

  if(abs(xlast - data.X)<= 3 || abs(ylast - data.Y) <= 3){
  movementInCurrentWindow = false;
}

  if ( ms - lastMovAtMs > 1000 ) {
    carDeemedStopped = true ;
     movementInCurrentWindow = false ;
  }

 if ( (((PIND & (1 << seatPin)) >> seatPin) == 0)  && state != state_t::SEAT_BELT_OK && state != state_t::START) {
   setState( state_t::SEAT_BELT_OK ) ;
 }

  switch ( state ) {

    case state_t::START : {
          for (int i = 0; i<3 ; i++){
         Sleep_CPU();
             }
          if ( movementInCurrentWindow )  {
          setState( state_t::IN_WARNING_1 ) ;
          movementInCurrentWindow = false ;
        }
        break ;
      }


    case state_t::IN_WARNING_1 : {
        for( int i = 0; i<5; i++){
        PORTD |= (1<<buzzer);
        WDT_INIT();
        PORTD &= ~(1<<buzzer);
        for (int i = 0; i<8 ; i++){
        WDT_INIT();
       }

    }
     setState( state_t::IN_WARNING_2 ) ;
         PORTD &= ~(1<<buzzer);
        break ;
      }
      
    case state_t::IN_WARNING_2 : {
      for( int i = 0; i<20; i++){
        PORTD |= (1<<buzzer);
        WDT_INIT();
        PORTD &= ~(1<<buzzer);
        WDT_INIT();
   }        
         PORTD &= ~(1<<buzzer);
         setState( state_t::SEAT_BELT_NOK_ACCEPTED ) ;
        break ;
      }     


    case state_t::SEAT_BELT_OK : {
        if ((((PIND & (1 << seatPin)) >> seatPin) == 1) ) {
        setState( state_t::START) ;
        }
        if ( carDeemedStopped ) {
        setState( state_t::START ) ;
        }
       for (int i = 0; i<16 ; i++){
        WDT_INIT();
       }
        break ;
      }


    case state_t::SEAT_BELT_NOK_ACCEPTED : {
        if ( carDeemedStopped ) {
        setState( state_t::START ) ;
        }
       for (int i = 0; i<16 ; i++){
        WDT_INIT();
       }
      break ;
    }
  }  // switch
}  // loop

what about the libraries?

A .map file would be handy as it tells you for each .o object file pulled into your code, which function in some other object file called it and required it to be linked into your code.

I don't know how to generate .map files for AVR targets in the Arduino IDE though.

Note that all of the lines from nm output tagged with " t " or " T " are in the "text" segment of your program - ie they occupy flash, not RAM.

That leaves only the following in RAM:

00000157 b Serial
00000033 b mpu
00000032 b twi_txBuffer
00000032 b twi_rxBuffer
00000032 b twi_masterBuffer
00000032 b TwoWire::txBuffer
00000032 b TwoWire::rxBuffer
00000018 d vtable for TwoWire

(' b ' means the "bss" segment - uninitialized data. ' d ' is initialized data.)

Serial is a bit of a hog, using 64byte buffers in each direction plus assorted pointers, and I'm not sure why it was included in your sketch. (Ah. The MPU6050 library (including i2cdev) contains a bunch of Serial output, although it looks like most of it should be excluded except for debugging... Perhaps this is a bug?)

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