I programmed two cases here.
One that moves all motors to a different random position,
and one that moves them to a common random position.
Compiles, but not hardware tested.
Read all the comments.
Leo..
/*
Nano | TPIC6B595
5V | 2 (VCC)
GND | GND
D8 | 8 (SRCLR)
D10 | 12 (RCK)
D11 | 3 (SER IN) of the first chip
D13 | 13 (SRCK)
| 9 to GND
| 10, 11,19 to GND
| 18 (SER OUT) to 3 of the next chip
*/
#include <SPI.h>
const byte motors = 48; // sets of four motors
// must be sets of four motors for SPI.transfer16, so 44 for 41 motors !
// but why not keep it simply the board number of 48
const byte clrPin = 8; // TPIC6B595 SRCLR pin8
const byte latchPin = 10; // TPIC6B595 RCK pin12
const byte rampSteps = 7; // accel/decel steps
byte rampDelay[motors]; // accel/decel delay between steps
byte lag[motors]; // speed reduction
int velocity[motors]; // signed, for direction
int nowPos[motors], newPos[motors]; // int is 16 rotations max (2.5m string with a 50mm dia wheel)
unsigned long prevMillis, prevMicros, interval; // timing
byte val[4]; // TPIC write bytes
bool torque; // reduced during homing
byte sequence, index; // patterns, motor index
int randNumber;
void setup() {
SPI.begin();
pinMode(clrPin, OUTPUT);
pinMode(latchPin, OUTPUT);
digitalWrite(clrPin, HIGH); // clear all shift register data
randomSeed(analogRead(0));
// load 41 (or 48) eeprom nowPos[] integers (= two bytes) here
}
void loop() {
switch (sequence) {
case 0: // load random positions for each motor into the new position array
for (byte i = 0; i < motors; i++) {
newPos[i] = random(-500, 500); // about a quarter turn max, right or left from start position
}
// eepromSave(); // could call eeprom write function here
sequence = 1; // go to next case
prevMillis = millis(); // but first mark the time
break;
case 1: // move all motors the same random direction/steps
if (millis() - prevMillis > 8000) { // after 8 seconds
randNumber = random(-1024, 1024); // max half a turn
for (byte i = 0; i < motors; i++) {
newPos[i] = randNumber; // all motors the same (random) new position
}
// eepromSave(); // could call eeprom write function here
sequence = 2; // next case
prevMillis = millis();
}
break;
case 2: // run sequence(s) again
if (millis() - prevMillis > 20000) { // after 20 sec
sequence = 1;
prevMillis = millis(); // mark
}
break;
}
goPos(); // let's step, one step each loop
}
void goPos() {
while (micros() - prevMicros < 1953); // step interval, motor RPM limitation, 1/(15rpm/60sec*2048steps)= ~1953ms, wait if arrived here too early
prevMicros = micros(); // update, for next interval
for (int i = 0; i < motors; i++) {
if (rampDelay[i]) rampDelay[i]--; // subtract one, do nothing
else { // step
if (newPos[i] > nowPos[i]) { // request forwards
if (velocity[i] >= 0) { // if standing still, or going forwards
nowPos[i]++; // one step forward
if (newPos[i] - nowPos[i] < rampSteps) velocity[i]--; // reduce speed if almost there
else if (velocity[i] < min(rampSteps, rampSteps - lag[i])) velocity[i]++; // increase speed if not there yet
rampDelay[i] = max(lag[i], rampSteps - velocity[i]); // insert ramp delay
} else { // if wrong direction
velocity[i]++; // reduce reverse speed
if (velocity[i] < 0) nowPos[i]--; // if still reverse, keep on going the wrong way
rampDelay[i] = rampSteps - abs(velocity[i]); // insert ramp delay
}
}
if (newPos[i] < nowPos[i]) { // request reverse
if (velocity[i] <= 0) { // if standing still, or going reverse
nowPos[i]--; // one step reverse
if (nowPos[i] - newPos[i] < rampSteps) velocity[i]++; // reduce speed if almost there
else if (abs(velocity[i]) < min(rampSteps, rampSteps - lag[i])) velocity[i]--; // increase speed if not there yet
rampDelay[i] = max(lag[i], rampSteps + velocity[i]); // insert ramp delay
} else { // wrong direction
velocity[i]--; // reduce forward speed
if (velocity[i] > 0) nowPos[i]++; // if still reverse, keep on going the wrong way
rampDelay[i] = rampSteps - velocity[i]; // insert ramp delay
}
}
}
if (newPos[i] == nowPos[i])val[i & 3] = 0; // cut coil power when there
else {
if (torque) { // this block for full torque
switch (nowPos[i] & 3) { // the two LSB translated to motor coils (full step)
case 0: val[i & 3] = B00000011; break; // two coils at the time
case 1: val[i & 3] = B00000110; break;
case 2: val[i & 3] = B00001100; break;
case 3: val[i & 3] = B00001001; break;
}
} else { // this block for low power
switch (nowPos[i] & 3) {
case 0: val[i & 3] = B00000001; break; // one coil at the time
case 1: val[i & 3] = B00000010; break;
case 2: val[i & 3] = B00000100; break;
case 3: val[i & 3] = B00001000; break;
}
}
}
if ((i & 3) == 3) { // process set of four motors (faster)
unsigned int chain = val[0] << 12 | val[1] << 8 | val[2] << 4 | val[3]; // concat
SPI.transfer16(chain); // transfer 4-motor int
}
}
digitalWrite(latchPin, HIGH); // transfer to TPIC driver outputs
digitalWrite(latchPin, LOW); // latch time ~8us
}