This is all the programs in the book on Motors. Cutting and pasting from this file to WORD will give properly formatted, good looking results. If you want what you copy to go into the editor for writing to the PIC use the bPBP language programs in the other file. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLEAR ; clear all memory DEFINE OSC 4 ; define the osc freq LEDID VAR 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 HIGH PORTD.0 ; turns LED connected to D0 on PAUSE 500 ; delay 0.5 seconds LOW PORTD.0 ; turns LED connected to D0 off PAUSE 500 ; 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 X VAR 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 X VAR 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 MAXPOS VAR WORD ; servo position variable MINPOS VAR WORD ; servo position variable POSSTEP VAR BYTE ; servo position step variable SERVO1 VAR 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_VALUE VAR BYTE ; Create A2D_Value to store result A2D_VALUE1 VAR BYTE ; Create A2D_Value to store result A2D_VALUE2 VAR BYTE ; Create A2D_Value to store result ADWALWAS VAR BYTE ; POS VAR WORD ; Servo positions CENTERPOS VAR WORD ; MAXPOS VAR WORD ; MINPOS VAR WORD ; POSSTEP VAR 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 ; POS VAR WORD ; servo positions CENTERPOS VAR WORD ; MAXPOS VAR WORD ; MINPOS VAR WORD ; POSSTEP VAR 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 = $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 ADVAL VAR BYTE ; Create adval to store result TICKS VAR WORD ; TENTHS VAR BYTE ; SECS VAR WORD ; MINS VAR BYTE ; ; ; 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 DPIN VAR PORTA.0 ; I2C data pin CPIN VAR PORTA.1 ; I2C clock pin B0 VAR BYTE ; B1 VAR BYTE ; B2 VAR 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 DEFINE LOADER_USED 1 ; LOADER_USED to allow use of the boot loader. ; This will not affect normal program operation. 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" ; 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" ; CS VAR PORTA.5 ; Chip select pin CLK VAR PORTC.3 ; Clock pin DI VAR PORTC.4 ; Data in pin DO VAR PORTC.5 ; Data out pin ADDR VAR BYTE ; ADDRESS B0 VAR BYTE ; 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 DEFINE LOADER_USED 1 ; ; 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_DREG PORTD ; define lcd pins DEFINE LCD_DBIT 4 ; DEFINE LCD_RSREG PORTE ; DEFINE LCD_RSBIT 0 ; DEFINE LCD_EREG PORTE ; DEFINE LCD_EBIT 1 ; ; Allocate variables COMMAND VAR BYTE ; Storage for command I VAR BYTE ; storage for loop counter TEMP VAR WORD ; Storage for temperature DQ VAR PORTC.0 ; Alias DS1820 data pin DQ_DIR VAR TRISC.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_DREG PORTD ; DEFINE LCD_DBIT 4 ; DEFINE LCD_RSREG PORTE ; DEFINE LCD_RSBIT 0 ; DEFINE LCD_EREG PORTE ; DEFINE LCD_EBIT 1 ; ; 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 B1 VAR 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 ; 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_DBIT 4 ; 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_DREG PORTD ; DEFINE LCD_DBIT 4 ; DEFINE LCD_RSREG PORTE ; DEFINE LCD_RSBIT 0 ; DEFINE LCD_EREG PORTE ; DEFINE LCD_EBIT 1 ; ; CHAR VAR BYTE ; Storage for serial character MODE VAR BYTE ; Storage for serial mode RCV VAR PORTB.7 ; Serial receive pin BAUD VAR PORTA.0 ; Baud rate pin - 0 = 2400, 1 = 9600 STATE VAR 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 DEFINE ADC_BITS 8 ; set number of bits in result DEFINE ADC_CLOCK 3 ; set internal clock source (3=rc) DEFINE ADC_SAMPLEUS 150 ; set sampling time in us POT0 VAR BYTE ; create adval to store result POT1 VAR 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 ; 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 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 ISPOSITION 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 X VAR 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 X VAR WORD ; set up the variable Y VAR WORD ; set up the variable Z VAR 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