Interrupt on one specific channel
Hello again. I am trying to implement interrupt on IND I/0 1286 top board. I am using all 8 digital channels for input & output. I just need to attach interrupt to monitor one single channel (lets say Indio CH3). However, I just read this section in user manual:
"The interrupt pin of the expander on the 12/24V digital side is connected to the INT7 pin of the 1286 topboard. This pin will trigger when a change on any of the 8 input or output channels occurs"
So if I do: attachInterrupt(7, count, CHANGE);
This will trigger the interrupt on all of the Indio's digital channel declared as INPUT right? I only want the code in ISR to be executed whenever CH3 is rising (from LOW to HIGH). How do I do that? I don't want other CH input trigger the ISR too as it will screw the logic in my code. Please advise.
Stefano: Here is my code.
void loop()
{
ReadFelt();
RunMotor();
timer.run();
//Update Watchdog Timer
wdt_reset();
}
void RunMotor() {
if (f_Motor_Active == true) {
sendPulse();
count += 1;
if (f_Cont_Run == false) {
//Run one step only
if (count >= stepPulse) {
//stop motor
StopMotor();
}
}
//Check if motor has hit home or end setpoint sensor
//*** This part slows down motor movement ***
if (forward == true) {
if (EndPinOn() == true) {
//motor is moving forward and has hit end setpoint
StopMotor();
}
}
else if (forward == false) {
if (HomePinOn() == true) {
//motor is moving backward and has hit home setpoint
StopMotor();
}
}
//*** This part slows down motor movement ***
}
else {
//Turn on brake after making sure that f_Motor_Active = false
if (brake_executed == false) {
delay(300);
BrakeOn();
brake_executed = true;
}
}
}
void StartMotor() {
if (f_Motor_Active == false) {
//Start motor
//Read Direction
ReadDirection();
//Release brake first
BrakeOff();
delay(300);
f_Motor_Active = true;
}
}
void StopMotor() {
if (f_Motor_Active == true) {
//stop motor
f_Motor_Active = false;
//reset count
count = 0;
//Flag to turn on brake after next iteration (make sure motor off first)
brake_executed = false;
}
}
bool HomePinOn() {
// check if head is at home position. Sensor HIGH = true.
return Indio.digitalRead(pin_Home);
}
bool EndPinOn() {
// check if head is at end position. Sensor HIGH = true.
return Indio.digitalRead(pin_End);
}
void PWMLow() {
digitalWrite(pin_PWM, LOW);
}
void PWMHigh() {
digitalWrite(pin_PWM, HIGH);
}
void CWDir() {
//Clockwise direction
digitalWrite(pin_Dir, HIGH);
}
void CCWDir() {
//Counter clockwise direction
digitalWrite(pin_Dir, LOW);
}
void BrakeOn() {
if (f_Brake_On == false) {
Indio.digitalWrite(pin_Brake, HIGH);
f_Brake_On = true;
}
}
void BrakeOff() {
if (f_Brake_On == true) {
Indio.digitalWrite(pin_Brake, LOW);
f_Brake_On = false;
}
}
void sendPulse() {
PWMHigh();
delayMicroseconds(fallingTime);
PWMLow();
delayMicroseconds(pulseDelay);
}
I've updated the post with more codes to clarify. Basically the PWM is generated by rapidly turning on and off of an internal digital pin (5V). The motor is Autonics A50K-M566-GB10. It is a stepper motor with 24Vdc brake (no voltage = brake on, has voltage = brake off). The controller is Autonics MD5-ND14 which accept 5V PWM input signal, thus the usage of internal pin. The motor is moving a ballscrew with pitch 5mm (5mm forward / backward movement for each rotation). The motor will turn 0.072 degree/pulse so 5000 pulses are needed for one full rotation. Now the ballscrew will rotate 1mm each step (1000 pulses) each time it receive signal from sensor. During each system reboot, it will move the ballscrew to it's home position. That's when I need the interrupt service in order to stop the motor since motor will run continuously until it hit the home sensor. The movement to home will only execute once on each startup. After that, motor will move in step during normal operation. Once it hit the 'end' sensor, the movement will be reversed until it hit the 'home' sensor and repeat again. Now for controlling the step movement, I need to be able to set the number of pulses being sent out. Also I need to be able to specify delay and pulse width in order to calculate the movement speed. Can I do that with analogWrite?
Hi Albert, thanks for posting the model numbers of the motor and driver you are using, that helps to offer some advice. As far as i can tell, you should not be using the Arduino PWM: > your motor driver expects a PWM signal with duty cycle 50% and the frequency will determin the motor speed. Arduino PWM is standard at 490/980Hz, and not trivial to change; analogWrite only changes the duty cycle (0-100%). > the INDIO's digital output switching frequency is limited to around 400Hz; too slow for your driver, so indeed you will need to use the MCU pin(s). > for your startup situation, with the motor returning to the home position, i don't think you need an interrupt. at this point, your sketch does not need to do anything else, so you can send a pulse, and read the sensor before you send another pulse for the next motor step. you can choose the speed by timing your pulses.
Hi Tom. But that is the problem. If I also check for Indio's digital input connected to 'home' sensor while moving the motor, it will slow down the motor speed significantly. I guess reading Indio input cost some significant overheads. At the moment, the pulseDelay and fallingTime values are set to 1 micro second. I guess reading sensor need much more time than that, thus slowing the loop. That's why I thought it was necessary to remove the part that read the sensor status when the motor is running and use interrupt to stop the motor. Or perhaps there is something else that I miss? Please let me know what do you think.
Hi Albert, now i understand better, sorry i had not thought that through. So there is a trade-off between speed and resolution: how about checking the sensor after moving the motor 1mm (1000 pulses), does that give enough resolution and acceptable overall speed? If you need less resolution, you can move more between the sensor pollings. Alternatively you could still use the interrupt on the INDIO digital inputs if you are not connecting (or changing status) on any other channels. Are you using the other channels during this operation?
Hi Tom. Hmm so reading once every certain pulses. I might try that out and play around with different timing. Yes, I do use other channels for counting rotational pulse. I might try the interrupt flag solution as it seems like it produce the least overhead. Thanks for your suggestion btw =)
Ok Albert, let me tell you what I would do in your situation from scratch. To control the pulses for the motor I would indeed use an interrupt, but a timer interrupt. The interrupt routine would count back a step counter which stops the motor when it reaches zero. So you just write a number into the variable and your MCU sends that number of pulses to the stepper motor. If you need it bidirectional and absolute, you can have a number for the position and another number for the step to be reached, so when you input a new coordinate the interrupt will bring your screw exactly to that point. Another good reason for using a timer interrupt is that you can change speed and write speedup and slowdown routines. So now that interrupt is taking care of everything, you just need in your main loop to write zero in the counter when you reach the endpoint. If you are working on absolute values, you also zero your reference variable. This should work without all the issues we are facing and speed is not influenced by anything. Sometimes we try hard to solve the last detail of a situation, while it is better to go back to the basics and find a more practical solution. ANyhow your tecnique is called bit banging in the case of PWM, but yours is not PWM as Tom as beautifully explained, but a stepper motor. In PWM frequency is not very important, it is the duty cycle that does the trick. Of course if you have a fixed pulse width, varying the frequency will also vary the duty cycle, but that's a very coarse way of doing things.
Hi Stefano. It seems like you have provided a promising solution. However, having a background of high level programming (.NET, php, etc), I am not very familiar with timer interrupt. So putting the Motor Run code on interrupt is a good idea since it will halt other process until motor stop. But since I also need to monitor setpoint sensors ('home' and 'end') to stop the motor when it hit the setpoint, where should I put the code? I thought Indio's library is not accessible from interrupt routine, thus the proposed flagging system to check which one of the eight digital pins triggered the interrupt? Perhaps a little code could shed some lights =D
If you don't mind, I would really love to see the basic codes to setup timer interrupts that you suggested and point me where to put the StartMotor(), StopMotor() and CheckSetpoint() routines (let's assume the code to check whether motor has hit the 'home' or 'end' setpoint is placed in CheckSetpoint routine).
What we will be doing is use timer 2 to generate a timed interrupt that will toggle the output you choose.
If you want to use it through the MCU hardware you will need to study it on the AT90USB1286 manual.
I will give you the software solution which is portable and should work fine up to a certain speed.
The best way is to use Timer2 library, which I have no way to check at the moment but should work without problems.
A single detail you are still missing is: we don't put the whole motor run under interrupt, we just put one step.
Interrupt is an interleaving matter, like a dance.
Main routine-step-main routine-step and so on.
So you have plenty of room to put your checking of sensors in the main routine because between steps the MCU is idle.
Otherwise what would be the use for interrupts? Here is a basic code:
#include <Indio.h>
#include <MsTimer2.h>//HERE YOU DEFINE OUR PINS
//CHECK WITH YOUR WIRING!!!
#define SENSOR_IN 5
#define MOTOR_OUT 5
#define DIR_OUT 6//VARIABLES FOR INTERRUPT ROUTINES ONLY
volatile int mStep; //steps to take
volatile bool mOut; //Output status//additional position counter
volatile int mDir; //direction - set to -1 or +1
volatile int mPos; //position counter//OTHER VARS
//loop vars
bool curDir=true;//interrupt routine
void intStep()
{
//disables the interrupt on counter = 0
if(mStep==0) MsTimer2::stop();
else
{
//toggle D5
mOut = !mOut;
digitalWrite(MOTOR_OUT, mOut);
mStep--;
mPos += mDir;
}
}void motorStart(int steps, bool dir)
{
mDir = dir;
mStep=steps;//Set direction
if(dir) Indio.digitalWrite(DIR_OUT,HIGH);
else Indio.digitalWrite(DIR_OUT,LOW);//enables the interrupt.
MsTimer2::start();
}void motorStop()
{
mStep=0;
//disables the interrupt.
MsTimer2::stop();
}void setup()
{
//this function sets a time on ms for the overflow. Each overflow, intStep will be called.
MsTimer2::set(1, intStep);pinMode(MOTOR_OUT,OUTPUT);
Indio.digitalMode(SENSOR_IN,INPUT);
Indio.digitalMode(DIR_OUT, OUTPUT);
}void loop()
{
//Start motor
motorStart(500,curDir);
//Check sensor, adjust to your wiring
if(Indio.digitalRead(SENSOR_IN)==LOW)
{
motorStop();
mPos=0; //zero position couter
}//the routine flows normally during motor run, but I add some commands when motor stops just for demonstration purposes
if(mStep==0)
{
curDir = !curDir; //invert direction
delay(2000);
}
}
Now, you made me work!
Hope this helps you devise a more solid software for you application.
Okay, this is very interesting. I will study your code and play around with it. Thanks a lot for writing the code. If you ever come to Indonesia let me know. I will pick you up and buy you some beers =)
Stefano, could you please send me an email at: albertlt@yahoo.com ? I need to discuss something in private with you.
Hello,
it is physically not possible, as the Philips 9555 I/O expander works this way.
What you can do is check the input register to filter the right IRQ's.
Only issue, it's an I2C chip, so reading the register takes time.
Usually the interrupt is done by setting a flag and checking in main loop.
It all depends on speed on all inputs.
If you are not using the 14-pin connector, you can find a few I/O pins which are regular MCU pins with interrupt.
I actually use a separate MCU to collect interrupts and send finished data through the serial port, but that's me!
I actually use pin 5 of the 14-pin connector to output PWM signal to a stepper driver as the input of the driver is 5V. I first attempted to not use interrupt by reading Indio CH5 while moving the motor via PWM however it slows down the motor significantly. That's the reason I'm trying to use interrupt. Btw, CH-5 is a proximity sensor so the motor stop when it hit the location of the sensor. Now if I use another pin from the idc 14-pin, the pin only accept 5V but sensor output is 12-24 Volt. Or is there another way to move motor while detecting if sensor is hit? At the moment I disable all other activities while the motor is moving so it does not slow down the PWM output (rising/falling and pulse width both are 1 micro second).
Moving a motor is a very slow process compared to microprocessor cycles. I would read CH5 in the main loop, which you can measure with millis() to find out how long it lasts in the worst case. Or you can convert the 12-24V to 5V using two resistors, or better a resistor and a zener diode, which limits the voltage to a fixed value protecting the input. The schematics is very simple and you can mount it directly in the 14-pin plug.
Actually I don't understand why reading CH5 should slow down your motor. Maybe a bit of code would make it more clear.
Stefano, I've updated with code. I haven't finish writing and I only post part that relevant to the problem
Hi Albert, please check here for a way to do this https://industruino.com/forum/help-1/question/multiple-channels-interrupts-on-32u4-topboard-205
Your answer
Please try to give a substantial answer. If you wanted to comment on the question or answer, just use the commenting tool. Please remember that you can always revise your answers - no need to answer the same question twice. Also, please don't forget to vote - it really helps to select the best questions and answers!
Keep Informed
About This Forum
This community is for professionals and enthusiasts of our products and services.
Read GuidelinesQuestion tools
Stats
Asked: 3/6/17, 3:48 PM |
Seen: 3572 times |
Last updated: 3/8/17, 11:09 AM |
Honestly I think you have it all wrong to start with! It is still not clear if your motor is a DC motor or rather a stepper motor, because the sendPulse() routine is not described. But if it's a DC motor I don't understand what are you doing with a single step. Something is wrong, as I said. First of all this is not PWM but a sort of bit banging technique. PWM is a hardware function where you set a duty cycle by using analog write on a digital output and the MCU sends a train of pulses. If indeed it is a stepper motor, did you consider using the interrupt with a timer to send the number of pulses you need? Please add some spec about the motor