This page created with Cool Page.  Click to get your own FREE copy of Cool Page!
Encodergeek/Encodergeek
12 Jan '09
Currently the book comes with a CD ROM that contains all the support information.

There have been no questions that I need to answer at this time.

The above applies to the self published version of the book.
That chapter is now closed.
The book is now availabe from Mc Graw Hill
Here are all the programs in the book
05 Aug 09
This is all the programs in the book on Motors.

+++++++++++++++++++++++++++++++++++++++++++++++++++++

CLEAR ; clear all memory
DEFINE OSC 4; define the osc freq
LEDIDVAR BYTE ; call out the two variables LEDID and I
I   VAR BYTE; as 8 bit bytes
TRISD =%00000000; set PORTD to all outputs
;
MAINLOOP:; loop is executed forever
I=1; initialize the counter to 1
FOR LEDID = 1 TO 8; do it for the 8 LEDs
PORTD=I; puts number in PORTD
PAUSE 100 ; pause so you can see the display
I=I * 2; multiplying by 2 moves lit LED left 1 pos
NEXT LEDID; go up and increment counter
GOTO MAINLOOP; Do it all forever
END; always end with END statement


Section 04.   Program 01.  The First Program
Blinking all 8 LEDs on Port D one at a time.


CLEAR ; clear memory locations
DEFINE  OSC 4; Osc speed

LOOP: ; main loop
HIGHPORTD.0 ; turns LED connected to D0 on
PAUSE500 ; delay 0.5 seconds
LOWPORTD.0 ; turns LED connected to D0 off
PAUSE500 ; delay 0.5 seconds
GOTO LOOP; go back to Loop and repeat operation
END; all programs must end with END


Section 05.    Program 01    Controlling (blinking) an LED.
Blinking an LED (rightmost LED on bargraph)


CLEAR ; clear memory
DEFINE  OSC 4; Osc speed
LEDID  VAR BYTE ; call out the two variables
A     VAR BYTE ; as 8 bit bytes
TRISD =%00000000 ; set PORTD to all outputs
;
MAINLOOP: ; this loop is executed forever
A=1 ; initialize the counter to 1
FOR LEDID = 1 TO 8 ; do it for the 8 LEDs
PORTD=A; puts number in PORTD
PAUSE 100 ; pause so you can see the display
A=A * 2 ; multiply by 2 moves lit LED left 1 position
NEXT LEDID ; go up and increment counter
GOTO MAINLOOP ; do it all forever
END; always end with END

Section 05.    Program 02  
Blinking 8 LEDs one after the other on bargraph.



CLEAR; always start with a CLEAR statement
DEFINE  OSC 4; osc speed
TRISD = %11111100 ; set only PORTD pin 0 and 1 to outputs
XVAR BYTE ; declare x as a variable
PORTD.1=1 ; turned on LED1 to compare it to LED0
;
LOOP:; start of loop
FOR X = 1 TO 255 STEP 2; set up loop for x
PWM PORTD.0, X, 3 ; vary the duty cycle
PAUSE 200/X ; pauses longer for the dimmer values.
NEXT X; end of loop for x
GOTO LOOP; return and do it again
END; all programs with an END statement

Section 05.    Program 03
Turns on an LED and dims the one next to it



CLEAR; Define LCD registers and control bits
DEFINE  OSC  4; Osc speed]
DEFINE LCD_DREG PORTD; data register]
DEFINE LCD_RSREG PORTE ; select register ]
DEFINE LCD_RSBIT 0 ; select bit] These Defines
DEFINE LCD_EREG PORTE; enable register] are all explained
DEFINE LCD_EBIT 1 ; enable bit] in the PBP
DEFINE LCD_RWREG PORTE ; read/write register] manual.
DEFINE LCD_RWBIT 2 ; read/write bit]
DEFINE LCD_BITS 8 ; width of data path] Can be 4
DEFINE LCD_LINES 2 ; lines in display]
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds]
DEFINE LCD_DATAUS 50 ; delay in micro seconds]
;
; Set the port directions. We are setting (must set) all of PORTD and all of PORTE as outputs
; even though PORTE has only 3 lines. The other 5 lines will be ignored by the system.
;
TRISD = %00000000; set all PORTD lines to output
TRISE = %00000000; set all PORTE lines to output
; Set the Analog to Digital control register
ADCON1=%00000111; needed for the 16F877A see notes above and below
; this makes all of ports A and E digital.
LOOP:; The main loop of the program
LCDOUT $FE, 1; clear screen, go to position 1
PAUSE 250; pause 0.25 seconds
LCDOUT "HELLO"; print
LCDOUT $FE, $C0; goto second line, first position
LCDOUT "WORLD"; print
PAUSE 250; pause 0.25 seconds to see the display
GOTO LOOP; repeat
END; all programs must end in END

Section 05.   Program 04.  Displaying and blinking
“HELLO WORLD” in the LCD display



CLEAR; clear memory
DEFINE  OSC 4; osc speed
DEFINE LCD_DREG PORTD; define LCD connections
DEFINE LCD_DBIT 4; define LCD connections
DEFINE LCD_RSREG PORTE; define LCD connections
DEFINE LCD_RSBIT 0; define LCD connections
DEFINE LCD_EREG PORTE; define LCD connections
DEFINE LCD_EBIT 1; define LCD connections
ADCON1=%00000110 ; Make PORTA and PORTE digital
LOW PORTE.2 ; LCD R/W low (write) We will do no reading
PAUSE 500 ; wait for LCD to start
;
NUMB VAR BYTE; assign variable
;
TRISD = %00000000 ; D7- -D0 outputs
NUMB = %10101010 ; this is decimal 170
;
LCDOUT $FE, 1 ; Clear the LCD
LCDOUT $FE, $80, BIN8 NUMB," ",HEX NUMB, " ", DEC5 NUMB," "; display numbers
END; end program

Section 05.   Program 05
Writing to the LCD display in FULL Binary, Hexadecimal and Decimal
Note that we are not looping around the display instruction in this program


CLEAR; define LCD connections
DEFINE  OSC 4; osc speed
DEFINE LCD_DREG PORTD; define LCD connections
DEFINE LCD_DBIT 4; define LCD connections
DEFINE LCD_RSREG PORTE; define LCD connections
DEFINE LCD_RSBIT 0; define LCD connections
DEFINE LCD_EREG PORTE; define LCD connections
DEFINE LCD_EBIT 1; define LCD connections
ADCON1=%00000110 ; Make PORTA and PORTE digital
LOW PORTE.2 ; LCD R/W low (set it to write only)
PAUSE 500 ; Wait for LCD to start up
;
NUMB VAR BYTE; assign variable
;
TRISD = %00000000 ; D7 to D0 outputs
A2D_VALUE VAR BYTE ; create A2D_Value to store result
TRISA = %11111111 ; set PORTA to all input
ADCON1 = %00000010 ; set PORTA analog input
;
LCDOUT $FE, 1 ; clear the LCD
; define ADCIN parameters
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in uS
;
LOOP: ; start loop
ADCIN 0, A2D_VALUE ; Read channel 0 to A2D_Value
LCDOUT $FE, $80, "VALUE= ",  HEX2  A2D_VALUE, " ", DEC5  A2D_VALUE
LCDOUT $FE,  $C0,  BIN8 A2D_VALUE ;
PORTD=A2D_VALUE ; the pause must come right after setting
; PORTD and before PORTD is used again
PAUSE 250 ; try setting PORTD before the LCDOUT
GOTO LOOP ; do it forever
END; end program

Section 05.   Program 06   Displaying the potentiometer
wiper position on the LCD and the LED bargraph

CLEAR; clear memory
DEFINE  OSC 4; osc speed
PWM PORTC.2, 127, 100; beep command
END; end the program

Section 05      Program 07
Generates a short tone on the piezo speaker.


CLEAR
DEFINE OSC  4; Osc speed
TRISD = %11111110 ; Set only PORTD pin 1 to output
XVAR BYTE; Declare x as a variable
;
LOOP:; start loop
FOR  X = 0 TO 255 STEP 5; ]  in this loop the value
PWM PORTD.0,  X,  3; ]  x represents the brightness
NEXT X; ]  of the LED at PORTD.0
GOTO LOOP; repeat loop
END; end program

Section 05    Program 08
LED dimming using the PWM command.

CLEAR; clears memory
DEFINE OSC 4; osc speed
DEFINE CCP1_REG PORTC ; Port to be used by HPWM 1
DEFINE CCP1_BIT 2 ; Pin to be used by HPWM 1
; since no timer is defined, Timer1 will be used;
HPWM 1,127,2500 ; the tone command
PAUSE 100 ; pause .1 sec to hear tone
END ; end program to stop tone.

Section 05    Program 09
Generates a tone on the piezo speaker.



CLEAR; clear memory
DEFINE OSC 4; osc speed
DTMFOUT PORTC.2, [5, 5, 5, 1, 2, 1, 2] ; telephone tones
END; end program

Section 05    Program 10
Generates telephone key tones on the piezo speaker. (555-1212)

CLEAR; clear memory
DEFINE OSC 4; osc speed
DEFINE LCD_DREG PORTD ; define LCD connections
DEFINE LCD_DBIT 4; define LCD connections
DEFINE LCD_RSREG PORTE; define LCD connections
DEFINE LCD_RSBIT 0; define LCD connections
DEFINE LCD_EREG PORTE; define LCD connections
DEFINE LCD_EBIT 1; define LCD connections
POS VAR WORD ; servo position variable
CENTERPOS  VAR WORD ; servo position variable
MAXPOSVAR WORD; servo position variable
MINPOSVAR WORD; servo position variable
POSSTEPVAR BYTE; servo position step variable
SERVO1VAR PORTC.1 ; alias servo pin   Use J7 for servo
POS=0; set variables
CENTERPOS =1540; set variables
MAXPOS =2340; set variables
MINPOS =740; set variables
POSSTEP =5; set variables
ADCON1 = %00000111 ; PORTA and PORTE to digital
LOW PORTE.2 ; LCD R/W low = write
PAUSE 100 ; wait for LCD to startup
OPTION_REG = $01111111 ; enable PORTB pullups
LOW SERVO1 ; servo output low
GOSUB CENTER ; center servo
LCDOUT $FE, 1; clears screen only
;
MAINLOOP: ; main program loop
PORTB = 0 ; PORTB lines low to read buttons
TRISB = $11111110 ; enable first button row
IF PORTB.4 = 0 THEN GOSUB LEFT; check if any button pressed to move servo
IF PORTB.5 = 0 THEN GOSUB CENTER;
IF PORTB.6 = 0 THEN GOSUB RIGHT;
LCDOUT $FE, $80, "POSITION = ", DEC POS , " ";
SERVO1 = 1 ; start servo pulse
PAUSEUS POS;
SERVO1 = 0 ; end servo pulse
PAUSE 16 ; servo update rate about 60 Hz
GOTO MAINLOOP ; do it all forever
;
LEFT: ; move servo left
IF POS < MAXPOS THEN POS = POS + POSSTEP;
RETURN;
;
RIGHT: ; move servo right
IF POS > MINPOS THEN POS = POS – POSSTEP;
RETURN;
;
CENTER: ; center servo
POS = CENTERPOS;
RETURN;
END; end program

Section 05     Program 11
Servo Position Control for an R/C servo from PORTB buttons.
This program uses a servo on Jumper J7


CLEAR;
DEFINE  OSC  4; Osc speed
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
DEFINE ADC_BITS 8 ; Set number of bits in result
DEFINE ADC_CLOCK 3 ; Set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; Set sampling time in uS
TRISA     = %11111111 ; Set PORTA to all input
TRISD = %00000000 ; set all PORTD lines to outputs
ADCON1 = %00000111; PORTA and PORTE to digital
LOW PORTE.2 ; LCD R/W line low (W)
A2D_VALUEVAR BYTE ; Create A2D_Value to store result
A2D_VALUE1VAR BYTE ; Create A2D_Value to store result
A2D_VALUE2VAR BYTE ; Create A2D_Value to store result
ADWALWASVAR BYTE;
POSVARWORD ; Servo positions
CENTERPOSVAR WORD;
MAXPOSVAR WORD;
MINPOSVAR WORD;
POSSTEPVAR BYTE;
PAUSE 500 ; Wait .5 second
SERVO1   VAR    PORTC.1 ; Alias servo pin
ADCIN 0, A2D_VALUE ; Read channel 0 to A2D_Value
OPTION_REG = $7F ; Enable PORTB pull ups
LOW SERVO1 ; Servo output low
GOSUB CENTER ; Center servo
LCDOUT $FE, 1 ; Clears screen only
PORTB = 0 ; PORTB lines low to read buttons
TRISB = %11111110 ; Enable first button row
;
MAINLOOP:; Main program loop
; Check any button pressed to move servo
IF PORTB.4 = 0 THEN GOSUB LEFT;
IF PORTB.5 = 0 THEN GOSUB CENTER;
IF PORTB.6 = 0 THEN GOSUB RIGHT ;
ADCIN 0, A2D_VALUE ; Read channel 0 to A2D_Value
ADCIN 1, A2D_VALUE1 ; Read channel 1 to A2D_Value 1
ADCIN 3, A2D_VALUE2 ; Read channel 2 to A2D_Value 2
MAXPOS =2350 –127 + A2D_VALUE1;
MINPOS =750 +127-A2D_VALUE1;
CENTERPOS=POS-127 + A2D_VALUE;
POSSTEP =A2D_VALUE2/13 +1;
SERVO1 = 1 ; Start servo pulse
PAUSEUS POS;
SERVO1 = 0 ; End servo pulse
LCDOUT $FE, $80, "POS=", DEC POS-127 + A2D_VALUE , " ",DEC A2D_VALUE," ",_
DEC A2D_VALUE1," " ,DEC POSSTEP," ";
PAUSE 1 ; Servo update rate about 60 Hz
GOTO MAINLOOP ; Do it all forever
;
LEFT: ; Move servo left
IF POS < MAXPOS THEN POS = POS + POSSTEP
RETURN;
;
RIGHT: ; Move servo right
IF POS > MINPOS THEN POS = POS - POSSTEP
RETURN;
;
CENTER: ; Center servo
POS = 1540-127 + A2D_VALUE ;
RETURN;
;
END; end program

Section 05.     Program 12.    Use servo on J7
Servo position control, with added functions


CLEAR; clear memory
DEFINE  OSC  4; osc speed
TRISB = %11110000 ; set the PORTB directions
PORTB = %11111110 ; Set only B0 made low.
; See page 31 of the data sheet for setting the pull ups
; on PORTB
OPTION_REG.7=0 ; bit 7 of the OPTION_REG sets the pull ups
; when cleared
TRISD = %11111110 ; set only PORTD.0 to an output.
PORTD.0=0 ; initialize this LED to OFF
;
MAINLOOP:;
IF PORTB.4=1 THEN ; check for first column being low
PORTD.0=0 ; if it is low turn D0 OFF
ELSE;
PORTD.0=1 ; if not turn it ON
ENDIF;
GOTO MAINLOOP ; repeat.
END;

Section 05.     Program 13.     Reading a switch
Program reads SW1 and turns LED on PORTD.0 ON while it is down.


CLEAR; Clear
DEFINE  OSC  4; Osc speed
DEFINE LCD_DREG PORTD; LCD defines
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
ADCON1 = %00000111; Make PORTA and PORTE digital
LOW PORTE.2 ; LCD R/W low (write)
PAUSE 500 ; Wait for LCD to start up
;
READING   VAR BYTE; Define the variables
ALPHA       VAR BYTE;
BUFFER     VAR BYTE;
; Set up port B pull ups
OPTION_REG.7 = 0 ; Enable PORTB pull ups to make B4-B7 high
TRISB = %11110000 ; Make B7-B4 inputs, B3-B0 outputs
BUFFER=%11111111 ; No key has been pressed for display
; Set up the initial LCD readings
LCDOUT $FE, 1; Clear the LCD
LCDOUT $FE, $C0, "ROW=",BIN4 (BUFFER & $0F)," COL=", BIN4 BUFFER >>4
;
LOOP: ;
PORTB =%00001110 ; Set line B0 low so we can read row 1 only
FOR ALPHA = 1 TO 4 ; Need to look at 4 rows
LCDOUT $FE, $80, BIN8 PORTB," SCANVIEW B"; see bits scanned
IF (PORTB & $F0)<>$F0 THEN ; As soon as one of the bits in B4 to B7 changes we
; immediately have to store the value of PORTB
BUFFER =PORTB; in a safe place.
GOSUB SHOWKEYPRESS;
ELSE;
ENDIF;
PAUSE 50; this pause lets us see the scanning but it also means
; that you have to hold a key down for over 50 usecs to
; have it register. This pause can be removed after you
; have seen the bits scanning on the LCD
PORTB= PORTB <<1 ; move bits left one place for next line low
PORTB= PORTB + 1 ; put 1 back in LSBit, the right bit
NEXT ALPHA ;
GOTO LOOP;
;
SHOWKEYPRESS:;
LCDOUT $FE, $C0, "ROW=", BIN4  (BUFFER & $0F)," COL=", BIN4  BUFFER >>4
RETURN;
END;

Section 05.     Program 14.     Read Keyboard
Reading the keyboard rows and columns


CLEAR; clear memory
DEFINE  OSC  4; osc speed
DEFINE LCD_DREG PORTD; define LCD connections
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
ADCON1 = 7 ; make PORTA and PORTE digital
LOW PORTE.2 ; LCD R/W low (write)
PAUSE 200 ; wait for LCD to start
; define the variables
BUFFER      VAR BYTE;
ALPHA        VAR BYTE; counter for rows
COLUMN    VAR BYTE;
ROW           VAR BYTE;
SWITCH      VAR BYTE;
; set up port b pull ups
OPTION_REG.7 = 0 ; enable PORTB pullups to make B4-B7 high
TRISB = %11110000 ; make B7-B4 inputs, B3-B0 outputs
; set up the initial LCD readings
LCDOUT $FE, 1 ; clear the LCD
LOOP: ;
PORTB =%00001110 ; set line B0 low so we can read row 1 only
FOR ALPHA = 1 TO 4 ; need to look at 4 rows
IF (PORTB & $F0)<>$F0 THEN ;
; as soon as one of the bits in B4 to B7 changes we
; immediately
; have to store the value of PORTB in a safe place.
BUFFER =PORTB;
GOSUB SHOWKEYPRESS;
ELSE;
ENDIF;
PORTB= PORTB <<1 ; move bits left one place for next line low
PORTB= PORTB + 1 ; put 1 back in LSBit, the right bit
NEXT ALPHA ;
GOTO LOOP;
;
SHOWKEYPRESS:;
BUFFER = BUFFER ^ %11111111 ; flips all the bits in the buffer
; print the first line
LCDOUT $FE, $80, "ROW=",BIN4 (BUFFER & $0F)," COL=", BIN4 BUFFER >>4
COLUMN=(NCD BUFFER) –4; calculate column
ROW=NCD (BUFFER &$0F) ; calculate row
SWITCH=((ROW-1) * 4) +COLUMN; calculate switch number
; print the second line
LCDOUT $FE, $C0, "ROW=", DEC ROW, " COL=", DEC COLUMN, " SW=", DEC SWITCH, " "
RETURN;
END;

Section 05.    Program 15.    Reading the Keyboard
Reading the keyboard rows and columns and show key number

DEFINE  OSC  4; osc speed
LOOP:; begin loop
ADCON0.2 = 1; start conversion
NOTDONE: ; marker if not done
PAUSE 5;
IF ADCON0.2 = 1 THEN NOTDONE ; wait for low on bit-2 of ADCON0, conversion finished
A2D_VALUE = ADRESH ; move high byte of result to A2D_Value
LCDOUT $FE, 1 ; clear screen
LCDOUT "VALUE: ", DEC A2D_VALUE,"  " ; display the decimal value
PAUSE 100 ; wait 0.1 second
GOTO LOOP; do it forever


;The complete program would look like this:;
DEFINE LCD_DREG PORTD; define LCD registers and bits
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
A2D_VALUE VAR BYTE ; create A2D_Value to store result
;
TRISA = %11111111 ; wet PORTA to all input
TRISD = %00000000; wet PORTD to all output
ADCON0 = %11000001 ; configure and turn on A/D Module
ADCON1 = %00000010 ; set PORTA analog and LEFT justify result
PAUSE 500 ; wait 0.5 second for LCD startup
;
LOOP: ;
ADCON0.2 = 1 ; start conversion
NOTDONE: ;
IF ADCON0.2 = 1 THEN NOTDONE ; wait for low on bit-2 of ADCON0,
; conversion finishes
A2D_VALUE = ADRESH ; move high byte of result to A2D_Value
LCDOUT $FE, 1 ; clear screen
LCDOUT "DEC VALUE= ", DEC A2D_VALUE," "; Display 3 values
LCDOUT $FE, $C0, "HEX=", HEX2 A2D_VALUE," ","BIN=", BIN8 A2D_VALUE," "
PORTD=A2D_VALUE ; displays value in bargraph
PAUSE 100 ; wait .1 second
GOTO LOOP ; do it forever
END; end program

Section 05.     Program 16.   Potentiometer readings
Displaying the value of potentiometer in all formats

CLEAR; define LCD connections
DEFINE  OSC  4; osc speed
DEFINE LCD_DREG PORTD;
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
LOW PORTE.2 ; LCD R/W line low (W)
PAUSE 500 ; wait .5 second for LCD
; the next 3 defines are needed for
; the ADCIN command
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; Set internal clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; Set sampling time in uS
;
TRISA = %11111111 ; Set PORTA to all input
TRISD = %00000000 ; set all PORTD lines to outputs
ADCON1 = %00000110; PORTA and PORTE to digital
A2D_Value0 VAR BYTE; Create A2D_Value to store result 1
A2D_Value1 VAR BYTE ; Create A2D_Value to store result 2
A2D_Value2 VAR BYTE ; Create A2D_Value to store result 3
;
LCDOUT $FE, 1 ; clear the display
;
MAINLOOP: ; main program loop
; check potentiometer values
ADCIN 0, A2D_VALUE0 ; read channel 0 to A2D_Value0
ADCIN 1, A2D_VALUE1 ; read channel 1 to A2D_Value1
ADCIN 3, A2D_VALUE2 ; read channel 2 to A2D_Value2
LCDOUT $FE, $80, DEC A2D_VALUE0," ",DEC A2D_VALUE1," " ,DEC A2D_VALUE2," ";
PAUSE 10 ;
GOTO MAINLOOP ; do it all forever
END; end program

Section 05     Program 17     Display potentiometer settings
Reading and displaying all three potentiometers values in decimal format

CLEAR ;
DEFINE  OSC  4; osc speed
DEFINE LCD_DREG PORTD ; define LCD connections
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
LOW PORTE.2 ; LCD R/W line low (W)
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in uS
TRISA   = %11111111 ; set PORTA to all input
TRISD   = %00000000 ; set all PORTD lines to outputs
ADCON1  = %00000111 ; PORTA and PORTE to digital
A2D_VALUE  VAR   BYTE; create A2D_Value to store result
A2D_VALUE1 VAR   BYTE ; create A2D_Value1 to store result
A2D_VALUE2 VAR   BYTE ; create A2D_Value2 to store result
ADWALWAS VAR  BYTE;
POSVAR  WORD ; servo positions
CENTERPOS VAR  WORD ;
MAXPOSVAR  WORD;
MINPOSVAR  WORD;
POSSTEPVAR  BYTE;
PAUSE 500 ; wait .5 second
SERVO1VAR  PORTC.1 ; alias servo pin
ADCIN 0, A2D_VALUE ; read channel 0 to A2D_Value
OPTION_REG = $01111111 ; enable PORTB pullups
LOW SERVO1 ; servo output low
GOSUB CENTER ; center servo
LCDOUT $FE, 1 ; clears screen only
PORTB  = 0 ; PORTB lines low to read buttons
TRISB    = %11111110 ; enable first button row
; main program loop
MAINLOOP:; check any button pressed to move servo
IF PORTB.4 = 0 THEN GOSUB LEFT;
IF PORTB.5 = 0 THEN GOSUB CENTER;
IF PORTB.6 = 0 THEN GOSUB RIGHT ;
ADCIN 0, A2D_VALUE ; Read channel 0 to A2D_Value
ADCIN 1, A2D_VALUE1 ; Read channel 1 to A2D_Value 1
ADCIN 3, A2D_VALUE2 ; Read channel 2 to A2D_Value 2
MAXPOS =1500 + A2D_VALUE1*3;
MINPOS =1500 - A2D_VALUE1*3;
CENTERPOS=1500+3*(A2D_VALUE-127) ;
POSSTEP =A2D_VALUE2/10 +1;
SERVO1 = 1 ; Start servo pulse
PAUSEUS POS;
SERVO1 = 0 ; End servo pulse
LCDOUT $FE, $80, "POS=", DEC POS , " "`;
LCDOUT $FE, $C0, DEC A2D_VALUE," ",DEC A2D_VALUE1," " ,DEC POSSTEP," "
PAUSE 10 ; Servo update rate about 60 Hz
GOTO MAINLOOP ; Do it all forever
; Move servo left
LEFT: IF POS < MAXPOS THEN POS = POS +  POSSTEP
RETURN;
; Move servo right
RIGHT: IF POS > MINPOS THEN POS = POS - POSSTEP
RETURN;
; Center servo
CENTER: POS = 1500+3*(A2D_VALUE-127) ;
RETURN;
END; end program

Section 05.    Program 18.    Servo/Potentiometers
Three Potentiometers controlling one servo
Connect the servo to Jumper J7 for this program

CLEAR; clears all memory locations
DEFINE OSC 4; using a 4 MHz oscillator here
TRISD = %11110000; make D0 to D3 outputs
PORTD.0 = 0; turn off bit D0
PORTD.1 = 1; turn on bit D1
ALPHA     VAR     WORD; Set up a variable for counting
;
MAINLOOP:; main loop
IF PORTD.1 = 0 THEN; the next lines of code turn the LEDs ON
PORTD.1 = 1; if they are OFF
PORTD.0 = 0;
ELSE ;
PORTD.1 = 0; and OFF if they
PORTD.0 = 1; are ON
ENDIF;
FOR ALPHA = 1 TO 300; this loop replaces a long pause command
PAUSEUS 100; with short pauses that are essentially
NEXT ALPHA; independent of the clock frequency.
GOTO MAINLOOP; do it all forever
END; all  programs need to end with END

Section 06.     Program 01
Foreground Program blinks two LEDs alternately.
No timer is being used in this program at this time.

CLEAR ; clear memory
DEFINE OSC 4; using a 4 MHz oscillator
;
OPTION_REG=%10000101; page 48 of data sheet
; bit 7=1 disable pull ups on PORTB
; bit 5=0 selects timer mode
; bit 2=1  }
; bit 1=0  } sets Timer0 pre-scaler to 64
; bit 0=1  }
;
INTCON=%10100000; bit 7=1 Enables all unmasked interrupts
; bit 5=1 Enables Timer0 overflow interrupt
; bit 2 flag will be set on interrupt and has to be cleared
; in the interrupt routine.  It is set clear to start with.
ALPHA    VAR  WORD; this variable counts in the PauseUS loop
BETA       VAR  BYTE; this variable counts the 61 interrupt ticks
TRISD   = %11110100; sets the 3 output pins in the D port
PORTD = %00000000; sets all pins low in the D port
BETA=0;
ON INTERRUPT GOTO INTERUPTROUTINE; this line needs to be early in the program, before
; the routine is called in any case.

MAINLOOP:; main loop blinks D0 and D1 alternately
IF PORTD.1 = 0 THEN; ]
PORTD.1 = 1; ]
PORTD.0 = 0; ]  this part of the program blinks two LEDs in
ELSE; ]   the foreground as described before
PORTD.1 = 0; ]
PORTD.0 = 1; ]
ENDIF; ]
;
FOR  ALPHA = 1 TO 300; the long pause is eliminated with this loop
PAUSEUS 100; PAUSE command with short latency
NEXT ALPHA;
GOTO MAINLOOP; end of loop
;
DISABLE; DISABLE and ENABLE must bracket the interrupt routine
INTERUPTROUTINE:; this information is used by the compiler only.
BETA = BETA + 1;
IF BETA < 61 THEN ENDINTERRUPT; one second has not yet passed
BETA = 0;
IF PORTD.3 = 1 THEN; interrupt loop turns D3 on and off every
PORTD.3 = 0; 61 times through the interrupt routine.
ELSE; That is about one second per full cycle
PORTD.3 = 1;
ENDIF;
ENDINTERRUPT:;
INTCON.2 = 0; clears the interrupt flag.
RESUME; resume the main program
ENABLE; DISABLE and ENABLE must bracket the int. routine
END; end program


Section 06.     Program 02.     Using TIMER0
Programs blinks two LEDs (D1 and D0) alternately and blinks
a third LED (D2) for one second ON and one second OFF as
controlled by the interrupt signal.
; LCD clock program using On Interrupt
; Uses TMR0 and prescaler.  Watchdog Timer should be
; set to off at program time and Nap and Sleep should not be used.
; Buttons may be used to set hours and minutes

DEFINE  LCD_DREG      PORTD; Define LCD connections
DEFINE  LCD_DBIT        4;
DEFINE  LCD_RSREG   PORTE;
DEFINE  LCD_RSBIT      0;
DEFINE  LCD_EREG      PORTE;
DEFINE  LCD_EBIT        1;
;
HOUR       VAR     BYTE; Define hour variable
DHOUR     VAR     BYTE; Define display hour variable
MINUTE    VAR     BYTE; Define minute variable
SECOND  VAR     BYTE; Define second variable
TICKS       VAR     BYTE; Define pieces of seconds variable
UPDATE   VAR     BYTE; Define variable to indicate update of LCD
I                VAR     BYTE; De bounce loop variable
ADCON1 = %00000111; parts of PORTA and E made digital
LOW PORTE.2; LCD R/W low = write
PAUSE 100; Wait for LCD to startup
HOUR      = 0; Set initial time to 00:00:00
MINUTE   = 0;
SECOND = 0;
TICKS      = 0;
UPDATE  = 1; Force first display
; Set TMR0 to interrupt every 16.384 milliseconds
OPTION_REG = %01010101; Set TMR0 configuration and enable PORTB pullups
INTCON           = %10100000; Enable TMR0 interrupts
ON INTERRUPT GOTO TICKINT;
; Main program loop –
MAINLOOP:; in this case, it only updates the LCD
TRISB = %11110000; Enable all buttons
PORTB =%00000000; PORTB lines low to read buttons
; Check any button pressed to set time
IF PORTB.7 = 0 THEN DECMIN;
IF PORTB.6 = 0 THEN INCMIN      ; Last 2 buttons set minute
IF PORTB.5 = 0 THEN DECHR;
IF PORTB.4 = 0 THEN INCHR       ;
; First 2 buttons set hour
CHKUP:  IF UPDATE = 1 THEN; Check for time to update screen
LCDOUT $FE, 1   ; Clear screen
; Display time as hh:mm:ss
DHOUR = HOUR    ; Change hour 0 to 12
IF (HOUR // 12) = 0 THEN;
     DHOUR = DHOUR + 12;
ENDIF;
;
IF HOUR < 12 THEN; Check for AM or PM
     LCDOUT DEC2 DHOUR, ":", DEC2 MINUTE, ":", DEC2 second, " AM"
     ELSE;
        LCDOUT DEC2 (DHOUR - 12), ":", DEC2 MINUTE, ":", DEC2 SECOND, " PM"
    ENDIF;
    UPDATE = 0      ; Screen updated
ENDIF;
GOTO MAINLOOP  ; Do it all forever
; Increment minutes
INCMIN: MINUTE = MINUTE + 1;
IF MINUTE >= 60 THEN;
    MINUTE = 0;
ENDIF;
GOTO DEBOUNCE;
; Increment hours
INCHR:  HOUR = HOUR + 1;
IF HOUR >= 24 THEN;
    HOUR = 0;
ENDIF;
GOTO DEBOUNCE;
; Decrement minutes
DECMIN: MINUTE = MINUTE - 1;
IF MINUTE >= 60 THEN;
    MINUTE = 59;
ENDIF;
GOTO DEBOUNCE;
; Decrement hours
DECHR:  HOUR = HOUR - 1;
IF HOUR >= 24 THEN;
    HOUR = 23;
ENDIF;
; De bounce and delay for 250 ms
DEBOUNCE: FOR I = 1 TO 25;
    PAUSE 10; 10 ms at a time so no interrupts are lost
NEXT I;
UPDATE = 1; Set to update screen
GOTO CHKUP;
; Interrupt routine to handle each timer tick
DISABLE; Disable interrupts during interrupt handler
TICKINT: TICKS = TICKS + 1; Count pieces of seconds
IF TICKS < 61 THEN TIEXIT; 61 ticks per second (16.384 ms per tick)
; One second elapsed - update time
TICKS = 0;
SECOND = SECOND + 1;
IF SECOND >= 60 THEN;
SECOND = 0;
MINUTE = MINUTE + 1;
IF MINUTE >= 60 THEN;
MINUTE = 0;
HOUR = HOUR + 1;
IF HOUR >= 24 THEN;
HOUR = 0;
ENDIF;
ENDIF;
ENDIF;
UPDATE = 1; Set to update LCD
TIEXIT: INTCON.2 = 0; Reset timer interrupt flag
RESUME;
END;

Section 06.     Program 03.     Timer0 usage per meLabs program
Hours, Seconds and Minutes digital Clock


; LCD clock program using On Interrupt
; Uses TMR0 and prescaler.  Watchdog Timer should be
; set to off at program time and Nap and Sleep should not be used.
; Buttons may be used to set hours and minutes

DEFINE  LCD_DREG      PORTD; Define LCD connections
DEFINE  LCD_DBIT        4;
DEFINE  LCD_RSREG   PORTE;
DEFINE  LCD_RSBIT      0;
DEFINE  LCD_EREG      PORTE;
DEFINE  LCD_EBIT        1;
;
HOUR       VAR     BYTE; Define hour variable
DHOUR     VAR     BYTE; Define display hour variable
MINUTE    VAR     BYTE; Define minute variable
SECOND  VAR     BYTE; Define second variable
TICKS       VAR     BYTE; Define pieces of seconds variable
UPDATE   VAR     BYTE; Define variable to indicate update of LCD
I                VAR     BYTE; De bounce loop variable
ADCON1 = %00000111; parts of PORTA and E made digital
LOW PORTE.2; LCD R/W low = write
PAUSE 100; Wait for LCD to startup
HOUR      = 0; Set initial time to 00:00:00
MINUTE   = 0;
SECOND = 0;
TICKS      = 0;
UPDATE  = 1; Force first display
; Set TMR0 to interrupt every 16.384 milliseconds
OPTION_REG = %01010101; Set TMR0 configuration and enable PORTB pullups
INTCON           = %10100000; Enable TMR0 interrupts
ON INTERRUPT GOTO TICKINT;
; Main program loop –
MAINLOOP:; in this case, it only updates the LCD
TRISB = %11110000; Enable all buttons
PORTB =%00000000; PORTB lines low to read buttons
; Check any button pressed to set time
IF PORTB.7 = 0 THEN DECMIN;
IF PORTB.6 = 0 THEN INCMIN      ; Last 2 buttons set minute
IF PORTB.5 = 0 THEN DECHR;
IF PORTB.4 = 0 THEN INCHR       ;
; First 2 buttons set hour
CHKUP:  IF UPDATE = 1 THEN; Check for time to update screen
LCDOUT $FE, 1   ; Clear screen
; Display time as hh:mm:ss
DHOUR = HOUR    ; Change hour 0 to 12
IF (HOUR // 12) = 0 THEN;
     DHOUR = DHOUR + 12;
ENDIF;
;
IF HOUR < 12 THEN; Check for AM or PM
     LCDOUT DEC2 DHOUR, ":", DEC2 MINUTE, ":", DEC2 second, " AM"
     ELSE;
        LCDOUT DEC2 (DHOUR - 12), ":", DEC2 MINUTE, ":", DEC2 SECOND, " PM"
    ENDIF;
    UPDATE = 0      ; Screen updated
ENDIF;
GOTO MAINLOOP  ; Do it all forever
; Increment minutes
INCMIN: MINUTE = MINUTE + 1;
IF MINUTE >= 60 THEN;
    MINUTE = 0;
ENDIF;
GOTO DEBOUNCE;
; Increment hours
INCHR:  HOUR = HOUR + 1;
IF HOUR >= 24 THEN;
    HOUR = 0;
ENDIF;
GOTO DEBOUNCE;
; Decrement minutes
DECMIN: MINUTE = MINUTE - 1;
IF MINUTE >= 60 THEN;
    MINUTE = 59;
ENDIF;
GOTO DEBOUNCE;
; Decrement hours
DECHR:  HOUR = HOUR - 1;
IF HOUR >= 24 THEN;
    HOUR = 23;
ENDIF;
; De bounce and delay for 250 ms
DEBOUNCE: FOR I = 1 TO 25;
    PAUSE 10; 10 ms at a time so no interrupts are lost
NEXT I;
UPDATE = 1; Set to update screen
GOTO CHKUP;
; Interrupt routine to handle each timer tick
DISABLE; Disable interrupts during interrupt handler
TICKINT: TICKS = TICKS + 1; Count pieces of seconds
IF TICKS < 61 THEN TIEXIT; 61 ticks per second (16.384 ms per tick)
; One second elapsed - update time
TICKS = 0;
SECOND = SECOND + 1;
IF SECOND >= 60 THEN;
SECOND = 0;
MINUTE = MINUTE + 1;
IF MINUTE >= 60 THEN;
MINUTE = 0;
HOUR = HOUR + 1;
IF HOUR >= 24 THEN;
HOUR = 0;
ENDIF;
ENDIF;
ENDIF;
UPDATE = 1; Set to update LCD
TIEXIT: INTCON.2 = 0; Reset timer interrupt flag
RESUME;
END;

Section 06.     Program 03.     Timer0 usage per meLabs program
Hours, Seconds and Minutes digital Clock
; Set p the LCD
CLEAR  ;
DEFINE OSC 4;
DEFINE LCD_DREG PORTD; lcd is on PORTD
DEFINE LCD_DBIT 4; we will use 4 bit protocol
DEFINE LCD_RSREG PORTE; register select register
DEFINE LCD_RSBIT 0; register select bit
DEFINE LCD_EREG PORTE; enable Register
DEFINE LCD_EBIT 1; enable bit
PORTE.2 = 0; set for write mode
PAUSE 500; wait .5 second
;
; Next let us define the variables we will be using
ADVALVARBYTE; Create adval to store result
TICKSVARWORD;
TENTHSVARBYTE;
SECSVARWORD;
MINSVARBYTE;
;
; Set the variable to specific values, not necessary in this program but a formality for clarity
TICKS = 0;
TENTHS = 0;
SECS = 0;
MINS = 0;
;
; Set the registers that will control the work.  This is the nitty gritty of it so we will call out each bit.
; INTCON is the interrupt control register.
;
INTCON=%11000000
; bit 7: GIE: Global Interrupt Enable bit, this has to be set for any interrupt to work.
; 1 = Enables all un-masked interrupts
; 0 = Disables all interrupts
; bit 6: PEIE: Peripheral Interrupt Enable bit
; 1 = Enables all un-masked peripheral interrupts
; 0 = Disables all peripheral interrupts
; bit 5: T0IE: TMR0 Overflow Interrupt Enable bit
; 1 = Enables the TMR1 interrupt
; 0 = Disables the TMR1 interrupt
; bit 4: INTE: RB0/INT External Interrupt Enable bit
; 1 = Enables the RB0/INT external interrupt
; 0 = Disables the RB0/INT external interrupt
; bit 3: RBIE: RB Port Change Interrupt Enable bit
; 1 = Enables the RB port change interrupt
; 0 = Disables the RB port change interrupt
; bit 2: T0IF: TMR0 Overflow Interrupt Flag bit
; 1 = TMR0 register has overflowed (must be cleared in software)
; 0 = TMR0 register did not overflow
; bit 1: INTF: RB0/INT External Interrupt Flag bit
; 1 = The RB0/INT external interrupt occurred (must be cleared in software)
; 0 = The RB0/INT external interrupt did not occur
; bit 0: RBIF: RB Port Change Interrupt Flag bit
; 1 = At least one of the RB7:RB4 pins changed state (must be cleared in software)
; 0 = None of the RB7:RB4 pins have changed state
;
; T1CON is the timer 1 control register. 
T1CON=%00000001
; bit 7-6: Unimplemented: Read as ’0’
; bit 5-4: T1CKPS1:T1CKPS0: Timer1 Input Clock Prescale Select bits
; 11 = 1:8 Prescale value
; 10 = 1:4 Prescale value
; 01 = 1:2 Prescale value
; 00 = 1:1 Prescale value
; bit 3: T1OSCEN: Timer1 Oscillator Enable Control bit
; 1 = Oscillator is enabled
; 0 = Oscillator is shut off (The oscillator inverter is turned off to eliminate power drain)
; bit 2:T1SYNC: Timer1 External Clock Input Synchronization Control bit
; TMR1CS = 1
; 1 = Do not synchronize external clock input
; 0 = Synchronize external clock input
; TMR1CS = 0
; This bit is ignored. Timer1 uses the internal clock when TMR1CS = 0.
; bit 1: TMR1CS: Timer1 Clock Source Select bit
; 1 = External clock from pin RC0/T1OSO/T1CKI (on the rising edge)
; 0 = Internal clock (FOSC/4)
; bit 0: TMR1ON: Timer1 On bit
; 1 = Enables Timer1
; 0 = Stops Timer1
;
; The option register
OPTION_REG= %00000000; Set Bit 7 to 0 and enable PORTB pullups
; All other bits are for Timer 0 and not applicable here
PIE1=%00000001; See data sheet, enables interrupt.
ADCON0= %11000001; Configure and turn on A/D Module
; bit 7-6: ADCS1: ADCS0:; A/D Conversion Clock Select bits
; 00 = FOSC/2
; 01 = FOSC/8
; 10 = FOSC/32
; 11 = FRC (clock derived from an RC oscillation)
; bit 5-3: CHS2:CHS0: Analog Channel Select bits
; 000 = channel 0, (RA0/AN0)
; 001 = channel 1, (RA1/AN1)
; 010 = channel 2, (RA2/AN2)
; 011 = channel 3, (RA3/AN3)
; 100 = channel 4, (RA5/AN4)
; 101 = channel 5, (RE0/AN5)(1)
; 110 = channel 6, (RE1/AN6)(1)
; 111 = channel 7, (RE2/AN7)(1)
; bit 2: GO/DONE: A/D Conversion Status bit
; If ADON = 1   See bit 0
; 1 = A/D conversion in progress (setting this bit starts the A/D conversion)
; 0 = A/D conversion not in progress (This bit is automatically cleared by hardware when the ; A/D conversion is complete)
; bit 1: Unimplemented: Read as ; 0;
; bit 0: ADON: A/D On bit
; 1 = A/D converter module is operating
; 0 = A/D converter module is shutoff and consumes no operating current
;
; The A to D control Register for Port A is ADCON1
ADCON1= %00000010; Set part of PORTA analog
; The relevant table is on page 112 of the data sheet
; There are a number of choices which give us analog capabilities on PORTA.0 and allow the voltage ; reference between Vdd and Vss.  We have chosen 0010 on the third line down in the table
;
; Next let us set up the port pin directions
TRISA = %11111111; Set PORTA to all input
TRISB = %11110000; Set up PORTB  for keyboard reads
PORTB.0 = 0; set so we can read row 1 only for now
;
ON INTERRUPT GOTO TICKINT; Tells the program where to go on interrupt
;
; Initialize display and write to top line
LCDOUT $FE, 1, $FE, $80, "MM SS T";
;
MAINLOOP:;
ADCON0.2 = 1; Conversion to reads POT-1. Conversion start now and
; takes place during loop.  If loop was short we would allow
; for that.
; Then check the buttons to decide what to do
IF PORTB.4  = 0 THEN STARTCLOCK;
IF PORTB.5  = 0 THEN STOPCLOCK;
IF PORTB.6  = 0 THEN CLEARCLOCK;
;
; and display what the clock status is
LCDOUT $FE, $80, DEC2 MINS, ":",DEC2 SECS,  ":", DEC TENTHS, "  POT1=",DEC ADVAL, "  "

; We are now ready to read what potentiometer setting is.
ADVAL = ADRESH; we assumed that enough time has passed to have an
; updated value in the registers.  If not add wait here.
GOTO MAINLOOP; Do it again
;
DISABLE; Disable interrupts during interrupt handler
TICKINT: ;
TICKS = TICKS + 1; ticks are influenced by the setting of POT-1
IF TICKS < 5 THEN TIEXIT; arbitrary value to get one second
TICKS = 0;
;
TENTHS = TENTHS + 1;
IF TENTHS <9 THEN TIEXIT;
TENTHS = 0;
;
SECS = SECS + 1; update seconds
IF SECS < 59 THEN TIEXIT;
SECS = 0;
MINS = MINS + 1; update minutes
TIEXIT:;
IF PORTB.5 = 0 THEN STOPCLOCK;
TMR1H=ADRESH;
PIR1=0;
RESUME; go back to the main routine
ENABLE;
;
DISABLE;
STARTCLOCK:;
INTCON = %10100011; Enable TMR1 interrupts
TICKS = 0;
GOTO MAINLOOP;
;
STOPCLOCK:;
INTCON = %10000011; Disable TMR1 interrupts
PAUSE 2;
TICKS = 0;
GOTO MAINLOOP;
;
CLEARCLOCK:;
INTCON = %10000011; Disable TMR1 interrupts
MINS = 0;
SECS = 0;
TENTHS = 0;
TICKS = 0;
GOTO MAINLOOP;
ENABLE;
END;

Section 06.     Program 04.    Timer 1 usage.
Rudimentary timer operation which depends on value of POT-1.

CLEAR; clear
DEFINE  OSC  4; Osc speed
TRISD = %00000000; set all PORTD lines to output
TRISE = %00000000; set all PORTE lines to output
; Set the A to D control register for digital ports  D, E
ADCON1=%00000111; needed, 16F877A because it has analog properties
T1CON  = %00000001; Turn on Timer0, prescaler = 1
INTCON = %11000000; Enable global interrupts, peripheral interrupts
;
I    VAR     WORD; counter variable
J   VAR     WORD; counter variable
PAUSE 500;
I=0; set counters to 0
J=0;
PIE1.0 = 1; Enable TMR1 overflow interrupt
ON INTERRUPT GOTO INTHANDLER;
PORTD=0; turn off the entire port
PORTD.3 = 0; light d3 on bargraph off, repeats above instr
PORTD.2 = 0; light d3 on bargraph off, repeats above instr
;
MAINLOOP: ;
IF PORTD.1 = 0 THEN; Routine lights Do and D1 alternately to
PORTD.1 = 1; that the program is running the main routine
PORTD.0 = 0;
ELSE;
PORTD.1 = 0;
PORTD.0 = 1;
ENDIF;
FOR I = 1 TO 300; This is in lieu of a long pause instruction
PAUSEUS 100; so that interrupt is not compromised
NEXT I;
GOTO MAINLOOP; do it all forever
;
DISABLE;
INTHANDLER:; This is the interrupt service routine
IF J < 6 THEN; This routine allows 6
J = J+1; interrupts for each change of state
GOTO COUNTNOTFULL; of LED D3.
ELSE;
J = 0;
ENDIF;
IF PORTD.3 = 1 THEN; The D3 blink routine
PORTD.3 = 0;
ELSE ;
PORTD.3 = 1;
ENDIF;
COUNTNOTFULL:  ;
PIR1.0 = 0; must now clear the interrupt flag
RESUME;
ENABLE;
END; end program


Section 06.     Program 05.     Using TIMER0
Programs blinks two LEDs alternately and blinks
a third LED approx. half second ON and half second OFF.

CLEAR
DEFINE OSC 4; 4 MHz clock
DEFINE  LCD_DREG PORTD ; data register
DEFINE  LCD_RSREG PORTE; register select
DEFINE  LCD_RSBIT 0; pin number
DEFINE  LCD_EREG PORTE; enable register
DEFINE  LCD_EBIT  1; enable bit
DEFINE  LCD_RWREG PORTE; read/write register
DEFINE  LCD_RWBIT  2; read/write  bit
DEFINE  LCD_BITS  8; width of data
DEFINE  LCD_LINES 2; lines in display
DEFINE  LCD_COMMANDUS 2000; delay in micro seconds
DEFINE  LCD_DATAUS  50; delay in micro seconds
;
DEFINE  CCP1_REG PORTC; define the hpwm settings
DEFINE  CCP1_BIT 2 ;
; define the A2D values
DEFINE  ADC_BITS 8; set number of bits in result
DEFINE  ADC_CLOCK 3; set internal clock source (3=rc)
DEFINE  ADC_SAMPLEUS 50; set sampling time in us
; set the analog to digital control register 
ADCON1=%00000110; needed for the 16F877A LCD 
TEST     VAR WORD ;
ADVAL0   VAR BYTE; create adval to store result
ADVAL1 VAR BYTE; create adval to store result
X        VAR WORD ;
Y        VAR WORD ;
PAUSE 500; LCD start up
LCDOUT $FE, 1; clear display 
OPTION_REG=%00110000;
TMR0=0; set up the register i/o
TRISC = %11110001 ; PORTC.0 is going to be the input to
; start the motor in that we are using a motor
; encoder for input
PORTC.3=0; enable the motor
PORTC.2=0; set the rotation direction
;
LOOP: ;
ADCIN 0, ADVAL0; read channel 0 to ADVAL0
ADCIN 1, ADVAL1; read channel 1 to ADVAL1
ADCIN 3, ADVAL2; read channel 3 to ADVAL2
;
TMR0=0;
PAUSE ADVAL1;
X=TMR0 ;
IF ADVAL0>20 THEN;
   HPWM 2, ADVAL0, 32000;
   LCDOUT $FE, $80, DEC4 X,"  ",DEC ADVAL1,"  ";
   LCDOUT $FE, $C0, "PWM = ",DEC ADVAL0,"          ";
ELSE ;
   LCDOUT $FE, $C0, "PWM TOO LOW ",DEC ADVAL0,"  ";
ENDIF;
;
GOTO LOOP ;
END ;

Section 06.     Program 06.     Using TIMER0.
Programs counts the pulses from a motor driven encoder.
You can change the speed of the motor and the time for counts with the 2 pots.

CLEAR;
DEFINE OSC 4 ; 4 MHz clock
DEFINE  LCD_DREG PORTD ; data register
DEFINE  LCD_RSREG PORTE; register select
DEFINE  LCD_RSBIT 0; pin number
DEFINE  LCD_EREG PORTE; enable register
DEFINE  LCD_EBIT 1; enable bit
DEFINE  LCD_RWREG PORTE; read/write register
DEFINE  LCD_RWBIT 2; read/write  bit
DEFINE  LCD_BITS 8; width of data
DEFINE  LCD_LINES 2; lines in display
DEFINE  LCD_COMMANDUS 2000; delay in micro seconds
DEFINE  LCD_DATAUS 50; delay in micro seconds

; The next two lines define which pin is going to be used for the HPWM signal that will control the speed
; of the motor.  The encoder that we are looking at is attached to the motor

DEFINE CCP1 REG PORTC; define the HPWM settings
DEFINE CCP1_BIT 2; pin C1

; The next three lines define the reading of the three potentiometers on the board.  Only the first
; potentiometer is being used in the program but the others are defined so that you can use them when
; you modify the program.  The potentiometers give you values you can change in real time.

; define the A2D values
DEFINE ADC_BITS 8; Set number of bits in result
DEFINE ADC_CLOCK 3; Set internal clock source (3=rc)
DEFINE ADC_SAMPLEUS 50; Set sampling time in uS

; Next we set ADCON1 to bring the MCU back into digital mode.  Since this PIC has analog capability, it
; comes up in analog mode after a reset or on startup. 

; Set the Analog to Digital control register 
ADCON1=%00000111; needed for the LCD operation 
; We create the variables that we will need.
TMR1 VAR WORD; set the variable for the timer
ADVAL0 VAR BYTE; Create adval to store result
ADVAL1 VAR BYTE; Create adval to store result
ADVAL2 VAR BYTE; Create adval to store result
X VAR WORD ; spare variable for experimentation
Y VAR WORD ; spare variable for experimentation
PAUSE 500; pause for LCD to start up
LCDOUT $FE, 1 ; Clear Display and cursor home
;
; Set up the register I/O
TRISC = %11110001 ; PORTC.0 is going to be the Input
CCP1CON= %00000101 ; Capture every rising edge
T1CON= %00000011 ; No prescale/Osc off/Sync on
; External source/TMR1 on
; start the motor, using a motor
; encoder for input
PORTC.3=0; enable the motor
PORTC.2=1; set the rotation direction

; Next we go into the body of the program.  The loop starts with reading all three potentiometers
; though we are using only the first one to set the power and thus the speed of the motor.

LOOP: ;
ADCIN 0, ADVAL0; Read channel 0 to ADVAL0
ADCIN 1, ADVAL1; Read channel 1 to ADVAL1
ADCIN 3, ADVAL2; Read channel 3 to ADVAL2 

; If the duty cycle of the motor is less than 20 out of 255 the motor will not come on so we make an
; allowance for that and display the condition on the LCD.

IF ADVAL0>20 THEN;
   HPWM 2, ADVAL0, 32000 ;
   LCDOUT $FE, $C0, "PWM = ",DEC ADVAL0,"          "
ELSE ;
   LCDOUT $FE, $C0, "PWM TOO LOW ",DEC ADVAL0,"  "
ENDIF;

; Then we read the two timer registers to see how may counts went by.  In our case the counts were too
; low to show up in the high bits so that was ignored but if you have a faster count input, you might want
; to add this information to the readout.

TMR1H = 0; Clear Timer1 high 8-bits
TMR1L = 0 ; Clear Timer1 low 8-bits
T1CON.0 = 1 ; Start 16-bit timer
PAUSE 100 ; Capture 100 ms of Input Clock Frequency
T1CON.0              = 0 ; Stop 16-bit Timer
TMR1.BYTE0 = TMR1L ; Read Low 8-bits
TMR1.BYTE1 = TMR1H ; Read High 8-bits
TMR1 = TMR1 - 11 ; Capture Correction
IF TMR1 = 65525 THEN NOSIGNAL  ; See PicBasic manual for explanation.
   LCDOUT $FE, $80, DEC5 TMR1," COUNTS"; Frequency display
   PAUSE 10 ; Slow down 
GOTO LOOP ; Do it again
;
NOSIGNAL: ;
   LCDOUT $FE, $80, "NO SIGNAL     " ;
GOTO LOOP ;
END;

Section 06.     Program 07.     Timer1 as counter.
Timer 1 counts signals from a motor encoder.

;MeLabs program
CLEAR; clear memory
SO  CON 0; define serial output pin
N2400  CON 4; set serial mode
; define variables
DPINVAR PORTA.0; I2C data pin
CPINVAR PORTA.1; I2C clock pin
B0VAR BYTE;
B1VAR BYTE;
B2VAR BYTE;
; write to the memory
FOR B0 = 0 TO 15; loop 16 times
I2CWRITE DPIN, CPIN, $A0, B0, [B0]  ; write each location’s address to itself
PAUSE 10; delay 10 ms after each write is needed
NEXT B0;
;
LOOP:;
FOR B0 = 0 TO 15 STEP 2; loop 8 times
I2CREAD DPIN, CPIN, $A0, B0, [B1, B2]; read 2 locations in a row
SEROUT SO, N2400, [#B1," ",#B2," "]; print 2 locations to CRT
NEXT B0;
;
SEROUT SO, N2400, [13,10]; print linefeed
GOTO LOOP;
END;

Section 07.    Program 01.
Program to read from and write to I2C SEEPROMs

DEFINELOADER_USED1; LOADER_USED to allow use of the boot loader.
; This will not affect normal program operation.
DEFINE LCD_DREGPORTD; Define LCD registers and bits
DEFINE LCD_DBIT4;
DEFINE LCD_RSREGPORTE;
DEFINE LCD_RSBIT0;
DEFINE LCD_EREGPORTE;
DEFINE LCD_EBIT1;
INCLUDE "MODEDEFS.BAS";
CS        VAR     PORTA.5; Chip select pin
SCK     VAR     PORTC.3; Clock pin
SI         VAR     PORTC.4; Data in pin
SO       VAR     PORTC.5; Data out pin
ADDR    VAR     WORD; Address
B0       VAR     BYTE ; Data
TRISA.5 = 0; Set CS to output
ADCON1 =%00000111; Set all of PORTA and PORTE to digital
LOW PORTE.2; LCD R/W line low (W)
PAUSE 100 ; Wait for LCD to start up
FOR ADDR = 0 TO 15; Loop 16 times
B0 = ADDR + 100; B0 is data for SEEPROM
GOSUB EEWRITE ; Write to SEEPROM
PAUSE 10; Delay 10 ms after each write
NEXT ADDR;
LOOP:   FOR ADDR = 0 TO 15; Loop 16 times
GOSUB EEREAD; Read from SEEPROM
LCDOUT $FE, 1, #ADDR,": ",#B0; Display
PAUSE 1000;
NEXT ADDR;
GOTO LOOP ;
; Subroutine to read data from addr in serial EEPROM
EEREAD: CS = 0 ; Enable serial EEPROM
SHIFTOUT SI, SCK, MSBFIRST, [$03, ADDR.BYTE1, ADDR.BYTE0]  ; Send read cmd and addr
SHIFTIN SO, SCK, MSBPRE, [B0]   ; Read data
CS = 1; Disable
RETURN;
; Subroutine to write data at addr in serial EEPROM
EEWRITE: CS = 0; Enable serial EEPROM
SHIFTOUT SI, SCK, MSBFIRST, [$06]  ; Send write enable command
CS = 1 ; Disable to execute command
CS = 0; Enable
SHIFTOUT SI, SCK, MSBFIRST, [$02, ADDR.BYTE1, ADDR.BYTE0, B0]   ; Send address and data
CS = 1; Disable
RETURN;
END;

Section 07.     Program 02
Program to read from and write to SPI SEEPROMs
MeLabs program
; PicBasic Pro program to read and write to Microwire SEEPROM 93LC56A
; Write to the first 16 locations of an external serial EEPROM
; Read first 16 locations back and send to LCD repeatedly
; Note: for SEEPROMs with byte-sized address
;
DEFINE  LCD_DREG  PORTD; Define LCD registers and bits
DEFINE  LCD_DBIT   4;
DEFINE  LCD_RSREG PORTE;
DEFINE  LCD_RSBIT  0;
DEFINE  LCD_EREG   PORTE;
DEFINE  LCD_EBIT    1;
INCLUDE "MODEDEFS.BAS";
CSVARPORTA.5; Chip select pin
CLKVARPORTC.3; Clock pin
DIVARPORTC.4; Data in pin
DOVARPORTC.5; Data out pin
ADDRVARBYTE; ADDRESS
B0VARBYTE; DATA
LOW CS ; Chip select inactive
ADCON1 = 7; Set PORTA and PORTE to digital
LOW PORTE.2 ; LCD R/W line low (W)
PAUSE 100; Wait for LCD to start up
GOSUB EEWRITEEN; Enable SEEPROM writes
FOR ADDR = 0 TO 15; Loop 16 times
B0 = ADDR + 100 ; B0 is data for SEEPROM
GOSUB EEWRITE ; Write to SEEPROM
PAUSE 10 ; Delay 10 ms after each write
NEXT ADDR;
LOOP:   FOR ADDR = 0 TO 15; Loop 16 times
GOSUB EEREAD; Read from SEEPROM
LCDOUT $FE, 1, #ADDR,": ",#B0 ; Display
PAUSE 1000;
NEXT ADDR;
GOTO LOOP;
; Subroutine to read data from addr in serial EEPROM
EEREAD: CS = 1 ; Enable serial EEPROM
SHIFTOUT DI, CLK, MSBFIRST, [%1100\4, ADDR]     ; Send read command and address
SHIFTIN DO, CLK, MSBPOST, [B0]; Read data
CS = 0  ; Disable
RETURN;
; subroutine to write data at
; addr in serial EEPROM
EEWRITE: CS = 1; Enable serial EEPROM
SHIFTOUT DI, CLK, MSBFIRST, [%1010\4, ADDR, B0] ; Send write command, address and data
CS = 0 ; Disable
RETURN;
; Subroutine to enable writes to serial EEPROM
EEWRITEEN: CS = 1; ENABLE SERIAL EEPROM
SHIFTOUT DI, CLK, MSBFIRST, [%10011\5, 0\7]     ; Send write enable command and dummy clocks
CS = 0; Disable
RETURN;
END

Section 07.    Program 03.
Program to read from and write to Microwire SEEPROMs

DEFINELOADER_USED1;
; Define LCD pins
DEFINE  LCD_DREG       PORTD;
DEFINE  LCD_DBIT        4;
DEFINE  LCD_RSREG     PORTE;
DEFINE  LCD_RSBIT       0;
DEFINE  LCD_EREG        PORTE;
DEFINE  LCD_EBIT         1;
INCLUDE "MODEDEFS.BAS"; Alias pins
;
CS      VAR     PORTC.5; chip select
CK      VAR     PORTC.3; clock
DI       VAR     PORTA.2; data in
DO     VAR     PORTC.1 ; data out
; Allocate variables
ADDR      VAR     BYTE; Channel address / mode
RESULT  VAR     WORD;
X       VAR     WORD;
Y       VAR     WORD;
Z       VAR     WORD;
        HIGH CS; Chip select inactive
        ADCON1 = 7; Set PORTA, PORTE to digital
        LOW PORTE.2; LCD R/W line low (W)
        PAUSE 100; Wait for LCD to start
GOTO    MAINLOOP; Skip subroutines
; subroutine to read a/d converter
GETAD: ;
CS = 0; Chip select active
; Send address / mode - Start bit, 3 bit addr,
; null bit]
SHIFTOUT DI, CK, MSBFIRST, [1\1, ADDR\3, 0\1]
SHIFTIN DO, CK, MSBPRE, [RESULT\12]  ; Get 12-bit result
CS = 1; Chip select inactive
RETURN;
; Subroutine to get x value (channel 0)
GETX:;
ADDR = %00000101; Single ended, channel 0, MSBF high
GOSUB GETAD;
X = RESULT;
RETURN;
; subroutine to get y value (channel 1)
GETY: ;
        ADDR = %00000111  ; Single ended, channel 1, MSBF high
        GOSUB GETAD;
        Y = RESULT;
RETURN;
; Subroutine to get z value (differential)
GETZ:;
        ADDR = %00000001      ; Differential (ch0 = +, ch1 = -), MSBF high
        GOSUB GETAD;
        Z = RESULT;
RETURN;
;
MAINLOOP: ;
GOSUB   GETX; Get x value
GOSUB   GETY; Get y value
GOSUB   GETZ; Get z value
LCDOUT $FE, 1, "X=", #X, " Y=", #Y, " Z=", #Z      ; Send values to LCD
PAUSE   100 ; Do it about 10 times a second
GOTO    MAINLOOP; Do it forever
END; end program

Section 07.    Program 04.
Program to read from 12 bit LTC1298 A2D Chip by ME Labs.

DEFINE  LCD_DREGPORTD; define lcd pins
DEFINE  LCD_DBIT4;
DEFINE  LCD_RSREGPORTE;
DEFINE  LCD_RSBIT0;
DEFINE  LCD_EREGPORTE;
DEFINE  LCD_EBIT1;
; Allocate variables
COMMAND VAR     BYTE; Storage for command
IVARBYTE; storage for loop counter
TEMPVARWORD; Storage for temperature
DQVARPORTC.0; Alias DS1820 data pin
DQ_DIRVARTRISC.0; Alias DS1820 data direction pin
;
;
ADCON1 =%00000111; set PortA and Porte to digital
LOW PORTE.2; lcd r/w line low (w)
PAUSE 100 ; wait for lcd to start
LCDOUT $FE, 1, "TEMP IN DEGREES C"; display sign-on message
;
; mainloop to read the temperature and display on lcd
MAINLOOP:;
GOSUB INIT1820 ; init the DS1802
COMMAND = %11001100; ; issue skip rom command
GOSUB WRITE1820;
COMMAND = %01000100; ; start temperature conversion
GOSUB WRITE1820;
PAUSE 2000 ; wait 2 seconds for conversion to complete
GOSUB INIT1820; do another init
COMMAND = %11001100; ; issue skip rom command
GOSUB WRITE1820;
COMMAND = %10111110; ; read the temperature
GOSUB WRITE1820;
GOSUB READ1820;
; display the decimal temperature
LCDOUT $FE, 1, DEC (TEMP >> 1), ".", DEC (TEMP.0 * 5), " DEGREES C"
GOTO MAINLOOP; do it forever
; initialize DS1802 and check for presence
INIT1820:
LOW DQ ; Set the data pin low to init
PAUSEUS 500  ; wait > 480 us
DQ_DIR = 1 ; release data pin (set to input for high)
PAUSEUS 100 ; wait > 60 us
IF DQ = 1 THEN;
LCDOUT $FE, 1, "DS1820 NOT PRESENT";
PAUSE 500;
GOTO MAINLOOP; try again
ENDIF;
PAUSEUS 400; Wait for end of presence pulse
RETURN;
; write "command" byte to the DS1820
WRITE1820:;
FOR I = 1 TO 8; 8 bits to a byte
IF COMMAND.0 = 0 THEN;
GOSUB WRITE0; write a 0 bit
ELSE;
GOSUB WRITE1; write a 1 bit
ENDIF;
COMMAND = COMMAND >> 1; shift to next bit
NEXT I;
RETURN;
; write a 0 bit to the DS1802
WRITE0:;
LOW DQ;
PAUSEUS 60 ; low for > 60 us for 0
DQ_DIR = 1; release data pin (set to input for high)
RETURN; Write a 1 bit to the DS1820
WRITE1:;
LOW DQ ; Low for < 15 us for 1
@NOP ; Delay 1us at 4 MHz
DQ_DIR = 1 ; release data pin (set to input for high)
PAUSEUS 60 ; use up rest of time slot
RETURN; Read temperature from the DS1820
READ1820:;
FOR I = 1 TO 16 ; 16 bits to a word
TEMP = TEMP >> 1; shift down bits
GOSUB READBIT ; get the bit to the top of temp
NEXT I;
RETURN; Read a bit from the DS1820
READBIT:;
TEMP.15 = 1; preset read bit to 1
LOW DQ ; Start the time slot
@NOP ; delay 1us at 4mhz
DQ_DIR = 1 ; release data pin (set to input for high)
IF DQ = 0 THEN;
TEMP.15 = 0; set bit to 0
ENDIF;
PAUSEUS 60 ; wait out rest of time slot
RETURN;
END; end

Section 07.    Program 05.      DS1820
Program to read temperature by MELabs

INCLUDE "MODEDEFS.BAS"; define lcd pins
DEFINE  LCD_DREGPORTD;
DEFINE  LCD_DBIT4;
DEFINE  LCD_RSREGPORTE;
DEFINE  LCD_RSBIT0;
DEFINE  LCD_EREGPORTE;
DEFINE  LCD_EBIT1;
; alias pins
RST     VAR     PORTC.0; reset pin
DQ      VAR     PORTC.1 ; data pin
CLK     VAR     PORTC.3; clock pin
; allocate variables
TEMP    VAR     WORD ; storage for temperature
LOW RST  ; reset the device
ADCON1 =%00000111; set PortA and PortE to digital
LOW PORTE.2 ; lcd r/w line low (w)
PAUSE 100  ; wait for lcd to start
LCDOUT $FE, 1, "TEMP IN DEGREES C"       ; display sign-on message
; mainloop to read the temp and display on lcd
MAINLOOP: ;
RST = 1; enable device
SHIFTOUT DQ, CLK, LSBFIRST, [$EE]; start conversion
RST = 0;
PAUSE 1000; wait 1 second for conversion to complete
RST = 1;
SHIFTOUT DQ, CLK, LSBFIRST, [$AA]; send read command
SHIFTIN DQ, CLK, LSBPRER, [TEMP\9]; read 9 bit temperature
RST = 0;
; display the decimal temperature
LCDOUT $FE, 1, DEC (TEMP >> 1), ".", DEC (TEMP.0 * 5), " DEGREES C"
GOTO MAINLOOP; do it forever
END;

Section 07.   Program 06.     DS1620
MELabs Program to read temperature

CLEAR; read and write hardware usart
OSC 4; osc speed
B1VAR     BYTE; initialize usart
TRISC = %10111111; set TX (PORTC) to out, rest in
SPBRG = 25; set baud rate to 2400
RCSTA = %10010000; enable serial port and continuous receive
TXSTA = %00100000; enable transmit and asynchronous mode
;
; echo received characters in infinite loop
LOOP:;
GOSUB CHARIN; get a character from serial input, if any
IF B1 = 0 THEN LOOP ; no character yet
GOSUB CHAROUT; send character to serial output
GOTO LOOP; do it forever
;
CHARIN:; subroutine to get a character from usart receiver
B1 = 0; preset to no character received
IF PIR1.5 = 1 THEN; if receive flag then...
B1 = RCREG; get received character to b1
ENDIF;
CIRET:;
RETURN; go back to caller
; subroutine to send a character
CHAROUT: ; to usart transmitter
IF PIR1.4 = 0 THEN CHAROUT; wait for transmit register empty
TXREG = B1; send character to transmit register
RETURN; go back to caller
END;

HSEROUT ["A"]; loop to send out the 75 ‘A’ characters
NEXT ALPHA;
END;
Section 08.    Program 01.    RS232 Communications
Program to communicate with a computer.

CLEAR;
DEFINE OSC 4 ;
DEFINE HSER_RXSTA 90h; setting up the communications
DEFINE HSER_TXSTA 20h; variables
DEFINE HSER_BAUD  2400;
DEFINE HSER_SPBRG 25 ;
HSEROUT [$D, $A, $A]; a carriage return and two line feeds
ALPHA   VAR  BYTE; set counter variable
FOR ALPHA =1 TO 75;
HSEROUT ["A"]; loop to send out the 75 ‘A’ characters
NEXT ALPHA;
END;


Section 08.   Program 02.    RS232 Communications
Program to send information to the computer.

CLEAR;
DEFINE  LCD_DREG PORTD; Define LCD registers and bits
DEFINE  LCD_DBIT4;
DEFINE  LCD_RSREG PORTE;
DEFINE  LCD_RSBIT 0;
DEFINE  LCD_EREG PORTE;
DEFINE  LCD_EBIT 1;
;
CHAR    VAR     BYTE ; Variables used in the routine
; storage for serial character
COL     VAR     BYTE; Keypad column
ROW     VAR     BYTE; Keypad row
KEY     VAR     BYTE; Key value
LASTKEY VAR     BYTE; Last key storage
;
ADCON1 = %00000111; Set PORTA and PORTE to digital
LOW PORTE.2; LCD R/W line low (W)
PAUSE 500; Wait for LCD to startup
OPTION_REG.7 = 0; Enable PORTB pullups
;
KEY = 0 ; Initialize vars
LASTKEY = 0;
;
LCDOUT $FE, 1; Initialize and clear display
;
LOOP:   HSERIN 1, TLABEL, [CHAR]; Get a char from serial port
LCDOUT CHAR; Send char to display
;
TLABEL: ;
GOSUB GETKEY; Get a keypress if any
IF (KEY != 0) AND (KEY != LASTKEY) THEN;
HSEROUT [KEY] ; Send key out serial port
ENDIF;
LASTKEY = KEY; Save last key value
GOTO LOOP; Do it all over again
;
GETKEY: ; subroutine to get a key from keypad
KEY = 0; Preset to no key
FOR COL = 0 TO 3; 4 columns in keypad
PORTB = 0; All output pins low
TRISB = (DCD COL) ^ $FF ; Set one column pin to output
ROW = PORTB >> 4; Read row
IF ROW != %00001111 THEN GOTKEY ; If any key down, exit
NEXT COL;
RETURN; No key pressed

;(continues on next page).





GOTKEY:; Change row and column to ASCII key number
KEY = (COL * 4) + (NCD (ROW ^ $F)) + "0";
RETURN; Subroutine over
END;

Section 08.   Program 03.    RS232 Communications
Program to receive information from the computer.

; PicBasic Pro program to simulate an LCD Backpack
; Define LCD registers and bits
DEFINE  LCD_DREGPORTD;
DEFINE  LCD_DBIT4;
DEFINE  LCD_RSREGPORTE;
DEFINE  LCD_RSBIT0;
DEFINE  LCD_EREGPORTE;
DEFINE  LCD_EBIT1;
;
CHARVAR     BYTE; Storage for serial character
MODEVAR     BYTE; Storage for serial mode
RCVVAR     PORTB.7; Serial receive pin
BAUDVAR     PORTA.0; Baud rate pin - 0 = 2400, 1 = 9600
STATEVAR     PORTA.1; Inverted or true serial data - 1 = true
;
ADCON1 = %00000111; Set PORTA and PORTE to digital
LOW PORTE.2; LCD R/W line low (W)
PAUSE 500; Wait for LCD to startup
;
MODE = 0; Set mode
IF (BAUD == 1) THEN;
MODE = 2; Set baud rate
ENDIF;
;
IF (STATE == 0) THEN;
MODE = MODE + 4; Set inverted or true
ENDIF;
;
LCDOUT $FE, 1; Initialize and clear display
;
LOOP:;
SERIN RCV, MODE, CHAR; Get a char from serial input
LCDOUT CHAR ; Send char to display
GOTO LOOP; Do it all over again
END; end

Section 09.     Program 01.     For a PIC 16F84A
Program to simulate back pack (by meLabs)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

CLEAR; start with clearing the variables
DEFINE OSC 4; define the oscillator
DEFINE LCD_DREG PORTD ; define lcd connections
DEFINE LCD_DBIT 4; 4 bit protocol
DEFINE LCD_RSREG PORTE; register select byte
DEFINE LCD_RSBIT 0; register select it
DEFINE LCD_EREG PORTE; enable port
DEFINE LCD_EBIT 1; enable bit
LOW PORTE.2 ; leave low for write
ADCON1=%00000111; set a to d control register
PAUSE 500 ; pause for .5 sec for lcd startup
TRISC=%00000000; PORTC is all outputs for servos
PORTC=%00000000; set to 0
PORTD=%00000000, PORTD is all outputs for LCD
PORTE=%00000000, PORTE is all outputs for LCD
LCDOUT $FE, 1, "CLEAR" ; clear display and show CLEAR
DEFINEADC_BITS 8; set number of bits in result
DEFINEADC_CLOCK 3; set internal clock source (3=rc)
DEFINEADC_SAMPLEUS 150; set sampling time in us 
POT0VAR BYTE; create adval to store result
POT1VAR BYTE; create adval to store result
LOOP:; main loop
LCDOUT $FE, $80, DEC3 POT0," ", DEC3 POT1," " ; print data
ADCIN 0, POT0; read port a0
ADCIN 1, POT1; read port a0
POT0=POT0 + 23; so actual pulse is displayed
PULSOUT PORTC.1, POT0; pulse port c.1
PAUSE POT1; pause 1/60 seconds approx (24)
PULSOUT PORTC.0, POT0; pulse port c.2 
PAUSE POT1; pause 1/60 seconds, approx value is 24
GOTO LOOP; do it again
END; always end with end

Section 13     Program 01     This is a “stand alone” program for finding
the exact servo setting to determine position for a servo.
No interrupt is being used in the above program.  Simple loop with two POT1 delays expressed in milliseconds.

CLEAR; start with clearing the variables
DEFINE OSC 4; define the oscillator
DEFINE LCD_DREG PORTD ; define lcd connections
DEFINE LCD_DBIT 4; 4 bit protocol
DEFINE LCD_RSREG PORTE; register select byte
DEFINE LCD_RSBIT 0; register select it
DEFINE LCD_EREG PORTE; enable port
DEFINE LCD_EBIT 1; enable bit
LOW PORTE.2 ; leave low for write
ADCON1=%00000111; set a to d control register
PAUSE 500 ; pause for .5 sec for lcd startup
;
LCDOUT $FE, 1, "One second blinker" ; clear display and show clear
ON INTERRUPT GOTO INT_ROUTINE;
OPTION_REG=%00000111;
INTCON=%00100000;
X VAR BYTE;
Y VAR BYTE;
Y=0;
X=0;
LOOP:; main loop
LCDOUT $FE, $C0, BIN8 X ,"  ",DEC2 Y;
PAUSE 1;
GOTO LOOP; do it again
;
DISABLE;
INT_ROUTINE:;
X=X+1;
IF X=200 THEN ;
Y=Y+1;
X=0;
ELSE;
ENDIF;
IF Y=2 THEN ;
Y=0;
TOGGLE PORTD.1;
ELSE;
ENDIF;
RESUME;
ENABLE;
END;

Section 13    Program 02
One second blinker on D.0

CLEAR;
DEFINE OSC 4;
DEFINE  LCD_DREG PORTD; define lcd connections
DEFINE  LCD_DBIT 4;
DEFINE  LCD_RSREG PORTE;
DEFINE  LCD_RSREG PORTE;
DEFINE  LCD_RSBIT 0;
DEFINE  LCD_EREG  PORTE;
DEFINE  LCD_EBIT 1;
LOW  PORTE.2; lcd r/w line low (w)
ADCON1 = %00000010; PortE to digital.
PAUSE 500; wait .5 second for lcd startup
TRISC=%00000000;
PORTC=0;
X VAR WORD;
Y VAR WORD;
AD1 VAR BYTE;
Y=150 ;
DEFINEADC_BITS 8; set number of bits in result
DEFINEADC_CLOCK 3; set internal clock source (3=rc)
DEFINEADC_SAMPLEUS 50; set sampling time in us
LCDOUT $FE, 1; CLEAR DISPLAY ;
;
LOOP:;
ADCIN 0, AD1;
LCDOUT $FE, $80, DEC5 (200+(8 * AD1));
HIGH PORTC.1;
PAUSEUS (200+(8 * AD1));
LOW PORTC.1;
PAUSE 10;
GOTO LOOP;
;
END;

Section 13    Program 03
Simple servo position determinations.

CLEAR; start with clearing the variables
DEFINE OSC 4; define the oscillator
DEFINE LCD_DREG PORTD ; define lcd connections
DEFINE LCD_DBIT 4; 4 bit protocol
DEFINE LCD_RSREG PORTE; register select byte
DEFINE LCD_RSBIT 0; register select it
DEFINE LCD_EREG PORTE; enable port
DEFINE LCD_EBIT 1; enable bit
LOW PORTE.2 ; leave low for write
ADCON1=%00000111; set PortE to digital
PAUSE 500 ; pause for .5 sec for lcd startup
;
LCDOUT $FE, 1, "SERVO LIMITS" ; clear display and show clear
PAUSE 500;
LCDOUT $FE, 1       ; clear the display again
ON INTERRUPT GOTO INT_ROUTINE; target for the interrupt routing
OPTION_REG=%00000011; set the prescaler
INTCON=%00100000; enable to interrupt flag
;
POT1 VAR BYTE; variable created
POT2 VAR BYTE; variable created
POS VAR WORD; variable created
X VAR BYTE; variable created
Y VAR WORD; variable created
Y=0; set variable
X=0; set variable
;
LOOP:; main loop
ADCIN 0, POT1; read POT1
ADCIN 1, POT2; read POT2
POS=POT1*8+(POT2/32); do calculation for POS
Y=Y+1; increment y in foreground task
LCDOUT $FE, $80, "Adjustments =", DEC4 POS," ", DEC1 (POT2/32)," "; display items
LCDOUT $FE, $C0, "X=",DEC3 X ,"  Y=",DEC5 Y; display items of interest
GOTO LOOP; do it again
;
DISABLE; disable interrupts
INT_ROUTINE:; interrupt routine
X=X+1; increment x
IF X=5 THEN ; check value of x
X=0; reset x
PORTC.1=1; make PORTC.1 high
PAUSEUS POS; pause to match position of servo
TOGGLE PORTC.1; make PORTC.1 low again
ELSE; logic
ENDIF; logic
INTCON.2=0; reset/clear the interrupt flag
RESUME; resume main program
ENABLE; enable interrupts again
END; all programs must end with end.

Section 13    Program 04
Servo control program with interrupts

CLEAR; start with clearing the variables
DEFINE OSC 4; define the oscillator
DEFINE LCD_DREG PORTD ; define lcd connections
DEFINE LCD_DBIT 4; 4 bit protocol
DEFINE LCD_RSREG PORTE; register select byte
DEFINE LCD_RSBIT 0; register select it
DEFINE LCD_EREG PORTE; enable port
DEFINE LCD_EBIT 1; enable bit
LOW PORTE.2 ; leave low for write
ADCON1=%00000111; set PortE to digital
PAUSE 500 ; pause for .5 sec for lcd startup
;
LCDOUT $FE, 1, "SERVO LIMITS" ; clear display and show clear
PAUSE 500;
LCDOUT $FE, 1; clear the display again
ON INTERRUPT GOTO INT_ROUTINE; target for the interrupt routing
OPTION_REG=%00000011; set the prescaler
INTCON=%00100000; enable to interrupt flag
;
POT1 VAR BYTE; variable created
POT2 VAR BYTE; variable created
POS VAR WORD; variable created
X VAR BYTE; variable created
Y VAR WORD; variable created
Y=0; set variable
X=0; set variable
;
LOOP:; main loop
ADCIN 0, POT1; read POT1
ADCIN 1, POT2; read POT2
POS=POT1*10; do calculation for POS
FOR Y=1 TO 100; ]
PAUSE 10; ] delay loop
NEXT Y ; ]
LCDOUT $FE, $80, DEC4 POS; display items
POS=POT2*10; ]
FOR Y=1 TO 100; ] delay loop
PAUSE 10; ]
NEXT Y; increment y in foreground task
LCDOUT $FE, $C0, DEC4 POS; display items of interest
GOTO LOOP; do it again
;
DISABLE; disable interrupts
INT_ROUTINE:; interrupt routine
X=X+1; increment x
IF X=5 THEN ; check value of x
X=0; reset x
PORTC.1=1; make PORTC.1 high
PAUSEUS POS; pause to match position of servo
TOGGLE PORTC.1; make PORTC.1 low again
ELSE; logic
ENDIF; logic
INTCON.2=0; reset/clear the interrupt flag
RESUME; resume main program
ENABLE; enable interrupts again
END; all programs must end with end.

Section 13    Program 05
Program to finding Servo limits (with interrupt driven update timing)

CLEAR
DEFINE     OSC 4 ; clock
DEFINE LCD_DREG PORTD ; define LCD connections
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
DEFINE ADC_BITS 8 ' Set number of bits in result
DEFINE ADC_CLOCK 3 ' Set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ' Set sampling time in uS
DEFINE CCP2_REG PORTC ' Port to be used by HPWM 1
DEFINE CCP2_BIT 1 ' Pin to be used by HPWM 1
' Since no timer is defined, Timer1 will be used
ADCON1=%00000111; Sets ports A and E to digital
LOW PORTE.2 ; Set LCD to write mode only
TRISC=%00000000;
PORTC.0=0; turn off brake
PORTC.1=1; PWM LINE
PORTC.3 = 1; Direction of motor
POT_VAL  VAR BYTE; Variable for the potentiometer

PAUSE 500 ; Pause ½ sec for LCD startup
LCDOUT $FE, 1, "Ready and reset"; Clear Display
PAUSE 500; Pause for message display
LCDOUT $FE, 1; Clear Display again

MAIN:;
ADCIN 0, POT_VAL ' Read channel 0 to POT_VAL
HPWM 2, POT_VAL, 20000; Put it in the PWM command
LCDOUT $FE, $80, "Speed=",DEC3 POT_VAL; Display speed
LCDOUT $FE, $C0, "Direction=1"; Display direction
GOTO MAIN; Return to loop

END; All programs must end with END
Section 14   Program 01
Basic motor speed control program.  Simple  0 to +100% power

CLEAR;
DEFINE     OSC 4 ; clock
DEFINE LCD_DREG PORTD ; define LCD connections
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
DEFINE ADC_BITS 8 ' Set number of bits in result
DEFINE ADC_CLOCK 3 ' Set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ' Set sampling time in uS
DEFINE CCP2_REG PORTC ' Port to be used by HPWM 1
DEFINE CCP2_BIT 1 ' Pin to be used by HPWM 1
ADCON1=%00000111; Sets ports A and E to digital
LOW PORTE.2 ; Set LCD to write mode only
TRISC=%00000000; all outputs
PORTC.0=0; TURN OFF BRAKE
PORTC.1=1; PWM LINE
PORTC.3=1; DIRECTION OF MOTOR

POT_VAL  VAR BYTE; Variable for the potentiometer
MOT_PWR VAR BYTE
PAUSE 500 ; Pause ½ sec for LCD startup
LCDOUT $FE, 1, "READY AND RESET"; Clear Display
PAUSE 500; Pause for message display
LCDOUT $FE, 1; Clear Display again

MAIN:;
ADCIN 0, POT_VAL ; Read channel 0 to Pot_Val
SELECT CASE POT_VAL; Implement the decisions
CASE IS <128;
      MOT_PWR=127-POT_VAL;
      PORTC.3=0;
CASE 128;
      MOT_PWR=0;
CASE IS >128;
      MOT_PWR=POT_VAL-127;
      PORTC.3=1;
CASE ELSE;
END SELECT;
HPWM 2, MOT_PWR, 20000; Put it in the PWM command, line C1
LCDOUT $FE, $80, "SPEED=",DEC3 POT_VAL; Display speed
LCDOUT $FE, $C0, "DIRECTION=",DEC1 PORTC.3; Display direction
GOTO MAIN; Return to loop
END; All programs must end with END

Section 14   Program 02
Comprehensive motor control.  No encoder


CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2; Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%00000000; Set status register
INTCON2.7=0; Set status register

POSITION VAR WORD; Set variables
TARGET VAR WORD; Set variables
ERROR VAR BYTE; Set variables
MOTPWR VAR WORD; Set variables

PORTC.0=0 ; break off, motor control
PORTC.1=0; PWM bit for Channel 2 of HPWM
PORTC.3=1; dir bit for motor control
PAUSE 500; LCD start up pause
LCDOUT $FE, $01, "START UP CLEAR"; Clear message
PAUSE 100; Pause to see message
TARGET=32513; Set target position
POSCNTH=127; Set counter for encoder, H bit
POSCNTL=0; Set counter for encoder, L bit

LOOP: ; Main loop
POSITION=256*POSCNTH + POSCNTL; Read position
ERROR=ABS(POSITION-TARGET); Determine error
MOTPWR=ERROR +3; Set motor gain (This may vary with motor)
IF TARGET>POSITION THEN ; Set motor direction
PORTC.3=1 ; Set direction
PORTD.2=0; Turn off LED
ELSE;
PORTC.3=0 ; Set direction in reverse
PORTD.2=1; Turn ON LED for reverse direction
ENDIF; End decision
HPWM 2, MOTPWR, 20000 ; C.1 PWM signal

LCDOUT $FE, $80, "Target    ",DEC5 TARGET
LCDOUT $FE, $C0, "Position  ",DEC5 POSITION

GOTO LOOP ; Go back to loop
END; All programs must end with END

Section 15    Program 01
Rudimentary “holding motor on position” program.

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2; Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%00000000; Set status register
INTCON2.7=0; Set status register
POSITION VAR WORD; Set variables
TARGET VAR WORD; Set variables
ERROR VAR BYTE; Set variables
MOTPWR VAR WORD; Set variables
POT_POS VAR BYTE; Potentiometer position
PORTC.0=0 ; break off, motor control
PORTC.1=0; PWM bit for Channel 2 of HPWM
PORTC.3=1; dir bit for motor control
PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
TARGET=1792; Set target position
POSCNTH=7; Set counter for encoder, H bit
POSCNTL=0; Set counter for encoder, L bit
LOOP: ; Main loop
ADCIN 0, POT_POS; Read incremental speed value
TARGET=TARGET + POT_POS; Incrementing the target position
POSITION=256*POSCNTH + POSCNTL; Read position
ERROR=ABS(POSITION-TARGET); Determine error
MOTPWR=ERROR +3; Set motor gain (This may vary with motor)
IF TARGET>POSITION THEN ; Set motor direction
PORTC.3=1 ; Set direction
PORTD.2=0; Turn off LED
ELSE;
PORTC.3=0 ; Set direction in reverse
PORTD.2=1; Turn ON LED for reverse direction
ENDIF; End decision
HPWM 2, MOTPWR, 20000 ; C.1 PWM signal
LCDOUT $FE, $80, "Target ",DEC5 TARGET," POT ",DEC3 POT_POS
LCDOUT $FE, $C0, "Position ",DEC5 POSITION
GOTO LOOP ; Go back to loop
END; All programs must end with END

Section 15    Program 02    Potentiometer control.
Program to control the motor speed from a Potentiometer position.

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2; Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register 
TRISD =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%00000000; Set status register
INTCON2.7=0; Set status register
POSITION VAR WORD; Set variables
TARGET VAR WORD; Set variables
MOTPWR VAR WORD; Set variables
MOV_LEN VAR WORD; length of move
POT_POS VAR BYTE; Potentiometer position
PORTC.0=0 ; Brake off, motor control
PORTC.1=0; PWM bit for Channel 2 of HPWM
PORTC.3=1; direction bit for motor control
PORTD=0;
PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
POSCNTH=125; Set counter for encoder, H bit
POSCNTL=0; Set counter for encoder, L bit
MOV_LEN=1000; length of move
LOOP:; Main loop    
TARGET=32000 + MOV_LEN; MOVE INCREMENT
GOSUB MOVE;
TARGET=32000 - MOV_LEN; MOVE DECREMENT
GOSUB MOVE;
GOTO LOOP ; Go back to loop
MOVE:;
POSITION=256*POSCNTH + POSCNTL; Read position
IF TARGET>POSITION THEN ; Set motor direction
PORTC.3=1 ; Set direction
PORTD.0=1;
ELSE;
PORTC.3=0; Set direction in reverse
PORTD.0=0;
ENDIF; End decision 
ADCIN 0, POT_POS; Read incremental speed value
MOTPWR=POT_POS; Set motor gain (This may vary with motor)
HPWM 2, MOTPWR, 20000 ; C.1 PWM signal
GOSUB SHOW_LCD;
IF ABS(TARGET-POSITION)>5 THEN GOTO MOVE
MOTPWR=0; set motor to stop
HPWM 2, MOTPWR, 20000 ; stop motor
POSITION=POSITION-MOV_LEN; set up for next move
GOSUB SHOW_LCD;
PAUSE 200; So we can see the motor stop
RETURN; End of subroutine
SHOW_LCD:;
LCDOUT $FE, $80, "TAR=",DEC5 TARGET," Pot=",DEC3 POT_POS
LCDOUT $FE, $C0, "POS=",DEC5 POSITION," Dir=",DEC1 PORTD.0
RETURN;
END; All programs must end with END

Section 15    Program 03    Motor moves back and forth
Motor speed is controlled by the potentiometer, move distance is preset.
We will play with a similar program as we develop our control algorithms.  We will come back to this.

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2; Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register 
TRISD =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%00000000; Set status register
INTCON2.7=0; Set status register
POSITION VAR WORD; Set variables
TARGET VAR WORD; Set variables
MOT_SPD VAR WORD; Potentiometer position
MOV_DST VAR WORD; Potentiometer position
READ_VAL VAR WORD; Potentiometer position
MOT_PWR VAR BYTE ; MOTOR POWER
PORTC.0=0 ; Brake off, motor control
PORTC.1=0; PWM bit for Channel 2 of HPWM
PORTC.3=1; direction bit for motor control
PORTD=0;
PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
POSCNTH=125; Set counter for encoder, H bit
POSCNTL=0; Set counter for encoder, L bit
LOOP:; Main loop    
TARGET=32000 + MOV_DST*20; MOVE INCREMENT
GOSUB MOVE;
TARGET=32000 - MOV_DST*20; MOVE DECREMENT
GOSUB MOVE;
GOTO LOOP ; Go back to loop
MOVE:; Subroutine for making each move
ADCIN 0, MOT_SPD; Read incremental speed value
ADCIN 1, MOV_DST; Read incremental speed value
POSITION=256*POSCNTH + POSCNTL; Read position
IF TARGET>POSITION THEN ; Set motor direction
PORTC.3=1 ; Set pos direction
PORTD.0=1; Display motor direction
ELSE; Decision point
PORTC.3=0; Set reverse direction
PORTD.0=0; Display motor direction
ENDIF; End decision
MOT_PWR=MOT_SPD; Set motor gain (This may vary with motor)
HPWM 2, MOT_PWR, 20000 ; C.1 PWM signal
GOSUB SHOW_LCD; Display info on LCD
IF ABS(TARGET-POSITION)>5 THEN GOTO MOVE
MOT_PWR=0; set motor to stop
HPWM 2, MOT_PWR, 20000 ; stop motor (kill it to prevent osc)
GOSUB SHOW_LCD; Display info on LCD
POSITION=POSITION-MOV_DST; set up for next move
PAUSE 200; So we can see the motor stop
RETURN; End of subroutine
SHOW_LCD:; Display info on LCD
LCDOUT $FE, $80, "TAR=",DEC5 TARGET,  " Dist=",DEC5 MOV_DST * 20
LCDOUT $FE, $C0, "POS=",DEC5 POSITION,"  Spd=",DEC3 MOT_SPD 
RETURN; End of subroutine
END; All programs must end with END

Section 15    Program 04    Modify distance Moved and Speed with the Pots.
Program for understanding gain effects on distance moved.

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2; Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register 
TRISD =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%10101100; Set interrupt status register
INTCON2.7=0; Set status register
T0CON=%10000000;
POSITION VAR WORD; Set variables
TARGET VAR WORD; Set variables
MOT_SPD VAR WORD; Potentiometer position
MOV_DST VAR WORD; Potentiometer position
READ_VAL VAR WORD; Potentiometer position
MOT_PWR VAR BYTE ; MOTOR Power
INT_NUM VAR WORD;
INT_NUM=0;;
MOT_PWR=0;
PORTC.0=0 ; Brake off, motor control
PORTC.1=0; PWM bit for Channel 2 of HPWM
PORTC.3=1; direction bit for motor control
PORTD=0;
PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
POSCNTH=125; Set counter for encoder, H bit
POSCNTL=0; Set counter for encoder, L bit
ON INTERRUPT GOTO INT_ROUTINE;
LOOP:; Main loop     
HPWM 2, MOT_PWR, 20000 ; C.1 PWM signal    
GOSUB SHOW_LCD;
GOTO LOOP ; Go back to loop

SHOW_LCD:;
LCDOUT $FE, $80, "Tar=",DEC5 TARGET,  " Gain=",DEC5 MOT_PWR
LCDOUT $FE, $C0, "POS=",DEC5 POSITION," Cntr=",DEC5 INT_NUM
RETURN;

DISABLE;
INT_ROUTINE:;
INT_NUM=INT_NUM + 1;
SELECT CASE INT_NUM;
CASE IS<100;
MOT_PWR=MOT_PWR + 1;
CASE IS<400;
; JUST KEEP GOING
CASE IS<499;
MOT_PWR=MOT_PWR-1;
CASE ELSE;
INT_NUM=0 ;
MOT_PWR=0;
PAUSE 200;
END SELECT;
INTCON.2=0; clear the interrupt bit
RESUME;
ENABLE;

END; All programs must end with END

Section 15    Program 05    Ramp up, run at speed, ramp down
Program for motor to follow a feed from an interrupt to the motor gain

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2 ;Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register 
TRISD =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%10101100; Set interrupt status register
INTCON2.7=0; Set status register
T0CON=%10000000; Timer 0 control register
POSITION VAR WORD; Set variables
TARGET VAR WORD; Set variables
MOT_SPD VAR WORD; Potentiometer position
MOV_DST VAR WORD; Potentiometer position
READ_VAL VAR WORD; Potentiometer position
MOT_PWR VAR word ; MOTOR Power
INT_NUM VAR WORD; Interval count
INT_NUM=0 ; Set to 0
MOT_PWR=0; Set to 0
PORTC.0=0 ; Brake off, motor control
PORTC.1=0; PWM bit for Channel 2 of HPWM
PORTC.3=1; direction bit for motor control
PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
POSCNTH=0; Set counter for encoder, H bit
POSCNTL=0; Set counter for encoder, L bit
ON INTERRUPT GOTO INT_ROUTINE; Interrupt target

LOOP:; Main loop
POSITION=256*POSCNTH + POSCNTL; Read position
MOT_PWR=ABS(TARGET-POSITION ); Get error
IF TARGET>POSITION THEN ; Set motor direction
PORTC.3=1 ; Set direction
PORTD.0=1 ; Turn ON LED
ELSE; Decide
PORTC.3=0; Set direction in reverse
PORTD.0=0; Turn OFF LED
ENDIF; End decision
IF MOT_PWR>255 THEN MOT_PWR=255    ; Limit the power
HPWM 2, MOT_PWR, 20000 ; C.1 PWM signal
GOSUB SHOW_LCD; Look at LCD to see what is going on
GOTO LOOP ; Go back to loop
;
SHOW_LCD:; LCD routine
LCDOUT $FE, $80, "Tar=",DEC5 target,  " Gain=",DEC5 MOT_PWR
LCDOUT $FE, $C0, "POS=",DEC5 POSITION," Cntr=",DEC5 INT_NUM
RETURN; Return
;
DISABLE; Required by compiler
INT_ROUTINE:; Interrupt routine
INT_NUM=INT_NUM + 1; Increment the count
SELECT CASE INT_NUM;
CASE IS<100; First 100 counts
TARGET=TARGET + INT_NUM;
CASE IS<200; Next 100 counts
TARGET=TARGET + 100;
CASE IS<=300; Last 100 counts
TARGET=TARGET +(300-INT_NUM);
CASE ELSE; After last count
PAUSE 1000 ; pause 1 second to see results
INT_NUM=0 ; ]
MOT_PWR=0; ]
POSCNTH=0 ; ] reset everything
POSCNTL=0; ]
TARGET=0 ; ]
END SELECT; ]
INTCON.2=0; clear the interrupt bit
RESUME; go back to loop
ENABLE; Required by the compiler
END; All programs must end with END

Section 15    Program 06    Ramp up, run at speed, ramp down
Program to follow a feed from an interrupt to the motor target position
This program does not have a derivative component in the control algorithm.  Integration
is embedded by tying it to the error.  The larger the error the greater the gain.

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available in hardware)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2 ;Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register 
TRISD =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%10101100; Set interrupt status register
INTCON2.7=0; Set status register
T0CON=%11000111;
INT_NUM VAR WORD; interrupt number
INT_NUM=0 ;

PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
ON INTERRUPT GOTO INT_ROUTINE; Specify routine
;
LOOP:; Main loop
LCDOUT $FE, $80, "Interrupt counting"   ; Display
LCDOUT $FE, $C0, "Count=",DEC5 INT_NUM  ; Display
GOTO LOOP ; Go back to loop
;
DISABLE; Required instruction
INT_ROUTINE:; Routine ID
INT_NUM=INT_NUM + 1; Increment the count
POSCNTL=0; Set counter for encoder, H bit
POSCNTH=0; Set counter for encoder, L bit
INTCON.2=0; clear the interrupt bit
RESUME; Go back to where you came from
ENABLE; Required instruction
END; All programs must end with END

Section 15    Program 07  
Program to manipulate and display the interrupt rate

CLEAR; clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; select register
DEFINE LCD_RSBIT 0; select bit
DEFINE LCD_EREG PORTE; enable register
DEFINE LCD_EBIT 1; select bit
LOW PORTE.2; set bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; set status register
TRISA =%00011111; set status register
LATA  =%00000000; set status register
TRISB =%00000000; set status register
LATB  =%00000000; set status register
TRISC =%00000000; set status register 
TRISD =%00000000; set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%10101100; set interrupt status register
INTCON2.7=0; set status register
T0CON=%10000000; set timer 0
POSITION VAR WORD; set variables
TARGET VAR WORD; set variables
MOT_SPD VAR WORD; potentiometer position
MOT_PWR VAR WORD ; motor power
INT_NUM VAR WORD; interrupt number
TTL_MOV VAR WORD  ; total move count
TTL_MOV=1000; total move   
MAX_SPD VAR WORD; Set variable
MAX_SPD = 40; maximum move speed
ACC_RTE VAR BYTE; Acceleration rate (not being used)
ACC_RTE=1; Set to low value
;
INT_NUM=0 ; Interrupt number
MOT_PWR=0; Motor Power
PORTC.0=0 ; brake off, motor control
PORTC.1=0; PWM bit for channel 2 of hpwm
PORTC.3=1; direction bit for motor control
PAUSE 400; lcd start up pause
LCDOUT $FE, $01, "START UP"; clear message
PAUSE 100; pause to see message
POSCNTL=0; set counter for encoder, h bit
POSCNTH=0; set counter for encoder, l bit
ON INTERRUPT GOTO INT_ROUTINE; Specify target
LOOP:; main loop
T0CON=0   ; Timer0 control register, stops timer
POSITION=256*POSCNTH + POSCNTL; read position
T0CON=%10000000   ; restarts timer
MOT_PWR=ABS(TARGET-POSITION ); Get absolute value
IF TARGET>POSITION THEN ; set motor direction
PORTC.3=1 ; set direction
PORTD.0=1 ; Show direction on LED1 of bargraph
ELSE; Decision
PORTC.3=0; set direction in reverse
PORTD.0=0; Show direction on LED1 of bargraph
ENDIF; end decision
IF MOT_PWR>250 THEN MOT_PWR=250    ; Limit motor power
HPWM 2, MOT_PWR, 20000 ; C.1 PWM signal
GOSUB SHOW_LCD; Show display information
GOTO LOOP ; Go back to loop
;
SHOW_LCD:; Subroutine
LCDOUT $FE, $80, "TAR=",DEC5 TARGET,  " GAIN=",DEC5 MOT_PWR
LCDOUT $FE, $C0, "POS=",DEC5 POSITION," CNTR=",DEC5 INT_NUM
RETURN; Return
;
DISABLE; Required by the system
INT_ROUTINE:; Routine ID
INT_NUM=INT_NUM + 1; Increment speed on ramp up
SELECT CASE INT_NUM; Base decision on motor position
CASE IS<MAX_SPD; If not at speed
TARGET=TARGET + INT_NUM; Speed up
CASE IS<TTL_MOV/MAX_SPD; If a speed
TARGET=TARGET + MAX_SPD; Continue at constant speed
CASE IS<=TTL_MOV/MAX_SPD+MAX_SPD; If near end of move
TARGET=TARGET + (TTL_MOV/MAX_SPD+MAX_SPD-INT_NUM)
CASE ELSE; If done then reset all things
HPWM 2, 0, 20000 ; C.1 PWM signal 
PAUSE 2000 ; Pause to see end of move
INT_NUM=0 ; Set variable to zero
MOT_PWR=0; Set variable to zero
POSCNTL=0 ; Set variable to zero
POSCNTH=0; Set variable to zero
TARGET=0 ; Set variable to zero
END SELECT; End of decisions
TMR0L=0; Set timer register
INTCON.2=0; clear the interrupt bit
RESUME; Go back to program
ENABLE; Required by the system
END; All programs must end with END

Section 15    Program 08    Specify two basic variables
Generalized Program to run Distance at Speed

CLEAR; Clear variables
DEFINE OSC 20; 20 MHz clock (40 is better)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; Select register
DEFINE LCD_RSBIT 0; Select bit
DEFINE LCD_EREG PORTE; Enable register
DEFINE LCD_EBIT 1; Select bit
LOW PORTE.2; Set Bit low for writing to the LCD
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; Set status register
TRISA =%00011111; Set status register
LATA  =%00000000; Set status register
TRISB =%00000000; Set status register
LATB  =%00000000; Set status register
TRISC =%00000000; Set status register 
TRISD =%00000000; Set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%10101100; Set interrupt status register
INTCON2.7=0; Set status register
T0CON=%11000111; Set the Timer0 selects
INT_NUM VAR WORD; interrupt number
PAUSE 300; LCD start up pause
LCDOUT $FE, $01, "START UP"; Clear message
PAUSE 100; Pause to see message
ON INTERRUPT GOTO INT_ROUTINE;

LOOP:; Main loop
LCDOUT $FE, $80, "Counting interrupt"   ;
LCDOUT $FE, $C0, "Count = ",DEC5 INT_NUM  ;
GOTO LOOP ; Go back to loop
;
DISABLE;
INT_ROUTINE:;
INT_NUM=INT_NUM + 1;
TMR0L=78; Set counter for encoder, L bit
INTCON.2=0; clear the interrupt bit
RESUME;
ENABLE   ;
END; All programs must end with END

Section 15    Program 09.  Counting the interrupts
This program has to be tuned for the rate you want for your application.
As written it provides >100 interrupts a second


CLEAR;
DEFINE OSC 4;
DEFINE ADC_BITS 8; set number of bits in result
DEFINE ADC_CLOCK 3; set internal clock source (3=rc)
DEFINE ADC_SAMPLEUS 20; set sampling time in us
DEFINE CCP1_REG  PORTB;
DEFINE CCP1_BIT  3;
A2D1 VAR WORD;
TRISA = %11110111;
TRISB = %11110000; set portb line
OSCCON =%01100000; 4 MHz internal
ADCON0 =%11001001;
ADCON1 =%01000000;
PORTB.0=0; dir
PORTB.1=0; brake
PORTB.2=0; lock this pin down
OPTION_REG.7=0; pulls up inputs on portb
LOOP:;
ADCIN 1 , A2D1 ;
HIGH PORTB.3;
PAUSEUS 10*A2D1; pulse line
LOW PORTB.3;
PAUSE 15; delay 1/60 sec
GOTO LOOP;
END;

Section 15    Program 10  for PIC 16F819
Servo exerciser program


CLEAR; clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; select register
DEFINE LCD_RSBIT 0; select bit
DEFINE LCD_EREG PORTE; enable register
DEFINE LCD_EBIT 1; select bit
LOW PORTE.2; set bit low for writing to the lcd
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; set status register
TRISA =%00011111; set status register
LATA  =%00000000; set status register
TRISC =%10000000; set status register 
TRISD =%00000000; set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
INTCON=%10101100; set interrupt status register
INTCON2.7=0; set status register
T0CON=%10000000; set timer 0
POSITION VAR WORD; set variables
TARGET VAR WORD; set variables
MOT_PWR VAR WORD ; motor power
INT_NUM VAR WORD; interrupt number
MULTIPLIER var byte; for the pulse width read

PORTC.0=0 ; brake off, motor control
PORTC.1=0; PWM bit for channel 2 of hpwm
PORTC.3=1; direction bit for motor control
PAUSE 400; lcd start up pause
LCDOUT $FE, $01, "START UP"; clear message
PAUSE 100; pause to see message
POSCNTL=0; set counter for encoder, h bit
POSCNTH=127; set counter for encoder, l bit
TARGET=32515-764; No move position
MULTIPLIER=2; for the pulse width read

LOOP:; main loop
PULSIN PORTC.7, 1, INT_NUM ; read the pulse width
TARGET=32512 +INT_NUM*MULTIPLIER; set up the move position
POSITION=256*POSCNTH + POSCNTL; read position
MOT_PWR=ABS(TARGET-POSITION ); Get absolute value
IF TARGET>POSITION THEN ; set motor direction
PORTC.3=1 ; set direction
PORTD.0=1 ; Show direction on LED1 of bargraph
ELSE; Decision
PORTC.3=0; set direction in reverse
PORTD.0=0; Show direction on LED1 of bargraph
ENDIF; end decision
IF MOT_PWR >250 THEN MOT_PWR=250    ; Limit motor power
HPWM 2, MOT_PWR, 20000 ; C.1 PWM signal    
GOSUB SHOW_LCD; Show display information
GOTO LOOP ; Go back to loop
;
SHOW_LCD:; Subroutine
LCDOUT $FE, $80, "TAR=",DEC5 TARGET,  " GAIN=",DEC5 MOT_PWR
LCDOUT $FE, $C0, "POS=",DEC5 POSITION," PULSE=",DEC4 INT_NUM
RETURN; Return
END; All programs must end with END

Section 15 Program 11
Program to turn motor into an R/C servo (distance traveled to follow R/C signal)


CLEAR; clear variables
DEFINE OSC 20; 20 MHz clock (40 is better if available)
DEFINE LCD_DREG PORTD; define lcd connections
DEFINE LCD_DBIT 4; 4 data bits
DEFINE LCD_BITS 4; data starts on bit 4
DEFINE LCD_RSREG PORTE; select register
DEFINE LCD_RSBIT 0; select bit
DEFINE LCD_EREG PORTE; enable register
DEFINE LCD_EBIT 1; select bit
LOW PORTE.2 ;set bit low for writing to the lcd
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 50 ; delay in micro seconds  
DEFINE ADC_BITS 8 ; set number of bits in result
DEFINE ADC_CLOCK 3 ; set clock source (3=rc)
DEFINE ADC_SAMPLEUS 50 ; set sampling time in us
DEFINE CCP2_REG PORTC; hpwm 2 pin port
DEFINE CCP2_BIT 1; hpwm 2 pin bit 1
CCP1CON=%00111111; set status register
TRISA =%00011111; set status register
LATA  =%00000000; set status register
TRISC =%10000000; set status register 
TRISD =%00000000; set status register
ANSEL0=%00000001; page 251 of data sheet, status register
ANSEL1=%00000000; page 250 of data sheet, status register
QEICON=%10001000; page 173 counter set up, status register
MOT_PWR VAR WORD ; motor power
INT_NUM VAR WORD; interrupt number
PORTC.0=0 ; brake off, motor control
PORTC.1=0; PWM bit for channel 2 of hpwm
PORTC.3=1; direction bit for motor control
PAUSE 400; lcd start up pause
LCDOUT $FE, $01, "START UP"; clear message
PAUSE 100; pause to see message

LOOP:; main loop
PULSIN PORTC.7, 1, INT_NUM ; read the pot
IF INT_NUM<760 THEN ; set motor direction
PORTC.3=1 ; set direction
PORTD.0=1 ; Show direction on LED1 of bargraph
ELSE; Decision
PORTC.3=0; set direction in reverse
PORTD.0=0; Show direction on LED1 of bargraph
ENDIF; end decision
MOT_PWR=4 + ABS(INT_NUM-760)/3; set motor gain
IF MOT_PWR>255 THEN MOT_PWR=255; Limit motor power
HPWM 2, MOT_PWR, 20000 ; C.1 PWM signal
GOSUB SHOW_LCD; Show display information
GOTO LOOP ; Go back to loop
;
SHOW_LCD:; Subroutine
LCDOUT $FE, $80, " GAIN = ",DEC3 MOT_PWR
LCDOUT $FE, $C0, " PULSE=",DEC4 INT_NUM
RETURN; Return
END; All programs must end with END


Section 15 Program 12
Program for motor speed to follow HOBBY R/C signal


CLEAR; Always start with clear statement
DEFINE OSC 4 ; Set the clock to 4 MHz
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4; Specify 4 bit path for the data
DEFINE LCD_BITS 4; Number of bits to be  used
DEFINE LCD_RSREG PORTE; Port for Register select
DEFINE LCD_RSBIT 0; Bit for Register select
DEFINE LCD_EREG PORTE; Register for enable bit
DEFINE LCD_EBIT 1; Bit for enable bit
DEFINE LCD_RWREG PORTE; Define read/write register
DEFINE LCD_RWBIT 2; Define read/write bit
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 20 ; delay in micro seconds
DEFINE ADC_BITS 8; number of bits in the A to D result
DEFINE ADC_CLOCK 3; clock 3 is the R/C clock
DEFINE ADC_SAMPLEUS 50; sample time for A to D
LOW PORTE.2; Set bit low for write only
ADCON1=%00000111; Set A to D control register 
PAUSE 500; Pause ½ sec to let LCD start up
LOOP:;
LCDOUT $FE, 1, "LCD Setup OK" ; Clear Display
PAUSE 400; Pause so you can see message
LCDOUT $FE, 1 ; Clear Display again
PAUSE 400; Pause so you can see the cleared screen
GOTO LOOP; Do it forever
END; End program

Section 16    Program 01    Making the LCD show a message
Basic LCD set up with DEFINEs

CLEAR; Always start with clear
DEFINE OSC 4 ; Set the clock to 4 MHz
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4; Specify 4 bit path for the data
DEFINE LCD_BITS 4; Number of bits to be used
DEFINE LCD_RSREG PORTE; Port for Register select
DEFINE LCD_RSBIT 0; Bit for Register select
DEFINE LCD_EREG PORTE; Register for enable bit
DEFINE LCD_EBIT 1; Bit for enable bit
DEFINE LCD_RWREG PORTE; Define read/write register
DEFINE LCD_RWBIT 2; Define read/write bit
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 20 ; delay in micro seconds
DEFINE ADC_BITS 8; number of bits in the A to D result
DEFINE ADC_CLOCK 3; clock 3 is the R/C clock
DEFINE ADC_SAMPLEUS 50; sample time for A to D
LOW PORTE.2; Set bit low for write only to LCD
ADCON1=%00000111; Set A to D control register 
PAUSE 500; Pause to let LCD start up
X VAR WORD; Pause variable
Y VAR BYTE; Delay variable adjustment
Z VAR WORD; Repeat counter
TRISA = %11111111; set PortA
TRISB=%00000000; set PortB 
TRISE=%00000000; set PortE
; PORTB.0; PWM (green wire)
; PORTB.1; brake (red wire)
; PORTB.2; dir (black wire) 
LOOP:; Main loop
ADCIN 0, Y; Read delay variable
X=100*Y; Calculate delay
LCDOUT $FE, 1, DEC4 X," ",DEC4 y; Load display LCD with x value in decimal format
FOR Z=1 TO 25; Forward 100 counts
PORTB=%00000110;
PAUSEUS X;
PORTB=%00110000;
PAUSEUS X ;
PORTB=%00000010;
PAUSEUS X;
PORTB=%00010000;
PAUSEUS X;
NEXT Z ;
FOR Z=1 TO 25; Reverse 100 counts
PORTB=%00000110;
PAUSEUS X;
PORTB=%00010000;
PAUSEUS X;
PORTB=%00000010;
PAUSEUS X;
PORTB=%00110000;
PAUSEUS X;
NEXT Z;
GOTO LOOP; Repeat
END; All programs end with END

Section 16  Program 02
Stepper motor forward and reverse 100 steps

CLEAR; Always start with clear
Y VAR BYTE;
WRITE 0,  %00000110   ; set how coils are energized step 1
WRITE 1, %00110000   ; set how coils are energized step 2
WRITE 2, %00000010   ; set how coils are energized step 3
WRITE 3, %00010000   ; set how coils are energized step 4
TRISB=%00000000; set PortB 
LOOP:; Main loop 
Y=Y+1; Read loop
Y=Y & %00000011; picks the last two digits (0 to 3)
READ Y, PORTB; reads the right bit array for PortB
PAUSEUS 1000 ; pause should be as short as possible
GOTO LOOP; do it again
END; All programs end with END

Section 16  Program 03
Stepper motor forward as fast as possible
Adjust the PAUSEUS 1000 constant till the motor stalls

CLEAR; Always start with clear
X VAR BYTE; New variable added for pot
Y VAR BYTE;
Z VAR BYTE;
WRITE 0,  %00000110; set how coils are energized step 1
WRITE 1, %00110000; set how coils are energized step 2
WRITE 2, %00000010; set how coils are energized step 3
WRITE 3, %00010000; set how coils are energized step 4
TRISB=%00000000; set PortB 
LOOP:; Main loop
ADCIN 0, X; Read delay variable
IF X=255 THEN GOTO LOOP ; Skip the motor winding update, stops motor
Y=Y+1; Read loop
Y=Y & %00000011; picks the last two digits
READ Y, PORTB; reads the right array for PortB
PAUSEUS 800 +X*50 ; Sets the delay between steps
GOTO LOOP; Do it forever.
END; All programs end with END

Section 16  Program 04
Stepper motor speed controlled from a potentiometer

CLEAR; always start with clear
DEFINE OSC 4; define oscillator speed
DEFINE LCD_DREG PORTD ; define lcd connections
DEFINE LCD_DBIT 4; 4 bit path
DEFINE LCD_RSREG PORTE; select reg
DEFINE LCD_RSBIT 0; select bit
DEFINE LCD_EREG PORTE; enable register  
DEFINE LCD_EBIT 1; enable bit
LOW PORTE.2; make low for write only;
TRISD = %00000000 ; set all PORTD lines to output
TRISE = %00000000 ; set all PORTE lines to output
XVAR WORD; set up the variable
ADCON1=%00000111; set the Analog to Digital control register
PAUSE 500; pause for LCD to start up
LCDOUT $FE, 1; clear screen
ON INTERRUPT GOTO INT_ROUT; tells program where to do on interrupt
INTCON.5=1; sets up the interrupt enable
INTCON.2=0; clears the interrupt flag so it can be set
OPTION_REG=%00000111; sets the prescaler to 256
X=0; sets the initial value for X
;
MAIN:; the main loop of the program
LCDOUT $FE, $80, DEC5 X; write X to line 1
GOTO MAIN; repeat to loop
;
DISABLE; req’d instruction, to the compiler
INT_ROUT:; interrupt service routine
X=X+1; Increment the X counter
INTCON.2=0; Clear the interrupt flag
RESUME; Go back to where you were
ENABLE; req’d instruction, to the compiler
END; all programs must end with End

Section 16    Program 05
Basic interrupt routine for Timer 0
Try changing the OPTION_REG to %00000000 (prescaler value) and see what happens (speed)

CLEAR; always start with clear
DEFINE OSC 4; define oscillator speed
DEFINE LCD_DREG PORTD ; define lcd connections
DEFINE LCD_DBIT 4; 4 bit path
DEFINE LCD_RSREG PORTE; select reg
DEFINE LCD_RSBIT 0; select bit
DEFINE LCD_EREG PORTE; enable register  
DEFINE LCD_EBIT 1; enable bit
LOW PORTE.2; make low for write only;      
TRISB = %00000000 ; set all PORTD lines to output
TRISD = %00000000 ; set all PORTD lines to output
TRISE = %00000000 ; set all PORTE lines to output
XVAR WORD; set up the variable
YVAR WORD; set up the variable
ZVAR WORD; set up the variable
WRITE 0, %00000110; set how coils are energized step 1
WRITE 1, %00110000; set how coils are energized step 2
WRITE 2, %00000010; set how coils are energized step 3
WRITE 3, %00010000; set how coils are energized step 4
ADCON1=%00000111; set the Analog to Digital control register
PAUSE 500; pause for LCD to start up
LCDOUT $FE, 1 ; clear screen
ON INTERRUPT GOTO INT_ROUT; tells program where to do on interrupt
INTCON.5=1; sets up the interrupt enable
INTCON.2=0; clears the interrupt flag so it can be set
OPTION_REG=%00000111; sets the prescaler to 256
X=0; sets the initial value for X
MAIN:; the main loop of the program
LCDOUT $FE, $80, DEC3 Z; write X to line 1
ADCIN 0, Z; Read delay variable
Z=Z/32; Calculate delay;
OPTION_REG=%00000000+ Z; Add pot reading to Opt Reg
GOTO MAIN; repeat loop
DISABLE; reqd instruction, to the compiler
INT_ROUT:; interrupt service routine
Y=Y+1; Interrupt loop
Y=Y & %00000011; pick last two digits
READ Y, PORTB; read port b from EPROM
INTCON.2=0; Clear the interrupt flag
RESUME; Go back to where you were
ENABLE; reqd instruction, to the compiler
END; all programs must end with End




Section 16     Program 06
Pot controlling speed via pre-scalers for Timer 0

CLEAR; Always start with clear
DEFINE OSC 4 ; Set the clock to 4 MHz
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4; Specify 4 bit path for the data
DEFINE LCD_BITS 4; Number of bits to be  used
DEFINE LCD_RSREG PORTE; Port for Register select
DEFINE LCD_RSBIT 0; Bit for Register select
DEFINE LCD_EREG PORTE; Register for enable bit
DEFINE LCD_EBIT 1; Bit for enable bit
DEFINE LCD_RWREG PORTE; Define read/write register
DEFINE LCD_RWBIT 2; Define read/write bit
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 20 ; delay in micro seconds
DEFINE ADC_BITS 8; number of bits in the A to D result
DEFINE ADC_CLOCK 3; clock 3 is the R/C clock
DEFINE ADC_SAMPLEUS 50; sample time for A to D
LOW PORTE.2; Set bit low for write only to LCD
ADCON1=%00000111; Set A to D control register
ON INTERRUPT GOTO INTERUPTROUTINE; This line needs to be early in the
; program, before the routine is called.
PAUSE 500; Pause to let LCD start up
X VAR WORD; Pause variable
Y VAR BYTE; Delay variable adjustment
Z VAR BYTE; Repeat counter
WRITE 0,  %00000110   ; set how coils are energized step 1
WRITE 1, %00110000   ; set how coils are energized step 2
WRITE 2, %00000010   ; set how coils are energized step 3
WRITE 3, %00010000   ; set how coils are energized step 4
OPTION_REG=%10000000; Page 48 on data sheet
; Bit 7=1 disable pull ups on PORTB
; Bit 5=0 selects timer mode
; Bit 2=0  }
; Bit 1=0  } sets Timer0 pre-scaler to 1
; Bit 0=0  }
INTCON=%10100011; Bit 7=1 Enables all unmasked interrupts
T1CON =%00000001; Bit 5=1 Enables Timer0 overflow interrupt
ADCON0=%11000001; Bit 2 flag will be set on interrupt and
ADCON1=%00000010;
PIE1  =%00000001; has to be cleared in the interrupt routine.
; It is set clear to start with.
TRISA=%11111111; set PortA
TRISB=%00000000; set PortB 
TRISE=%00000000; set PortE

LOOP:; Main loop 
LCDOUT $FE, 1, DEC3 X, " ",DEC2 Y," ",BIN8 PORTB," ",DEC2 Z
ADCIN 0, Z; Read delay variable
PAUSE 10;
Z=Z/8; reduce sensitivity of constant
GOTO LOOP; Repeat
DISABLE; DISABLE and ENABLE must bracket the
; interrupt routine
INTERUPTROUTINE:; this information is used by the compiler only.
X = X + 1;
IF X < Z THEN ENDINTERRUPT; one second has not yet passed
X = 0;
Y=Y+1; Interrupt loop
Y=Y & %00000011; picks the last two digits
READ Y, PORTB; reads the right array for PortB
ENDINTERRUPT:;
INTCON.2 = 0; clears the interrupt flag.
RESUME; resume the main program
ENABLE; DISABLE and ENABLE must bracket int. routine
END; All programs end with END

Section 16    Program 07
Running motor with Timer0 and Potentiometer 0
This program does not give us interrupts fast enough for the high speeds we want to achieve.

CLEAR; Always start with clear
TRISB=%00000000;
X VAR WORD;
Y VAR WORD;
Z VAR WORD;
WRITE 0, %00000110   ; set how coils are energized step 1
WRITE 1, %00110000   ; set how coils are energized step 2
WRITE 2, %00000010   ; set how coils are energized step 3
WRITE 3, %00010000   ; set how coils are energized step 4
TRISB=%00000000; set PortB
LOOP:; Main loop
ADCIN 0, X; Read delay variable
IF X=255 THEN GOTO LOOP ; Skip move at this point
FOR Z=1 TO 500; Move 500 moves
Y=Y+1; Read loop
Y=Y & %00000011; picks the last two digits
READ Y, PORTB; reads the right array for PortB
PAUSEUS 800 +X*50 ; Pause between moves, speed
NEXT Z; Do it again
IF X=255 THEN GOTO LOOP ; Skip move at this point
FOR Z=1 TO 500; Move 500 moves
Y=Y-1; Read loop
Y=Y & %00000011; picks the last two digits
READ Y, PORTB; reads the right array for PortB
PAUSEUS 800 +X*50 ; Pause between moves, speed
NEXT Z  ; Do it again
GOTO LOOP; Do it again
END; All programs end with END

Section 16    Program 08
Running motor back and forth with Potentiometer 0 speed control

CLEAR; Always start with clear
DEFINE OSC 4 ; Set the clock to 4 MHz
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4; Specify 4 bit path for the data
DEFINE LCD_BITS 4; Number of bits to be used
DEFINE LCD_RSREG PORTE; Port for Register select
DEFINE LCD_RSBIT 0; Bit for Register select
DEFINE LCD_EREG PORTE; Register for enable bit
DEFINE LCD_EBIT 1; Bit for enable bit
DEFINE LCD_RWREG PORTE; Define read/write register
DEFINE LCD_RWBIT 2; Define read/write bit
DEFINE LCD_LINES 2 ; lines in display
DEFINE LCD_COMMANDUS 2000 ; delay in micro seconds
DEFINE LCD_DATAUS 20 ; delay in micro seconds
DEFINE ADC_BITS 8; number of bits in the A to D result
DEFINE ADC_CLOCK 3; clock 3 is the R/C clock
DEFINE ADC_SAMPLEUS 50; sample time for A to D
LOW PORTE.2; Set bit low for write only to LCD
TRISB=%00000000;
X VAR WORD; pot reading
Y VAR WORD; motor position
Z VAR WORD;
WRITE 0,  %00000110   ; set how coils are energized step 1
WRITE 1, %00110000   ; set how coils are energized step 2
WRITE 2, %00000010   ; set how coils are energized step 3
WRITE 3, %00010000   ; set how coils are energized step 4
TRISB=%00000000; set portb
ADCON1=%00000111; Set A to D control register
Z=128;
PAUSE 500;
LOOP:; Main loop 
LCDOUT $FE, $80, DEC4 X," ",DEC4 Z;
ADCIN 0, X; Read potentiometer variable
IF X=128 THEN GOTO LOOP ;
IF X>Z THEN;
Z=Z+1;
Y=Y+1; Read loop
Y=Y & %00000011; picks the last two digits
READ Y, PORTB; reads the right array for portb 
ENDIF   ;
IF X<Z THEN  ;
Z=Z-1;
Y=Y-1; Read loop
Y=Y & %00000011; picks the last two digits
READ Y, PORTB; reads the right array for portb
ENDIF;
GOTO LOOP ;
END; All programs end with END

Section 16    Program 09
Positioning motor with the Potentiometer as position controller

CLEAR; Clear variables
DEFINE OSC 4; Define osc
DEFINE LCD_DREG PORTD ; Define LCD connections
DEFINE LCD_DBIT 4;
DEFINE LCD_RSREG PORTE;
DEFINE LCD_RSBIT 0;
DEFINE LCD_EREG PORTE;
DEFINE LCD_EBIT 1;
LOW PORTE.2 ; pull write bit low
;
TRISD     = %00000000 ; set all PORTD lines to outputs
TRISE     = %00000000;
ADCON1 = %00000111  ; don’t forget to set ADCON
;
PAUSE 500; pause .500 seconds for LCD startup
;
LCDOUT $FE, 1; clear LCD, go to first line, first position
LCDOUT "Now is the time for"; print
LCDOUT $FE, $C0; go to second line
LCDOUT "a cup of pea soup!"; print
END; end program properly

Section 18.     Program 01.
A short, rudimentary program for testing the LCD