When you call digitalWriteOnce()
, data is not written immediately, but rather it is scheduled for a write in the future, after the render()
function has finished; similarly, when you call digitalRead()
, what you get is a value that has been read in the past, before the render()
function was called. That's how it works when you have block-based processing; you can read more here.
For this reason, if you were to place a wire between channel 0 and channel 1 and you were to do something like this:
int writeValue = 1;
digitalWriteOnce(context, n, 0, writeValue);
int readValue = digitalRead(context, n, 1);
there would be no guarantee that readValue
and writeValue
would be the same: there is a 2-block + 1 sample time interval between when you write a value and you can read the same value back. The "+ 1 sample" part is down to the fact that for every given frame, we first read the digital inputs and a few nanoseconds later write the digital outputs.
This means that if you drive the clocks yourself, you should add a 2*context->digitalFrames + 1
delay between when you write the clock and when you read data back. The ShiftRegisterIn
class does not drive the clocks itself, rather it reads them:
void ShiftRegisterIn::process(BelaContext* context, unsigned int n)
{
...
bool latchValue = digitalRead(context, n, pins.latch);
bool dataValue = digitalRead(context, n, pins.data);
bool clockValue = digitalRead(context, n, pins.clock);
...
In the example above, the latch and clock signals are generated by the ShiftRegisterOut
instance and those are then looped back with a wire to the pins used by the ShiftRegisterIn
instance. If you have enough pins available, the easiest solution would be to keep the above example as is and simply ignore the dataOutPin, call shiftOut.setData()
with dummy data of the desired size and you'll have that shiftOut
will act uniquely as a clock generator. If you are short on pins, you'll have to implement that delay I mentioned above.