Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSD1306 dual I2C display - can't write independently #923

Closed
fm4dd opened this issue Jun 16, 2019 · 5 comments

Comments

@fm4dd
Copy link

commented Jun 16, 2019

No description provided.

@fm4dd fm4dd changed the title SSD1306 dual I2C display - SSD1306 dual I2C display - can't write independently Jun 16, 2019

@fm4dd

This comment has been minimized.

Copy link
Author

commented Jun 16, 2019

I am running two SSD1306 128x64 OLEDs on a single I2C bus with separate addresses (0x3C and 0x3D). They work as listed in issue #434.
However unless the buffer fully cleared after writing to one display, updating them independently does not work. I believe the buffer pointers are both written into, but let me first explain the issue on a few examples.
Example 1:

#include <Wire.h>
#include <U8g2lib.h>
U8G2_SSD1306_128X64_NONAME_F_HW_I2C dleft(U8G2_R0);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C dright(U8G2_R0);

void setup() {
  dleft.setI2CAddress(0x3C * 2);
  dright.setI2CAddress(0x3D * 2);
  dleft.begin();
  dright.begin();
  dleft.clearBuffer();
  dright.clearBuffer();
}
void loop() {
  // write ABC to left display
  dleft.clearBuffer();
  dleft.setFont(u8g2_font_inr19_mr);
  dleft.drawStr(0,32, "ABC");
  dleft.updateDisplay();
  // write DEF to right display
  //dright.clearBuffer();
  dright.setFont(u8g2_font_inr19_mr);
  dright.drawStr(64,32, "DEF");
  dright.updateDisplay();
  delay(500000);
}

In this case, the intention is to write ABC to display-1 (left, I2C 0x3C), and DEF to display-2 (right, I2C 0x3D). It only works after I uncomment the dright.clearBuffer() line, but that should not be necessary. The right display gets the content from the left display as well, even though that wasn't intended.

DSC_2926s

After adding in debug code, I believe that drawing data updates always happen to both display buffers together instead of each individually. I added following serial line debugging code to see the data content of both buffers.
Example code:

#include <Wire.h>
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_HW_I2C dleft(U8G2_R0);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C dright(U8G2_R0);

uint8_t *dleft_ptr;
uint8_t dleft_h;
uint8_t dleft_w;
int dleft_bufsize;
uint8_t *dright_ptr;
  
void setup() {
  Wire.begin();
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  dleft.setI2CAddress(0x3C * 2);
  dright.setI2CAddress(0x3D * 2);
  dleft.begin();
  dright.begin();
  dleft_ptr = dleft.getBufferPtr();
  dleft_h = dleft.getBufferTileHeight();
  dleft_w = dleft.getBufferTileWidth();
  dleft_bufsize = 8*(dleft_h*dleft_w);
  dright_ptr = dright.getBufferPtr();
  Serial.print("Left Display buffer addr: ");
  Serial.println( (long) &dleft_ptr, DEC);
  Serial.print("Left Display buffer height: ");
  Serial.println(dleft_h);
  Serial.print("Left Display buffer width: ");
  Serial.println(dleft_w);
  Serial.print("Left Display buffer memory: ");
  Serial.println(dleft_bufsize);
  Serial.print("Right Display buffer addr: ");
  Serial.println( (long) &dright_ptr, DEC);
}

void loop() {
  dleft.clearBuffer();
  dright.clearBuffer();
  debug(1),
  
  dleft.setFont(u8g_font_6x10);
  debug(2);
  
  dleft.drawStr(0,8, "ABC");
  debug(3);
  //dright.drawStr(30,8, "DEF");
  dleft.updateDisplay();
  debug(4);
  
  dright.clearBuffer();
  debug(5);
  dright.setFont(u8g_font_6x10);
  dright.updateDisplay();
  delay(500000);
}

void debug(int line) {
  Serial.println();
  Serial.print(line);
  Serial.println(" Left Display buffer content: ");
  for(int i=0; i<30; i++) {
    uint8_t b1 = *(dleft_ptr+i);
    Serial.print(b1, HEX);
  }
  Serial.println();
  Serial.print(line);
  Serial.println(" Right Display buffer content: ");
  for(int j=0; j<30; j++) {
    uint8_t b2 = *(dright_ptr+j);
    Serial.print(b2, HEX);
  }
  Serial.println();
}

Serial debug output:

Left Display buffer addr: 536871280
Left Display buffer height: 8
Left Display buffer width: 16
Left Display buffer memory: 1024
Right Display buffer addr: 536871460

1 Left Display buffer content: 
000000000000000000000000000000
1 Right Display buffer content: 
000000000000000000000000000000

2 Left Display buffer content: 
000000000000000000000000000000
2 Right Display buffer content: 
000000000000000000000000000000

3 Left Display buffer content: 
F8242224F8082FE92926C07C828282440000000000000
3 Right Display buffer content: 
F8242224F8082FE92926C07C828282440000000000000

4 Left Display buffer content: 
F8242224F8082FE92926C07C828282440000000000000
4 Right Display buffer content: 
F8242224F8082FE92926C07C828282440000000000000

5 Left Display buffer content: 
000000000000000000000000000000
5 Right Display buffer content: 
000000000000000000000000000000

It looks as if writing data to either display updates both displays buffer content. Is there a way to separate the buffers to enable the displays to work fully independent? For larger code, a display only gets partially updated, and they can't be cleared together.

Thanks,
Frank

@fm4dd fm4dd closed this Jun 16, 2019

@fm4dd fm4dd reopened this Jun 16, 2019

@fm4dd

This comment has been minimized.

Copy link
Author

commented Jun 16, 2019

I noticed this issue is a duplicate of issue #796.

@fm4dd fm4dd closed this Jun 16, 2019

@olikraus

This comment has been minimized.

Copy link
Owner

commented Jun 16, 2019

Yes, the display buffer will be shared between u8g2 objects if the constructor is the same.

@fm4dd

This comment has been minimized.

Copy link
Author

commented Jun 19, 2019

Solution

Just as reference, this is how I solved it: I simply made a 2nd copy of the original template, setup function, and buffer allocation function, and it worked right away. Not very elegant but simple and effective, especially at a 2:00 AM deadline 👍 The solution in issue #540 didn't work out of the box, it couldn't be implemented quickly (to big and intrusive change).

Details:

In C:\Users\root\Documents\Arduino\libraries\U8g2\src I updated the following four files: u8g2lib.h, u8g2.h, setup.cpp and u8g2_d_memory.c

  1. In u8g2lib.h line 1282 I made a copy of the class template U8G2_SSD1306_128X64_NONAME_F_HW_I2C, and called it U8G2_SSD1306_128X64_NONAME_F_HW_I2C_2. I made a reference to a new setup function u8g2_Setup_ssd1306_i2c_128x64_noname_f2.
class U8G2_SSD1306_128X64_NONAME_F_HW_I2C_2 : public U8G2 {
  public: U8G2_SSD1306_128X64_NONAME_F_HW_I2C_2(const u8g2_cb_t *rotation, uint8_t reset = U8X8_PIN_NONE, uint8_t clock = U8X8_PIN_NONE, uint8_t data = U8X8_PIN_NONE) : U8G2() {
    u8g2_Setup_ssd1306_i2c_128x64_noname_f2(&u8g2, rotation, u8x8_byte_arduino_hw_i2c, u8x8_gpio_and_delay_arduino);
    u8x8_SetPin_HW_I2C(getU8x8(), reset, clock, data);
  }
};
  1. In file u8g2.h line 592 I made a copy of the original function definition, and called it u8g2_Setup_ssd1306_i2c_128x64_noname_f2.

  2. In file u8g2_d_setup.c line 303 I made a copy of the original setup function, and added it under the new function name:

void u8g2_Setup_ssd1306_i2c_128x64_noname_f2(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  uint8_t *buf;
  u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_16_8_f2(&tile_buf_height);
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}
  1. Finally, in file u8g2_d_memory.c line 40 I made a copy of the original buffer function, and added it under the 2nd name u8g2_m_16_8_f2:
uint8_t *u8g2_m_16_8_f2(uint8_t *page_cnt)
{
  static uint8_t buf[1024];
  *page_cnt = 8;
  return buf;
}

The working Arduino code looks as follows:

#include <Wire.h>
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_HW_I2C dleft(U8G2_R0);
U8G2_SSD1306_128X64_NONAME_F_HW_I2C_2 dright(U8G2_R0);

void setup() {
  dleft.setI2CAddress(0x3C * 2);
  dright.setI2CAddress(0x3D * 2);
  dleft.begin();
  dright.begin();
  dleft.clearBuffer();
  dright.clearBuffer();
}

void loop() {
  // write ABC to left display
  dleft.setFont(u8g2_font_inr19_mr);
  dleft.drawStr(0,32, "ABC");
  dleft.updateDisplay();
  
  // write DEF to right display
  dright.setFont(u8g2_font_inr19_mr);
  dright.drawStr(64,32, "DEF");
  dright.updateDisplay();
  
  delay(500000);
}

The independent display updates now work like a charm. :-) Sensor data gets updated in each respective display, one display continuously runs the clock. Thanks @olikraus for the library!

DSC_3003

P.S. For library update, I think adding another constructor type may be the way to go. The new constructor could add a display ID argument (e.g. 1..4). The setup function checks if that arg exists, and then calls further buffer allocation functions as needed. This would keep all existing code working exactly "as-is".

@olikraus

This comment has been minimized.

Copy link
Owner

commented Jun 19, 2019

ok... nice solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.