Version 1:
I am using a Raspberry Pi Pico as SPI master and an ATtiny841 as SPI slave.
Master code:
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include <stdio.h>
#define SPI_PORT spi0
#define PIN_MISO 16
#define PIN_MOSI 19
#define PIN_SCK 18
#define PIN_CS 17
uint8_t tx_dummy = 0xAA;
uint8_t rx_buf[3];
void spi_init_master()
{
spi_init(SPI_PORT, 1000000);
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_init(PIN_CS);
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_put(PIN_CS, 1);
}
int main()
{
stdio_init_all();
spi_init_master();
while(true)
{
gpio_put(PIN_CS, 0); // Select slave
tx_dummy = 0xAA;
for(int i = 0; i < 3; i++)
{
spi_write_read_blocking(SPI_PORT, &tx_dummy, &rx_buf[i], 1);
tx_dummy++;
}
gpio_put(PIN_CS, 1); // Deselect slave
printf("Ontvangen bytes: ");
for(int i = 0; i < 3; i++)
{
printf("0x%02X ", rx_buf[i]);
}
printf("\n");
sleep_ms(1000);
}
}
Slave code:
#define SS PA7
#define MOSI PA6
#define MISO PA5
#define SCK PA4
byte dataToSend[3] = {0xDE, 0xAD, 0xBE};
volatile byte sendIndex = 0;
void setup() {
pinMode(SCK, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
pinMode(SS, INPUT);
SPCR = (1 << SPE) | (1 << SPIE);
SPDR = dataToSend[0];
}
ISR(SPI_STC_vect) {
byte received = SPDR;
sendIndex++;
if (sendIndex >= 3) {
sendIndex = 0;
}
SPDR = dataToSend[sendIndex];
}
void loop() {
}
Instead of receiving the array {0xDE, 0xAD, 0xBE}, I receive {0xDE, 0xAA, 0xAB}.
Does anyone know how I can write the code so the SPDR register of the ATtiny841 overwrite the dummy value from the master with the next value of the array before the master ask for a new value?
Version 2:
By generating an interrupt on the CS pin of the ATtiny841 and placing some small delays in the master code on the Pico I was able to transfer data from the ATiny841 slave to the Pi Pico master.
(Using the SPI interrupt always returns the dummy data that I send from the master to the slave)
Now I try to add a register structure by sending 2 bytes (byte 1 = register address, byte 2 = dummy value). By each byte the CS goes LOW and HIGH to trigger the interrupt on the slave.
This code almost work, but if the master ask for reg 0x01 it gets the value of reg 0x02. If the master ask for reg 0x02 it get the value of reg 0x01.
Does anyone sees/know how I can fix this?
Output from master:
Reg value 0x01: 0x99 - 0x01
Reg value 0x02: 0x43 - 0x02
Reg value 0x01: 0x99 - 0x01
Reg value 0x02: 0x43 - 0x02
Reg value 0x01: 0x99 - 0x01
Reg value 0x02: 0x43 - 0x02
Master code:
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include <stdio.h>
#define SPI_PORT spi0
#define PIN_MISO 16
#define PIN_MOSI 19
#define PIN_SCK 18
#define PIN_CS 17
uint8_t tx_dummy = 0xAA;
uint8_t rx_buf[2];
void spi_init_master()
{
spi_init(SPI_PORT, 1000000);
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_init(PIN_CS);
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_put(PIN_CS, 1);
}
int main()
{
stdio_init_all();
spi_init_master();
uint8_t adress = 0x01;
uint8_t test = 0x02;
while(true)
{
gpio_put(PIN_CS, 0);
sleep_us(10);
spi_write_read_blocking(SPI_PORT, &adress, &rx_buf[0], 1);
gpio_put(PIN_CS, 1);
sleep_us(10);
gpio_put(PIN_CS, 0);
sleep_us(10);
spi_write_read_blocking(SPI_PORT, 0x00, &rx_buf[1], 1);
gpio_put(PIN_CS, 1);
sleep_us(10);
printf("Reg value 0x01: 0x%02X - 0x%02X\n", rx_buf[0], rx_buf[1]);
sleep_ms(1000);
gpio_put(PIN_CS, 0);
sleep_us(10);
spi_write_read_blocking(SPI_PORT, &test, &rx_buf[0], 1);
gpio_put(PIN_CS, 1);
sleep_us(10);
gpio_put(PIN_CS, 0);
sleep_us(10);
spi_write_read_blocking(SPI_PORT, 0x00, &rx_buf[1], 1);
gpio_put(PIN_CS, 1);
sleep_us(10);
printf("Reg value 0x02: 0x%02X - 0x%02X\n", rx_buf[0], rx_buf[1]);
sleep_ms(1000);
}
}
Slave code:
#define SS PA7
#define MOSI PA6
#define MISO PA5
#define SCK PA4
volatile byte registerMap[3] = {};
volatile bool lastSSState = 0;
volatile byte receivedAddress = 0;
volatile byte transactionStep = 0;
void setup()
{
pinMode(SCK, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
pinMode(SS, INPUT);
SPCR = (1 << SPE);
SPDR = registerMap[0];
registerMap[0x01] = 0x43;
registerMap[0x02] = 0x99;
PCMSK0 |= (1 << PCINT7);
GIMSK |= (1 << PCIE0);
lastSSState = digitalRead(SS);
sei();
}
void loop(){}
ISR(PCINT0_vect)
{
bool currentState = digitalRead(SS);
if(lastSSState && !currentState) //Falling edge
{
if(transactionStep == 0) //First falling edge
{
while(!(SPSR & (1 << SPIF)));
byte received = SPDR;
receivedAddress = received;
if(SPSR & (1 << WCOL))
{
volatile byte tmp = SPDR;
}
SPDR = registerMap[receivedAddress];
transactionStep = 1;
}
else if(transactionStep == 1) //Second falling edge
{
while(!(SPSR & (1 << SPIF)));
volatile byte dummy = SPDR;
transactionStep = 0;
receivedAddress = 0;
if(SPSR & (1 << WCOL))
{
volatile byte tmp = SPDR;
}
SPDR = 0x00;
}
}
lastSSState = currentState;
}
Try this, it builds on your version 2 idea but simplifies things so that only the minimal SPI transaction is conducted. You will only see the value 0x12 and that is useful to know if there is a glitch before the master application begins running.
SLAVE
void setup()
{
pinMode(SCK, INPUT);
pinMode(MOSI, INPUT);
pinMode(MISO, OUTPUT);
pinMode(SS, INPUT);
SPCR = (1 << SPE); // SPI CONTROL REGISTER, ENABLE SPI IN SLAVE MODE
SPDR = 0x12; // CONSTANT VALUE SLAVE SENDS RESPONSE TO MASTER ADDRESS
PCMSK0 |= (1 << PCINT7); // PIN CHANGE MASK SELECTS PA7/!SS AS PIN TO WATCH
GIMSK |= (1 << PCIE0); // PIN CHANGE INTERRUPT ENABLE 0
sei(); // SREG I-BIT = 1
}
void loop(){}
ISR(PCINT0_vect)
{
// PIN CHANGED (LOW)
while( !digitalRead(SS) ) ; // WAIT FOR MASTER TO SET SS HIGH
SPDR = 0x34; // SS IS HIGH, UPDATE DATA REGISTER, NEXT LOW SS WILL GET THIS VALUE
}
MASTER
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include <stdio.h>
#define SPI_PORT spi0
#define PIN_MISO 16
#define PIN_MOSI 19
#define PIN_SCK 18
#define PIN_CS 17
void spi_init_master()
{
spi_init(SPI_PORT, 1000000);
gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
gpio_set_function(PIN_SCK, GPIO_FUNC_SPI);
gpio_init(PIN_CS);
gpio_set_dir(PIN_CS, GPIO_OUT);
gpio_put(PIN_CS, 1);
}
int main()
{
stdio_init_all();
spi_init_master();
uint8_t address = 0x00;
uint8_t value = 0x00;
uint8_t dontcare = 0x00;
gpio_put(PIN_CS, 0);
sleep_us(10);
spi_write_read_blocking(SPI_PORT, &address, &dontcare, 1);
gpio_put(PIN_CS, 1);
printf("SENT ADDRESS 0x%02X GOT BACK 0x%02X\n", address, dontcare);
sleep_us(10);
gpio_put(PIN_CS, 0);
sleep_us(10);
spi_write_read_blocking(SPI_PORT, &dontcare, &value, 1);
gpio_put(PIN_CS, 1);
sleep_us(10);
printf("SENT DUMMY 0x%02X GOT BACK 0x%02X\n", dontcare, value);
}