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

Second HW I2C not working, SW I2C doing strage things (STM32, Arduino, BluePill, SSD1306) #1209

Closed
konkav-js opened this issue May 29, 2020 · 10 comments
Labels

Comments

@konkav-js
Copy link

Hello.

I am trying to make two OLED (128x32) displays to work on two I2C controller. The STM32F103 has two HW I2Cs. First I made a simple I2C scanner, connected two OLEDs to it, and run it. It nicely found them both (I was looking on it with logic analyzer too). It both had a 100kHz frequency. After that, I put some codes for one OLED display. Looking on it with LA, I saw that the freq of the first I2C has changed to 360kHz, but everything worked (scanner found both displays, and one display showed text). Then I added the second display, and nothing changed. So I tried few things. First, connected second display in parallel with first, and both of them showed same text. Then put first disp. to second I2C, no text. When I disconnected the second SDA, the scanner coudn't find the disp. I tried with both UNIVISION and WINSTAR constructor (with UNI, the text was looking good, with WIN, it was kind of duplicated with offset), but only first disp. worked. If you look at the LA capture:

image

it is clearly visible, that there is a missing datastream on second I2C for display data, but not for scanner.

The cleared code:

#include <Wire.h>
TwoWire wire1(PB7, PB6);
TwoWire wire2(PB11, PB10);

#include <U8g2lib.h>
U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C oled1(U8G2_R0, U8X8_PIN_NONE);
U8G2_SSD1306_128X32_UNIVISION_F_2ND_HW_I2C oled2(U8G2_R0, U8X8_PIN_NONE);

void setup() {
  wire1.begin();
  wire2.begin();
  oled1.begin();
  oled2.begin();
}

void loop() {
  oled1.clearBuffer();
  oled1.setFont(u8g2_font_ncenB14_tr);
  oled1.drawStr(0, 20, "Hello World 1!");
  oled1.sendBuffer();
  oled2.clearBuffer();
  oled2.setFont(u8g2_font_ncenB14_tr);
  oled2.drawStr(10, 30, "Hello World 2!");
  oled2.sendBuffer();

  <I2C scanner code here>
}

Then I tried to drive second disp with SW I2C:
U8G2_SSD1306_128X32_UNIVISION_F_SW_I2C oled2(U8G2_R0, PB10, PB11 , U8X8_PIN_NONE);
It messed up the wire2, the scanner was working more than 3s for a scan, compared to 28ms in the first case (no screens, 100kHz), but I think, this is logical (wire2 is HW I2C, and the same pins are used by u8g2 library for bitbanging I2C stream). The first display worked fine, the second (I put a counter, to see if they are working continuously) had just the first printout. So I thought, I will disable the wire2 completely, and it should fix it. When commented out everything for wire2, the second display stopped working altogether. This was weird, so I uncommented the commented out lines, and the second disp again showed first printout. Switched the displays, but it was the same. I thought, maybe it's the pullup resistors? Took out the displays, but they have 4k7 pullups. Just to be sure, I added 4k7 in parallel, but it changed nothing. Then I had an idea. To me it looked like if the init of the second HW I2C made a display working through SW I2C, but then actually using that HW I2C broke things. So, I left wire2 init uncommented, but disabled it in scanner. And now both displays are working. The first in HW (360kHz), the second in SW (37kHz).

Here is a snapshot from LA:
image

So this now works, but don't know why. The SW I2C updates the display in 150ms, compared to 16ms with HW I2C what is awfully slow.

And here is a comparison of part of the two streams (the working and the non-working SW I2C):

image

The upper one is the working SW I2C, the lower one is when the disp. doesn't upgrade. There are strange glitches in there, I don't know why. I tried to look at the source, maybe I could figure something out about the second HW I2C not working, or those glitches, but I am not a programmer, I couldn't figure out a single thing. If someone has some idea how to make that second HW I2C to work with u8g2, I am all ears. The SW I2C is not important to me, I just thought to post here my findings about it, maybe it will be helpful for somebody.

Arduino IDE: 1.8.12
STM32 Cores: 1.9.0
U8g2: 2.27.6
Sketch: sketch.txt
Optimize: tried "Smallest -Os" and "Fastest -O3"

Thanks.

@espHorst
Copy link

espHorst commented Jun 4, 2020

For the SW I2C on the STM32 core I confirm the glitches for my setup here. Due to this glitches my 1306 did not work at all. Finally I found the reason in function u8x8_gpio_and_delay_arduino(): Line 130/131:

pinMode(u8x8_GetPinValue(u8x8, msg), OUTPUT);
digitalWrite(u8x8_GetPinValue(u8x8, msg), 0);

When before

pinMode(u8x8_GetPinValue(u8x8, msg), INPUT_PULLUP);

was set the line

pinMode(u8x8_GetPinValue(u8x8, msg), OUTPUT);

causes the pin going for a short glitch to low and then back to high again.

digitalWrite(u8x8_GetPinValue(u8x8, msg), 0);

sets the pin finally to low.

Switching the order of the two mentioned lines to

digitalWrite(u8x8_GetPinValue(u8x8, msg), 0);
pinMode(u8x8_GetPinValue(u8x8, msg), OUTPUT);

works for me!

@espHorst
Copy link

espHorst commented Jun 4, 2020

Maybe at least for STM32 those two lines can be switched in the code?

@olikraus
Copy link
Owner

olikraus commented Jun 5, 2020

@espHorst Any url to the code line?

Repository owner deleted a comment from dalwinder1578singh Jun 5, 2020
@espHorst
Copy link

espHorst commented Jun 7, 2020

@olikraus : Yes, sorry, quite new to github, here it is:

pinMode(u8x8_GetPinValue(u8x8, msg), OUTPUT);

@olikraus
Copy link
Owner

ok... but does this make sense? I mean, the mode was input before and needs to be switched to output first.

Well, I keep the comments here in the hope it will be useful for others.
Probably I need to know more about digitalWrite implementation for STM32

@espHorst
Copy link

In fact this seems to be an issue of the STM hardware, not of the software implementation.
Here is an article describing this effect:
https://community.particle.io/t/can-i-not-call-digitalwrite-before-pinmode/49920
The article proposes the same solution as I proposed above, here a citation from the article:
"I think the only way to completely avoid a glitch is to set the port’s data register before the direction register."
I did measurements on my scope and I fully agree to this article. When writing an output to low then for the STM32 you definitely have to set at first the data register then the port mode register, otherwise you get a High-Low-High glitch on the data line.
I did not encounter this issue on the ATMEGA328.

@olikraus
Copy link
Owner

Thanks for the input. Nevertheless I hesitate to implement this because I don't know the impact on other platforms.

@konkav-js
Copy link
Author

Hi. I am no programmer, so what I am going to ask may be stupid, but it sound logical to me. For every microcontroller family, there are people, who develop the Arduino core. For me it looks like they are fairly stable, and thing as Serial, SPI, ADC, I2C works rather well. But at the same time, it looks to me, that this library also implements the same protocols. But why not just use the core's possibilities? Something like this (not a valid code, just to demonstrate):

#include <Wire.h>
TwoWire wire1(PB7, PB6);

#include <U8g2lib.h>
U8G2_SSD1306_128X32_UNIVISION_F oled1(U8G2_R0, wire1); // here we give the defined I2C

void setup() {
  wire1.begin();
  oled1.begin(); }

void loop() {
  oled1.clearBuffer();
  oled1.setFont(u8g2_font_ncenB14_tr);
  oled1.drawStr(0, 20, "Hello World 1!");
  oled1.sendBuffer(); }

And then, this library could be "HW-agnostic", at least, in terms of the uC. And the library should just calculate the bytes, what needs to be sent, and just dump it to the I2C object, in my example wire1, and just let it deal with it.

So, not wanting to just blab here, I would like to ask a question in regard to this issue: is there a possibility to use second HW I2C? My program would be timing sensitive, I would have two interrupts, one is for keeping a fan at steady RPM (just a convenient way of making a rotating disk of mirrors turn in constant angular velocity), and the second would be for timing of the different parts of this disk, to start sending out a stream of bits (triggering the laser driver). And I am afraid, that those interrupts would mess up a SW I2C (or worse, if the interrupts would be disabled in the SW I2C writing mode, it would mess up the trigger, because a display refresh in SW is 150ms). I decided to use STM32, because for me it looks like it is a nice uC, it has a DMA, and is working az 72MHz, so looks like there is enough resource, has enough pins, not like ESP8266.

Sorry for long writing, and thanks in advance.

@olikraus
Copy link
Owner

olikraus commented Jun 15, 2020

For me it looks like they are fairly stable, and thing as Serial, SPI, ADC, I2C works rather well.

I would say, this is more a wish then reality. From far distance this is true, unfortunately there are a lot of details , which differ a lot. You can also say this: The Arduino core is not defined good enough to have a stable, reliable and portable implementation, so all those implementations differ here and there. For a library developer (like me) it had beomce a nightmare to support all those flavors of so called "Arduino cores". Actually I gave up and just support the standard official Arduino core.

Just an example: For ESP SPI modes 2 & 3 had been swapped: esp8266/Arduino#2416

Many people asked me to modify u8g2 / u8glib, but actually it was a bug in the Arduino core for ESP chips. So i finally made pictures and created an issue and pull request. Still it took 3 (three) years to fix this.

Your idea regarding passing the object itself is nice (but not new #1083)

U8G2_SSD1306_128X32_UNIVISION_F oled1(U8G2_R0, wire1); // here we give the defined I2C

There are two things why I again hesitate:

  1. Arduino users are not used to pass those objects to a lib. Almost all libs just use those com objects internally (same thing what happens in u8g2 also)
  2. It would require a huge rework on u8g2. There are also special cases to consider, e.g. if a I2C interface doesn't exist at all. On those uC I also have to disable the constructor (because TwoWire class doesn't exist)

@olikraus
Copy link
Owner

olikraus commented Jul 5, 2020

closing this, #1083 is still open.

@olikraus olikraus closed this as completed Jul 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants