Při expertimentech s ATtiny15L jsem zjistil, že nemám volné vývody na připojení nějakého zobrazovače hodnoty. Například LCD displeje. Dostal jsem tedy nápad a k jednomu volnému pinu PB4 připojil sluchátko. Hlavní myšlenka je, že když nemohu hodnotu zobrazovat, můžu ji odvysílat morseovkou.
Následující kód je místy velmi divoký a řadu instrukcí jsem nahradil jen komentáři. Jsou to totiž imstrukce značně závislé na způsobu kterým měřím čas. Rovněž tento kód není moc přehledný. Ke své lítosti jsem opravdu nemohl použít podprogramy, které by kód velmi zpřehlednily. Program vznikal a ladil jsem jej na MCU ATtiny15L, který má zásobník návratových adres jen 3 úrovně hluboký. Počítaje přerušení které si může vzít kdykoliv jednu úrověň, a proceduru TX_Message která vysílá celý řetězec znaků a používá pro vysílání jednotlivých znaků tuto proceduru TX_Symbol. Tím jsem vyčerpal všechny úrovně zásobníku návratových adres.
; Transmit one morse symbol (character) given in register ARG. TX_SYMBOL: ; Code to handle 0x20 (Space) as a special case. CPI Arg, 0x20 BREQ Send2T ; Initialize Z pointer to MorseTab LDI ZL, low(MorseTab << 1) LDI ZH, high(MorseTab << 1) ; Make some correction to arg. The table begins with space, code=0x20. SUBI Arg, 0x20 ; Add Arg to Z, so Z will point to character given by Arg. ADD ZL, Arg BRCC LoadCode INC ZH LoadCode: LPM ; Now R0 contains encoded symbol. MOV Seq, R0 SendLoop: CLC ; Must be cleared before ROR ROL Seq ; The C contains dih/dat, but only if Seq is non zero. ; In such case Seq is empty and C contains stop mark. BREQ EndSymbol ; We can send a . or - depending on the Carry value. SBR BITF, 0b00000001 ; Sound On BRCC SendDih ; Vysíláme čárku (dah). Protože zvuk je zaplý. ;Wait 1T ;Wait 1T ;Wait 1T CBR BITF, 0b00000001 ; Sound Off ;Wait 1T RJMP SendLoop EndSymbol: ;Wait 1T ;Wait 1T RET
Na kódu je zvláštní jedna věc. Se zdrojem časových impulsů a zdrojem tónu komunikuje přes bity v registru BitF. Bit 0 tohoto registru určuje, zdali je vytvářen tón. 0 znamená že nikoliv a je tedy ticho, 1 znamená že se generuje tón. Další bit 1 tohoto registru slouží ke komunikaci se zdrojem času. Zdroj času totiž tento bit v pravidelných intervalech 1T nastavuje na 1. Všechna čekání jsou tedy krátké posloupnosti tří instrukcí podobné této:
; Wait 1T CBR BitF, 0b00000010 ; smaž bit 1 Loop: SBRS BitF, 1 ; čekej dokud jej zdroj času opět nenastaví RJMP Loop
Kód programu je rovněž „zkrácen“ vzájemným nakombinováním různých větví do sebe. Například vysílání čárky a tečky je udělané tak že tečka je poslední třetina čárky.
Sound On If tečka Go tecka Wait 1T Wait 1T tecka: Wait 1T Sound Off Wait 1T
Za každou tečku nebo čárku je rovněž ihned vysláno ticho o délce 1T což mi zjednodušuje vysílání posloupnosti teček a čárek. Na to pak musím myslet, když dělám mezeru mezi písmeny, že kousek ticha o délce 1T už byl odvysílán a že zbývají jen dva kousky ticha.
Poslední částí, která je nutná k pochopení programu je tabulka morse kódů.
MorseTab: .db 0b00000000, 0b10101110, 0b01001010, 0b00000000 ; SP ! " # .db 0b00000000, 0b00000000, 0b01000100, 0b01111010 ; $ % & ' .db 0b10110100, 0b10110110, 0b00000000, 0b01010100 ; ( ) * + .db 0b11001110, 0b10000110, 0b01010110, 0b10010100 ; , - . / ; 0x30 - 0x39 Numbers .db 0b11111100, 0b01111100, 0b00111100, 0b00011100 ; 0 1 2 3 .db 0b00001100, 0b00000100, 0b10000100, 0b11000100 ; 4 5 6 7 .db 0b11100100, 0b11110100 ; 8 9 ; 0x3A - 0x3F .db 0b11100010, 0b10101010, 0x00000000, 0b10001100 ; : ; < = .db 0b00000000, 0b00110010 ; > ? ; 0x40 Alphabet .db 0b10000000, 0b01100000, 0b10001000, 0b10101000 ; @ A B C .db 0b10010000, 0b01000000, 0b00101000, 0b11010000 ; D E F G .db 0b00001000, 0b00100000, 0b01111000, 0b10110000 ; H I J K .db 0b01001000, 0b11100000, 0b10100000, 0b11110000 ; L M N O .db 0b01101000, 0b11011000, 0b01010000, 0b00010000 ; P Q R S .db 0b11000000, 0b00110000, 0b00011000, 0b01110000 ; T U V W .db 0b10011000, 0b10111000, 0b11001000, 0b00000000 ; X Y Z [ .db 0b00000000, 0b00000000, 0b00000000, 0b00110110 ; \ ] ^ _
Vymyslel jsem spoustu složitých kódování a způsobů jak pospat morse pro jedno písmeno v jednom bajtu. Ale pak se mi rožlo a já byl osvícen. Kódování se kterým jsem přišel je velmi jednoduché a ke všemu ještě dokáže kódovat o jednu tečku nebo čárku více než nejsložitější se kterým jsem příšel. Princip je takový že tečku kóduji nulovým bitem a čárku jedničkovým bitem. Jediné co potřebuji jednoznačně detekovat, je konec posloupnosti. A v tom je ten ftip. Konec posloupnosti je označen 1 po které následují již jen samé 0. Protože při vysílání posouvám kódové slovo jedním směrem, v mém případě do leva, a zajišťuji že se zprava plní nulami je detekce konce dána tím že posledním bitem který se do Carry načetl je 1 a v registru jsou jen samé nuly. Toto čtení kódu je realizováno jen třemi instrukcemi:
SendLoop:
CLC ; Must be zero before ROR
ROR Seq
BREQ EndSymbol