SPI does not work after self-wakeup

Discussion about Z-Uno product. Visit http://z-uno.z-wave.me for more details.
Post Reply
jucs
Posts: 35
Joined: 28 Dec 2016 17:56

SPI does not work after self-wakeup

Post by jucs »

Hello everyone,

since I didn't get I2C to work, I today tried SPI. Works great... until the chip wakes up from deep sleep mode. Like the ADC, the SPI does not work any longer...

p0lyg0n1
Posts: 183
Joined: 04 Aug 2016 07:14

Re: SPI does not work after self-wakeup

Post by p0lyg0n1 »

So, you give me another test case:) I will try it too!

jucs
Posts: 35
Joined: 28 Dec 2016 17:56

Re: SPI does not work after self-wakeup

Post by jucs »

Here it is; Z-Uno code (master):

Code: Select all

#include "Arduino.h"
#include "EEPROM.h"
#include "SPI.h"

ZUNO_SETUP_ASSOCIATIONS(ZUNO_ASSOCIATION_GROUP_SET_VALUE); // Send Basic Set to association group

word getterMoisture() {
    int moisture = read(18);
    return moisture;
}

// set up channel
ZUNO_SETUP_CHANNELS(
    ZUNO_SENSOR_MULTILEVEL(ZUNO_SENSOR_MULTILEVEL_TYPE_MOISTURE,
                           SENSOR_MULTILEVEL_SCALE_PERCENTAGE_VALUE,
                           SENSOR_MULTILEVEL_SIZE_TWO_BYTES,
                           SENSOR_MULTILEVEL_PRECISION_ONE_DECIMAL,
                           getterMoisture)
);

ZUNO_SETUP_SLEEPING_MODE(ZUNO_SLEEPING_MODE_SLEEPING);

ZUNO_SETUP_DEBUG_MODE(DEBUG_ON);

void setup() {
    SPI.begin();
    pinMode(8, OUTPUT);
    digitalWrite(8, HIGH);
}

void loop() {
  SPISettings spi_settings = SPISettings(1000000, MSBFIRST, SPI_MODE0);
  SPI.beginTransaction(&spi_settings);
  digitalWrite(8, LOW);
  int x = 0;
  for (unsigned long i = 0; i < 1 * 65535; i++)
    x += 1;
  SPI.transfer(0);
  for (unsigned long i = 0; i < 1 * 65535; i++)
    x += 1;
  SPI.transfer(1);
  for (unsigned long i = 0; i < 1 * 65535; i++)
    x += 1;
  int moisture = SPI.transfer(2);
  for (unsigned long i = 0; i < 1 * 65535; i++)
    x += 1;
  moisture |= SPI.transfer(3) << 8;
  digitalWrite(8, HIGH);
  SPI.endTransaction();
  moisture = map(moisture, 1023, 0, 0, 1000);
  write(18, moisture);

  // send data to channel
  zunoSendReport(1);

  delay(300);
  zunoSendDeviceToSleep();
}
Atmega328p (slave):

Code: Select all

#include "Arduino.h"
#include "LowPower.h"
#include "SPI.h"

void wakeUp() {
  
}

void setup(void) {
    pinMode(14, OUTPUT);
    pinMode(MISO, OUTPUT);
    
    // turn on SPI in slave mode
    SPCR |= _BV(SPE);
    
    // turn on interrupts
    SPCR |= _BV(SPIE);
    
    attachInterrupt(0, wakeUp, FALLING);
}

volatile int moisture;

void loop(void) {
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);
    delay(3000);
}


// SPI interrupt routine
ISR (SPI_STC_vect) {
    byte c = SPDR;
    if(c == 0) {
      moisture = analogRead(A5);
      SPDR = 0;
    } else if(c >= 1 && c <= 2) {
      byte *m = (byte*)&moisture;
      SPDR = m[c - 1];
    } else {
      SPDR = 0;
    }
}
I see that some of the code (in particular the delays) are a little hacky, but it does work fine before the Z-Uno falls asleep. The chips are connected using the SPI pins as well as GND. Additionally, you need to connect CS to INT0 to wake the Atmega up before the communication.

I am using the Arduino Uno chip and software, however it is running at 3.3V (it's just the chip + quartz).

p0lyg0n1
Posts: 183
Joined: 04 Aug 2016 07:14

Re: SPI does not work after self-wakeup

Post by p0lyg0n1 »

I made my own code for SPI, before I see yours...
So, here is my code for Mega2560(SPI SLAVE)

Code: Select all

#include <SPI.h>

#define COMMAND_READADC     0x40
#define COMMAND_PININ       0x20
#define COMMAND_PINOUTLOW   0x30
#define COMMAND_PINOUTHIGH  0x50
#define COMMAND_PINGET      0x70
#define COMMAND_READDATA    0xA0

#define PACKET_LEN 4

byte spi_input_buff[PACKET_LEN];
byte spi_output_buff[PACKET_LEN];
volatile byte spi_count;
volatile byte spi_tmp;

//Initialize SPI slave.
void SlaveInit(void) {
  // turn on SPI in slave mode
  SPCR |= bit (SPE);

  // have to send on master in, *slave out*
  pinMode(MISO, OUTPUT);
  
  // get ready for an interrupt 
  spi_count = 0;  
  
  // now turn on interrupts
  SPI.attachInterrupt();
  
}

// SPI interrupt routine
ISR (SPI_STC_vect)
{ 
  if(spi_count < PACKET_LEN)
  {
    spi_input_buff[spi_count] = SPDR;
    SPDR = spi_output_buff[spi_count];
    spi_count++;
  }
}  // end of interrupt routine SPI_STC_vect


// The setup() function runs after reset.
void setup() {
  // Initialize serial for troubleshooting.
  Serial.begin(9600);
  // Initialize SPI Slave.
  SlaveInit();
  Serial.println("Slave Initialized");
}

int last_result = 0;
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()

void receiveEvent()
{
    if(spi_count != PACKET_LEN)
      return;
    
    byte c = spi_input_buff[0];
    byte pin = c &0x0F;
    c &= 0xF0;
    last_result = 0;

    Serial.println("***C:");
    Serial.println(c, HEX);
    Serial.println(pin, HEX);
    switch(c)
    {
       case COMMAND_READADC:
         last_result = analogRead(A0+pin);
          break;
        case COMMAND_PININ:
          last_result = 1;
          pinMode(pin, INPUT_PULLUP);
          break;
        case COMMAND_PINOUTLOW:
          last_result = 1;
          pinMode(pin, OUTPUT);
          digitalWrite(pin, 0);
          break;
        case COMMAND_PINOUTHIGH:
          last_result = 1;
          pinMode(pin, OUTPUT);
          digitalWrite(pin, 1);
          break;   
        case COMMAND_PINGET:
          last_result = digitalRead(pin);
          break;   
    }
    spi_output_buff[0] = spi_input_buff[0]; 
    spi_output_buff[1] = last_result >> 8; 
    spi_output_buff[2] = last_result & 0xFF; 
    spi_output_buff[3] = 0xDA; 
    

    spi_count = 0;
    
    
}
// The loop function runs continuously after setup().
void loop() {
  // Slave Enabled?
  // Slave Enabled?
  receiveEvent();
  
}
Here is Z-Uno sketch (SPI MASTER)

Code: Select all

#include <SPI.h>

#define SLEEPING

#define COMMAND_READADC     0x40
#define COMMAND_PININ       0x20
#define COMMAND_PINOUTLOW   0x30
#define COMMAND_PINOUTHIGH  0x50
#define COMMAND_PINGET      0x70

ZUNO_SETUP_SLEEPING_MODE(ZUNO_SLEEPING_MODE_SLEEPING);
ZUNO_SETUP_DEBUG_MODE(DEBUG_ON);

#define MY_SERIAL           Serial0



#define SS_PIN 9

SPISettings spi_settings;

byte last_error = 0;

void setup() {
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH);

  MY_SERIAL.begin(115200);
  MY_SERIAL.println("Sketch is starting...");

  SPI.begin();
}

word transferSPICMD(byte cmd, word d)
{
    byte spi_bytes[4];
    word w;

    last_error = 0;
    
    digitalWrite(SS_PIN, LOW);
    SPI.beginTransaction(&spi_settings);
    spi_bytes[0] = cmd;
    spi_bytes[1] = 0xAA;
    spi_bytes[2] = 0xBB;
    spi_bytes[3] = 0xCC;
    
    SPI.transfer(spi_bytes, 4);
    delay(d);
    spi_bytes[0] = 0xF1;
    spi_bytes[1] = 0xF2;
    spi_bytes[2] = 0xF3; 
    spi_bytes[3] = 0xF4;

    SPI.transfer(spi_bytes, 4);
    SPI.endTransaction();
    digitalWrite(SS_PIN, HIGH);

    if(spi_bytes[1] != cmd)
      last_error = 1;

    w = spi_bytes[2];
    w <<= 8;
    w |= spi_bytes[3];


    return w;

}
word readADC(byte pin)
{   
    return transferSPICMD(COMMAND_READADC | (pin & 0x0F), 20);
}
void pinONOFF(byte pin, byte onoff)
{
    
    pin &= 0x0F;   
    if(onoff)
    {
       pin |= COMMAND_PINOUTHIGH; 
    }
    else
    {
       pin |= COMMAND_PINOUTLOW;
    }
    transferSPICMD(pin, 2);
   
}
byte g_counter =0;
void loop() {

  pinONOFF(13, g_counter & 0x08);
  
  MY_SERIAL.print("millis:");
  MY_SERIAL.println(millis());
  
  
  byte i;
  for(i=0;i<4;i++)
  {
      word w =  readADC(i);
      if(last_error == 0)
      {
        MY_SERIAL.print("ADC ");
        MY_SERIAL.print(i);
        MY_SERIAL.print(" value:");
        MY_SERIAL.println(w, HEX);
      }
      delay(5);
      
  }
  g_counter++;

  if(g_counter > 20)
  {
    MY_SERIAL.print("GO SLEEP");
    zunoSendDeviceToSleep(); 
  } 
  
  delay(100);
}

So, first of all. Please don't do analogRead inside ISR. You will miss data in that case. Here is some concepts of SPI slave:
1. You can send data to master only when master sends data to you. So, the master will see what you place in SPDR before transmition.

Post Reply