From 33848be3161304b52fbaf5cd7fe750dba67edb7a Mon Sep 17 00:00:00 2001 From: Werner Johansson Date: Sat, 8 Mar 2003 01:22:05 -0800 Subject: [PATCH] v0.5 - Issues slave breaks seemingly without hickups (!) Signed-off-by: Werner Johansson --- wj-uni.asm | 457 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 files changed, 365 insertions(+), 92 deletions(-) diff --git a/wj-uni.asm b/wj-uni.asm index 31f5bc1..08ddc24 100644 --- a/wj-uni.asm +++ b/wj-uni.asm @@ -1,4 +1,4 @@ - title "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2003" + title "PIC16F870 Unilink(R) Interface by Werner Johansson (c) 2002-2003" subtitl "Definitions" list c=150,P=16F870,R=DEC,F=inhx8m include "p16f870.inc" ; Standard equates & Macros @@ -25,6 +25,7 @@ ;---------------------------------------------------------------- ; Version ; +; 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) ; 0.2 First attempt at responding to the Anyone command @@ -68,50 +69,132 @@ ;---------------------------------------------------------------- ; FILE REGISTER USAGE ;---------------------------------------------------------------- -Dcount equ 20h -e_LEN equ 21h - -Counter equ 22h -SlaveBreakState equ 23h ; Hold state and time-out information about slave break, indicates when it can happen -Icount equ 2Dh ; Offset of string to print -TxTemp equ 2Eh ; blahblah -TxTemp2 equ 2Fh ; Blahblah2 - -LCDWTmp equ 30h -Dcount2 equ 31h -temp equ 32h - -DataCount equ 33h ; Temp storage for the bit counter used during bit shifts (Unilink TX/RX) -DataStore equ 34h ; This is a kludge - -UnilinkSelected equ 3bh -UnilinkBit equ 3ch ; This is my "bitmask" to be used for requests -UnilinkID equ 3dh ; This is my Bus ID -UnilinkCmdLen equ 3eh ; This gets updated with the actual packet length after CMD1 has been received -UnilinkTXRX equ 3fh ; This is a pointer to the Unilink packet below, used with indirect addressing - -UnilinkRAD equ 40h ; Beginning of Unilink packet - the Receiving Address -UnilinkTAD equ 41h ; Transmitter address -UnilinkCMD1 equ 42h ; CMD1 byte -UnilinkCMD2 equ 43h ; CMD2 byte -UnilinkParity1 equ 44h ; First or only parity byte for short packets (6 bytes) -UnilinkData1 equ 45h ; Extra data for medium/large packets, or zero for short packets -UnilinkData2 equ 46h ; -UnilinkData3 equ 47h ; -UnilinkData4 equ 48h ; -UnilinkData5 equ 49h ; Data5 if this is a large packet -UnilinkParity2M equ 49h ; Parity2 shares the same byte if it's a medium sized packet -UnilinkData6 equ 4ah ; Extra data for large packets, or zero for medium packets -UnilinkData7 equ 4bh ; -UnilinkData8 equ 4ch ; -UnilinkData9 equ 4dh ; -UnilinkParity2 equ 4eh ; Parity byte for large packets -UnilinkZero equ 4fh ; Should always be zero (possibly used to signal corrupt packets from slave to master?) +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 + +UnilinkRAD equ 50h ; Beginning of Unilink packet - the Receiving Address +UnilinkTAD equ 51h ; Transmitter address +UnilinkCMD1 equ 52h ; CMD1 byte +UnilinkCMD2 equ 53h ; CMD2 byte +UnilinkParity1 equ 54h ; First or only parity byte for short packets (6 bytes) +UnilinkData1 equ 55h ; Extra data for medium/large packets, or zero for short packets +UnilinkData2 equ 56h ; +UnilinkData3 equ 57h ; +UnilinkData4 equ 58h ; +UnilinkData5 equ 59h ; Data5 if this is a large packet +UnilinkParity2M equ 59h ; Parity2 shares the same byte if it's a medium sized packet +UnilinkData6 equ 5ah ; Extra data for large packets, or zero for medium packets +UnilinkData7 equ 5bh ; +UnilinkData8 equ 5ch ; +UnilinkData9 equ 5dh ; +UnilinkParity2 equ 5eh ; Parity byte for large packets +UnilinkZero equ 5fh ; Should always be zero (possibly used to signal corrupt packets from slave to master?) + +UnilinkTimeout equ 60h ; Counts up every 0.5ms to "age out" faulty bytes clocked in +UnilinkSelected equ 61h ; High bit is set when selected +UnilinkBit equ 62h ; This is my "bitmask" to be used for requests +UnilinkID equ 63h ; This is my Bus ID +UnilinkCmdLen equ 64h ; This gets updated with the actual packet length after CMD1 has been received +UnilinkTXRX equ 65h ; This is a pointer to the Unilink packet above, used with indirect addressing +SlaveBreakState equ 66h ; Hold state and time-out information about slave break, indicates when it can happen +DisplayStatus equ 67h ; What information will be put on the display next, bit 7 cleared if nothing +Icount equ 68h ; Offset of string to print +TxTemp equ 69h ; blahblah +TxTemp2 equ 6ah ; Blahblah2 +LCDWTmp equ 6bh +Dcount2 equ 6ch +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 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 + subtitl "Startup" page ;---------------------------------------------------------------- @@ -137,7 +220,8 @@ IRQW equ 7fh ; swapf STATUS,w ; Get the status register into w clrf STATUS ; Zero out the status reg, gives Reg Bank0 movwf IRQSTATUS ; Store the STATUS reg - movf PCLATH,w ; Get the PCLATH reg +; Not using PCLATH for anything in the ISR right now +; movf PCLATH,w ; Get the PCLATH reg ; movwf IRQPCLATH ; And store it ; clrf PCLATH ; Go to low memory ; Maybe save FSR here as well (if there's a need for it in the non-ISR code) @@ -240,7 +324,7 @@ 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 a packet, should check the checksum(s) as well, but I don't care right now +; 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! :)) ; 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)) @@ -254,13 +338,11 @@ IRQINTRecvNotCMD1 ; Check for 01 00 (Bus Re-Initialization) movf UnilinkCMD2,w -; xorlw 00h bnz IRQINTParseNot0100 - clrf UnilinkID ; Clear the existing Unilink ID, if any - bcf BUSON_OUT_BIT ; Clear the cascade BUSON pin, not activated again until we have a new ID + call ClearUnilinkStatus ; Clear everything Unilink (ID, BUSON_OUT) - goto IRQINTParseComplete ; Don't send any reply to this + goto IRQINTParseComplete ; Don't send any reply to this (clear the packet buffer though) IRQINTParseNot0100 @@ -272,27 +354,39 @@ IRQINTParseNot0100 movf UnilinkID,w ; Do I have an ID already? bnz IRQINTParseNot0102 ; Yep, I don't want another one! +; clrf UnilinkParity1 + call ClearUnilinkBuffer ; Zero it out completely + movlw 10h ; Sending to Master + addwf UnilinkParity1,f movwf UnilinkRAD movlw 0d0h ; I'm in the MD changer group + addwf UnilinkParity1,f movwf UnilinkTAD movlw 8ch ; Device discovery command reply + addwf UnilinkParity1,f movwf UnilinkCMD1 - movlw 00h ; 00?? + movlw 10h ; 00?? + addwf UnilinkParity1,f movwf UnilinkCMD2 - movlw 6ch ; Hard coded parity (!) - movwf UnilinkParity1 + + movf UnilinkParity1,w + movwf UnilinkParity2M + movlw 24h ; My internal MD sends 25 here first time, and then 24 when appointed!?? + addwf UnilinkParity2M,f movwf UnilinkData1 - movlw 2ch ; 2c?? + movlw 0a8h ; 2c?? + addwf UnilinkParity2M,f movwf UnilinkData2 - movlw 22h ; 22?? + movlw 17h ; 22?? + addwf UnilinkParity2M,f movwf UnilinkData3 - movlw 00h ; 00?? + movlw 0a0h ; 00?? 0a0=10 disc? + addwf UnilinkParity2M,f movwf UnilinkData4 - movlw 0deh ; Hard coded parity 2 (!) - movwf UnilinkData5 - clrf UnilinkData6 + +; clrf UnilinkData6 goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet IRQINTParseNot0102 @@ -306,7 +400,8 @@ IRQINTParseNot0102 xorwf UnilinkID,w ; Is it for me? bnz IRQINTParseNot0112 ; Nope - clrf UnilinkParity1 +; clrf UnilinkParity1 + call ClearUnilinkBuffer movlw 10h ; Sending to Master addwf UnilinkParity1,f movwf UnilinkRAD @@ -323,11 +418,146 @@ IRQINTParseNot0102 addwf UnilinkParity1,f movwf UnilinkCMD2 - clrf UnilinkData6 +; clrf UnilinkData1 goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet IRQINTParseNot0112 +; Check for 01 13 (Request Time poll) + movf UnilinkCMD2,w + xorlw 13h + bnz IRQINTParseNot0113 + + movf UnilinkRAD,w + xorwf UnilinkID,w ; Is it for me? + bnz IRQINTParseNot0113 ; Nope + + 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 + movlw 90h + addwf UnilinkParity1,f + movwf UnilinkCMD1 + movlw 50h + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2M + +; movlw 01h + movf DisplayStatus,w + addwf UnilinkParity2M,f + movwf UnilinkData1 + movlw 00h + addwf UnilinkParity2M,f + movwf UnilinkData2 + 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 + + goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet + +IRQINTParseNot0113 + +; Check for 01 15 (Who sent the slave break?) + movf UnilinkCMD2,w + xorlw 15h + bnz IRQINTParseNot0115 + + 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 + movwf UnilinkRAD + movlw 18h ; Broadcast address sending in this special case + addwf UnilinkParity1,f + movwf UnilinkTAD + movlw 82h ; Who wants to talk reply command + addwf UnilinkParity1,f + movwf UnilinkCMD1 + + clrw + call Bit_Frig + addwf UnilinkParity1,f + movwf UnilinkCMD2 + + movf UnilinkParity1,w ; Carry the parity forward + movwf UnilinkParity2M + + movlw 20h + call Bit_Frig + addwf UnilinkParity2M,f + movwf UnilinkData1 + movlw 40h + call Bit_Frig + addwf UnilinkParity2M,f + movwf UnilinkData2 + movlw 60h + call Bit_Frig + addwf UnilinkParity2M,f + movwf UnilinkData3 + movlw 80h + call Bit_Frig + 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) +; and is returned with the byte to write (0x00 if wrong stage). + +Bit_Frig: + xorwf UnilinkBit, 0 + andlw 0xe0 ; Strip off low bits + + 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? + goto Bit_Frig_Swap + + movf UnilinkBit, 0 + andlw 0x0F + return + +Bit_Frig_Swap: + swapf UnilinkBit, 0 + andlw 0xF0 + return + +IRQINTParseNot0115 + IRQINTParseNot01 ; Check for CMD1 = 02h (Appoint) @@ -342,7 +572,8 @@ IRQINTParseNot01 movf UnilinkCMD2,w ; Get the bitmask movwf UnilinkBit ; And store it (this is needed when doing slave breaks and actually responding) - clrf UnilinkParity1 +; clrf UnilinkParity1 + call ClearUnilinkBuffer movlw 10h ; Sending to Master addwf UnilinkParity1,f movwf UnilinkRAD @@ -352,7 +583,7 @@ IRQINTParseNot01 movlw 8ch ; Device discovery command again addwf UnilinkParity1,f movwf UnilinkCMD1 - movlw 00h + movlw 10h addwf UnilinkParity1,f movwf UnilinkCMD2 @@ -362,17 +593,17 @@ IRQINTParseNot01 movlw 24h addwf UnilinkParity2M,f movwf UnilinkData1 - movlw 2ch ; My internal MD sends 1c here... (external/internal difference) + movlw 0a8h ; My internal MD sends 1c here... (external/internal difference) addwf UnilinkParity2M,f movwf UnilinkData2 - movlw 22h + movlw 17h addwf UnilinkParity2M,f movwf UnilinkData3 - movlw 00h + movlw 0a0h ; 0a0=10disc addwf UnilinkParity2M,f movwf UnilinkData4 - clrf UnilinkData6 +; clrf UnilinkData6 goto IRQINTParseBypassClear ; Don't clear the data, the buffer will be sent as the next packet IRQINTParseNot02 @@ -405,37 +636,23 @@ IRQINTParseNot87 bnz IRQINTParseF0Deselect bsf UnilinkSelected,7 ; Now we're selected + bsf DisplayStatus,7 goto IRQINTParseComplete IRQINTParseF0Deselect bcf UnilinkSelected,7 ; Now we're de-selected + bcf DisplayStatus,7 goto IRQINTParseComplete IRQINTParseNotF0 IRQINTParseComplete -; The CPU ends up here when parsing is complete and it's not interested in sending any reply back to the master +; The code ends up here when parsing is complete and it's not interested in sending any reply back to the master ; (that's why we clear out all the packet buffer bytes) -; TODO: Replace this with an FSR access to save space and make the code neater - 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 + call ClearUnilinkBuffer IRQINTParseBypassClear @@ -462,8 +679,8 @@ IRQNotINT ; 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 5ms (10 loops) -; If a packet would be received the packet handler automatically clears out the SlaveBreakState, which means start all over +; 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 @@ -481,7 +698,7 @@ IRQTMR2HighData btfsc DATA_BIT ; Looking for a high data line, if it's high - increment state, otherwise wait goto IRQTMR2HighDataOK movlw 080h - btfss SlaveBreakState,6 ; Test the "first time around" bit + 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 @@ -489,8 +706,6 @@ IRQTMR2HighData IRQTMR2HighDataOK IRQTMR2LowDataOK bsf SlaveBreakState,6 ; Set the "first time around" bit - btfss SlaveBreakState,4 ; Only increment to 0x10 - incf SlaveBreakState,f movf SlaveBreakState,w andlw 1fh @@ -503,8 +718,14 @@ IRQTMR2LowDataOK goto IRQAfterTMR2 ; Issue slave break here + + clrf SlaveBreakState + incf Counter,f -; clrf SlaveBreakState + + btfss DisplayStatus,7 ; Only do this if high bit is set + goto IRQAfterTMR2 + movlw 20h movwf SlaveBreakState bcf DATA_BIT @@ -514,7 +735,7 @@ IRQTMR2LowDataOK goto IRQAfterTMR2 IRQTMR2FoundLow - xorlw 7 + xorlw 10 skpz goto IRQAfterTMR2 movlw 80h ; Prepare for state 2, looking for data line high @@ -524,7 +745,7 @@ IRQTMR2FoundLow IRQTMR2SlaveBreak movf SlaveBreakState,w andlw 01fh - xorlw 4 + xorlw 8 skpz goto IRQAfterTMR2 bsf STATUS,RP0 @@ -533,6 +754,8 @@ IRQTMR2SlaveBreak 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 @@ -549,14 +772,60 @@ IRQNotTMR2 swapf IRQW,w ; Restore W retfie ; Interrupt return +;---------------------------------------------------------------- +; ClearUnilinkStatus - Zeroes out the Unilink state (used when initializing) + +ClearUnilinkStatus + + clrf UnilinkID ; Clear the existing Unilink ID, if any + 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 + + bsf STATUS,RP0 ; Reg bank 1 + bsf DATA_BIT ; Make sure data is tristated + bcf STATUS,RP0 ; Reg bank 0 + + movlw UnilinkRAD ; Get the pointer to the first byte in the receive buffer + movwf UnilinkTXRX ; Store it - this way the next byte that gets received goes into RAD + + clrf UnilinkCmdLen ; No command length while waiting for a new packet + + return + +;---------------------------------------------------------------- +; ClearUnilinkBuffer - Zeroes out the Unilink packet buffer + +ClearUnilinkBuffer + +; TODO: Replace this with an FSR access to save space and make the code neater + 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 + + return + subtitl "Main loop" page ;---------------------------------------------------------------- -; Main program begins here. [Called after bootloader, lcdinit and irqinit...] +; Main program begins here. [Called after bootloader, lcdinit and irqinit...] +; Here all other house keeping tasks are performed, like displaying info on the LCD.. -; org 100h ; Maybe not force this to a specific address later Main movlw high LookUp movwf PCLATH @@ -579,6 +848,7 @@ MainLoop ; movlw '0' movf SlaveBreakState,w + andlw 80h btfsc PORTB,0 ; Test CLK movlw 'C' call TxLCDB @@ -620,6 +890,7 @@ 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 @@ -898,7 +1169,9 @@ DelayInner org 600h StartUpText1 DT "----- WJ UniLink" - + +InfoText1 + DT "WJ UniLink " LookUp movwf PCL ; Go to it (this assumes PCLATH == 06h) -- 1.7.3