'**************************************
'
'Program to use an inexpensive digital caliper as a digital height gauge for
'either a planer or a drum sander
'
'12/26/09 - Calipers are modified and ready to go now.  Had to change program to
'subtract reading from base instead of adding since we are mounting calipers
'in a different configutration. i.e. closing calipers produces larger height.
'Also adding more comments.
'
'10/16/09 - Finally built up new PCB.  Everything is working except after a calibrate
'the reading goes back to the reading from the caliper.  Also calibrate settings do
'not include decimal point yet.  Fixed both items above.
'
'04/18/09 - Integrated caliper reading and encoder calibration routines - so far
'everything is working correctly, still adding comments - 18% code usage
'
'04/16/09 - Program now working with new encoder.
'
'04/14/09 - Program seems to be working well.  I can read data from calipers and
'my reading compares exactly with calipers.  Next I need to test new encoder
'
'03/29/09 -  Start
'
'**************************************


$regfile = "m168def.dat"
$crystal = 8000000

'First configure LCD and LCD pins. Display will be 8 * 2 but 16 * 2 works fine

Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portc.0 , Rs = Portc.1
Config Lcd = 16 * 2

Dim Flag As Bit                         'flag to update display in either display or measure mode

Dim Currmode As Byte                    'mode can be calmode 'Calibrate' or measmode 'Measure'
Dim Overflow As Byte                    'timer overflow count for encoder routine
Dim Newpins As Byte                     'used in quadrature encoder to determine direction
Dim Oldpins As Byte                     'used in quadrature encoder to determine direction
Dim Direction As Byte                   '1=forward and 0=reverse

Dim Lsd As Integer                      'least significant digit in measurement 0.000X
Dim Encset As Integer                   'encoder setpoint
Dim Adder As Integer                    'amount to add in encoder setting routine

Dim Absread As Long                     'absolute reading from digital caliper (not very useful)
Dim Relread As Long                     'caliper reading relative to last 'zero'
Dim Newread As Long                     'saved caliper reading so it can't get corrupted
Dim Finalread As Long

Dim Stringfinal As String * 10          'string to hold formatted reading for display
Dim Stringencset As String * 10         'string to hold calibrate reading for display

'Define constants
Const Testing = 0                       'set testing = 1 to enable serial port debugging
Const Scale = 20480                     'caliper reading is in 1/20480 inches
Const True = 1
Const False = 0
Const Calmode = 1
Const Measmode = 0

'configure backlight control pin for LCD display and turn it on
Backlight Alias Portb.2
Config Backlight = Output
Set Backlight

'configure pin to reset caliper to zero
Calzero Alias Portd.6
Config Calzero = Output

'configure pin to control 1.5 volt regulator and turn on regulator
Calipervoltage Alias Portd.7
Config Calipervoltage = Output
Reset Calipervoltage

'configure encoder pushbutton to toggle mode and turn on pull-up
Modepb Alias Pind.5
Config Modepb = Input
Set Portd.5

'configure reading LED
Heartbeatled Alias Portb.5
Config Heartbeatled = Output

'configure mode LED
Modeled Alias Portb.0
Config Modeled = Output

'configure clock and data pins from caliper
Clockpin Alias Pind.2
Datapin Alias Pinb.1
Config Clockpin = Input
Config Datapin = Input

'configure encode channel A an B pins
Cha Alias Pind.3
Chb Alias Pind.4
Config Cha = Input
Config Chb = Input

'configure COM1 for debugging purposes
#if Testing
   Open "com1:" For Binary As #1
   $baud = 9600
#endif

'Next configure all interrupts

'Timer1 is used in the encoder calibrate routine
'With prescaler of 8 and 8MHZ clock, timer1 will overflow in 65.536 ms
'when timer overflows, we will increment a counter and stop at 5.
'The overflow value will determine how much to increment or decrement
'the base value when in calibrate mode. Smaller values of overflow = faster turn
'of the dial and therefore bigger increments
Config Timer1 = Timer , Prescale = 8
On Ovf1 Ovf1_isr

'We use the pin change interrupt for the encoder. The two channels are on Pin D.3 and D.4
'These statements enable the pin change interrupt for those 2 pins and then
'establish the interrupt vector.
Pcmsk2 = &B00011000
On Pcint2 Pcint2_isr

'The caliper clock is connected to the Int0 pin and an interrupt on this pin
'starts the caliper reading routine. We enable this interrupt since we always start
'in the measure mode
Config Int0 = Falling
On Int0 Int0_isr
Enable Int0

'Clear any spurious interrupts on Int0, Pcint2, and Ovf1
Set Eifr.intf0
Set Pcifr.pcif2
Set Tifr1.tov1

'Initialize display
Cursor Off Noblink
Cls

'start in measure mode
Currmode = Measmode

'no current reading
Flag = False

'indicate mode on LCD and turn off calibrate LED
Lowerline
Lcd "Measure "
Reset Modeled

'we need to somehow synch with the readings coming from the caliper
'this bit of code waits for any action on the clock line and then waits 5 ms
'to get past the current reading and then enables the interrupt
Bitwait Clockpin , Reset
Waitms 5
Enable Interrupts

'configure watchdog for 2 seconds
Config Watchdog = 2048
Start Watchdog

'start main loop which loops forever
Do

   Reset Watchdog
   'see if mode pushbutton pressed to toggle mode
   If Modepb = 0 Then
      Waitms 25                         'debounce
      'if it's still pressed then wait for release, disable interrupts, and toggle mode
      If Modepb = 0 Then
         Bitwait Modepb , Set
         Disable Interrupts
         If Currmode = Measmode Then
         'set calibrate mode
            Currmode = Calmode
            'set up display  for cal mode
            Upperline
            Lcd "0.0000  "
            Lowerline
            Lcd "Calibrat"
            'turn on calibrate mode LED
            Set Modeled
            'enable and disable correct interrupts for this mode
            Disable Int0
            Enable Ovf1
            Enable Pcint2
            'clear any pending interrupts
            Set Pcifr.pcif2
            Set Tifr1.tov1
            Timer1 = 0
            'set up flags and variables
            Overflow = 0
            Flag = False
            Encset = 0
            'zero out caliper reading
            Set Calzero
            Waitms 250
            Reset Calzero
            'start timer and interrupts
            Start Timer1
            Enable Interrupts
         Else
            'set measure mode
            Currmode = Measmode
            'set up display for measure mode
            Upperline
            Lcd Spc(8)
            Lowerline
            Lcd "Measure "
            'turn off calibrate mode LED
            Reset Modeled
            'enable and disable correct interrupts for this mode
            Disable Ovf1
            Disable Pcint2
            Enable Int0
            'clear any pending interrupts
            Set Eifr.intf0
            'set up flags and variables
            Flag = False
            'ensure that we are in synch for next reading
            Bitwait Clockpin , Reset
            Waitms 5
            'start interrupts
            Enable Interrupts
         End If
      End If
   End If

   Reset Watchdog
   'check mode and display correct reading for that mode
   If Currmode = Measmode Then
      If Flag = True Then
         'we are in measure mode and have a new reading
         'scale reading from caliper interrupt routine
         Finalread = Newread * 1000
         Finalread = Finalread / 2048
         'next round least significant digit to .0005"
         Lsd = Finalread Mod 10
         Finalread = Finalread - Lsd
         Select Case Lsd
            Case 3 To 7 : Finalread = Finalread + 5
            Case 8 To 9 : Finalread = Finalread + 10
            Case Else:
         End Select
         'subtract caliper reading from encode setpoint since
         'caliper opens for smaller reading
         Finalread = Encset - Finalread
         'convert reading to string
         Stringfinal = Str(finalread)
         'and display on LCD
         Upperline
         Lcd Format(stringfinal , "0.0000") ; "  "
         Flag = False
      End If
   Else
      'we are in calibrate mode
      If Flag = True Then
         'convert encoder reading to string
         Stringencset = Str(encset)
         'and display on LCD
         Upperline
         Lcd Format(stringencset , "0.0000") ; "  "
         Flag = False
      End If
   End If

Loop                                    'go back and do it all over again


'pin change interrupt used to interpret reading from a quadrature encoder
'this code was presented on the Bascom AVR forum by Glen on 7/7/08
'http://www.mcselec.com/index2.php?option=com_forum&Itemid=59&page=viewtopic&t=6157
Pcint2_isr:

   'disable overflow interrupt while we are in this routine
   Disable Ovf1

   'based on number of overflows, determine how much to add or subtract
   'if we are turning the necoder slow - add smallest increment, otherwise
   'add consecutively larger increments
   Select Case Overflow
      Case 0 : Adder = 100
      Case 1 : Adder = 50
      Case 2 : Adder = 25
      Case 3 To 4 : Adder = 10
      Case Else : Adder = 5
   End Select

   'determine direction of encoder and either add or subtract increment
   Newpins = Pind
   Direction = Oldpins.3 Xor Newpins.4
   If Direction = 1 Then
     Encset = Encset + Adder
   Else
     Encset = Encset - Adder
   End If
   Oldpins = Newpins

   'we have a new reading for display so set flag
   Flag = 1
   'now restart timer and enable interrupt for next reading
   Overflow = 0
   Timer1 = 0
   Enable Ovf1

Return


'timer 1 overflow interrupt used in pin change interrupt to determine
'amount to add or subtract to calibrate reading. This routine simply increments
'the overflow variable each time through until it reaches 5
Ovf1_isr:

   If Overflow < 5 Then
      Incr Overflow
   End If

Return


'This is the Int0 interrupt which is responsible for reading the data stream
'from the calipers.  The data consists of a clock and a data line.  We come
'to this routine when we get the first clock pulse then we use the Bascom
'shiftin routine to read the two 24 bit values that are sent from the calipers
Int0_isr:

   'first toggle heartbeat so we can see we are getting readings
   Toggle Heartbeatled
   'then shift in two readings from caliper
   Shiftin Datapin , Clockpin , Absread , 6 , 24
   Shiftin Datapin , Clockpin , Relread , 6 , 24
   'move value we want into new variable
   Newread = Relread

   'if bit 23 is set, we convert value to negative reading
   If Newread.23 = 1 Then
      Newread = Newread - 16777216
   End If

   'indicate we have a new reading
   Flag = True

   'wait for clock to go high again
   Waitus 50

   'clear any pending interrupts
   Set Eifr.intf0

Return

End