Tachometer; photo input with 4bit LCD

geezer88

Senior Member
I've posted this in code snippets because this is working, but not finished, and won't be. However, parts may be useful to folks. The LCD code supports four bit interface. The retro-reflective photo proximity sensor uses a one transistor amplifier and a photo diode and LED from a junk inkjet printer. My MPU is a 18M2.

RPM is one of several rate related calculations that typically drives the need to divide a 16 or 32 bit word by a 16 word. Jeremy Leach, a poster in this forum, wrote a math routine set, and this division was stolen from his work. See the listing remarks for more about his work, and where to get it from this forum.

Leading zero suppression is another need for a project like this, and for this I used the routine from inglewoodpete. Again, see the listing for more info.

tom

Code:
#rem
TACHOMETER WITH PHOTO DIODE REFLECTIVE PROXIMITY INPUT   Tom    5/26/2011
This program implements a simple photo tachometer using a photo diode and LED from scrounged 
from a dead printer.  Ambient light makes an imact on operation, so a variable resistor is
used to adjust for stable operation.
#endrem

'* Pin Assignments  **************************************

    'LCD pin  6 = SE = c.6 = pin 15 CPU
    'LCD pin  4 = RS = c.7 = pin 16 CPU

    'LCD pin 11 = D4 = b.4 = pin 10 CPU
    'LCD pin 12 = D5 = b.5 = pin 11 CPU
    'LCD pin 13 = D6 = b.6 = pin 12 CPU
    'LCD pin 14 = D7 = b.7 = pin 13 CPU
    'phototransistor input = b.0

'* Symbols ************************************************

    symbol LcdSE = c.6            'strobe data pin
    symbol LcdRS = c.7            'data direction pin (read/write)

    symbol LcdByte = b0            'data byte sent to LCD
    symbol tthous = b5            'result of bintoascii command
    symbol thous = b4            'result of bintoascii command
    symbol huns = b3            'result of bintoascii command
    symbol tens = b2            'result of bintoascii command
    symbol ones = b1            'result of bintoascii command
    symbol j = b6                'scratch variable
    symbol i = b5                'scratch variable                *SHARED WITH tthous*
    symbol scratchword = w5        'scratch word variable             *SHARED WITH WORDB*
    
    symbol WordA = w4            'one of two word multiplied to create a 32 bit numerator
    symbol WordB = w5            'second word to be multiplied
    symbol WordC = w6            'denominator word   in my case the pulsein value

    symbol LSW = w7                'least significant word of the numerator
    symbol MSW = w8                'most significant work of the numerator

    symbol wholew = w9            'whole word part of some calculations
    symbol remain = w10            'remainder of some calculations
    symbol Fwhole = w11            'final whole word part of the finished calculation
    symbol Fremain = w12        'final remainder word of the finished calc.  I don't use 
    symbol scratch = w13        'scratch variable I don't understand

        
        
'   ****Set Up LCD ****

    EEPROM 0, ("RPM = ")            'load static text
    
    gosub LCD_Init                    'get lcd ready
    
    For j = 0 to 5
        read j, LcdByte
        gosub LCD_WrChr                'write the message
    next j

    
do    ' ****Main Loop****

        '****Get preliminary rotation period to allow calculation of average without overflow
    WordC = 0                        'zero variable for rotation period
    pulsin b.0, 0, scratchword        'get reflected light part of rotation
    pause 2
    pulsin b.0, 1, WordC            'get dark part of rotation
    pause 2
    WordC = WordC + scratchword        'add to get total time of revolution in 10 usecs
    
    i = 60000 / WordC max 10        'i will be number of samples to average without overflow
        
        '****Now begin getting data to display
    WordC = 0                        'zero variable for rotation period
    for j = 1 to i                    'begin collecting rotation times
        pulsin b.0,1,scratchword    'get reflected light part of rotation
        pause 2
        pulsin b.0,0,scratchword    'get dark part of rotation
        pause 2
        WordC = WordC + scratchword    'add to get total time of revolution in 10 usecs
    next j
    
    WordC = WordC / i                'calculate average of "i" readings
    
        '****Initialize a few variables    
    Fwhole = 0
    Fremain = 0
    WordA = 6000
    WordB = 1000
        
        '****Loop to get RPM = 6000000 / WordC
#rem
This routine was stolen from Jeremy Leach via a posting he made on the PicAxe Forum.
It was a modularized routine from a larger collection of math functions that he wrote.
I have modified it to run as part a simpler program. I confess to not understanding
all that goes on in the routine, but I have checked it for accuracy in one specialized
application for a tachometer.  In this special case, the RPM of a rotating shaft is
related to the time of rotation using pulsein, measured in units of 10 usec.  The 
resulting equation is: RPM = 6000000 / pulsein word. So for this case, I used 
6000 for WordA, 1000 for WordB which multiplied make 6 million.  WordC is the 16 bit word
resulting from pulsein.  For 12000 RPM pulsein is 500.  For 100 RPM pulsein is
60048; nearly up to the 65535 limit of a word varible.
For the whole enchalada see:  http://www.picaxeforum.co.uk/showpost.php?p=75560&postcount=1
#endrem

    do

        MSW = WordA ** WordB
        LSW = WordA * WordB
            
        wholew = 65535 / WordC * MSW
        remain = 0
    
        gosub Update
    
        wholew = LSW / WordC
        remain = LSW // WordC
    
        gosub Update
    
        WordA = 65535 // WordC + 1
        WordB = MSW
    
    loop until WordB = 0        'calculation is finished
    
    '****This neat leading zero procedure came from inglewoodpete of PicAxe forum fame
    '****See this post:  http://www.picaxeforum.co.uk/showpost.php?p=154989&postcount=16
    
    bintoascii Fwhole, tthous, thous, huns, tens, ones
    
    if tthous = "0" then
           tthous = " "
           if thous = "0" then
              thous = " "
              if huns = "0" then
                 huns = " "
                     if tens = "0" then
                     tens = " "
                 endif
             endIf
           endIf
    endIf
    
    LcdByte = $86                'move cursor two spaces past equal sign
    gosub LCD_WrIns
        
    LcdByte = tthous            'print tenthousands
    gosub LCD_WrChr    
        
    LcdByte = thous                'print thousands
    gosub LCD_WrChr
    
    LcdByte = huns                'print hundreds
    gosub LCD_WrChr
    
    LcdByte = tens                'print tens
    gosub LCD_WrChr
    
    LcdByte = ones                'print ones
    gosub LCD_WrChr

loop                            'return to beginning of main loop
    
end


'* Initialise LCD *****************************************
LCD_Init:    
    
    'set relevant pins to output
    dirsb = %11110000
    dirsc = %11000000
    low LcdRS
    low LcdSE

    'wait for LCD to stabilise (>40ms after Vcc > 2.7V)
    pause 100

    'send 00110000 three times to initialise LCD by instruction
    outpinsb = %00110000
    pause 1
    pulsout c.6,1000
    pause 1
    pulsout c.6,1000
    pause 1
    pulsout c.6,1000
    pause 1
    
    outpinsb = %00100000    'set to 4 bit mode
    pause 1
    pulsout c.6,1000
    
    LcdByte = %00101000 'set interface
    pause 1
    gosub LCD_WrIns
    
    LcdByte = %00001100 'enable display, no cursor
    pause 1
    gosub LCD_WrIns
    
    LcdByte = %00000001 'clear and home cursor
    pause 1
    gosub LCD_WrIns
    
    LcdByte = %00000110 'set cursor direction
    pause 1
    gosub LCD_WrIns
    
    return
    
    
'* Display Character On LCD *******************************
LCD_WrChr:

    high LcdRS    'set RS high (write data)
    pause 1
    
    outpinsb = LcdByte and %11110000
    pause 2
    pulsout LcdSE,1
    
    outpinsb = LcdByte * 16
    pause 2
    pulsout LcdSE,1
    
    return


'* Send Instruction to LCD ********************************
LCD_WrIns:

    low LcdRS    'set RS low (write instruction)
    pause 1
    
    outpinsb = LcdByte and %11110000
    pause 1    
    pulsout LcdSE,1
    
    outpinsb = LcdByte * 16
    pause 1
    pulsout LcdSE,1
    
    return
    
    
'* Clear LCD **********************************************
LCD_Clear:

    LcdByte = 1
    pause 1
    gosub LCD_WrIns

    return
    
    
'* Home Cursor  *******************************************
LCD_Home:

    LcdByte = 2
    pause 1
    gosub LCD_WrIns

    return


'* Move To Line 1 *****************************************
LCD_Line1:

    LcdByte = 128
    pause 1
    gosub LCD_WrIns

    return


'* Move To Line 2 *****************************************
LCD_Line2:

    LcdByte = 192
    pause 1
    gosub LCD_WrIns

    return
    
'* Move To Line 3 *****************************************
LCD_Line3:

    LcdByte = 148
    pause 1
    gosub LCD_WrIns

    return


'* Move To Line 4 *****************************************
LCD_Line4:

    LcdByte = 212
    pause 1
    gosub LCD_WrIns

    return
    


'*Update the divide function; read Jeremy Leach document for how it works
Update:

    scratch = WordC - Fremain
    
    Fremain = Fremain + remain
    
    if remain >= scratch then
    
        Fremain = Fremain - WordC
        inc wholew
        
    endif
    
    Fwhole = Fwhole + wholew
    
return
 

Attachments

Last edited:
Top