; TITLE 'WH40K Rhino controller chip 6/30/00' ;Copyright Dennis Clark and TTT Enterprises. ;You may not publish or sell it without written permission from me, the ;author. ; ; I included the "include" file for the 508 for reference and changed it ; LIST P = 12C508A, F = INHX8M ; P12C508.INC Standard Header File, Version 1.02 Microchip Technology, Inc. ;========================================================================== ; Verify Processor ;========================================================================== IFNDEF __12C508A MESSG "Processor-header file mismatch. Verify selected processor." ENDIF ;========================================================================== ; Register Definitions ;========================================================================== W EQU H'0000' F EQU H'0001' ;----- Register Files ----------------------------------------------------- INDF EQU H'0000' ; Uses FSR to address data mem. TMR0 EQU H'0001' ; 8 bit real time clock/counter PCL EQU H'0002' ; Low order 8 bits of PC STATUS EQU H'0003' ; STATUS FSR EQU H'0004' ; Indirect data memory addr pointer OSCCAL EQU H'0005' ; Calibration data for osc. GPIO EQU H'0006' ; General Purpose I/O ;----- STATUS Bits -----------Page 14-------------------------------------- GPWUF EQU H'0007' ; GPIO reset bit PA0 EQU H'0005' ; Program Page preselect NOT_TO EQU H'0004' ; Time Out bit NOT_PD EQU H'0003' ; Power Down bit Z EQU H'0002' ; Zero bit DC EQU H'0001' ; Digit carry/*borrow bit C EQU H'0000' ; carry/*borrow bit ;----- OPTION Bits -----------Page 15-------------------------------------- NOT_GPWU EQU H'0007' ; Enable wake-up on pin change NOT_GPPU EQU H'0006' ; Enable weak pull-ups T0CS EQU H'0005' ; Timer0 clock source select T0SE EQU H'0004' ; Timer0 sources edge select PSA EQU H'0003' ; Prescalar assignment bit PS2 EQU H'0002' ;\ PS1 EQU H'0001' ; > Prescalar rate select bits PS0 EQU H'0000' ;/ ;========================================================================== ; RAM Definition ;========================================================================== __MAXRAM H'3F' ;========================================================================== ; Configuration Bits ;========================================================================== _MCLRE_ON EQU H'0FFF' _MCLRE_OFF EQU H'0FEF' _CP_ON EQU H'0FF7' _CP_OFF EQU H'0FFF' _WDT_ON EQU H'0FFF' _WDT_OFF EQU H'0FFB' _LP_OSC EQU H'0FFC' _XT_OSC EQU H'0FFD' _IntRC_OSC EQU H'0FFE' _ExtRC_OSC EQU H'0FFF' __CONFIG ( _MCLRE_OFF & _CP_OFF & _WDT_OFF & _IntRC_OSC ) ;========================================================================== ; Program Variables ;========================================================================== ;Port definitions and other constants or macros #define RMOTORS GPIO,5 ; Pin 2 = GP5 = bit 5 O #define RGUNS GPIO,4 ; Pin 3 = GP4 = Bit 4 O #define DATAIO GPIO,3 ; Pin 4 = GP3 = Bit 3 I - serial #define RLEDS GPIO,2 ; Pin 5 = GP2 = Bit 2 O #define S1 GPIO,1 ; Pin 6 = GP1 = Bit 1 O #define S0 GPIO,0 ; Pin 7 = GP0 = Bit 0 O #define DBIT shadow,3 ; data bit!!! don't forget! #define SERVO1 shadow,1 ; Shadow register Bit 1 #define SERVO0 shadow,0 ; Shadow register Bit 0 #define GUNS shadow,4 ; fire guns #define MOTORS shadow,5 ; make motor sounds #define LEDS shadow,2 ; flash LED some day #define SCONFIG B'11001000' ; Only really using first 2 servos #define DELAY1 D'19' ; 2400 BAUD delay amount (~405us) #define DELAY2 D'24' ; 1.3 bit times (start bit) #define SSTART D'16' ; Start of look up table #define CMDBIT sfsr,1 ; = 1 then its a command, not servos #define CBITS 0x00 ; clear bits mask #define BDELAY D'50' ; send data loop #define LOWS D'130' ; servo low value #define HIGHS D'254' ; servo high value #define SOFFSET D'128' ; offset to 1ms #define BFD 0x04 ; both forward #define BRV 0x07 ; both reverse #define PRT 0x05 ; pivot right #define PLF 0x06 ; pivot left #define O50 0x08 ; set GP5 to output = 0 #define O51 0x09 ; set GP5 to output = 1 #define O40 0x0C ; set GP4 to output = 0 #define O41 0x0D ; set GP4 to output = 1 #define O20 0x0E ; set GP2 to output = 0 #define O21 0x0F ; set GP2 to output = 1 LIST ;Register locations for important stuff,only 9-15 are available before table rcount EQU D'8' ; bit count rcvreg EQU D'9' ; incoming byte storage delay EQU D'10' ; delay storage register scratch EQU D'11' ; math scratch pad register ntmr EQU D'12' ; most recent TMR0 reading otmr EQU D'13' ; last stored TMR0 reading rocount EQU D'14' ; rollover count (10 max) shadow EQU D'15' ; I/O shadow register Serv0 EQU D'16' Serv1 EQU D'17' timing EQU D'18' ; 200ms timer sfsr EQU D'22' ; shadow FSR register sdata EQU D'23' ; shadow data register smask EQU D'24' ; servo enable mask ;================================ ; Startup Code = ;================================ ORG 0x1FF ; MOVLW 0x50 ; My EPROM calibration value,remove for OTPs ORG 0x000 MOVWF OSCCAL ; Store the factory osc. calibration value GOTO start ; jump to end of memory to make room for subs ;=========================================================================== ; This program will take a single byte of serial data at 2400 baud and ;control the chosen servo with one of 64 possible settings X 4, max 252. ;The two MS bit chooses the servo, 0-1 and the LS 6 bits the 64 positions. ;If the two MS bit = 1 then there is a special command being issued. ; ;commands with MSB = '1' follow. ; 132 (0x84) both forward ; 135 (0x87) both reverse ; 133 (0x85) pivot right ; 134 (0x86) pivot left ; 137 (0x89) turn motor sound on ; 141 (0x8D) turn guns sound on ; 143 (0x8F) turn LEDs on ; subtract one from each of the prior three to turn off those pins. ; ;Hopefully this is all in real-time. ;The servos will be given positions from approx 1ms to 2ms, or 90 degrees ;of motion and 1.4 degrees per step. ; ; If there is a match or near miss in the compare, then set that bit (servo) ;to '0'. If a new timer value is less than the last saved timer value then ;it means that we rolled over the counter. After 20ms all servos are set back ;to '1'. Any RS232 value is gotten in between about a dozen timer checks ;for all of the servos. There will be a little jitter, but not enough to ;matter, and this only during RS232 communication times. ; A 0 location on servo 0 will disable servos 0 and 1. It will not set the ;position of this servo, this will remain at its last setting, so will the ;setting for servo 1. When a 0 position is used for position 1 then ;servos 0 and 1 will be re-enabled. You can change the starting position ;of servos 0 and 1 while they are disabled and they will start up at the ;new positions as soon as they are re-enabled. ;Servo 1 will have it's value inverted so that sending the same number to ;either the left or right servo will result in proper left/right motor ;operation. Motors will be left on for only 2 seconds at a time, then an ;auto shutoff will occur - If I get to this part anyway... ; Adding a 200ms 'on' pulse on the unused I/O pin for sound effects. ; ;Copyright Dennis Clark and TTT Enterprises Revision A.1 7/4/00 ;=========================================================================== ;================================ ; ; Subroutines Follow ; ;================================ getbyte ;--------------------------------- ; This is the routine that will ;get the data byte from the serial ;line. We do LOTS of in-between ;servo value checking while doing ;math here, to stop jitter. ;--------------------------------- MOVLW 6 ; only getting first 6 bits of positional data here MOVWF rcount ; save bit count here CLRF sdata ; clear out the receive register CLRF sfsr ; clear out the servo register MOVLW DELAY2 ; 1.3 bit times to move well into the next bit MOVWF delay ; delay counter r_next CALL bdelay ; do the delay, which checks all servo timers too BCF STATUS,C ; default to zero for incoming bit RRF sdata,F ; and rotate it into the receive buffer BTFSC DATAIO ; check the incoming bit now BSF sdata,7 ; nope, its a one really MOVLW DELAY1 ; normal delay time MOVWF delay DECFSZ rcount,F ; and loop to get the rest GOTO r_next BCF STATUS,C ; leave data rotated one place (mult by 2) RRF sdata,F ; rotate to proper location MOVLW DELAY1 ; 1 bit time to first servo bit MOVWF delay CALL bdelay ; do the delay, which checks all servo timers too BTFSC DATAIO ; check the incoming bit now BSF sfsr,0 ; nope, its a one really MOVLW DELAY1 ; 1 bit time to second servo bit MOVWF delay CALL bdelay ; do the delay, which checks all servo timers too BTFSC DATAIO ; check the incoming bit now BSF sfsr,1 ; nope, its a one really MOVLW DELAY1 ; delay time for stop bit MOVWF delay CALL bdelay BTFSS DATAIO ; check stop bit, must = 1 or its a nogo GOTO freturn ; its 0, framing error, don't use this cforc BTFSC CMDBIT ; check for a command bit set (MSB = 1) GOTO docmd ; go here to decode the command cfor0 MOVF sdata,F ; check for zero value, here no settings change BTFSS STATUS,Z GOTO gotall ; Naw, just leave MOVF sfsr,F ; check for zero on zero servo BTFSS STATUS,Z GOTO cfor1 ; There we look for setting others to zero MOVLW 0x3C ; masking off servo 1 and 0 ANDWF smask,F GOTO freturn ; all done, servo 0 and 1 are now disabled cfor1 MOVLW 0x03 ; clear all mask bits IORWF smask,F GOTO freturn ; do NOT update servo locations! gotall ; now have the data, we need to decode and use it MOVLW SSTART ; FSR location MOVWF FSR MOVF sfsr,W ; get new FSR ADDWF FSR,F MOVF sdata,W ; get new data BTFSS sfsr,0 ; look for servo 1 to invert value GOTO dsavit ; servo 0, don't invert XORLW 0xff ; invert data ANDLW 0xfe ; ditch lower one bit dsavit IORLW 0x80 ; set MSB always (effectively add 128) MOVWF INDF ; save new servo value MOVLW D'5' SUBWF INDF,F ; subtract for compensation GOTO freturn ; all done with servo settings docmd ; decode specific command here RRF sdata,F ; rotate data to correct location ANDLW 0x3F ; only look at lower 6 bits MOVLW BFD ; check for full forward SUBWF sdata,W BTFSC STATUS,Z GOTO dofwd ; do the forward thing MOVLW BRV ; check for full reverse SUBWF sdata,W BTFSC STATUS,Z GOTO dorev ; do the reverse thing MOVLW PRT ; check for pivot right SUBWF sdata,W BTFSC STATUS,Z GOTO doprt ; do pivot right MOVLW PLF ; check for pivot left SUBWF sdata,W BTFSC STATUS,Z GOTO dolft ; do pivot left MOVLW O41 ; check for guns activation SUBWF sdata,W ; sdata - W -> W BTFSC STATUS,Z ; GOTO doguns ; do guns thing MOVLW O51 ; check for motor sounds SUBWF sdata,W BTFSC STATUS,Z GOTO domotors ; do motor sounds MOVLW O21 ; check for LED turned on SUBWF sdata,W BTFSC STATUS,Z GOTO doleds ; activate LEDs GOTO ncheck ; place holder for future commands dofwd MOVLW LOWS ; go forward MOVWF Serv0 MOVLW HIGHS MOVWF Serv1 GOTO freturn dorev MOVLW HIGHS MOVWF Serv0 MOVLW LOWS MOVWF Serv1 GOTO freturn doprt MOVLW HIGHS MOVWF Serv0 MOVWF Serv1 GOTO freturn dolft MOVLW LOWS MOVWF Serv0 MOVWF Serv1 GOTO freturn doguns BSF GUNS ; set GUNS bit in shadow register MOVLW 0xFF MOVWF timing ; set timing to 255 GOTO freturn domotors BSF MOTORS ; set MOTORS bit in shadow register MOVLW 0xFF MOVWF timing ; set timing to 255 GOTO freturn doleds BSF LEDS ; set LEDS bit in shadow register MOVLW 0xFF MOVWF timing ; set timing to 255 ncheck ; decode next command freturn RETLW 0 ; We are done with data and check, return bdelay ;--------------------------------- ; This will do the usual 417us ; delay, but it will do useful ; stuff during the delay, it will ; check each servo for proper '0' ; settings. It will also check ; for rollover to reset all servos ;--------------------------------- MOVF shadow,W MOVWF GPIO ; output the shadow register now bloop ; Just service the servos here then MOVF Serv0,W SUBWF TMR0,W ; ntmr - W -> W BTFSC STATUS,C ; C = 1 then W is less than TMR0 BCF SERVO0 ; bit now = 0 MOVF Serv1,W ; Get servo to scan SUBWF TMR0,W ; TMR0 - W -> W BTFSC STATUS,C ; C = 1 then W is less than TMR0 BCF SERVO1 ; bit now = 0 MOVF TMR0,W ; get THE most recent timer value MOVWF ntmr ; and make it the last saved value SUBWF otmr,W ; subtract from the last saved value BTFSS STATUS,C ; if c = 1 then we rolled over GOTO bdone ; no rollover brllovr MOVLW 3C ; clear all servo bits ANDWF shadow,F ; always output through shadow register CLRF TMR0 ; and start at zero again CLRF ntmr DECF rocount,F ; decrement the rollover count BTFSS STATUS,Z ; do a rollover if we have 0 GOTO bdone ; do nothing more if not brset MOVF smask,W ; Set all unmasked servos back to '1' IORWF shadow,F ; and set the bits now MOVLW 0x0A ; and reset counter MOVWF rocount bdone MOVF ntmr,W ; Get that last one MOVWF otmr ; and make it the last saved value MOVF shadow,W MOVWF GPIO ; output the shadow register now DECFSZ delay,F ; decrement loop count GOTO bloop ; and do it again NOP RETLW 0 ; all done now ;========================================================================= ; MAIN PROGRAM STARTS HERE ;========================================================================= start MOVLW SCONFIG ; Set GPIO all but 3 as outputs TRIS GPIO ; Configure pins MOVLW B'01000010' ; Set OPTION bits Prescaler divide by 8 OPTION ; Implement OPTION bits MOVLW SSTART ; preset servos to center position MOVWF FSR MOVLW D'187' ; This is ~1.5ms (1/2 90 deg servo swing)(192) MOVWF INDF ; servo 0 INCF FSR,F MOVWF INDF ; servo 1 MOVLW 0x00 ; set all servos to '0' MOVWF shadow ; save in the shadow register MOVWF GPIO ; set the bits MOVLW CBITS ; All servos masked off MOVWF smask CLRF otmr ; initial setup, whatever... MOVLW 0x0A ; rollover count MOVWF rocount ; reset rollover count too CLRF TMR0 CLRF timing ; set timing to 0 main mloop BTFSS DATAIO ; Look for a start bit CALL getbyte ; Start bit detected, get the input byte now MOVLW 0x01 MOVWF delay CALL bloop ; Nope, just check out the servos then MOVLW 0x01 SUBWF rocount,W ; have we done 9 rollovers? BTFSS STATUS,Z GOTO mloop ; nope, still wating for a rollover MOVF timing,F BTFSC STATUS,Z ; don't decrement timer if = 0 GOTO mloop DECFSZ timing,F ; decrement timer, check for done GOTO mloop ; not done yet, still counting down BCF MOTORS ; clear all pending bits BCF GUNS BCF LEDS emloop GOTO mloop ; and start the loop over again END