; $Header: d:/usr/max/progs/pic/rx2/RCS/rx2_c84.asm 2.2 99/06/20 20:59:05 max Exp $ ;-----------------------------------------------------------------------------; ; Micro control for RIG RX2 vhf weather receiver ; ; ; ; Version 4.0: Heavily modified from the original by Max Hadley to add ; ; RS-232 control and simplify customisation. ; ; mail: max@susato.demon.co.uk ; ; See RCS header above for release date ; ;-----------------------------------------------------------------------------; ;This source code is written for Microchip's Windows based assemmbler V01.40 ;and later. (Including MPLAB) ; ; $Log: rx2_c84.asm $ ; ; Revision 2.3 05/04/22 14:59:00 sam ; Corrected double entry of channel 9 in LSB and MSB. Now reads channel 8 ; and channel 9. ; ; Revision 2.2 99/06/20 20:59:05 max ; Added power-on lamp test delay to prevent scan task from suffereing ; premature AOS as the tone decoder powers up. ; ; Revision 2.1 99/06/09 21:56:07 max ; Added i2c_channel to prevent garbage being written to the synthesiser ; if the channel is changed by another task while i2c_task is part way ; through programming it. Moved initialisation code to the end to make ; more room in page 0 for jump tables. ; Relocated variables from the start of data RAM. ; Added some extra comments. ; ; Revision 1.5.1.4 99/06/06 22:15:28 max ; Split 'button_task' out from 'input_task' to simplify logic. RD and BD (was FE) ; bits are now in separate registers input_state and button_state, and only one ; at most can be set at a time by serial_task ; ; Revision 1.5.1.3 99/06/04 22:25:03 max ; Completed change to new tasking model and external customisation file ; Ready for more extensive testing ; ; Revision 1.5.1.2 99/06/02 18:21:13 max ; Completed change to new tasking model (interim version - untested) ; ; Revision 1.5.1.1 99/05/23 21:53:48 max ; A start at adding a system timer, and switching the input task to ; a table-driven state machine, with a unified approach to timeouts. ; All tasks will be called at the same tick rate. ; ; Revision 1.5 99/05/23 19:01:09 max ; Added I2C task to ensure serial data reception is not affected by ; bus communication. ; Re-organised user variables, and removed some redundant locations. ; Changed some ??_serv names to ??_task to make the operation more explicit. ; Moved routines around to keep table lookups in the first 256 words of ; program space ; ; Revision 1.4 99/05/23 13:56:24 max ; Changed serial input task to use a table-driven state machine ; implementation. Remove ser_tick as it is no longer used. ; ; Revision 1.3 99/05/16 21:43:32 max ; Added buffer register to serial handler to avoid problems with delay ; before received character can be handled. ; Replaced key_serv with input_serv to handle serial input as well as ; button pushes ; -- note: this version stops serial input while programming the ; -- synthesiser chip ; ; Revision 1.2 99/05/16 12:29:52 max ; Revised timings of cyclic executive and added 1200 baud ; serial input routine ser_serv ; ; ;For data on this device and development software see Microchip's site at ;http://www.microchip.com or contact a Microchip supplier. ; ;Pin uses of PIC16C84, PIC16F83 or PIC16C554 ; ; PIN NO PIN USE ; ; 1 RA2 channel switch, active low, also used as serial data in. ; 2 RA3 scan input, low to stop scan ; 3 RA4 beep ; 4 RESET Tie to vdd. internal reset used.(Backed up by watchdog.) ; 5 vss negative or ground ; 6 RB0 segment A ; 7 RB1 segment B ; 8 RB2 segment C ; 9 RB3 segment D ; 10 RB4 segment E ; 11 RB5 segment F ; 12 RB6 segment G ; 13 RB7 decimal point (scan led) ; 14 vdd positive 5v supply ; 15 clk/out crystal ; 16 clk/in crystal ; 17 RA0 SCL to synth ; 18 RA1 SDA to synth, pull high with 10K ; ;############################################################################## ;############################################################################## list p=16F84A __config b'11111111111101' ; ;Configuration Byte for:Watchdog on, Startup timer on, normal Xt crystal ;oscilator (30pf crystals) no code protection. ;-----------------------------------------------------------------------------; ; SFR locations ; ;-----------------------------------------------------------------------------; TMR0 equ 01 PCL equ 02 STATUS equ 03 FSR equ 04 porta equ 05 portb equ 06 PCLATH equ 0a intcon equ 0b option_reg equ 81 trisa equ 85 trisb equ 86 ;-----------------------------------------------------------------------------; ; User variables ; ;-----------------------------------------------------------------------------; button_state equ 0c ; state of button task ser_state equ 0d ; State of s/w UART ser_input_reg equ 0e ; used for character assembly ser_data equ 0f ; Received serial data buffer I2C_state equ 10 ; State of I2C transmitter loop_count equ 11 ; counts bits of transmitted character i2c_data equ 12 ; transmitted character i2c_channel equ 13 ; channel currently being programmed input_state equ 14 ; State of input handling task scan_state equ 15 ; State of channel scanner beep_mask equ 16 ; 'state' of beeper channel_no equ 17 ; Current channel SysTimerLow equ 18 ; System timer - cycles in 18.2 secs SysTimerHigh equ 19 BeepTimeoutLow equ 1a ; Timer for beeps BeepTimeoutHigh equ 1b DebounceTimeout equ 1c ; Timer for button debouncing KeypressTimeout equ 1d ; Timer for long keypress to start scan ScanTimeout equ 1e ; Timer for AOS or LOS wait ;-----------------------------------------------------------------------------; ; Bit equates ; ;-----------------------------------------------------------------------------; C equ 0 ; clear bit in STATUS Z equ 2 ; Zero bit in STATUS t0if equ 2 ; t0 interupt flag in intcon RD equ 1 ; in input_state, 1 if serial data received BD equ 2 ; in button_state, 1 if button down ;-------------------------------------------------------------------------------; ; Label values & defines ; ;-------------------------------------------------------------------------------; s_out_sda_out equ b'00001100' ; tristate masks for I2C bus states s_out_sda_in equ b'00001110' s_in_sda_in equ b'00001111' tsa_mod_add equ b'11000100' rc_data equ b'01100100' time_tick equ .188 ; Counter value for 1/3 bit time at 1200 ; baud (T = (256-188)*4 + 5 microseconds) ; actual baud rate is 1203.4 baud ; was 185 in original ; defines to support customisation file TSA6057 equ .6057 ; chip types TSA6060 equ .6060 HighCurrent equ 1 ; PLL control bit states LowCurrent equ 0 ; convert F in kilohertz to the synthesiser control bytes #define DataByte0(F) low ((2 *(((F) - .10700)/.10)) | ChargePump) #define DataByte1(F) high (2 *(((F) - .10700)/.10)) ; convert T in milliseconds to a count of system timer high byte ticks #define SysHighTicks(T) ((T) / d'71') ; convert T in milliseconds to a count of system timer low byte ticks #define SysLowTicks(T) ((((T) % d'71') * d'36') / d'10') ; Macro to build a function to start a beep of the duration passed as argument ; (time in milliseconds) BeepFor MACRO duration movlw SysLowTicks(duration) addwf SysTimerLow,w movwf BeepTimeoutLow movlw SysHighTicks(duration) btfsc STATUS,C addlw .1 addwf SysTimerHigh,w movwf BeepTimeoutHigh movlw b'00010000' movwf beep_mask return ENDM ; Read in customisation file INCLUDE RX2_opts.inc ;-----------------------------------------------------------------------------; ; Port A pin assignments ; ;-----------------------------------------------------------------------------; key_port equ porta scl equ 0 sda equ 1 button equ 2 ; bit 2 serin equ 2 ; also bit 2! scan_control equ 3 ; bit 3 beep_pin equ 4 ; bit 4 ;-----------------------------------------------------------------------------; ; Port B pin assignments ; ;-----------------------------------------------------------------------------; display_port equ portb scan_led equ 7 ; bit 7 ;-----------------------------------------------------------------------------; ; Reset and Interrupt vector area ; ;-----------------------------------------------------------------------------; org 0 ; start of program rom area reset_vector goto initialise org 4 interrupt_vector goto initialise ; should never have any interrupts ;-----------------------------------------------------------------------------; ; Make7Seg: converts bcd (or binary) in w to seven segment data for ; ; common anode display, result left in w. 0x0a is a 't' ; ;-----------------------------------------------------------------------------; Make7Seg addwf PCL,f IF (Make7SegTableEnd > 0FF) MESSG "Jump table out of page 0 in Make7Seg" ENDIF IF ((Make7SegTableStart - 1) & 0FF00) != ((Make7SegTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in Make7Seg" ENDIF Make7SegTableStart retlw b'11000000' ; '0' retlw b'11111001' ; '1' retlw b'10100100' ; '2' retlw b'10110000' ; '3' retlw b'10011001' ; '4' retlw b'10010010' ; '5' retlw b'10000010' ; '6' retlw b'11111000' ; '7' retlw b'10000000' ; '8' retlw b'10010000' ; '9' retlw b'10000111' ; 't' is effectively channel 10 Make7SegTableEnd ;-----------------------------------------------------------------------------; ; LSB: generate frequency programming LSB from channel number & return ; ;-----------------------------------------------------------------------------; LSB addwf PCL,f IF (LSBTableEnd > 0FF) MESSG "Jump table out of page 0 in LSB" ENDIF IF ((LSBTableStart - 1) & 0FF00) != ((LSBTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in LSB" ENDIF LSBTableStart retlw DataByte0(Chan0Freq) retlw DataByte0(Chan1Freq) retlw DataByte0(Chan2Freq) retlw DataByte0(Chan3Freq) retlw DataByte0(Chan4Freq) retlw DataByte0(Chan5Freq) retlw DataByte0(Chan6Freq) retlw DataByte0(Chan7Freq) retlw DataByte0(Chan9Freq) retlw DataByte0(Chan9Freq) retlw DataByte0(TestChanFreq) LSBTableEnd ;-----------------------------------------------------------------------------; ; MSB: generate frequency programming MSB from channel number & return ; ;-----------------------------------------------------------------------------; MSB addwf PCL,f IF (MSBTableEnd > 0FF) MESSG "Jump table out of page 0 in MSB" ENDIF IF ((MSBTableStart - 1) & 0FF00) != ((MSBTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in MSB" ENDIF MSBTableStart retlw DataByte1(Chan0Freq) retlw DataByte1(Chan1Freq) retlw DataByte1(Chan2Freq) retlw DataByte1(Chan3Freq) retlw DataByte1(Chan4Freq) retlw DataByte1(Chan5Freq) retlw DataByte1(Chan6Freq) retlw DataByte1(Chan7Freq) retlw DataByte1(Chan9Freq) retlw DataByte1(Chan9Freq) retlw DataByte1(TestChanFreq) MSBTableEnd ;-----------------------------------------------------------------------------; ; I2C task: transmits data to the synthesiser chip, one byte per tick ; ;-----------------------------------------------------------------------------; I2C_task movf I2C_state,w ; Get state (0 to 8) IF (I2CStateTableEnd > 0FF) MESSG "Jump table out of page 0 in I2C_task" ENDIF IF ((I2CStateTableStart - 1) & 0FF00) != ((I2CStateTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in I2C_task" ENDIF addwf PCL,f ; vectored goto based on current state I2CStateTableStart return ; 0 (waiting for something to do) goto SendI2CStart ; 1 goto SendMainAddress ; 2 goto SendSubAddress ; 3 goto SendDataByte0 ; 4 goto SendDataByte1 ; 5 goto SendDataByte2 ; 6 goto SendDataByte3 ; 7 goto SendI2CStop ; 8 I2CStateTableEnd SendI2CStart movf channel_no,w ; Save current channel in case it is movwf i2c_channel ; changed underneath us while programming call i2c_start incf I2C_state,f return SendMainAddress movlw tsa_mod_add ; Get synthesiser module address call i2c_tx IF SynthChip == TSA6057 incf I2C_state,f ELSE IF SynthChip == TSA6060 movlw 2 addwf I2C_state,f ELSE ERROR "Synthesiser chip type not defined!" ENDIF ENDIF return SendSubAddress movlw 0 call i2c_tx incf I2C_state,f return SendDataByte0 movfw i2c_channel call LSB ; Synthesiser divider LSB call i2c_tx incf I2C_state,f return SendDataByte1 movfw i2c_channel call MSB ; Synthesiser divider MSB call i2c_tx incf I2C_state,f return SendDataByte2 movlw rc_data ; Synthesiser control bits LSB call i2c_tx incf I2C_state,f return SendDataByte3 movlw 0 ; Synthesiser control bits MSB call i2c_tx incf I2C_state,f return SendI2CStop call i2c_stop ; Send stop condition clrf I2C_state ; Finished I2C transaction... movfw channel_no ; Maybe, check if the current channel subwf i2c_channel,w ; is the one we just programmed btfss STATUS,Z incf I2C_state,f ; No! re-start I2C with new channel return ;-----------------------------------------------------------------------------; ; Scan task: monitors channels for AOS/LOS when active ; ;-----------------------------------------------------------------------------; scan_task movf scan_state,w ; Get state (0 to 3) IF (ScanStateTableEnd > 0FF) MESSG "Jump table out of page 0 in Scan Task" ENDIF IF ((ScanStateTableStart - 1) & 0FF00) != ((ScanStateTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in Scan Task" ENDIF addwf PCL,f ; vectored goto based on current state ScanStateTableStart return ; 0 (not scanning) goto ChangeChannel ; 1 (scanning, jump to next channel) goto CheckForAOS ; 2 (scanning, looking for AOS) goto CheckForLOS ; 3 (scanning, signal on this channel) ScanStateTableEnd CheckForAOS btfss key_port,scan_control ; Any signal? goto AOS ; Yes, we have AOS movf ScanTimeout,w ; no, check if the scan delay is up subwf SysTimerHigh,w btfss STATUS,Z return ; still waiting, just return movlw 1 ; yes, try the next channel movwf scan_state return AOS call VeryLongBeep movlw SysHighTicks(LOSWait) ; add LOSWait in timer ticks addwf SysTimerHigh,w ; to system timer high byte movwf ScanTimeout ; and save in scan timer movlw 3 movwf scan_state ; Goto next state return ChangeChannel call NextChannel call ProgramSynth call UpdateDisplay movlw SysHighTicks(AOSWait) ; add AOSWait in timer ticks addwf SysTimerHigh,w ; to system timer high byte movwf ScanTimeout ; and save in scan timer movlw 2 ; Now wait for AOS movwf scan_state return CheckForLOS btfsc key_port,scan_control ; check for signal goto LOS ; no signal, wait for LOSWait to finish movlw SysHighTicks(LOSWait) ; still got signal, move timeout addwf SysTimerHigh,w ; on another LOSWait seconds movwf ScanTimeout return LOS movf ScanTimeout,w ; check if the scan delay is up subwf SysTimerHigh,w btfss STATUS,Z return ; no, just return call Beep movlw 1 ; Yes, change channel movwf scan_state return ;-----------------------------------------------------------------------------; ; Serial task: builds up characters from serial data on the ; ; serial input (= button) pin, and detects button pushes. ; ; Note that the pin is always sampled on the first instruction after ; ; the jump table to minimise sampling jitter. ; ;-----------------------------------------------------------------------------; serial_task movf ser_state,w ; Get state (0 to 29) IF (SerStateTableEnd > 0FF) MESSG "Jump table out of page 0 in Serial Task" ENDIF IF ((SerStateTableStart - 1) & 0FF00) != ((SerStateTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in Serial Task" ENDIF addwf PCL,f ; vectored goto based on current state SerStateTableStart goto WaitForStartBit ; 0 goto CheckStartBit ; 1 goto NextStateSer ; 2 goto NextStateSer ; 3 goto ReceiveBit ; 4 (sample bit 0) goto NextStateSer ; 5 goto NextStateSer ; 6 goto ReceiveBit ; 7 (sample bit 1) goto NextStateSer ; 8 goto NextStateSer ; 9 goto ReceiveBit ; 10 (sample bit 2) goto NextStateSer ; 11 goto NextStateSer ; 12 goto ReceiveBit ; 13 (sample bit 3) goto NextStateSer ; 14 goto NextStateSer ; 15 goto ReceiveBit ; 16 (sample bit 4) goto NextStateSer ; 17 goto NextStateSer ; 18 goto ReceiveBit ; 19 (sample bit 5) goto NextStateSer ; 20 goto NextStateSer ; 21 goto ReceiveBit ; 22 (sample bit 6) goto NextStateSer ; 23 goto NextStateSer ; 24 goto ReceiveBit ; 25 (sample bit 7) goto NextStateSer ; 26 goto NextStateSer ; 27 goto CheckStopBit ; 28 return ; 29 (used to prevent input & lock into test mode) SerStateTableEnd WaitForStartBit btfss key_port,serin ; test for start bit incf ser_state,f ; found one! return CheckStartBit btfss key_port,serin ; test for start bit still there goto NextStateSer clrf ser_state ; no start bit, just noise return ReceiveBit btfss key_port,serin ; Get input pin to C bit bsf STATUS,C ; (C is always clear after vectored GOTO) rrf ser_input_reg,f ; shift data in from C (lsb first) NextStateSer incf ser_state,f return CheckStopBit btfss key_port,serin ; check if the stop bit is there goto GotButton bsf input_state,RD ; set received data flag comf ser_input_reg,w ; invert received data... movwf ser_data ; ...and copy to buffer register clrf ser_state ; back to hunting for a start bit return GotButton bsf button_state,BD ; set BD bit to signal button_task clrf ser_state ; back to hunting for a start bit return ;-----------------------------------------------------------------------------; ; input_task: called each tick to process serial input ; ;-----------------------------------------------------------------------------; input_task movf input_state,w ; Get state (0 to 3) bcf input_state,RD ; Clear bit to show we handled the character IF (InputStateTableEnd > 0FF) MESSG "Jump table out of page 0 in Input Task" ENDIF IF ((InputStateTableStart - 1) & 0FF00) != ((InputStateTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in Input Task" ENDIF addwf PCL,f ; vectored goto based on current state ; and the RD and BD status bits InputStateTableStart ; no character waiting... return ; 0 (waiting for character) return ; 1 (got an 'F', waiting for digit) ; RD bit high, character available.. goto CheckForCmd ; 2 (waiting for key or character) goto CheckForDigit ; 3 (got an 'F', waiting for digit) InputStateTableEnd ; ; A character has arrived while we are expecting a command. Check if it is F, ; and if so, await the following digit. If not, it may be an S, in which case ; start scanning ; CheckForCmd movf ser_data, w ; Get new character andlw b'11011111' ; convert l/c to U/C xorlw 'F' btfsc STATUS, Z ; was it 'f' or 'F'? goto WaitForDigit ; Yes, goto state 1 xorlw 'F' ^ 'S' ; restore & test for 'S' btfss STATUS,Z ; was it 's' or 'S'? return ; No, stay in current state call StartScanning ; Yes, start scanning WaitForCmd clrf input_state return WaitForDigit incf input_state,f ; (wait for digit) return ; ; A character has arived while we are expecting a digit (channel number) ; CheckForDigit movlw '0' subwf ser_data, w ; subtract '0' from Rx'ed char btfss STATUS, C ; did we borrow? (< '0') goto WaitForCmd ; yes, abandon input sublw '9' - '0' btfss STATUS, C ; did we borrow? (> '9') goto WaitForCmd ; yes, abandon input movlw '0' ; subwf ser_data, w ; recover Rx'ed char - '0' movwf channel_no ; save channel number clrf scan_state ; stop scanning (if we were) call ProgramSynth ; set synthesiser and call UpdateDisplay ; display clrf input_state return ;-----------------------------------------------------------------------------; ; Button_task: handles button pushes to change channel and switch in and out ; ; of scan mode ; ;-----------------------------------------------------------------------------; button_task movf button_state,w ; Get state (0 to 15) bcf button_state,BD ; Clear the button bit as we processed it IF (ButtonStateTableEnd > 0FF) MESSG "Jump table out of page 0 in Button Task" ENDIF IF ((ButtonStateTableStart - 1) & 0FF00) != ((ButtonStateTableEnd - 1) & 0FF00) MESSG "Jump table crosses page boundary in Button Task" ENDIF addwf PCL,f ; vectored goto based on current state ; and the BD bit ButtonStateTableStart ; Button up... return ; 0 (waiting for key) goto CheckTimers ; 1 (key down, waiting for for 0.5 secs) goto WaitForKeyUp ; 2 (key still down after 0.5 secs) goto initialise ; 3 Can't happen! ; Button down... goto ButtonDown ; 4 goto KeyDown ; 5 goto KeyStillDown ; 6 goto initialise ; 7 Can't happen! ButtonStateTableEnd ; ; the button has been pressed for the first time. Set timers and ; stop scanning ; ButtonDown clrf scan_state ; stop scanning call NextChannel call ShortBeep call ProgramSynth call UpdateDisplay movlw SysLowTicks(DebounceTime) ; add debounce in timer ticks addwf SysTimerLow,w ; to system timer low byte movwf DebounceTimeout ; and save in debounce timer movlw SysHighTicks(LongButtonTime) ; Add long keypress wait time addwf SysTimerHigh,w ; to system timer high byte movwf KeypressTimeout ; and save in keypress timer incf button_state,f ; next state is KeyDown (=1) return ; ; Come here when we are waiting to see if we have a long (0.5s) keypress, and the ; button is down ; KeyDown movlw SysLowTicks(DebounceTime) ; Button is down, so reset addwf SysTimerLow,w ; debounce timer to 50ms from movwf DebounceTimeout ; now goto CheckKeypressTimer ; ; Come here when we are waiting to see if we have a long (0.5s) keypress, and the ; button is not down just at present ; CheckTimers movf DebounceTimeout,w ; check if debounce timed out subwf SysTimerLow,w btfsc STATUS,Z ; yes, the button really is up, goto WaitForButton ; back to state 0 CheckKeypressTimer movf KeypressTimeout,w ; no, button still down, check if subwf SysTimerHigh,w ; the half second is up btfss STATUS,Z ; Has the half second timed out? return ; No call Beep ; Yes, start scanning... call StartScanning incf button_state,f ; ...and wait for the key to come up (in state 2) return WaitForButton clrf button_state ; Yes, back to state 0 return ; ; Come here when the key has been pressed for 0.5s, and is still pressed ; KeyStillDown movlw SysLowTicks(DebounceTime) ; Key is still down, so reset addwf SysTimerLow,w ; debounce timer movwf DebounceTimeout return ; ; Come here when the key has been pressed for 0.5s, but is not currently pressed ; WaitForKeyUp movf DebounceTimeout,w ; check if debounce timed out subwf SysTimerLow,w btfsc STATUS,Z clrf button_state ; Yes, back to state 0 return ;------------------------------------------------------------------------------; ; Beep task, called every beep half cycle. Turns off beeper by changing ; ; beep_mask when the current beep times out ; ;------------------------------------------------------------------------------; beep_task movf BeepTimeoutLow,w subwf SysTimerLow,w btfss STATUS,Z return movf BeepTimeoutHigh,w subwf SysTimerHigh,w btfss STATUS,Z return clrf beep_mask return ;------------------------------------------------------------------------------; ; Beep pin driver task, called every beep half cycle. Toggles the beep ; ; pin if the beep mask is set to b'00010000', otherwise does nothing ; ; It always takes the same time so serial pin sampling is not affected ; ; by any variation. ; ;------------------------------------------------------------------------------; beep_pin_driver_task movf beep_mask,w ; mask specifies whether beep output xorwf porta,f ; changes state return ; END OF TASKS ----------------------- ; Support routines follow tasks to keep all the jump tables in page 0 ;------------------------------------------------------------------------------; ; Beep routines set the timeout and turn on the beep mask for various ; ; useful length beeps (These are macros which expand into code) ; ;------------------------------------------------------------------------------; ShortBeep BeepFor(ShortBeepTime) Beep BeepFor(BeepTime) LongBeep BeepFor(LongBeepTime) VeryLongBeep BeepFor(VLongBeepTime) ;-----------------------------------------------------------------------------; ; i2c comunication routines (no error handling performed) ; ; Taken unchanged from the original by Steve Drury ; ;-----------------------------------------------------------------------------; i2c_start movlw s_out_sda_out ;set scl and sda BANKSEL trisa movwf trisa ;as output BANKSEL porta bsf porta,sda nop bsf porta,scl nop bcf porta,sda ;set start condition nop bcf porta,scl nop return i2c_tx movwf i2c_data movlw s_out_sda_out ;set scl and sda BANKSEL trisa movwf trisa ;as output BANKSEL loop_count movlw .8 ;loop 8 times (8 bits) movwf loop_count shift_loop rlf i2c_data,f ;rotate into carry bsf porta,sda btfss STATUS,C bcf porta,sda nop bsf porta,scl ;clock data bit nop bcf porta,scl ;out nop decfsZ loop_count,1 goto shift_loop movlw s_out_sda_in ;set sda as BANKSEL trisa movwf trisa ;input to allow for BANKSEL porta bsf porta,scl ;aknowledge nop bcf porta,scl nop return i2c_stop movlw s_out_sda_out ;set scl and BANKSEL trisa movwf trisa ;sda as output BANKSEL porta bcf porta,sda nop bsf porta,scl nop bsf porta,sda nop movlw s_in_sda_in ;send scl and sda BANKSEL trisa movwf trisa ;to high imp. BANKSEL porta return ;-----------------------------------------------------------------------------; ; ProgramSynth: Starts I2C_task to send channel_no to the synthesiser ; ; chip, unless the I2C bus is already busy (in which case nothing ; ; happens) ; ;-----------------------------------------------------------------------------; ProgramSynth movf I2C_state,f ; return immediately if the I2C bus is btfss STATUS, Z ; already busy: frequency will not change return movlw 1 movwf I2C_state ; Kick off I2C transmission task return ;-----------------------------------------------------------------------------; ; Next Channel, increments channel number, with wrap-around from ; ; ScanEnd back to ScanStart. ; ;-----------------------------------------------------------------------------; NextChannel incf channel_no,f ; Next channel up movlw ScanStart subwf channel_no, w ; subtract from new current channel btfss STATUS, C ; did we borrow? (< ScanStart) goto FirstChannel ; yes, from the top sublw ScanEnd - ScanStart btfsc STATUS, C ; did we borrow? (> ScanEnd) return ; No, return incremented channel FirstChannel movlw ScanStart ; yes, go back to ScanStart movwf channel_no return ;-----------------------------------------------------------------------------; ; StartScanning: Enable scanning if it isn't already, from the next ; ; channel if this is in the range, otherwise from the start ; ;-----------------------------------------------------------------------------; StartScanning movf scan_state,f ; test for scan_state == 0 btfss STATUS,Z return ; non-zero: we're already scanning movlw 1 movwf scan_state ; start scanning return ;-----------------------------------------------------------------------------; ; Display routine, displays channel no. and scan led ; ;-----------------------------------------------------------------------------; UpdateDisplay movfw channel_no call Make7Seg movwf display_port ; display seven segments movf scan_state,f ; Is scan_task in state 0? btfss STATUS,Z bcf display_port,scan_led ; No, turn on scan LED return ;-----------------------------------------------------------------------------; ; initialise: set up and clear everything and start normal operation ; ; or test mode, depending on the button. Come here from reset vector ; ; or interrupt vector (interrupts should never be enabled, but... ; ;-----------------------------------------------------------------------------; initialise BANKSEL trisb ; set to bank 1 clrf trisb ; tristate portb movlw b'11101111' movwf trisa ; tristate porta movlw b'11000001' ; set up TMR0 with prescaler movwf option_reg ; div by 2,internal clock BANKSEL ser_state ; reset to bank 0 clrf display_port ; Lamp test - turn on all segments clrf scan_state ; clear scanner state machine clrf input_state ; clear input handler state machine clrf button_state ; clear button push handler state machine clrf ser_state ; clear serial state machine clrf I2C_state ; clear I2C transmitter state machine clrf beep_mask ; clear beep mask clrf SysTimerHigh ; clear system timer clrf SysTimerLow call LongBeep btfss key_port,button ; test for key pressed goto EnterTestMode ; yes so test mode requested movlw ScanEnd movwf channel_no ; pre-tune to last scan channel call ProgramSynth call StartScanning ; It won't actually start until main loop power_on_loop movf beep_mask,f ; This loop only tunes the synth & beeps btfsc STATUS,Z ; When the beep stops, go to main loop goto main_loop power_on_delay btfss intcon,t0if ; loop until TMR0=0 goto power_on_delay movlw time_tick ; load TMR0 for next tick movwf TMR0 bcf intcon,t0if ; clear t0 interupt flag clrwdt ; clear watchdog timer call beep_pin_driver_task ; no scan_task, so we ignore the call I2C_task ; signal indicator until it settles call beep_task ; after power-on incf SysTimerLow, f ; Increment system timer btfss STATUS,Z goto power_on_loop incf SysTimerHigh, f goto power_on_loop EnterTestMode movlw .10 ; Channel '10' is the test frequency movwf channel_no call UpdateDisplay call ProgramSynth movlw .29 ; State 29 locks out all input movwf ser_state goto main_loop ;-----------------------------------------------------------------------------; ; Cyclic executive main loop. Responsible for calling each task ; ; at the appropriate interval: each is a multiple of the tick rate ; ; which in turn is 1/3rd of a bit period ; ;-----------------------------------------------------------------------------; main_loop btfss intcon,t0if ; loop until TMR0=0 goto main_loop movlw time_tick ; load TMR0 for next tick movwf TMR0 bcf intcon,t0if ; clear t0 interupt flag clrwdt ; clear watchdog timer call beep_pin_driver_task ; task order minimises jitter... call serial_task call I2C_task call scan_task ; ...and peak workload call button_task call input_task call beep_task incf SysTimerLow, f ; Increment system timer btfss STATUS,Z goto main_loop incf SysTimerHigh, f goto main_loop ;-----------------------------------------------------------------------------; end