From: Werner Johansson Date: Sun, 9 Mar 2003 23:19:40 +0000 (-0800) Subject: v0.7 - Debug Serial TX in ISR now, checksum check for incoming packets in place,... X-Git-Url: http://git.xnk.nu/?p=wj-unilink.git;a=commitdiff_plain;h=68e0b7acbba32b2b6786ce364128377fae40572f v0.7 - Debug Serial TX in ISR now, checksum check for incoming packets in place, A/D works, solved the master reset prob (by calling the INT handler from TMR2 ISR code (too much interrupt latency when transmitting) Signed-off-by: Werner Johansson --- diff --git a/wj-uni.asm b/wj-uni.asm index c6e4840..69624e6 100644 --- a/wj-uni.asm +++ b/wj-uni.asm @@ -1,9 +1,30 @@ - title "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2002-2003" + title "PIC16F870 Unilink(R) Interface by Werner Johansson, wj@yodel.net" subtitl "Definitions" list c=150,P=16F870,R=DEC,F=inhx8m include "p16f870.inc" ; Standard equates & Macros ERRORLEVEL 1,-302 ; Get rid of those annoying 302 msgs! +;****************************************************************************** +; +; This program 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. +; +; This program 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 this program; if not, write to the Free Software +; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +; +; Author: Werner Johansson (wj@yodel.net), with some code and ideas from +; Simon Woods' GNUnilink, radix conversion utilities from piclist.com +; and of course the reverse-engineered Unilink(R) command list +; +;****************************************************************************** ;---------------------------------------------------------------- ; The Configuration Word @@ -12,20 +33,22 @@ ;---------------------------------------------------------------- ; TODO ;---------------------------------------------------------------- -; No checksum checking is done on incoming packets ; Investigate whether I actually have to save PCLATH in ISH, maybe save FSR? - Not saving any of them for now -; Move RS232 code into ISH ; Check Overrun errors from the UART ; Implement lots of other Unilink commands (Text display, time display etc.) ; Implement the Watchdog Timer (might be useful even though I haven't seen it hang yet..) ; Make the bit shift routine at the beginning of the ISR timeout if the clock suddenly stops (in the middle of a byte) ; (will keep it from hanging until the next bit gets clocked out, just ignore the faulty bits and carry on) +; Implement command b0 0x (change CD to x (1-a)) +; Implement command 08 10 (Tel Mute on) and 08 18 (Tel Mute off)? ;---------------------------------------------------------------- ; HISTORY ;---------------------------------------------------------------- ; Version ; +; 0.7 Debug Serial TX in ISR now, checksum check for incoming packets in place, A/D works, solved the master reset prob +; (by calling the INT handler from TMR2 ISR code (too much interrupt latency when transmitting) ; 0.6 Some more LCD info and clean-up of the Unilink recovery code, some problems with master resetting :( ; 0.5 Issues slave breaks seemingly without hickups (!) ; 0.4 Some fixups in the bootstrap code (I actually had to put the PIC in my PICSTART Plus programmer again :)) @@ -49,8 +72,9 @@ ; RS-232 TX from computer connected to RC7/RX ; RS-232 RX to computer connected to RC6/TX ; RS-232 RI to computer connected to RC5 +; B+ connected via trimmer and resistors to AN0 (divider approx 20k5/5k to give 20.48V maximum scale) ; -; This leaves RC0, RC1 and the analog inputs (AN0-AN4) free for now... +; This leaves RC0, RC1 and four analog inputs (AN1-AN4) free for now... #define BUSON_IN_BIT PORTC,2 #define DATA_BIT PORTC,3 @@ -155,12 +179,19 @@ temp equ 6dh Dcount equ 6eh e_LEN equ 6fh - Counter equ 70h DataCount equ 71h ; Temp storage for the bit counter used during bit shifts (Unilink TX/RX) -DataStore equ 72h ; This is a kludge +UnilinkCurID equ 72h ; This is a kludge DisplayCounter equ 73h UnilinkAttenuation equ 74h ; The amount of attenuation the volume control is currently set to +NumH equ 75h +NumL equ 76h +TenK equ 77h +Thou equ 78h +Hund equ 79h +Tens equ 7ah +Ones equ 7bh +UnilinkReInits equ 7ch IRQPCLATH equ 7dh ; ISH storage IRQSTATUS equ 7eh ; Needs to be located in a shared area accessible from all register banks IRQW equ 7fh ; @@ -229,8 +260,128 @@ DiscName1f equ 0bfh ; clrf PCLATH ; Go to low memory ; Maybe save FSR here as well (if there's a need for it in the non-ISR code) + call IRQCheckINT ; Implemented as a subroutine as there's a need to call it repeatedly from the other ISRs + + btfss PIR1,TMR2IF ; Check if it's the TMR2 interrupt (0.5ms timing) + goto IRQNotTMR2 ; No it's not, check the other sources + + incf Counter,f ; Increment the general purpose counter (increments every 0.5ms) + +; Slave break opportunity detection here - the logic works as follows: +; Look for a data low period of at least 5 ms (10 loops) +; Look for a data high period of at least 2 ms (4 loops) +; If the Slave Break request bit has been set, issue a slave break by holding the data line low for 4ms (8 loops) +; If a bit would be received (CLK activates) the packet handler automatically clears out the SlaveBreakState, which means start all over + + call IRQCheckINT ; Check the Unilink INT as well + + btfsc SlaveBreakState,5 ; Check if already pulling the data line low + goto IRQTMR2SlaveBreak + + btfsc SlaveBreakState,7 ; Looking for low or high data + goto IRQTMR2HighData + btfss DATA_BIT ; Looking for a low data line, if it's low, increment state, if it's high, reset state + goto IRQTMR2LowDataOK + clrf SlaveBreakState ; Got a high data line while waiting for a low one, reset state + + call IRQCheckINT ; Check the Unilink INT as well + + goto IRQAfterTMR2 ; Leave ISR + +IRQTMR2HighData + call IRQCheckINT ; Check the Unilink INT as well + + btfsc DATA_BIT ; Looking for a high data line, if it's high - increment state, otherwise wait + goto IRQTMR2HighDataOK + movlw 080h + btfsc SlaveBreakState,6 ; Test the "first time around" bit + clrw ; Not the beginning of the state, have to restart the entire thing now, not just this state + andwf SlaveBreakState,f ; Mask out the 1 upper control bits and restart this state + goto IRQAfterTMR2 + +IRQTMR2HighDataOK +IRQTMR2LowDataOK + call IRQCheckINT ; Check the Unilink INT as well + + bsf SlaveBreakState,6 ; Set the "first time around" bit + + movf SlaveBreakState,w + andlw 1fh + + btfss SlaveBreakState,7 ; Checking whether it's low or high + goto IRQTMR2FoundLow + + xorlw 4 ; It's high now, and if 4 periods have passed we can activate slave break + skpz + goto IRQAfterTMR2 + +; Issue slave break here + + clrf SlaveBreakState + +; incf Counter,f + + btfss DisplayStatus,7 ; Only do this if high bit is set + goto IRQAfterTMR2 + + movlw 20h + movwf SlaveBreakState + bcf DATA_BIT + bsf STATUS,RP0 + bcf DATA_BIT + bcf STATUS,RP0 + goto IRQAfterTMR2 + +IRQTMR2FoundLow + xorlw 10 + skpz + goto IRQAfterTMR2 + + call IRQCheckINT ; Check the Unilink INT as well + + movlw 80h ; Prepare for state 2, looking for data line high + movwf SlaveBreakState + goto IRQAfterTMR2 + +IRQTMR2SlaveBreak + call IRQCheckINT ; Check the Unilink INT as well + + movf SlaveBreakState,w + andlw 01fh + xorlw 8 + skpz + goto IRQAfterTMR2 + bsf STATUS,RP0 + bsf DATA_BIT + bcf STATUS,RP0 + clrf SlaveBreakState + +IRQAfterTMR2 + btfss SlaveBreakState,4 ; Only increment to 0x10 + incf SlaveBreakState,f + bcf PIR1,TMR2IF ; Clear the IRQ source bit to re-enable TMR2 interrupts again + +IRQNotTMR2 + +; Finally restore CPU state and return from the ISR + +; If I have to save the FSR in the beginning I also need to restore it here... + +; movf IRQPCLATH,w +; movwf PCLATH ; Restore PCLATH + swapf IRQSTATUS,w + movwf STATUS ; Restore STATUS + swapf IRQW,f + swapf IRQW,w ; Restore W + retfie ; Interrupt return + +;---------------------------------------------------------------- +; IRQCheckINT - This part is the actual Unilink tranceiver, have to call it often as there are only ~20 spare cycles +; (which is only a problem if we're going to transmit, but the check can be done anyway, it's cheap if no bit is there) + +IRQCheckINT btfss INTCON,INTF ; Check if it's the INT edge interrupt (Unilink CLK) - goto IRQNotINT ; No it's not, check the other sources + return ; No it's not, return again after only four cycles ; If there's activity on the clock line (the clock goes high) the CPU will stay in here until eight bits have been clocked in ; - this reduces context switching (and it's just a few hundred cpu cycles after all (20us*8 bits=160us=800 instruction @@ -300,6 +451,10 @@ IRQINTCLKWaitHigh IRQINTRecvDone clrf SlaveBreakState ; First of all, clear the break state - this got in the way, restart detection.. + + movf INDF,w + call BootTXB ; Send the byte to the serial port + movf UnilinkTXRX,w ; Find out which byte # that was received andlw 0fh ; Mask bnz IRQINTRecvNotFirst ; Not the first byte @@ -327,8 +482,41 @@ IRQINTRecvNotCMD1 andlw 0fh ; and mask - this results in a zero result when finished receiving bnz IRQINTRecvIncomplete ; Packet not ready yet -; Here a packet is actually received, should check the checksum(s) as well, but I don't care right now -; (I need music in my car! :)) +; Here a packet is actually received, should check the checksum(s) now + + movf UnilinkRAD,w ; QnD checksum check + addwf UnilinkTAD,w + addwf UnilinkCMD1,w + addwf UnilinkCMD2,w + xorwf UnilinkParity1,w ; This should be zero + bnz IRQINTParseComplete ; Don't allow packet parsing on corrupt packets + + btfss UnilinkCMD1,7 ; Test whether there's more parity to check (medium or long packet) + goto IRQINTParser ; No, skip directly to parsing logic + + movf UnilinkParity1,w ; QnD checksum check for the remaining part of the packet + addwf UnilinkData1,w + addwf UnilinkData2,w + addwf UnilinkData3,w + addwf UnilinkData4,w + + btfss UnilinkCMD1,6 ; Test for a long packet + goto IRQINTBypassLongPacket + + xorwf UnilinkParity2M,w ; Fix for the medium packet parity check a few lines down... + addwf UnilinkData5,w + addwf UnilinkData6,w + addwf UnilinkData7,w + addwf UnilinkData8,w + addwf UnilinkData9,w + xorwf UnilinkParity2,w ; This should be zero when xor:ed with the Parity2M + +IRQINTBypassLongPacket + xorwf UnilinkParity2M,w ; This should be zero for valid medium and long packets + bnz IRQINTParseComplete + +IRQINTParser + ; This is inefficient, I know, I'll improve it later... (Not that it matters, there's plenty of time here ; (there won't be any more communication for at least another 4.8ms)) @@ -345,6 +533,8 @@ IRQINTRecvNotCMD1 call ClearUnilinkStatus ; Clear everything Unilink (ID, BUSON_OUT) + incf UnilinkReInits,f ; increment the debug counter + goto IRQINTParseComplete ; Don't send any reply to this (clear the packet buffer though) IRQINTParseNot0100 @@ -355,9 +545,8 @@ IRQINTParseNot0100 bnz IRQINTParseNot0102 movf UnilinkID,w ; Do I have an ID already? - bnz IRQINTParseNot0102 ; Yep, I don't want another one! + bnz IRQINTParseComplete ; Yep, I don't want another one! -; clrf UnilinkParity1 call ClearUnilinkBuffer ; Zero it out completely movlw 10h ; Sending to Master @@ -389,7 +578,6 @@ IRQINTParseNot0100 addwf UnilinkParity2M,f movwf UnilinkData4 -; clrf UnilinkData6 goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet IRQINTParseNot0102 @@ -403,7 +591,6 @@ IRQINTParseNot0102 xorwf UnilinkID,w ; Is it for me? bnz IRQINTParseNot0112 ; Nope -; clrf UnilinkParity1 call ClearUnilinkBuffer movlw 10h ; Sending to Master addwf UnilinkParity1,f @@ -421,7 +608,6 @@ IRQINTParseNot0102 addwf UnilinkParity1,f movwf UnilinkCMD2 -; clrf UnilinkData1 goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet IRQINTParseNot0112 @@ -438,7 +624,6 @@ IRQINTParseNot0112 btfss DisplayStatus,7 ; If not displaying, skip this goto IRQINTParseComplete -; clrf UnilinkParity1 call ClearUnilinkBuffer movlw 70h ; Sending to Display Group addwf UnilinkParity1,f @@ -456,7 +641,6 @@ IRQINTParseNot0112 movf UnilinkParity1,w ; Carry the parity forward movwf UnilinkParity2M -; movlw 01h movf DisplayStatus,w addwf UnilinkParity2M,f movwf UnilinkData1 @@ -466,14 +650,14 @@ IRQINTParseNot0112 movlw 01h addwf UnilinkParity2M,f movwf UnilinkData3 + ; movlw 0c0h movf DisplayStatus,w andlw 0f0h + addwf UnilinkParity2M,f movwf UnilinkData4 -; clrf UnilinkData6 - incf DisplayStatus,f ; Temporary debug info bsf DisplayStatus,7 @@ -489,7 +673,6 @@ IRQINTParseNot0113 btfss DisplayStatus,7 ; First of all check if there should be anything displayed goto IRQINTParseComplete ; No, not at this time -; clrf UnilinkParity1 call ClearUnilinkBuffer movlw 10h ; Sending to Master addwf UnilinkParity1,f @@ -526,28 +709,27 @@ IRQINTParseNot0113 addwf UnilinkParity2M,f movwf UnilinkData4 -; clrf UnilinkData6 - goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet ;****************************************************************************** ; Bit frig - works out which bit to set in the response to Master Poll - -; W register is input of which stage you are on (0x00, 0x20, 0x30, 0x40 etc) +; This is taken more or less verbatim from Simon Woods' GNUnilink code! +; +; W register is input of which stage you are on (0x00, 0x20, 0x40 etc) ; and is returned with the byte to write (0x00 if wrong stage). Bit_Frig: xorwf UnilinkBit, 0 - andlw 0xe0 ; Strip off low bits + andlw 0xe0 ; Strip off low bits - btfsc STATUS, Z ; Do we have a hit? + btfsc STATUS, Z ; Do we have a hit? goto Bit_Frig_Hit movlw 0x00 return Bit_Frig_Hit: - btfss UnilinkBit, 4 ; Do we need to swap nybbles? + btfss UnilinkBit, 4 ; Do we need to swap nybbles? goto Bit_Frig_Swap movf UnilinkBit, 0 @@ -568,14 +750,19 @@ IRQINTParseNot01 xorlw 02h bnz IRQINTParseNot02 - bsf BUSON_OUT_BIT ; Now activate the cascade BUSON pin, to allow others to be discovered + movf UnilinkID,w ; Do I have an ID already? + bnz IRQINTParseComplete ; Yep, I don't want another one! + + movf UnilinkRAD,w ; So I don't have any ID yet, see what the master is trying to set + andlw 0f0h ; Check the device group + xorlw 0d0h ; Verify it's a MD changer + bnz IRQINTParseComplete ; No, something else, skip this movf UnilinkRAD,w ; Get the ID the master has given me movwf UnilinkID ; Store my id movf UnilinkCMD2,w ; Get the bitmask movwf UnilinkBit ; And store it (this is needed when doing slave breaks and actually responding) -; clrf UnilinkParity1 call ClearUnilinkBuffer movlw 10h ; Sending to Master addwf UnilinkParity1,f @@ -606,7 +793,8 @@ IRQINTParseNot01 addwf UnilinkParity2M,f movwf UnilinkData4 -; clrf UnilinkData6 + bsf BUSON_OUT_BIT ; Now activate the cascade BUSON pin, to allow others after us to be discovered + goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet IRQINTParseNot02 @@ -616,6 +804,36 @@ IRQINTParseNot02 xorlw 087h bnz IRQINTParseNot87 +; This part could use some more packet sniffing (really), it's sketchy to say the least.. :( +; The idea here is that the 18 10 87 2a PP 12 00 80 00 PP ZZ command is sent on power-up from my headunit if green color and +; if amber it looks like 18 10 87 2a PP 02 00 80 00 PP ZZ, giving away that high nibble (or bit 4) of D1 sets the color +; Also interesting is that the exact same commands gets sent when pressing the power-off button, this makes slaves pause +; playing, and then a few seconds later the unit shuts down with the following command: +; 18 10 87 22 PP 02 00 80 00 PP ZZ or +; 18 10 87 22 PP 12 00 80 00 PP ZZ depending on the color of the backlight +; This makes me think that bit 3 in CMD2 reflects the actual power state of the headunit (actually sleeping or bus active) +; Anyway I use this to set/clear the RI pin used for WakeOnRing on my laptop +; Also I de-select to make everything pause and clear the display status (if we're doing slave breaks after power status +; the headunit will never enter sleep!) +; From what I have gathered the bit mapping of DATA1 is as follows: +; 7 6 5 4 3 2 1 0 +; X - Backlight color changed if 1 +; ? ? +; X - Backlight color, 0=Amber, 1=Green +; X - Dimmer setting changed if 1 +; X X - Dimmer setting, 01=Dimmer Auto, 10=Dimmer On, 00=Dimmer Off, 11=??? +; X - Beep setting, 0=Beep on(!), 1=Beep off +; +; Also bit field of CMD2 for now: +; 7 6 5 4 3 2 1 0 +; X X - These two bits are set when changing color, beep etc, but now when actually powering on/off the system??? +; X - Always set to 1 on my headunit +; X - Always set to 0 on my headunit +; X - Set to 1 when power is on, 0 when powering off (last command sent before bus dies is 87 22 on my unit) +; X - Always set to 0 on my headunit +; X - Always set to 1 on my headunit +; X - Always set to 0 on my headunit + ; Test for power-on bit (it seems like bit 3 (0x08h) of CMD2 is set when the power is on) btfsc UnilinkCMD2,3 goto IRQINTParse87PowerOn @@ -625,6 +843,13 @@ IRQINTParseNot02 IRQINTParse87PowerOn bcf RS232_RI_BIT ; Clear this to make RI pin go high (waking the computer) + + btfsc UnilinkCMD2,7 ; Test high bit if it's just a "set" command, if yes don't clear status + goto IRQINTParseComplete + + bcf UnilinkSelected,7 ; Also de-select us (this gets sent when powering off but before the actual power down) + clrf DisplayStatus + goto IRQINTParseComplete IRQINTParseNot87 @@ -655,6 +880,8 @@ IRQINTParseNot90 bnz IRQINTParseNotF0 movf UnilinkCMD2,w + movwf UnilinkCurID ; Store it for display and debugging + xorwf UnilinkID,w ; Check if it's selecting me bnz IRQINTParseF0Deselect @@ -688,112 +915,14 @@ IRQINTParseBypassClear IRQINTRecvIncomplete IRQINTRecvNullByte - movf INDF,w - movwf DataStore ; Store it so the non-irq code can snoop +; movf INDF,w +; movwf DataStore ; Store it so the non-irq code can snoop IRQAfterINT bcf INTCON,INTF ; Clear the IRQ source bit to re-enable INT interrupts again IRQNotINT - - btfss PIR1,TMR2IF ; Check if it's the TMR2 interrupt (0.5ms timing) - goto IRQNotTMR2 ; No it's not, check the other sources - - incf Counter,f ; Increment the general purpose counter (increments every 0.5ms) - -; Slave break opportunity detection here - the logic works as follows: -; Look for a data low period of at least 5 ms (10 loops) -; Look for a data high period of at least 2 ms (4 loops) -; If the Slave Break request bit has been set, issue a slave break by holding the data line low for 4ms (8 loops) -; If a bit would be received (CLK activates) the packet handler automatically clears out the SlaveBreakState, which means start all over - - btfsc SlaveBreakState,5 ; Check if already pulling the data line low - goto IRQTMR2SlaveBreak - - btfsc SlaveBreakState,7 ; Looking for low or high data - goto IRQTMR2HighData - btfss DATA_BIT ; Looking for a low data line, if it's low, increment state, if it's high, reset state - goto IRQTMR2LowDataOK - clrf SlaveBreakState ; Got a high data line while waiting for a low one, reset state - goto IRQAfterTMR2 ; Leave ISR - -IRQTMR2HighData - btfsc DATA_BIT ; Looking for a high data line, if it's high - increment state, otherwise wait - goto IRQTMR2HighDataOK - movlw 080h - btfsc SlaveBreakState,6 ; Test the "first time around" bit - clrw ; Not the beginning of the state, have to restart the entire thing now, not just this state - andwf SlaveBreakState,f ; Mask out the 1 upper control bits and restart this state - goto IRQAfterTMR2 - -IRQTMR2HighDataOK -IRQTMR2LowDataOK - bsf SlaveBreakState,6 ; Set the "first time around" bit - - movf SlaveBreakState,w - andlw 1fh - - btfss SlaveBreakState,7 ; Checking whether it's low or high - goto IRQTMR2FoundLow - - xorlw 4 ; It's high now, and if 4 periods have passed we can activate slave break - skpz - goto IRQAfterTMR2 - -; Issue slave break here - - clrf SlaveBreakState - -; incf Counter,f - - btfss DisplayStatus,7 ; Only do this if high bit is set - goto IRQAfterTMR2 - - movlw 20h - movwf SlaveBreakState - bcf DATA_BIT - bsf STATUS,RP0 - bcf DATA_BIT - bcf STATUS,RP0 - goto IRQAfterTMR2 - -IRQTMR2FoundLow - xorlw 10 - skpz - goto IRQAfterTMR2 - movlw 80h ; Prepare for state 2, looking for data line high - movwf SlaveBreakState - goto IRQAfterTMR2 - -IRQTMR2SlaveBreak - movf SlaveBreakState,w - andlw 01fh - xorlw 8 - skpz - goto IRQAfterTMR2 - bsf STATUS,RP0 - bsf DATA_BIT - bcf STATUS,RP0 - clrf SlaveBreakState - -IRQAfterTMR2 - btfss SlaveBreakState,4 ; Only increment to 0x10 - incf SlaveBreakState,f - bcf PIR1,TMR2IF ; Clear the IRQ source bit to re-enable TMR2 interrupts again - -IRQNotTMR2 - -; Finally restore CPU state and return from the ISR - -; If I have to save the FSR in the beginning I also need to restore it here... - -; movf IRQPCLATH,w -; movwf PCLATH ; Restore PCLATH - swapf IRQSTATUS,w - movwf STATUS ; Restore STATUS - swapf IRQW,f - swapf IRQW,w ; Restore W - retfie ; Interrupt return + return ;---------------------------------------------------------------- ; ClearUnilinkStatus - Zeroes out the Unilink state (used when initializing) @@ -801,6 +930,7 @@ IRQNotTMR2 ClearUnilinkStatus clrf UnilinkID ; Clear the existing Unilink ID, if any + clrf UnilinkCurID ; Clear the currently selected ID as well bcf BUSON_OUT_BIT ; Clear the cascade BUSON pin, not activated again until we have a new ID clrf DisplayStatus ; No crazy display updates when resetting.. :) clrf UnilinkSelected ; We're not selected anymore @@ -814,6 +944,8 @@ ClearUnilinkStatus clrf UnilinkCmdLen ; No command length while waiting for a new packet + clrf SlaveBreakState ; Slave Break Processing has to start all over + return ;---------------------------------------------------------------- @@ -853,9 +985,21 @@ Main movlw high LookUp ; Set the high PC bits to indicate data lookup page movwf PCLATH - movlw 0ffh + movlw 0ffh ; Set infinite attenuation to begin with movwf UnilinkAttenuation + clrf UnilinkReInits ; Clear the bus re-initialization counter + + bsf STATUS,RP0 ; Reg bank 1 + movlw 080h ; Right adjusted A/D, all analog inputs, no Vrefs + movwf ADCON1 + bcf STATUS,RP0 + + movlw 081h ; Activate A/D, ch 0, Fosc/32 (for 20MHz operation) + movwf ADCON0 + + bsf ADCON0,2 ; Start the first A/D operation + movlw 8 ; Display page timing (approx 8/sec) movwf DisplayCounter @@ -907,14 +1051,17 @@ MainLoop MainDontPrintCmd -; Default text (see data declarations for actual text) -; UnilinkID @ 11-12 +; Default text +; UnilinkID @ 13-14 ; UnilinkAttenuation @ 16-17 -; UnilinkSelected @ 53-54 +; UnilinkSelected @ 28-29 +; UnilinkReInits @ 38,39 +; UnilinkCurID @ 54-55 ; DisplayStatus @ 62-63 +; BattVoltage @ 66,67,69,70 (thou,hund,tens,unit) bcf LCD_RS_BIT ; LCD Command mode - movlw 80h+11 ; DisplayRam 11 + movlw 80h+13 ; DisplayRam 13 call TxLCDB bsf LCD_RS_BIT @@ -930,7 +1077,7 @@ MainDontPrintCmd call TxLCDHEX bcf LCD_RS_BIT ; LCD Command mode - movlw 80h+40h+13 ; DisplayRam 53 + movlw 80h+28 ; DisplayRam 28 call TxLCDB bsf LCD_RS_BIT @@ -938,6 +1085,22 @@ MainDontPrintCmd call TxLCDHEX bcf LCD_RS_BIT ; LCD Command mode + movlw 80h+38 ; DisplayRam 38 + call TxLCDB + bsf LCD_RS_BIT + + movf UnilinkReInits,w + call TxLCDHEX + + bcf LCD_RS_BIT ; LCD Command mode + movlw 80h+40h+14 ; DisplayRam 54 + call TxLCDB + bsf LCD_RS_BIT + + movf UnilinkCurID,w + call TxLCDHEX + + bcf LCD_RS_BIT ; LCD Command mode movlw 80h+40h+22 ; DisplayRam 62 call TxLCDB bsf LCD_RS_BIT @@ -945,6 +1108,67 @@ MainDontPrintCmd movf DisplayStatus,w call TxLCDHEX + btfsc ADCON0,2 ; Test if A/D is ready + goto MainADNotReady + + bsf STATUS,RP0 + movf ADRESL,w ; Add to our result + bcf STATUS,RP0 + addwf NumL,f + skpnc + incf NumH,f + movf ADRESL,w + addwf NumH,f ; And the high byte + + movlw 20h + addwf NumH,f + skpc ; When this overflows we know there are 8 samples collected + goto MainADStartAD + +; Now shift the added results two steps down (/4) as there are 8 added samples here, and filter high bits + movlw 1fh + andwf NumH,f + clrc + rrf NumH,f + rrf NumL,f + clrc + rrf NumH,f + rrf NumL,f + + movf Counter,w + bnz MainADSkipDisplay + + bcf LCD_RS_BIT ; LCD Command mode + movlw 80h+40h+26 ; DisplayRam 66 + call TxLCDB + bsf LCD_RS_BIT + + call BCDConvert + movf Thou,w + addlw 30h + call TxLCDB + movf Hund,w + addlw 30h + call TxLCDB + movlw '.' + call TxLCDB + movf Tens,w + addlw 30h + call TxLCDB + movf Ones,w + addlw 30h + call TxLCDB + +MainADSkipDisplay + + clrf NumL + clrf NumH + +MainADStartAD + bsf ADCON0,2 ; Start a new conversion + +MainADNotReady + ; This part handles display "scroll" by shifting one screen at a time btfss Counter,7 ; Test high bit @@ -985,52 +1209,26 @@ MainSkipScroll ; Display scroll part ends here... - movf DataCount,w ; Load bit counter (if 0 then byte is available) - skpz - goto MainLoop +; movf DataCount,w ; Load bit counter (if 0 then byte is available) +; skpz +; goto MainLoop - decf DataCount,f ; Set it non-zero +; decf DataCount,f ; Set it non-zero - movf DataStore,w - call BootTXB ; Send to terminal +; movf DataStore,w +; call BootTXB ; Send to terminal goto MainLoop ;---------------------------------------------------------------- ; IRQInit - Sets up the IRQ Handler ; Set up Timer2 to generate 2000 interrupts per second, used for timing - 1/16 prescaler and a PR2 reg of 156 (0x9c) is set +; Also enable INT interrupts for Unilink CLK processing IRQInit -; Start with clearing the Unilink packet buffer before enabling any interrupts, otherwise the first packet might become corrupt -; TODO: Replace this with FSR access - clrf UnilinkSelected - clrf DisplayStatus - clrf UnilinkID - clrf UnilinkBit - clrf UnilinkCmdLen - clrf UnilinkRAD - clrf UnilinkTAD - clrf UnilinkCMD1 - clrf UnilinkCMD2 - clrf UnilinkParity1 - clrf UnilinkData1 - clrf UnilinkData2 - clrf UnilinkData3 - clrf UnilinkData4 - clrf UnilinkData5 - clrf UnilinkData6 - clrf UnilinkData7 - clrf UnilinkData8 - clrf UnilinkData9 - clrf UnilinkParity2 - clrf UnilinkZero - - clrf DataStore - movlw UnilinkRAD ; Get the pointer to the first byte in the receive buffer - movwf UnilinkTXRX ; Store it - - clrf SlaveBreakState ; Zero out the status, we're starting from the beginning + call ClearUnilinkStatus + call ClearUnilinkBuffer ; Fix the output state of RI and BUSON_OUT to a safe default @@ -1158,6 +1356,88 @@ TxLCDHEX return ;---------------------------------------------------------------- +; +; Binary-to-BCD. Written by John Payson. +; Taken from piclist.com - why re-invent the wheel when writing open-sourced code? +; +; Enter with 16-bit binary number in NumH:NumL. +; Exits with BCD equivalent in TenK:Thou:Hund:Tens:Ones. +; + +BCDConvert: ; Takes number in NumH:NumL + ; Returns decimal in + ; TenK:Thou:Hund:Tens:Ones + swapf NumH,w + andlw 0Fh ;*** PERSONALLY, I'D REPLACE THESE 2 + addlw 0F0h ;*** LINES WITH "IORLW 11110000B" -AW + movwf Thou + addwf Thou,f + addlw 0E2h + movwf Hund + addlw 032h + movwf Ones + + movf NumH,w + andlw 0Fh + addwf Hund,f + addwf Hund,f + addwf Ones,f + addlw 0E9h + movwf Tens + addwf Tens,f + addwf Tens,f + + swapf NumL,w + andlw 0Fh + addwf Tens,f + addwf Ones,f + + rlf Tens,f + rlf Ones,f + comf Ones,f + rlf Ones,f + + movf NumL,w + andlw 0Fh + addwf Ones,f + rlf Thou,f + + movlw 07h + movwf TenK + + ; At this point, the original number is + ; equal to TenK*10000+Thou*1000+Hund*100+Tens*10+Ones + ; if those entities are regarded as two's compliment + ; binary. To be precise, all of them are negative + ; except TenK. Now the number needs to be normal- + ; ized, but this can all be done with simple byte + ; arithmetic. + + movlw 0Ah ; Ten +Lb1: + addwf Ones,f + decf Tens,f + btfss 3,0 + goto Lb1 +Lb2: + addwf Tens,f + decf Hund,f + btfss 3,0 + goto Lb2 +Lb3: + addwf Hund,f + decf Thou,f + btfss 3,0 + goto Lb3 +Lb4: + addwf Thou,f + decf TenK,f + btfss 3,0 + goto Lb4 + + retlw 0 + +;---------------------------------------------------------------- ; TxLCD16B ; Send a string to the LCD. @@ -1324,14 +1604,17 @@ DelayInner org 600h ; Default text -; UnilinkID @ 11-12 +; UnilinkID @ 13-14 ; UnilinkAttenuation @ 16-17 -; UnilinkSelected @ 53-54 +; UnilinkSelected @ 28-29 +; UnilinkReInits @ 38,39 +; UnilinkCurID @ 54-55 ; DisplayStatus @ 62-63 +; BattVoltage @ 66,67,69,70 (thou,hund,tens,unit) DefaultText1 - DT "----- WJ", "ID:xx Se", "xx dB at", "LCDPage3", "LCDPage4" - DT " Unilink", "lect:xx ", "t Dsp:xx", " Right 3", " Right 4" + DT "----- WJ", "MyID:xx ", "xx dB at", "Sel:xx B", "Inits:xx" + DT " Unilink", "CurID:xx", "t Dsp:xx", "atxx.xxV", " " LookUp movwf PCL ; Go to it (this assumes PCLATH == 06h) @@ -1381,7 +1664,7 @@ Bootstrap call BootTXStr ; movlw 0e8h ; Initialize timeout timer (e8 is about 3 secs) - movlw 0fdh ; Initialize timeout timer (e8 is about 3 secs) + movlw 0fdh ; Initialize timeout timer (fd is short enough to get the headunit to recognize us) movwf BootTimerL movwf BootTimerM movwf BootTimerH @@ -1634,14 +1917,14 @@ BootEEX ; To produce compact code the end zero byte has to be in the LSB (that means an even number of chars in every string) BootStartText +; "WJBoot - press ESC to flash\x00" DW 0x2bca,0x216f,0x37f4,0x102d,0x1070,0x3965,0x39f3,0x1045,0x29c3,0x1074,0x37a0,0x336c,0x30f3,0x3400 -; DE "WJBoot - press ESC to flash\x00" BootFlashText +; "\r\nSend INHX8 file now...\r\x00" DW 0x068a,0x29e5,0x3764,0x1049,0x2748,0x2c38,0x1066,0x34ec,0x32a0,0x376f,0x3bae,0x172e,0x0680 -; DE "\r\nSend INHX8 file now...\r\x00" BootRunText +; "\r\nExiting loader\r\x00" DW 0x068a,0x22f8,0x34f4,0x34ee,0x33a0,0x366f,0x30e4,0x32f2,0x0680 -; DE "\r\nExiting loader\r\x00" ;---------------------------------------------------------------------- @@ -1649,5 +1932,12 @@ BootRunText org 2100h ; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh +; de 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh END