Die Tastatur "hängt" an den Pins PB3 und
PB4. Die verwendete Buchse ist keine Mini-DIN-Buchse, sondern eine
USB-Buchse, weil meine USB-Tastatur auch PS/2 "kann" - es lag ein
PS/2-Adapter bei, den ich weg gelassen habe. Zu beachten ist jedoch,
dass nicht jede USB-Tastatur PS/2-fähig ist! Wer eine Tastatur mit PS/2
Stecker hat, müsste ihn mutig abschneiden oder das Board-Layout
anpassen. Auf einem alten Screenshot ist die Belegung einer PS/2-Buchse
ersichtlich:
Die Stromversorgung der Tastatur wird
über PB1 geschaltet. So ist kein Ausschalter erforderlich. Der
Controller wird mit einem Reset-Taster geweckt, legt sich nach etwa 20
Minuten Inaktivität selbst schlafen und schaltet vorher die
Stromversorung der Tastatur ab. Meine Tastatur nebötigt nur 6 mA, deshalb
konnte ich die Stromversorgung einfach per Attiny-Pin schalten. Falls
die Tastatur mehr als 20 mA benötigt,sollte man einen Schalttransistor
einfügen.
An PB0 wird ein Rechteck-Tonsignal ausgegeben und an PB2 das
invertierte CW-Tastsignal für einen KW-Transceiver.
Quelltext mit Kommentaren:
' Programm wandelt Ausgangssignal einer PS/2-Tastatur in Morsezeichen
' Manche USB-Tastaturen "können" auch PS/2
' Compiler: kostenfreie Bascom-Schnupper-Version (www.mcselec.com)
'
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
'
' Grundeinstelllungen:
$Regfile = "ATtiny85.dat"
$Crystal = 8000000
$Hwstack = 32 ' wegen Interrupt-Routine
$Swstack = 4
$Framesize = 4
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Variablen und Konstanten dimensionieren:
'
Dim I As Byte ' Fuer temporären Gebrauch als Schleifenzaehler
Dim M As Byte ' Index des aktuellen Morsebuchstabens in Morsetabelle
Dim L As Byte ' fuer niederwertigstes Bit beim bitweisen Erzeugen des Morsezeichens
Dim Pufferstring As String * 128
Dim Puffer(128) As Byte At Pufferstring Overlay ' so kann man wahlweise String-Befehle oder Array-Befehle nutzen
Dim Position As Byte
Dim Recbyte As Byte
Dim Recchar As String * 1
Dim N As Byte
Dim Wpm As Word ' words per minute
Dim Punktlaenge As Word ' Punktlaenge
Dim Strichlaenge As Word ' Strichlaenge
Dim Wortpause As Word ' Laenge Wortpause
Dim Offtimer As Word
Const Tonhoehe = 200 ' Tonhoehe Rechteck-Mithoerton
' einige Speichertexte:
' am Ende sollte immer ein Leerzeichen stehen, damit ohne zus. <Return> los gesendet wird
Const speichertext4 = "vvv "
Const speichertext5 = "test test test "
Const speichertext6 = "F6 "
Const speichertext7 = "F7 "
Const speichertext8 = "F8 "
Const speichertext9 = "F9 "
Clkpr = 128 ' Taktumschaltung auf 8 MHz
Clkpr = 0
Clkpr = 0
' Configure the pins to use for the clock and data
' Can be any pin that can serve as an input
' Keydata is the label of the key translation table
'
Config Keyboard = Pinb.4 , Data = Pinb.3 , Keydata = Keydata
' Rechteck-Mithoerton:
' Timer0 zaehlt das Register TCNT0 hoch. Es wird mit Register OCR0A verglichen.
' Wenn TCNT0 = OCR0A, wird Output OC0A/PB0 umgeschaltet (getoggled)
' Timer0 erzeugt so Dauerton; er wird jedoch nur an den Ausgangspin durchgeschaltet,
' wenn das Datenrichtungs- Register DDRB.0 = 1 ist. Bei 0 wird der Ton unterdrückt.
Config Timer0 = Counter , Prescale = 64 , Compare A = Toggle , Clear Timer = 1
Ocr0a = Tonhoehe
Acsr.acd = 1 ' Analog-Komparator ausschalten, spart etwas Strom
Pcmsk.4 = 1 ' PCINT für Port B.4 (Keyboard-Clock) freigeben
Gimsk.pcie = 1 ' PCints aktivieren
Sreg.7 = 1 ' Interrupts generell freigeben
DDRB = &B00000110 ' DDRB.2 ist Tastausgang, DDRB.1 Stromvers. Keyboard
Portb = &B00000011
On Pcint0 Kbd_lesen ' Interrupt-Routine definieren
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Main:
Wpm = 30 ' Standard-Geschwindigkeit
Gosub Timing ' in ms umrechnen
Wait 1 ' Boot-Zeit fuer die Tastatur
Pufferstring = "" ' Tastatur sendete beim Starten unerwuenschte Zeichen
' Hauptschleife
Do
Do ' Leerlauf-Warteschleife bis zum Timeout
Incr Offtimer
If Offtimer > 64000 Then ' Schleife erzeugt einige min. Wartezeit vor Powerdown
Offtimer = 0
PortB = 0
DDRB = 0
Gimsk.pcie = 0
Powerdown
End If
Waitms 10 ' 10ms und 64000: 10 min Leerlauf bis Powerdown
Loop until Pufferstring <> "" ' erst wenn Zeichen im Puffer, gehts weiter
Do ' Puffer auf Leerzeichen oder Return durchsuchen ("word mode")
For N = 1 To Len(Pufferstring)
If Puffer(N) = 32 Or Puffer(N) = 13 Then ' Wenn gefunden, ist Wort komplett und Morsezeichen-Erzeugung startet
M = Asc(puffer(1)) ' Ascii- Wert des untersten Zeichens im Puffer ermitteln
If M > 95 Then
M = M - 32 ' Ascii-Kleinbuchstaben in ASCII-Grossbuchstaben wandeln
End If
M = Lookup(m , Morsetabelle) ' Morsezeichen aus Tabelle holen
Gosub Morse
Pufferstring = Mid(pufferstring , 2) ' das unterste Zeichen im Pufferstring entfernen
Offtimer = 0
End If
Next
Loop
Loop
' Morsezeichenerzeugung:
Morse:
If M = 0 Then ' Sonderfall: Leerzeichen;
Waitms Wortpause
Goto Zeichenende
End If
For I = 1 To 8
If M = 1 Then ' Das Byte hat nur noch den Wert 1; Zeichenende!
Goto Zeichenende
End If
L = M And &B00000001 ' niederwertigstes Bit lesen
Ddrb.0 = 1
PortB.2 = 1
If L = 1 Then
Waitms Strichlaenge ' ist das Bit 1 -> dah
Else
Waitms Punktlaenge ' ist das Bit 0 -> dit
End If
Ddrb.0 = 0
PortB.2 = 0
Waitms Punktlaenge ' Pause innerhalb des Morsezeichens
Shift M , Right , 1 ' Bits um eine Stelle nach rechts shiften
Next I
Zeichenende:
Waitms Strichlaenge ' Pause zwischen Morsezeichen
Return
Timing: ' Funktion fuer Geschwindigkeit, Werte in msec
' Bei Verwendung von Ganzzahl-Variablen wird Punktlaenge immer abgerundet; Rundungsfehler aber unter 1 WpM
Punktlaenge = 1200 / Wpm : Strichlaenge = Punktlaenge * 3 : Wortpause = Punktlaenge * 5
Return
End
' Interrupt-Routine
Kbd_lesen:
Pcmsk.4 = 0 ' Solange Bits reinkommen, PCINT sperren
Recbyte = Getatkbd()
Select Case Recbyte
Case 8: ' Backspace-Taste
Position = Len(Pufferstring)
If Puffer(Position) = 32 or Puffer(Position) = 13 Then
Delchar Pufferstring , Position - 1
End If
Delchar Pufferstring , Position
Case 20: Pufferstring = "" ' F12
Case 21: Wpm = Wpm - 2 : Gosub Timing ' F1
Case 22: Wpm = Wpm + 2 : Gosub Timing ' F2
Case 23: Pufferstring = Pufferstring + "wpm: " + Str(Wpm) + " " ' F3
Case 24: Pufferstring = Pufferstring + speichertext4 ' F4
Case 25: Pufferstring = Pufferstring + speichertext5 ' F5
Case 26: Pufferstring = Pufferstring + speichertext6 ' F6
Case 27: Pufferstring = Pufferstring + speichertext7 ' F7
Case 28: Pufferstring = Pufferstring + speichertext8 ' F8
Case 29: Pufferstring = Pufferstring + speichertext9 ' F9
Case Else: Recchar = Chr(recbyte)
Pufferstring = Pufferstring + Recchar
End Select
Pcmsk.4 = 1 ' PCINT wieder freigeben
Return
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Morsetabelle:
'Ascii 0 - 32 ; hier wird nur Linefeed in <kn> umgesetzt
Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , &B00101101 , 0 , 0, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 0 , 0
' Ascii 32 - 47 -> _!"#$%&'()*+,-./
Data 0 , 0 , &B01010010 , 0 , 0 , 0 , &B00101010 , 0 , &B00101101 , &B01101101 , 0 , &B00101010 , &B01110011 , &B01100001
Data &B01101010 , &B00101001
'Ascii 48 - 57 -> 0123456789
Data &B00111111 , &B00111110 , &B00111100 , &B00111000 , &B00110000 , &B00100000 , &B00100001 , &B00100011 , &B00100111 , &B00101111
'Ascii 58 - 64 -> :;<=>?@
Data &B01000111 , &B01110011 , &B00101101 , &B00110001 , &B01101101 , &B01001100 , 0
'Ascii 65 - 90 ABCDEFGHIJKLMNOPQRSTUVWXYZ
Data &B00000110 , &B00010001 , &B00010101 , &B00001001 , &B00000010 , &B00010100 , &B00001011 , &B00010000 , &B00000100
Data &B00011110 , &B00001101 , &B00010010 , &B00000111 , &B00000101 , &B00001111 , &B00010110 , &B00011011 , &B00001010
Data &B00001000 , &B00000011 , &B00001100 , &B00011000 , &B00001110 , &B00011001 , &B00011101 , &B00010011
'Umlaute willkürlich zugeordnet:
'Ascii 91-93 ÄÖÜ Ascii 94 - 96 -> unbelegt
Data &B00011010 , &B00010111 , &B00011100 , 0 , 0 , 0
Keydata:
' normal keys lower case
'------
' Aenderungen an Original-Tabelle:
' Y,Z Korrektur: Dez-Zahlen 121 und 122 getauscht
' Fragezeichen auf sz gelegt (man muss also nicht die Shift-Teste druecken)
' weitere Zuordnungen:
' Taste hex dez pseudo-ascii
' ö 4C 76 124
' ä 52 82 123
' ü 54 84 125
' F1 05 05 21
' F2 06 06 22
' F3 04 04 23
' F4 0C 12 24
' F5 03 03 25
' F6 0B 11 26
' F7 83 131 27
' F8 0A 10 28
' F9 01 01 29
' F10 09 09 30
' F11 78 120 31
' F12 07 07 20
' bksp 66 102 08
' ab dez 96 alles (ausser backspace) auf 0, damit werden folgende
' unnuetze tasten tot gelegt: einf entf cursors pos1 ende bild, pause
Data 0 , 29 , 0 , 25 , 23 , 21 , 22 , 20 , 0 , 30 , 28 , 26 , 24 , 0 , &H5E , 0
Data 0 , 0 , 0 , 0 , 0 , 113 , 49 , 0 , 0 , 0 , 121 , 115 , 97 , 119 , 50 , 0
Data 0 , 99 , 120 , 100 , 101 , 52 , 51 , 0 , 0 , 32 , 118 , 102 , 116 , 114 , 53 , 0
Data 0 , 110 , 98 , 104 , 103 , 122 , 54 , 7 , 8 , 44 , 109 , 106 , 117 , 55 , 56 , 0
Data 0 , 44 , 107 , 105 , 111 , 48 , 57 , 0 , 0 , 46 , 45 , 108 , 124 , 112 , 63 , 0
Data 0 , 0 , 123 , 0 , 125 , 0 , 0 , 0 , 0 , 0 , 13 , 43 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 0 , 8 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 31 , 0 , 0 , 0 , 0 , 0 , 0 , 0
'
' > 127: shifted keys UPPER case
' -------
' Aenderungen an Original-Tabelle:
' Y,Z Korrektur: Dez-Zahlen 89 und 90 getauscht
' an Platz 204, 210, 212 Ascii-Zeichen für Umlaute eingetragen 92 91 93
' auch hier alles ueber dez 224 auf Null:
Data 0 , 0 , 0 , 27 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 81 , 33 , 0 , 0 , 0 , 89 , 83 , 65 , 87 , 34 , 0
Data 0 , 67 , 88 , 68 , 69 , 0 , 35 , 0 , 0 , 32 , 86 , 70 , 84 , 82 , 37 , 0
Data 0 , 78 , 66 , 72 , 71 , 90 , 38 , 0 , 0 , 76 , 77 , 74 , 85 , 47 , 40 , 0
Data 0 , 59 , 75 , 73 , 79 , 61 , 41 , 0 , 0 , 58 , 95 , 76 , 92 , 80 , 63 , 0
Data 0 , 0 , 91 , 0 , 93 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Bedienung
Der ATtiny wird mit dem Reset-Taster geweckt, versorgt die
Tastatur mit Strom und wartet auf Eingaben. Sie werden allerdings erst
ausgesendet, nachdem ein Leerzeichen oder ein "Return" eingegeben wurde
("word mode").
Mit der F1- und der F2-Taste wird die Geschwindigkeit um 2 WpM
vermindert oder erhöht. Der initiale Wert ist 30 Wpm. Mit der F3-Taste
kann man eine Ausgabe der Geschwindigkeit in WpM anstoßen. Mit F4 bis
F9 kann man einen von sechs Speichertexten aussenden und mit F12 den
Vorschreibspeicher komplett löschen. Die Speichertexte müssen mit
Const-Befehlen fest in die Firmware einkompoliert werden.
Falls man sich mal vertippt hat und es rechtzeitig merkt, kann man
mit der "Backspace-Taste" das jeweils letzte Zeichen aus dem
Vorschreib-Puffer löschen.
Nach etwa 20 Minuten Nichtbenutzung legen sich Attiny und Tastatur schlafen.
PS/2- Tastaturen
Wer keine alte PS/2-Tastatur herumliegen hat: Bei Reichelt sind einige
USB-Tastaturen gelistet, die noch PS/2 "sprechen" können.
Der Nachfolger meines Exemplars heißt "KEYSONIC ACK595P". Daneben
gibt es noch einige flexible, aufrollbare Tastaturen, die sehr
preiswert sind, aber hinsichtlich der Bedienung gewöhnungsbedürftig
sein dürften, z.B. "LOGILINK ID0018A" und teure Cherry-Tastaturen.
Neue Version der Software
Die Software enthält einen "blöden" Flüchtigkeitsfehler: sie geht nie
in den energiesparenden Powerdown. Die zweite innere Schleife, in der
die Erzeugung der Morsezeichen erfolgt, wird nie verlassen, weil sie
mit "Loop" und nicht mit "Loop until Pufferstring = "" " endet.
Ausserdem wurde der Wunch geäußert, einige zusammengezogene
Verkehrszeichen einzubauen:
Die Verkehrszeichen "kn" "ka" "as" "ve" und "sk" liegen nun auf den
Tasten #, ^, Esc, Tab und Strg.
Download des (aktualisierten) Quellcodes: 1122-morse-keyboard.zip