Go Down

Topic: Using Hall Sensor(TLE-4905) with Arduino Mega (Read 907 times) previous topic - next topic

Southpark

#15
Jul 09, 2019, 01:22 am Last Edit: Jul 09, 2019, 04:35 am by Southpark
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')!

cattledog

Quote
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

Southpark

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!

Southpark

#18
Jul 09, 2019, 08:08 am Last Edit: Jul 09, 2019, 09:05 am by Southpark
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.

Code: [Select]

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:

Code: [Select]

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.

Code: [Select]

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:

Code: [Select]

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

Southpark

#19
Jul 10, 2019, 08:14 am Last Edit: Jul 10, 2019, 11:29 pm by Southpark
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 :

Code: [Select]

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



The code that works nicely is now:

Code: [Select]

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:

Code: [Select]

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

Southpark

#20
Jul 10, 2019, 11:34 pm Last Edit: Jul 10, 2019, 11:44 pm by Southpark
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.
Cattledog ..... when I used your original code, and when I changed millis to micros, I get an increased number of counts .... up to 80 counts (instead of the expected 50). I get the same effect with two different MEGA 2560 boards. Interestingly --- when I keep only the serial.print statement for printing the number of counts (and remove all other serial.print statements), the number of counts becomes the expected '50', or close to it.

I don't yet know the reason behind the increased the number of counts due to having additional serial.print lines. The nice thing is --- we definitely get improvement when we reset the reference times AFTER the set of serial.print lines.

cattledog

From what I saw in your post #18, when you were getting the high counts, the time period is not 1000 microseconds, but closer to 2000.

The serial print statements are slowing down the loop. At 115200 baud, and 10 bits per character you get 11.5 characters out per millisecond.

I'm not sure what you can do with serial output every millisecond. :)

Quote
we definitely get improvement when we reset the reference times AFTER the set of serial.print lines.
It makes the measurement period more discontinuous, but that might not be an issue. That is, you alternatively measure for a millisecond, and print for a millisecond

You can always divide the number of counts by the actual measurement time period to get Hz.

Southpark

#22
Jul 11, 2019, 02:04 am Last Edit: Jul 11, 2019, 02:44 am by Southpark
Cattledog .... thanks for your comments and recommendations. My aim is/was to get as accurate RPM readings as I can from a fairly small DC motor (pretty-much unloaded, or lightly-loaded via a shaft that attaches to a rotary encoder for RPM measurements), and see if the arduino mega 2560 is able to do some closed-loop control on the dc motor's RPM (basic rpm step response) ---- like a rolling start ... eg 1000 rpm to 2000 rpm. The aim is to just get a rough 'Kv' value --- ie. rough Kv factor to cover linear RPM versus pwm (sure --- there's dead-band to consider), and rough 'first order' time constant. And hopefully will get the system to work roughly like a second order system. My dc motor will only work up to around 4000 rpm max --- usually operating around 1000 to 3500 rpm range.

I was thinking that if I can use the method based on your code, then at least I might be able to fairly conveniently get decent RPM measurements and do some motor control the same time.

Now, with Nick Gammon's frequency counter method --- that uses two timers --- one for counting and the other for controlling gating duration. I haven't yet thought about whether Nick Gammon's frequency counting method will work for my case ----- as his code has a while-loop in the main loop that keeps looping until the counter has finished counting.

But with your method, that makes use of the external counter only --- I think it might be workable for my case. I'll see how it goes! Hopefully the mega 2560 can do it for me, without needing to go for heavier artillery hehehe. Thanks again CD.

Southpark

Hi again Cattledog!!

I noticed a line in your code that says : byte number_of_sensors = 1;

If the number of sensors were instead '2', instead of 1, then will that mean 1 channel, but expecting 2 pulses per revolution?

Or can that mean 2 channels (A and B) and feeding channel A to one external timer, and feeding channel B to a different external timer ----- and then combining the counts from both timers?

Just asking only! At the moment, I have a rotary incremental encoder that has 2 channels. Your code works excellently with 1 channel feeding timer 5 of the MEGA2560. I was then thinking about whether the other output channel of the encoder can be used as well ------ which is where I'm coming from with the question about byte number_of_sensors = 1;
.

Thanks CD!


Code: [Select]

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
  }
}

cattledog

Quote
If the number of sensors were instead '2', instead of 1, then will that mean 1 channel, but expecting 2 pulses per revolution?
Yes. number_of_sensors is used in the division to get revolutions from raw counts.

Quote
Or can that mean 2 channels (A and B) and feeding channel A to one external timer, and feeding channel B to a different external timer ----- and then combining the counts from both timers?
You could send both A and B signals to the same Timer external clock source input for 2x counts. I would see no reason to send the signals to different timers.

I'm not certain of the benefit of using the two outputs from a quadrature encoder to increase the resolution of a high speed counter. Typically the quadrature encoders will have multiple pulses per revolution on A/B and perhaps one per revolution on a Z output.

If you can find value in the doubled output, there should be no obstacle to using it. One thing to be aware of is that the code does not handle rollovers in the TCNT value, so be careful of the counting time period and the number of counts at expected rpm.

Southpark

#25
Aug 22, 2019, 04:25 am Last Edit: Aug 23, 2019, 08:11 am by Southpark
Really appreciated your comments and help again CD. They were really useful and helpful comments.

At the moment, I'm just using 1 channel of a 2-channel incremental encoder. And was just thinking about whether I could use the rising edges of both channels to trigger the counter --- to provide more resolution for a case when a small DC motor is starting up. But you're right about the roll-over. I made use of Nick Gammon's code (which you linked me too before) to handle roll-overs.

My encoder is a 1024 count per rev device. I might not need both channels. I was only contemplating it - for cases where I might need fine enough time resolution to do simple approximated integration (approximated integrating function) of motor speed when the motor begins at some constant velocity, and then steps up to a different velocity. Last year, when I was using regular software loop and doing pulse counting with interrupts, it just seems that the interrupt method isn't going to do well. And I think that I might fair a bit better with the external timer count method of motor speed measurement.

After thinking about it some more, combining rising edges of two channels (eg. A and B) is not going to work - because just focusing on the rising edges of both waveforms (and combining them) won't provide a regular distribution of rising edges (even if I were to apply monostables to each edge before combining). I would actually have to apply monostables to both the rising and falling edges of both waveforms A and B in order to get a regular distribution in waveform pattern after combining them all.

So it looks like the only option I have for now is to just use my 1024 count encoder and use the 1 channel to trigger the timer5 counter. And then I just have to choose an appropriate count duration like 1 millisecond, or 2 millisecond etc.

Thanks again CD!


Go Up