Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 12 of 12

Thread: SSD1306 Library crashing Teensy 2.0

  1. #1

    SSD1306 Library crashing Teensy 2.0

    I love these little 128x64 OLED displays, and have used them in many projects. However, I'm encountering a very odd issue. I have a menu driven system, and use a rotary encoder to select items on the menu. Pictures say it all, so here is a picture of the type of menu driven interface I am using:

    Click image for larger version. 

Name:	IMG_1618r.jpg 
Views:	17 
Size:	78.2 KB 
ID:	12370

    When I turn the encoder knob, the highlight will scroll through Selection #1 through #4. The problem is, my Teensy 2.0 is crashing after I spin the knob a few times. The highlight will scroll through all of the selections a few times, and then it will hang.

    Through a careful set of Serial.println lines, I have determined that it is crashing at the I2C write within the Adafruit_SSD1306 library, called when I call display.display(). I have plenty of memory (1156 bytes of dynamic memory), and see no indication that I am running out of memory.

    There is something interesting I've noticed though. If you look at my code, you'll see that my println lines to the OLED have spaces that go all the way to the right side of the screen. This is so the highlight extends all the way across.

    Code:
    display.println(F("Selection #1         "));
    If I remove one of the spaces, the crashes are less frequent, two spaces and even less frequent... Removing four spaces from the end seems to eliminate the crashes, though maybe if I play with it long enough I can get it to happen. That's a workaround, but doesn't seem to solve the real problem, and I can't have my code crashing even once in a while.

    Any tips on what might be going on would be really appreciated. Or even how to debug the problem.

    Here is source code to reproduce the issue. I've cut it down to the bare minimum to reproduce the problem:

    Code:
    #include <Wire.h>
    #include <Adafruit_SSD1306.h>
    
    #define ENC_A   14
    #define ENC_B   15
    
    #define OLED_RESET 17
    Adafruit_SSD1306 display(OLED_RESET);
    
    int encoderPos, encoderPosPrev;
     
    void setup()
    {
    
     pinMode(ENC_A, INPUT_PULLUP);
     pinMode(ENC_B, INPUT_PULLUP);
    
     display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // OLED I2C Address, may need to change for different device,
                                                // Check with I2C_Scanner
      updateSelection();
    
    }
    
    
    void loop()
    {
      updateEncoderPos();  
    }
    
    void updateEncoderPos() {
        static int encoderA, encoderB, encoderA_prev;   
    
        encoderA = digitalRead(ENC_A); 
        encoderB = digitalRead(ENC_B);
          
        if((!encoderA) && (encoderA_prev)){ // A has gone from high to low 
          encoderPosPrev = encoderPos;
          encoderB ? encoderPos++ : encoderPos--;   
          updateSelection();
        }
        encoderA_prev = encoderA;     
    }
    
    
    void updateSelection() { // Called whenever encoder is turned
      display.clearDisplay();
      display.setCursor(0,0); 
      display.setTextColor(WHITE,BLACK);
      display.print(F("SELECTIONS"));
      display.setCursor(0,16);
      setHighlight(0,4);
      display.println(F("Selection #1         "));
      setHighlight(1,4);
      display.println(F("Selection #2         "));
      setHighlight(2,4);
      display.println(F("Selection #3         "));
      setHighlight(3,4);
      display.println(F("Selection #4         "));
    
      display.display(); 
    }
    
    void setHighlight(int menuItem, int numMenuItems) {
      if (mod(encoderPos, numMenuItems) == menuItem) {
        display.setTextColor(BLACK,WHITE);
      }
      else {
        display.setTextColor(WHITE,BLACK);
      }
    }
    
    int mod(int a, int b)
    {
        int r = a % b;
        return r < 0 ? r + b : r;
    }

  2. #2
    A few more observations: it seems to be more likely to crash if I spin the encoder knob fast. I can turn the knob slowly all day with no problems.

    Also, I have seen a crash at least once now when removing four spaces from the end of my println statements, i.e.,
    Code:
    display.println(F("Selection #1     "));
    instead of
    Code:
    display.println(F("Selection #1         "));
    So my workaround really isn't a workaround. It just makes the problem much less likely to occur.

  3. #3
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    2,068
    What does this F("something") function? What happens when you put your bare text into println() without that function? My guess is that it is somewhat very slow and ressource consuming...

  4. #4
    F() is a macro that tells the compiler to store and access the string from flash memory rather than RAM, saving the RAM for more important things. I believe Paul Stoffregen wrote this macro ages ago, and it is certainly a useful one to know. I have a lot of these menu driven screens in my code, and would quickly run out of RAM if I didn't use it.

    Nevertheless, I did try removing this macro to see if it makes any difference. It didn't. Good suggestion to try though.

  5. #5
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    1,643
    Quote Originally Posted by elkayem View Post
    A few more observations: it seems to be more likely to crash if I spin the encoder knob fast. I can turn the knob slowly all day with no problems.
    I've tried to reproduce your problem here with T2 and OLED (but only 128x32, no rotary encoder) and just touching pins 14 and 15 to ground. I couldn't get it to fail, but that jives with your turning knob slowly. Further study required ...

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,686
    Can you measure the voltage on SDA and SCL when it gets into the locked up state?

  7. #7
    Quote Originally Posted by PaulStoffregen View Post
    Can you measure the voltage on SDA and SCL when it gets into the locked up state?
    Hi Paul, I sense you're on to something with your question.

    I looked at SCL and SDA on my scope. When the message gets through (no crash), it appears to me that a full I2C command to the display takes about 45 ms (it's a lot of data). When the Teensy crashes, the message is cut short, with the SCL held high, but the SDA held low.

    Here are two images. The first shows a full message:
    Click image for larger version. 

Name:	DS1Z_QuickPrint4.png 
Views:	12 
Size:	75.6 KB 
ID:	12371

    The second shows what happens when I get a crash. I only see part of the message, less than 5 ms in this case:
    Click image for larger version. 

Name:	DS1Z_QuickPrint6.png 
Views:	12 
Size:	45.0 KB 
ID:	12372

  8. #8
    And a close-up of the I2C message as it met its untimely end.

    Click image for larger version. 

Name:	DS1Z_QuickPrint7.png 
Views:	15 
Size:	47.7 KB 
ID:	12373

  9. #9
    And one more example of the I2C when the chip freezes up. This one is interesting, as it looks like SCK freezes up, but there is one more bit of data that gets sent. Still the same at freeze up (SCL high, SDA low). That seems true for all the cases I've seen, though the moment at which it freezes always seems to be different.

    Click image for larger version. 

Name:	DS1Z_QuickPrint8.png 
Views:	14 
Size:	40.3 KB 
ID:	12374

  10. #10
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,686
    We see something like this occasionally with Teensy 3.2. It's pretty rare on Teensy 2.0, but perhaps still possible?

    If noise or interference couples into the I2C signals, it's possible for Teensy to believe it has lost I2C bus control to another bus master. But there isn't another master talking on the bus, only noise.

    Those rise times look pretty slow. You might first try adding 2K or even 1K pullup resistors on SDA and SCL. Maybe that will just magically fix the problem? Adding a small capacitance, like 47 or 100 pF between each signal to GND (close to the Teensy) might also help. If the signals run a fairly long distance, you might consider whether they're properly shielded from cross-talk of other digital signals. If some other digital signal runs close by for some distance, especially in the same cable, adding a 100 ohm resistor between the driving pin and the cable can really help.

  11. #11
    Interesting idea. Iíll give these ideas a try when I get an opportunity. I can also try slowing down the I2C clock. It is running at 400K, as you noted by the short rise time. I do wonder, if Teensy thinks there is another master, who is holding SDA low?

  12. #12
    Problem possibly resolved... I swapped out the OLED for another identical one, and the problem went away (so far). I twirled the wheel for several minutes and couldn't get it to recur. When I go back to the original OLED, the problem comes back. So it appears to be a HW issue. When I see the microcontroller hang up, I usually assume it is a SW issue. (With all the boneheaded mistakes I make in code, it usually is!) It's a good lesson not to assume all the HW is running as intended. Time will tell if it is gone permanently. My guess is that the transients from the rotary encoder were disrupting the I2C communication. I wouldn't see it if I turned the knob slowly, but if I turned it fast enough, I was probably grounding the encoder pin in the middle of an I2C message. For whatever reason, it appears one of my OLEDs was sensitive to this, but the other was not. It seems odd that this would cause the Teensy to completely hang though. I couldn't even reprogram it without power cycling!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •