Een asynchrone unidirectionele datalink van PC naar µC
De µC software
Er is slechts één input vrij dus dit betekent een verbinding zonder clocksignaal en zonder feedback van de µC. Omdat er geen clocksignaal is wordt gekozen gekozen voor een soort morse signaal. Kort hoog (aan de parallelle poort) is 0, lang hoog is 1. Bij de input van de PIC moet hoog als laag gelezen worden vanwege de inverterende versterker. De input is dus eigenlijk een active low input in dit geval.
Om een verbinding op te zetten zendt de PC eerst twee start/kalibratie bits. De eerste bit is kort (0) en de tweede lang (1). Dit is een vast gegeven zodat de µC aan de hand hiervan de grens (in tijd) tussen een lang en een kort signaal kan bepalen. Om deze timing te doen wordt TMR1 gebruikt. De timing gebeurd steeds met een lus die er als volgt uitziet. Van tevoren wordt TMR1 gecleared en ge-enabled.
CalibrateShortLoop
; Raise Error1 if TMR1 overflows (time-out).
BTFSC PIR1, TMR1IF
GOTO Error1
; Loop if RA5 is low.
BTFSS PORTA, 5
GOTO CalibrateShortLoop
Na de lus wordt de waarde in TMR1 uitgelezen. Zoals te zien is wordt bij elke fout gejumpt naar een ErrorN label, waar het getal N op PORTA wordt gezet, zodat de fout af te lezen is aan de aan/uit stand van de lampjes (met 14 lampjes kan je toch mooi 214 errorcodes weergeven :-) (er worden er "slechts" 23 gebruikt). Zodra een kort (0) en een lang (1) signaal zijn gemeten wordt het gemiddelde uitgerekend. In assembly valt dit op de volgende manier eenvoudig te doen.
MOVF short_lo, W
ADDWF long_lo, F
MOVF short_hi, W
BTFSC STATUS, C
INCF short_hi, W
ADDWF long_hi, F
RRF long_hi, F
RRF long_lo, F
Zoals wel enigszins logisch is staat de lengte van het korte signaal in short_lo en short_hi en de lengte van het lange signaal in long_lo en long_hi. Het gemiddelde komt uiteindelijk in long_lo en long_hi terecht. Merk op dat de carry van de laatste ADDWF meteen aan de rechterkant het gemiddelde ingeschoven wordt (geschoven omdat delen door 2 hetzelfde is als 1 bit naar rechts schuiven).
Als deze kalibratie gebeurd is dan is er in principe een verbinding. Het kan natuurlijk voorkomen dat het parallelle poort signaal door een ander programma op de PC toevallig achtereenvolgens kort hoog en lang hoog wordt gemaakt. Om te voorkomen dat de uC dan gaat wachten op invoer of corrupte data krijgt wordt er door de PC eerst een 32 bits identifier overgezonden. Met 4 keer het volgende code fragment (steeds een andere letter, in mijn applicatie is de ID "KrSt") worden de ontvangen 32 bits vergeleken met de waarde die gezonden zou moeten zijn.
CALL ReceiveByte
SUBLW 'K'
BTFSS STATUS, Z
GOTO Error6
Zoals te zien is wordt hier ook naar een Error gesprongen als het niet klopt. Wanneer deze verbinding echt gebruikt wordt is het ook mogelijk om alle jumps naar errors te vervangen door code om het binnenkomende signaal verder te negeren. Op die manier kunnen (met verschillende ID's) een willekeurig aantal apparaten aan één pin van de parallelle poort worden aangesloten. En daar zijn er dan weer 8 van ;-).
In het voorgaande stukje assembly wordt de functie ReceiveByte aangeroepen. Deze functie zit vrij simpel in elkaar. In een lus die altijd 8 keer (voor 8 bits per byte) wordt doorlopen (tenzij een time-out optreedt) wordt steeds gewacht tot RA5 naar ground wordt getrokken, waarna de lengte van het signaal wordt gemeten. Deze lengte (in tmr1_hi en tmr1_lo) valt eenvoudig te vergelijken met het gemiddelde in ave_lo en ave_hi.
MOVF ave_hi, W
SUBWF tmr1_hi, W ; tmr1-ave ==> C=0 if tmr1<ave, C=1 if tmr1>=ave.
BTFSS STATUS, Z
GOTO ReceiveByte_jnz
MOVF ave_lo, W
SUBWF tmr1_lo, W
ReceiveByte_jnz
RLF bytebuf, F
Het gehele stukje m.u.v. de laatste regel is een fragment om twee 16 bits waarden te vergelijken. Het resultaat komt terecht in de carry flag en deze wordt door RLF direct in de buffer bytebuf gezet, waarbij voorgaande bits opzij worden geschoven. Na 8 bits is de buffer vol en is er een byte ontvangen!
Omdat dit projectje een eerste experiment met zo'n verbinding was heb ik ook enkele methoden bedacht om te checken of de bytes nog wel goed aankomen nadat de ID goed is gearriveerd. De belangrijkste is het invoeren van een checksum, een waarde die met een bepaalde formule steeds wordt aangepast als functie van elke ontvangen byte en die af en toe vergeleken kan worden met de waarde die het moet zijn (deze wordt dan overgezonden door de PC). Het eenvoudigst is een waarde bij te houden die met:
XORWF bytexor, F
steeds bijgewerkt wordt (waarbij de ontvangen byte in W staat). Om de 8 bytes wordt door de PC een extra byte gezonden die gelijk moet zijn aan bytexor. Is dit niet het geval dan wordt de transmissie verbroken en een errorcode weergegeven.