Hi,
I’m trying to get an ATTiny2313 to run as SPI slave, fed from an Arduino UNO as master. The SPI interrupt on the slave is in fact being triggered whenever data arrives but somehow the data seems to be mangled. I suspect an issue with the SPI modes but I can’t figure it out so it might be something different after all.
The test setup consists of the UNO reading a button input on pin 3, toggling its own LED on pin 2 with each button press and sending ON/OFF commands (byte 0xFF / 0x00, respectively) to the SPI slave which in turn is supposed to toggle its LED in sync.
The example is rather dull but I figured I’d better start with something simple before I send more complex command structures through SPI.
I’ve spend quite a lot of time googling for examples but they mostly deal with Attinys as SPI masters, not slaves.
Code for the master:
#include <SPI.h>
#define PIN_SS_ATTINY2313 10
#define PIN_LED 2
#define PIN_BTN 3
#define CMD_LEDON 0xFF
#define CMD_LEDOFF 0x00
byte bitOrder = MSBFIRST;
int btnState; int lastBtnState; int ledState;
void setup() {
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_BTN, INPUT);
Serial.begin(9600);
SPI.begin();
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(bitOrder);
SPI.setClockDivider(SPI_CLOCK_DIV128); // 128kKz
ledOff();
}
void loop() {
lastBtnState = btnState;
btnState = digitalRead(PIN_BTN);
if (lastBtnState == 0 && btnState == 1) {
if (ledState == LOW) {
ledOn();
} else {
ledOff();
}
}
Serial.println(btnState);
}
void ledOn() {
digitalWrite(PIN_LED, HIGH);
ledState = HIGH;
Serial.println("switching LED on");
digitalWrite(PIN_SS_ATTINY2313, LOW);
SPI.transfer(CMD_LEDON);
digitalWrite(PIN_SS_ATTINY2313, HIGH);
}
void ledOff() {
digitalWrite(PIN_LED, LOW);
ledState = LOW;
Serial.println("switching LED off");
digitalWrite(PIN_SS_ATTINY2313, LOW);
SPI.transfer(CMD_LEDOFF);
digitalWrite(PIN_SS_ATTINY2313, HIGH);
}
Code for the slave:
/**
* Slave part of the SPI-LED-blink experiment
*
* To be run on an ATTiny2313.
*
* The ATtiny2313 will receive commands via SPI and is supposed to
* switch its LED on/off accordingly.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define CTRL_PORT DDRB
#define DATA_PORT PORTB
#define SS_PIN PB4
#define CLK_PIN PB7
#define DI_PIN PB5
#define DO_PIN PB6
#define PIN_LED PD5
#define CMD_LEDON 0xFF
#define CMD_LEDOFF 0x00
// Some macros that make the code more readable
#define output_low(port,pin) port &= ~(1<<pin)
#define output_high(port,pin) port |= (1<<pin)
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)
char cData;
static char res = 1;
byte ledState = 0;
int main(void) {
init();
while (1) {}
return 0;
}
void init() {
// initialize the direction of PORTD #5 to be an output
set_output(DDRD, PIN_LED);
output_high(PORTD, PIN_LED);
ledState = 1;
// DO pin is configured for output
CTRL_PORT |= _BV(DO_PIN);
// other pins as input
CTRL_PORT &= ~_BV(DI_PIN) | ~_BV(CLK_PIN) | ~_BV(SS_PIN);
// set pull ups.
DATA_PORT |= _BV(DI_PIN) | _BV(CLK_PIN);
// enable USI overflow interrupt, set three wire mode and set
// clock to External, positive edge.
USICR = _BV(USIOIE) | _BV(USIWM0) | _BV(USICS0) | _BV(USICS1);
// clear overflow flag
USISR = _BV(USIOIF);
// interrupt Enable
sei();
}
/**
* USI overflow intterupt - triggered when transfer complete
*/
ISR(USI_OVERFLOW_vect) {
res = USIDR;
USISR = _BV(USIOIF);
if (res == CMD_LEDOFF) {
output_low(PORTD, PIN_LED);
ledState = 0;
} else if (res == CMD_LEDON) {
output_high(PORTD, PIN_LED);
ledState = 1;
}
USIDR = ~res;
}
Any hints would be greatly appreciated.
Oh by the way, does anyone know a page that explains the code syntax for the 2313 properly? By now I kind of figured out things like _BV() and stuff but the lack of understanding of the registers and commands makes debugging much more tedious than it should be. I’ve read mostly through the 2313s datasheet but naturally it contains a reference of commands and registers, but not a longer explanation.
Thanks a ton!