adafruit / Adafruit_SSD1306

Arduino library for SSD1306 monochrome 128x64 and 128x32 OLEDs
http://www.adafruit.com/category/63_98
Other
1.74k stars 963 forks source link

Using an array to substantiate 2 instances of an OLED fails on the second begin function with a reset #242

Closed bfarnam closed 2 years ago

bfarnam commented 2 years ago

Using the following to substantiate 2 instances of an OLED fails during begin:

#include <Arduino.h>
#include **<Wire.h>
#include** <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET    -1

Adafruit_SSD1306 oled[2] = { Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET) };

The first call to begin is OK. The second call to begin FAILS with a reset. It does not matter the order you call them in, the second always fails with a reset. if you comment out one of the begins, the code continues.

setup() {
oled[0].begin(SSD1306_SWITCHCAPVCC, 0x3c);
oled[1].begin(SSD1306_SWITCHCAPVCC, 0x3d);
}

OR

setup(){
oled[1].begin(SSD1306_SWITCHCAPVCC, 0x3d);
oled[0].begin(SSD1306_SWITCHCAPVCC, 0x3c);
}

If you try to only set the parameters on the first instance, the first instance succeeds on begin, but the second instance fails with a reset:

Adafruit_SSD1306 oled[2] = { Adafruit_SSD1306(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), Adafruit_SSD1306() };

Using the depreciated constructor does NOT FAIL IF you leave the screen size definition in the Adafruit_SSD1306.h file to 128x32

Adafruit_SSD1306 oled[2] = { Adafruit_SSD1306(), Adafruit_SSD1306() };

If you try to include the OLED_RESET the second call again fails with a reset:

Adafruit_SSD1306 oled[2] = { Adafruit_SSD1306(OLED_RESET), Adafruit_SSD1306(OLED_RESET ) };

If you change the screen size in the Adafruit_SSD1306.h file the second begin fails with a reset!

// ONE of the following three lines must be #defined:
#define SSD1306_128_64 ///< DEPRECTAED: old way to specify 128x64 screen
//#define SSD1306_128_32 ///< DEPRECATED: old way to specify 128x32 screen

If you leave the define to SSD1306_128_32 the font scaling and positions are messed up but the calls to begin work. I was able to fix this by manually scaling x and y and then setting the position using an offset of 16 to bring the line positions up.

Also, the old constructor does not support fast i2c. I was able to change the following to allow high speed:

Adafruit_SSD1306::Adafruit_SSD1306(int8_t rst_pin)
    : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), spi(NULL), wire(&Wire),
      buffer(NULL), mosiPin(-1), clkPin(-1), dcPin(-1), csPin(-1),
      rstPin(rst_pin), wireClk(400000UL) {}
// ADDED wireClk above to force high speed using the old constructor

My full code is located at: https://github.com/bfarnam/FloLux5000

The first iteration of the code was just using 1 oled, then I brought in the second oled. If you used the steps above this will fail as I have duplicated this in the example sketch.

The code is changing quickly as it is under active development.

Brett

bfarnam commented 2 years ago

OK - I dug into the library more and figured out the reason using the new constructor or the old constructor with SSD1306_128_64 fails.. each buffer is 1024 bytes in size and obviously an 328p can't handle that.

Not a bug or issue per say, but more a lack of documentation that explains the buffer size requirements. The work around worked because the buffer allocation was smaller. However, perhaps an improvement would be to allow a method of passing the i2c address at the call time and sharing a single buffer for two screens. The user would be responsible for tracking the buffer and making sure to start "new" each time. For text only displays this would be great.

For now I will define these as 64x32 screens and then just scale all my fonts accordingly. That should save me some ram.

Brett

bfarnam commented 2 years ago

And another update - so using the screens does not work as a 64x32, but using them as a 128x32 does. Apparently this is because of a bug or "feature" in the SSD1306 chip instructions that takes the 128x32 buffer and somehow scales it so it appears on the 128x64 screen in it's entirety, but at 2x in the vertical (as if it was scaled at 128x128.) So by then manipulating the font scaling using x and y, you can then display a 128x64 screen as normal.

I then heavily modified the SSD1306 and GFX libs and removed ALL SPI instructions and variables, and all drawing (buttons, circles, triangles, etc) that are not being used. I left the text (which uses line draws), the text and char bounds (as I use these to automatically center and scale fonts, and the basics for text functions. I then modified the command and display to accept an i2c address and allowed the begin to be called twice in a row. Since it checks for a buffer, if it exists, it does not create a new one.

Before when I substantiated 2 instances at 128x32, the free ram was approx 1230 bytes and after 230 bytes, not enough to run for long without a hard reset. (Two 512 byte buffers = 1024)

After the code modifications, after substantiating 2 instances at 128x32, the freem ram before is approx 1330 bytes and after approx 800 bytes! (ONE SHARED 512 byte buffer!)

Removing all the SPI functions saved some space, but then sharing a single buffer saved 512 bytes! In order to use this you MUST do a clearDisplay() first which clears the working buffer, update your screen (in it's entirety!), then call display(0x3c) or display(0x3d) which then passes the i2c address to the internal variable i2caddr which is used by the i2c transaction handler. The buffer is then dumped to the screen by the address you passed in display(). If you omit the address, it defaults to the last known address. I use this trick in my CA9500 universal xCA95xx i2c GPIO Handler.

All code is on the GitHub located at https://github.com/bfarnam/FloLux5000

ladyada commented 2 years ago

yep correct, an UNO does not have enough RAM - you can update to a SAMD21 or RP2040 and you'll hve plenty. of memory for lots of OLEDs!