; ; ; Development code for servo control with RS232 comm to host PC ; Version 5 -- initial development ; * Changing how mask is built ; * Removing servo_ctr var ; * Hacking fix for RB0 to be moved to RA3 ; * Implement interrupts on RB0 for serial port input ; * Fix timing for pulse widths ; * Now attempting add query ('A') command (it works) ; * Adding protection from bad servo number and overwriting memory ; * Added fix for 0 value (off) for servos 1-7 ; ; PINOUTS: ; ; A0 - Status LED ; A1 - NC ; A2 - Output - RS232 output (transmit) ; A3 - Servo control output ; A4 - NC ; B0 - Input - RS232 input (receive) ; B1-B7 - Servo control outputs ; ; ; processor 16F84 ;;;;;;;;;;;;;;;;;;;;;; ; Configuration word : bits 13-4 - 1 (code protection off) ;;;;;;;;;;;;;;;;;;;;;; bit 3 - 1 (power up timer disabled) ; bit 2 - 0 (watchdog timer disabled) ; bit 1,0 - 10 (HS oscillator) ; __config B'11111111111010' ; ; include "p16f84.inc" title "Variables *****************************************" RS232_delay_ctr equ 0x0c RS232_counter equ 0x0d RS232_datachar equ 0x0e temp equ 0x0f temp2 equ 0x10 temp3 equ 0x11 w_temp equ 0x12 ; ; servo positions ; pos0 equ 0x13 pos1 equ 0x14 pos2 equ 0x15 pos3 equ 0x16 pos4 equ 0x17 pos5 equ 0x18 pos6 equ 0x19 pos7 equ 0x1A curr_pos equ 0x1B servo_mask equ 0x1C ctr equ 0x1D ; Used in ASCII conversion ASCIIO EQU 0x1F ASCIIT EQU 0x20 ASCIIH EQU 0x21 status_temp equ 0x22 intmp equ 0x23 cmd0 equ 0x24 cmd1 equ 0x25 cmd2 equ 0x26 cmd3 equ 0x27 incbytes equ 0x28 fsr_temp equ 0x29 ;------------------------------------------------------------------ ; Assembler defines CR equ 0x0D LF equ 0x0A ;------------------------------------------------------------------ ; Reset vector ORG 0 goto main_code ;------------------------------------------------------------------ ; Interrupt vector ORG 4 _interrupt_vector movwf w_temp ; Save w register value swapf STATUS, w ; swap status and w movwf status_temp ; Save status register movf FSR, W ; Get the FSR register into W movwf fsr_temp ; Save it into temp variable call RS232_getchar ; Read incoming character savebyte btfsc incbytes, 0 goto savebyte2 bsf incbytes, 0 ; Set flag to indicate first char received movwf cmd0 goto icleanup savebyte2 btfsc incbytes, 1 goto savebyte3 bsf incbytes, 1 movwf cmd1 goto icleanup savebyte3 btfsc incbytes, 2 goto gotfullcmd bsf incbytes, 2 movwf cmd2 goto icleanup gotfullcmd movwf cmd3 clrf incbytes ; Clear out flags ; Now see if we got one of the special commands: movf cmd0, W sublw 'A' btfss STATUS, Z ; See if we match 'A' goto fullcmd1 ; If here, we got the 'A'sk command call handleask goto icleanup fullcmd1 ; If we made it here, assume that we have a position setting command... movlw '0' ; subwf cmd0, 1 ; Convert single ascii value to binary subwf cmd0, W ; Convert single ascii value to binary andlw B'00000111' ; Mask off upper bits to protect from overwriting deeper memory! movwf cmd0 call tobinary ; Convert ascii representation to binary (result in cmd3) movlw 0x13 ; Point to start of servo positions in memory addwf cmd0, W ; Add the cmd0 offset movwf FSR ; Then set up our pointer address movf cmd3, W ; W <- cmd3 movwf INDF ; Store new value at pointer reference! icleanup ; Clean up before returning from interrupt bcf INTCON, INTF ; Clear RB0 interrupt flag bit movf FSR, W movwf FSR ; Restore FSR register swapf status_temp, w movwf STATUS ; Restore status register swapf w_temp, F swapf w_temp, w end_interrupt retfie ; return from interrupt ;-------------------------------------------------------------------- ; Main program start main_code call main_init ; Beep LED 3 times movlw D'3' movwf temp3 ; temp3 = 3 init_led call beepled decfsz temp3, 1 ; temp3 = temp3 - 1 goto init_led bsf PORTA, 0 ; Turn LED on ; ; This is where all the fun begins... ; term_loop movlw B'10000000' ; Start mask on last (bit 7) servo movwf servo_mask movlw 0x1A ; This is where the servo positions end in memory. movwf FSR ; Store in indirect register each_servo call do_single_servo ; Handle this single output decf FSR, 1 ; Decrement pointer bcf STATUS, C ; Clear carry flag rrf servo_mask, 1 ; Rotate mask to right... btfss STATUS, C ; See if we're in carry yet goto each_servo ; If not, keep going ; ; Delay for post processing after 8 servos (14ms) ; ; movlw D'14' movlw D'6' movwf temp call delay_ms goto term_loop ; Repeat main loop for each servo _main__end ;------------------------------------------------------------------ ; handleask - Handles an 'A'sk command from the serial port handleask call tobinary ; Convert ascii representation to binary (result in cmd3) movlw 0x13 ; Point to start of servo positions in memory addwf cmd3, W ; Add the cmd3 (servo number) offset movwf FSR ; Then set up our pointer address movf INDF, W ; W <- servo[num] call printbyte ; Print the byte in ASCII on the seial port ; That's all there is... return ;------------------------------------------------------------------ ; Does the processing for a single servo output do_single_servo btfss servo_mask, 0 ; Hacking fix for RB0 goto do_ss_cont1 ; If here, then we have to use RA3 instead of RB0 call pinfix ; Do the pin fix for RB0 -> RA3 return do_ss_cont1 movf INDF, W ; W <- servo[num] btfsc STATUS, Z ; Hop around if servo pos == 0 goto do_ss_cont2 ; so we don't turn on servo pulse at all... movf servo_mask, W ; W <- servo_mask iorwf PORTB, 1 ; OR mask to PORTB to turn on current serv do_ss_cont2 movlw D'1' movwf temp ; Delay 1ms which is 0 base time call delay_ms movf INDF, W ; W <- servo[num] movwf ctr ; Store in counter ; Not entirely sure about this next line (currently untested) btfss STATUS, Z ; Skip call to stepfor if Z bit set (set on movf above) call stepfor comf servo_mask, 0 ; W <- ~servo_mask andwf PORTB, 1 ; PORTB = ~mask * PORTB (turn off current servo) movf INDF, W ; W <- servo[num] movwf ctr comf ctr, 1 ; Compliment...so (ctr = 255 - servo[num]) btfss STATUS, Z ; If ctr == 0, don't stepfor (it would cause problems!) call stepfor return ;------------------------------------------------------------------ ; pinfix - handles hack for using RA3 instead of RB0 pinfix movf INDF, W ; W <- servo[num] btfsc STATUS, Z ; Hop around if servo pos == 0 goto pinfix1 bsf PORTA, 3 pinfix1 movlw D'1' movwf temp call delay_ms movf INDF, W ; W <- servo[num] movwf ctr ; Store in counter ; Not entirely sure about this next line (currently untested) btfss STATUS, Z ; Skip call to stepfor if Z bit set (set on movf above) call stepfor bcf PORTA, 3 movf INDF, W ; W <- servo[num] movwf ctr comf ctr, 1 ; Compliment...so (ctr = 255 - servo[num]) btfss STATUS, Z ; If ctr == 0, don't stepfor (it would cause problems!) call stepfor return ;------------------------------------------------------------------ ; Implements a "for" loop with ctr. Uses/modifies ctr. ; Total instructions used per iteration: 5 stepfor nop nop ; New extra nops to double nop nop nop nop nop decfsz ctr, 1 goto stepfor return ;------------------------------------------------------------------ ; Initialization for main program entry main_init ; ; basic register setup and pin i/o steup ; kind of stuff every prog requires ; clrwdt ; Clear watchdog timer bcf INTCON, GIE ; Clear interrupts bsf STATUS, RP0 ; Select bank 1 movlw 0x01 movwf TRISB ; Set TRISB to 0x01 (all output except RB0 input) movlw D'2' movwf TRISA ; Set TRISA to 0x02 (Pins 1 is input) bcf OPTION_REG, INTEDG ; Configure interrupt to happen on FALLING edge bcf STATUS, RP0 ; Select bank0 back bcf PORTA, 0 ; Turn LED off bsf PORTA, 2 ; Set RS232 output to HIGH idle state clrf incbytes ; No incoming serial bytes... ; ; set up initial servo positions ; movlw D'127' ; Initial servo position (should be 0 for production) movwf pos0 movwf pos1 movwf pos2 movwf pos3 movwf pos4 movwf pos5 movwf pos6 movwf pos7 ; Turn all servo outputs off clrf PORTB bcf PORTA, 3 ; Now lets configure our interrupt on RB0 clrf INTCON ; Disable all interrupts bsf INTCON, INTE ; Enable RB0 external interrupt bsf INTCON, GIE ; Enable all unmasked interrupts return ;------------------------------------------------------------------ ; beepled - Flashes LED on pin A0 on then off with short delays beepled bsf PORTA, 0 movlw D'250' movwf temp call delay_ms movlw D'250' movwf temp call delay_ms bcf PORTA, 0 movlw D'250' movwf temp call delay_ms movlw D'250' movwf temp call delay_ms return ;------------------------------------------------------------------ ; delay_ms - Delays for number of milliseconds in temp ; Uses/modifies temp2 ; Assumes 10MHz clock ; 29 * 0.0000001 * 4 * 86 = 0.0009976 sec delay_ms delay_ms_1 movlw D'86' movwf temp2 nop nop delay_ms_2 nop ; 1 nop ; 2 nop ; 3 nop ; 4 nop ; 5 nop ; 6 nop ; 7 nop ; 8 nop ; 9 nop ; 10 nop ; 11 nop ; 12 nop ; 13 nop ; 14 nop ; 15 nop ; 16 nop ; 17 nop ; 18 nop ; 19 nop ; 20 nop ; 21 nop ; 22 nop ; 23 nop ; 24 nop ; 25 nop ; 26 decfsz temp2, 1 ; +1 goto delay_ms_2 ; +2 --> Total = 29 nop decfsz temp, 1 goto delay_ms_1 nop return ;------------------------------------------------------------------ ; RS232_putchar - Sends a char via serial port on pin RA2 ; Uses true RS232 polarity ; Char is passed in reg W RS232_putchar movwf RS232_datachar ; Put w into storage byte movlw D'9' ; Put 9 into W movwf RS232_counter ; Put W into counter byte (10 bits = 1 start + 8 data + 1 stop) bcf PORTA, 2 ; Set pin RA2 low (start bit) nop nop RS232_1 call RS232_bitdelay ; Delay for the bit decfsz RS232_counter, 1 ; See if all bits sent (decrement counter) goto RS232_2 ; if not, jump ahead bsf PORTA, 2 ; Otherwise, set pin high for stop bit call RS232_bitdelay ; wait for bit delay return ; and return RS232_2 rrf RS232_datachar, 1 ; Rotate byte right through carry, store result in RS232_datachar btfss STATUS, C ; Bit test C, skip if set goto RS232_3 ; C not set (bit is a 0) bsf PORTA, 2 ; Set pin output to high goto RS232_1 ; Loop for next bit RS232_3 bcf PORTA, 2 ; Set output pin low C is set (bit is a 1) goto RS232_1 ; Loop for next bit return ;------------------------------------------------------------------ ; RS232_getchar - Receives a char via serial port on pin RB0 ; Uses true RS232 polarity ; Char is returned in W RS232_getchar movlw D'8' movwf RS232_counter ; _counter__c2c_getchar clrf RS232_datachar ; _ret__c2c_getchar RS232_getch1 btfsc PORTB, 0 goto RS232_getch1 call RS232_bitdelay2 ; __c2c_rs232_delay2 btfsc PORTB, 0 goto RS232_getch1 RS232_getch2 call RS232_bitdelay ;__c2c_rs232_delay rrf RS232_datachar, F ; _ret__c2c_getchar, F bcf RS232_datachar, 7 ; _ret__c2c_getchar, 7 btfsc PORTB, 0 ; Input on PORTB bsf RS232_datachar, 7 ; _ret__c2c_getchar, 7 decfsz RS232_counter, 1 ; _counter__c2c_getchar, 1 goto RS232_getch2 call RS232_bitdelay ; __c2c_rs232_delay movf RS232_datachar, W ; _ret__c2c_getchar, W return ;------------------------------------------------------------------ ; RS232_bitdelay - Delays for a bit being sent over serial port ; Assumes 10MHz clock speed RS232_bitdelay movlw D'82' movwf RS232_delay_ctr RS232_bitdelay_1 decfsz RS232_delay_ctr, 1 goto RS232_bitdelay_1 return ;------------------------------------------------------------------ ; RS232_bitdelay2 - Delay used in receiving chars from serial port RS232_bitdelay2 movlw D'41' movwf RS232_delay_ctr RS232_bitdelay2_1 decfsz RS232_delay_ctr, 1 goto RS232_bitdelay2_1 return ;------------------------------------------------------------------ ; printcrlf - prints a CR+LF pair on the serial port ; Modifies: W, temp printcrlf movlw '\r' call RS232_putchar movlw '\n' call RS232_putchar return ;------------------------------------------------------------------ ; printbyte - Prints an 8-bit binary value in ASCII over the serial ; port. ; Input: W ; Modifes: W, ASCIIH, ASCIIT, ASCIIO printbyte movwf ASCIIO call toascii movf ASCIIH, 0 call RS232_putchar movf ASCIIT, 0 call RS232_putchar movf ASCIIO, 0 call RS232_putchar return ;------------------------------------------------------------------ ; tobinary - Converts a 3-digit ascii representation of a number ; into binary. ; Input: cmd1, cmd2, cmd3 ; Output: cmd3 tobinary movlw '0' subwf cmd1, 1 ; Convert these guys to binary first subwf cmd2, 1 subwf cmd3, 1 movlw D'100' tobin2 movf cmd1, 1 ; Test the file for zero btfsc STATUS, Z ; If zero, move on... goto tobin3 addwf cmd3, 1 ; Add 100 to cmd3 decf cmd1, 1 goto tobin2 tobin3 movlw D'10' tobin4 movf cmd2, 1 btfsc STATUS, Z goto tobin5 addwf cmd3, 1 ; Add 10 to cmd3 decf cmd2, 1 goto tobin4 tobin5 return ;------------------------------------------------------------------ ; toascii - Converts an 8-bit binary value in ASCIIO to an ; ASCII 3-digit representation. ; Output: ASCIIH - hundredths, ASCIIT - tenths, ASCIIO - ones toascii MOVLW '0' MOVWF ASCIIH MOVWF ASCIIT DO100S MOVLW D'100' SUBWF ASCIIO,W BNC DO10S MOVWF ASCIIO INCF ASCIIH, 1 GOTO DO100S DO10S MOVLW D'10' SUBWF ASCIIO,W BNC ADJUST MOVWF ASCIIO INCF ASCIIT, 1 GOTO DO10S ADJUST MOVLW '0' ADDWF ASCIIO, 1 return END