Using Hall Sensor(TLE-4905) with Arduino Mega

Hi,

I am using hall sensor with Arduino Mega to measure RPM of my car. I have seen many posts about how to use it but I have an issue. The pin connection in my setup is as shown in the image i.e the hall sensor is connected to external clock input (T5) of Timer/Counter5. Pin T5 is digital pin 47 in arduino mega and this pin doesn’t have an external interrupt associated with it. So how can I measure RPM without using interrupt in this case?

PS: The circuit is much more complex and therefore I cannot change the pin connections.

Thank in advance. :slight_smile:

So how can I measure RPM without using interrupt in this case?

The input of the hall sensor to the external clock source T5 will count pulses in TCNT5. The hall sensor is open collector so be sure to have an external pullup or use INPUT_PULLUP. Set up the timer to trigger on one pulse edge. You will need to refer to the Atmel processor data sheet for the Mega.

You can determine RPM from the number of pulses occurring in a fixed period of time. Use another hardware timer, or a milis() software timer as a counting period.

If possible, can you share a code which does this? Because I am not that well acquainted with Arduino yet...

Thanks :slight_smile:

How you powered up your sensor? Be advised, that arduino doesn't tolerate voltage above Vcc on its inputs.
Here is the code, reply #4.

The sensor is powered correctly I think. No issues with that. And thanks for sharing the code. It looks very complex but can you just drop me some hints how can I use it in my case? (I mean I have no idea what this means "to get maximum precision out of arduino's ICP feature")

cyber_warrior:
The sensor is powered correctly I think. No issues with that. And thanks for sharing the code. It looks very complex but can you just drop me some hints how can I use it in my case? (I mean I have no idea what this means "to get maximum precision out of arduino's ICP feature")

It's mean best accuracy. There are many ways to measure frequency (or RPM) by arduino.

Here is the code, reply #4.

That code is using the ICP(input capture) pin. ICP5 is on D48.

The OP claims that he only has the external clock source pin(T5) on D47 available.

If that's the case, you can not determine the pulse characteristics, but only the count the number of pulses in a period of time.

You will be using the timer/counter as a counter. Here is some sample code written for Timer1 on a UNO.

unsigned long count_period = 2000;
unsigned int count_to_rpm = 60000UL / count_period; //convert counts to RPM
byte number_of_sensors = 1;
unsigned long final_counts;
unsigned long start_time = millis();
unsigned long measured_time;
unsigned long current_time = millis();

void setup()
{
  Serial.begin(115200);
  TCCR1A = 0; //initialize Timer1
  TCCR1B = 0;
  TCNT1 = 0;
  pinMode( 5, INPUT_PULLUP); //external source pin for timer1
  //set external clock source pin D5 rising edge
  TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12);

  //Test pulses to confirm circuit 50 pps =  3000
  //pinMode(11, OUTPUT);
  //tone(11, 50);//test pulse jumper pin 11 to pin 5
}

void loop()
{
  current_time = millis();
  if (current_time - start_time >= count_period)
  {
    TCCR1B = 0; //stop counter
    final_counts = TCNT1; //frequency limited by unsigned int TCNT1 without rollover counts
    TCNT1 = 0;
    measured_time = current_time - start_time;
    start_time = current_time;;
    unsigned int rpm = (final_counts * count_to_rpm) / number_of_sensors;
    TCCR1B =  bit (CS10) | bit (CS11) | bit (CS12); //restart external clock source

    Serial.print(measured_time);
    Serial.print("\t\t");
    Serial.print(final_counts);
    Serial.print("\t\t");
    Serial.println(rpm);
  }
}

cattledog:
That code is using the ICP(input capture) pin. ICP5 is on D48.

The OP claims that he only has the external clock source pin(T5) on D47 available.

Right, only ICP4 on pin D49. But since OP didn't noticed, it means he did not read anything in the code, except first comments line.
For average person it would not take much time to change registers names Timer4 to Timer5. Think of it like stupidity test, and sadly, OP failed. No offence meant.

The circuit is much more complex and therefore I cannot change the pin connections.

I am not that well acquainted with Arduino yet...

I too, have my doubts that the OP will have success wandering off the standard path and into Timer/Counter country with register settings. The code I suggested can be pretty much directly converted from Timer1 to Timer5.

cattledog:
That code is using the ICP(input capture) pin. ICP5 is on D48.

The OP claims that he only has the external clock source pin(T5) on D47 available.

If that's the case, you can not determine the pulse characteristics, but only the count the number of pulses in a period of time.

You will be using the timer/counter as a counter. Here is some sample code written for Timer1 on a UNO.

Okay so I understand this code pretty well. I tried running it with the following changes and it prints only zeroes in the output. Where am I going wrong? (I think the pin number in pinMode is not right...)

unsigned long count_period = 2000;
unsigned int count_to_rpm = 60000UL / count_period; //convert counts to RPM
byte number_of_sensors = 1;
unsigned long final_counts;
unsigned long start_time = millis();
unsigned long measured_time;
unsigned long current_time = millis();

void setup()
{
  Serial.begin(115200);
  TCCR5A = 0; //initialize Timer5
  TCCR5B = 0;
  TCNT5 = 0;
  pinMode( 47, INPUT_PULLUP); //external source pin for timer1
  //set external clock source pin D5 rising edge
  TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52);

  //Test pulses to confirm circuit 50 pps =  3000
  //pinMode(11, OUTPUT);
  //tone(11, 50);//test pulse jumper pin 11 to pin 5
}

void loop()
{
  current_time = millis();
  if (current_time - start_time >= count_period)
  {
    TCCR5B = 0; //stop counter
    final_counts = TCNT5; //frequency limited by unsigned int TCNT5 without rollover counts
    TCNT5 = 0;
    measured_time = current_time - start_time;
    start_time = current_time;;
    unsigned int rpm = (final_counts * count_to_rpm) / number_of_sensors;
    TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52); //restart external clock source

    Serial.print(measured_time);
    Serial.print("\t\t");
    Serial.print(final_counts);
    Serial.print("\t\t");
    Serial.println(rpm);
  }
}

Thanks for the help. :slight_smile:

it prints only zeroes in the output.

What is your input?

Before trying with the hall sensor, use the test I suggested with a pulse from tone() jumpered from an output pin to your input pin 47.

I think the pin number in pinMode is not right..

Pin 47 should be correct.

Okay it works now. The hall sensor wasn't receiving power from the arduino (I had to power it with battery). Thanks alot for the help :))))

cattledog:
I too, have my doubts that the OP will have success wandering off the standard path and into Timer/Counter country with register settings. The code I suggested can be pretty much directly converted from Timer1 to Timer5.

Approx 1 year late getting here hehehe. But --- cattledog, first --- thanks very much for posting your code, as I am just about to try your code and then study and understand it real carefully.

Last year, I was trying to use the arduino mega to read rpm of small dc motors up to around 4000 rpm (max) --- using interrupt counting of pulses. Wvmarle (member) correctly pointed out that there can be (or will be) over-head associated with interrupt counting. I'm using a 1024 p/r rotary encoder at the moment, so that could be an issue with the number of interrupts coming at the angular speeds being used here. I can later try lower pulse per rev rotary encoders too. I've been getting maybe +/- 5 percent variation (from averaged rpm value) with my interrupt counting code, while the hand-held laser tachometer gives quite nice and stable readings at the 'correct' (averaged) RPM value.

So I'm going to turn toward the method used in your code!

One small question is --- what did you mean by "have my doubts that the OP will have success wandering off the standard path and into Timer/Counter country with register settings" ?

What did you mean by wandering off the standard path, and into timer/counter country with register settings? Your method for RPM measurement is based on timer/counter country, right? Just trying to understand this - as I don't want to head down the wrong path myself. Thanks again!

One small question is --- what did you mean by "have my doubts that the OP will have success wandering off the standard path and into Timer/Counter country with register settings" ?

No worries for you.

The OP was a self proclaimed newbie who was "not well acquainted with Arduino yet". He even appeared to be short of pins on a Mega somehow and couldn't figure out how to change anything around to free different pins.

Using the external clock source as a high speed rpm counter is very much recommended when interrupts techniques start to fail.

Cattledog ..... your code works very nicely on my mega 2560 (as was expected!)

I'll paste your code (with the timer numbers and pin numbers in some comments for aligning with the mega 2560).

I'll also paste some results from the serial monitor.

cattledog:
Using the external clock source as a high speed rpm counter is very much recommended when interrupts techniques start to fail.

A very significant and great recommendation cattledog!

unsigned long count_period = 2000;  // units of millisec
unsigned int count_to_rpm = 60000UL / count_period; //convert counts to RPM
byte number_of_sensors = 1;
unsigned long final_counts;
unsigned long start_time = millis();
unsigned long measured_time;
unsigned long current_time = millis();

void setup()
{
  Serial.begin(115200);
  TCCR5A = 0; //initialize Timer5
  TCCR5B = 0;
  TCNT5 = 0;
  pinMode( 47, INPUT_PULLUP); //external source pin for timer 5 of MEGA 2560
  //set external clock source pin T5 (digital pin 47) to clock on a rising edge
  TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52);

  //Test pulses to confirm circuit 50 pps =  3000
  pinMode(11, OUTPUT);
  tone(11, 50);//test pulse jumper pin 11 to pin 47 of MEGA 2560
}

void loop()
{
  current_time = millis();
  if (current_time - start_time >= count_period)
  {
    TCCR5B = 0; //stop counter
    final_counts = TCNT5; //frequency limited by unsigned int TCNT5 without rollover counts
    TCNT5 = 0;
    measured_time = current_time - start_time;
    start_time = current_time;;
    unsigned int rpm = (final_counts * count_to_rpm) / number_of_sensors;
    TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52); //restart external clock source

    Serial.print(measured_time);
    Serial.print("\t\t");
    Serial.print(final_counts);
    Serial.print("\t\t");
    Serial.println(rpm);
  }
}
2000 101 3030
2000 100 3000
2000 100 3000
2000 100 3000
2000 100 3000
2000 100 3000
2000 101 3030
2000 100 3000
2000 100 3000

Hi again cattledog! For your code ..... I decided to try 1 millisecond count duration.... ie count period set to '1'. And I set the tone function to 50000 .... ie. 50 kHz squarewave (tone function set to '50000').

The expected number of captured pulses is 50 (in a 1 millisecond duration), and the output counts seen in serial monitor are approximately 70...... much higher than 50. I think I have to look at the waveform on the oscilloscope to check that first.

The code works nicely with 2 second count duration (ie. 2000 millisecond count period) and 50 pulses per sec from tone generator (using the tone function set to '50')!

counts seen in serial monitor are approximately 70...... much higher than 50. I think I have to look at the waveform on the oscilloscope to check that first.

I don't confirm what you are seeing. I changed the millis() timer to micros() and set the measurement period = 1000. With tone(11,50000) I am seeing counts of 49 or 50 with a period from 1000 to 1012 us.

For more precise high speed counting, you want to have the period defined by a hardware timer an not a software one using millis() or micros(). See Nick Gammon's Frequency Timer sketch at https://www.gammon.com.au/timers

cattledog:
I don't confirm what you are seeing. I changed the millis() timer to micros() and set the measurement period = 1000. With tone(11,50000) I am seeing counts of 49 or 50 with a period from 1000 to 1012 us.

For more precise high speed counting, you want to have the period defined by a hardware timer an not a software one using millis() or micros(). See Nick Gammon's Frequency Timer sketch at https://www.gammon.com.au/timers

Thanks very much for your help and time CD! And for letting me know that you got counts of 49 or 50!! That sounds promising for me. I'll checking everything again over here, and will let you know what happened on my side. Also - thanks for passing on that link! I'll definitely be learning a lot from it, and also from what you mentioned. Really appreciated. I'll let you know what I did incorrectly later --- about getting the higher counts instead of around 50! Thanks again!

Hi again cattledog!

Getting closer to the reason now ..... which relates to what you already mentioned about software timing.

The serial comms activity/software-loop could be affecting the timing.

When I run the code with a few serial.print commands for outputting results to serial monitor, I see counts ranging from 91 to 95.

The code below gives counts of 91 to 95 for 1 millisecond capture intervals at 50 kHz. Counts of approx 50 is expected.

unsigned long count_period = 1000;  // units of MICROsec
unsigned int count_to_rpm = 60000UL / count_period; //convert counts to RPM
byte number_of_sensors = 1;
unsigned long final_counts;
unsigned long start_time = micros();
unsigned long measured_time;
unsigned long current_time = micros();

void setup()
{
  Serial.begin(115200);
  TCCR5A = 0; //initialize Timer5
  TCCR5B = 0;
  TCNT5 = 0;
  pinMode( 47, INPUT_PULLUP); //external source pin for timer 5 of MEGA 2560
  //set external clock source pin T5 (digital pin 47) to clock on a rising edge
  TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52);

  //Test pulses to confirm circuit 50 pps =  3000
  pinMode(11, OUTPUT);
  tone(11, 50000);//test pulse jumper pin 11 to pin 47 of MEGA 2560
}

void loop()
{
  current_time = micros();
  if (current_time - start_time >= count_period)
  {
    TCCR5B = 0; //stop counter
    final_counts = TCNT5; //frequency limited by unsigned int TCNT5 without rollover counts
    TCNT5 = 0;
    measured_time = current_time - start_time;
    start_time = current_time;;
    unsigned int rpm = (final_counts * count_to_rpm) / number_of_sensors;
    TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52); //restart external clock source

    Serial.print(measured_time);
    Serial.print("\t\t");
    Serial.print(final_counts);
    Serial.print("\t\t");
    Serial.println(rpm);
  }
}

Results:

1844 91 5460
1916 95 5700
1920 95 5700
1912 95 5700
1888 93 5580
1912 93 5580
1848 93 5580
1912 95 5700
1920 95 5700
1916 94 5640
1916 95 5700

When a couple of those serial.print lines are removed, the count values become the ones we want to see ...... ie. approx. counts of 50.

unsigned long count_period = 1000;  // units of MICROsec
unsigned int count_to_rpm = 60000UL / count_period; //convert counts to RPM
byte number_of_sensors = 1;
unsigned long final_counts;
unsigned long start_time = micros();
unsigned long measured_time;
unsigned long current_time = micros();

void setup()
{
  Serial.begin(115200);
  TCCR5A = 0; //initialize Timer5
  TCCR5B = 0;
  TCNT5 = 0;
  pinMode( 47, INPUT_PULLUP); //external source pin for timer 5 of MEGA 2560
  //set external clock source pin T5 (digital pin 47) to clock on a rising edge
  TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52);

  //Test pulses to confirm circuit 50 pps =  3000
  pinMode(11, OUTPUT);
  tone(11, 50000);//test pulse jumper pin 11 to pin 47 of MEGA 2560
}

void loop()
{
  current_time = micros();
  if (current_time - start_time >= count_period)
  {
    TCCR5B = 0; //stop counter
    final_counts = TCNT5; //frequency limited by unsigned int TCNT5 without rollover counts
    TCNT5 = 0;
    measured_time = current_time - start_time;
    start_time = current_time;;
    unsigned int rpm = (final_counts * count_to_rpm) / number_of_sensors;
    TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52); //restart external clock source


    Serial.println(final_counts);

  }
}

Results:

50
50
50
50
50
50
50
50
50
50
49
50
50

Back again! After taking a longer look at the code to find reasons for the unwanted increase in number of captured counts when more serial.print lines are added (and thus giving undesired number of extra counts), it just seemed that a single serial.print line didn't cause any issues. But multiple serial.print lines led to increased number of counts.

To address this issue, I decided to put the codes for resetting the time AFTER all of the serial.print lines. So in the code, I placed the following lines of code AFTER all the serial.prints :

current_time = micros();
start_time = micros();   
    
TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52); //restart external clock source

The code that works nicely is now:

unsigned long count_period = 1000;  // units of MICROsec
unsigned int count_to_rpm = 60000UL / count_period; //convert counts to RPM
byte number_of_sensors = 1;
unsigned long final_counts;
unsigned long start_time = micros();
unsigned long measured_time;
unsigned long current_time = micros();

void setup()
{
  Serial.begin(115200);
  TCCR5A = 0; //initialize Timer5
  TCCR5B = 0;
  TCNT5 = 0;
  pinMode( 47, INPUT_PULLUP); //external source pin for timer 5 of MEGA 2560
  //set external clock source pin T5 (digital pin 47) to clock on a rising edge
  TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52);

  //Test pulses to confirm circuit 50 pps =  3000
  pinMode(11, OUTPUT);
  tone(11, 50000);//test pulse jumper pin 11 to pin 47 of MEGA 2560
}

void loop()
{
  current_time = micros();
  
  if (current_time - start_time >= count_period)
  {
    TCCR5B = 0; //stop counter
    final_counts = TCNT5; //frequency limited by unsigned int TCNT5 without rollover counts
    TCNT5 = 0;
    measured_time = current_time - start_time;
    unsigned int rpm = (final_counts * count_to_rpm) / number_of_sensors;

    Serial.print(measured_time);
    Serial.print("\t\t");
    Serial.print(final_counts);
    Serial.print("\t\t");
    Serial.println(rpm);
    
    current_time = micros();
    start_time = micros();   
    
    TCCR5B =  bit (CS50) | bit (CS51) | bit (CS52); //restart external clock source
  }
}

And now we get the expected number of counts of approximately '50' in the following results:

1008 50 3000
1008 49 2940
1012 49 2940
1008 50 3000
1016 49 2940
1004 50 3000