Hi,

this will be a long post with a lot of questions, if it would be better to split in separate ones, please let me know.

I want to create little modules with an SSD1306 OLED and an RGB encoder (https://www.sparkfun.com/products/10982). These modules should be somehow daisy chainable and I want to have many of them (about 32).

The multiobjective optimization problem I am trying to solve is:
- I have only limited time designing the circuits, pcbs and software
- Since I need 32 of those, costs should not be too high.

My current plan is as follows:
- a teensy 3.5 (5V tolerance is necessary, as discussed below) as a master unit
- little pcbs for 2 OLEDs and 2 encoders each that are driven by an ATMEGA 328p. This chip just has enough pins for this task. It will run at 5v.
- The connection between the teensy and the "modules" is realized by i2c. Since the ATMEGA 328 runs at 5v and the teensy at 3.3v, I would use the i2c pullups to 5v.
- The reason for 5v is that the LEDs in the encoders have a forward voltage of 3.3v to 3.6v and I guess I cannot drive them from a 3.3v MCU. Furthermore, running the ATMEGA 328 at 3.3v means only 8MHz, which is slow.

Ok, now a few difficulties:
- These OLEDs have a pixel size of 128x64, which means storing a buffer takes about 1 kB ram.
- Driving those OLEDs via i2c is horribly slow, about 20 ms for one OLED, makes more than half a second if all OLEDs have to be updated.
- The AVRs have not enough ram to store a separate buffer for two displays. Furthermore, drawing more complex shapes on the screen is also beyond the computation power of those AVRs.
Therefore, I decided to drive the OLEDs NOT via the AVRs on each module, but run two/three separate lines to each module for SPI. Filling the complete screen via SPI at 10MHz with a teensy 3.2 (AVR much slower) takes about 1ms, which is close to the optimal value.
I successfully connected 8 OLEDs to a teensy 3.2. It takes about 16 ms to draw a complete picture on all those displays. It is such a pleasure to be able to have enough ram and computation power on this platform!!!

This decision, however, imposes the problem that each module would need to have two separate chip select lines for the displays, which I do not like.
A solution to this is that the chip select is performed by the AVRs. The SPI communication would then work by first sending a message to the module via i2c and the do the SPI stuff from the teensy.


Now to the questions/problems:
1) Do you see another way of achieving this? Multiplexing 32 encoders, 32 buttons (the push of the encoder) and 32 RGB LEDs with PWM seems to be a bit challenging with shift registers?!?

2) Is it ok to use i2c pullups to 5v?

3) Do I really need 5v for powering those LEDs in the encoders? Or can I just add a lets say 10Ohm resistor and drive from 3.3v? (Since those encoders are not available at the moment I cannot test this)

4) Are people interested in this project? The goal is to make this a midi control surface like an avid S6 knob module

5) What kind of operations is one allowed to perform before the void setup() routine is called? Is there any documentation on this? I am asking since I needed to modify the adafruit SSD1306 library to be able to have own buffers for each display and other bits and pieces.

6) I have a problem with connecting a teensy 3.2 with an arduino (in this case a mega for testing purposes) via i2c.
Teensy code:
Code:
#include "ssd1306.h"

#define OLED_DC     14
#define OLED_CS1    2
#define OLED_RST    15

//#include <i2c_t3.h>
#include <Wire.h>

SSD1306* display1;


long startTime;
long endTime;
char cstr[16];

void setup() {
  display1 = new SSD1306(OLED_DC, OLED_RST, OLED_CS1);

  //Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
  Wire.begin();
  Wire.setClock(400000);
  
  SPI.begin();
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));

  
  Serial.begin(9600);
  Serial.println("ready");
  delay(100);

  Wire.beginTransmission(1); 
  Wire.write('S');
  Wire.endTransmission();
  //Wire.requestFrom(1, 1);
  display1->begin(SSD1306_SWITCHCAPVCC);
  display1->clearDisplay();
  display1->display();
  Wire.beginTransmission(1); 
  Wire.write('D');
  Wire.endTransmission();
  //Wire.requestFrom(1, 1);
}

long t;

void loop() {

  startTime = micros();
  
  display1->clearDisplay();
  display1->setTextSize(2);
  display1->setTextColor(WHITE);
  display1->setCursor(0,0);
  display1->println(t);
  display1->fillRect(0, 33, 128, 15, 1);
  display1->fillRect(0, 49, 100, 15, 1);
  Wire.beginTransmission(1); 
  Wire.write('S');
  Wire.endTransmission();
  //Wire.requestFrom(1, 1);
  
  delayMicroseconds(50);
  display1->display();
  
  Wire.beginTransmission(1); 
  Wire.write('D');
  Wire.endTransmission();
  //Wire.requestFrom(1, 1);
  
  
  endTime = micros();
  t = endTime - startTime;
  
  delay(1000);
}
Arduino code:
Code:
#include <Wire.h>

#define CSPin 2

bool mode;

void setup() {
  pinMode(CSPin, OUTPUT);
  digitalWrite(CSPin, HIGH);
  Wire.begin(1);          
  //Wire.setClock(400000);      
  Wire.onReceive(receiveEvent); // one variant
  //Wire.onRequest(requestEvent); // other variant
  Serial.begin(9600);

  mode = false;
}

void loop() {}

void receiveEvent(int howMany) {
  char cmd = Wire.read();
  if(cmd == 'S') {
    digitalWrite(CSPin, LOW); // one variant
    mode = true;
  } else {
    digitalWrite(CSPin, HIGH); // one variant
    mode = false;
  }
}
This code works and is fast, since it uses 400kHz i2c. However, if I remove the delayMicroseconds(50); before writing the buffer to the display, it does not work anymore. This is something I do not understand fully. Is it correct that the i2c communication has ended before the Wire.onReceive callback is finished? Then this behavior would make sense. Since I do not like delays in the code, I tried a different communication protocol
Teensy code:
Code:
#include "ssd1306.h"

#define OLED_DC     14
#define OLED_CS1    2
#define OLED_RST    15

//#include <i2c_t3.h>
#include <Wire.h>

SSD1306* display1;


long startTime;
long endTime;
char cstr[16];

void setup() {
  display1 = new SSD1306(OLED_DC, OLED_RST, OLED_CS1);

  //Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
  Wire.begin();
  //Wire.setClock(400000);
  
  SPI.begin();
  SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));

  
  Serial.begin(9600);
  Serial.println("ready");
  delay(100);

  Wire.beginTransmission(1); 
  Wire.write('S');
  Wire.endTransmission();
  Wire.requestFrom(1, 1);
  display1->begin(SSD1306_SWITCHCAPVCC);
  display1->clearDisplay();
  display1->display();
  Wire.beginTransmission(1); 
  Wire.write('D');
  Wire.endTransmission();
  Wire.requestFrom(1, 1);
}

long t;

void loop() {

  startTime = micros();
  
  display1->clearDisplay();
  display1->setTextSize(2);
  display1->setTextColor(WHITE);
  display1->setCursor(0,0);
  display1->println(t);
  display1->fillRect(0, 33, 128, 15, 1);
  display1->fillRect(0, 49, 100, 15, 1);
  Wire.beginTransmission(1); 
  Wire.write('S');
  Wire.endTransmission();
  Wire.requestFrom(1, 1);
  
  //delayMicroseconds(50);
  display1->display();
  
  Wire.beginTransmission(1); 
  Wire.write('D');
  Wire.endTransmission();
  Wire.requestFrom(1, 1);
  
  
  endTime = micros();
  t = endTime - startTime;
  
  delay(1000);
}
Arduino:
Code:
#include <Wire.h>

#define CSPin 2

bool mode;

void setup() {
  pinMode(CSPin, OUTPUT);
  digitalWrite(CSPin, HIGH);
  Wire.begin(1);          
  //Wire.setClock(400000);      
  Wire.onReceive(receiveEvent); // one variant
  Wire.onRequest(requestEvent); // other variant
  Serial.begin(9600);

  mode = false;
}

void loop() {}

void receiveEvent(int howMany) {
  char cmd = Wire.read();
  if(cmd == 'S') {
    mode = true;
  } else {
    mode = false;
  }
}

void requestEvent() {
  if(mode) {
    digitalWrite(CSPin, LOW);
  } else {
    digitalWrite(CSPin, HIGH);
  }
  Wire.write('K');
}
The problem with this code is that it somehow does not work with 400kHz. The Wire.onReceive callback is called, but the Wire.onRequest not. If I change to 100kHz, then everything works. Pullups are 3.9k. The downside of this protocol with 100kHz is that the time to update the display increases by nearly 1 ms. Is i2c really this slow? I mean I am only sending 1 byte each, makes 8 bytes in total if I am not mistaken (counting the address byte), is there so much overhead in the function calls?



7) I am driving the CS select pin of the SSD1306 display with 5v from the arduino. I tested it for 2 days now, no smoke whatsoever. However, I do not find in the datasheet anything about this being allowed nor disallowed. Very strangely, if I disconnect the power from the teensy, the teensy is still somehow powered from the CS pin.



Thanks again for the teensy products!