Electron SPI1 Slave has SPI_MODE_SLAVE cyclic reboot>>>SOLVED

Should I try 0.6.0 or is it something else?

This code at 0.5.3 just keeps rebooting every time I get a SPI transaction from the master.

I have a DTR pin defined for the Electron (B5) to let the Master wait until the Electron has had a chance to set up and initialize status internally. The master does not initiate any SPI traffic until the DTR pin goes HIGH from the Electron. This seems to work OK on the master side because the Teensy(master) sits cycling through the other tasks until the DTR line goes high and then I see its( the teensy) status lights indicate a transfer initiated. The teensey is clocking at 2MB as well. Should I not set the slave clock, or should I set this at something they both have in common. I guess the SPI1 port can go to 30Mz,

the Teensy port goes to For T3.6@180mhz, the bus is 60mhz…

maybe I can run at 240Mhz and clockdivide by 8 for 30 MHz. I could try other combinations unless someone suggests something else.

I can post the Teensy code too…If wanted.

Is the proper way to put the SPI begin before or after the parameter sets? I have tried both ways and it doesn’t produce different results and I have looked at the example for SPI slave…as adapted from here

https://docs.particle.io/reference/firmware/photon/#transfer-void-void-size_t-std-function-

I guess , I have another problem as well. The electron boots normally. but when it raises the DTR pin, the master starts an SPI transaction and I guess I overflow the stack…the master is only supposed to do one 64 byte transfer. That is I have a one shot on the master side for now to do testing of a single transfer.

This just repeats with the Electron rebooting getting online to breathing CYAN, and then raising the DTR line, and stack overflow with red flashing light on the electron.

My slave code snippet (which is all I have in the electron at the moment) is as follows:


// This #include statement was automatically added by the Particle IDE.
#include "neopixel/neopixel.h"


#include "application.h"

// #include "neopixel.h" // use for local build

SYSTEM_MODE(AUTOMATIC);

// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN 2
#define PIXEL_COUNT 8
#define PIXEL_TYPE WS2812B
 typedef enum Hregs{
  ADC_VAL,
  SEC_SAMPLE, //sample interval
  BRIX,       //immediate reading 
  READ_BRIX, //conditioned reading
  ATEMP,    //Analyzer temperature reading
  AREFI,     //Analyzer Refractive index
  APSI,       //sample identifier
  ITEMP,      //internal temp
  SAMPLET,    //sample pump run value
  SAMPLED,    // sample pump run speed
  PAMPLEV,    //spatter sample count
  SAMPLES,    // number of presamples-1
  SAMPLEV,    // sampled volume
  RINSES,     //number of rinses in a CIP
  WASHES,     //number of washes
  RINSEV,     //volume in micoliters
  RINSEBV,    //back up deadleg in microliters
  WASHV,      //wash volume in microliters
  WASHBV,     //backup volume in microliters
  ABLOWT,     //airblow time in number of scans (about 250ms per scan)
  STATE_SI,   //State of unit
  CIP_SI,    //CIP module state
  DRAIN_SI,
  WASH_SI,
  RINSE_SI,
  FRINSE_SI,
  SAMPLE_SI,
  PURGE_SI,
  ITCR, //Internal temperature
  Ia0H,
  Ia0L,  //first 2053 scale data
  Ia1H,
  Ia1L,//second 2053 scale data
  Ia2H,
  Ia2L,//third 2053 scale data
  Ia3H,
  Ia3L,//fourth 2053 scale data
  IaAH,
  IaAL,//first 2053 mV input
  IaBH,
  IaBL,//second 2053 mV input
  IaCH,
  IaCL,//third 2053 mV input
  IaDH,
  IaDL,//fourth 2053 mV input
  I2cA0,//first ads1115 V input
  I2cA1,//second ads1115 V input
  I2cA2,//second ads1115 V input
  I2cA3,//second ads1115 V input
  I2cA4,//first ads1115 V input
  I2cA5,//second ads1115 V input
  I2cA6,//second ads1115 V input
  I2cA7,//second ads1115 V input
  I2cP0,
  I2CP1,
  I2CP2,
  I2CP3,
  I2CP4,
  I2CP5,
  I2cP6,
  I2cP7,
  PWM16,
  PWM17,
  PWM18,
  PWM19,
  HOLDING_REGS_SIZE // leave this one
  // total number of registers for function 3 and 16 share the same register array
  // i.e. the same address space
};

 typedef enum Iregs{
  VVVVV,
  INPUT_REGS_SIZE
};

typedef enum Cbits{
  _Cfault,
 _C1,
 _C2,
  _C3,
  _C4,
  _C5,
  COIL_REGS_SIZE
};
typedef enum PixColor{
  P_BLACK,
  P_BROWN,
  P_RED,
  P_ORANGE,
  P_YELLOW,
  P_GREEN,
  P_BLUE,
  P_VIOLET,
  P_GREY,
  P_WHITE
};

//phase steps
typedef enum PhaseStates{
  SROUTDN,
  SCYCLSDN,
  SPULSCHECK,
  SBVCHECK,
  SBVCALC,
  SPAIRCHECK,
  SPPCALC,
  SABLOW,
  SSETUP,
  SRUNAWAY
};

typedef struct{
  char Pred;
  char Pgreen;
  char Pblue;
} 
PCOLORS_DEF;

PCOLORS_DEF pcolor[10] ={ 
  0,0,0, //black
  36,12,5, //brown
  48,0,0, //red
  48,16,0,  //orange
  48,32,0, //yellow
  0,48,0, // green
  0,0,48, // blue
  32,0,32, //violet
  32,32,32, //grey
  84,84,85 //white

};

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

// Prototypes for local build, ok to leave in for Build IDE

unsigned long int AIRtime = 100000;
static char rx_buffer[64];
static char tx_buffer[64];
static uint32_t select_state = 0x00;
static uint32_t transfer_state = 0x00;
const int DTRPin = B5;
const int CSPin = A2;
char Mb_R[64];


void onTransferFinished() {
    transfer_state = 1;
}

void onSelect(uint8_t state) {
    if (state)
        select_state = state;
}


void setup()
{
  pinMode(DTRPin, OUTPUT);
  pinMode(CSPin, INPUT_PULLUP);
  digitalWrite(DTRPin, LOW);
  pixels.begin();
  pixels.show();
  delay(3000);
  Mb_R[CIP_SI] = 0x1; // CIP Step color.
  Mb_R[DRAIN_SI] = 0x2; // Drain Step color.
  Mb_R[WASH_SI] = 0x3; // Wash Step color.
  Mb_R[RINSE_SI] = 0x4; // Rinse Step color.
  Mb_R[FRINSE_SI] = 0x5; // Final Rinse Step color.
  Mb_R[SAMPLE_SI] = 0x6; // Sample Step color.
  Mb_R[PURGE_SI] = 0x0; //initialize purg of lines.
  Mb_R[ADC_VAL] = 0x7;// Initialize all pixels to 'off'
  UpdateLights();
  for (int i = 0; i < sizeof(tx_buffer); i++)
      tx_buffer[i] = Mb_R[i];
  delay(1000);
  SPI1.onSelect(onSelect);
  SPI1.setBitOrder(MSBFIRST);
  SPI1.setClockSpeed(2000000);
  SPI1.setDataMode(SPI_MODE0);
  SPI1.begin(SPI_MODE_SLAVE, CSPin);//this was D5 but A2 is closer 
}
void loop()
{

if (select_state == 0);
{
    SPI1.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), onTransferFinished);
    digitalWrite(DTRPin, LOW);
    while(SPI1.available()<64);
     for (int i = 0; i < sizeof(rx_buffer); i++)
      Mb_R[i] = rx_buffer[i] % 10;
    select_state = 1;
    transfer_state = 0;
}
UpdateLights();
}

void UpdateLights(){

  pixels.setPixelColor(0, pixels.Color(pcolor[Mb_R[STATE_SI]].Pred,pcolor[Mb_R[STATE_SI]].Pgreen,pcolor[Mb_R[STATE_SI]].Pblue)); // Main task state color.
  pixels.setPixelColor(1, pixels.Color(pcolor[Mb_R[CIP_SI]].Pred,pcolor[Mb_R[CIP_SI]].Pgreen,pcolor[Mb_R[CIP_SI]].Pblue)); // CIP Step color.
  pixels.setPixelColor(2, pixels.Color(pcolor[Mb_R[DRAIN_SI]].Pred,pcolor[Mb_R[DRAIN_SI]].Pgreen,pcolor[Mb_R[DRAIN_SI]].Pblue)); // Drain Step color.
  pixels.setPixelColor(3, pixels.Color(pcolor[Mb_R[WASH_SI]].Pred,pcolor[Mb_R[WASH_SI]].Pgreen,pcolor[Mb_R[WASH_SI]].Pblue)); // Wash Step color.
  pixels.setPixelColor(4, pixels.Color(pcolor[Mb_R[RINSE_SI]].Pred,pcolor[Mb_R[RINSE_SI]].Pgreen,pcolor[Mb_R[RINSE_SI]].Pblue)); // Rinse Step color.
  pixels.setPixelColor(5, pixels.Color(pcolor[Mb_R[FRINSE_SI]].Pred,pcolor[Mb_R[FRINSE_SI]].Pgreen,pcolor[Mb_R[FRINSE_SI]].Pblue)); // Final Rinse Step color.
  pixels.setPixelColor(6, pixels.Color(pcolor[Mb_R[SAMPLE_SI]].Pred,pcolor[Mb_R[SAMPLE_SI]].Pgreen,pcolor[Mb_R[SAMPLE_SI]].Pblue)); // Sample Step color.
  if (AIRtime>millis()){
    pixels.setPixelColor(7, pixels.Color(pcolor[P_RED].Pred,pcolor[P_RED].Pgreen,pcolor[P_RED].Pblue));
  }
  else
  { 
    pixels.setPixelColor(7, pixels.Color(pcolor[P_GREEN].Pred,pcolor[P_GREEN].Pgreen,pcolor[P_GREEN].Pblue));// Moderately bright green color.
  }
  pixels.show(); // This sends the updated pixel color to the hardware.
  // Magical sprintf creates a string for us to send to the s7s.
  //  The %4d option creates a 4-digit integer.
  digitalWrite(DTRPin, HIGH);

}

maybe @ScruffR @peekay123

Can you narrow down the place where SOS happens?

e.g.
add a “premature” return; in void UpdateLights() to tick that off as the offending function
vs.
comment out the respective calls, see if the call itself causes the stack to overflow
and lots of other debugging steps.

By just skimming over your code nothing obvious jumped at me, but when you can narrow down the offending code, we might be able to see more.

Should I try 0.6.0 or is it something else?

I don’t even use the lights…

Teensy is at 120Mhz and runs the clock at spi clock at 2MHz…

with code below I get the same thing, booting cyclic.

I really deleted all except the essentials i don’t even use the lights…same result.

I might just try 30Mhz as the default and get the teensy to have an integral divisor for the spi clock…There may be clock jitter, but I dont think that is the problem. If there are any other ideas…

My slave code snippet (which is all I have in the electron at the moment) is now as follows:


// This #include statement was automatically added by the Particle IDE.
#include "neopixel/neopixel.h"


#include "application.h"

// #include "neopixel.h" // use for local build

SYSTEM_MODE(AUTOMATIC);

// IMPORTANT: Set pixel COUNT, PIN and TYPE
#define PIXEL_PIN 2
#define PIXEL_COUNT 8
#define PIXEL_TYPE WS2812B


Adafruit_NeoPixel pixels = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);

// Prototypes for local build, ok to leave in for Build IDE

unsigned long int AIRtime = 100000;
static char rx_buffer[64];
static char tx_buffer[64];
static uint32_t select_state = 0x00;
static uint32_t transfer_state = 0x00;
const int DTRPin = B5;
const int CSPin = A2;
char Mb_R[64];


void onTransferFinished() {
    transfer_state = 1;
}

void onSelect(uint8_t state) {
    if (state)
        select_state = state;
}


void setup()
{
  pinMode(DTRPin, OUTPUT);
  pinMode(CSPin, INPUT_PULLUP);
  digitalWrite(DTRPin, HIGH);
  SPI1.onSelect(onSelect);
  SPI1.setBitOrder(MSBFIRST);
  SPI1.setClockSpeed(2000000);
  SPI1.setDataMode(SPI_MODE0);
  SPI1.begin(SPI_MODE_SLAVE, CSPin);//this was D5 but A2 is closer

}
void loop()
{

if (select_state == 0)
{
    SPI1.transfer(tx_buffer, rx_buffer, sizeof(rx_buffer), onTransferFinished);
    while(transfer_state < 1);
    digitalWrite(DTRPin, LOW);
    delay(100);
    select_state = 1;
    transfer_state = 0;
}

Thankyou();
}

void Thankyou(){

    digitalWrite(DTRPin, HIGH);
    
}

void UpdateLights(){
return;


}


Actually, it appears that i get between 4 and 11 trnsactions between reboots.

Yes, you need to upgrade to 0.6.0. There’s a bug in all of 0.5.x versions where using SPI1 on an Electron will SOS fault.

2 Likes

Can I take you to lunch? Thanks!

1 Like

Thanks. I upgraded to 0.6.0 and all is well.

Everything is fine…the teensy has repetitive successful spi transactions with the ELECTRON. Now for the next hoop. It would be interesting to have a selectable mode, which is kind of like what I am planning anyway…Were slave and master switch roles to use your sdfirmwareflash-master or some variation to have the Teensy act as a flash loader for the ELECTRON…then they could reverse roles later in the application for the ELECTRON to be the slave during normal operation?? Where the ELECTRON can be the web publisher for the Teensy??

I guess I can just USB flash either from the PI.

HAR. In my other app I was going to use the PI as the bootloader for the teensy… this little trio could self mutate remotely.