Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 45

Thread: i2c slave controller for multiple slave arduino/teensies

  1. #1
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714

    SPI & I2C slave controller code for multiple slave arduino/teensies

    little project im working on that im building up if anyone is interested, its a library for the master to control a slave arduino(or teensy), the slave doesnt run any libraries, only a sketch with defines and switch statements. the first video demonstrates 2 lcds running off a mega slave at 200,000baud, and while hotplugging the displays, the host library is able to resync fine over the i2c bus.

    the second video demonstrates, in addition to the 2 running lcds in the first video, ive attached an mcp23s17 SPI expander on the slave and the master was able to poll its register bank over the i2c bus

    hooking up 2 teensy 3.5/3.6 should gain you 12 hardware uarts accessible on a single mcu

    analogread (uint16_t) and analogwrite (uint16_t) support is included, as well as pinMode, digitialWrite, and digitalRead

    note: i could have made an SPI mcu controller as well, but attaching 2 or more slaves would lead to a wiring mess.
    note: now its possible to have additional SPI busses, running over i2c. Should we call it 2-wire spi?


    https://m.youtube.com/watch?v=U2Pn0HOlUQo 200k baud uart over i2c

    https://m.youtube.com/watch?v=3SYlMVwZRGw spi over i2c
    Last edited by tonton81; 10-01-2017 at 04:24 PM.

  2. #2
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    here we initialize the objects we will be using: (interface, slave address, wirePort (0,1,2?))
    Code:
    I2CmcuController Serial50 = I2CmcuController("Serial", 9, &Wire);
    I2CmcuController Serial60 = I2CmcuController("Serial1", 9, &Wire);
    I2CmcuController Serial61 = I2CmcuController("Serial2", 9, &Wire);
    I2CmcuController Serial62 = I2CmcuController("Serial3", 9, &Wire);
    I2CmcuController gpio     = I2CmcuController("GPIO",    9, &Wire);
    I2CmcuController SPI66    = I2CmcuController("SPI",    9, &Wire);
    serial baudrates:
    Code:
      Serial60.begin(200000);
      Serial62.begin(200000);
    gpio controls:
    Code:
      gpio.pinMode(13, OUTPUT);
      gpio.digitalWrite(53, HIGH);
      gpio.digitalWrite(13, !gpio.digitalRead(13)); // toggle led
      Serial.println(gpio.analogRead(A0)); // read analog port
    spi:
    Code:
      gpio.digitalWrite(53, HIGH);
      SPI66.begin();
      SPI66.beginTransaction();
      gpio.digitalWrite(53, LOW);
      SPI66.transfer(0x40); // Write command to init all chips.
      SPI66.transfer(0x0A); // Write IOCONA register
      SPI66.transfer(0x18); // DISSLW & HAEN on, other bits off.
      gpio.digitalWrite(53, HIGH);
      SPI66.endTransaction();
      SPI66.beginTransaction();
      gpio.digitalWrite(53, LOW);
      SPI66.transfer(0x4E); // Write command to init all chips.
      SPI66.transfer(0x0A); // Write IOCONA register
      SPI66.transfer(0x18); // DISSLW & HAEN on, other bits off.
      gpio.digitalWrite(53, HIGH);
      SPI66.endTransaction();
    i couldnt figure out why spisettings was freezing when dynamically being set, so they are statically defined in the slave sketch, and the command falls onto that when beginTransaction() is called.

  3. #3
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    has anyone tested Wire as slave on teensy?

    test run of 2560 master to 2560 slave was a success of gpios, uarts, and spi, however, teensy as an i2c slave tends to crash for some reason, so ill be debugging to see whats going on to see where the data is getting lost or whatever, but rebooting the master seems to cure the issue until a few secs/mins later where the i2c halts again. this type of behaviour is seen if there is a byte missing or extra appearing on the i2c bus, have not had any issues with the 2 megas running for about a week now, its only now i tried the wire slave sketch on teensy and testing only the uart data, so only uart and wire are used, tomorrow will be another day for tests

  4. #4
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    just a note:

    mega2560 master to mega2560 slave was ok
    mega2560 master to teensy 3.5 slave got very intermittant i2c lockups

    i dropped 120mhz for the slave down to 24MHz and so far it seems to be running non stop! ill keep it running till tomorrow now

    does this mean i2c is more stable at slower cpu speeds?
    Last edited by tonton81; 09-25-2017 at 01:32 AM.

  5. #5
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Here it is:

    mega2560 master controlling teensy 3.5 slave set at 24MHz
    2 lcds running at 625,000 serial baud rates over the i2c bus

    master contains lcd library (unmodified) and slave control library
    slave contains only a sketch .

    https://www.youtube.com/watch?v=DR5b2hqNYEs


    toggling led at same time over i2c as well:
    Code:
      if ( millis() - tyme > 800 ) {
        tyme = millis();
        gpio.digitalWrite(13, !gpio.digitalRead(13)); // toggle led
      }
    https://www.youtube.com/watch?v=8My-nLKtOI8
    Last edited by tonton81; 09-25-2017 at 01:03 AM.

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,186
    I've tested I2C slave mode many times, but mostly just simple tests using the examples that come with the Wire library.

    Do you want me to attempt to investigate this issue?

  7. #7
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    yeah ill send you it later, i havnt posted it yet because i never made a release before so i pretty much have no headers or readme, if someone here wants to help out with that stuff im sure we can contribute this to the commuunity

  8. #8
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    also im also working on the spi version but having troubles to get t3.5 to behave as an spi slave, once i figure it out i will work on mcu controller code for spi bus, but the i2c is pretty stable, especially hammering 2 lcds at 625k baud (at 24mhz)

    i never tried other cpu speeds because im working on the spi code atm

  9. #9
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    mcuUart.zip

    Here is the I2C controller WIP

    the gpios, and begin functions run through crc checks to make sure there will be no corruption (if ever) and no, theres been none at all so far (knock on wood)

    uarts, and spi busses dont do crc for streaming
    im sure there is great potential for this type of code, having extra spi busses and uarts from a spare mcu accessible within the same local sketch, it's alot cheaper to add a teensy than to buy an i2cuart adaptor with flaky library code, with benefits of several integrated hardware features... i plan to extend it with additional features if the need arises, but, just think of the gpio accessibility of it. it's alot easier to control single, or many t3.5/3.6 over i2c with 60 gpios than it is to run 4 mcp23017's to gain 64 gpios, in terms of accessibility, and small footprint, 2 teensies can easily replace an 8 chip bank of mcp23017 port expanders for about 120gpio access using a single host, a single sketch, a small footprint, a single class per teensy slave, with many additional hardware benefits included. Just try to control 8 different types of mcp23017 using adafruit's library, you will see how simple my code here will stand out regarding that:
    gpio <-- was the initializer, but you know as well as I that you can call it whatever you want
    expander1? ok..
    expander1.pinMode(13,OUTPUT);
    expander1.digitalRead(13);
    expander1.digitalWrite(13,HIGH);
    expander1.analogRead(A0);
    expander1.analogWrite(A0, 255);
    i try to make it simple ;P

    Also paul, the test with the i2c issue on 3.5 at 120mhz with the i2c bus locking up was tested on latest teensyduino using Faster, constantly read/writing to the uart channel, which never happened on the mega
    my initial attempts were to delay some of the transactions but it didnt work, my 2nd test was to drop to 24mhz cpu and thats when it was stable, i presume this is an issue with code being slave needs to be clocked lower, not sure, im not an expert on that.

    btw...

    The file here is the library, this is run on the master, be it arduino or teensy, both use Wire.h (i didnt use i2c_t3 because it doesnt support Wire onreceive etc for slave support???)

    The slave sketch is posted in CODE brackets, the initializers can be set using the posts above where i given examples
    Like i said, i dont have time to do readmes, headers, or prepare example files, or rename the files themself (mcuUart.h) it should have a new name since initially i started it as a remote uart controller cuz i needed additional uarts, also a benefit is the high speed uart access teensy->teensy, FIFO, and huge buffers over i2c that you cannot obtain using other expensive commercial single hardware interface use chips. I mean really, who does or has a chip in the market capable of 625K baud serial device transfers over the i2c bus?? lol I've also looked around, I dont even think i2c to SPI bus chips exist, but here we have a 2-wire SPI bus.
    Benefits?? Sure...

    Lets say you have an SPI device and you dont have additional busses for it, or no available gpios for CS, OR, perhaps you plan to install the device further away..... and want to use less wiring back to your host...
    this is ideal

    Enjoy it guys! Leave feedback if anything is worthwhile adding for support, ill mark it in my todo list
    Tony

    Code:
    #include <Wire.h>
    #include <SPI.h>
    
    volatile uint8_t PORT;
    volatile uint8_t PIN;
    volatile uint8_t bytes[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    volatile uint32_t spi_speed = 4000000;
    volatile uint8_t spi_byte;
    uint8_t checksum;
    
    void setup() {
      Wire.begin(1);
      Wire.onReceive(receiveEvent);
      Wire.onRequest(requestEvent);
      SPI.begin();
      Serial.begin(115200);
    }
    
    void loop() {
    }
    
    void receiveEvent(int howMany) {
      bytes[0] = Wire.read();
      switch ( bytes[0] ) {
        case 0: { // COMMAND BYTE
            if (Wire.available() != 11) { // wrong length of bytes for CMD/CRC, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            checksum = 0x00;
            for ( uint8_t i = 1; i <= 11; i++ ) bytes[i] = Wire.read();
            for ( uint8_t i; i <= 10; i++ ) checksum ^= bytes[i];
            Serial.print(bytes[0]); Serial.print(" : "); Serial.print(bytes[1]); Serial.print(" : "); Serial.print(bytes[2]); Serial.print(" : "); Serial.print(bytes[3]); Serial.print(" : "); Serial.print(bytes[4]); Serial.print(" : "); Serial.print(bytes[5]); Serial.print(" : "); Serial.print(bytes[6]); Serial.print(" : "); Serial.print(bytes[7]); Serial.print(" : "); Serial.print(bytes[8]); Serial.print(" : "); Serial.print(bytes[9]); Serial.print(" : "); Serial.print(bytes[10]); Serial.print(" : "); Serial.println(bytes[11]);
            if ( bytes[11] != checksum ) break; // bad checksum exit
            switch ( bytes[1] ) { // FUNCTION
              case 1: { // SERIAL PORT AND BAUD RATE
                  uint8_t baudrate_split[4] = {bytes[3], bytes[4], bytes[5], bytes[6]};
                  uint32_t baudrate = *(uint32_t*)&baudrate_split;
                  switch ( bytes[2] ) {
                    case 0: {
                        Serial.begin(baudrate); break;
                      }
    #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
                    case 1: {
                        Serial1.begin(baudrate); break;
                      }
                    case 2: {
                        Serial2.begin(baudrate); break;
                      }
                    case 3: {
                        Serial3.begin(baudrate); break;
                      }
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
                    case 4: {
                        Serial4.begin(baudrate); break;
                      }
                    case 5: {
                        Serial5.begin(baudrate); break;
                      }
                    case 6: {
                        Serial6.begin(baudrate); break;
                      }
    #endif
                  }
                  break;
                }
    
              case 2: { // PINMODE
                  pinMode(bytes[2], bytes[3]); break;
                }
    
              case 3: { // DIGITALWRITE
                  digitalWrite(bytes[2], bytes[3]); break;
                }
    
              case 4: { // SPI begin()
                  switch ( bytes[2] ) { // PORT
                    case 0: { // SPI
                        SPI.begin(); break;
                      }
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
                    case 1: { // SPI
                        SPI1.begin(); break;
                      }
                    case 2: { // SPI
                        SPI2.begin(); break;
                      }
    #endif
                  }
                  break;
                }
    
              case 5: { // SPI beginTransaction
                  uint8_t speed_split[4] = {bytes[3], bytes[4], bytes[5], bytes[6]}, msblsb = bytes[7], mode = bytes[8], port = bytes[2];
                  spi_speed = *(uint32_t*)&speed_split;
                  switch ( port ) { // PORT
                    case 0: { // SPI
                        SPI.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); break;
                      }
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
                    case 1: { // SPI1
                        SPI1.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); break;
                      }
                    case 2: { // SPI2
                        SPI2.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); break;
                      }
    #endif
                  }
                  break;
                }
            }
            break;
          }
    
        case 1: { // SEND A SERIAL BYTE
            if (Wire.available() != 2) {
              // wrong length of bytes for serial, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            for ( uint8_t i = 1; i <= 2; i++ ) bytes[i] = Wire.read();
            switch ( bytes[1] ) {
              case 0: {
                  Serial.write(bytes[2]); break;
                }
    #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 1: {
                  Serial1.write(bytes[2]); break;
                }
              case 2: {
                  Serial2.write(bytes[2]); break;
                }
              case 3: {
                  Serial3.write(bytes[2]); break;
                }
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 4: {
                  Serial4.write(bytes[2]); break;
                }
              case 5: {
                  Serial5.write(bytes[2]); break;
                }
              case 6: {
                  Serial6.write(bytes[2]); break;
                }
    #endif
            }
            break;
          }
    
        case 2: { // READ A SERIAL BYTE
            if (Wire.available() != 1) {
              // wrong length of bytes for serial read, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            PORT = Wire.read(); break;
          }
    
        case 3: { // AVAILABLE SERIAL BYTES
            if (Wire.available() != 1) {
              // wrong length of bytes for serial available, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            PORT = Wire.read(); break;
          }
    
        case 4: { // PEEK SERIAL BYTE
            if (Wire.available() != 1) {
              // wrong length of bytes for serial peek, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            PORT = Wire.read(); break;
          }
    
        case 5: { // digitalRead pin
            if (Wire.available() != 1) {
              // wrong length of bytes for digitalRead, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            PIN = Wire.read(); break;
          }
    
        case 6: { // analogRead pin
            if (Wire.available() != 1) {
              // wrong length of bytes for analogRead, FLUSH and EXIT!
              while ( Wire.available() > 0 ) Wire.read();
              return;
            }
            PIN = Wire.read(); break;
          }
    
        case 7: { // spi.transfer
            PORT = Wire.read(); uint8_t toSend = Wire.read(); spi_byte = SPI.transfer(toSend); break;
          }
    
        case 8: { // spi.endTransaction
            PORT = Wire.read();
            switch ( PORT ) {
              case 0: {
                  SPI.endTransaction(); break;
                }
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 1: {
                  SPI1.endTransaction(); break;
                }
              case 2: {
                  SPI2.endTransaction(); break;
                }
    #endif
            }
            break;
          }
    
    
        case 9: { // analogWrite
            PIN = Wire.read();
            uint8_t value_split[2] = {(uint8_t)Wire.read(), (uint8_t)Wire.read()};
            uint16_t value = *(uint16_t*)&value_split;
            analogWrite(PIN, value);
            break;
          }
    
          //    case 10: { // analogWriteResolution
          //        PIN = Wire.read();
          //        uint8_t res = Wire.read();
          //        analogWriteResolution(res);
          //        break;
          //      }
    
    
    
      }
    }
    
    void requestEvent() {
    
      switch ( bytes[0] ) { // COMMAND BYTE
        case 0x02: { // break; read byte from serial
            switch ( PORT ) {
              case 0: {
                  Wire.write(Serial.read()); break;
                }
    #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 1: {
                  Wire.write(Serial1.read()); break;
                }
              case 2: {
                  Wire.write(Serial2.read()); break;
                }
              case 3: {
                  Wire.write(Serial3.read()); break;
                }
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 4: {
                  Wire.write(Serial4.read()); break;
                }
              case 5: {
                  Wire.write(Serial5.read()); break;
                }
              case 6: {
                  Wire.write(Serial6.read()); break;
                }
    #endif
            }
            break;
          }
    
    
    
        case 0x03: { // break; available bytes on serial port
            switch ( PORT ) {
              case 0: {
                  Wire.write(Serial.available()); break;
                }
    #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 1: {
                  Wire.write(Serial1.available()); break;
                }
              case 2: {
                  Wire.write(Serial2.available()); break;
                }
              case 3: {
                  Wire.write(Serial3.available()); break;
                }
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 4: {
                  Wire.write(Serial4.available()); break;
                }
              case 5: {
                  Wire.write(Serial5.available()); break;
                }
              case 6: {
                  Wire.write(Serial6.available()); break;
                }
    #endif
            }
            break;
          }
    
    
    
        case 0x04: { // break; peek byte on serial port
            switch ( PORT ) {
              case 0: {
                  Wire.write(Serial.peek()); break;
                }
    #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 1: {
                  Wire.write(Serial1.peek()); break;
                }
              case 2: {
                  Wire.write(Serial2.peek()); break;
                }
              case 3: {
                  Wire.write(Serial3.peek()); break;
                }
    #endif
    #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
              case 4: {
                  Wire.write(Serial4.peek()); break;
                }
              case 5: {
                  Wire.write(Serial5.peek()); break;
                }
              case 6: {
                  Wire.write(Serial6.peek()); break;
                }
    #endif
            }
            break;
          }
    
    
    
        case 0x05: { // break; digital pin status
            Wire.write(digitalRead(PIN)); break;
          }
    
    
    
        case 0x06: { // break; analog pin value
            uint16_t value = analogRead(PIN);
            Wire.write((uint8_t)(value >> 8));
            Wire.write((uint8_t)value);
            break;
          }
    
    
    
        case 0x07: { // spi byte return
            Wire.write(spi_byte); break;
          }
    
    
    
    
      }
    
    }
    Last edited by tonton81; 09-26-2017 at 01:31 PM.

  10. #10
    Senior Member brtaylor's Avatar
    Join Date
    Mar 2016
    Location
    Portland, OR
    Posts
    343
    Looks interesting. I use Teensy as an I2C slave a bunch with the i2c_t3 library. There is onRequest and onReceive functionality available...
    https://github.com/nox771/i2c_t3#function-summary

  11. #11
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    it wouldnt compile when i replaced the headers, dont know why, hopefully it wasnt a mistake on my part, but if it was, sorry ;P

  12. #12
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    good news guys, and perhaps i need a suggestion

    good news! spi slave is working on teensy 3.5 using a single 15 line function in the slave code!
    i need a quick suggestion before i start adapting an SPI version of this code for improved high speed functions
    right now ill put teensy spi slave support, avr slave support i will add later, they will be switched automatically with the defines

    before i start i need to know whats best for the transfers
    should i keep the communication to teensy's native 16bit transfer mode or drop it to usual 8bit transfers (for compatibility, if any?) i know some of you might find this question silly, but it's not to me :P

    whats a good decision to make, keep it 8 or 16 for the mcu controller code?
    SPI.transfer16(0xFFFF); or SPI.transfer(0xFF);

    this i have to know before i start writing the packets
    also, another suggestion, should i (actually i should but i should ask anyways) CRC every interaction with the other controller with verification on success, and if fail, (for uart/i2c/spi/digital/analog etc data) to automatically resend 1-3 times before ending the function? or just stream the data but continue to CRC the main controls?

    btw, im transferring data at 8mhz from mega2560 to teensy using 16bit spi mode for testing, all the frames seem to be intact

    the mcp23s17 chip can do 10mhz, i wonder how fast teensy-teensy can go for remote GPIO?

    since teensy includes digital(x)fast in it's core, that will definately be added to integration

    I'd also like to point out for people not familar with mcu-mcu spi traffic (this first time i did this so... )
    MISO ON MASTER GOES TO MOSI ON SLAVE
    MOSI ON MASTER GOES TO MISO ON SLAVE
    Last edited by tonton81; 09-26-2017 at 06:15 PM.

  13. #13
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    i was just wondering the complexity of keeping transfers at uint16_t for performance (if there isnt any), for sure it will add complexity to the code, is it worth it? im still playing around with it

    so far I have it setup for 16bit transfers ready for command processing (crc) or streaming(non crc, concurrent transfers)

    Click image for larger version. 

Name:	aa7rj8.jpg 
Views:	50 
Size:	300.7 KB 
ID:	11626

    6x 16-byte transfers every 500 ms from mega2560 to teensy 3.5



    lets not forget the setup of wiring

    master, left: mega2560
    slave, right: teensy 3.5

    Click image for larger version. 

Name:	4hs6jn.jpg 
Views:	66 
Size:	248.9 KB 
ID:	11627
    Last edited by tonton81; 09-26-2017 at 11:15 PM.

  14. #14
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    working on the SPI slave sketch today, functional commands using 16bit SPI transfers with CRC checking:


    Implemented currently:
    digitalReadFast
    digitalWriteFast
    analogReadResolution
    analogWriteResolution
    analogRead
    analogWrite
    Serialx.begin
    serialx.write
    serialx.read
    serialx.peek
    serialx.available
    wirex.read
    wirex.write
    wirex.begin
    wirex.begintransmission
    wirex.endtransmission
    wirex.requestfrom
    supports all remote wire interfaces... 2 T3.5's == 6 I2C busses

    the PUSHR register is flushed (set to 0x0000) once the ISR is exited as well, since the stale value exists indefinately, until changed by the code/user
    Slave mode function is 11 lines of commands to get up and running, using pin 14 instead of 13 so the led can be used.

    Still working on it, but its pretty stable !
    Last edited by tonton81; 10-01-2017 at 09:54 PM.

  15. #15
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    well guys heres a sample video of mega master, controlling a teensy spi slave. master running the spi controller library im building, and the lcd library.
    the slave teensy is controlling a display on the uart line at 625,000 baud

    the stream of the spi bus is setup so you can continuously control objects without cycling the cs line, you can still toggle it if you wish, doing so would allow you to run several devices on the same CS line, or, reasserting the signal will also reset the buffer of the slave. all switches verify for CRC errors as well

    https://youtu.be/FOLaDUJmNdw


    Code:
    spi_controller teensySerial1 = spi_controller("Serial1", 53, &SPI);
    spi_controller gpio = spi_controller("GPIO", 53, &SPI);
    
    teensySerial1.begin(625000);
    while ( teensySerial1.available() > 0 ) Serial.print((char)teensySerial1.read());
    Serial.println((char)teensySerial1.peek());
    
      gpio.pinMode(13, OUTPUT);
     for ( int i = 0; i < 10; i++) {
        gpio.digitalWrite(13, !gpio.digitalRead(13));
        delay(150);
      }
      gpio.digitalWrite(13, gpio.analogRead(16));
      gpio.analogReadResolution(4);
    below is testing 2x uarts read/writing constantly at 625,000 baud rate, (read+write) toggling pin 13 at 10ms, and polling Analog2 (pin16), all over a single SPI connection.
    I do, however, get intermittant lockups on the teensy, lowering the SPI speed on mega seems to help, but I will keep monitoring it this week to see whats related
    Example of 1 issue, at 2MHz, polling the A2 pin pulled HIGH seems to get mostly 1023, but occassional 0x0001 or 0x0002 returns, lowering to 1MHz fixed this.
    Example #2, between 800khz-1mhz, i still see very itntermittant lockups. I dropped it to 500Khz and its been running for awhile now. Whether this is slave related, or code related, not sure, but consistant processing without toggling the CS line seems to be working pretty well with these settings.

    this is all also with 16bit SPI transfers, with a buffer max of 6 bytes (uint16_t)

    Click image for larger version. 

Name:	11ln8zb.jpg 
Views:	50 
Size:	321.8 KB 
ID:	11671

    assert signaling vs continous test:
    https://www.youtube.com/watch?v=cclnPb7Ci1U

    its hard to see in a video due to the nature of the led i guess, but visually you can see it toggling faster IRL without reasserting the CS line and running constant packets
    Last edited by tonton81; 10-01-2017 at 06:30 PM.

  16. #16
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,186
    Should we show this on the blog?

    Robin writes the blog posts for projects. She'll need a brief "stand alone" description that explains the value of using this peripheral expansion, ideally readable for beginners who may not necessarily know the background behind the idea.

  17. #17
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    great news paul! i got the timing going! running 8mhz!!!!

    i need a little class help i will ask later because im not home, but its getting there! ill try to finish it up hopefully before next week, with some info i guess, the issues with the spi bus speed are done and running at 8mhz with 120mhz cpu clock t3.5 slave

    i still have to sprinkle in a couple of ifdefs for different teensy boards, but as it stands, its setup for teensy 3.x only (if they all follow the same SPI0 register codes, avr support will be last (if any), although the library for master will work for both, slave was designed around the 3.5, and there is nothing in setup() other than the SPI slave function setup call. ps, i also added reset detection support, so the host knows if the slave was reprogrammed/reset, so the end user could reinitialize their hardware automatically

    i just drove home to check the analogread/write polling, 10ms led toggling, and uart rx/tx flooding over the SPI bus and its still kicking strong after 24hrs at 8mhz/120cpu, this week i want to add SPI bus ports support as well to finalize things, the rest is testing stages with spi hardware and i2c hardware, like my i2c controller was designed to do (and it controlled the mcp23s17 spi chip over the i2c bus pretty well indeed), but wasnt conplete because SPI would have potential for fastest speeds, so I started from thereon

    also note: the 8mhz is because its the mega2560 limit, i have not yet attempted to try a teensy 3.5 to 3.5 performance test of centralized hardware in a single sketch, my plan of design is drop in support for libraries , like the lcd, where passing the object to an unmodified library worked as is

    another benefit is, perhaps you want a T3.6 or Teensy 4.0 and want 5V tolerancy with extra peripheral access, your thumb sized dual teensy setup could have not only the power of the newer teensies, and peripheral access of the slave, but 5v tolerancy as well without dealing with level shifters since T3.6+ will be 3.3V only

    should i even add support for AVR? or just keep it teensy only, we might get more users
    someone saw this project here and told me theyre ordering teensy just because of it, i _could_ make the slave codes and defines switchable automatically based on the slave device, but let the voters decide, i just might release it as teensy only (the slave) and not waste time with other micros, and just work on adding functionality and features., master library can run on any avr/teensy (that support SPI.transfer16() of course).

    I might even put the i2c version on hold as well indefinately, and stick to SPI protocol, its more stable than i2c in general, and faster, that and keeping up support for both takes time to write up the protocols. Its better to stick to one hardware interface and work up from there, and I don't believe relying on i2c will make anyone happy either, especially with the amount of data pushing through to control the hardware at fastest possible speeds. when ill be done i will also setup, test out, and post a dual 3.6+3.5 setup, to demonstrate all the features of this
    Last edited by tonton81; 10-03-2017 at 04:04 PM.

  18. #18
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Just an update:

    This is with TWire1.beginTransmission, TWire1.write, and TWire1.endTransmission calls controlling an MCP23017 (I2C)

    Click image for larger version. 

Name:	2mm5oc5.jpg 
Views:	54 
Size:	287.2 KB 
ID:	11699

    video of 3.3v pullups, and mcp23017 powered by 3.3V, running on teensy slave
    https://www.youtube.com/watch?v=rsOufGfbJ3M

    same as above, but with 5v applied to port expander chip and pullups to 5V as well on T3.5
    https://www.youtube.com/watch?v=NO-32Cwx6Ls

  19. #19
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Today's update:

    It seems the slave doesnt like to initialize the SPI1/2 ports remotely, so, we will leave those pre-set and initialized, SPI1 using alternate pins to free up Serial1, and SPI2 using default pins, you may choose your Transaction speeds for them before writing to slave. Irregardless, there is progress. In my test I assigned pin2 as the CS using SPI1 alternate pins. So we will be using only 2/3 commands in the master. digitalWrite, transfer, or transfer16 is supported
    I am attaching a screenshot of the loop function together with the spi and i2c mcp23x17 expanders on the teensy slave
    Code:
      if ( millis() - tyme > 2000 ) {
        tyme = millis();
        ( !flipper ) ? flipper = 0b10000000 : flipper = 0;
        TWire1.beginTransmission(0x20);
        TWire1.write(0x00);
        TWire1.write(0x00);
        TWire1.endTransmission();
    
        TWire1.beginTransmission(0x20);
        TWire1.write(0x12);
        TWire1.write(flipper);
        TWire1.endTransmission();
        // initialize chip
        TSPI1.digitalWrite(2, LOW); TSPI1.transfer(0x40); TSPI1.transfer(0x0A); TSPI1.transfer(0x18); TSPI1.digitalWrite(2, HIGH);
        // set registers
        TSPI1.digitalWrite(2, LOW);
        TSPI1.transfer(0x40 | ((0x20 & 0b111) << 1));
        TSPI1.transfer(0x00);
        TSPI1.transfer(0x00);
        TSPI1.digitalWrite(2, HIGH);
    
        TSPI1.digitalWrite(2, LOW);
        TSPI1.transfer(0x40 | ((0x20 & 0b111) << 1));
        TSPI1.transfer(0x12);
        TSPI1.transfer(flipper);
        TSPI1.digitalWrite(2, HIGH);
    
    
      }
    At 8 MHz, the loop is cycling these devices ( and pretty soon i wont have a workbench at this rate of massive combined wiring & device tests LOL ) :

    Serial1 @ 650,000 baud constant read/writes to a display
    Serial2 @ 650,000 baud constant read/writes to a display
    gpio.digitalWrite(13, !gpio.digitalRead(13)); led toggle with a 10ms delay (so the flicker will be seen);
    analogWriting
    gpio.analogWrite(15, random(1,200));
    Serial.println(gpio.analogRead(16));
    toggling of mcp23017 bank A pin 7 every 2 seconds over Wire1 bus
    toggling of mcp23S17 bank A pin 7 every 2 seconds over SPI bus

    everything combined over a single SPI interface!

    https://www.youtube.com/watch?v=eN6KXOUNS90&t=7s

    Mega2560 == host
    Teensy 3.5 == slave
    BLACK breadboard sports a MCP23S17 SPI expander
    RED breadboard sports a MCP23017 I2C expander
    Last edited by tonton81; 10-04-2017 at 08:01 PM.

  20. #20
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    ooooooooooooooh, something to play with this week(end)!
    Click image for larger version. 

Name:	207x99s.jpg 
Views:	60 
Size:	158.1 KB 
ID:	11702

  21. #21
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Update: Progress!

    Soldered headers onto the 3.6 and controlling slave via SPI2 bus

    Click image for larger version. 

Name:	2581q3t.jpg 
Views:	50 
Size:	101.9 KB 
ID:	11705
    Click image for larger version. 

Name:	faveps.jpg 
Views:	56 
Size:	246.2 KB 
ID:	11706

    here is the video of both displays running 625,000baud rate on slave teensy 3.5, controlled by teensy 3.6 master. the master is also toggling the led as well on the slave
    https://www.youtube.com/watch?v=nQWWDY5XhXg

  22. #22
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Update:
    Seeing as the T3.5 has no alternate pins for TX2 situated on pin10, I had to relocate the slave to use pin2 instead. This allows (tested) slave to continue operation while Serial2 remains functional.
    After careful debugging, there is definately uart corruption going above 4Mhz, which can be seen in this video, where those digits were written to the display and read back to display in debug monitor, 4Mhz seemed to be stable when comparing against 6Mhz

    https://www.youtube.com/watch?v=7Y3k9itEhiQ
    Last edited by tonton81; 10-07-2017 at 11:45 PM.

  23. #23
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Update! 24 MHz SLAVE SUPPORT! it seems every 16bit transfer needed a few microsec delays to be "stable" between each other (aparently not just for reading, but writing as well!), there are no more serial issues at 24MHz using stock cpu speeds of T3.5(120MHz) slave and T3.6 master(180Mhz).

    I am, however having problems (not with sending SPI traffic to a remote SPI port, but receiving it through the same ISR. This I havn't done before but it is indeed tricky and I'll keep at it!

    ^--- edit, I was using the wrong command to read the chip. fixed on the next post
    Last edited by tonton81; 10-09-2017 at 08:30 PM.

  24. #24
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    Update:
    1) SPI begin and end transactions finally working remotely via uint32_t speed specified at the host
    2) x.transfer and x.transfer16 are both working and both return their values if requested as well to the host, as shown in the video
    3) same goes for wire read, it reads the value from the remote expander and flips it to toggle the chip's gpios as demonstrated on the DMM
    4) both lcds still run non stop at 625000 baud rate
    5) the led is blinking and analogread is running as well. Analogwrite was tested on both DAC0 and DAC1
    6) I defined most of the peripheral ports used in this code (with adjusted pin change) of the slave to be active, so initializing them in the host is not necessary, but still possible
    Serial2 is usable as the slave's CS line is moved to pin2 of the slave. The 3.6 is connected from it's SPI2 to the SPI0 of the T3.5slave
    7) I decided to keep the code teensy specific
    8) I have no knowledge of DMA, however, you'll be glad to know this doesnt use it at these speeds!
    9) This code uses the built in FIFO buffer of the slave, the slave goes through the buffer for the correct data, so yeah, it prolly wont work on a 1 FIFO slave

    I have attached a video of the teensy 3.5+3.6 running at 24mhz spi bus speed, with the 3.5 controlling 5v i2c+spi port expanders, while the T3.6 host treated it as it's own with no level shifters involved!
    https://www.youtube.com/watch?v=mhdjaL9d31U&t=5s

    Code:
    spi_controller teensyUART = spi_controller("Serial", 43, &SPI2);
    spi_controller teensyUART1 = spi_controller("Serial1", 43, &SPI2);
    spi_controller teensyUART2 = spi_controller("Serial2", 43, &SPI2);
    spi_controller teensyUART3 = spi_controller("Serial3", 43, &SPI2);
    spi_controller teensyGPIO = spi_controller("GPIO", 43, &SPI2);
    spi_controller TWire = spi_controller("Wire", 43, &SPI2);
    spi_controller TWire1 = spi_controller("Wire1", 43, &SPI2);
    spi_controller TSPI1 = spi_controller("SPI1", 43, &SPI2);
    spi_controller TSPI2 = spi_controller("SPI2", 43, &SPI2);
    also, the initializer above also puts the specified CS line (43 in this case) in a HIGH output state (pinmode+digitalwrite) after assigning the object to it.
    SPI0 code is blocked and not used, since it belongs to the slave, it musn't be touched.
    Last edited by tonton81; 10-09-2017 at 08:34 PM.

  25. #25
    Senior Member
    Join Date
    Dec 2016
    Location
    Montreal, Canada
    Posts
    2,714
    i think i remember SPISettings clocks SPI down to max speed, master FBUS/2, slave FBUS/4 is this correct?
    if so, at stock 120mhz slave teensy 3.5 and 180mhz master T3.6, with spisettings of 24,000,000, spisettings doesnt actually set that speed correct?
    does anyone know what kind of SPI bus speed i should be achieving at stock speed vs what i could achieve during the tests of the master/slave code if i were to overclock the cpu of the slave (168mhz?) and master(240mhz), i mightes well test it since the devices are connected and pumping traffic every day now non stop to test the stability

    i also really wanna test a dual slave setup, perhaps 1 master teensy3.6 and 1x t3.5 slave and 1x teensy 3.6 slave, but i need to go get more headers first

    edit:
    new observation, 3MHz is minimum it seems for teensy to be slave, i tried lower than 3mhz for spisettings and it's actually causing loss of data between master slave, from all peripheral ports
    anywhere between 3000000 -> 24000000 is retaining values

    observation 2, master OC @ 240mhz, slave OC @ 168mhz, SPISettings set to 75,000,000, both set to Faster
    again, im pretty sure spisettings lowers the rate to the max supported, and the code is still working with the overlocked master/slave setup and exagerated spisettings

    observation #3, the 3.6 @ 240 seems a bit warmer than the 3.5 @ 168, but we're talking luke warm by touch test, i dont have a thermometer here :P slave is the one doing most of the work tho hehe

    observation #4, keeping SPISettings at 75,000,000 was running stable (while debugging monitor values) with 240mhz t3.6 master and 186mhz t3.5 slave.
    keeping it at 75,000,000, i put the stock cpu speeds back, 180 for 3.6, 120 for 3.5
    Result: data corruptions, albeit intermittant
    Changed to 30,000,000, intermittant, less errors.
    Changed to 24,000,000, stable at stock speeds
    I would guess SPISettings doesnt go towards the less highest if it got errors at stock, but what the heck, did I actually run a 75mhz spi setup? and if so, how far could i push it? lol./..
    Last edited by tonton81; 10-11-2017 at 04:01 PM.

Posting Permissions

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