Finally got 2 slaves working.
Just in case someone else lands up here:
- make sure slave select is turned HIGH for all slaves in setup() of master. Even if you miss one, it will cause a mess as bus contention on MISO might drive slaves into unknown states. Point being, slave select LOW of one slave must never overlap with that slave select LOW of another.
- in the pin change ISR, its recommended to do this in this order to tristate MISO:
// Tristate the MISO pin. Turn DO as Input to tristate.
DDRB &= ~(1 << DO);
PORTB &= ~(1 << DO);
First make the the respective DDRB port bit as input AND THEN clear the corresponding PORTB port bit.
Then it should work fine.
Here is the slave code that worked for me for 2 slaves. Its a toy example. ISR can be made cleaner.
For slave 1, use SLAVE1_* inside pin change ISR. For slave 2, use SLAVE2_* inside pin change ISR.
#define CS PB3 // Chip Select or Slave Select
#define DO PB1 // MISO or Data Out
#define USCK PB2 // Clock
#define LED PB4 // Test LED
#define SLAVE2_DATA 0x08
#define SLAVE2_ACK 0x0A
#define SLAVE2_NACK 0x0B
#define SLAVE1_DATA 0x02
#define SLAVE1_ACK 0x05
#define SLAVE1_NACK 0x09
void setup() {
cli(); // Deactivate interrupts
SPI_USI_init();
sei(); // Activate interrupts
}
void loop() {
// put your main code here, to run repeatedly:
}
void SPI_USI_init() {
DDRB |= 1 << LED; // Set LED pin as output
DDRB |= 1 << DO; // MISO Pin has to be an output
USICR = ((1 << USIWM0) | (1 << USICS1)); // Activate 3- Wire Mode and use of external clock but NOT the interrupt at the Counter overflow (USIOIE)
PORTB &= ~(1 << LED); // Turn LED off
PORTB |= 1 << CS; // Activate Pull-Up resistor on CS
PCMSK |= 1 << CS; // Activate Interrupt on CS
GIMSK |= 1 << PCIE; // General Interrupt Mask Register / PCIE bit activates external interrupts
delay(500); // Let things settle
}
// Interrupt routine at the CS pin. Always executed when value on CS pin changes:
ISR (PCINT0_vect)
{
if ((PINB & (1 << CS)) == 0) {
// If edge is falling, the command and index variables shall be initialized
// and the 4-bit overflow counter of the USI communication shall be activated:
USICR |= (1 << USIOIE);
USISR = 1 << USIOIF; // Clear Overflow bit
DDRB |= 1 << DO; // ensure that MISO is set to OUTPUT
}
else {
// If edge is rising, turn the 4-bit overflow interrupt off:
USICR &= ~(1 << USIOIE);
// Tristate the MISO pin. Turn DO as Input to tristate.
DDRB &= ~(1 << DO);
PORTB &= ~(1 << DO);
}
}
// USI interrupt routine. Always executed when 4-bit overflows (after 16 clock edges = 8 clock cycles):
// for test purposes: the master will send 0x01 as data and should receive from slave 0x05 as response
// for test purposes: if the master sends anything other than 0x01, it will receive from slave 0x09 as response
ISR (USI_OVF_vect) {
byte received = USIDR;
if (received == SLAVE2_DATA) {
USIDR = SLAVE2_ACK;//ACK for successfully receiving 0x01
//Testing: XOR operation to turn PB3 (LED) on. (Not needed for USI)
PORTB |= 1 << LED;
}
else {
USIDR = SLAVE2_NACK;//ACK indicating non 0x01 value received
//Testing: XOR operation to turn PB3 (LED) off. (Not needed for USI)
PORTB &= ~(1 << LED);
}
USISR = 1 << USIOIF;
}
corresponding master code is:
#include <SPI.h>
#include "pins_arduino.h"
int dummyData = 7;
int slaveSelect = SS; // SS is pin 10
int slaveSelect1 = 9;
int delayTime = 500;
byte masterReceive;
//bool toggle = LOW;
// the setup function runs once when you press reset or power the board
void setup() {
pinMode(slaveSelect, OUTPUT);
pinMode(slaveSelect1, OUTPUT);
digitalWrite(slaveSelect, HIGH);
digitalWrite(slaveSelect1, HIGH);
Serial.begin(9600); // set default baud rate for uart
SPI.begin();
// SPI.setBitOrder(MSBFIRST); // deprecated. use SPI.beginTranscation() with SPISettings
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(slaveSelect, LOW);
delay(500);
{
SPI.beginTransaction(SPISettings(9600, MSBFIRST, SPI_MODE0));
delayMicroseconds(10);
// send test string
int sentData = 2;
SPI.transfer(sentData);
delay(500);
byte recvData = SPI.transfer(dummyData);
//digitalWrite(slaveSelect, HIGH);
SPI.endTransaction();
Serial.print("Sent data:");
Serial.print(sentData);
Serial.print(":::Received data from slave:");
Serial.println(recvData);
}
{
SPI.beginTransaction(SPISettings(9600, MSBFIRST, SPI_MODE0));
//digitalWrite(slaveSelect, LOW);
delayMicroseconds(10);
// send test string
int sentData = 3;
SPI.transfer(sentData);
delay(500);
byte recvData = SPI.transfer(dummyData);
digitalWrite(slaveSelect, HIGH);
SPI.endTransaction();
Serial.print("Sent data:");
Serial.print(sentData);
Serial.print(":::Received data from slave:");
Serial.println(recvData);
}
delay(1000);
digitalWrite(slaveSelect1, LOW);
delay(500);
{
SPI.beginTransaction(SPISettings(9600, MSBFIRST, SPI_MODE0));
delayMicroseconds(10);
// send test string
int sentData = 8;
SPI.transfer(sentData);
delay(500);
byte recvData = SPI.transfer(dummyData);
//digitalWrite(slaveSelect1, HIGH);
SPI.endTransaction();
Serial.print("Sent data:");
Serial.print(sentData);
Serial.print(":::Received data from slave2:");
Serial.println(recvData);
}
{
SPI.beginTransaction(SPISettings(9600, MSBFIRST, SPI_MODE0));
//digitalWrite(slaveSelect1, LOW);
delayMicroseconds(10);
// send test string
int sentData = 2;
SPI.transfer(sentData);
delay(500);
byte recvData = SPI.transfer(dummyData);
digitalWrite(slaveSelect1, HIGH);
SPI.endTransaction();
Serial.print("Sent data:");
Serial.print(sentData);
Serial.print(":::Received data from slave2:");
Serial.println(recvData);
}
delay(1000);
}
best regards,
aquaman