Skip to content

Class OpenSkyhawk::ShiftBus

ClassList > OpenSkyhawk > ShiftBus

One shared SPI shift-register bus ('165 inputs + '595 outputs). More...

  • #include <ShiftBus.h>

Public Static Attributes

Type Name
constexpr uint8_t MAX_CHAIN = 8
chips per direction (64 in + 64 out)
constexpr uint8_t MAX_ISR_CONSUMERS = 16
sampled encoders per bus

Public Functions

Type Name
ShiftBus (SPIClass & spi, uint8_t sckPin, uint8_t misoPin, uint8_t mosiPin, uint8_t loadPin, uint8_t latchPin)
Construct a bus. No hardware activity — begin() runs inPanelGroup::setup() .
bool active () const
Any SR pin configured on this bus? Dormant buses skip begin() entirely.
void addIsrConsumer (void(*)(void *ctx) hook, void * ctx)
Register a hook called from the sampling ISR after each transfer() . Used byRotaryEncoder::configure() to auto-attach SR-pinned encoders.
void begin ()
Claim pins + start SPI. Called by PanelGroup::setup() for active buses only.
void beginIsrSampling (TIM_TypeDef * tim, uint16_t sampleHz)
Start a hardware timer that runs transfer() + all registered consumer hooks every tick. Called byPanelGroup::setup() when SHIFTBUS_ISR_HZ is defined.
bool dirty () const
Pending output changes not yet shifted?
void flushNow ()
transfer() immediately —StepperMotor 's per-step flush path.
bool isrActive () const
True once beginIsrSampling() has started the timer.
void noteInput (uint8_t chip)
configureAsInput() hook: mark active, grow the '165 chain to cover chip .
void noteOutput (uint8_t chip)
configureAsOutput() hook: mark active, grow the '595 chain to cover chip .
bool readBit (uint8_t chip, uint8_t bit) const
Cached '165 input bit — no bus traffic.
bool readLiveBit (uint8_t chip, uint8_t bit)
Live '165 read — one transfer() , then the cached bit.
bool readOutBit (uint8_t chip, uint8_t bit) const
Last written '595 output bit ( PinRef::read() on an output pin).
void transfer ()
One bus transaction: LOAD pulse → full-duplex SPI of the auto-sized frame (outputs written, inputs captured) → LATCH pulse. Clears the dirty flag.
void writeBit (uint8_t chip, uint8_t bit, bool v)
Set a '595 stage bit + mark the stage dirty. Published by the next loop-context transfer() /flushNow() (commit), never mid-group by the sampling ISR.

Detailed Description

Constructor stores parameters only — no SPI, no GPIO, no cross-global calls (static-init safe; the PIN_NC lesson). All hardware work happens in begin(), called by PanelGroup::setup() for buses that have at least one configured SR pin.

Public Static Attributes Documentation

variable MAX_CHAIN

chips per direction (64 in + 64 out)

constexpr uint8_t OpenSkyhawk::ShiftBus::MAX_CHAIN;

variable MAX_ISR_CONSUMERS

sampled encoders per bus

constexpr uint8_t OpenSkyhawk::ShiftBus::MAX_ISR_CONSUMERS;

Public Functions Documentation

function ShiftBus

Construct a bus. No hardware activity — begin() runs inPanelGroup::setup() .

OpenSkyhawk::ShiftBus::ShiftBus (
    SPIClass & spi,
    uint8_t sckPin,
    uint8_t misoPin,
    uint8_t mosiPin,
    uint8_t loadPin,
    uint8_t latchPin
) 

Parameters:

  • spi SPI peripheral. Dedicated to this bus — the '165 QH output is never tristated, so MISO cannot be shared with another SPI reader.
  • sckPin SPI clock pin (must belong to spi — e.g. PB3 for SPI1-remap).
  • misoPin SPI MISO pin (← '165 QH).
  • mosiPin SPI MOSI pin (→ '595 DS).
  • loadPin '165 SH/LD̄ strobe (idles HIGH; pulsed LOW to capture inputs).
  • latchPin '595 STCP strobe (pulsed HIGH to publish outputs).

function active

Any SR pin configured on this bus? Dormant buses skip begin() entirely.

inline bool OpenSkyhawk::ShiftBus::active () const

function addIsrConsumer

Register a hook called from the sampling ISR after each transfer() . Used byRotaryEncoder::configure() to auto-attach SR-pinned encoders.

void OpenSkyhawk::ShiftBus::addIsrConsumer (
    void(*)(void *ctx) hook,
    void * ctx
) 

function begin

Claim pins + start SPI. Called by PanelGroup::setup() for active buses only.

void OpenSkyhawk::ShiftBus::begin () 

Releases JTAG (SWJ → SWD-only; PB3/PB4 are JTDO/NJTRST) so the SPI1-remap pins are usable, configures LOAD/LATCH strobes, starts SPI, shifts an all-zeros output frame * latch (defined '595 state as early as possible), then runs one transfer() to prime the input cache before the forceReport() boot burst reads it.


function beginIsrSampling

Start a hardware timer that runs transfer() + all registered consumer hooks every tick. Called byPanelGroup::setup() when SHIFTBUS_ISR_HZ is defined.

void OpenSkyhawk::ShiftBus::beginIsrSampling (
    TIM_TypeDef * tim,
    uint16_t sampleHz
) 

Parameters:

  • tim Timer instance (TIM2 by default — TIM3 is backlight PWM).
  • sampleHz Sample rate (e.g. 1000).

Note:

The ISR performs no CAN, no I2C, no allocation. Consumers read cached bits only.


function dirty

Pending output changes not yet shifted?

inline bool OpenSkyhawk::ShiftBus::dirty () const

function flushNow

transfer() immediately —StepperMotor 's per-step flush path.

inline void OpenSkyhawk::ShiftBus::flushNow () 

function isrActive

True once beginIsrSampling() has started the timer.

inline bool OpenSkyhawk::ShiftBus::isrActive () const

function noteInput

configureAsInput() hook: mark active, grow the '165 chain to cover chip .

void OpenSkyhawk::ShiftBus::noteInput (
    uint8_t chip
) 

function noteOutput

configureAsOutput() hook: mark active, grow the '595 chain to cover chip .

void OpenSkyhawk::ShiftBus::noteOutput (
    uint8_t chip
) 

function readBit

Cached '165 input bit — no bus traffic.

bool OpenSkyhawk::ShiftBus::readBit (
    uint8_t chip,
    uint8_t bit
) const

Note:

With ISR sampling active, a multi-bit consumer reading bit-by-bit can see two sample instants mixed within one poll (the ISR replaces the whole frame between reads). Debounced/hold-last consumers (MultiPosInput) absorb this; do not add an unfiltered multi-bit consumer without considering it.


function readLiveBit

Live '165 read — one transfer() , then the cached bit.

bool OpenSkyhawk::ShiftBus::readLiveBit (
    uint8_t chip,
    uint8_t bit
) 

function readOutBit

Last written '595 output bit ( PinRef::read() on an output pin).

bool OpenSkyhawk::ShiftBus::readOutBit (
    uint8_t chip,
    uint8_t bit
) const

function transfer

One bus transaction: LOAD pulse → full-duplex SPI of the auto-sized frame (outputs written, inputs captured) → LATCH pulse. Clears the dirty flag.

void OpenSkyhawk::ShiftBus::transfer () 

When ISR sampling is active, loop-context callers are wrapped in a short interrupt-masked critical section so the frame buffers stay single-owner.


function writeBit

Set a '595 stage bit + mark the stage dirty. Published by the next loop-context transfer() /flushNow() (commit), never mid-group by the sampling ISR.

void OpenSkyhawk::ShiftBus::writeBit (
    uint8_t chip,
    uint8_t bit,
    bool v
) 

Writes land in a loop-owned stage frame; the ISR ships only the last committed frame. A multi-pin group (StepperMotor's four coils) therefore always reaches the '595 outputs as one atomic pattern, regardless of ISR timing between the writes.



The documentation for this class was generated from the following file Firmware/Libraries/PanelGroup/Helpers/ShiftBus/ShiftBus.h