# Lathe Tachometer with PIC16F886

Status
Not open for further replies.

#### tgrandahl

##### New Member
Hello,

I am working on replacement tachometer for a 30Yr old lathe that im helping rebuild. I am planning on using a hall sensor pickup and a 4x7-seg display readout. The system needs to read from 10Rpm to 5000Rpm, the hall pickup is from a 30 tooth gear.

I have had experience writing assembly for mega128 mC's although its been a few years. This will be my first venture into PIC land.

From reading around it looks like a PicKit 2 is a well regarded programmer / debugger?

I am also considering a PIC16F886 as I would like a chip with an internal oscillator, as well as comparator and 16-bit timer.

The main function would increment a count variable everytime a comparator interrupt occurs from the hall sensor.

I would like to update the display every .5 sec, so i have been planning on using a timer that would interrupt after this long. At the timer interrupt the current count value will be fed into a LUT function using "case" statements that will output the binary to drive the 7-seg's from the current count.

The 7-segs will then be updated and the 7-segs cleared.

Does this sounds like an efficient way of accomplishing my goal with using this chip?

Also, I would like a resolution of 5rpm, this would result with almost 1000 values in that LUT, I am concerned that this will not fit within this device, as the binary values for the display alone will be almost 16kb. How can i translate this into program memory requirements to size my device?

#### Pommie

##### Well-Known Member
You could use the comparator interrupt to count pulses but with a multiplex interrupt as well it could get messy. I would suggest feeding the comparator output into the Timer1 input and use timer1 to count the pulses. Use timer2 to generate a 1mS interrupt to multiplex the display and use this interrupt to work out when the 0.5 seconds is up. With a range of 10RPM (5 pulses per second) to 5000RPM (2500pps), timer1 will contain 2.5 (yuk) to 1250 and so your table will contain over 1000 entries. This is not a problem as you have 8K of 14 bit memory and so each location can contain 1 entry in BCD format (assuming 3 relevant digits). Using 2 locations per timer1 value will still only use 1/3 of the memory.

The Pickit2 or a clone (Junebug) is an excellent choice.

Mike.

#### Mr RB

##### Well-Known Member
I just think that's a poor way to do it; the effort to generate the table and the poor resolution and accuracy of the final system.

I would measure the elapsed period of 4, 16 or 32 pulses (depending what speed range the lathe is in) using the PIC timer you can get an elapsed time accurate to fractions of a microsecond, even more accurate if you average the period over a number of input pulses.

Then use a math function to calculate "frequency" in RPM which will give you full accuracy, using all 4 digits, so for low ranges it could say 12.72 RPM as an example.

No look up table needed, and much better accuracy and finer resolution.

#### Pommie

##### Well-Known Member
I just think that's a poor way to do it; the effort to generate the table and the poor resolution and accuracy of the final system.

I would measure the elapsed period of 4, 16 or 32 pulses (depending what speed range the lathe is in) using the PIC timer you can get an elapsed time accurate to fractions of a microsecond, even more accurate if you average the period over a number of input pulses.

Then use a math function to calculate "frequency" in RPM which will give you full accuracy, using all 4 digits, so for low ranges it could say 12.72 RPM as an example.

No look up table needed, and much better accuracy and finer resolution.

Agree completely, that is the best way to do it, however most people shy away from maths in asm. Lookup tables are just easier.

Mike.

#### Mike - K8LH

##### Well-Known Member
Does that "30 tooth gear" mean you'll get 30 pulses per revolution from the Hall sensor?

#### Chippie

##### Member
IIRC, a Hall sensor requires a magnet in order to generate a pulse...

However in answer to the question, 30 pulses would be generated by a suitable sensor...

As it happens I'm working on a similar project, I'm using a slotted opto and a wheel( which is directly mounted on the spindle) with 20 slots in it...PIC is a 16F628A and displaying on a 2*16 line display......

#### Mike - K8LH

##### Well-Known Member
Chippie,

So you could potentially count pulses at quarter second intervals on your 20 ppr opto-sensor and collect 50 pulses for 10 RPM and 25,000 pulses for 5000 RPM, correct? That's 0.2 RPM resolution, isn't it? If so, that's pretty neat.

Mike

#### Chippie

##### Member
Ermm...I'm suffering brain fade at the moment so my maths is a bit rusty...

I'm trying to limit the upper speed to around 3000 rpm as I have a 3phase motor and VFD ( yeah despite the grey matter acting up atm I managed to do a 1ph to 3ph conversion using a Teco FM50 ) and the speed can go up a lot higher if I increase the motor frequency to 100Hz...but I dont really want to do that.

#### tgrandahl

##### New Member
May I ask what effect the "30 tooth gear" will have on the number of pulses generated per revolution. I apologize for not being able to deduce that info' from your original post.

If you're going to count pulses, would there be any advantage generating multiple pulses per revolution, especially for the lower speeds?

The hall sensor will output 30 pulses per revolution of the spindle, the sensor outputs about 2.2V while low and about 4.7V while high. I think having a larger number of pulses per revolution will certainly help with lower speeds, I am however considering the 30 pulses / rev fixed for this project.

I agree with the guys. Using simple math is a good alternative to a large table. If as Pommie suggested you were getting 2 to 1250 pulses in 1/2 second which correspond to 8 to 5000 RPMs then you could shift this 16 bit number two places to the left to get a number in the range of 8 to 5000 (4 RPM resolution). Run this number through a bin2dec or bin2asc routine to extract your 4 digit LED display data.

I like this idea a lot, although having a finer resolution would be nice its simple and still eliminates the need for the table.

I would measure the elapsed period of 4, 16 or 32 pulses (depending what speed range the lathe is in) using the PIC timer you can get an elapsed time accurate to fractions of a microsecond, even more accurate if you average the period over a number of input pulses.

Then use a math function to calculate "frequency" in RPM which will give you full accuracy, using all 4 digits, so for low ranges it could say 12.72 RPM as an example.

I would love the increase in resolution, although I am still thinking through how the math works here.

I have not used any asm math functions before although they look straight forward enough. From looking at the available functions here I am not sure how to approach the situation. I am considering using this 24bit by 16bit division.

(6,000,000 / time for 30 pulse count in mS) = RPM in hundredths.

Then use a bin2dec function and shift the digits as the reading grows over 100's and 1000's.

#### Chippie

##### Member
Rather than invent the wheel here chaps, what I did was take an existing pic application( it was used to measure the speed of a boat prop..) and modify the code accordingly...There was a lot of other functionality in the code which I took out...I took me about 3 attempts to get it to compile without errors....Besides I'm too old and lazy to write code....

The maths routines were part of the code and I retained them in order for it to work...They were written by Pete Hemsley...if needed I can email them to those interested...

Last edited:

#### tgrandahl

##### New Member
is anyone here familiar with the PIC16F886? I am using HI-Tech C and c-wizard to make init files and for some reason there is no comparator option provided for configuring peripherals when I select this chip. However it is listed as a feature on Maxim's website and in the user manual. Is this just a bug in c-wiz or did i miss some fine print?

Additionally I am looking at using

this binary to ASCII converter for breaking out the digits, although i am still not quite sure how to implement it. I fell like sometimes its easier to write your own code than understand someone else's.

this SPI interface
for outputting to my 7-seg driver

Last edited:

#### Mr RB

##### Well-Known Member
...
So you could potentially count pulses at quarter second intervals on your 20 ppr opto-sensor and collect 50 pulses for 10 RPM and 25,000 pulses for 5000 RPM, correct? That's 0.2 RPM resolution, isn't it? If so, that's pretty neat.
...

Actually that's the problem Mike it's not 10 rev per second but RPM, so 10 RPM is only 0.167 rotations a second, even with a 30 pulse encoder thats only 5 pulses per second with a +/- 1 pulse count error. So 4-6 pulses per second which is lousy resolution for the lookup table.

I don't see that the math part is gonna be much harder really than the lookup table system. It might even be easier.

Lets say you can easily use the timer to measure pulse period and get a period (in uS) for one spindle revolution;

RPM = (60 mil / period uS)

Or if you just get a period for one pulse count (which is 1/30th spindle rev);

RPM = (2 mil / period uS)

Which is now much more manageable. Just do the 1/period conversion as a successive addition, so keep adding period to Y until Y reaches 2000000. The math was discussed recently in the chronograph thread? This is plenty fast enough to do one calc every 1/2 sec or 1/4 sec.

#### Mike - K8LH

##### Well-Known Member
Thanks Roman. Makes perfect sense. My goof by thinking revs/second instead of revs/minute.

Take care. Mike

#### tgrandahl

##### New Member
Actually that's the problem Mike it's not 10 rev per second but RPM, so 10 RPM is only 0.167 rotations a second, even with a 30 pulse encoder thats only 5 pulses per second with a +/- 1 pulse count error. So 4-6 pulses per second which is lousy resolution for the lookup table.

I don't see that the math part is gonna be much harder really than the lookup table system. It might even be easier.

Lets say you can easily use the timer to measure pulse period and get a period (in uS) for one spindle revolution;

RPM = (60 mil / period uS)

Or if you just get a period for one pulse count (which is 1/30th spindle rev);

RPM = (2 mil / period uS)

Which is now much more manageable. Just do the 1/period conversion as a successive addition, so keep adding period to Y until Y reaches 2000000. The math was discussed recently in the chronograph thread? This is plenty fast enough to do one calc every 1/2 sec or 1/4 sec.

Yes, this exactly what I was thinking, Im a little confused as to exactly how to implement it though.

This is the first Pic C I have written and I don't remember much C.

Regarding the math, I am assuming I need to use a 32bit unsigned int32 to hold the denominator values for time count as well as numerator constants.

Do I need to use a division function such as the one here?:

PIC Microcontoller Math Method 48 by 24 bit division by Andy Lee

Or can i simply use "div_constant / t_count = rpm" and the HI_TECH C compiler will figure it out.
In that case do i even need to declare the numerator as a constant or can i just type it into the code?
What happens to the remainder in this case, does it just round down?

Im also assuming here that I can work with variables longer than 8bits in the PIC16F886 even though its an 8bit micro. Is the data just broken up into different registers?

Sorry for the newb questions but im not finding any answers that make sense to me elsewhere.

#### Pommie

##### Well-Known Member
It's early in the morning and so maybe my brain is not awake yet but, don't you just multiply the pulses per second by a fixed value to get RPM. If you use 1 second as the time base then it is simply multiply by 2.

To smooth out your reading you may want to consider doing a very simple moving average. Simply keeping the last 4 values and averaging them will provide a much steadier reading.

I haven't used HiTech C but every compiler I have used has handled the maths with no problem.

Mike.

#### Mr RB

##### Well-Known Member
Yeah if you are doing it in C it gets pretty easy, for one reason C will handle the long variables and division for you and for the other reason that there are a ton of code samples and C projects out there you can call on.

Rather than completely reinvent the wheel you could look up some "microcontroller C code" for "tacho" "frequency meter" "speedo" etc they all do the same basic method of; averaging the input pulses, inversion to turn a period into a frequency, scale to appropriate display units and drive a display in digits.

Even if you want to code it from scratch for fun and experience I would suggest looking at a couple of existing projects first.

#### tgrandahl

##### New Member
soo, im making progress

So I have spent about 4 days familiarizing and playing with mplab and hitech c now. Ultimately I have had issues getting consistent readings for the pulses period. So I stripped the code down to just the pulse counting part.

I have port b set to input and interrupt on change enabled.

When an interrupt occurs it increments hall_count.

The main function checks to see if three pulses have been recorded before it records the time.

I am running the code in debug mode with a pickit2 and having issues getting consistent time readings.

using a 555 to generate a input steady input signal, the period of the input signal fluctuated < 1uS looking at it with my scope.

My time_count is always coming up around 400 counts low and can fluctuate greatly. What can I do to get more repeatable readings?

Code:
// Tyler Grandahl
// 16F867A Tachometer
//

#include <htc.h>
#define _XTAL_FREQ 20000000	// define the clock freq

/* Program device configuration word
* Oscillator = HS
* Watchdog Timer = Off
* Power Up Timer = Off
* Brown Out Detect = On
* Low Voltage Program = Enabled
* Flash Program Write = Write Protection Off
* Background Debug = Disabled
* Data EE Read Protect = Off
* Code Protect = Off
*/
__CONFIG(HS & WDTDIS & PWRTDIS & BOREN & LVPEN & WRTEN & DEBUGDIS & DUNPROT & UNPROTECT);

// Global Variables
volatile char hall_count;
volatile char time_count;
volatile long time;
volatile short freq;

// Peripheral initialization function
void init(void)
{
/***** Common Code ****
// port b interrupts enabled
*  Global interrupt disabled during initialization
*/
INTCON    = 0b00001000;

/*  Port directions: 1=input, 0=output
*/
TRISB    = 0b11111111;

//Timer 1:enabled
//1:1 prescaller
//

T1CON	= 0b00000001;

ei();    // Global interrupts enabled
}

/***************************************/
/*************** Main Function *********/
/***************************************/

void main()
{

hall_count = 0;	// clear the counter just incase its not zero
time_count = 0;	// clear the timer counter just incase
init();			// function call to initilization function to get things started

while(1)
{
if(hall_count == 3)
{

// the time calculation
time = 0;
time = (TMR1L+(256*TMR1H)); // calculate the elapsed time in uS and write to time register
time = (time + (64000*time_count));
freq = (5000000/(time/3));

T1CON	= 0b00000000;		//disable timer 1 to record the elapsed time

time_count = 0;	// reset counter values
TMR1H = 0;
TMR1L = 0;
hall_count = 0;

T1CON	= 0b00000001;		// re enable timer 1 for another count

}

// timer overflow counter, polls value of TMR1H, when high the timer is stopped, zeroed, and time_count is increminted.

if (TMR1H >= 250)
{
T1CON	= 0b00000000;		//disable timer 1 to record the elapsed time
time_count ++;
TMR1H = 0;
T1CON	= 0b00000001;	// re enable timer 1 for another count
}
}
}

/*****************************/
/********* INTERRUPTS ********/
/*****************************/
void interrupt my_isr(void)
{
if((RB4 == 1))
{
//__delay_us(200);
//CLRWDT();
//if((RB4 == 1))
//	{
hall_count ++;
if (hall_count == 3)
{
T1CON	= 0b00000000;		//disable timer 1 to record the elapsed time
//INTCON    = 0b01000000;
}
//	}
}
RBIF = 0;
}

#### Mr RB

##### Well-Known Member
That's a rather sloppy procedure. You are stopping TMR1 which can cause issues and you are reading TMR1L and TMR1H in the middle of a C expression which can really cause issues...

I would try a totally different approach. Leave TMR1 free running, ie never stop it or write to it. First thing when you get in your interrupt grab the TMR1 value using assembler, starting with TMR1L, like this;

Code:
// read TMR1 in asm for speed
asm MOVF TMR1L,W      // 16bit timer capture occurs this instruction
asm MOVWF t1_low
asm MOVF TMR1H,W
asm MOVWF t1_high

// now correct TMR1H if TMR1L rolled between reads;
if(t1_low >= 254) t1_high--;

I've used that code for years in many 16bit timer speed control applications, it will give a perfect instantaneous read of the 16bit timer but requires that TMR1 be set at 1:1 prescaler (which is best anyway since you need precision).

Some of the new PICs can read a 16bit timer accurately in 1 hit, but not the 16F876 you are using.

Once you grab that 16bit value in every interrupt, you can subtract the previous 16bit value from the new 16bit value giving an elapsed time that will self correct every cycle and give 100% average accuracy in measuring the period between events. It's also best to store the last few values in a circular buffer and average them to reduce display jitter.

Then you have an accurate period, you can do the calc to turn it into a freq display (RPM etc).

Status
Not open for further replies.

Replies
8
Views
944
Replies
2
Views
1K
Replies
0
Views
1K
Replies
2
Views
3K
Replies
10
Views
872