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

Thread: i2c betwee two 4.0s: Do I need pull up resistors?

  1. #1
    Junior Member
    Join Date
    Jan 2023
    Posts
    10

    i2c betwee two 4.0s: Do I need pull up resistors?

    I am making a split keyboard with a pair of 4.0s and I am unable to get a basic master read sample working. Do I need pull up resistors for this scenario? I'm using a common ground and pins 18 and 19. Any tips would be appreciated.

    slave code:
    Code:
    #include <Arduino.h>
    #include <i2c_driver.h>
    #include <i2c_driver_wire.h>
    #include <i2c_register_slave.h>
    void requestEvent();
    
    int led = LED_BUILTIN;
    
    void setup()
    {
      Serial.begin(9600);
      pinMode(led, OUTPUT);
      Wire.begin(8);        // join i2c bus with address #8
      Wire.onRequest(requestEvent); // register event
    }
    
    void loop(){
      Serial.println("hey");
    
      delay(100);
    }
    
    // function that executes whenever data is requested by master
    // this function is registered as an event, see setup()
    void requestEvent()
    {
      digitalWrite(led, HIGH);      // briefly flash the LED
      Wire.write("hello ");     // respond with message of 6 bytes
                                    // as expected by master
      digitalWrite(led, LOW);
    }
    master code:
    Code:
    #include <i2c_driver.h>
    #include <i2c_driver_wire.h>
    #include <i2c_device.h>
    void setup() {
      Wire.begin();        // join i2c bus (address optional for master)
      Serial.begin(9600);  // start serial for output
    }
    
    void loop() {
      Wire.requestFrom(8, 6);    // request 6 bytes from peripheral device #8
    
      while (Wire.available()) { // peripheral may send less than requested
        char c = Wire.read(); // receive a byte as character
        Serial.print(c);         // print the character
      }
    Serial.println("hey");
      delay(500);
    }

  2. #2
    Member
    Join Date
    Oct 2017
    Location
    Roskilde, Denmark
    Posts
    20
    Yes, the i2c interface cant work without Pull-Up's. It's an Open-Drain bus structure
    It's due to the fact, no output will ever pull the given wire (SCL/SDA) HIGH, only LOW, so to be able to ever go HIGH a pull up is required.
    @3.3V 100-400Khz and short wires 4K7 resistors is fine, but if You want to go to 1MHz, you will need to lower the resistors to 1K8 - 2K2 Ohm

    https://en.wikipedia.org/wiki/I%C2%B2C

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,692
    You really should use real pullup resistors, between 1K to 4.7K. But if you omit them, if using the Wire library on Teensy 4.0, the weak internal pullup resistors are activated. They result in a weak / slow pullup with is far from ideal, but usually works if the wires aren't long.

    Not every Teensy model turns on its internal pullups when using I2C. Teensy 3.2, 3.5, 3.6 in particular do not. With those you must have a real pullup resistor. You can pretty easily check with a voltmeter. If the SDA or SCL signal isn't 3.3V when idle, that's a sure sign you need to add a real resistor.

    Quote Originally Posted by DrGarbisk View Post
    Any tips would be appreciated.
    First check which software version you have installed. If older than 1.57, update. Old versions only supported I2C master mode. In Arduino 1.8.x, click Help > About. In Arduino 2.0.x, click the Boards Manager and search for "Teensy".

    Your code is including i2c_driver.h and i2c_driver_wire.h. Maybe this library really supports Teensy, or maybe not, or maybe only in master mode? I don't know, as I'm not familiar with that particular library. But I do know for sure that Wire works. The master_reader and slave_sender examples definite do work. Maybe open those from File > Examples > Wire. If you really need that other library, just for the sake of testing you could run these known-good examples to make sure everything is really working first.

  4. #4
    Junior Member
    Join Date
    Jan 2023
    Posts
    10
    I think I have a capacitance problem (or something similar). I got my i2c bus working between that two keyboard halves without pull up resistors. However, whenever I press a key in the 5th or 6th column to be read i get the timeout most of the time. Adding 2K pull up resistors has no effect. The interesting part is that it is independent of the column. If I reorder the column pins in the array same result. The last two columns in the array of IO pins are the only ones to have this problem.

    Here is the code I'm using to read the key pins

    Code:
    //################### keyboard #########################
    
    
    byte rows[] = { 23, 22, 21, 20, 15, 14, 9 };
    const int rowCount = sizeof(rows) / sizeof(rows[0]);
    
    byte cols[] = { 5, 4, 3, 2, 1, 0 };
    const int colCount = sizeof(cols) / sizeof(cols[0]);
    
    byte keyStates[colCount][rowCount];
    
    
    void setupKeys() {
    
      initKeyStates();
    
      Keyboard.begin();
      for (int x = 0; x < rowCount; x++) {
        pinMode(rows[x], INPUT_DISABLE);
      }
      for (int x = 0; x < colCount; x++) {
        pinMode(cols[x], INPUT_DISABLE);
        //digitalWrite(cols[x], LOW);
      }
    }
    
    bool readKeys(int keyBufSize, byte* keysBuffer) {
      int keyIdx = 0;
      bool keyRead = false;
      for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
        byte row = rows[rowIdx];
        pinMode(row, INPUT_PULLDOWN);
        for (int colIdx = 0; colIdx < colCount; colIdx++) {
          byte col = cols[colIdx];
          pinMode(col, OUTPUT);
          digitalWrite(col, HIGH);
          delayMicroseconds(5);
          byte btnState = digitalRead(row);
          digitalWrite(col, LOW);
          pinMode(col, INPUT_DISABLE);
    
          if (keyStates[colIdx][rowIdx] != btnState) {  // check if button state changed
            keyRead = true;
            if (keyIdx >= keyBufSize) {  // Only so many button presses can be tracked and sent at one time via i2c
              println("Key buffer full");
              return true;  // buffer full
            }
            // col row nonce state
            // 111 111 1     1
            byte state = colIdx;
            state = state << 3;
            state += rowIdx;
            state = state << 2;
            state += 2;  // flip the empty bit as a nonce so we can distinguish between no value and 0,0 released
            state += btnState;
            keysBuffer[keyIdx++] = state;
          }
    
          keyStates[colIdx][rowIdx] = btnState;
        }
        pinMode(row, INPUT_DISABLE);
      }
    
      return keyRead;
    }
    
    
    void initKeyStates() {
      for (int rowIdx = 0; rowIdx < rowCount; rowIdx++) {
        for (int colIdx = 0; colIdx < colCount; colIdx++) {
          keyStates[colIdx][rowIdx] = 0;
        }
      }
    }
    Last edited by DrGarbisk; 02-27-2023 at 09:53 PM.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,692
    Quote Originally Posted by DrGarbisk View Post
    Here is the code I'm using to read the key pins
    I don't understand why it seems to read only 1 row after driving 1 column high? Also don't understand why you're changing the pinMode on rows?

    A more traditional approach would be the leave the rows always configured as inputs with weak pull resistors. Then configure 1 column as active output drive and all the other columns as tri-state, wait the signal settling time (ideally longer than only 5 microseconds) and then read the result on all the rows. Repeat for driving each column with all the others high impedance. You can allow longer settling time and still achieve better overall performance if you read all the rows.

  6. #6
    Junior Member
    Join Date
    Jan 2023
    Posts
    10
    I give that a try. This code does work reliably to read the keys on both halves when they are the primary. This issue only occurs when reading from the secondary via i2c. Which is the odd part.

    Wha does "tri-state" mean for a digital pin?

    Thanks for the help so far.

  7. #7
    Junior Member
    Join Date
    Jan 2023
    Posts
    10
    So I get the pattern you recommended a try and it had no effect. I did work around the problem by not scanning the keys during the event handler. Not sure I like that though. I may switch to a pattern where the slave writes to the master.

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,692
    Quote Originally Posted by DrGarbisk View Post
    I did work around the problem by not scanning the keys during the event handler.
    Glad you found a solution.

    Sounds like there may be some sort of unanticipated interaction with the rest of your program (which we can't see) if running the same function from different context causes it to work differently.

  9. #9
    Junior Member
    Join Date
    Jan 2023
    Posts
    10
    I think I'd need a scope hooked up to the i2c circuit to really figure this out.

    Now I'm on to a new problem around Keyboard.press() notworking for keys like backspace and ctrl. But I'll start a new post for that. Thanks for the input on this one!

Posting Permissions

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