(Interruptgesteuerte PS/2-Schnittstelle am AVR) |
(kein Unterschied)
|
Aktuelle Version vom 25. Dezember 2009, 12:13 Uhr
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
