Fernbedienung für c't-Lab

Die Zeitschrift c't hat Mitte 2007 ein Lötprojekt mit dem Namen c't-Lab gestartet www.ct-lab.de . Es handelt sich um ein Modulares Messgerät, das vom PC aus gesteuert werden kann und ebenfalls Messergebnisse an den PC zurückschicken kann. Damit das LAB auch ohne PC bequem zu bedienen ist, kann das folgende Modul dazugehängt werden. Befehle können dann per PS2-Tastatur oder IR-Fernbedienung eingegeben und an die Module abgeschickt werden. Ein Timer, der Befehle in bestimmten Zeitabständen automatisch abschickt ist ebenfalls integriert.
Verwirklicht hab ich das mit einem AT-Mega8 von Atmel. Das Programm ist mit Bascom-Basic von www.mcselec.com entstanden.
Plan und Quellcode liegen im Downloadbereich zur freien Verfügung bereit.

Plan:

Quellcode

'---------------------------------------------------------------------------
'     c't LAB TERMINAL 1.0
'
'     PS2-Tastatur und RC5-Fernbedienung f?r das C'T-LAB
'
'     RC5 Fernbedienung: Tasten anlernen, Speicherpl?tze abrufen
'     PS2 Tastatur: Befehle senden, Speicher f?llen und auslesen,
'                   Timer starten und stoppen
'
'
'---------------------------------------------------------------------------

$regfile = "m8def.dat"
$crystal = 14745600
$baud = 38400

$hwstack = 40
$swstack = 16
$framesize = 32

Declare Sub Writeserinp
Declare Sub Checkserinp
Declare Sub Fblearn
Declare Sub Lcdanzeige
Declare Sub Init


Config Serialin = Buffered , Size = 63 , Bytematch = 13
Config Serialout = Buffered , Size = 63
Config Rc5 = Pind.3
Config Lcdpin = Pin , Db4 = Portc.2 , Db5 = Portc.3 , Db6 = Portc.4 , Db7 = Portc.5 , E = Portb.1 , Rs = Portb.0
Config Lcdbus = 4
Config Lcd = 16 * 2
Config Keyboard = Pind.2 , Data = Pind.6 , Keydata = Keydata
Config Timer1 = Timer , Prescale = 1024
Config Watchdog = 512                                       'Mit dem Watchdogtimer ist das an- und abstecken der Tastatur im Betrieb m?glich

On Timer1 Ontimer1
On Int0 Isr0
On Int1 Isr1
Enable Int0
Enable Int1
Config Int0 = Falling
Config Int1 = Falling

Dim Lcd_dirty As Bit
Dim Serinpstr As String * 63
Dim Fb_learning_on As Bit                                   'Es werden gerade Tasten eingelernt
Dim Fb_speicher As Bit                                      'es wird ein Speicherplatz ausgelesen
Dim Cr As Word
Dim Cr_received As Bit
Dim Strsize As Byte
Dim Strlang As Byte
Dim Address As Byte , Command As Byte
Dim Fbstring As String * 33
Dim Lcdstring As String * 33
Dim Lcdstringptr(33) As Byte At Lcdstring Overlay
Dim Timerstring As String * 9
Dim Timerstringptr(9) As Byte At Timerstring Overlay
Dim Lcdstringoben As String * 16
Dim Lcdstringunten As String * 16
Dim Taste As Word
Dim Fbin As Bit
Dim Timerlearn As Bit
Dim Lcd_command As Bit
Dim V_ausrufezeichen As Byte
Dim V_fragezeichen As Byte
Dim V_doppelpunkt As Byte
Dim V_gleich As Byte
Dim V_speicher As Byte
Dim V_enter As Byte
Dim V_back As Byte
Dim V_jungfrau As Byte
Dim Ib As Byte
Dim Ic As Byte
Dim Id As Byte
Dim Speicher As Word
Dim Checkbuff As String * 33
Dim Timerdauer As Word
Dim Timerarr(9) As String * 33
Dim Fb_textarr(7) As String * 5                             'diese Zeichen werden beim FB-einlernen abgefragt
Fb_textarr(1) = "!"
Fb_textarr(2) = "?"
Fb_textarr(3) = ":"
Fb_textarr(4) = "="
Fb_textarr(5) = "Store"
Fb_textarr(6) = "Enter"
Fb_textarr(7) = "Back"

Deflcdchar 4 , 14 , 17 , 17 , 22 , 17 , 25 , 22 , 16        ' ?
Deflcdchar 1 , 17 , 14 , 17 , 17 , 31 , 17 , 17 , 32        ' ?
Deflcdchar 2 , 17 , 14 , 17 , 17 , 17 , 17 , 14 , 32        ' ?
Deflcdchar 3 , 17 , 32 , 17 , 17 , 17 , 17 , 14 , 32        ' ?

Enable Interrupts
Echo Off
Cursor Off
Start Watchdog
Cr = 1
Call Init
 If V_jungfrau = 255 Then                                   'wenn das EEPROM leer ist die Speicher mit Null-Strings f?llen
 Fbstring = ""
 While Cr < 10
         Taste = Cr * 30
         Taste = Taste - 29
         Writeeeprom Fbstring , Taste
         Incr Cr
 Wend
 Cr = 0
 Writeeeprom Cr , 8175                                      'nicht mehr Jungfrau
 Cr = 1
 Writeeeprom Cr , 8173                                      'Standardwert f?r Timer1
 Taste = 0
 Timerlearn = 0
End If
Cls

Do

If Cr_received = 1 Then                                     'wenn was im ser. Buffer ist abholen
   Cr_received = 0
   Input Serinpstr
   'zum Abtrennen eines oder mehrerer LF am Anfang des Strings, das noch vom Vorg?nger-String im Buffer steht
   While Asc(serinpstr) = 10
      Strsize = Len(serinpstr) - 1
      Serinpstr = Right(serinpstr , Strsize)
   Wend
   'Hier ist der String empfangen und ein event. LF entfernt
   Call Checkserinp
End If
If Fb_learning_on = 0 And Fbin = 1 Then                     'wenn nicht gerade die FB eingelernt wird und ein Interrupt da war
   Fbin = 0
   Getrc5(address , Command)
   If Command > 127 Then Command = Command - 128            'FB-Tastendruck ?
   Select Case Command
    Case 0 To 9 :
      Fbstring = Fbstring + Str(command)
      If Fb_speicher = 1 And Command <> 0 Then              ' wenn ein Speicherplatz angefordert wurde
         Fb_speicher = 0
         Command = Command * 30
         Command = Command - 29
         Readeeprom Fbstring , Command
      End If
    Case V_ausrufezeichen :
     Fbstring = Fbstring + "!"
    Case V_fragezeichen :
     Fbstring = Fbstring + "?"
    Case V_doppelpunkt :
     Fbstring = Fbstring + ":"
    Case V_gleich :
     Fbstring = Fbstring + "="
    Case V_speicher :
     Fbstring = "Speichernummer: "
      Fb_speicher = 1
    Case V_back:
      If Len(fbstring) = 1 Then
       Fbstring = ""
      Else
       Strsize = Len(fbstring) - 1
       Fbstring = Left(fbstring , Strsize)
      End If
    Case V_enter :
      Serinpstr = Fbstring
      Fbstring = ""
      Call Writeserinp
 End Select
 Lcd_dirty = 1
End If

If Timerlearn = 1 Then
   Ic = 1
   For Ic = 1 To 9
      If Timerstringptr(ic) > 48 And Timerstringptr(ic) < 58 Then
       Id = Timerstringptr(ic)
       Id = Id - 48
       Id = Id * 30
       Id = Id - 29
       Readeeprom Timerarr(ic) , Id
     End If
   Next Ic
   Timerlearn = 0
   Fbstring = ""
   Lcd_dirty = 1
End If
If Lcd_dirty = 1 Then
   Lcd_dirty = 0
   Call Lcdanzeige
End If
Reset Watchdog
Loop

'---------------------------------------------------------------------------
'     Init
'
'---------------------------------------------------------------------------
Sub Init

   Readeeprom V_jungfrau , 8175
   Readeeprom V_ausrufezeichen , 8176                       'alle Tastencodes aus dem Eeprom holen
   Readeeprom V_fragezeichen , 8177                         'die Speicherzellen im EEPROM ganz hinten...
   Readeeprom V_doppelpunkt , 8178
   Readeeprom V_gleich , 8179
   Readeeprom V_speicher , 8180
   Readeeprom V_enter , 8181
   Readeeprom V_back , 8182

End Sub
'---------------------------------------------------------------------------
'     Writeserinp
'     Leitet den empfangenen String zum n?chsten Modul weiter
'---------------------------------------------------------------------------

Sub Writeserinp
  Print Serinpstr                                           ' Befehl Weiterreichen
End Sub
'---------------------------------------------------------------------------
'     Serial0charmatch
'     Im Eingangspuffer wurde ein "13" empfangen
'---------------------------------------------------------------------------
 Serial0charmatch:
    Cr_received = 1
  Return
'---------------------------------------------------------------------------
'     LCD Anzeige
'
'     Routine zum konvertieren des FBstrings f?r LCD.
'     die Sonderzeichen haben andere Asciicodes...
'     Zeilenumbruch des Displays
'---------------------------------------------------------------------------
Sub Lcdanzeige

Lcdstring = Fbstring
   For Ib = 1 To Len(lcdstring)
     Select Case Lcdstringptr(ib)
        Case 129:
            Lcdstringptr(ib) = 245
        Case 132:
            Lcdstringptr(ib) = 225
        Case 148:
            Lcdstringptr(ib) = 239
        Case 225:
            Lcdstringptr(ib) = 4
        Case 142:
            Lcdstringptr(ib) = 1
        Case 153:
            Lcdstringptr(ib) = 2
        Case 154:
            Lcdstringptr(ib) = 3
     End Select
   Next Ib                                                  'n?chstes zeichen
Locate 1 , 1
Lcdstringoben = Left(lcdstring , 16)
Lcd Lcdstringoben ; "                "
Lowerline
Strsize = Len(lcdstring)
Lcdstringunten = Mid(lcdstring , 17 , 32)
Lcd Lcdstringunten ; "                "
If Lcd_command = 1 Then
   Stop Watchdog
   Waitms 500
   Start Watchdog
   Fbstring = ""
   Lcd_command = 0
   Lcd_dirty = 1
End If
End Sub
'---------------------------------------------------------------------------
'     Checkserinp
'     ist das erste Zeichen im String ein "#" und das 4. ein "%" ?
'     Zeichen 2 und 3 sind das Kommando und werden ausgewertet
'---------------------------------------------------------------------------

Sub Checkserinp

   If Asc(serinpstr) = 35 Then                              'wenn der String mit # losgeht kanns f?r uns sein
     Checkbuff = Mid(serinpstr , 4 , 1)                     'Variable zweckentfremdet um Speicher zu sparen
     Speicher = Asc(checkbuff)
     If Speicher = 37 Then                                  'wenn an 4. Stelle ein % kommt ists f?r uns
      Checkbuff = Mid(serinpstr , 2 , 2)
      Speicher = Val(checkbuff)
      Select Case Speicher
         Case 1 To 9:
            Speicher = Speicher * 30
            Speicher = Speicher - 29
            Checkbuff = Mid(serinpstr , 5 , 32)
            Writeeeprom Checkbuff , Speicher
            Serinpstr = "#Speicher " + Str(speicher) + " Wert " + Checkbuff + " geschrieben"
            Call Writeserinp
          Case 99:                                          ' Tastenkonfiguration wird aufgerufen
            Call Fblearn
          Case 98:
            Checkbuff = Mid(serinpstr , 5 , 5)
            Speicher = Val(checkbuff)
            If Speicher > 65000 Then Speicher = 65000
            Writeeeprom Speicher , 8173
            Serinpstr = "# Geschwindigkeit gespeichert"
            Call Writeserinp
          Case 97:
            Timerlearn = 1
            Timerstring = Mid(serinpstr , 5 , 9)
            Serinpstr = "# Abfolge: " + Timerstring + " gespeichert"
            Call Writeserinp
          Case Else
            Serinpstr = "# Speichererror FB-Modul"
            Call Writeserinp
      End Select
      Else
         Call Writeserinp                                   'kein % dann schick den String weiter
      End If
   Else
      Call Writeserinp                                      'kein # dann schick den String weiter
   End If
End Sub
'---------------------------------------------------------------------------
'     FBlearn
'     Routine zum Zuordnen von Tasten zu den Befehlen
'---------------------------------------------------------------------------
Sub Fblearn
Dim I As Byte
Dim K As Integer
Stop Watchdog
Fb_learning_on = 1
I = 1
While I < 8
   Cls
   Lcd "Bitte Taste f" ; Chr(245) ; "r"
   Lowerline
   Lcd Fb_textarr(i) ; " dr" ; Chr(245) ; "cken"
   Do
      Getrc5(address , Command)
      If Command > 127 Then Command = Command - 128         'Togglebit loswerden
   Loop Until Command <> 127                                'Togglebit loswerden
   K = I + 8175
   Writeeeprom Command , K
   Cls
   Lcd Str(command) ; " Stored"
   Waitms 2000
   Incr I
Wend
Cls
Lcd "FB einlernen"
Lowerline
Lcd "ist beendet"
Call Init
Waitms 2000
Cls
Fb_learning_on = 0
Start Watchdog
End Sub

'---------------------------------------------------------------------------
'     Isr0
'     Interruptroutine f?r die Tastatur
'     der externe Interrupt 0 ?berwacht die Dataleitung der Tastatur
'---------------------------------------------------------------------------
Isr0:                                                       'interrupt routine
 Taste = Getatkbd()                                         'Get data from AT keyboard
 Select Case Taste
 Case 13:
      Serinpstr = Fbstring
      Fbstring = ""
      Call Writeserinp
 Case 8:
      If Len(fbstring) = 1 Then
       Fbstring = ""
      Else
       Strlang = Len(fbstring) - 1
       Fbstring = Left(fbstring , Strlang)
      End If
 Case 171:                                                  'F-Tasten auslesen
      Readeeprom Fbstring , 1
      Lcdstring = Fbstring
 Case 172:
      Readeeprom Fbstring , 31
      Lcdstring = Fbstring
 Case 173:
      Readeeprom Fbstring , 61
      Lcdstring = Fbstring
 Case 174:
      Readeeprom Fbstring , 91
      Lcdstring = Fbstring
 Case 175:
      Readeeprom Fbstring , 121
      Lcdstring = Fbstring
 Case 176:
      Readeeprom Fbstring , 151
      Lcdstring = Fbstring
 Case 177:
      Readeeprom Fbstring , 181
      Lcdstring = Fbstring
 Case 178:
      Readeeprom Fbstring , 211
      Lcdstring = Fbstring
 Case 179:
      Readeeprom Fbstring , 241
      Lcdstring = Fbstring
 Case 181:                                                  'Shift und F-Taste: Befehl unter F-Taste speichern
      Writeeeprom Fbstring , 1
      Timerlearn = 1
 Case 182:
      Writeeeprom Fbstring , 31
      Timerlearn = 1
 Case 183:
      Writeeeprom Fbstring , 61
      Timerlearn = 1
 Case 184:
      Writeeeprom Fbstring , 91
      Timerlearn = 1
 Case 185:
      Writeeeprom Fbstring , 121
      Timerlearn = 1
 Case 186:
      Writeeeprom Fbstring , 151
      Timerlearn = 1
 Case 187:
      Writeeeprom Fbstring , 181
      Timerlearn = 1
 Case 188:
      Writeeeprom Fbstring , 211
      Timerlearn = 1
 Case 189:
      Writeeeprom Fbstring , 241
      Timerlearn = 1
 Case 190:                                                  'Rollen: Befehl st?ndig senden
      If Timerdauer = 0 Then                                'Timer starten
          Readeeprom Timerdauer , 8173
          Enable Timer1
          Fbstring = "Timer l" + Chr(132) + "uft"
          Lcd_command = 1
      Else                                                  'Timer stoppen
         Timerdauer = 0
         Disable Timer1
         Fbstring = "Timer ist aus"
         Lcd_command = 1
      End If
 Case 191:                                                  'Shift und Rollen: Das Timerarray wird programmiert
         Timerlearn = 1
         Timerstring = Left(fbstring , 9)                   'n?chstes zeichen
 Case Else:
      If Taste <> 0 Then
         Fbstring = Fbstring + Chr(taste)
      End If
 End Select
 'End If
 Set Gifr.intf0
 Taste = 0
 Lcd_dirty = 1
Return

'---------------------------------------------------------------------------
'     RC5 Interrupt
'     Hier wird nur ein Flag gesetzt.
'     Der FB-Code kann hier nicht geholt werden, da der Interrupt auch
'     den Bascom Highlevelbefehl stoppt, denn dieser benutzt Timer0 und dessen Interrupt
'---------------------------------------------------------------------------
Isr1:
Fbin = 1
  Set Gifr.intf1
Return
'---------------------------------------------------------------------------
'     Timer1 Interrupt
'
'     es wird auf eine Timerdauer zw. 1 und 65535 gewartet
'
'---------------------------------------------------------------------------
Ontimer1:
  If Timerdauer <> 0 Then
  If Id = 0 Or Id > 9 Then Id = 1
    Timer1 = Timerdauer
    While Asc(timerarr(id)) = 0 And Id < 10
        Incr Id
    Wend
    If Id < 10 Then
     Serinpstr = Timerarr(id)
     Call Writeserinp
     Incr Id
    End If
  End If
Return

End
'---------------------------------------------------------------------------
'     Keydata
'     Codetabelle f?r die Tastatur
'     da Tastaturen keinen Ascii-Code ausgeben wird in dieser Tabelle
'     jeder Taste ein Ascii-Code zugewiesen
'---------------------------------------------------------------------------
Keydata:
'Kleinbuchstaben
Data 0 , 179 , 0 , 175 , 173 , 171 , 172 , 0 , 0 , 180 , 178 , 176 , 174 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 113 , 49 , 4 , 4 , 4 , 121 , 115 , 97 , 119 , 50 , 0
Data 0 , 99 , 120 , 100 , 101 , 52 , 51 , 4 , 4 , 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 , 4 , 4 , 46 , 45 , 108 , 148 , 112 , 225 , 0
Data 0 , 0 , 132 , 228 , 129 , 92 , 4 , 4 , 4 , 4 , 13 , 4 , 4 , 35 , 0 , 0
Data 0 , 60 , 0 , 0 , 0 , 0 , 8 , 0 , 0 , 49 , 0 , 52 , 55 , 0 , 0 , 0
Data 48 , 44 , 177 , 53 , 54 , 56 , 0 , 0 , 0 , 43 , 51 , 45 , 42 , 57 , 190 , 0

'Grossbuchstaben
Data 0 , 189 , 0 , 185 , 183 , 181 , 182 , 0 , 0 , 0 , 188 , 186 , 184 , 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 , 153 , 80 , 63 , 0
Data 0 , 0 , 142 , 0 , 154 , 96 , 0 , 0 , 0 , 0 , 13 , 94 , 0 , 42 , 0 , 0
Data 0 , 62 , 0 , 0 , 0 , 8 , 0 , 0 , 49 , 0 , 52 , 55 , 0 , 0 , 0 , 0
Data 48 , 44 , 187 , 53 , 54 , 56 , 0 , 0 , 0 , 43 , 51 , 45 , 42 , 57 , 191 , 0