Hi,
In a project I have 48 steppers (24BYJ-48) with 3D printed gears attached to them. I'll insert a magnet on the gears, use digital Hall sensor modules for homing the steppers.
What should I use as port expander ? Waveshare MCP23017 I2C or MCP23008 I2C or PCF8575 or....... ?
Time ago, I read somewhere, that some of these chips give you interrupts ?!?
Port expanders are for signals, they can't drive motors. So you also need motor drivers if you are using port expanders, which makes things more complex than just using the TPIC chips you use now. The driver might influence the expander choice.
The MCP23S17 (note the "S") is SPI, and can be much faster.
Leo..
Sorry, don't understand you.
Yes, I'm using the TPIC's to drive the motors. Want to add the expanders just for a signal to home steppers.
Which part number?
Are you using a 5V or 3.3V arduino?
What is the distance between your motors and the arduino?
Any of those will be suitable for reading your hall sensors. Also pcf8574, sx1509 and probably others. They all have interrupt function, I think.
Are you using the TPIC chips with hardware SPI interface? If so, another option could be 74hc165 shift registers. These can share the CLK and CS/latch pins with the TPIC chips and connect to the MISO pin, so only one additional pin is required. The hall sensors can then be read simultaneously with sending data to the TPIC chips.
The hardware SPI interface is bi-directional. MOSI pin sends data out while MISO pin receives data in. So while data is being shifted out to the TPIC chips, incoming data from the sensors can be shifted in simultaneously from a chip like 74hc165.
(Note: we are no longer supposed to use the terms MOSI and MISO because some people could be offended, apparently. But I can't remember the new terms right now.)
It sounds wasteful to throw away your PCBs. You can make another board for the 74hc165(s) and hall sensors and connect both boards in parallel to the Arduino. To begin, build a prototype on breadboard. Once you have the circuit working, you can build a permanent circuit on proto board of some kind, or design a PCB if you need to build several of them.
If you need more detailed advice, you need to give more detail of your existing design.
Thank you Paul. I'll order the chip, test and report back.
In the mean time Happy Holidays to all.
Don't forget to put a 0.1uF bypass cap across the Vcc and Ground pins of each chip (the 74hc165 and the TPICs).
You will need some pull-up resistors on the input pins of the '165 and connect your hall sensors to pull the pins down.
Ahh, then go for SPI port expanders. You don't want the slower I2C bus to hold up the signals to the TPIC drivers. The expanders can use the same SPI bus as the TPIC chips.
Leo..
I read your deleted post.
For homing, I would turn all motors one full turn, all at the same time. Remember that all motors only take a single step at the same time, with some waiting time after one step. Read the port expander after one step, in that waiting time, and see which hall sensor(s) detects pos#0 (no interrupts needed). Then make nowPos[] of the motor(s) that are there newPos[].
if (hallSensor[x] == HIGH) newPos[x] = nowPos[x]; // stop the motors that are there
Leo..
Thanks Leo. I deleted it cause I thought it was wrong.
Waiting for the port expanders I'm testing with a couple of Hall sensors I had. I noticed that the signal from them goes LOW when triggered so that code should be changed to LOW.
Also, I was testing with a Nano Every. A simple code to turn on the onboard LED when Hall sensor triggered worked fine but when testing with your code written for Opto Coupler and Interrupt ( modified for Hall sensor, no Opto) I kept getting false trigger. !?! Thought interrupt pins were different on EVERY but pin 2 seems correct.
Will change setup using an Uno, test and report back.
I don't see why you want to use interrupts. With 48 motors you have at least a millisecond between steps. That should give you enough time to read 48 hall sensors and act on it.
Leo..
Pls bear with my confusion as always.
The reason why I thought of using interrupts is because I want to stop motor[n] in position zero as soon as I get the signal, not just record it's nowPos.
I have never used an MCP23S17, so I don't fully understand it's interrupt capabilities, but to me it makes more sense to just check all hall sensors after one step of all motors.
Leo..
ok. Testing one motor with one sensor and the following code, the motor stops on signal. Now I'll need to expand it to all the motors, create sequences, so when all homed, I can do other stuff.
void loop() {
if(digitalRead(hallPin) == HIGH){
newPos[index_motor] = 2048;
goPos();}
}
What if I just use one pin for homing all motors and save myself a lot of wiring and port expanders ?
Start by stepper 0, turn a full turn, stop when hit magnet, back up a bit so no more signal, go to the next motor, do the same .. ... until all motors are homed.
Something along the following code which compiles but won't work
/*
Nano | TPIC6B595
5V | 2 (VCC)
GND | GND
D8 | 8 (SRCLR) - blue
D10 | 12 (RCK) - green
D11 | 3 (SER IN) of the first chip - yellow
D13 | 13 (SRCK) - orange
| 9 to GND
| 10, 11,19 to GND
| 18 (SER OUT) to 3 of the next chip
*/
#include <SPI.h>
const byte motors = 8; // 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 =true; // reduced during homing
byte index; // patterns, motor index
byte hallPin = 2; // interrupt pin
int hallVal;
//*******************************************************************
int index_motor= 6;
//*******************************************************************
const byte Ck_Sensor = 0;
const byte Away_From_Magnet = 1;
const byte Next_Motor = 2;
const byte Wait = 3;
const byte Idle = 4;
byte sequence = 0;
void setup() {
//Serial.begin(9600);
SPI.begin();
pinMode(clrPin, OUTPUT);
pinMode(latchPin, OUTPUT);
pinMode (hallPin, INPUT_PULLUP);
digitalWrite(clrPin, HIGH); // clear all shift register data
for (byte i = 0; i < motors; i++) nowPos[i] = 0; //
for (byte i = 0; i < motors; i++) newPos[i] = 0;
}
void loop() {
switch (sequence) {
case Ck_Sensor:
if(digitalRead(hallPin) == HIGH){
newPos[index_motor] = 2048;}
else if(digitalRead(hallPin) == LOW){
sequence = Away_From_Magnet;
}
break;
case Away_From_Magnet:
newPos[index_motor] = -200;
sequence = Next_Motor;
break;
case Next_Motor:
index_motor++;
if (index_motor < motors) {
sequence = Ck_Sensor;}
else {
sequence = Idle;}
break;
case Idle:
sequence = 111;
break;
}
}
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
}
That's the default way with input port expanders. Many hall sensors to one Arduino pin.
Not using port expanders could only work if all hall sensors would be wired in parallel (OR gate) and none of them would initially be triggered.
I think six 74HC165 would be the easiest way.
Leo..
Not exactly. They give you an output pin that is labeled interrupt. It outputs a zero when any of the pins specified in the interrupt register changes, and a one when a processor reads any of the registers.
Its normal use is for using it as a polling device. That is read the interrupt pin until you find it is a zero and then you know when to look into the chip's registers. This is useful when you are say trying to monitor lots of signals that only occasionally change. It saves a lot of time.
Note the interrupt output pins are "wired OR" pins and so require a pull up resistor.
Wiring them in parallel is no problem.
Right, I didn't think about that.(dumb me).
Waiting for delivery of MCP23S17 modules.