; 
;
; Development code for servo control with RS232 comm to host PC
; Version 2 -- initial development
;              * Changing how mask is built
;              * Removing servo_ctr var
;
; PINOUTS:
; 
; A0 - Status LED
; A1 - Input  - RS232 input (receive)
; A2 - Output - RS232 output (transmit)
; A3 - NC
; A4 - NC
; B0-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

;------------------------------------------------------------------
; Assembler defines
CR								equ 0x0D
LF								equ 0x0A

;------------------------------------------------------------------
; Reset vector

	ORG 0
	goto main_code

;------------------------------------------------------------------
; Interrupt vector

	ORG 4
_interrupt_vector
			; Do absolutely nothing....no interrupts used
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

	; DEBUGGING ONLY!!!
;	movlw		0x17				; DEBUG!!!
;	movwf		FSR					; DEBUG!!!
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



;	decfsz		servo_ctr, 1		; Decrement the counter
;	goto		each_servo

	;
	; 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

;------------------------------------------------------------------
; Does the processing for a single servo output
do_single_servo

	movf		servo_mask, W		; W <- servo_mask
	iorwf		PORTB, 1			; OR mask to PORTB to turn on current servo
	; Note: If servo pos value == 0, mask should be set to 0 and servo won't be turned on

	; Delay 1.25 ms
	call		delay_baseon

	movf		INDF, W				; W <- servo[num]
	movwf		ctr					; Store in counter

	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

;------------------------------------------------------------------
; Implements a "for" loop with ctr.  Uses/modifies ctr.
; Total instructions used per iteration: 5
stepfor
	nop
	nop
	decfsz		ctr, 1
	goto		stepfor
	return

;------------------------------------------------------------------
; Delays for 1.25 ms, which is the base "on" time for a pulse
; 3125 instructions = 125 * 25 == (approx) 41 * 25 * 3 + (41*5) (= 3280)
; Uses temp and temp2
;
; THIS ROUTINE WORKS...delay is closer to 1.3ms, but IT WORKS...
; MAYBE FINE TUNE WHEN CLOSER TO OVERALL SOLUTION
; DON'T QUESTION THIS ROUTINE FOR YOUR PROBLEMS.  :)  jp
;
;
delay_baseon
	
;	movlw		D'41'
	movlw		D'40'
	movwf		temp				; 41 stored in temp

delay_base_out

	movlw		D'25'
	movwf		temp2

delay_baseon_in

	decfsz		temp2, 1
	goto		delay_baseon_in

	decfsz		temp, 1
	goto		delay_base_out

	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		0x00
	movwf		TRISB				; Set TRISB to 0x00 (all output)
	movlw		D'2'
	movwf		TRISA				; Set TRISA to 0x02 (Pins 1 is input)
	bcf			STATUS, RP0			; Select bank0 back
	bcf			PORTA, 0			; Turn LED off
	bsf			PORTA, 2			; Set RS232 output to HIGH idle state

	;
	; 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

	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 RA1
; 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 PORTA, 1
	goto RS232_getch1
	call RS232_bitdelay2				; __c2c_rs232_delay2
	btfsc PORTA, 1
	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 PORTA, 1						; Input on port A
	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
;	movwf, inchar
	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: temp
; Modifes: W, ASCIIH, ASCIIT, ASCIIO

printbyte
	movf	temp, 0
	movwf	ASCIIO
	call	toascii
	movf	ASCIIH, 0
	call	RS232_putchar
	movf	ASCIIT, 0
	call	RS232_putchar
	movf	ASCIIO, 0
	call	RS232_putchar
	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
	GOTO    DO100S

DO10S
	MOVLW   D'10'
	SUBWF   ASCIIO,W
	BNC     ADJUST

	MOVWF   ASCIIO
	INCF    ASCIIT
	GOTO    DO10S

ADJUST
	MOVLW   '0'
	ADDWF   ASCIIO

	return


	END
