How to read rising edge of hall effect switch input without interrupt

Hello,

I have a loop that pulses a stepper motor until input from the hall effect is detected (digital 5v) but this routine did not work.

However, the following code detects the switch nicely. But is there a way to do it without using interrupts? That way I could have a smooth loop running and detect very precisely the position of the switch.

Thanks

/*
Arduino Hall Effect Sensor Project
by Arvind Sanjeev
Please check out  http://diyhacking.com for the tutorial of this project.
DIY Hacking
*/


 volatile byte half_revolutions;
 unsigned int rpm;
 unsigned long timeold;
 void setup()
 {
   Serial.begin(115200);
   attachInterrupt(0, magnet_detect, RISING);//Initialize the intterrupt pin (Arduino digital pin 2)
   half_revolutions = 0;
   rpm = 0;
   timeold = 0;
 }
 void loop()//Measure RPM
 {
   if (half_revolutions >= 20) { 
     rpm = 30*1000/(millis() - timeold)*half_revolutions;
     timeold = millis();
     half_revolutions = 0;
     //Serial.println(rpm,DEC);
   }
 }
 void magnet_detect()//This function is called whenever a magnet/interrupt is detected by the arduino
 {
   half_revolutions++;
   Serial.println("detect");
 }

Using interrupts can be beneficial for operations or events where timing is important..... or things really need to get done right away when the event occurs.

Using interrupts is like having a dedicated person keeping watch of a particular event for you.....and then coming to tell immediately when the event is detected.

When you don't use interrupts for signalling and detecting an event or occurrence......... you would then need to regularly go around to periodically check to see if something has changed. If the rate at which you go to check is high enough..... then ok. But if the rate isn't high enough.... there might be increased chance that some activity might be missed in between your checks. The terminology for checking every once in a while is 'polling'.

For sure..... you can get your program loop to check the status (eg. read a value) for each loop. You can even store the current value as well as the previous value of whatever is being read. However, there might be chances that you miss some activity in-between the checking...... which could lead to issues. But..... if you know in advance that it's not going to be an issue...... then no problem.

I can't see it being a problem since speed isn't an issue, the steppers advance 0.00025" per loop cycle. But I don't know how to check the hall effect input using digital or analogread. Does anyone know?

I may end up using interrupts, since they will halt the loop when sensor is triggered, and that just might work... but since I need 3 of them most likely, will have to use the arduino 2560 which has more interrupt pins. I bought one out of curiosity a while ago. I just don't know if they use the same programming.

You should be able to use digitalread of a pin. Once you read it.... it'll give you a value. Store that value (which will become previous value for the next read). Then read again.... and compare this reading with previous value. If those two compared values are different.... then hopefully you detected an edge.

If you use digitalread.... you might need to set the pinmode to 'INPUT_PULLUP'.

This is based on your code try it out let us know if it works better

volatile float rpm;
void setup()
{
  Serial.begin(115200);
  attachInterrupt(0, magnet_detect, RISING);//Initialize the interrupt pin (Arduino digital pin 2)
}
void loop()//Measure RPM
{
  static float RPMcopy;
  static unsigned long SpamTimer;
  if ( (unsigned long)(millis() - SpamTimer) >= (100)) {
    SpamTimer = millis();
    noInterrupts (); // prevents corrupted data when reading rpm
    RPMcopy = rpm; // make a copy
    rpm = 0;// set rpm to zero because if the motor stops or we get less than 1 pulse every 100 milliseconds we can assume the motor is stopped
    interrupts ();
    Serial.print(RPMcopy , 3);
    Serial.print(" RPM");
    Serial.println();
  }
}
void magnet_detect()//This function is called whenever a magnet/interrupt is detected by the arduino
{
  unsigned int time;
  static int timeold;// remember value with static but keep it local
  time = micros(); // Lets get the time now and use the same value everywhere
  // your divisor looks like the following
  // your encoder has 40 pulses per revolution according to your code.
  // 1500000.0 = 60 * 1000000 microseconds in a minute / 40 pulses
  // So at 1 rpm you will have 1500000.0 microseconds pass between pulses
  rpm = 1500000.0 / (time - timeold);
  timeold = time;
}

tog1:
I may end up using interrupts, since they will halt the loop when sensor is triggered, and that just might work… but since I need 3 of them most likely, will have to use the arduino 2560 which has more interrupt pins. I bought one out of curiosity a while ago. I just don’t know if they use the same programming.

NOTE: You can have every input pin trigger as an interrupt:
This code should work I modified it from a class library I created. It compiles but it may fail to run. Let me know and I will fully test it. Getting late or I would test it tonight.
This code allows every pin to be an interrupt pin and has a similar function to attachInterrupt ( Pin, Callback Function, Type ) the code uses onPinInterrupt (Pin, Callback Function, Type);
I provided 3 interrupts and guessed as to which pins you wish to use but you could have 10 interrupts and it should work smoothly. as long as your RPM rates don’t get too high.
Let me know how it goes :slight_smile:

// interrupt on any pin code for Arduino UNO only:

static void nothing(void) {};
static void nothing(uint32_t, uint32_t, uint32_t) {};

typedef void (*voidFuncVoidPtr)(void);
voidFuncVoidPtr  PinCB[20] = {nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing, nothing}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
uint8_t  BitNumber[20] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21}; // Key = Pin Val = Position List of pins that could be used as ping inputs:
uint8_t PinType[20];
union Mask {
  volatile uint32_t All;
  volatile uint8_t  Port[4];
};
Mask _Pins;

volatile uint32_t _PinMask; //volatile uint8_t
volatile uint32_t _PCintLast; //volatile uint8_t

void ISR_Exe(void);
ISR(PCINT2_vect) { //this ISR pins 0-7
  ISR_Exe();
}
ISR(PCINT1_vect) { //this ISR pins A0~A5
  ISR_Exe();
}
ISR(PCINT0_vect) { //this ISR  pins 8-13
  ISR_Exe();
}
void ISR_Exe() {
  _Pins.Port[0] = PIND; // 0-7
  _Pins.Port[1] = PINB; // 8-13
  _Pins.Port[2] = PINC; // A0-A6
  uint32_t _cTime = micros();
  _Pins.Port[3] = 1;    // Placeholder
  uint32_t _Changed = _Pins.All ^ _PCintLast;// doing a ^ between the current interruption and the last one indicates wich pin changed
  uint32_t _Masked = _Changed & _PinMask;// Has one of the pins we need changed
  if (!_Masked)return;                   //No Data move on
  PinChange(_cTime, _Masked, _Pins.All); // Callback Function
  _PCintLast = _Pins.All;          // we memorize the current state of all PINs in group
}
bool CheckPin(uint8_t Pin) {
  return (bitRead(_Pins.All, BitNumber[Pin]));
}

void PinChange(uint32_t Time, uint32_t PinsChanged, uint32_t Pins) {
  for (byte Pin = 0; Pin < 20; Pin++) {
    if (bitRead(PinsChanged, Pin)) {
      if (PinCB[Pin]) {
        if ((PinType[Pin] == CHANGE) || ((PinType[Pin] == FALLING) && CheckPin(Pin) == false ) || ((PinType[Pin] == RISING) && CheckPin(Pin) == true )) {
          PinCB[Pin]();
        }
      }
    }
  }
}

void AddPin(uint8_t Pin) {
  if (bitRead(_PinMask, Pin)) return; // return Pointer to this class
  bitWrite(_PinMask, BitNumber[Pin], 1);
  *digitalPinToPCMSK(Pin) |= bit (digitalPinToPCMSKbit(Pin));  // enable pin
  PCIFR  |= bit (digitalPinToPCICRbit(Pin)); // clear any outstanding interrupt
  PCICR  |= bit (digitalPinToPCICRbit(Pin)); // enable interrupt for the group
}

void onPinInterrupt(int Pin, void (*_CB)(), byte Type ) {
  AddPin(Pin);
  PinCB[Pin] = _CB;
  PinType[Pin] = Type;
}

// End Interrupt on any pin code
//***********************************************************************************************
//Your Code

volatile float rpm1, rpm2, rpm3;

void magnet_detect1()//This function is called whenever a magnet/interrupt is detected by the arduino
{
  unsigned int time;
  static int timeold;// remember value with static but keep it local
  time = micros(); // Lets get the time now and use the same value everywhere
  // your divisor looks like the following
  // your encoder has 40 pulses per revolution
  // 1500000.0 = 60 * 1000000 microseconds in a minute / 40 pulses
  rpm1 = 1500000.0 / (time - timeold);
  timeold = time;
}
void magnet_detect2()//This function is called whenever a magnet/interrupt is detected by the arduino
{
  unsigned int time;
  static int timeold;// remember value with static but keep it local
  time = micros(); // Lets get the time now and use the same value everywhere
  // your divisor looks like the following
  // your encoder has 40 pulses per revolution
  // 1500000.0 = 60 * 1000000 microseconds in a minute / 40 pulses
  rpm2 = 1500000.0 / (time - timeold);
  timeold = time;
}
void magnet_detect3()//This function is called whenever a magnet/interrupt is detected by the arduino
{
  unsigned int time;
  static int timeold;// remember value with static but keep it local
  time = micros(); // Lets get the time now and use the same value everywhere
  // your divisor looks like the following
  // your encoder has 40 pulses per revolution
  // 1500000.0 = 60 * 1000000 microseconds in a minute / 40 pulses
  rpm3 = 1500000.0 / (time - timeold);
  timeold = time;
}


// Pick Which pins to use Digital pins 0 ~ 13 and Analog pin 0 = 14 ... 15,16,17,18 ... analog pin 5 = 19 (Note that the analog pins triggar like Digital Pins)
#define MagnetDetectorPin1 6
#define MagnetDetectorPin2 8
#define MagnetDetectorPin3 10

void setup() {
  Serial.begin(115200);
  pinMode(MagnetDetectorPin1,INPUT);
  pinMode(MagnetDetectorPin2,INPUT);
  pinMode(MagnetDetectorPin3,INPUT);
  onPinInterrupt(MagnetDetectorPin1, magnet_detect1, RISING);
  onPinInterrupt(MagnetDetectorPin2, magnet_detect2, RISING);
  onPinInterrupt(MagnetDetectorPin3, magnet_detect3, RISING);
}

void loop() {
  // put your main code here, to run repeatedly:
  static float RPMcopy1, RPMcopy2, RPMcopy3;
  static unsigned long SpamTimer;
  if ( (unsigned long)(millis() - SpamTimer) >= (100)) {
    SpamTimer = millis();
    noInterrupts (); // prevents corrupted data when reading rpm
    RPMcopy1 = rpm1; // make a copy
    rpm1 = 0;// set rpm to zero because if the motor stops or we get less than 1 pulse every 100 miliseconds we can assume the motor is stopped
    RPMcopy2 = rpm2; // make a copy
    rpm2 = 0;// set rpm to zero because if the motor stops or we get less than 1 pulse every 100 miliseconds we can assume the motor is stopped
    RPMcopy3 = rpm3; // make a copy
    rpm3 = 0;// set rpm to zero because if the motor stops or we get less than 1 pulse every 100 miliseconds we can assume the motor is stopped
    interrupts ();
    Serial.print("readings 1,2,3: ");
    Serial.print(RPMcopy1 , 3);
    Serial.print(" RPM, ");
    Serial.print(RPMcopy2 , 3);
    Serial.print(" RPM, ");
    Serial.print(RPMcopy3 , 3);
    Serial.print(" RPM");
    Serial.println();
  }
}

Z

Thank you I can see a lot of effort put into these solutions.

I got it to work based on HIGH and LOW between motor pulses as it moves 0.00025" at a time and it works very well so far, stopping within 0.001" of home based on dial indicators. I think the precision of the setup can be adjusted by adjusting motor speed and giving it lots of opportunity to catch that sensor in LOW.

Thanks again