X-Git-Url: http://git.xnk.nu/?p=wj-unilink.git;a=blobdiff_plain;f=wj-uni.asm;h=c1623e0be2b719b2130a5770bf1139bf591070ed;hp=08ddc2400de92413ee9e1f80668604db43384e4c;hb=HEAD;hpb=33848be3161304b52fbaf5cd7fe750dba67edb7a diff --git a/wj-uni.asm b/wj-uni.asm index 08ddc24..c1623e0 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,19 +33,25 @@ ;---------------------------------------------------------------- ; TODO ;---------------------------------------------------------------- -; Fix the DelayW routine so it actually delays W/10 ms... -; No checksum checking is done on incoming packets -; Investigate whether I actually have to save PCLATH in ISH, maybe save FSR? -; Move RS232 code into ISH +; Check how the slave should sense "power off" de-selection. Now it continues to play, as the slave doesn't stop requesting updates! +; Investigate whether I actually have to save PCLATH in ISH, maybe save FSR? - Not saving any of them for now ; 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.9 Interrupt driven UART RX for status updates, using raw unilink packets for everything else, dynamic text +; 0.8 Some text commands implemented, only static text for now though +; 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 :)) ; 0.3 Implementing more Unilink commands and RingIndicator control (to wake the computer from sleep) @@ -47,8 +74,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 @@ -69,54 +97,23 @@ ;---------------------------------------------------------------- ; FILE REGISTER USAGE ;---------------------------------------------------------------- -TrackName00 equ 20h ; Buffer for TrackName -TrackName01 equ 21h -TrackName02 equ 22h -TrackName03 equ 23h -TrackName04 equ 24h -TrackName05 equ 25h -TrackName06 equ 26h -TrackName07 equ 27h -TrackName08 equ 28h -TrackName09 equ 29h -TrackName0a equ 2ah -TrackName0b equ 2bh -TrackName0c equ 2ch -TrackName0d equ 2dh -TrackName0e equ 2eh -TrackName0f equ 2fh -TrackName10 equ 30h -TrackName11 equ 31h -TrackName12 equ 32h -TrackName13 equ 33h -TrackName14 equ 34h -TrackName15 equ 35h -TrackName16 equ 36h -TrackName17 equ 37h -TrackName18 equ 38h -TrackName19 equ 39h -TrackName1a equ 3ah -TrackName1b equ 3bh -TrackName1c equ 3ch -TrackName1d equ 3dh -TrackName1e equ 3eh -TrackName1f equ 3fh -TrackName20 equ 40h -TrackName21 equ 41h -TrackName22 equ 42h -TrackName23 equ 43h -TrackName24 equ 44h -TrackName25 equ 45h -TrackName26 equ 46h -TrackName27 equ 47h -TrackName28 equ 48h -TrackName29 equ 49h -TrackName2a equ 4ah -TrackName2b equ 4bh -TrackName2c equ 4ch -TrackName2d equ 4dh -TrackName2e equ 4eh -TrackName2f equ 4fh + +; Free from 20h-48h + +TrackName equ 20h +TracknameEnd equ 3eh + +DiscName equ 3fh +DiscNameEnd equ 48h + +CurDisc equ 49h +CurTrack equ 4ah +CurMins equ 4bh +CurSecs equ 4ch + +RecvType equ 4dh +TargetPos equ 4eh +CurRecvPos equ 4fh UnilinkRAD equ 50h ; Beginning of Unilink packet - the Receiving Address UnilinkTAD equ 51h ; Transmitter address @@ -153,47 +150,25 @@ 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 ; -DiscName00 equ 0a0h ; Buffer for DiscName -DiscName01 equ 0a1h -DiscName02 equ 0a2h -DiscName03 equ 0a3h -DiscName04 equ 0a4h -DiscName05 equ 0a5h -DiscName06 equ 0a6h -DiscName07 equ 0a7h -DiscName08 equ 0a8h -DiscName09 equ 0a9h -DiscName0a equ 0aah -DiscName0b equ 0abh -DiscName0c equ 0ach -DiscName0d equ 0adh -DiscName0e equ 0aeh -DiscName0f equ 0afh -DiscName10 equ 0b0h -DiscName11 equ 0b1h -DiscName12 equ 0b2h -DiscName13 equ 0b3h -DiscName14 equ 0b4h -DiscName15 equ 0b5h -DiscName16 equ 0b6h -DiscName17 equ 0b7h -DiscName18 equ 0b8h -DiscName19 equ 0b9h -DiscName1a equ 0bah -DiscName1b equ 0bbh -DiscName1c equ 0bch -DiscName1d equ 0bdh -DiscName1e equ 0beh -DiscName1f equ 0bfh +RecvBuf equ 0a0h ; Buffer for received data from PC (32 bytes) +RecvBufEnd equ 0bfh ; subtitl "Startup" page @@ -226,8 +201,241 @@ 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 : Debug! + + 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 + + call IRQCheckINT ; Check the Unilink INT as well + + btfss PIR1,RCIF ; Check if it's the UART RX interrupt + goto IRQNotRCIF ; No it's not, check the other sources +;---------------------------------------------------------------- +; This is the UART RX routine, this should be control info from the connected PC we're receiving + + movf CurRecvPos,w ; Load fsr + movwf FSR + movf RCREG,w ; Get the byte + bcf PIR1,RCIF ; Clear current interrupt condition + movwf INDF ; Store the recv byte at the position waiting + movlw RecvType + xorwf FSR,w ; Check if it's the recvtype coming in + bnz NotRecvType + + call IRQCheckINT ; Check the Unilink INT as well + + movf RecvType,w ; Load type + bz RecvTimeUpdate ; Position update + xorlw 1 ; Track Name perhaps? + bz RecvTrackName + xorlw 3 ; this is xor 1 and then xor 2, disc name? + bz RecvDiscName + + call IRQCheckINT ; Check the Unilink INT as well + + btfss RecvType,7 ; Check Hi byte + goto IRQNotRCIF + + movf RecvType,w + xorlw 81h + bnz NotText + + call IRQCheckINT ; Check the Unilink INT as well + + movf RecvType,w + goto TextUpdate +NotText + call IRQCheckINT ; Check the Unilink INT as well + + movf RecvType,w + btfss DisplayStatus,7 ; Only if not updating already! +TextUpdate + movwf DisplayStatus ; Force it into DisplayStatus + goto Finished + +RecvTimeUpdate + movlw CurDisc ; Load DL start here + movwf CurRecvPos + movlw CurSecs+1 ; And stop here + movwf TargetPos + goto IRQNotRCIF + +RecvTrackName + movlw TrackName ; Load DL start here + movwf CurRecvPos + movlw TracknameEnd+1 ; And stop here + movwf TargetPos + goto IRQNotRCIF + +RecvDiscName + movlw DiscName ; Load DL start here + movwf CurRecvPos + movlw DiscNameEnd+1 ; And stop here + movwf TargetPos + goto IRQNotRCIF + +NotRecvType + incf CurRecvPos,f + + call IRQCheckINT ; Check the Unilink INT as well + + movf CurRecvPos,w ; Check if we're done + xorwf TargetPos,w + bnz NotFinished +Finished + call IRQCheckINT ; Check the Unilink INT as well + +; movlw 81h ; Assume we do it from the beginning (text and pos) +; movf RecvType,f ; Just check for time-update cmd +; skpnz ; No adjustment if another cmd +; movlw 85h ; Skip the first 4 display cmds +; movwf DisplayStatus + +; call IRQCheckINT ; Check the Unilink INT as well + + movlw RecvType ; restore for another go + movwf CurRecvPos + +NotFinished + +IRQNotRCIF + + call IRQCheckINT ; Check the Unilink INT as well + + btfss PIR1,TXIF ; Check if it's the UART TX interrupt + goto IRQNotTXIF ; No it's not, check the other sources + bsf STATUS,RP0 ; Reg bank 1 + btfss PIE1,TXIE ; As TXIF is set as long as nothing gets sent, is the interrupt actually enabled? + goto IRQTXIFDisabled +;---------------------------------------------------------------- +; This is the UART TX routine, gets called when TXIE has been set and the TX load register is empty + + + + + +IRQTXIFDisabled + bcf STATUS,RP0 ; Make sure we're going back to reg bank 0 + +IRQNotTXIF + +; 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 @@ -297,6 +505,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 @@ -324,8 +536,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)) @@ -342,6 +587,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 @@ -352,9 +599,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 @@ -386,7 +632,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 @@ -400,7 +645,6 @@ IRQINTParseNot0102 xorwf UnilinkID,w ; Is it for me? bnz IRQINTParseNot0112 ; Nope -; clrf UnilinkParity1 call ClearUnilinkBuffer movlw 10h ; Sending to Master addwf UnilinkParity1,f @@ -418,7 +662,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 @@ -435,14 +678,19 @@ IRQINTParseNot0112 btfss DisplayStatus,7 ; If not displaying, skip this goto IRQINTParseComplete -; clrf UnilinkParity1 call ClearUnilinkBuffer + movlw 70h ; Sending to Display Group addwf UnilinkParity1,f movwf UnilinkRAD movf UnilinkID,w ; This is my ID addwf UnilinkParity1,f movwf UnilinkTAD + + movf DisplayStatus,w + xorlw 80h ; First slave break? + bnz IRQINTParse0113Not80 + movlw 90h addwf UnilinkParity1,f movwf UnilinkCMD1 @@ -453,26 +701,256 @@ IRQINTParseNot0112 movf UnilinkParity1,w ; Carry the parity forward movwf UnilinkParity2M -; movlw 01h - movf DisplayStatus,w + movf CurTrack,w addwf UnilinkParity2M,f movwf UnilinkData1 - movlw 00h + movf CurMins,w addwf UnilinkParity2M,f movwf UnilinkData2 - movlw 01h + movf CurSecs,w addwf UnilinkParity2M,f movwf UnilinkData3 + ; movlw 0c0h + movf CurDisc,w +; andlw 0f0h +; iorlw 0eh + + addwf UnilinkParity2M,f + movwf UnilinkData4 + +; clrf DisplayStatus +; goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet + + goto IRQINTParse0113Complete + +IRQINTParse0113Not80 + + movf DisplayStatus,w + xorlw 81h ; Second slave break? + bnz IRQINTParse0113Not81 + + movlw 0cdh ; Disc name + addwf UnilinkParity1,f + movwf UnilinkCMD1 + movf DiscName,w + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2 + + movf DiscName+1,w + addwf UnilinkParity2,f + movwf UnilinkData1 + movf DiscName+2,w + addwf UnilinkParity2,f + movwf UnilinkData2 + movf DiscName+3,w + addwf UnilinkParity2,f + movwf UnilinkData3 + movf DiscName+4,w + addwf UnilinkParity2,f + movwf UnilinkData4 + movf DiscName+5,w + addwf UnilinkParity2,f + movwf UnilinkData5 + movf DiscName+6,w + addwf UnilinkParity2,f + movwf UnilinkData6 + movf DiscName+7,w + addwf UnilinkParity2,f + movwf UnilinkData7 + movlw 00h + addwf UnilinkParity2,f + movwf UnilinkData8 + movlw 0eh + addwf UnilinkParity2,f + movwf UnilinkData9 + goto IRQINTParse0113Complete + +IRQINTParse0113Not81 + + movf DisplayStatus,w + xorlw 82h ; Third slave break? + bnz IRQINTParse0113Not82 + + movlw 0cdh ; Disc name + addwf UnilinkParity1,f + movwf UnilinkCMD1 + movf DiscName+8,w + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2 + + movf DiscName+9,w + addwf UnilinkParity2,f + movwf UnilinkData1 + movlw 0 + addwf UnilinkParity2,f + movwf UnilinkData2 + movlw 0 + addwf UnilinkParity2,f + movwf UnilinkData3 + movlw 0 + addwf UnilinkParity2,f + movwf UnilinkData4 + movlw 0 + addwf UnilinkParity2,f + movwf UnilinkData5 + movlw 0 + addwf UnilinkParity2,f + movwf UnilinkData6 + movlw 0 + addwf UnilinkParity2,f + movwf UnilinkData7 + movlw 00h + addwf UnilinkParity2,f + movwf UnilinkData8 + movlw 1eh + addwf UnilinkParity2,f + movwf UnilinkData9 + goto IRQINTParse0113Complete + +IRQINTParse0113Not82 + + movf DisplayStatus,w + xorlw 83h ; Fourth slave break? + bnz IRQINTParse0113Not83 + + movlw 0c9h ; Track name 1 + addwf UnilinkParity1,f + movwf UnilinkCMD1 + movf TrackName,w + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2 + + movf TrackName+1,w + addwf UnilinkParity2,f + movwf UnilinkData1 + movf TrackName+2,w + addwf UnilinkParity2,f + movwf UnilinkData2 + movf TrackName+3,w + addwf UnilinkParity2,f + movwf UnilinkData3 + movf TrackName+4,w + addwf UnilinkParity2,f + movwf UnilinkData4 + movf TrackName+5,w + addwf UnilinkParity2,f + movwf UnilinkData5 + movf TrackName+6,w + addwf UnilinkParity2,f + movwf UnilinkData6 + movf TrackName+7,w + addwf UnilinkParity2,f + movwf UnilinkData7 + movlw 00h + addwf UnilinkParity2,f + movwf UnilinkData8 + movlw 0eh + addwf UnilinkParity2,f + movwf UnilinkData9 + + goto IRQINTParse0113Complete + +IRQINTParse0113Not83 + + movf DisplayStatus,w + xorlw 84h ; Fifth slave break? + bnz IRQINTParse0113Not84 + + movlw 0c9h ; Track name (2) + addwf UnilinkParity1,f + movwf UnilinkCMD1 + movf TrackName+8,w + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2 + + movf TrackName+9,w + addwf UnilinkParity2,f + movwf UnilinkData1 + movf TrackName+10,w + addwf UnilinkParity2,f + movwf UnilinkData2 + movf TrackName+11,w + addwf UnilinkParity2,f + movwf UnilinkData3 + movf TrackName+12,w + addwf UnilinkParity2,f + movwf UnilinkData4 + movf TrackName+13,w + addwf UnilinkParity2,f + movwf UnilinkData5 + movf TrackName+14,w + addwf UnilinkParity2,f + movwf UnilinkData6 + movf TrackName+15,w + addwf UnilinkParity2,f + movwf UnilinkData7 + movlw 00h + addwf UnilinkParity2,f + movwf UnilinkData8 + movlw 1eh + addwf UnilinkParity2,f + movwf UnilinkData9 + goto IRQINTParse0113Complete + +IRQINTParse0113Not84 + movf DisplayStatus,w - andlw 0f0h + xorlw 85h ; Sixth slave break? + bnz IRQINTParse0113Not85 + + movlw 90h + addwf UnilinkParity1,f + movwf UnilinkCMD1 + movlw 50h + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2M + + movf CurTrack,w + addwf UnilinkParity2M,f + movwf UnilinkData1 + movf CurMins,w + addwf UnilinkParity2M,f + movwf UnilinkData2 + movf CurSecs,w + addwf UnilinkParity2M,f + movwf UnilinkData3 + +; movlw 0c0h + movf CurDisc,w +; andlw 0f0h +; iorlw 0eh + addwf UnilinkParity2M,f movwf UnilinkData4 - -; clrf UnilinkData6 - incf DisplayStatus,f ; Temporary debug info - bsf DisplayStatus,7 + clrf DisplayStatus ; for now! + goto IRQINTParse0113Complete + +IRQINTParse0113Not85 + clrf DisplayStatus + incf DisplayStatus,f ; Skip step one for now + goto IRQINTParseComplete + +IRQINTParse0113Complete + + incf DisplayStatus,f ; Increment display state counter +; bsf DisplayStatus,7 goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet @@ -486,7 +964,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 @@ -523,28 +1000,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 @@ -565,14 +1041,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 @@ -603,28 +1084,101 @@ 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 +; Check for CMD1 = 80h (Display button) + movf UnilinkCMD1,w + xorlw 080h + bnz IRQINTParseNot80 + + movf UnilinkID,w ; Check if I'm currently selected + xorwf UnilinkCurID,w + skpnz ; No, skip this command +; bsf DisplayStatus,7 ; Make sure we update the display again + movlw 81h + movwf DisplayStatus + goto IRQINTParseComplete + +IRQINTParseNot80 + ; Check for CMD1 = 87h (Power control) movf UnilinkCMD1,w xorlw 087h bnz IRQINTParseNot87 -; 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 +; 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 not when actually powering the system on or off??? +; 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 + + bsf RS232_RI_BIT ; Set this to make RI pin go low (after RS-232 levels) + goto IRQINTParseComplete + +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 + +; Check for CMD1 = 90h (Display/DSP info, volume etc.) + movf UnilinkCMD1,w + xorlw 090h + bnz IRQINTParseNot90 + +; Check for 90 10 (Current Volume) + movf UnilinkCMD2,w + xorlw 010h + bnz IRQINTParseNot9010 + + movf UnilinkData1,w ; Store current volume setting + movwf UnilinkAttenuation + + goto IRQINTParseComplete ; Don't send any reply to this (clear the packet buffer though) - bsf RS232_RI_BIT ; Set this to make RI pin go low (after RS-232 levels) - goto IRQINTParseComplete +IRQINTParseNot9010 -IRQINTParse87PowerOn - bcf RS232_RI_BIT ; Clear this to make RI pin go high (waking the computer) - goto IRQINTParseComplete -IRQINTParseNot87 +IRQINTParseNot90 ; Check for CMD1 = f0h (Source Select) movf UnilinkCMD1,w @@ -632,11 +1186,15 @@ IRQINTParseNot87 bnz IRQINTParseNotF0 movf UnilinkCMD2,w + movwf UnilinkCurID ; Store it for display and debugging + xorwf UnilinkID,w ; Check if it's selecting me bnz IRQINTParseF0Deselect bsf UnilinkSelected,7 ; Now we're selected - bsf DisplayStatus,7 +; bsf DisplayStatus,7 + movlw 81h + movwf DisplayStatus goto IRQINTParseComplete IRQINTParseF0Deselect @@ -665,112 +1223,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 - -; 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 - -; incf Counter,f ; Increment the general purpose counter - - 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) @@ -778,6 +1238,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 @@ -791,6 +1252,8 @@ ClearUnilinkStatus clrf UnilinkCmdLen ; No command length while waiting for a new packet + clrf SlaveBreakState ; Slave Break Processing has to start all over + return ;---------------------------------------------------------------- @@ -827,11 +1290,41 @@ ClearUnilinkBuffer ; Here all other house keeping tasks are performed, like displaying info on the LCD.. Main - movlw high LookUp + movlw high LookUp ; Set the high PC bits to indicate data lookup page movwf PCLATH - movlw low StartUpText1 ; Show something on the LCD - call TxLCD16B + movlw 0ffh ; Set infinite attenuation to begin with + movwf UnilinkAttenuation + + clrf RecvType + movlw RecvType + movwf CurRecvPos + + 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 + + bcf LCD_RS_BIT ; LCD Command mode + movlw 80h ; DisplayRam 0 + call TxLCDB + bsf LCD_RS_BIT + + movlw low DefaultText1 + movwf Icount + movlw 80 + movwf e_LEN + call TxLCD8BLoop ; Send 80 bytes to the LCD MainLoop @@ -870,52 +1363,176 @@ MainLoop MainDontPrintCmd - movf DataCount,w ; Load bit counter (if 0 then byte is available) +; Default text +; UnilinkID @ 13-14 +; UnilinkAttenuation @ 16-17 +; 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+13 ; DisplayRam 13 + call TxLCDB + bsf LCD_RS_BIT + + movf UnilinkID,w + call TxLCDHEX + + bcf LCD_RS_BIT ; LCD Command mode + movlw 80h+16 ; DisplayRam 16 + call TxLCDB + bsf LCD_RS_BIT + + movf UnilinkAttenuation,w + call TxLCDHEX + + bcf LCD_RS_BIT ; LCD Command mode + movlw 80h+28 ; DisplayRam 28 + call TxLCDB + bsf LCD_RS_BIT + + movf UnilinkSelected,w + 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 + + 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 + goto MainCounterLow + +; So bit is high, set high bit of displaycounter as well... + bsf DisplayCounter,7 + goto MainSkipScroll + +MainCounterLow +; OK, bit is low, now figure out whether it was high or low last time -> check high bit of DisplayCounter + btfss DisplayCounter,7 + goto MainSkipScroll + + bcf DisplayCounter,7 ; Clear the high bit to allow countdown to commence + movf Counter,w ; Load it skpz - goto MainLoop + goto MainSkipScroll + decfsz DisplayCounter,f + goto MainSkipScroll + + movlw 8 + movwf DisplayCounter + + bcf LCD_RS_BIT ; LCD Command mode + movlw 18h ; Display shift Left + call TxLCDB ; Shift it 8 positions + call TxLCDB + call TxLCDB + call TxLCDB + call TxLCDB + call TxLCDB + call TxLCDB + call TxLCDB + bsf LCD_RS_BIT + +MainSkipScroll - decf DataCount,f ; Set it non-zero +; Display scroll part ends here... - 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 @@ -939,6 +1556,7 @@ IRQInit bsf INTCON,INTE ; Enable the RB0/INT bsf INTCON,PEIE ; Enable the peripheral interrupts bsf PIE1,TMR2IE ; Enable the Timer2 peripheral interrupt + bsf PIE1,RCIE ; Enable the UART receive interrupt bsf INTCON,GIE ; Enable global interrupts bsf TXSTA,TXEN ; Enable UART TX @@ -964,12 +1582,12 @@ LCDInit ; This is a standard reset sequence for the LCD controller - movlw 160 ; Need to delay for at least 15ms, let's go for 16ms delay + movlw 170 ; Need to delay for at least 15ms, let's go for 17ms delay call DelayW movlw 3 ; Write 3 to the LCD call TxLCD ; Send to LCD - movlw 50 ; Need to delay for at least 4.1ms, let's go for 5ms delay + movlw 60 ; Need to delay for at least 4.1ms, let's go for 6ms delay call DelayW movlw 3 ; Write 3 to the LCD @@ -1001,10 +1619,130 @@ LCDInit movlw 06h ; Auto Increment cursor position call TxLCDB - + + bsf LCD_RS_BIT ; Accept data + return ;---------------------------------------------------------------- +; TxLCDHEX +; Sends two characters hex to the LCD + +TxLCDHEX + +; Original binary to 2-digit hex conversion from piclist.com, modified to fit here + + movwf Icount + swapf Icount,w + andlw 0x0f + + addlw 6 + skpndc + addlw 'A'-('9'+1) + addlw '0'-6 + + xorwf Icount,w + xorwf Icount,f + xorwf Icount,w + + andlw 0x0f + + addlw 6 + skpndc + addlw 'A'-('9'+1) + addlw '0'-6 + + movwf e_LEN + movf Icount,w + call TxLCDB + movf e_LEN,w + call TxLCDB + + 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. @@ -1031,12 +1769,13 @@ TxLCD8B movlw 8 movwf e_LEN ; Move to e_LEN -Txm_lp movf Icount,w ; get the byte +TxLCD8BLoop + movf Icount,w ; get the byte call LookUp incf Icount,f ; ...else ++Icount (table index) call TxLCDB ; Send out the byte decfsz e_LEN,f - goto Txm_lp + goto TxLCD8BLoop return ;---------------------------------------------------------------- @@ -1067,6 +1806,7 @@ NotReady call TxLCD ; And send that one as well return + ;---------------------------------------------------------------- ; RxLCDB - recv a byte from the LCD @@ -1167,11 +1907,29 @@ DelayInner ; Data can be stored between 600 and 6ffh... org 600h -StartUpText1 - DT "----- WJ UniLink" -InfoText1 - DT "WJ UniLink " +; Default text +; UnilinkID @ 13-14 +; UnilinkAttenuation @ 16-17 +; UnilinkSelected @ 28-29 +; UnilinkReInits @ 38,39 +; UnilinkCurID @ 54-55 +; DisplayStatus @ 62-63 +; BattVoltage @ 66,67,69,70 (thou,hund,tens,unit) + +DefaultText1 + DT "----- WJ", "MyID:xx ", "xx dB at", "Sel:xx B", "Inits:xx" + DT " Unilink", "CurID:xx", "t Dsp:xx", "atxx.xxV", " " + +PCWaitText0 +; DT ">Waiting" +; DT "Wait->PC" + DT "Booting." +PCWaitText1 +; DT " for PC<" +; DT " Booting" + DT "..",0,0,0,0,0,0 + LookUp movwf PCL ; Go to it (this assumes PCLATH == 06h) @@ -1218,7 +1976,8 @@ Bootstrap movlw low BootStartText ; Send boot banner to the serial port call BootTXStr - movlw 0e8h ; Initialize timeout timer +; movlw 0e8h ; 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 @@ -1391,10 +2150,14 @@ BootRXW1 BootRXHEXNibble call BootRXB ; Receive nibble + +; This code is from piclist.com, really neat! + addlw -'A' ; Convert from BCD to binary nibble skpc ; Test if if was 0-9 or A-F, skip if A-F addlw 'A' - 10 - '0' ; It was numeric '0' addlw 10 ; Add 10 (A get to be 0ah etc.) + return ;---------------------------------------------------------------------- @@ -1467,20 +2230,25 @@ 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 - DW 0x2bca,0x216f,0x37f4,0x102d,0x1070,0x3965,0x39f3,0x1045,0x29c3,0x1074,0x37a0,0x336c,0x30f3,0x3400 -; DE "WJBoot - press ESC to flash\x00" + DA "WJBoot - press ESC to flash\x00" + BootFlashText - DW 0x068a,0x29e5,0x3764,0x1049,0x2748,0x2c38,0x1066,0x34ec,0x32a0,0x376f,0x3bae,0x172e,0x0680 -; DE "\r\nSend INHX8 file now...\r\x00" -BootRunText - DW 0x068a,0x22f8,0x34f4,0x34ee,0x33a0,0x366f,0x30e4,0x32f2,0x0680 -; DE "\r\nExiting loader\r\x00" + DA "\r\nSend INHX8 file now...\r\x00" +BootRunText + DA "\r\nExiting loader\r\x00" ;---------------------------------------------------------------------- ; EE Data (64 bytes), located at 2100h 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