Inconsistent SPI behavior when moving SPI instantiation

I'm experiencing inconsistent behavior when moving the SPI transaction code from the .ino file to a source .cpp.

An Arduino Nano Every (Master) controlling a small PCB (Slave) with a 74HC595 that manages a 7-segment display.

┌── B ──┐			7-segment display diagram
A   	C				for explanation purposes
├──	G ──┤
F		D
└──	E ──┘	DP

Case A: everything in a single file.

// 7-segment.ino
void setup() {
    SPI.begin();

    pinMode(6, OUTPUT);
    digitalWrite(6, HIGH);
}

void loop() {
    digitalWrite(6, LOW);
    SPI.transfer(0b11111110);
    digitalWrite(6, HIGH);
    delay(200);

    digitalWrite(6, LOW);
    SPI.transfer(0b11111111);
    digitalWrite(6, HIGH);
    delay(200);
}

Result: Consistent blinking of segment F no matter how roughly the hardware is handled.

Case B: code split between source and header.

// 7-segment.ino
#include "ExCU_gear.h"

ExCU_Gear gearIndicator = ExCU_Gear(6);

void setup() {
    gearIndicator.begin();
}

void loop() {
    gearIndicator.sendRaw(0b11111110, true);
    delay(200);

    gearIndicator.sendRaw(0b11111111, true);
    delay(200);
}


// ExCU_Gear.h
#ifndef ExCU_Gear_h
#define ExCU_Gear_h
#pragma once

#include <Arduino.h>
#include <SPI.h>

class ExCU_Gear
{
private:
  uint8_t latchPin;
  uint8_t last = NULL;

public:
  ExCU_Gear(uint8_t latch_pin);
  void begin();
  bool sendRaw(uint8_t data, bool force = false);
};

#endif


// ExCU_Gear.cpp
#include "ExCU_gear.h"

ExCU_Gear::ExCU_Gear(uint8_t latch_pin)
{
    latchPin = latchPin;
}

void ExCU_Gear::begin()
{
    SPI.begin();
    pinMode(latchPin, OUTPUT);
    digitalWrite(latchPin, HIGH);
}

bool ExCU_Gear::sendRaw(uint8_t data, bool force)
{
    if (data == last && !force)
    {
        return false;
    }
    else
    {
        digitalWrite(latchPin, LOW);
        SPI.transfer(data);
        digitalWrite(latchPin, HIGH);
        last = data;
    }

    return true;
}

Result: Segment B blinks inconsistently (a second segment sparingly flashes at random) and everything breaks down when the cables are touched.

  • Pads 1 through 5 connect to the Nano pins via cables
  • VCC provided by external regulator (GND shared with Nano)

I have also tried using beginTransaction() instead of begin() with no success.

After digging around I found out that the proper way to interact with SPI in different classes is to pass a SPIClass pointer to the object constructor and access the bus through that.
You would then use .beginTransaction() / .endTransaction() for communicating, just remember to call the regular .begin() somewhere in the setup.
I'm not sure of the underlying technical issue that causes my issue but this is the solution, I have a feeling this is by design but it's not well explained anywhere in the docs that I read.

In my case the code now looks like this:

ExCU_Gear::ExCU_Gear(const uint8_t latch_pin, SPIClass & spi) : 
    mSPI (spi),
    latchPin (latch_pin)
{
}

void ExCU_Gear::begin()
{
    mSPI.begin();

    mSPISettings = SPISettings(20000000, MSBFIRST, SPI_MODE0);
    
    pinMode(latchPin, OUTPUT);
    digitalWrite(latchPin, HIGH);
}

bool ExCU_Gear::sendRaw(uint8_t data, bool force)
{
    if (data == last && !force)
    {
        return false;
    }
    else
    {
        SPI.beginTransaction(mSPISettings);

        digitalWrite(latchPin, LOW);
        SPI.transfer(data);
        digitalWrite(latchPin, HIGH);
        last = data;

        SPI.endTransaction();
    }
    return true;
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.