Hi,
Last year I posted a thread asking for help on how to make a small copy of the BMW kinetic sculpture
Lots of excellent ideas and discussions.
Two fellows, Crossroad (Robert) and wawa (Leo) actually made some prototypes.
Leo, made boards with 48 chips (TPIC6B595) driving 28BYJ-48 steppers.
He then kindly sent me 2 of his boards and posted a sketch.
Unfortunately, I've never been able to make them work.
I've followed his instructions on wiring and used his sketch.
Can someone please tell me why I can't get the steppers to move ?
I'd hate to throw the boards away after all the work by wawa and his kindness for sending them to me.
I'm using the 5v version of 28BYj's, don't know if wawa used the same version.
Here's his code
/*
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
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
void setup() {
SPI.begin();
pinMode(clrPin, OUTPUT);
pinMode(latchPin, OUTPUT);
digitalWrite(clrPin, HIGH); // clear all shift register data
for (byte i = 0; i < motors; i++) nowPos[i] = 1024; // homing all motors 1/2 turn
}
void loop() {
switch (sequence) {
case 0: // break free from the top, all motors
if (nowPos[0] == newPos[0]) { // finished homing
for (byte i = 0; i < motors; i++) nowPos[i] = -128; // 1/16 turn
torque = true; // was low power during homing
sequence = 1; // case 0 is not called anymore
prevMillis = millis(); // so mark here
}
break;
case 1: // 4.5 turns down, all motors
if (millis() - prevMillis > 8000) {
for (byte i = 0; i < motors; i++) newPos[i] = 9216; // 4.5 * 2048
sequence = 2;
}
break;
case 2: // all return to zero, one after the other
if (millis() - prevMillis > 30000 + interval) {
if (index < motors) {
newPos[index] = 0;
interval += 500;
index += 1;
} else { // when done
index = 0;
interval = 0;
sequence = 3;
}
}
break;
case 3: // run sequence(s) again
if (millis() - prevMillis > 75000) {
prevMillis = millis();
sequence = 1;
}
break;
}
}
Yes, a big part seems to be missing.
This is the full code.
Working, nothing wrong with is.
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
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
void setup() {
SPI.begin();
pinMode(clrPin, OUTPUT);
pinMode(latchPin, OUTPUT);
digitalWrite(clrPin, HIGH); // clear all shift register data
for (byte i = 0; i < motors; i++) nowPos[i] = 1024; // homing all motors 1/2 turn
}
void loop() {
switch (sequence) {
case 0: // break free from the top, all motors
if (nowPos[0] == newPos[0]) { // finished homing
for (byte i = 0; i < motors; i++) nowPos[i] = -128; // 1/16 turn
torque = true; // was low power during homing
sequence = 1; // case 0 is not called anymore
prevMillis = millis(); // so mark here
}
break;
case 1: // 4.5 turns down, all motors
if (millis() - prevMillis > 8000) {
for (byte i = 0; i < motors; i++) newPos[i] = 9216; // 4.5 * 2048
sequence = 2;
}
break;
case 2: // all return to zero, one after the other
if (millis() - prevMillis > 30000 + interval) {
if (index < motors) {
newPos[index] = 0;
interval += 500;
index += 1;
} else { // when done
index = 0;
interval = 0;
sequence = 3;
}
}
break;
case 3: // run sequence(s) again
if (millis() - prevMillis > 75000) {
prevMillis = millis();
sequence = 1;
}
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
}
@DrDiettrich Thanks. I had copied the code blindly.
Thank you Wawa, I can now stop banging my head against the wall.
Still one thing is not clear to me, the steppers are fed with 12v even if they are the 5v version ? Or I'd fry the chips ?
5volt stepper motors and a 12volt supply will of course fry the chips, and eventually the motors.
For 5volt stepper motors you must use a 5volt supply (ignore the 12volt marking on the board).
It is wise to use a separate 5volt motor supply, and a separate supply for the Uno (or Nano).
A 5volt cellphone supply with USB socket is perfect for the Uno. Just plug the USB lead into it.
Leo..
Can someone please help me modify/expand Wawa's code ?
for example how would I move all steppers together instead of one by one?
I've tried with no success.
Thanks Leo, I understand switch statements.
To learn how shift registers work, I read articles and followed the tutorials I could find.
All the tutorials on the net regarding shift registers refer to LED's.
I followed one of these, hooked up some LED's and learned the principles.
Then I tried to modify your code for another project I'm doing ( I've postponed the BMW one cause I don't think I'm up to it for now).
In this project, much simpler, I need to move 41 steppers all together at slow speed to an array of prefixed positions and stay there until a PIR sensor is triggered, then move to the next prefixed position and so on.
I tried to simplify your code, taking out sequences, millis, micros, speed and torque keeping the essential part of your goPos function but somehow I'm goofing up somewhere.
I just need to read the current position from the EEPROM on power up, calculate the steps I need to go to one of the prefixed positions and move all the steppers there. Then update the EEPROM with the new position.
Don't understand why you want to do this.
That goPos() block takes care of all motor movements/speed/braking/overshoot, with minimal load on the MCU. Taking things out will surely result in missing steps.
Can't you just write the nowPos[] array to EEPROM, and load it back in setup().
Leo..
Because your code does more than I need and does it differently. For example "homing" and "breaking away" and the part where all motors go to "home position" one by one.
Maybe I wasn't clear enough describing my project.
I have a prefixed array (example) TargetPos[100, 165, 220, 350 ....., 490].
At boot up, in the void setup, I read the last known position (one integer) of all steppers (they all move together and to the same position) from the EEPROM.
In the void loop I keep checking if a PIR sensor is triggered. if yes, then I'd call my GoToPos function. This function generates a random number (n), calculates the number of half steps need to reach TargetPos[n], moves all steppers to that position and stays there. It then writes the nowPos to EEPROM and maybe cut off current to the steppers. Back to void loop checking the the PIR sensor.
I've got everything figured out except the part where I send Hex code to all shift registers to activate the coils of the steppers for "x" steps or half steps to go to TargetPos[n].
Leo,
I already tried that approach, it won't work. The motors do a half turn then just sit there.
I realize that due to my lack of knowledge I can't do this by myself and also I hate to keep bothering you. I'd like to post in the "paid job" section of the forum but I read you have to ask a moderator to move the thread to "gigs" ?!?
How does it work ?
Post your code here.
There are way more clever people than me on this forum that could help you.
And your posts might inspire other people to build something similar.
Leo..