PDA

View Full Version : PIC16F84A Tachometer LED routine



ThaSanta
- 18th October 2008, 23:29
I'm writing a routine to a tachometer. I just doesn't work! I've searched the forum, but i haven't found anything that looks like this.




'************************************************* ***************
'* Name : Tacho.BAS *
'* Author : Christian Lerche *
'* Notice : Copyright (c) 2008 Lerche *
'* : All Rights Reserved *
'* Date : 07-10-2008 *
'* Version : 1.0 *
'* Notes : PIC16F84A @ 10 Mhz XT *
'* : XT osc, BODEN off, WD off *
'************************************************* ***************
'Include------------------------------------------------------------------------

'Definitions--------------------------------------------------------------------
Define osc 10 ' Define Osc as 10 Mhz
DEFINE PULSIN_MAX 25000 ' Lowest RPM 600

'Inputs/outputs-----------------------------------------------------------------
TRISB = $80 ' All PortB Output
TRISA = $f0 ' All PortA Output
INPUT PORTA.4 ' PortA.4 Input

'Variables----------------------------------------------------------------------
B0 VAR BYTE ' Define B0 as byte
W1 var WORD ' Define W1 as word

'Main program-------------------------------------------------------------------
loop:
IF PORTA.4 THEN ' Wait until start of low pulse
GOTO MAIN1
ELSE
GOTO loop
ENDIF
MAIN1:
PULSIN PORTA.4,0,W1 ' Measure pulse-width in 4µS resolution
GOTO MAIN
MAIN:
W1 = W1 * 4 ' Convert µS to mS
W1 = (60000 / W1) ' Revs in 60 mS
W1 = W1 * 1000 ' Revs in 60 sec
GOSUB display ' Go to display sobroutine
GoTo LOOP ' Do forever

'Display subroutine-------------------------------------------------------------
display: B0 = W1 / 1000 ' Find number of thousands
W1 = W1 // 1000 ' Remove thousands from W1
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $17 ' Turn on fourth digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
B0 = W1 / 100 ' Find number of hundreds
W1 = W1 // 100 ' Remove hundreds from W1
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $1B ' Turn on third digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
B0 = W1 / 10 ' Find number of tens
W1 = W1 // 10 ' Remove tens from W1
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $1D ' Turn on second digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
B0 = W1 ' Get number of ones
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $1E ' Turn on first digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
Return ' Return to main
' Routine copied from ledart.

'Lookup table-------------------------------------------------------------------
bin2seg:
Lookup B0,[$40,$79,$24,$30,$19,$12,$02,$78,$00,$18],B0
Return


Anyone got a clue, what is does wrong?

Thanks in advance!

/ ThaSanta

mackrackit
- 19th October 2008, 02:33
What part does not work?

Acetronics2
- 19th October 2008, 09:43
Hi, Santa ( de bois )

1) Just the usual gag with PORTA.4 ...

Did you place an external pullup resistor or something to drive the pin to HIGH state ???


2) I note 10 Mhz need a HS Osc ... ( but generally works with XT !!! )


3)

MAIN1:
PULSIN PORTA.4,0,W1 ' Measure pulse-width in 4µS resolution
GOTO MAIN


you'd better try



MAIN1:
RCTIME PORTA.4,0,W1 ' Measure pulse-width in 4µS resolution
GOTO MAIN



4) your Mathematics are really bad for INTEGERS using ...




W1 = W1 * 4 ' Convert µS to mS
W1 = (60000 / W1) ' Revs in 60 mS
W1 = W1 * 1000 ' Revs in 60 sec


you'd much better use DIV32 ...



That's the first hits ...

Alain

ThaSanta
- 19th October 2008, 15:58
Sorry for letting the problem out (I was kinda tired).

The attached image views the first digit of 4. As if the algorithm doesn't calculate the RPM correct.

1) - I have not yet build the circuit, as I don't have a programmer.
2) - I don't think the Simulator cares about HS or XT :-)

3) I wrote RCTIME instead of PULSIN, but it didn't chance anything.

Actually, after reading the manual to PBP, i don't know how to use DIV32.
I've tried examples, but I still can't figure it out.


- Thanks again guys. :-)

Acetronics2
- 19th October 2008, 17:25
Hi,

The most annoying thing in your project is ... the PIC must drive continuously the 4 digits AND while that, must measure the rotation period.

I think you'll quickly understand you'll have to use interrupts and ... a 16Bits timer ... what the 16F84 doesn't have aboard ...
so, you'll also have to watch for TMR0 overflows ...to increment a 8 bits counter for the timer Upper 8 bits counting.

Not really easy ( "asm" recommended here )... but doable.

seeing your pain for the first steps ... I'd recommend you to switch to a 16F628a that has everything to EASILY realise such a RPM meter. ( I mean the capture module ... )

The project is yours ... you are the one who decides !!!

Alain

skimask
- 19th October 2008, 18:13
Hi, Santa ( de bois )
1) Just the usual gag with PORTA.4 ...
Did you place an external pullup resistor or something to drive the pin to HIGH state ???

Only applies when using PortA.4 as an output...


2) - I don't think the Simulator cares about HS or XT :-)
Arg! Simulators!

ThaSanta
- 19th October 2008, 20:47
I think you'll quickly understand you'll have to use interrupts and ... a 16Bits timer ... what the 16F84 doesn't have aboard ...
so, you'll also have to watch for TMR0 overflows ...to increment a 8 bits counter for the timer Upper 8 bits counting.

I'll go along and try to use the CCP on Pic16F628A :-)
I'll be back with updates.


Thanks for the fast replies!

skimask
- 19th October 2008, 23:51
Here's one small problem...


Define osc 10 ' Define Osc as 10 Mhz

http://www.picbasic.co.uk/forum/showthread.php?t=558

Archangel
- 19th October 2008, 23:53
edit:all good . . .

ThaSanta
- 20th October 2008, 00:20
I really think somethings wrong with the math. I just can't figure it out :/

Now an example:

MAIN:
W1 = W1 * 2 ' To get whole period
W1 = W1 * 4 ' now the count is in mS
W1 = (60000/W1) ' Revs per 60 mS
W1 = W1 * 1000 ' Revs per minute
GOSUB display ' Go to display sobroutine
GoTo LOOP ' Do forever

The input has a period of 12 mS (5000 RPM) - Which is 1500 increments @ 4µS.
As I only measure the low part of the 50% duty-cycle, I multiply with 2. Full period.
Then I multiply with 4. Now it's in mS. 12,000.
60,000 / 12,000 = 5.
5 * 1000 = 5000 RPM. NICE! - But i doesn't work.

I have seriously no idea of why this shouldn't work.

EDIT:
And then i thought. Figure out when i goes wrong:
So i tried to make the calculations. With a number, that wouldn't go straight up into 60,000. Example: 2000.
2,000*2=4,000
4,000*4=16,000
60,000/16,000=3.75 <- I guess this is were the PIC can't follow my lead.
3.75*1,000= 3750.

skimask
- 20th October 2008, 01:18
EDIT:
And then i thought. Figure out when i goes wrong:
So i tried to make the calculations. With a number, that wouldn't go straight up into 60,000. Example: 2000.
2,000*2=4,000
4,000*4=16,000
60,000/16,000=3.75 <- I guess this is were the PIC can't follow my lead.
3.75*1,000= 3750.
Well, since PicBasicPro (nor the PIC itself) doesn't support floating point variables, it's not surprising...

Andy Wood
- 20th October 2008, 02:47
I'm writing a routine to a tachometer. I just doesn't work! I've searched the forum, but i haven't found anything that looks like this.




'************************************************* ***************
'* Name : Tacho.BAS *
'* Author : Christian Lerche *
'* Notice : Copyright (c) 2008 Lerche *
'* : All Rights Reserved *
'* Date : 07-10-2008 *
'* Version : 1.0 *
'* Notes : PIC16F84A @ 10 Mhz XT *
'* : XT osc, BODEN off, WD off *
'************************************************* ***************
'Include------------------------------------------------------------------------

'Definitions--------------------------------------------------------------------
Define osc 10 ' Define Osc as 10 Mhz
DEFINE PULSIN_MAX 25000 ' Lowest RPM 600

'Inputs/outputs-----------------------------------------------------------------
TRISB = $80 ' All PortB Output
TRISA = $f0 ' All PortA Output
INPUT PORTA.4 ' PortA.4 Input

'Variables----------------------------------------------------------------------
B0 VAR BYTE ' Define B0 as byte
W1 var WORD ' Define W1 as word

'Main program-------------------------------------------------------------------
loop:
IF PORTA.4 THEN ' Wait until start of low pulse
GOTO MAIN1
ELSE
GOTO loop
ENDIF
MAIN1:
PULSIN PORTA.4,0,W1 ' Measure pulse-width in 4µS resolution
GOTO MAIN
MAIN:
W1 = W1 * 4 ' Convert µS to mS
W1 = (60000 / W1) ' Revs in 60 mS
W1 = W1 * 1000 ' Revs in 60 sec
GOSUB display ' Go to display sobroutine
GoTo LOOP ' Do forever

'Display subroutine-------------------------------------------------------------
display: B0 = W1 / 1000 ' Find number of thousands
W1 = W1 // 1000 ' Remove thousands from W1
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $17 ' Turn on fourth digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
B0 = W1 / 100 ' Find number of hundreds
W1 = W1 // 100 ' Remove hundreds from W1
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $1B ' Turn on third digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
B0 = W1 / 10 ' Find number of tens
W1 = W1 // 10 ' Remove tens from W1
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $1D ' Turn on second digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
B0 = W1 ' Get number of ones
Gosub bin2seg ' Convert number to segments
PortB = B0 ' Send segments to LED
PortA = $1E ' Turn on first digit
Pause 1 ' Leave it on 1 ms
PortA = $1F ' Turn off digit to prevent ghosting
Return ' Return to main
' Routine copied from ledart.

'Lookup table-------------------------------------------------------------------
bin2seg:
Lookup B0,[$40,$79,$24,$30,$19,$12,$02,$78,$00,$18],B0
Return


Anyone got a clue, what is does wrong?

Thanks in advance!

/ ThaSanta


Please check this line:

loop:
IF PORTA.4 THEN ' Wait until start of low pulse


Should it be:

IF PORTA.4 = 0 THEN ' Wait until start of low pulse

Andy

skimask
- 20th October 2008, 05:04
Please check this line:
loop:
IF PORTA.4 THEN ' Wait until start of low pulse
Should it be:
IF PORTA.4 = 0 THEN ' Wait until start of low pulse
Andy

Unless he's just making sure that the timing loop doesn't start in the middle of the pulse...

ThaSanta
- 20th October 2008, 10:01
Please check this line:

loop:
IF PORTA.4 THEN ' Wait until start of low pulse


Should it be:

IF PORTA.4 = 0 THEN ' Wait until start of low pulse

Andy

- As Skimask says: I am making sure it is not starting from the middle of a period :D

Acetronics2
- 20th October 2008, 10:06
Hi Christian,

I'd like to be sure of one thing : what do you use as a rotation detector ???

... as in your calculations, the pulsin signal is supposed to be equal to the rotation period, something hurts me ... but what ???

Alain

ThaSanta
- 20th October 2008, 16:13
Hi Christian,

I'd like to be sure of one thing : what do you use as a rotation detector ???

... as in your calculations, the pulsin signal is supposed to be equal to the rotation period, something hurts me ... but what ???

Alain

As it is, I am only using a simulator. I do not have a programmer.

Actually, it's supposed to measure the RPM of a computer-fan. 50% duty-cycle. :-)

Dave
- 21st October 2008, 11:45
ThaSanta, Here is a snipit of code I wrote about 4 years ago to do exactly what you are requesting..

ASM
PutMulResult macro Const32
MOVE?CB low Const32, R2
MOVE?CB low (Const32 >> 8), R2 + 1
MOVE?CB low (Const32 >> 16), R0
MOVE?CB low (Const32 >> 24), R0 + 1
endm
ENDASM

PULSIN B_IN,1,POINTER1 'MEASURE HIGH PULSE
IF POINTER1 > 0 THEN
@ PutMulResult 15000000 ' Load PBP internal vars Max= 2,147,483,648
POINTER2 = DIV32 POINTER1 ' Divide 15000000/(PERIOD/2)
SCRATCH = R2 ' Get remainder of Div32
' Change remainder to Decimals
SCRATCH = SCRATCH * 10000 ' Multiply remainder * 10,000
SCRATCH = DIV32 POINTER1 ' Divide remainder by original divisor
ELSE
POINTER2 = 0
SCRATCH = 0
ENDIF
LCDOUT $FE,LINE(1)
PAUSEUS 100
LCDOUT DEC5 POINTER2,".",DEC4 SCRATCH

I hope it works for you... It did for me...

Dave Purola,
N8NTA

ThaSanta
- 19th June 2009, 01:20
Hey again guys. Long time, no code.

Finally, I got the idea.

- This rountine uses an external freq-to-voltage chip, and reads it using ADCIN, multiplies it, and show the result a lot of times per second.

Here goes.


'************************************************* ***************
'* Name : RPM.BAS *
'* Author : Christian Lerche *
'* Notice : Copyright (c) 2009 DSB El-teknik *
'* : All Rights Reserved *
'* Date : 28-05-2009 *
'* Version : 1.0 *
'* Notes : PIC16F913, Boden Off, WDT off, PWR off *
'* : *
'************************************************* ***************
'
' Includes
'

'
' Defines-----------------------------------------------------------------------
'
Define osc 10 ' Define oscillator XT 10 MHz
DEFINE ADC_BITS 10 ' Define A/D Converter 10 bit
DEFINE ADC_CLOCK 3 ' Define ADC clock source
DEFINE ADC_SAMPLEUS 50 ' Wait 50 µS before convert to digi
'
' INPUT / OUTPUT----------------------------------------------------------------
'
TRISA = 0 ' PORTA is output
TRISA.5 = 1 ' PORTA.5 is input
TRISB = 1 ' PORTB is input
TRISC = 0 ' PORTC is Output
TRISB.0 = 1 ' B0 / Int is count input
ADCON0 = %10010001 ' ADC On, right justified
ADCON1 = 0 ' ADC Freq = 625KHz
ASM ; Insert ASM
BANKSEL LCDCON ; LCD Configuration
CLRF LCDCON ; Turn off LCD Driver
ENDASM ; End of ASM
'
' Variables and constants-------------------------------------------------------
'
W1 VAR WORD ' W1 is 16 bit variable
W2 var word ' W2 is 16 bit variable
W3 var word ' W3 is 16 bit variable
B0 var byte ' B0 is 8 bit variable
'
' Main program------------------------------------------------------------------
'
Main:
ADCIN 4, W2 ' Read Freq-to-voltage device
W3 = W2 * 977 ' Freaky constant makes it work.
W1 = DIV32 100 ' Put the result in W1
GOSUB display4 ' Show RPM
GOTO Main ' Do forever
END ' For the compilers sake
'
' Subroutines-------------------------------------------------------------------
'
display4:
B0 = W1 / 1000 ' Find number of thousands
W1 = W1 // 1000 ' Remove thousands from W1
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $01 ' Turn on fourth digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
B0 = W1 / 100 ' Find number of hundreds
W1 = W1 // 100 ' Remove hundreds from W1
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $02 ' Turn on third digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
B0 = W1 / 10 ' Find number of tens
W1 = W1 // 10 ' Remove tens from W1
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $04 ' Turn on second digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
B0 = W1 ' Get number of ones
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $08 ' Turn on first digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
RETURN ' Return to caller!


I'm sorry that there is some comments that shouldn't be in the code. But I've made a lot of changes through time!!

ThaSanta
- 5th July 2009, 00:54
Hey All.

I've finally finished my tachometer, with full 4 7 segments displays. It updates very fast, with the calculation, and showing the new RPM's.
- Later I will post the full code, and probably the hardware to convert. It's really cheap, and the only thing you have to change in it, it the potentiometer value, so that 166,6 * the number of cilenders your car have. That's the frequency of the top 5volts. For some of the guys, that doesn't have a squarewave signal generator, to adjust it with, you can grab the schematic of LM2907, and start calculating the resistor the fits the purpose.

My routine is simply the routine from the melabs, site. 4*7 Segments, multiplexed, and a calculator, plus ADC converter, the it updates, more than you'll ever get to see.
It's not finished, cuz i need to remove the first zeroes, before the RPM, cuz it is confusing.

Anyways, schematic and code will be applied this week.


May the code be with you all!

DanPBP
- 5th July 2009, 02:41
Great! I'm looking forward to see them!

ThaSanta
- 24th July 2009, 02:58
'************************************************* ***************
'* Name : RPM.BAS *
'* Author : Christian Lerche *
'* Notice : Copyright (c) 2009 DSB El-teknik *
'* : All Rights Reserved *
'* Date : 28-05-2009 *
'* Version : 1.0 *
'* Notes : PIC16F913, Boden Off, WDT off, PWR off *
'* : *
'************************************************* ***************
'
' Includes
'

'
' Defines-----------------------------------------------------------------------
'
Define osc 10 ' Define oscillator XT 10 MHz
DEFINE ADC_BITS 10 ' Define A/D Converter 10 bit
DEFINE ADC_CLOCK 3 ' Define ADC clock source
DEFINE ADC_SAMPLEUS 50 ' Wait 50 µS before convert to digi
'
' INPUT / OUTPUT----------------------------------------------------------------
'
TRISA = 0 ' PORTA is output
TRISA.5 = 1 ' PORTA.5 is input
TRISB = 1 ' PORTB is input
TRISC = 0 ' PORTC is Output
TRISB.0 = 1 ' B0 / Int is count input
ADCON0 = %10010001 ' ADC On, right justified
ADCON1 = 0 ' ADC Freq = 625KHz
ASM ; Insert ASM
BANKSEL LCDCON ; LCD Configuration
CLRF LCDCON ; Turn off LCD Driver
ENDASM ; End of ASM
'
' Variables and constants-------------------------------------------------------
'
W1 VAR WORD ' W1 is 16 bit variable
W2 var word ' W2 is 16 bit variable
W3 var word ' W3 is 16 bit variable
B0 var byte ' B0 is 8 bit variable
'
' Main program------------------------------------------------------------------
'
Main:
ADCIN 4, W2 ' Read Freq-to-voltage device
W3 = W2 * 9774 ' 9999 / 1023 = 9.774
W1 = DIV32 1000 ' Put the result in W1
GOSUB GETNUM ' Show RPM
Goto Main ' Do this forever
END ' For the compilers sake
'
' Subroutines-------------------------------------------------------------------
'
GETNUM:
IF W1 >= 1000 THEN display4
IF W1 >= 100 THEN display3
IF W1 >= 10 THEN display2
IF W1 >= 0 THEN display1
display4:
B0 = W1 / 1000 ' Find number of thousands
W1 = W1 // 1000 ' Remove thousands from W1
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $01 ' Turn on fourth digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
display3:
B0 = W1 / 100 ' Find number of hundreds
W1 = W1 // 100 ' Remove hundreds from W1
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $02 ' Turn on third digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
display2
B0 = W1 / 10 ' Find number of tens
W1 = W1 // 10 ' Remove tens from W1
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $04 ' Turn on second digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
display1
B0 = W1 ' Get number of ones
Lookup B0,[$3F,$06,$5B,$4F,$66,$6D,$7D,$07,$7F,$67],B0
PORTC = B0 ' Send segments to LED
PORTA = $08 ' Turn on first digit
Pause 1 ' Leave it on 1 ms
PORTA = $00 ' Turn off digit to prevent ghosting
RETURN ' Return to caller!


Here's the code for the project. I've made it, so it doesn't show the zeroes before the actual RPM.

I hope some of you can use it. Later I'll post the schematic for F/V converter :)

** EDIT **

http://farm3.static.flickr.com/2593/3751256360_c8b5b1c550.jpg?v=0
Here's the schematic for the F/V Converter. For the 8V source, i use a small regulator, so that the car can run between 12 and 13.8 V at the battery. Depending on the source to the converter, the potentiometer is turned, until satisfied. Mine will run of the 12V to the ignition coil, so that i have 4 pulses for one revolution of the engine.
Depending on your choice of pulses, you may use a Frequency generator, at square-wave, and if your car pulses 4 times per revolution, then adjust it, until you get 5V out, at 4*166.66.
If the car pulses once per revolution, set your frequency generator to 166.66 (mine was digital), and adjust the potentiometer until 5V is reached.

You've now succesfully made a digital tachometer, that updates faster than the code could ever do, by it self.

Good luck, and have fun all of you!