Inhaltsverzeichnis
Wissenswertes über die PS/2-Schnittstelle
Im WWW gibt es sehr viele Seiten, die sich mit der Schnittstelle und deren Protokoll befassen. Besonders empfehlen möchte ich folgende:
Warum nicht die BASCOM-Funktionen GETATKBD() und GETATKBDRAW() benutzen?
Die genannten Funktionen werden i.d.R. in der Hauptprogrammschleife eines BASCOM-Programms zyklisch aufgerufen. Da die Clockfrequenz der PS/2-Schnittstelle mit 10-16kHz arbeitet, muß ebenfalls mindestens in dieser Frequenz das Hauptprogramm abgearbeitet werden, ansonsten gehen Daten verloren. Das kann bei umfangreicheren Programmen schon mal zum Problem werden. Alternativ könnte man den AVR ja per Timer-Interrupt zwingen, die Funktionen GETATKBD() bzw. GETATKBDRAW() im o.g. Zeitraster auszuführen. Da die Übertragung eines Bytes jedoch bis zu 2ms dauern kann, ist für diese 2ms dann aber auch der AVR blockiert und andere Interrupts können erst anschließend ausgeführt werden. Obwohl die PS/2-Schnittstelle eine bidirektionale ist, liefert BASCOM leider keine Funktion zum Senden eines Bytes an ein PS/2-Device, wie man es z.B. zur Ansteuerung der Tastatur-LED benötigt. Es müßte also sowieso etwas Programmentwicklung geleistet werden.
Hardware-Anschluss PS/2
Das Schema zeigt ausschliessliche den Anschluss der PS/2-Schnittstelle, die Restbeschaltung ist den eigenen Bedürfnissen anzupassen. Das PS/2-Clock-Signal muss an einen externen Interrupt-Eingang (Int0, Int1, etc.) des AVR angeschlossen werden. Der verwendete Interrupteingang ist der Datei PS2_CONF.BAS dem Kompiler mitzuteilen. Der verwendete Interrupt (Int0, Int1, etc.) ist ebenfalls an verschiedenden Stellen in den Dateien PS2_CONF.BAS und PS2.BAS anzupassen.
BASCOM-Quellcode
Der Quellcode ist in die folgenden drei Dateien aufgeteilt:
Hauptprogramm.bas | (Das zu kompilierende Hauptprogramm) |
Ps2_Conf.bas | (Konfigurationsdatei) |
Ps2.bas | (Programmdatei PS/2-Funktionen) |
Hauptprogramm.bas
Das Programmbeispiel zeigt bei Tastenbetätigung die Scancodes einer angeschlossenen PS/2-Tastatur in hexadezimaler Form auf einem Text-LCD an.
'******************************************************************************* '*** Demomonstration Anschluß PS/2 Tastatur an AVR *** '******************************************************************************* '*** Autor: screwdriver *** '*** Datum: 25.12.2009 *** '******************************************************************************* '******************************************************************************* '*** Konfiguration *** '******************************************************************************* '*** Konfiguration AVR ***************************************************** $regfile = "m16def.dat" $crystal = 16e6 $hwstack = 64 $swstack = 64 $framesize = 64 '*** Konfiguration LCD ***************************************************** Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.6 , Rs = Portc.7 Config Lcd = 16 * 4 Cursor Off , Noblink Cls '******************************************************************************* '*** Externe Dateien (Config) *** '******************************************************************************* $include "PS2_Conf.bas" 'Konfiguration PS/2 Tastatur '******************************************************************************* '*** Konstanten *** '******************************************************************************* '******************************************************************************* '*** Variablen *** '******************************************************************************* Dim Btemp As Byte 'Allgemeine Byte-Variable '******************************************************************************* '*** HAUPTPROGRAMM *** '******************************************************************************* Call Ps2_init() 'PS/2-Schnittstelle initialisieren Enable Interrupts 'Globale Interruptfreigabe Do If Ps2_count() <> 0 Then 'Elemente in Ringpuffer vorhanden? Btemp = Ps2_buffer_read() 'Ein Element herrausnehmen.. Lcd Hex(btemp) ; " " '..und auf LCD anzeigen End If Loop '******************************************************************************* '*** Externe Dateien (Programm) *** '******************************************************************************* $include "PS2.bas" 'Konfiguration PS/2 Tastatur
Ps2_Conf.bas
'******************************************************************************* '*** Konfigurationsdatei PS/2-Schnittstelle an AVR *** '******************************************************************************* '*** Autor: screwdriver *** '*** Datum: 25.12.2009 *** '******************************************************************************* $nocompile 'Include-Datei! '******************************************************************************* '*** Hardwarekonfiguration *** '******************************************************************************* Ps2_clock Alias Pind.3 'PS/2-Clock (Eingang) Ps2_clock_port Alias Portd.3 'PS/2-Clock (Ausgang) Ps2_data Alias Pind.4 'PS/2-Data (Eingang) Ps2_data_port Alias Portd.4 'PS/2-Data (Ausgang) Config Int1 = Falling 'PS/2-Clock auf Ext. Int1 On Int1 Isr_int1 'Sprungadresse Int1 festlegen '******************************************************************************* '*** Konstanten *** '******************************************************************************* Const Ps2_buff_size = 8 'Größe PS/2-Ringpuffer '******************************************************************************* '*** Variablen *** '******************************************************************************* Dim Ps2_mode As Bit '0: Host <- Device, 1: Host -> Device Dim Ps2_state As Byte 'State-Counter PS/2 Kommunikation Dim Ps2_parity As Byte 'Paritätszähler Dim Ps2_byte As Byte 'Empfangenes bzw. Zusendendes Byte Dim Ps2_buffer(ps2_buff_size) As Byte 'PS/2-Ring-Puffer Dim Ps2_in_ptr As Byte 'Input-Pointer PS/2-Ring-Puffer (Schreiben) Dim Ps2_out_ptr As Byte 'Ouput-Pointer PS/2-Ring-Puffer (Auslesen) '******************************************************************************* '*** Deklaration Unterprogramme '******************************************************************************* Declare Sub Ps2_init() 'Initialisierung PS/2 Schnittstelle Declare Sub Ps2_buffer_write(byval Ps2data As Byte) 'Schreibt Element in PS/2-Ring-Puffer Declare Function Ps2_buffer_read() As Byte 'Liest Element aus PS/2-Ring-Puffer Declare Sub Ps2_sendbyte(byval Ps2_byte2send As Byte) 'Gibt ein Byte über PS/2-Schnittstelle aus Declare Function Ps2_count()as Byte 'Liefert aktuelle Anzahl Elemente im Ringpuffer
Ps2.bas
'******************************************************************************* '*** Programmdatei PS/2-Schnittstelle an AVR *** '******************************************************************************* '*** Autor: screwdriver *** '*** Datum: 25.12.2009 *** '******************************************************************************* $nocompile 'Include-Datei! '******************************************************************************* '*** Unterprogramme *** '******************************************************************************* Sub Ps2_init() Config Ps2_clock_port = Input Config Ps2_data_port = Input Ps2_mode = 0 'Transmission Device -> Host Ps2_state = 0 'Statemachine auf Anfang setzen Ps2_in_ptr = 1 'Ring-Puffer "Schreiben" auf Anfang Ps2_out_ptr = 1 'Ring-Puffer "Lesen" auf Anfang Ps2_buffer(1) = 0 'Erstes Element Nullsetzen Enable Int1 'Interruptfreigabe Int1 End Sub '******************************************************************************* Sub Ps2_buffer_write(byval Ps2data As Byte) Ps2_buffer(ps2_in_ptr) = Ps2data 'Element in Ringpuffer schreiben If Ps2_in_ptr < Ps2_buff_size Then 'Wenn Pufferende nicht erreicht, Ps2_in_ptr = Ps2_in_ptr + 1 'PointerVar erhöhen Else 'sonst Ps2_in_ptr = 1 'PointerVar auf Anfang setzen End If End Sub '******************************************************************************* Function Ps2_buffer_read() As Byte If Ps2_in_ptr <> Ps2_out_ptr Then 'Wenn Elemente in Puffer vorhanden Ps2_buffer_read = Ps2_buffer(ps2_out_ptr) 'Element auslesen If Ps2_out_ptr < Ps2_buff_size Then 'Wenn Pufferende nicht erreicht, Ps2_out_ptr = Ps2_out_ptr + 1 'PointerVar erhöhen Else 'sonst Ps2_out_ptr = 1 'PointerVar auf Anfang setzen End If Else Ps2_buffer_read = 0 'Keine Elemente im Puffer, Rückgabewert 0 End If End Function '******************************************************************************* Sub Ps2_sendbyte(byval Ps2_byte2send As Byte) Disable Int1 'Interruptsperre Int1 Ps2_clock = 0 'Clock-Signal auf Low Config Ps2_clock_port = Output 'Clock-Leitung als Ausgang Config Ps2_data_port = Input 'Data-Leitung als Eingang 'Clock-Port für mind. 60µs auf Low halten 'Waitus 20 'Waitus 20 'Waitus 20 Ps2_mode = 1 'Transmission Host -> Device Ps2_state = 0 'Statemachine Nullsetzen Ps2_in_ptr = 1 'Ring-Puffer auf.. Ps2_out_ptr = 1 '..Anfang setzen Ps2_buffer(1) = 0 'Erstes Element Nullsetzen Ps2_byte = Ps2_byte2send Config Ps2_data_port = Output Ps2_data_port = 0 Config Ps2_clock_port = Input Enable Int1 'Interruptfreigabe Int1 End Sub '******************************************************************************* Function Ps2_count()as Byte Local Ps2_var As Byte 'Hilfsvariable push r0:in r0,gicr:push r0:disable int1 'Int1 sperren Ps2_var = Ps2_in_ptr - Ps2_out_ptr 'Ringpufferelemente bestimen pop r0:!out gicr,r0:pop r0 'Int1 wiederherstellen If Ps2_in_ptr < Ps2_out_ptr Then Ps2_count = Ps2_var + Ps2_buff_size 'Korrektur bei negativem Ergebnis Else ps2_count=ps2_var End If End Function '******************************************************************************* '*** Serielle Empfangsroutine per Interrupt *** '******************************************************************************* Isr_int1: If Ps2_mode = 0 Then 'Transmission Device -> Host Select Case Ps2_state Case 0 'Startbit Ps2_state = 1 Case 1 To 8 '8 Databits empfangen Shift Ps2_byte , Right 'Platz für nächstes Bit schaffen Ps2_byte.7 = Ps2_data 'Bit lesen Ps2_state = Ps2_state + 1 'Folge-State setzen Case 9 'Paritybit (keine Auswertung) Ps2_state = 10 'Folge-State setzen Case 10 'Stopbit, d.h. Byte komplett Call Ps2_buffer_write(ps2_byte) 'Byte in Ringpuffer schreiben Ps2_state = 0 'Statemachine auf Anfang End Select Else 'Transmission Host -> Device Select Case Ps2_state Case 0 'Startbit Ps2_data_port = 0 'Data-Leitung Low Ps2_parity = 0 'Paritätszähler Nullen Ps2_state = 1 'Folge-State setzen Case 1 To 8 '8 Datenbit ausgeben If Ps2_byte.0 = 1 Then Ps2_data_port = 1 'PS/2-Data-Leiung High Ps2_parity = Ps2_parity + 1 'Paritätszähler verwalten Else Ps2_data_port = 0 'PS/2-Data-Leiung Low End If Shift Ps2_byte , Right 'Nächstes Bit holen Ps2_state = Ps2_state + 1 'Folge-State setzen Case 9 'Paritätsbit ausgeben If Ps2_parity.0 = 0 Then Ps2_data_port = 1 'PS/2-Data-Leiung High Else Ps2_data_port = 0 'PS/2-Data-Leiung Low End If Ps2_state = 10 'Folge-State setzen Case 10 'Stopbit Config Ps2_data_port = Input Ps2_state = 11 'Folge-State setzen Case 11 'Acknowlegde Call Ps2_init() End Select End If Return