I've never used interrupts before and have a project that looks like I need to use them. I'm still learning to use arduino.
I have a motor that has an optical sensor that outputs a squarewave.
This is the sequence that I need to write code for.
Move motor clockwise to endstop
Detect when the pulses have stopped.
Move motor anti clockwise to endstop
Count pulses from end to end
Move motor to mid position
Interrupts are most useful to detect events like rising or falling edges of signals and especially if the signals are not "bouncing" (which may put unnecessary load on the controller). So counting pulses via their edges is a valid task and not to complecated if you take care of the basic requirements of interrupt handling.
If you interrupt on edges you need to identify that no further pulses are detected outside the interrupt routine, probably easiest in loop().
For rotary encoders there are examples around, that could be adopted (just a quick google search, no quality assurance )
The big question for automatic clockwise/counterclockwise detection is whether your hardware gives a single or two signals with a phase shift between them (scroll down to " Working of Rotary encoder:"):
I don't know what your motor driver is so you will have to define those functions on your own.
const byte PulsePin = 2; // Pin conneccted to encoder
unsigned long PulseCount;
unsigned long Midpoint;
// Timeout value looking for an encoder pulse
const unsigned MaxPulseLength = 1000;
void Clockwise(); // Run the motor clockwise
void Anticlockwise(); // Run the motor anticlockwise
void Stop(); // Stop the motor
void CountPulsesUntilTheyStop()
{
int previousPulseState = digitalRead(PulsePin);
unsigned long lastPulseTime = millis();
while (1)
{
int pulseState = digitalRead(PulsePin);
if (pulseState != previousPulseState)
{
// State change
previousPulseState = pulseState;
PulseCount++;
lastPulseTime = millis();
}
if (millis() - lastPulseTime >= MaxPulseLength)
{
// No pulse state change for a while. Must have hit a stop
return;
}
}
}
void WaitUntilPulseCount(unsigned long count)
{
int previousPulseState = digitalRead(PulsePin);
while (1)
{
int pulseState = digitalRead(PulsePin);
if (pulseState != previousPulseState)
{
// State change
previousPulseState = pulseState;
PulseCount++;
if (PulseCount >= count)
return;
}
}
}
void MoveToCenter()
{
// Move motor clockwise
Clockwise();
// to endstop
// Detect when the pulses have stopped.
CountPulsesUntilTheyStop();
Stop();
// Move motor anti clockwise to endstop
// Count pulses from end to end
PulseCount = 0;
Anticlockwise();
CountPulsesUntilTheyStop();
Stop();
Midpoint = PulseCount / 2;
// Move motor to mid position
Clockwise();
PulseCount = 0;
WaitUntilPulseCount(Midpoint);
Stop();
}
Warning: The motor is likely to overshoot so this is not a good way to hit a particular target and will be off a little each time you move. If you need to keep track of an accurate position and reach specific position, change the encoder to a quadrature encoder (two channels that overlap) and use a PID control loop to slow the motor before it overshoots.
There is no need to detect rotation direction, if all he is trying to accomplish is what he described. Each phase of the cycle will always begin with the motor stopped. So, he has to turn it on in a given direction. He knows which direction he has turned on. From there, he needs to only count pulses until the motor stops turning again, then turn the motor off, and proceed to the next phase of the cycle, or end the cycle. Quadrature is not required, only pulse counting. He knows the motor is stopped when no new pulses are received within some reasonable period of time, like 10X the average period of the most recent 10 pulses.
How exactly do you know pulses stopped for sure? you have timeout after which you can say they stopped? what if the motor just slowed down? if you extend timeout then you introduce delay in response, so unless you have another sensor telling you the limit is reached you can’t be sure the motor is stopped or rotating slowly
Yes, exactly. How long you wait is determined by the performance/behavior of the hardware, which we know nothing about. In the absence of an explicit hardware limit switch, how else CAN you do it?
What would make it slow down, rather than stop? The description sounds like it will move until it hits an end stop.
Are you absolutely sure the motor and gearbox in that motor will tolerate being stalled? In many of those things, the motor will VERY quickly overheat and burn itself out, and/or the gearbox will break. In any case, with 1125:1 reduction, you can probably ignore any risk of the motor simply slowing down.
That is why I wrote "for automatic clockwise/counterclockwise detection"; if you have a priori knowledge, you can use that of course.
Yes, but - as I wrote - has to be detected outside an event driven interrupt (as no pulses == no events ) One can use loop() just count the loops, use millis() or microseconds() or create a timer routine reset by each incoming pulse ...
If you take the 10 pulses period as you mentioned as the limit for detection this would be the timing depending on the RPM of the motor when each single motor axis rotation is detected:
Depending on the RPM you can choose how long it may take to switch off.
Oops. I didn't notice that the desire was for the pulse counter to use interrupts. Here is the corrected code.
const byte PulsePin = 2; // Pin conneccted to encoder
unsigned long PulseCount;
unsigned long Midpoint;
unsigned long LastPulseTime = 0;
// Timeout value looking for an encoder pulse
const unsigned MaxPulseLength = 1000;
// Fill these in to control the motor
void Clockwise() {} // Run the motor clockwise
void Anticlockwise() {} // Run the motor anticlockwise
void Stop(){} // Stop the motor
void PulseISR()
{
PulseCount++;
LastPulseTime = millis();
}
void setup()
{
attachInterrupt(digitalPinToInterrupt(PulsePin), PulseISR, CHANGE);
MoveToCenter();
}
void CountPulsesUntilTheyStop()
{
noInterrupts();
LastPulseTime = millis();
interrupts();
while (1)
{
noInterrupts();
unsigned long LPT = LastPulseTime;
interrupts();
if (millis() - LPT >= MaxPulseLength)
{
// No pulse state change for a while. Must have hit a stop
return;
}
}
}
void WaitUntilPulseCount(unsigned long count)
{
while (1)
{
noInterrupts();
unsigned long PC = PulseCount;
interrupts();
if (PC >= count)
return;
}
}
void MoveToCenter()
{
// Move motor clockwise
Clockwise();
// to endstop
// Detect when the pulses have stopped.
CountPulsesUntilTheyStop();
Stop();
// Move motor anti clockwise to endstop
// Count pulses from end to end
PulseCount = 0;
Anticlockwise();
CountPulsesUntilTheyStop();
Stop();
Midpoint = PulseCount / 2;
// Move motor to mid position
Clockwise();
PulseCount = 0;
WaitUntilPulseCount(Midpoint);
Stop();
}
void loop() {}
Sure. But, again, we know almost NOTHING about the OPs actual hardware, nor his performance requirements. And, he may well get the performance he needs by simply waiting for a SINGLE pulse that exceeds perhaps 2X the "full-speed" pulse rate. And that is probably about as fast as it CAN realistically be detected. We simply do not KNOW what he needs, as he has not told us.
Johnwasser thank you for the 2 codes. I've looked at the one with interrupts and added the relevant code to control the motor. It moves the motor clockwise for a second then anti clockwise for a second so I'm thinking i have a problem with the pulses coming from the motor.
Will have a look with the scope and see whats going on.
Ive attached what the sensor is outputing with the motor at full speed.
johnwasser the first program without interrupt will not complie for my nano. The second program with interrupt does not count the pulses. Ive added Serial.print to show plusecount and midpoint and both show 0.
Here is the code as I have it
const byte PulsePin = 2; // Pin conneccted to encoder
const int in1 = 6;
const int in2 = 7;
unsigned long PulseCount;
unsigned long Midpoint;
unsigned long LastPulseTime = 0;
// Timeout value looking for an encoder pulse
const unsigned MaxPulseLength = 20;
// Fill these in to control the motor
void Clockwise() {
digitalWrite(in1, HIGH); digitalWrite(in2, LOW);
} // Run the motor clockwise
void Anticlockwise() {
digitalWrite(in1, LOW); digitalWrite(in2, HIGH);
} // Run the motor anticlockwise
void Stop() {
digitalWrite(in1, LOW); digitalWrite(in2, LOW);
} // Stop the motor
void PulseISR()
{
PulseCount++;
LastPulseTime = millis();
}
void setup()
{
Serial.begin (9600);
pinMode(in1, OUTPUT); pinMode(in2, OUTPUT);
attachInterrupt(digitalPinToInterrupt(PulsePin), PulseISR, CHANGE);
MoveToCenter();
}
void CountPulsesUntilTheyStop()
{
noInterrupts();
LastPulseTime = millis();
interrupts();
while (1)
{
noInterrupts();
unsigned long LPT = LastPulseTime;
interrupts();
if (millis() - LPT >= MaxPulseLength)
{
// No pulse state change for a while. Must have hit a stop
return;
}
}
}
void WaitUntilPulseCount(unsigned long count)
{
while (1)
{
noInterrupts();
unsigned long PC = PulseCount;
interrupts();
if (PC >= count)
return;
}
}
void MoveToCenter()
{
// Move motor clockwise
Clockwise();
// to endstop
// Detect when the pulses have stopped.
CountPulsesUntilTheyStop();
Stop();
// Move motor anti clockwise to endstop
// Count pulses from end to end
PulseCount = 0;
Anticlockwise();
CountPulsesUntilTheyStop();
Stop();
Midpoint = PulseCount / 2;
Serial.print(PulseCount);
Serial.print("\t");
Serial.println();
Serial.print(Midpoint);
Serial.print("\t");
// Move motor to mid position
Clockwise();
PulseCount = 0;
WaitUntilPulseCount(Midpoint);
Stop();
}
void loop() {}