Aus RN-Wissen.de
Wechseln zu: Navigation, Suche


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.

PS2-Anschluss.png


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