spiraspberry-pi-picoattiny

ATtiny841 slave SPDR register


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?

I've tried it with an interrupt, I've tried it without an interrupt. But every time I get the dummy values from the master back as an input. I would prefer it to work with an interrupt

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;
}

Solution

  • 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);
    }