Following Giulio's advice here is a version of the shift-out code that fills the shift registers continually, on every digital frame. This method breaks the dependence on the block size and allows you to daisy chain multiple 74hc595s using only three digital pins from the Bela.
You just need to define the number of chips used and the data, latch and clock pins at the top of render.cpp
. The data that is written to the shift registers can be stored in an array called data[]
. In the example below I am also cycling through the LEDs one by one.
There are two methods in render, one that writes to the register at the first frame of the next available block (currently commented out) and another that writes at the next available digital frame.
The plan is to add this a default example on the board, any feedback would be appreciated. I will also add this as an example for Pure Data using the custom render.
The wiring used is the same as the one on this page for Arduino: http://bildr.org/?s=74hc595
/***** render.cpp *****/
#include <Bela.h>
#include <ShiftOut.h>
// Number of chips used
#define TotalIC 3
#define number_of_bits TotalIC * 8
// Array for storing the values that are written to the register
bool data[number_of_bits] = {1,0,1,0,1,0,1,0};
//Pin DS of 74HC595
int mData_Pin = 3;
//Pin ST_CP of 74HC595
int mLatch_Pin = 4;
//Pin SH_CP of 74HC595
int mClock_Pin = 5;
// For cycling through the LEDs
int gCounter = 0;
int interval = 2000;
int litLED = 0;
ShiftRegister shiftRegister;
bool setup(BelaContext *context, void *userData)
{
shiftRegister.setup(context, mData_Pin, mClock_Pin, mLatch_Pin, number_of_bits);
return true;
}
void render(BelaContext *context, void *userData)
{
// Push reading to register at the first frame of the next available block
// initialise in setup with the mLatch/mData/mClock pins
// if(shiftRegister.dataSent())
// shiftRegister.sendData(data, number_of_bits, 0);
// shiftRegister.render(context, number_of_bits);
// Push reading to register at the first frame possible
for(unsigned int n = 0; n < context->digitalFrames; ++n)
{
shiftRegister.render(context, n, number_of_bits);
if(shiftRegister.dataSent())
shiftRegister.sendData(data, number_of_bits, n);
}
// ========CYCLE THROUGH LEDS=========
if(gCounter > interval){
for (int j=0;j<number_of_bits;j++){
// Clear all LEDs
data[j] = 0;
}
data[litLED] = 1;
litLED += 1;
if(litLED >= number_of_bits){
litLED = 0;
}
gCounter = 0;
rt_printf("Interval passed, litLED %d\n", litLED);
}
gCounter += context->audioFrames;
// ===================================
}
void cleanup(BelaContext *context, void *userData)
{
}
/***** ShiftOut.h *****/
#include <Bela.h>
#include <vector>
class ShiftRegister
{
private:
unsigned int mData_Pin;
unsigned int mClock_Pin;
unsigned int mLatch_Pin;
enum {
kStart,
kTransmitting,
kStop,
kIdle,
};
int gState = kStop;
int gCurrentDataFrame = 0;
std::vector<bool> data;
public:
ShiftRegister();
ShiftRegister(BelaContext* context, unsigned int dataPin, unsigned int clockPin, unsigned int latchPin, unsigned int maxSize);
bool setup(BelaContext* context, unsigned int dataPin, unsigned int clockPin, unsigned int latchPin, unsigned int maxSize);
bool dataSent();
void render(BelaContext* context, int number_of_bits);
void render(BelaContext* context, unsigned int n, int number_of_bits);
void sendData(bool* dataBuffer, unsigned int length, unsigned int startingFrame);
};
/***** ShiftOut.cpp *****/
#include <ShiftOut.h>
ShiftRegister::ShiftRegister(){};
ShiftRegister::ShiftRegister(BelaContext* context, unsigned int dataPin, unsigned int clockPin, unsigned int latchPin, unsigned int maxSize){
setup (context, dataPin, clockPin, latchPin, maxSize);
};
bool ShiftRegister::setup(BelaContext* context, unsigned int dataPin, unsigned int clockPin, unsigned int latchPin, unsigned int maxSize)
{
mData_Pin = dataPin;
mClock_Pin = clockPin;
mLatch_Pin = latchPin;
data.resize(maxSize);
pinMode(context, 0, mData_Pin,OUTPUT);
pinMode(context, 0, mClock_Pin,OUTPUT);
pinMode(context, 0, mLatch_Pin,OUTPUT);
return true;
}
bool ShiftRegister::dataSent()
{
return gState == kIdle;
}
void ShiftRegister::render(BelaContext* context, int number_of_bits)
{
for(unsigned int n = 0; n < context->digitalFrames; ++n)
{
render(context, n, number_of_bits);
}
}
void ShiftRegister::render(BelaContext* context, unsigned int n, int number_of_bits)
{
bool latchValue = 0;
bool dataValue = 0;
bool clockValue = 0;
if(gState == kStart)
{
latchValue = 1;
dataValue = 0;
clockValue = 0;
gState = kTransmitting;
gCurrentDataFrame = 0;
} else if(gState == kTransmitting)
{
latchValue = 1;
dataValue = data[gCurrentDataFrame / 2];
if((gCurrentDataFrame % 2) == 0)
{
clockValue = 0;
} else {
clockValue = 1;
}
gCurrentDataFrame++;
if(gCurrentDataFrame == 2 * number_of_bits)
{
gState = kStop;
}
} else if(gState == kStop || gState == kIdle)
{
latchValue = 0;
dataValue = 0;
clockValue = 0;
gState = kIdle;
}
digitalWriteOnce(context, n, mLatch_Pin, latchValue);
digitalWriteOnce(context, n, mData_Pin, dataValue);
digitalWriteOnce(context, n, mClock_Pin, clockValue);
}
void ShiftRegister::sendData(bool* dataBuffer, unsigned int length, unsigned int startingFrame)
{
// should we check for length and expand `data` if needed? That wouldn't be RT-safe
for(unsigned int n = 0; n < std::min(length, data.size()); ++n)
{
data[n] = dataBuffer[n];
}
gState = kStart;
}