HI,
The attached sketch files run light patterns on strings of leds (called "footlights" because they're 12 inches long) driven by shift registers. Most of these routines use SPI, but there is one that toggles the pins manually and needs SPI temporarily disabled. This works fine on an Uno and has worked fine on a Teensy 3.2 and a T3.6 with Teensyduino 1.36. However, with 1.37, and now with 1.4, the non-SPI routine fails.
I'm hoping this is just some new detail of usage that I don't know about.
The attached code is a simplified version of my sketch including just 2 routines, an SPI and a non-SPI routine. Description of the attached files:
- demo_simple1.ino -- the main sketch file; in this implementation it alternates between running the 2 routines
- ChainGang_1color_lite.h -- a class for easily addressing the outputs on chained shift registers, usage similar to digitalWrite: object_name.setChain(output_number, state)
- chasers.ino -- the SPI-using routine that runs fine on all versions
- cascade.ino -- the non-SPI routine that fails with newer T-duino versions
demo_simple1.ino
Code:
/* ***********************************************************************************************************
* Footlights/simple_demo.ino
* Output to chained 74HC595s driving "footlights" of 120 leds each.
* M Ward 12/04/2014, latest edit 8/31/17
*************************************************************************************************************/
#define QUANT_FEET 2 // Number of 120-led, 15-shift register footlights
#define QUANT_REG QUANT_FEET * 15 // Number of chained 8-bit shift registers
#define MAX_CHASERS QUANT_FEET * 4 // Arbitrary maximum number, enough for all routines
#define LATCH 9 // Shift register control
#define DATA 11 // DATA and CLOCK defined for non-SPI routine(s)
#define CLOCK 13
#define BAIL_BTN 5
#define MIN_RUN 15000 // Routine run time randomization limits
#define MAX_RUN 30000
//#include "digitalWriteFast.h" // Uncomment for AVR
#include <SPI.h>
#include <Bounce.h>
#include "ChainGang_1color_lite.h"
Bounce bail(BAIL_BTN, 10);
ChainGang chaser[MAX_CHASERS];
uint32_t runStart;
uint32_t runTime;
bool routineSet = false;
bool reStart = false;
//=========================================== setup =========================================================+
void setup() {
randomSeed(analogRead(A0));
pinMode(DATA, OUTPUT);
pinMode(CLOCK, OUTPUT);
pinMode(LATCH, OUTPUT); // Use pinModeFast for AVR
pinMode(BAIL_BTN, INPUT_PULLUP);
SPI.begin();
SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0));
Serial.begin(1000000);
Serial.println(F("Sketch: Footlights/demo_simple\n"));
}
//=========================================== loop ==========================================================+
void loop() {
const uint8_t quantRoutines = 2;
static uint8_t routine;
buttonCheck();
if (!routineSet) routine = chooseRoutine(quantRoutines);
switch(routine) {
case 1:
cascade();
break;
case 0:
chasers(250, 15000); // (min_stepTime, max_stepTime) micros
break;
}
}
//------------------------------------------- chooseRoutine -------------------------------------------------+
uint8_t chooseRoutine(uint8_t quantRoutines) {
static uint8_t lastRoutine = random(quantRoutines);
uint8_t choice;
choice = random(quantRoutines);
if (choice == lastRoutine) chooseRoutine(quantRoutines);
else {
lastRoutine = choice;
return choice;
}
}
//------------------------------------------- buttonCheck ---------------------------------------------------+
void buttonCheck() {
bail.update();
if (bail.fallingEdge()) { // Check whether to abort routine
reStart = true;
Serial.println(F(">>>>>>>>> RESTARTED <<<<<<<<<\n"));
}
}
ChainGang_1color_lite.h
Code:
/* ***********************************************************************************************************
* The ChainGang class is for easily controlling the outputs in an arbitrary number of chained shift
* registers, eg, 74HC595s. Usage similar to digitalWrite:
* - object_name.setChain(output_number, state)
* - Be sure to define QUANT_REG before including ChainGang. It assumes 8-bit devices or equivalent--
* eg, define QUANT_REG = 4 for a 32-bit '6818
* M Ward 12/04/2014, latest edit 9/12/17
*************************************************************************************************************/
#ifndef CHAINGANG_1COLOR_H
#define CHAINGANG_1COLOR_H
const uint16_t QUANT_OUT = QUANT_REG * 8;
const uint16_t LAST_OUT = (QUANT_REG * 8) - 1; // Last output in chain, zero-relative
//------------------------------------------- ChainGang lite ------------------------------------------------+
class ChainGang {
private:
static uint8_t link[QUANT_REG]; // Array of byte-sized elements for output to byte-sized shift registers
public:
uint16_t location; // Outputs by number--0 through LAST_OUT
uint16_t outFirst; // outFirst/Last can be used to divide chains into zones
uint16_t outLast;
uint8_t dir; // Right/Left
uint32_t stepTime;
uint32_t stepStart;
ChainGang() {
outFirst = 0;
outLast = LAST_OUT;
}
//------------------- xferLinks -----------------------+
static void xferLinks() { // Transfer array contents to shift registers
digitalWriteFast(LATCH, LOW);
for (int8_t n = QUANT_REG - 1; n >= 0; n--) SPI.transfer(link[n]);
// SPI.transfer(link, QUANT_REG);
digitalWriteFast(LATCH, HIGH);
}
//------------------- setChain ------------------------+
static void setChain(uint16_t outputNum, uint8_t state) {
uint8_t linkNumber = outputNum >> 3; // Calculate which link holds output--ie, / 8 or sizeof(link[0])
uint8_t linkPosition = outputNum - (linkNumber << 3); // Position of output within link
bitWrite(link[linkNumber], linkPosition, state); // Write to link
ChainGang::xferLinks();
}
//------------------- clearChain ----------------------+
static void clearChain() {
for (uint8_t n = 0; n < QUANT_REG; n++) link[n] = 0;
ChainGang::xferLinks();
}
};
uint8_t ChainGang::link[QUANT_REG];
#endif
chasers.ino
Code:
//------------------------------------------- chasers -------------------------------------------------------+
void chasers(uint32_t intervalMin, uint32_t intervalMax) {
static uint8_t quantChasers = random(2, MAX_CHASERS);
if (!routineSet) {
runTime = random(MIN_RUN, MAX_RUN);
runStart = millis();
quantChasers = random(2, MAX_CHASERS);
if (quantChasers < 3 || quantChasers > 8) quantChasers = random(2, MAX_CHASERS); // Weight number toward midrange
uint8_t startEnd = random(2); // Decide which side to start from
for (uint8_t n = 0; n < quantChasers; n++) {
!startEnd ? chaser[n].location = chaser[n].outFirst : chaser[n].location = chaser[n].outLast;
chaser[n].stepTime = random(intervalMin, intervalMax); // Randomize each chaser's interval
}
routineSet = true;
Serial.println(F("Routine: POLYRHYTHM"));
Serial.print(F(" runTime = "));
Serial.print(runTime / 1000);
Serial.println(F(" seconds"));
for (uint8_t n = 0; n < quantChasers; n++) {
Serial.print(F(" chaser["));
Serial.print(n);
Serial.print(F("]: "));
Serial.print(F("stepTime (us) = "));
Serial.println(chaser[n].stepTime);
}
Serial.println();
}
//------------------- core routine ------------------+
if (millis() - runStart < runTime) {
for (uint8_t n = 0; n < quantChasers; n++) { // For every chaser...
if (micros() - chaser[n].stepStart >= chaser[n].stepTime) { // If it's stepTime...
chaser[n].stepStart = micros();
chaser[n].setChain(chaser[n].location, LOW); // Current position off
if (chaser[n].location == chaser[n].outLast) chaser[n].dir = 1; // Check whether to change direction
else if (chaser[n].location == chaser[n].outFirst) chaser[n].dir = 0;
chaser[n].dir == 0 ? chaser[n].location++ : chaser[n].location--; // Increment/decrement position
chaser[n].setChain(chaser[n].location, HIGH); // New position on
}
}
}
else routineSet = false;
if (reStart) { // Abort routine?
routineSet = false;
reStart = false;
ChainGang::clearChain();
return;
}
}
cascade.ino
Code:
//------------------------------------------- cascade -------------------------------------------------------+
void cascade() {
static bool reWind;
static uint32_t stepTime;
if (!routineSet) {
stepTime = random(3, 11);
!random(2) ? reWind = false : reWind = true;
runStart = millis();
runTime = random(1250, 2600);
Serial.println(F("Routine: CASCADE"));
Serial.print(F(" stepTime (ms) = "));
Serial.println(stepTime);
Serial.print(F(" reWind = "));
reWind ? Serial.println(F("true")) : Serial.println(F("false"));
Serial.println();
SPI.end(); // Release SPI pin control
routineSet = true;
}
if (millis() - runStart < runTime) {
for (int16_t location = LAST_OUT; location >= 0; location--) bitbang(location, stepTime);
if (reWind) for (int16_t location = 0; location <= LAST_OUT; location++) bitbang(location, stepTime);
}
else {
SPI.begin(); // Restore SPI
routineSet = false;
reStart = false; // Note: this routine blocks; reStart will not interrupt it
ChainGang::clearChain();
}
}
//------------------- bitbang -----------------------+
void bitbang(int16_t location, uint32_t stepTime) {
digitalWriteFast(LATCH, LOW);
digitalWrite(DATA, HIGH);
for (int n = 0; n < location; n++) {
digitalWrite(CLOCK, HIGH);
digitalWrite(DATA, LOW);
digitalWrite(CLOCK, LOW);
}
digitalWriteFast(LATCH, HIGH);
delay(stepTime);
}
Thanks for any help,
Michael