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

Thread: Multi-master I2C using Teensy3.2 + ATtiny85

  1. #1
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22

    Multi-master I2C using Teensy3.2 + ATtiny85

    Hey there!

    I'm currently working on the following project:
    Teensy 3.2 and ATtiny85 (programmed via Arduino ISP) are connected to an FRAM chip (http://www.fujitsu.com/us/Images/MB8...0017-3v0-E.pdf) via I2C.
    ATtiny is reading an analog sensor and writes the value to the FRAM. Then the Teensy (which is supposed to work as the "main microcontroller" when everything works) is told that it's his turn and reads out the just written sensor value from the FRAM. The ATtiny always saves the current FRAM memory address in the first memory address. So when Teensy is told to read, it reads out the first address to know where the newest sensor value is saved in the FRAM and then reads out that value at the current address.
    For testing, I use the Serial Monitor with the Teensy to print the values (sampled with with 1 Hz) it read from the FRAM.

    I began with 2 Teensies which worked perfectly. When I switched to the ATtiny (8 MHz) using the TinyWireS library it became buggy:
    The Teensy is still reading a value every second, but the it's always the same value. Even when I connect the analog input of the ATtiny to GND or any other potential the Teensy is always reading the same value.

    I noticed that when I switch off the ATtiny just for a second, the correct value is printed on the monitor. But again, when I change the analog input the read-out value won't change!
    That's why I think the ATtiny is causing the trouble. It either has trouble with:
    -analog reading of the value
    -saving the current address at the first address of the FRAM
    -saving the current analog reading

    Here's the code of ATtiny:
    Code:
    /*To-Do:
    
    
    
    
    */
    #include <TinyWireM.h>
    
    
    #define EEPROM_ADR 0x50
    #define EEPROM_SIZE 32000
    #define a_count 1                   //Number of sensors, maximum is 3
    
    #if (a_count == 1)
    #define MAX_ADDR 32000
    #elif (a_count == 2)
    #define MAX_ADDR 32000
    #elif (a_count == 3)
    #define MAX_ADDR 31998
    #else
    #error "a_count maximum is 3"
    #endif
    
    /////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////  Constants & Variables  //////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    uint32_t current_addr = 1;
    uint8_t counter = 0;
    const uint32_t sample_int = 1000000;             //microseconds
    uint32_t previous_us = 0;
    uint16_t a_data[a_count];                     //Buffer to save analog read
    uint8_t WakePin = 1;
    uint8_t sample_pin = A3;
    /*
      struct analog_data {
      uint16_t data[a_count];
      //  byte high_b[a_count];
      //  byte low_b[a_count];
      } a_data;
    */
    const uint8_t addr_step = sizeof(a_data);
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////    Setup   ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void setup() {
      delay(100);
      TinyWireM.begin();
      pinMode(WakePin, OUTPUT);
      digitalWrite(WakePin, LOW);
      delay(100);                             // to make sure that T3.2 is in loop() and waiting
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////    Loop    ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void loop() {
      if (digitalRead(WakePin) == HIGH) {                    //WakePin set LOW, just if it is HIGH
        digitalWrite(WakePin, LOW);
      }
      uint32_t current_us = micros();
      if (current_us - previous_us >= sample_int ) {    //sample-routine
        previous_us = current_us;
        sample();
    
        writeEEPROM_ADDR(current_addr);                //write the current address to the first memory address
        writeEEPROMPage(current_addr, a_data);        //write the data at the current address
    
        current_addr += addr_step;
        if (current_addr >= MAX_ADDR) {                //prevent overflow of address
          current_addr = 1;
        }
        digitalWrite(WakePin, HIGH);                  // activate Teensy
        delay(1);
      }
    
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////      Functions      ////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void writeEEPROMPage(uint32_t eeAddress, uint16_t a_data[])
    {
    
      TinyWireM.beginTransmission(EEPROM_ADR);
    
      TinyWireM.write((int)(eeAddress >> 8)); // MSB
      TinyWireM.write((int)(eeAddress & 0xFF)); // LSB
      for (uint8_t i = 0; i < sizeof(a_data); i++) {
        byte high = highByte(a_data[i]);
        byte low = lowByte(a_data[i]);
        TinyWireM.write(high); //Write the data
        TinyWireM.write(low);
      }
    
      TinyWireM.endTransmission(); //Send stop condition
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    void writeEEPROM_ADDR(uint32_t eeAddress)
    {
    
      TinyWireM.beginTransmission(EEPROM_ADR);
    
      TinyWireM.write((int)(0x00)); // MSB
      TinyWireM.write((int)(0x00)); // LSB
    
      TinyWireM.write((int)(eeAddress >> 8)); // MSB
      TinyWireM.write((int)(eeAddress & 0xFF)); // LSB
      TinyWireM.endTransmission(); //Send stop condition
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    void sample() {                                   //analogRead, loop just runs once for testing
      for (uint8_t i = 0; i < a_count; i++) {
        a_data[i] = analogRead(sample_pin);
      }
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    void readEEPROM(uint32_t eeaddress, uint16_t fram_array[])
    {
      TinyWireM.beginTransmission(EEPROM_ADR);
    
      TinyWireM.write((int)(eeaddress >> 8)); // MSB
      TinyWireM.write((int)(eeaddress & 0xFF)); // LSB
      TinyWireM.endTransmission();
    
      TinyWireM.requestFrom(EEPROM_ADR, (sizeof(a_data)));
      byte high = 0xFF;
      byte low = 0xFF;
      uint8_t i = 0;
      while (TinyWireM.available()) {
        high = TinyWireM.read();
        low = TinyWireM.read();
        fram_array[i] = ((unsigned int)high << 8 ) + low;
        i++;
      }
    
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    And here for the Teensy:

    Code:
    /*To-Do:
    
    
    
    
    */
    #include <Wire.h>
    
    #define EEPROM_ADR 0x50
    #define EEPROM_SIZE 32000
    #define a_count 1                   //must be the same on ATtiny
    
    #if (a_count == 1)
    #define MAX_ADDR 32000
    #elif (a_count == 2)
    #define MAX_ADDR 32000
    #elif (a_count == 3)
    #define MAX_ADDR 31998
    #else
    #error "a_count maximum is 3"
    #endif
    
    
    /////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////  Constants & Variables  //////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    uint32_t current_addr = 0;
    uint8_t counter = 0;
    const uint32_t sample_int = 1000000;             //microseconds
    uint32_t previous_us = 0;
    uint16_t a_data[a_count];                     //dummy for buffer size, could use fram_read[] too...
    uint16_t fram_read[a_count];                  //buffer for the values out of the FRAM
    uint8_t WakePin = 10;
    volatile bool read_allowed = false;
    /*
      struct analog_data {
      uint16_t data[a_count];
      //  byte high_b[a_count];
      //  byte low_b[a_count];
      } a_data;
    */
    const uint8_t addr_step = sizeof(a_data);
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////    Setup   ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void setup() {
      Wire.begin();
    //  Wire.setClock(100000);
      attachInterrupt(digitalPinToInterrupt(WakePin), wake_up, RISING);
      read_allowed = false;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////    Loop    ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void loop() {
      while (!read_allowed) {
      }                                            // wait for Attiny
    
      current_addr = readEEPROM_ADDR();      //reads the current address at the first memory address of the FRAM
      readEEPROM(current_addr, fram_read);    //reads the values at the current address
    
      for (uint8_t i = 0; i < a_count; i++) {
        Serial.print(fram_read[i]);
        Serial.print(" ");
      }
      Serial.println();
    
      read_allowed = false;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////      Functions      ////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void writeEEPROMPage(uint32_t eeAddress, uint16_t a_data[])
    {
    
      Wire.beginTransmission(EEPROM_ADR);
    
      Wire.write((int)(eeAddress >> 8)); // MSB
      Wire.write((int)(eeAddress & 0xFF)); // LSB
      for (uint8_t i = 0; i < sizeof(a_data); i++) {
        byte high = highByte(a_data[i]);
        byte low = lowByte(a_data[i]);
        Wire.write(high); //Write the data
        Wire.write(low);
      }
    
      Wire.endTransmission(); //Send stop condition
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    void sample() {                                   //analogRead
      for (uint8_t i = 0; i < a_count; i++) {
        a_data[i] = analogRead(A0);
      }
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    void readEEPROM(uint32_t eeaddress, uint16_t fram_array[])
    {
      Wire.beginTransmission(EEPROM_ADR);
    
      Wire.write((int)(eeaddress >> 8)); // MSB
      Wire.write((int)(eeaddress & 0xFF)); // LSB
      Wire.endTransmission();
    
      Wire.requestFrom(EEPROM_ADR, (sizeof(a_data)));
      byte high = 0xFF;
      byte low = 0xFF;
      uint8_t i = 0;
      while (Wire.available()) {
        high = Wire.read();
        low = Wire.read();
        fram_array[i] = ((unsigned int)high << 8 ) + low;
        i++;
      }
    
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    uint32_t readEEPROM_ADDR()
    {
      Wire.beginTransmission(EEPROM_ADR);
    
      Wire.write((int)(0x00)); // MSB
      Wire.write((int)(0x00)); // LSB
      Wire.endTransmission();
    
      Wire.requestFrom(EEPROM_ADR, (sizeof(a_data))); 
      byte high = 0xFF;
      byte low = 0xFF;
      uint32_t addr = 0;
      while (Wire.available()) {
        high = Wire.read();
        low = Wire.read();
        addr = ((unsigned int)high << 8 ) + low;
      }
      return addr;
    }
    
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    void wake_up() {               //ISR
      read_allowed = true;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    Any ideas on how to solve that?

    Best regards,
    Julian

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,367
    Have you tried asking on Adafruit's forum? TinyWireM is their library.

    I looked briefly at the ATtiny85 datasheet. I don't see any mention of multi-master arbitration in the USI chapter. But it does at least mention checking the data signal to allow clock stretching. Whether the hardware even has the capability to check both signals for another master, and whether the TinyWireM would try to do that if the hardware even makes it possible is unknown to me.

    Probably best to ask Adafruit. But I believe it's very likely their answer will be no support for multi-master arbitration.

  3. #3
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Hey Paul,

    No, I haven't asked yet, but I will...
    I thought that at least for now, when I sample so slowly, I don't really have to care about multi-master arbitration, because I can be quite sure that they don't try to claim the bus at the same time, can't I?
    Master 1 reads, saves to FRAM, wakes Master 2 which reads and then waits to be woken up again...
    I also planned to try this: https://www.hackster.io/chipmc/ardui...and-how-93f638 but I'm still waiting for that RTC to arrive
    Could the missing multi-master arbitration be the problem?

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,367
    Quote Originally Posted by j_neu View Post
    Could the missing multi-master arbitration be the problem?
    Could be. Or it could be something else not "playing nice with others" on either side, maybe something as simple as the clock signal being driven as output instead of open collector? I agree the fact that the I2C bus is idle nearly all the time makes collisions requiring mulit-master arbitration seem like an unlikely cause. Seems likely the problem is in that "something else" about not fully implementing I2C.

    Teensy 3.2 definitely does support sharing I2C devices with other I2C masters, so I'm pretty sure the problem lies with the TinyWireM side.

    If there is a reason to believe Teensy isn't behaving properly, I definitely will look into the Teensy side. But I can't pour that engineering time into troubleshooting TinyWireM or ATtiny85-based products.
    Last edited by PaulStoffregen; 09-17-2018 at 05:58 PM. Reason: typo

  5. #5
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Quote Originally Posted by PaulStoffregen View Post
    maybe something as simple as the clock signal being driven as output instead of open collector?
    Indeed it seems that in TinyWire library SCL and SDA are set as outputs:
    Code:
    static void SET_USI_TO_SEND_DATA( )
    {
      /* set SDA as output */
      DDR_USI |=  ( 1 << PORT_USI_SDA );
      /* clear all interrupt flags, except Start Cond */
      USISR    = 
           ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) |
           ( 1 << USIDC) |
           /* set USI to shift out 8 bits */
           ( 0x0 << USICNT0 );
    }
    But I guess it's not done with just changing it at this point in the library

    Quote Originally Posted by PaulStoffregen View Post
    But I can't pour that engineering time into troubleshooting TinyWireM or ATtiny85-based products.
    Sure! I don't expect you to :P Just hoped to get support from the powerful Teensy-community

  6. #6
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,908
    The USI engine in the ATTiny MCUs can (among others) emulate a kind of I2C or SPI, depending on the configuration. But that emulation remains relatively restricted to very basic/elementary functionality. It's far from being a true I2C module as you can find it in the Teensy processors.

    I understand that the components' price might have a certain weight, but what about spending a little more and using a Teensy LC as a sub-processor, since it has a full I2C engine?

  7. #7
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Quote Originally Posted by Theremingenieur View Post
    The USI engine in the ATTiny MCUs can (among others) emulate a kind of I2C or SPI, depending on the configuration. But that emulation remains relatively restricted to very basic/elementary functionality. It's far from being a true I2C module as you can find it in the Teensy processors.

    I understand that the components' price might have a certain weight, but what about spending a little more and using a Teensy LC as a sub-processor, since it has a full I2C engine?
    It's not only about the price but mostly energy consumption...
    I contacted the author of the TinyWire library. He came across a similar problem when using multiple masters, probably because of the lines being declared as output.

    I will also try to just use the ATtiny as single-master, where it writes/reads to/from FRAM and sends the readings to the Teensy-slave. This will be slower I assume, which is why I tried the multi-master solution first.

  8. #8
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    I just had another idea for the multi-master approach:
    Would it be possible to use some kind of multiplexing to only pass the "desired" SDA/SCL signals to the FRAM?

    The system could work like this:
    ATtiny samples -> writes to FRAM -> signals that it's ready, which changes the multiplexer outputs and starts the I2C communication between Teensy and FRAM -> when it's time to sample, the signal is changed again, which changes the multiplexer outputs again and so on...
    Does that sound feasible? It does to me at least... :-P

  9. #9
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,908
    Try it out. As Germans say: "Versuch macht kluch"

  10. #10
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    I will
    Just looking for the right multiplexer at the moment..

    I'll give an update as soon as possible.

    If anyone else has an idea what's the problem with the TinyWireM library feel free to post :P

  11. #11
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,908
    To me, it's not a library problem, but the result of limited hardware capabilities. The ATTiny85 does not have a true I2C building block, it only has the so called USI which can emulate elementary functionality of either I2C, SPI, or UART without ever coming close to the power of dedicated engines. This comparison chart could perhaps allow you a different ATTiny MCU which has full I2C support: https://en.wikipedia.org/wiki/ATtiny...mparison_chart

  12. #12
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Hehe, I just found that chart a few hours ago

    Seems like the ATtiny48/88 could be another solution. But you're right, could be an easier approach. If it fails, I still have the multiplexer option.
    I guess the ATtiny48/88 can be used with the standard Wire.h library?

  13. #13
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,367
    Quote Originally Posted by j_neu View Post
    It's not only about the price but mostly energy consumption...
    Maybe look at Duff's Snooze library. Teensy LC can achieve very low power if you use its special features.

  14. #14
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Just wanted to give an update:

    I ordered an ATtiny88 and tried the code from the first post and it worked fine! Even for 1 kHz sampling rate.
    So the problem really was the TinyWire library and/or the emulated I2C in the USI.

    Now I tried to send the current memory address directly to the Teensy instead of saving it always in the first memory address, which does not work for some reason (no value is read from the fram by the Teensy) . I guess there could be a problem with the Teensy who is a slave first to receive the current memory address and then tries to be the master and read the value that is saved at the current address.

    In the example from the first post both controllers are masters-only and just read values from the slave device...

    I looked at the signals with the scope and this is what happens when ATtiny is sending the memory address:
    Click image for larger version. 

Name:	IMG_9135.jpg 
Views:	2 
Size:	129.5 KB 
ID:	14841

    So after transmitting the correct device address of the Teensy which is 255 Teensy does not send an acknowledge-bit.

    For any reason the 3rd transmission works:
    Click image for larger version. 

Name:	IMG_9136.jpg 
Views:	2 
Size:	134.9 KB 
ID:	14842
    (Teensy uses 1MHz SCL speed, but I also tried 400 kHz without success)

    After the 4th try SDA and SCL are pulled low permanently and nothing happens.

    Here is the code of the Teensy:
    Code:
    #include <Wire.h>
    
    #define EEPROM_ADR 0x50
    #define EEPROM_SIZE 32000
    #define TEENSY_ADDR 0xFF
    #define a_count 1                   //must be the same on ATtiny
    
    #if (a_count == 1)
    #define MAX_ADDR 32000
    #elif (a_count == 2)
    #define MAX_ADDR 32000
    #elif (a_count == 3)
    #define MAX_ADDR 31998
    #else
    #error "a_count maximum is 3"
    #endif
    
    
    /////////////////////////////////////////////////////////////////////////////////////
    //////////////////////////////  Constants & Variables  //////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    uint32_t current_addr = 0;
    uint16_t counter = 0;
    const uint32_t sample_int = 1000000;             //microseconds
    uint32_t previous_us = 0;
    uint16_t a_data[a_count];                     //dummy for buffer size, could use fram_read[] too...
    uint16_t fram_read[a_count];                  //buffer for the values out of the FRAM
    uint8_t WakePin = 10;
    volatile bool read_allowed = false;
    const uint8_t addr_step = sizeof(a_data);
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////    Setup   ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void setup() {
      Wire.begin(TEENSY_ADDR);
      Wire.setClock(1000000);
      Wire.onReceive(receiveEvent);
      read_allowed = false;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////    Loop    ////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void loop() {
      while (!read_allowed) {
      }                                                            // wait for Attiny to transmit memory address
      readEEPROM(current_addr, fram_read);    // reads the values at the current address
      read_allowed = false;
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    ////////////////////////////////      Functions      ////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////
    
    void readEEPROM(uint32_t eeaddress, uint16_t fram_array[])
    {
      Wire.beginTransmission(EEPROM_ADR);
    
      Wire.write((int)(eeaddress >> 8)); // MSB
      Wire.write((int)(eeaddress & 0xFF)); // LSB
      Wire.endTransmission();
    
      Wire.requestFrom(EEPROM_ADR, (sizeof(a_data)));
      byte high = 0xFF;
      byte low = 0xFF;
      uint8_t i = 0;
      while (Wire.available()) {
        high = Wire.read();
        low = Wire.read();
        fram_array[i] = ((unsigned int)high << 8 ) + low;
        i++;
      }
    
    }
    
    /////////////////////////////////////////////////////////////////////////////////////
    
    
    void receiveEvent(int howMany) {
      current_addr = Wire.read();                    //LSB
      current_addr |= Wire.read() << 8;          //MSB
      while (Wire.available()) {
        uint8_t dummy = Wire.read();              //not needed, just to sure..
        }
      read_allowed = true;                              //when TRUE, initial while-loop is left and FRAM-reading starts
    }
    So why does the Teensy not acknowledge sometimes? And why does it once a while?
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	IMG_9135.jpg 
Views:	1 
Size:	130.3 KB 
ID:	14840   Click image for larger version. 

Name:	IMG_9134.jpg 
Views:	1 
Size:	142.9 KB 
ID:	14839  


  15. #15
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    I don't know if it was better to open a new thread or not... but I'll just post here again.

    I tried to implement the code from the first post for an SPI-FRAM device, to have some extra speed: ATtiny88 samples -> saves on FRAM -> tells Teensy to read -> Teensy reads -> repeat
    An extra pin is used to tell Teensy that it's time to read (just like in in the first post).

    To avoid problems I just enable the SPI OUTPUT (MOSI, SCK, CS) for each device when it uses the SPI bus, in between they are set to INPUT (is that enough to tri-state the pins?), which does not work correctly.

    Now I found that the ATtiny is still doing its job, as long as Teensy does not try to access the bus.
    Do you have any tips & tricks for me? I read about a voltage-level converter http://www.ti.com/lit/ds/symlink/txb0104.pdf to ensure a try-stated output of MOSI, SCK and CS. Could that do the trick?

  16. #16
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,908
    I'm not sure if it's only a voltage level problem. The ATtiny would really have to release (tri-state) completely the SPI lines. If it can't, a line driver IC like the 74HC244 which has extra pins to enable/disable a block of lines would help. Since the 74HC244 is an octal (2x quad) driver, you could use 3 channels of the first group for the ATtiny MOSI, CS, and SCK and 3 of the second group for the Teensy MOSI, CS and SCK.

  17. #17
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,367
    SPI isn't really meant to be a multi-master bus. Normally there's 1 controller which talks to several peripheral chips.

    At the very least, you're going to need to get Teensy to tri-state MOSI, CLK and CS. You'll have to write the pin mux registers to disable the pins, and to re-assign them to the SPI port.

    You'll probably need a real pullup resistor on CS, so it stays inactive when neither controller drives it.

    For testing, I'd recommend using 100K resistors on all the pins. For MOSI, MISO, SCK, use two 100K resistors, so the line "floats" at 1.6 volts. Likewise for CS, use a stronger pullup but also weak pulldown resistor, so the CS pin "floats" at a logic high but not all the way up to 3.3 volts. This will let you see on the oscilloscope when the pin is not being driven by either microcontroller.

  18. #18
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Quote Originally Posted by PaulStoffregen View Post
    SPI isn't really meant to be a multi-master bus. Normally there's 1 controller which talks to several peripheral chips.

    At the very least, you're going to need to get Teensy to tri-state MOSI, CLK and CS. You'll have to write the pin mux registers to disable the pins, and to re-assign them to the SPI port.
    I know, but I couldn't think of any fast way with only one master... I am thinking about an arbitration with 2 extra pins between ATtiny and Teensy. One for ATtiny to tell Teensy to read, and one for Teensy to tell ATtiny when it's done reading. But first it has to run at all..

    Quote Originally Posted by PaulStoffregen View Post
    At the very least, you're going to need to get Teensy to tri-state MOSI, CLK and CS. You'll have to write the pin mux registers to disable the pins, and to re-assign them to the SPI port.
    Is it just unnecessary to use some kind of tri-state buffer or would it not work? And why..?
    So it's better to use DDRX |= ... and PORTX |= ... ? Or what exactly do you mean by "disable" and "re-assign them to the SPI port"?

    Good idea to use a voltage divider on CS and co.

    Thanks so far for all the advise

  19. #19
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,367
    Quote Originally Posted by j_neu View Post
    Is it just unnecessary to use some kind of tri-state buffer or would it not work?
    You could add dedicated buffer chips. That would work.

    Or you could disable the pins within Teensy, so no extra chips (probably) needed.


    So it's better to use DDRX |= ... and PORTX |= ... ?
    Those are AVR register names. They don't apply to Teensy 3.2, except that Teensy has some software emulation for the AVR stuff. But that's not what you want.


    Or what exactly do you mean by "disable" and "re-assign them to the SPI port"?
    Unlike AVR, where the peripherals partially or fully take over a pin when enabled, on Teensy 3.x every pin has an 8 way mux. The mux controls which peripheral has access to the pin. On Teensy 3.2, GPIO access is consider to be one of the mux choices. This is very different from AVR, where GPIO's DDRx registers still have some control over the pins, even when they're being used by some peripherals. On Teensy 3.2, the mux controls which thing gets to access the pin and GPIO is considered to be just another peripheral.

    When the mux gives SPI access to a pin, the GPIO registers have no access. When the mux gives GPIO access, this GPIO register access applies.

    If you want Teensy to tri-state its pin, you've got to write to the mux that controls which peripheral has access to the pin. Every pin goes into a low-power tri-state mode if you set the mux to 0, so that should be good enough. When in that state, the SPI has no access to the pin.

  20. #20
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Ok, so I fought my way through the reference manual. Here's what I found out:

    In chapter 11.14.1 it says that the pin control register PORTx_PCRn is used to control several options of each pin, like slew rate limiter or interrupt configuration.
    Bits 10-8 in each PCR are used for the pin mixing you were talking about.
    In chapter 10.3.1 there is a table where the possible functions of all pins are shown. These functions can be changed by using the port control module.

    The description of the PCR states that setting bits 10-8 to 0, will disable the pin and the table in 10.3.1 shows which other function can be set by the different possible configurations of the three bits.
    It shows that for Teensy 3.2 pin 13 (which the MCU knows as PTC 5) the alternative 2 is SPI0_SCK.. so that seems to be the right direction

    Now I tried to disable PTC5 by writing directly to the port control register:
    Code:
    PORTC_PCR5 &= ~(11100000000)
    But SPI is still working... am I on the wrong track?

  21. #21
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,908
    As soon as you call a SPI library function, the library will reconfigure the pins for SPI again (Arduino fool proof). An external tri-state buffer is the way to go.

  22. #22
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    Ok, at least that explains it

    The buffers just arrived
    I will update you as soon as I have new results.

  23. #23
    Member
    Join Date
    Jun 2018
    Location
    Germany
    Posts
    22
    So I just successfully tested the tri-state buffer!
    I sampled 3 analog values with 1 kHz sampling rate, save them on the FRAM via ATtiny88 and read them back with the Teensy 3.2.

    I had to solder it on a PCB because the breadboard was not reliable. I used pull-ups on the tri-state enabling input of the buffer chip and pull them down only for transmission.

    Now I will try to read out more than just the previously written value, but as much as is possible, until the ATtiny wants to sample and save new data again.

    Thanks for your help everyone!

  24. #24
    Senior Member+ Theremingenieur's Avatar
    Join Date
    Feb 2014
    Location
    Colmar, France
    Posts
    1,908
    You are welcome! That's why we like these forums!

Posting Permissions

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