[Arduino] Super weird issue with init OLED display when other code present (not even executed)

Discussion in 'Electronics & Electrics' started by Azrael, May 16, 2019.

  1. Azrael

    Azrael Member

    Joined:
    Jun 27, 2001
    Messages:
    8,726
    Location:
    Melbourne
    Background: I have a pretty basic project that is a fork of another project based on the LeonardoCAN board from Hobbytronics. That project uses the MCP2515 chip onboard to the LeonardoCAN, and an SSD1306 OLED display driven over SW or HW SPI (D10-13).

    Im converting this to use the same MCP2515 CAN chip but offboard via SPI (D10-13), and an SSD1306 OLED display driven by I2C (A4&5)

    Hardware: Arduino Uno, MCP2515 CAN chip w TJA1050 interface (D10-13), SSD1306 128x64 OLED display (A4&5)
    [​IMG]

    Problem: This is where it gets strange, i can initialise everything just fine except when it comes to filtering the CAN input using init_Filt from mcp_can.h (Seeduino shield library). However, the means of failure is super odd. If it was an issue with the filtering function I would expect it to fail in the loop() section of code. But whenever the init_Filt function is present within the codebase it fails in setup() with the display.begin() function [line 82], and never gets to executing the offending init_Filt() [line 184]. This is super odd, because logically the call to init_Filt() should only occur after the display has been initialised on line 82 with display.begin().

    I have already tried trimming down the code base to just the SSD1306 and MCP2515 relevant sections in case it was a memory error, but to no avail. Both the MCP2515 and SSD1306 example code run flawlessly.

    Im a bit stumped (could be because im groggy with cold and flu tabs), but I cant think of why adding a function into another portion of the codebase causes a failure in setup().

    Code is available here: https://github.com/porterble/EliteGaugeDiscrete/blob/master/Elite_Gauge_Pro.ino

    Help?
     
  2. mtma

    mtma Member

    Joined:
    Aug 12, 2009
    Messages:
    4,855
    I would say that the only reason why it should fail there (if it is entering that infinite loop) is if malloc fails in the SSD1306 driver's begin function. The SSD1306's driver is trying to dynamically allocate 1,135 contiguous bytes of RAM when you call the begin function, which malloc() is trying to find amongst all the statically compiled things in memory for you.

    The other possibility is that the micro stack is exceeded entering interrupts, which can leave you somewhere totally unexpected, but considering that the uno only has 2KB of RAM I think that's the previous is the most likely possibility.

    I'm guessing the tools you're using doesn't support step by step debugging?

    Getting back to malloc() and related kinds of dynamically assigned memory, generally speaking it can be problematic and somewhat pointless to use this kind of memory allocation in embedded programming. On a desktop PC, where the platform will manage memory fragmentation and expand overflows to swap-disk it can be acceptable. But in an embedded situation, if malloc() can't find the space, what do you do when malloc() fails? Not use the OLED screen in this instance? Basically as a developer you have to be wary of what it does.

    Relevant reading : https://barrgroup.com/Embedded-Systems/How-To/Malloc-Free-Dynamic-Memory-Allocation

    Possibly also getting a readout of this may help in debugging:
    https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory

    And returning to the driver, as you gathered its suitability from an embedded programming viewpoint is dubious: whilst I think you might find the uno lacking in memory in general, one possible solution to this issue is to rewrite the malloc() part of the SSD1306 driver to a statically allocated memory location. That way the compiler will arrange your memory space for you at compile time, and you will be told in no uncertain terms if you don't have enough space (unless there's other things trying to malloc() elsewhere).
     
    Last edited: May 16, 2019
  3. OP
    OP
    Azrael

    Azrael Member

    Joined:
    Jun 27, 2001
    Messages:
    8,726
    Location:
    Melbourne
    Yeah, i had a fiddle with a newer micro (Leonardo) and had some more luck with getting it to compile and malloc() correctly. However, this has other issues as the Atmega32U4 impersonates USB HID and so serial comms are dicky.

    However, weirdly the 32U4 is supposed to be the same memory stack as the 328, so I cant quite see why its working there.
     
  4. mtma

    mtma Member

    Joined:
    Aug 12, 2009
    Messages:
    4,855
    To delve deeper into the root cause first the compiler's memory map output (containing the statically allocated memory) should be inspected. Unfortunately the Arduino IDE keeps this detail away from the user, but the underlying GCC compiler is able to produce the information for analysis if you really want to know.

    Also isn't the 32U4 512 bytes bigger for RAM? Could be all that it needs - and secondly the intrinsic overheads in the Arduino platform may differ between the two platforms.
     
    Last edited: May 16, 2019
  5. OP
    OP
    Azrael

    Azrael Member

    Joined:
    Jun 27, 2001
    Messages:
    8,726
    Location:
    Melbourne
    You are right on the memory size, just found that buried in the docs. Lots of them just say that the 32U4 is identical to the 328P other than the onboard USB.

    Ugh, havent done GCC malloc analysis for too long.
     
  6. OP
    OP
    Azrael

    Azrael Member

    Joined:
    Jun 27, 2001
    Messages:
    8,726
    Location:
    Melbourne
    Didnt do the malloc analysis, but switching to the Leonardo seems to have resolved the issue:
    [​IMG]

    Ill add the rest of the codebase back in and see if that still works.

    Edit: readded codebase, works fine.
     
    Last edited: May 17, 2019
  7. @rt

    @rt Member

    Joined:
    Nov 30, 2005
    Messages:
    2,329
    There appears to be a return code for malloc error, but you didn’t mention the result.
    Code:
    // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
    if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
    }
    
    It’s spot on 1024 bytes for a 128x64 bitwise monochrome frame buffer. Still a lot of memory for some platforms.
     
  8. mtma

    mtma Member

    Joined:
    Aug 12, 2009
    Messages:
    4,855
    The Adafruit driver wants more than that.

    Based on the responses I gather the OP wants to keep the code compatible with the open-source driver, so isn't interested in re-writing the driver.

    But certainly if you didn't really need to use some of the vfx functions in the Adafruit driver, you could differentially update the base glyphs into the buffer of the SSD1306 and not carry a local frame-buffer, which would free up a lot of memory.
     
  9. OP
    OP
    Azrael

    Azrael Member

    Joined:
    Jun 27, 2001
    Messages:
    8,726
    Location:
    Melbourne
    There isn’t a return code, just FALSE.

    And yeah. Trying to keep it broadly compatible so that it’s not locked in to a specific display.
     

Share This Page

Advertisement: