; Copyright 1996-2001 Andy Shaw ; ; This file is part of Neptune. ; ; Neptune is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; Neptune is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with Foobar; if not, write to the Free Software ; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ; ; Contact ; Details of the Neptune sub control system can be found at: ; www.gloomy-place.com ; for further information to report fixes etc. please contact Andy Shaw ; andy@gloomy-place.com ; ; ; General purpose functions ; ; Helper functions do division and multiplication by two, input ; in W result in W ; Mult2 movwf Temp ; get in mem addwf Temp, W ; and mult by two return ; and return value Div2 movwf Temp ; save in mem ready for shift bcf STATUS, C ; make sure status is clear rrf Temp, W ; shift right and return result to W andlw 0xff ; is it zero btfsc STATUS, Z ; if it is we don't need the sign bit return ; so just return btfsc Temp, 7 ; was it -ve ? iorlw 0x80 ; yes or in sign bit return ; return result ; ; Helper function to return absolute value of W ; Abs addlw 0x80 ; test to see if -ve xorlw 0x80 ; put things back btfss STATUS, C ; if it -ve C is set return ; go do +ve value sublw 0 ; make -ve val +ve return if TASKS > 0 ; ; Schedule tasks. This will be called every 2ms or so. We basically ; accumulate counters for 1, 10, 20, 50 and 100ms periods and schedule tasks ; associated with these events as required. ; Schedule movlw TE1MS ; get 1ms tasks iorwf TiTask, F ; and make then runnable decf TiCount, F ; decrement 1 ms counter decf TiCount, F ; and again movf TiCount, W ; get new value andlw 0xf ; check 1ms counter (bottom nib of w) btfss STATUS, Z ; is it zero ? if yes we have 10ms goto EndSchedule ; no so continue on movlw TE10MS ; get 10ms tasks iorwf TiTask, F ; make them runnable movlw .10 ; get value to reload counter iorwf TiCount, F ; and store it in lower nibble movlw -(1<<4) ; add -1 addwf TiCount, F ; to upper nibble to dec 10ms counter btfsc TiCount, 4 ; is bottom bit of upper nibble set goto Check50 ; yes so not multiple of 20ms check for 50ms movlw TE20MS ; get 20ms tasks iorwf TiTask, F ; make them runnable Check50 movf TiCount, W ; get new value andlw 0xf0 ; work on just the 10ms count addlw -(.5 <<4) ; is it 5? btfss STATUS, Z ; if it is Z is set goto Check100 ; not five so continue on movlw TE50MS ; get 50ms tasks iorwf TiTask, F ; make them runnable goto EndSchedule ; all done Check100 movf TiCount, W ; get new value andlw 0xf0 ; check 10ms counter (upper nib of w) btfss STATUS, Z ; is it zero ? if so we have 10*10ms goto EndSchedule ; not yet zero so continue movlw TE100MS|TE50MS ; get 50ms & 100ms tasks iorwf TiTask, F ; make them runnable movlw .10<<4 ; get new count iorwf TiCount, F ; and set it in upper nibble EndSchedule bcf TiTask, TETick ; clear int seen flag return ; ; Default timer handling. The following function provides a "safe" default timer function. ; It simply checks for a timer roll over event and marks the scheduler as ready to run. ; This function can be used in systems which do not make use of a clock interrupt. If this is ; the case this function should be called from Task0. ; TaskTick btfss INTCON, T0IF ; 2ms up yet? return ; no so no need to schedule anything bsf TiTask, TETick ; Set int seen flag bcf INTCON, T0IF ; and clear flag return ; ; Initialise the tasking system. ; TasksInit clrf TiTask ; No tasks runnable movlw .10<<4 | .10 ; get intial count for ms counter movwf TiCount ; and set it up return endif if (TASKS > 0) || (RXINPUT > 0) ; ; Setup the TMR0 and pre-scaler to provide a TMR0 count of ; approx 128 per 1 ms. This provides good resolution for ; capturing R/C pulse trains which are 1 to 2 ms in length. ; TMR0Init ; Set up TMR0 and pre-scaler bsf STATUS, RP0 ; select bank 1 movlw SCALE ; set up the prescaler value movwf (OPTION_REG&0x7f); and store into Option register bcf STATUS, RP0 ; select bank 0 clrf TMR0 ; Clear down the TMR0 bcf INTCON, T0IF ; clear int flag return endif page if RXINPUT > 0 ; ; Radio control Input Functions ; The following set of function perform the reading operation for a standard radio ; control input pulse (+ve pule of length 1-2mS with 20mS repeat factor). Various ; smoothing and anti-glitch functions are provided. ; LimitThrow LimitN MAXTHW LimitRXTO LimitN RXTO DeadBand DeadN DEADB CheckInvalid DeadN MAXVAL ; ; Setup the software RX functions ; RxInit ifdef NORXTO movlw RXTO ; get inital value for timeout movwf RxTOut ; and set it else movlw RXTOIN ; get inital value for timeout movwf RxTOut ; and set it call RxSignalLost ; Indicate no signal endif clrf RxStat ; and clear the status return ; Disbale Rx sampling so that ints do not screw up timing loops etc. RxDisable bcf INTCON, RBIE ; turn ints off movlw ~RXSMASK ; Reset state andwf RxStat, F ; back to initial state return ; ; Non interrupt time RX processing. To minimize time spent in the interrupt ; handler we do most of the work here. We make use of a state machine to ; define and handle the steps required to robtain and validate the RX pulse. ; The standard RX pulse consists of a +ve pulse of length 1-2ms repeated ; every 20ms. We assume that the system is setup with a clock that is free ; running with a period greater than 2ms. ; if (($+0x20)/0x100 != ($/0x100)) org (($+0xff)/0x100)*0x100 endif RxProcess movlw HIGH RxStates movwf PCLATH movf RxStat, W ; get current state andlw RXSMASK ; and out upper nibble addlw LOW RxStates ; add to form table offset movwf PCL ; and perform computed goto ; RxStates ; Start of RX state table goto RxSetup goto RxEnable goto RxWaitStart goto RxGotStart goto RxWaitEnd goto RxGotEnd RxEndStates ; ; Make sure that above table is all on one page! if (RxStates/0x100 != RxEndStates/0x100) ERROR 'Jump table is not contained within a single page' endif ; ; Set state machine into intial state ready to go ; RxSetup movlw RXCMASK ; andwf RxPort, F ; Indicate that we expect a +ve signal clrf RxTime ; Clear current time. incf RxStat, F ; move on to the next state return ; ; Enable the RX interrupt ensuring that the the signal is not currently set ; RxEnable ; bcf INTCON, RBIF ; clear down any old int movf RxPort, W ; Get current chan bits andlw RXCMASK ; mask out other bits andwf PORTB, W ; check current chan bit btfss STATUS, Z ; only enable when nothing happening return ; otherwise continue in this state bcf INTCON, RBIF ; clear down any old int bsf INTCON, RBIE ; and enable ints incf RxStat, F return ; ; Remain in this state waiting for the rising edge or for a timeout ; RxWaitStart btfsc RxStat, RXINT ; Int seen yet? incf RxStat, F ; yes so move to next state return ; otherwise contine to wait ; ; Got rising edge of pulse ensure still valid and start timing process ; RxGotStart movf RxPort, W ; Get current chan bits andlw RXCMASK ; mask out other bits andwf PORTB, W ; check current chan bit btfsc STATUS, Z ; bit should still be set goto RxBadValue ; abort sample movf RxTime, W ; get actual start time saved by int sublw 0 ; negate it ready for end calculation movwf RxTime ; and store back ready for int incf RxStat, F ; move to next state swapf RxPort, W iorwf RxPort, F ; indicate that we need a 0 bcf RxStat, RXINT ; clear int seen bit bsf INTCON, RBIE ; and enable ints return ; ; Wait for falling edge or timeout ; RxWaitEnd btfsc RxStat, RXINT ; Int seen yet? incf RxStat, F ; yes so move to next state return ; otherwise contine to wait ; ; Got final value so calculate things and finish up ; RxGotEnd movf RxPort, W ; Get current chan bits andlw RXCMASK ; mask out other bits andwf PORTB, W ; check current chan bit btfss STATUS, Z ; bit should not be set goto RxBadValue ; abort sample movf RxTime, W ; Get elapsed time addlw -RXBASE ; take off the base movwf RxTime ; and save for later call CheckInvalid ; check to make sure it is in range andlw 0xff ; if it is val is 0 btfss STATUS, Z ; goto RxBadValue ; got an invalid input movf RxTime, W ; get value call LimitThrow ; and make sure it is in range btfsc RxPort, RX0 ; Is this RX0 call Rx0NewValue ; Let application know about the new value if RXINPUT > 1 btfsc RxPort, RX1 ; Is this RX1 call Rx1NewValue ; Let application know about the new value endif if RXINPUT > 2 btfsc RxPort, RX2 ; Is this RX2 call Rx2NewValue ; Let application know about the new value endif movf RxTOut, W ; get current timeout val addlw RXGOOD ; inc with good value call LimitRXTO ; Limit range movwf RxTOut ; and save addlw -RXTO ; are we at the good value threshold btfsc STATUS, Z ; if we are Z will be set call RxSignalOK ; Tell the system about it RxBadValue bcf INTCON, RBIE ; Turn of interrupts clrf RxStat ; and reset state. bcf STATUS, C ; just in case rrf RxPort, W ; move on to next chan andlw RXCMASK ; check to see if we have completed the set btfsc STATUS, Z ; if we have we have a zero movlw 1<