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

Rutscherle 2

Rutscherle 2 - Ein selbstbalancierender Elektroroller (Segway Eigenbau)

Beim Rutscherle 2 handelt es sich um einen selbstbalancierenden Elektroroller mit großer Ähnlichkeit zum bekannten Segway. Es war beabsichtigt, besser zu sein als das Original. Nach vielen Versuchen ist das im Bezug auf Geräuschentwicklung und Regelung gelungen. Bei den Akkus besteht aus Kostengründen noch etwas Nachholbedarf.

Dies ist kein kommerzielles Projekt und soll es auch nicht werden.


Und so fährt der Eigenbau:
Video
Und so etwas schneller:
Video
Und so sieht die nächste Generation aus:
Video

Eine Warnung vorab

Dieser Artikel stellt keine komplette Bauanleitung dar. Ein Nachbau kann nur erfolgreich sein, wenn man die Schaltbilder und Quelltexte auch versteht. Es fehlen auch einige PC-Testprogramme. Mit diesen werden Motorregler und Hauptsteuerung vor dem ersten Start konfiguriert. Leider bietet rn-wissen keine Möglichkeit solche Dateien hochzuladen. Wer die Quelltexte und Schaltbilder versteht, kann sich diese jedoch einfach selbst Programmieren. Es fehlen auch die notwendigen Bibliotheken zum Display im Tacho. Die können wegen Copyright hier nicht veröffentlicht werden. Sie werden aber beim Display mitgeliefert. Gehen Sie einen Nachbau also nur an, wenn Sie hier im Artikel alles verstanden haben. Ich würde ja gerne allen weiterhelfen. Bei Menschen mit ähnlichem technischen Hintergrund macht das sogar richtig Spass. Bei anderen ist das nur noch mühsam. Besonders wenn die Kommunikation durch den Google-Übersetzer laufen muss.


Technische Daten

Antrieb

2 BLDC Felgenmotoren mit je 1000W/36V. Vom Hersteller in China direkt importiert. Mittlerweile gibt es auch in Europa vergleichbare Motoren zu kaufen. Die stammen zwar auch aus China, müssen jedoch nicht selbst importiert werden. Die Motoren sind im Lieferzustand nicht direkt verwendbar. Die Sensorik muss um 3 weitere Hallsensoren erweitert werden. Die bisherige Höchstgeschwindigkeit beträgt 27km/h. Um die Mobilitätshilfenverordnung einzuhalten wird diese zukünftig auf 20km/h begrenzt. Die maximale Verzögerung beim Bremsen beträgt -5m/s² (gemessen). Damit werden auch die Vorgaben der Mobilitätshilfenverordnung (-3,5m/s²) eingehalten.

Akkus

4 Bleiakkus mit 12V/18AH von denen 3 in Reihe geschaltet sind für die Motoren, einer allein für die Steuerung und eine zukünftige Beleuchtung. Geladen werden die Akkus an einem handelsüblichen Kfz-Ladegerät. Die Reichweite beträgt mehr als 20km. (Auf ebener Fahrbahn, bei einem Fahrer von 80kg.) Die Entscheidung für Bleiakkus wurde bewusst getroffen. Eine kWh verteuert sich um ca. 50 cent wenn sie durch den Bleiakku geht bei Litium-Polymer-Akkus sind das ca. 5 Euro. Auch sind Bleiakkus beim Laden wesentlich unempfindlicher.

Elektronik

2 Motorregler mit Atmega 168/10Mhz sowie eine Hauptsteuerung mit einem Atmega 644/20Mhz. Zusätzlich noch ein Display von Display3000 als Tacho und Datenlogger. Beschleunigungssensor: ADXL335, Gyros: LISY300AL (ArduIMU Sensor Board - Six Degrees of Freedom) Die ganze Elektronik wurde sehr einfach gehalten. (Zu mehr hats nicht gereicht) Das hat aber den Vorteil, daß keine besonderen Spezialkentnisse erforderlich sind. Wer schonmal eine Platine mit dem Bügeleisen hergestellt hat bekommt auch das hin.

Software

Die Software ist in Bascom geschrieben. Zeitkritische Teile in Assembler.

Mechanik

Der Mechanische Aufbau besteht nahezu komplett aus Aluminium. Dabei wurde darauf geachtet, dass alles mit einfachen Werkzeugen herstellbar ist. (Eine Ständerbohrmaschine sollte man schon haben.) Die Konstruktion ist so ausgelegt, dass so wenige Teile wie möglich hinter den Rädern hervorstehen. Dies verhindert Kratzer bei den ersten Fahrversuchen.

Entwicklungskosten

Im Vergleich zu den Entwicklungskosten des Originals, laut Wikipedia 100 Mio US$, liegt dieses Projekt mit unter 2000€ zuzüglich Zeit noch im erträglichen Bereich.

Sicherheitshinweis

Achtung! Gehen Sie so ein Projekt nur an, wenn Sie genau wissen, auf was Sie sich da einlassen. Man braucht schon ein paar Elektronik-Kenntnisse. Es können an der Elektonik sehr hohe Ströme auftreten und so entstehen schnell Rauchwölkchen. An den Motoren können - vor allem im Fehlerfall - sehr hohe Drehmomente entstehen, die für Mobiliar und Knochen sehr beeindruckend sein können. Zwei linke Hände wären also fehl am Platz. Beschweren Sie sich also bei sich selbst, wenn Sie im Krankenhaus aufwachen sollten. Sie haben es so gewollt.

Funktion

Die Funktion ist im groben recht banal. Im Prinzip macht das Teil das gleiche wie jeder Mensch beim gehen. Kommt ein Mensch nach vorn aus dem Gleichgewicht macht er einen Schritt nach vorn. Entsprechend nach hinten. Hier wird dieses Vorgehen elektronisch nachbildet. Kippt die Platform nach vorn, dann wird nach vorn beschleunigt und umgekehrt. Die genaue technische Umsetzung ist dann doch etwas aufwändiger. Die Regelung selbst erledigt ein PID-Regler. Danach kommen jedoch noch einige zusätzliche Faktoren die z.B. die Motorkennlinie ausgleichen sowie ungewollte Richtungsänderungen bei unebenen Oberflächen reduzieren. Ähnlich aufwändig wird es bei den Motorreglern. Eine reine Blockkommutierung, wie bei solchen Motoren üblich, ist auf unebenen Untergründen suboptimal. Ich habe die Motoren um zusätzliche Hallsensoren erweitert und diese mit Drehstrom oder genauer: Drehspannung angesteuert.

Elektronik

Motorregler

Der Motorregler regelt natürlich den Motor. Zusätzlich misst und übermittelt der Motorregler auch Spannung, Strom und Fahrstrecke. Die Temperatur des Kühlkörpers wird ebenfalls gemessen und wenn nötig ein Lüfter zugeschaltet. Die Ansteuerung des Motors ist keine Blockkomutierung. Diese hatte sich bei Experimenten als verbesserungswürdig gezeigt. Tatsächlich wird auf den 3 Leitungen eine angenäherte Sinusspannung ausgegeben. Das reduziert die Drehmomentwelligkeit deutlich. Ob und wieweit sich dadurch der Motorwirkungsgrad ändert wurde bisher nicht detailiert gemessen. Einen Anhaltspunkt liefert jedoch die Beobachtung, daß für eine Drehzahl von 100 1/min bei Blockkommutierung 10,5W benötigt werden, beim der hier verwendeten sinusähnlichen Kommutierung nur noch 6,5W. Die ganze Einheit wird vom PC aus über die Serielle Schnittstelle (5V) parametriert und kalibriert.

Der Motorregler ist auf zwei Platinen aufgebaut. Der Grund dafür ist die einfachere Herstellung der Platinen mit der Bügeleisenmethode sowie die räumliche Trennung der hohen Ströme vom Kontroller. Ob dies wegen der Störsicherheit wirklich notwendig ist wurde aus Kostengründen nie getestet. An der Stelle ist aber eine unnötige Sicherheit besser als eine vergessene. Die Verbindung zwischen den beiden Platinen bildet ein 16-adriges Flachkabel. Programmierstecker und der serielle Bus werden seitlich am Controllerboard gesteckt. Die Verbindung zu den Hallsensoren wird über ein 6-adriges Flachkabel hergestellt. Zusätzlich zu den beiden Platinen gehört zum Motorregler auch die im Motor verbaute Sensorplatine. Auf dieser werden die Signale der drei zusätzlichen Hallsensoren zu einem Signal zusammengefasst. Nach einem Ausfall wurde auch noch eine Zusatzplatine mit den Snubbern und den Supressordioden nachgerüstet. Zuvor war eine Halbbrücke nach ca. 125 km Fahrt ausgefallen.

Schaltbilder

Software Version 19

'Version 19


$prog &HFF , &HFF , &HD4 , &HF9                             'Fusebits
$regfile = "m168def.dat"
$crystal = 10000000
$baud = 57600                                               
$hwstack = 32
$swstack = 10
$framesize = 40

Declare Sub Setspeed
Declare Sub Pwm_start
Declare Sub Pwm_stop

Config Portc.0 = Input                                      'UBAT
Config Portc.1 = Input                                      'USHUNT
Config Portc.2 = Input                                      'TEMP
Config Portc.3 = Output                                     'LED1
Config Portc.4 = Output                                     'Lüfter
Config Portc.5 = Input                                      'Jumper1

Config Portd.2 = Input                                      'Hall_C
Config Portd.3 = Output                                     'A+
Config Portd.4 = Input                                      'Hall_B
Config Portd.5 = Output                                     'C+
Config Portd.6 = Output                                     'C-
Config Portd.7 = Input                                      'Hall_A
Config Portb.0 = Input                                      'Hall_D
Config Portb.1 = Output                                     'B-
Config Portb.2 = Output                                     'B+
Config Portb.3 = Output                                     'A-
Config Portb.4 = Output                                     'LED2
Config Portb.5 = Input                                      'Unbelegt (SCK)

Hall_a Alias Pind.7                                         'gelb
Hall_b Alias Pind.4                                         'blau
Hall_c Alias Pind.2                                         'Grün
Hall_d Alias Pinb.0
Led1 Alias Portc.3
Led2 Alias Portb.4
Fan Alias Portc.4
Jumper1 Alias Pinc.5

Dim Uzu As Integer At $100
Dim Izu As Integer At $102
Dim Ausgabeticks As Integer At $104                         'Anzahl Kommutieren zwischen Seriellen Telegrammen
Dim Temperatur As Integer At $106                           '
Dim Drehzahl As Integer At $108                             'Drehzahl
Dim M1 As Byte At $100 Overlay
Dim M2 As Byte At $101 Overlay
Dim M3 As Byte At $102 Overlay
Dim M4 As Byte At $103 Overlay
Dim M5 As Byte At $104 Overlay
Dim M6 As Byte At $105 Overlay
Dim M7 As Byte At $106 Overlay
Dim M8 As Byte At $107 Overlay
Dim M9 As Byte At $108 Overlay
Dim M10 As Byte At $109 Overlay
Dim Kalibrierwert As Integer At $10a
Dim Kal(2) As Byte At $10a Overlay
Dim A(15) As Byte At $10c
Dim S(12) As Byte At $11b
Dim Sinus(12) As Byte At $127
Dim Richtung(255) As Byte At $133
Dim Motorposition As Byte At $232
Dim Istrichtung As Integer At $233
Dim Altrichtung As Integer At $235
Dim Aposition As Byte At $237
Dim Zposition As Byte At $238
Dim Zwangskommutieren As Byte At $239
Dim Temp As Word At $23a
Dim Fancount As Word At $23c
Dim Motoradresse As Byte At $23e
Dim Vwinkel As Byte At $23f
Dim Rwinkel As Byte At $240
Dim Ticks As Integer At $241
Dim N As Byte At $243
Dim Notaus As Byte At $244
Dim Loopcount As Word At $245
Dim Sollrichtung As Byte At $247
Dim Altsollrichtung As Byte At $248
Dim Sollspeed As Byte At $249
Dim Altsollspeed As Byte At $24a
Dim Bdummy As Byte At $24b
Dim Intbdummy As Byte At $24c
Dim Intidummy As Integer At $24d
Dim Sdummy As Single At $24f
Dim Idummy1 As Integer At $253
Dim Idummy2 As Integer At $255
Dim Uw As Word At $257
Dim Iw As Word At $259
Dim Uwsum As Long At $25b
Dim Iwsum As Long At $25f
Dim Wcount As Long At $263
Dim Uoffset As Single At $267
Dim Ufaktor As Single At $26b
Dim Ioffset As Single At $26f
Dim Ifaktor As Single At $273
Dim Ep0 As Byte At $277
Dim Ep1 As Byte At $278
Dim Ep2 As Byte At $279
Dim Ep3 As Byte At $27a
Dim Ep4 As Byte At $27b
Dim Ep5 As Byte At $27c
Dim Ta As Byte At $27d
Dim Tb As Byte At $27e
Dim Tc As Byte At $27f
Dim Fehler As Byte At $280
Sollspeed = 0
Pwm_stop                                                    'Erstmal alles auschalten
'Variablen für die Richtungserkennung vorbelegen
'Diese Variante frisst zwar viel Arbeitsspeicher, ist aber sehr schnell
For N = 1 To 15
A(n) = 0
Next

For N = 1 To 255
Richtung(n) = 2
Next

If Jumper1 = 0 Then                                         'R
   Motoradresse = 82
   A(12) = 12
   A(4) = 11
   A(6) = 10
   A(14) = 9
   A(10) = 8
   A(2) = 7
   A(3) = 6
   A(11) = 5
   A(9) = 4
   A(1) = 3
   A(5) = 2
   A(13) = 1
   Richtung(21) = 3
   Richtung(93) = 3
   Richtung(220) = 3
   Richtung(196) = 3
   Richtung(70) = 3
   Richtung(110) = 3
   Richtung(234) = 3
   Richtung(162) = 3
   Richtung(35) = 3
   Richtung(59) = 3
   Richtung(185) = 3
   Richtung(145) = 3
   Richtung(81) = 1
   Richtung(25) = 1
   Richtung(155) = 1
   Richtung(179) = 1
   Richtung(50) = 1
   Richtung(42) = 1
   Richtung(174) = 1
   Richtung(230) = 1
   Richtung(100) = 1
   Richtung(76) = 1
   Richtung(205) = 1
   Richtung(213) = 1
Else
   Motoradresse = 76                                        'L
   A(11) = 1
   A(3) = 2
   A(1) = 3
   A(9) = 4
   A(13) = 5
   A(5) = 6
   A(4) = 7
   A(12) = 8
   A(14) = 9
   A(6) = 10
   A(2) = 11
   A(10) = 12
   Richtung(236) = 3
   Richtung(196) = 3
   Richtung(69) = 3
   Richtung(93) = 3
   Richtung(217) = 3
   Richtung(145) = 3
   Richtung(19) = 3
   Richtung(59) = 3
   Richtung(186) = 3
   Richtung(162) = 3
   Richtung(38) = 3
   Richtung(110) = 3
   Richtung(42) = 1
   Richtung(171) = 1
   Richtung(179) = 1
   Richtung(49) = 1
   Richtung(25) = 1
   Richtung(157) = 1
   Richtung(213) = 1
   Richtung(84) = 1
   Richtung(76) = 1
   Richtung(206) = 1
   Richtung(230) = 1
   Richtung(98) = 1
End If




'AD-Wandler einstellen
Config Adc = Single , Prescaler = Auto , Reference = Off
Start Adc

'<Test Hallsensoren>
   Fehler = 1
   Fan = 1
   While Fehler = 1
   Fehler = 0
   Motorposition = 0
   Motorposition.0 = Hall_a
   Motorposition.1 = Hall_b
   Motorposition.2 = Hall_c
   Motorposition.3 = Hall_d
   If Motorposition = 0 Then Fehler = 1                     'Stecker nicht gesteckt
   If Motorposition > 0 Then
      If A(motorposition) = 0 Then Fehler = 1               'Das währe eine Motorposition die es nicht gibt!
   End If
   If Fehler = 1 Then
      Toggle Led1
      Toggle Led2
      Waitms 500
   End If
   Wend
'</Test Hallsensoren>

On Timer2 Update_pwm Nosave

'Pin-Change Interrupt einstellen:
Pcmsk2 = &B10010100
Pcmsk1 = &B00000000
Pcmsk0 = &B00000001
Pcicr = &B00000101

On Pcint2 Kommutieren Nosave
On Pcint0 Kommutieren Nosave

Readeeprom Uoffset , 1
Readeeprom Ufaktor , 5
Readeeprom Ioffset , 9
Readeeprom Ifaktor , 13
'Angepasste Sinuskurve aus dem EEprom laden
For N = 1 To 12
Bdummy = N + 16
Readeeprom Sinus(n) , Bdummy
Next
'Voreilwinkel:
Readeeprom Vwinkel , 29
Readeeprom Rwinkel , 30

If Ufaktor = 0 Then Ufaktor = 1
If Ifaktor = 0 Then Ifaktor = 1


On Urxc Datenempfang Nosave
Enable Urxc
Enable Interrupts



Fancount = 15000                                            'Lüfter erstmal einschalten

Do
Led1 = 0

'<Messen>
   Uw = Getadc(0)
   Uwsum = Uwsum + Uw
   Iw = Getadc(1)
   Iwsum = Iwsum + Iw
   Incr Wcount
   Temp = Getadc(2)
'</Messen>


'<Serielle Telegramme abarbeiten>
If Ep0 = 13 Then
   If Ep5 = 13 Then
      If Ep1 = Motoradresse Then                            '"R", "L"
         Disable Urxc
         $asm
         lds R24,{EP1}
         LDs R25,{EP2}
         add R24,R25
         LDs R25,{EP3}
         add R24,R25
         STS {Bdummy},R24
         $end Asm
            If Bdummy = Ep4 Then                            'Die Prüfsumme stimmt
            Led1 = 1
            Pcicr = &B00000000     'Kommutieren kurz deaktivieren, weil sonst die Ticks während dem Zugriff geändert werden könnten
            Ausgabeticks = Ticks
            Ticks = 0
            Pcicr = &B00000101

            $asm
               lds R24,{M1}
               LDs R25,{M2}
               add R24,R25
               LDs R25,{M3}
               add R24,R25
               LDs R25,{M4}
               add R24,R25
               LDs R25,{M5}
               add R24,R25
               LDs R25,{M6}
               add R24,R25
               STS {Bdummy},R24
               'Antwort ausgeben
               'Print Chr(m1) ; Chr(m2) ; Chr(m3) ; Chr(m4) ; Chr(m5) ; Chr(m6) ; Chr(bdummy) ; Chr(13) ;
               LdS     R24,{M1}
               STS     udr,R24
               Warten1:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten1
               LDS     R24,{M2}
               STS     udr,R24
               Warten2:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten2
               lds     R24,{M3}
               STS     udr,R24
               Warten3:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten3
               LDS     R24,{M4}
               STS     udr,R24
               Warten4:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten4
               LDS     R24,{M5}
               STS     udr,R24
               Warten5:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten5
               LDS     R24,{M6}
               STS     udr,R24
               Warten6:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten6
               LDS     R24,{bdummy}
               STS     udr,R24
               Warten7:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten7
               LDi     R24,13
               STS     udr,R24
               Warten8:
               LDS     R24,UCSR0A
               BST     R24,5
               Brtc warten8
            $end Asm
            If Ep2 < 3 Then
               Notaus = 0
               Loopcount = 0
               Sollrichtung = Ep2
               Sollspeed = Ep3
               If Sollrichtung = 2 Then
                  Sollrichtung = 1
                  Notaus = 1
                  Loopcount = 250
                  Sollspeed = 0
               End If
               Setspeed
               '<Messwerte berechnen>
               'Messwerte werden hier berechnet, weil genau jetzt 1/100s Zeit ist.
               'Spannung berechnen
               Sdummy = Uwsum / Wcount
               Sdummy = Sdummy - Uoffset
               Sdummy = Sdummy * Ufaktor
               Uzu = Int(sdummy)
               Uwsum = 0
               'Strom berechnen
               Sdummy = Iwsum / Wcount
               Sdummy = Sdummy - Ioffset
               Sdummy = Sdummy * Ifaktor
               Izu = Int(sdummy)
               Iwsum = 0
               Wcount = 0
               '</Messwerte berechnen>
            Else
               Select Case Ep2
                  Case 3                                    'Sinus(1) empfangen
                     Sinus(1) = Ep3
                     Writeeeprom Sinus(1) , 17
                  Case 4                                    'Sinus(2) empfangen
                     Sinus(2) = Ep3
                     Writeeeprom Sinus(2) , 18
                  Case 5                                    'Sinus(3) empfangen
                     Sinus(3) = Ep3
                     Writeeeprom Sinus(3) , 19
                  Case 6                                    'Sinus(4) empfangen
                     Sinus(4) = Ep3
                     Writeeeprom Sinus(4) , 20
                  Case 7                                    'Sinus(5) empfangen
                     Sinus(5) = Ep3
                     Writeeeprom Sinus(5) , 21
                  Case 8                                    'Sinus(6) empfangen
                     Sinus(6) = Ep3
                     Writeeeprom Sinus(6) , 22
                  Case 9                                    'Sinus(7) empfangen
                     Sinus(7) = Ep3
                     Writeeeprom Sinus(7) , 23
                  Case 10                                   'Sinus(8) empfangen
                     Sinus(8) = Ep3
                     Writeeeprom Sinus(8) , 24
                  Case 11                                   'Sinus(9) empfangen
                     Sinus(9) = Ep3
                     Writeeeprom Sinus(9) , 25
                  Case 12                                   'Sinus(10) empfangen
                     Sinus(10) = Ep3
                     Writeeeprom Sinus(10) , 26
                  Case 13                                   'Sinus(11) empfangen
                     Sinus(11) = Ep3
                     Writeeeprom Sinus(11) , 27
                  Case 14                                   'Sinus(12) empfangen
                     Sinus(12) = Ep3
                     Writeeeprom Sinus(12) , 28
                  Case 15                                   'Spannung Kalibrieren (Byte 1)
                     Kal(1) = Ep3
                  Case 16                                   'Spannung Kalibrieren (Byte 2)
                     Kal(2) = Ep3
                     Sdummy = Uwsum / Wcount
                     Ufaktor = Kalibrierwert / Sdummy
                     Writeeeprom Ufaktor , 5
                     Uoffset = 0                            'Schaltungsbedingt
                     Writeeeprom Uoffset , 1
                  Case 17                                   'Strom Kalibrieren (Byte 1)
                     Kal(1) = Ep3
                  Case 18                                   'Strom Kalibrieren Byte (2)
                     Kal(2) = Ep3
                     Sdummy = Iwsum / Wcount
                     If Kalibrierwert = 0 Then              'Das ist der Offset
                        Ioffset = Sdummy + 1
                        Writeeeprom Ioffset , 9
                     Else                                   'Jetzt den Faktor berechnen
                        Sdummy = Sdummy - Ioffset
                        Ifaktor = Kalibrierwert / Sdummy
                        Writeeeprom Ifaktor , 13
                     End If
                  Case 19
                     Vwinkel = Ep3
                     Writeeeprom Vwinkel , 29
                  Case 20
                     Rwinkel = Ep3
                     Writeeeprom Rwinkel , 30

               End Select
            End If

            $asm                                            'Empfangspuffer löschen
               LDI     R24,0
               sTS     {EP1},R24
               sTS     {EP2},R24
               sTS     {EP3},R24
               sTS     {EP4},R24
            $end Asm

            End If
         Enable Urxc

      End If
   End If
End If
'</Serielle Telegramme abarbeiten>



'<Lüfter schalten>
   If Temp > 740 Then Fancount = 15000                      'Schaltet den Lüfter ein
   If Fancount > 0 Then
      Fan = 1
   Else
      Fan = 0
   End If
   If Fancount > 0 Then Decr Fancount
'</Lüfter schalten>


'Notaus mit Freilauf wenn keine Nachricht von Seriell
If Loopcount < 50 Then                                      'Das sind schätzungsweise 0.1s
   Led2 = 1
   Notaus = 0
   Incr Loopcount
Else
   Led2 = 0
   Notaus = 1
   Sollspeed = 0
   Setspeed
End If
Loop


'********************************************************************************************
Kommutieren:
$asm
      PUSH    R10
      PUSH    R11
      PUSH    R16
      PUSH    R17
      PUSH    R20
      PUSH    R21
      PUSH    R24
      PUSH    R26
      PUSH    R27
      IN      R24,sreg
      PUSH    R24
      clr R16
      lds R17, $0023
      BST R17,0
      BLD R16,3
      LDS R17, $0029
      BST R17,2
      bld R16,2
      BST R17,4
      bld R16,1
      BST R17,7
      bld R16,0
      lds R17,{aposition}
      !swap R17
      andi R17,&B11110000
      add R17,R16
      STS {Aposition},R17
      CLR     R17
      LDI     R26,$0B
      LDI     R27,$01
      ADD     R26,R16
      ADC     R27,R17
      LD      R24,X
      LDS     R16,{Sollrichtung}
      CPI     R16,0
      BREQ    Sollrichtungnull
      Jmp     Sollrichtungnichtnull
      Sollrichtungnull:
      LDS     R20,{Vwinkel}
      Add     R24,R20
      JMP     Richtunggesetzt
      Sollrichtungnichtnull:
      LDS     R20,{Rwinkel}
      Add     R24,R20
      Richtunggesetzt:
      CPI     R24,$0D
      BRCC    Istgroesser13_1
      JMP     Istkleiner13_1
      Istgroesser13_1:
      SUBI    R24,$0C
      Istkleiner13_1:
      CLR     R20
      Ldi     R26,$1a
      LDI     R27,$01
      ADD     R26,R24
      ADC     R27,R20
      LD      R16,X
      STS     {Ta},R16
      subi     R24,$fc
      CPI     R24,$0D
      BRCC    Istgroesser13_2
      JMP     Istkleiner13_2
      Istgroesser13_2:
      SUBI    R24,$0C
      Istkleiner13_2:
      CLR     R20
      Ldi     R26,$1a
      LDI     R27,$01
      ADD     R26,R24
      ADC     R27,R20
      LD      R16,X
      STS     {Tc},R16
      subi     R24,$fc
      CPI     R24,$0D
      BRCC    Istgroesser13_3
      JMP     Istkleiner13_3
      Istgroesser13_3:
      SUBI    R24,$0C
      Istkleiner13_3:
      CLR     R20
      Ldi     R26,$1a
      LDI     R27,$01
      ADD     R26,R24
      ADC     R27,R20
      LD      R16,X
      STS     {Tb},R16
      lds     R10,{Aposition}
      CLR     R11
      LDI     R26,$32
      LDI     R27,$01
      ADD     R26,R10
      ADC     R27,R11
      LD      R16,X+
      CLR     R17
      LDI     R20,$02
      LDI     R21,$00
      !Sub R16 , R20
      SBC     R17,R21
      LDI     R26,$33
      LDI     R27,$02
      ST      X+,R16
      ST      X,R17
      LDI     R26,$41
      LDI     R27,$02
      LD      R16,X+
      LD      R17,X
      LDI     R26,$33
      LDI     R27,$02
      LD      R20,X+
      LD      R21,X
      ADD     R16,R20
      ADC     R17,R21
      LDI     R26,$41
      LDI     R27,$02
      ST      X+,R16
      ST      X,R17
      LDS     R24,timsk2
      ORI     R24,$01
      STS     timsk2,R24
      POP     R24
      !Out sreg , R24
      POP     R27
      POP     R26
      POP     R24
      POP     R21
      POP     R20
      POP     R17
      POP     R16
      POP     R11
      POP     R10
$end Asm

Return
'********************************************************************************************





'********************************************************************************************
Update_pwm:
$asm
      PUSH    R24
      IN      R24,sreg
      PUSH    R24
      lds R24,{Ta}
      STS     ocr2a,R24
      STS     ocr2b,R24
      LDS R24,{Tb}
      STS     ocr1al,R24
      STS     ocr1bl,R24
      lds R24,{Tc}
      !OUT    ocr0a,R24
      !OUT    ocr0b,R24
      LDS     R24,timsk2
      ANDI    R24,$FE
      STS     timsk2,R24
      POP     R24
      !OUT    sreg,R24
      POP     R24
$end Asm
Return
'********************************************************************************************


'********************************************************************************************
Datenempfang:
$asm
PUSH R24;
IN R24, SREG;
PUSH R24;
LDS R24,{EP1}
STS {EP0},R24
LDS R24,{EP2}
STS {EP1},R24
LDS R24,{EP3}
STS {EP2},R24
LDS R24,{EP4}
STS {EP3},R24
LDS R24,{EP5}
STS {EP4},R24
IN  R24,UDR
STs {EP5},R24
POP R24;
Out Sreg , R24;
POP R24;
$end Asm
Return
'********************************************************************************************

'********************************************************************************************
Sub Setspeed



If Notaus = 1 Then
   Pwm_stop
   Sollspeed = 0
   For N = 1 To 12
      S(n) = 0
   Next
Else
   For N = 1 To 12
      Idummy1 = Sollspeed * Sinus(n)
      Idummy1 = Idummy1 / 100
      S(n) = Idummy1
   Next
   If Tccr0a = 0 Then                                       'Wenn die PWM aus ist
      Pwm_start
   End If
   '<Zwangskommutieren>
   Zwangskommutieren = 0
   If Sollrichtung <> Altsollrichtung Then Zwangskommutieren = 1
   If Sollspeed <> Altsollspeed Then Zwangskommutieren = 1

   If Zwangskommutieren = 1 Then
      Pcicr = &B00000000                                    'Den Eigentlichen Kommutierinterrupt abschalten
         $asm
         clr R16
         lds R17, $0023
         BST R17,0
         BLD R16,3
         LDS R17, $0029
         BST R17,2
         bld R16,2
         BST R17,4
         bld R16,1
         BST R17,7
         bld R16,0
         lds R17,{aposition}
         !swap R17
         andi R17,&B11110000
         add R17,R16
         STS {Aposition},R17
         CLR     R17
         LDI     R26,$0B
         LDI     R27,$01
         ADD     R26,R16
         ADC     R27,R17
         LD      R24,X
         LDS     R16,{Sollrichtung}
         CPI     R16,0
         BREQ    Sollrichtungnullz
         Jmp     Sollrichtungnichtnullz
         Sollrichtungnullz:
         LDS     R20,{Vwinkel}
         Add     R24,R20
         JMP     Richtunggesetztz
         Sollrichtungnichtnullz:
         LDS     R20,{Rwinkel}
         Add     R24,R20
         Richtunggesetztz:
         CPI     R24,$0D
         BRCC    Istgroesser13_1z
         JMP     Istkleiner13_1z
         Istgroesser13_1z:
         SUBI    R24,$0C
         Istkleiner13_1z:
         CLR     R20
         Ldi     R26,$1a
         LDI     R27,$01
         ADD     R26,R24
         ADC     R27,R20
         LD      R16,X
         STS     {Ta},R16
         subi     R24,$fc
         CPI     R24,$0D
         BRCC    Istgroesser13_2z
         JMP     Istkleiner13_2z
         Istgroesser13_2z:
         SUBI    R24,$0C
         Istkleiner13_2z:
         CLR     R20
         Ldi     R26,$1a
         LDI     R27,$01
         ADD     R26,R24
         ADC     R27,R20
         LD      R16,X
         STS     {Tc},R16
         subi     R24,$fc
         CPI     R24,$0D
         BRCC    Istgroesser13_3z
         JMP     Istkleiner13_3z
         Istgroesser13_3z:
         SUBI    R24,$0C
         Istkleiner13_3z:
         CLR     R20
         Ldi     R26,$1a
         LDI     R27,$01
         ADD     R26,R24
         ADC     R27,R20
         LD      R16,X
         STS     {Tb},R16
         $end Asm
      Pcicr = &B00000101                                    'Den Eigentlichen Kommutierinterrupt einschalten
      'Nur Overflow Interrupt (Seite 105)
      Timsk2.toie2 = 1

      Toggle Led2

   End If

   '</Zwangskommutieren>
End If
Altsollrichtung = Sollrichtung
Altsollspeed = Sollspeed
End Sub
'********************************************************************************************

'********************************************************************************************
Sub Pwm_start
Disable Interrupts                                          'Damit die Timer auch wirklich synchron laufen
   Tccr0a = 161
   Tccr0b = 1
   Ocr0a = 0
   Ocr0b = 0
   Tccr1a = 161
   Tccr1b = 1
   Ocr1ah = 0
   Ocr1al = 0
   Ocr1bh = 0
   Ocr1bl = 0
   Tccr2a = 161
   Tccr2b = 1
   Ocr2a = 0
   Ocr2b = 0
   Tcnt0 = 0                                                'Timer Startwert, wegen Synchron
   Tcnt1 = 6                                                'Timer Startwert, wegen Synchron
   Tcnt2 = 9                                                'Timer Startwert, wegen Synchron
Enable Interrupts
End Sub
'********************************************************************************************


'********************************************************************************************
Sub Pwm_stop
   Tccr0a = 0
   Tccr0b = 0
   Ocr0a = 0
   Ocr0b = 0
   Tccr1a = 0
   Tccr1b = 0
   Ocr1ah = 0
   Ocr1al = 0
   Ocr1bh = 0
   Ocr1bl = 0
   Tccr2a = 0
   Tccr2b = 0
   Ocr2a = 0
   Ocr2b = 0
   'alles aus
   Portd.3 = 0                                              'A+
   Portb.3 = 1                                              'A-
   Portb.2 = 0                                              'B+
   Portb.1 = 1                                              'B-
   Portd.5 = 0                                              'C+
   Portd.6 = 1                                              'C-
End Sub
'********************************************************************************************

Hauptsteuerung

Die Hauptsteuerung erledigt die gesamte Lageregelung. Sie steuert die Motoren und gibt die entsprechenden Meldungen an den Tacho/Datenlogger aus. Sie stellt auch die 5V für den Tacho bereit. Darum die Möglichkeit einen größeren Spannungsregler zu bestücken. Die Lageregelung speichert auch die gefahrenen Km, einigen Min-Max Werte und diverse Werte bei einem Notaus. Diese Werte werden im EEprom als Text abgelegt, also direkt lesbar. Beim Einschalten ist die Gewschwindigkeit auf 6km/h begrenzt. Diese Begrenzung kann jedoch aufgehoben werden. Beachten Sie, daß Sie das Gerät dann nur noch auf Privatgelände mit Zustimmung des Eigentümers benutzen dürfen. Eine Bertriebserlaubnis für das Gerät existiert nicht.

Schaltbild

Sensoradapter

Zur Hauptsteuerung gehört auch der Sensoradapter. (Schaltbild siehe Hauptsteuerung) Dieser stellt die 3,3V für die Sensoren bereit. Im Unterschied zu ähnlichen Projekten besitzt das Rutscherle2 kein Potentiometer für die Lenkung. Zum Lenken werden die gesamten Sensoren gekippt. Darum auch dieser Sensoradapter. Die Sensoren werden mit dem Adapter direkt mit der Achse der Lenkung verbunden. Die ideale Position ist dabei natürlich genau im Schnittpunkt von Lenk- und Motorachse.

Wie im Bild zu sehen ist, sitzen die Sensoren in Verlängerung der Lenkachse zwischen den Beiden Lüftern für die Motorregler. Die Ideale Position ist der Schnittpunkt von Radachse und Lenkachse. Dicht dran ist ausreichend.

Sensoren

Die verwendeten Sensoren stammen von Sparkfun. Verwendet wurden diese:
ArduIMU Sensor Board - Six Degrees of Freedom (Main)
mit zwei mal Tochterboard:
ArduIMU Sensor Board - Six Degrees of Freedom (Daughter)
Die Sensoren sind mittlerweile leider nicht mehr erhältlich. Bei einem Nachbau müssen darum andere gewählt,und die Software entsprechend angepasst werden.

Software Version 67

$prog &HFF , &HFF , &HD1 , &HFF                                                 'Fusebits für Atmega644
$regfile = "m644def.dat"
$framesize = 64
$swstack = 64
$hwstack = 64
$crystal = 20000000
$baud = 57600
Declare Sub Onbutton1
Declare Sub Logtext(byval Adresse As Word , Text As String )
Declare Sub Akkugrenzen
Declare Sub Unfalldatenschreiber
Declare Sub Betriebsdatenschreiber
Config Portb.0 = Output
Config Portb.1 = Output
Config Portb.2 = Output
Config Portb.3 = Output
Config Portb.4 = Output
Config Portb.6 = Input                                                          'Fusstaster
Config Portc.0 = Input
Config Portc.1 = Input
Config Portc.2 = Input
Config Portc.3 = Input
Config Portc.4 = Input
Config Portc.5 = Input
Config Portc.6 = Input
Config Portc.7 = Input
Config Portd.7 = Output
Config Portd.6 = Output
Config Portd.5 = Output
Config Portd.4 = Output
Config Portd.3 = Output
Config Portd.2 = Input
Led1 Alias Portb.4
Led2 Alias Portb.3
Sensor_aus Alias Portb.2
Sensor_test Alias Portb.1                                                       'Self-Test. 0=Normalbetrieb
Led3 Alias Portd.4
Led4 Alias Portd.5
Led5 Alias Portd.6
Led6 Alias Portd.3
Led7 Alias Portd.7
Button1 Alias Pind.2
Fusstaster Alias Pinb.6
'******************************************************************************************************************************
'******************************************************************************************************************************
'Konstanten für die Geschwindigkeits und Streckenmessung
Const Kom_pro_u = 276                                                           '276*Kommutieren für eine Umdrehung.
Const Radumpfang = 139                                                          'RaduMPFang in Zentimeter
'Maximalwert bis zu dem die Abweichung in y aufaddiert wird
Const Max_mit_acc_nick_s = 1024
Const Min_mit_acc_nick_s = -1024
Const Max_lenk = 128
Const Min_lenk = -128
'Konstanten für die Leistungsbegrenzung, Geschwindigkeitsbegrenzung
'Maximalleistung
Const Maxpwm = 255
Const Minpwm = -255
'PWM ab der automatisch gebremst wird um blos nie an die Grenze zu kommen
Const Spwm = 128
Const Spwmr = -128
'Offsetwerte für die Mittellage
Const Offset_roll = 67700
Const Offset_nick = 65400
Const O_roll_u = 66200
Const O_roll_o = 69200
Const O_nick_u = 63900
Const O_nick_o = 66900
Const O_nick_oo = 67700
Const Vmax = 60                                                                 'Vmax Vorwärts in km/h*10
Const Vmaxr = -60                                                               'Vmax Rückwärts in km/h*10
'Konstanten für die Akkus
Const Akkuleer = 3600                                                           '36V, 12V pro Akku -> Akkus leer
Const Akkufastleer = 3750
'Konstanten für die Signalaufbereitoung
Const Gyro_anz = 10000
Const Acc_anz = 50
Const Gyro_faktor = 91
'******************************************************************************************************************************
'******************************************************************************************************************************
'Variablen für die Rückmeldungen von den Motorreglern
Dim U As Integer At $100
Dim I As Integer At $102
Dim K As Integer At $104
Dim Checksumme As Byte At $106
Dim Ende As Byte At $107
Dim Motormeldung(8) As Byte At $100 Overlay
Dim M1 As Byte At $100 Overlay
Dim M2 As Byte At $101 Overlay
Dim M3 As Byte At $102 Overlay
Dim M4 As Byte At $103 Overlay
Dim M5 As Byte At $104 Overlay
Dim M6 As Byte At $105 Overlay
Dim M7 As Byte At $106 Overlay
Dim M8 As Byte At $107 Overlay
'Variablen für die Telegramme an die Motorregler
Dim Checksum_rechts As Byte At $108
Dim Checksum_links As Byte At $109
Dim Richtung_rechts As Byte At $10a
Dim Richtung_links As Byte At $10b
Dim Pwm_rechts As Byte At $10c
Dim Pwm_links As Byte At $10d
Dim Zeiger_meldung As Byte At $10e
Dim Ta As Byte At $10f
Dim U_links As Integer At $111
Dim U_rechts As Integer At $113
Dim I_links As Integer At $115
Dim I_rechts As Integer At $117
Dim K_links As Integer At $119
Dim K_rechts As Integer At $11b
Dim B(5) As Byte At $11d
Dim Kilometer As Word At $122
Dim Kilometermeldung(2) As Byte At $122 Overlay
Dim Akkustand As Integer At $124
Dim Akkumeldung(2) As Byte At $124 Overlay
Dim Kmh As Integer At $126
Dim Kmhmeldung(2) As Byte At $126 Overlay
Dim Meter As Integer At $128
Dim Metermeldung(2) As Byte At $128 Overlay
Dim Di As Single At $12a                                                        'Distanz pro Kommutieren
Dim Kmhfaktor As Single At $12e
Dim Zentimeter As Single At $132
Dim Strecke As Single At $136                                                   'Gefahrene Strecke während 1/100s
Dim Kx As Single At $13a                                                        'Anzahl Kommutierungen für die Streckenberechnung
Dim Kv As Single At $13e                                                        'Anzahl Kommutierungen für die Geschwindigkeitosberechnung
Dim Vbrems As Long At $142                                                      'Bremse bei Überlast oder zu hoher Geschwindigkeit
Dim Lbrems As Long At $146
Dim Brems As Long At $14a
Dim P_faktor As Single At $14e
Dim Pf1 As Byte At $14e Overlay
Dim Pf2 As Byte At $14f Overlay
Dim Pf3 As Byte At $150 Overlay
Dim Pf4 As Byte At $151 Overlay
Dim I_faktor As Single At $152
Dim If1 As Byte At $152 Overlay
Dim If2 As Byte At $153 Overlay
Dim If3 As Byte At $154 Overlay
Dim If4 As Byte At $155 Overlay
Dim D_faktor As Single At $156
Dim Df1 As Byte At $156 Overlay
Dim Df2 As Byte At $157 Overlay
Dim Df3 As Byte At $158 Overlay
Dim Df4 As Byte At $159 Overlay
Dim Gyro_nick As Word At $015a
Dim Gyro_roll As Word At $015c
Dim Gyro_roll_alt As Word At $015e
Dim Gyro_nick_alt As Word At $0160
Dim Acc_nick As Long At $0162
Dim Acc_roll As Long At $0166
Dim Sum_acc_roll As Long At $016a
Dim Sum_acc_nick As Long At $016e
Dim Sum_gyro_roll As Long At $0172
Dim Sum_gyro_nick As Long At $0176
Dim Korr_acc_roll As Long At $017a
Dim Korr_acc_nick As Long At $017e
Dim Maccroll As Long At $0182
Dim Maccroll1 As Byte At $0182 Overlay
Dim Maccroll2 As Byte At $0183 Overlay
Dim Maccroll3 As Byte At $0184 Overlay
Dim Maccroll4 As Byte At $0185 Overlay
Dim Maccnick As Long At $0186
Dim Maccnick1 As Byte At $0186 Overlay
Dim Maccnick2 As Byte At $0187 Overlay
Dim Maccnick3 As Byte At $0188 Overlay
Dim Maccnick4 As Byte At $0189 Overlay
Dim Mit_acc_nick_s As Long At $018a
Dim Mit_gyro_roll As Long At $018e
Dim Mit_gyro_nick As Long At $0192
Dim Sig_gyro_roll As Long At $0196
Dim Sig_gyro_nick As Long At $019a

Dim Nullroll As Long At $019e
Dim Nullroll1 As Byte At $019e Overlay
Dim Nullroll2 As Byte At $019f Overlay
Dim Nullroll3 As Byte At $01a0 Overlay
Dim Nullroll4 As Byte At $01a1 Overlay
Dim Nullnick As Long At $01a2
Dim Nullnick1 As Byte At $01a2 Overlay
Dim Nullnick2 As Byte At $01a3 Overlay
Dim Nullnick3 As Byte At $01a4 Overlay
Dim Nullnick4 As Byte At $01a5 Overlay
Dim Stellwert As Single At $01a6

Dim Mrg As Single At $01aa
Dim Mrg1 As Byte At $01aa Overlay
Dim Mrg2 As Byte At $01ab Overlay
Dim Mrg3 As Byte At $01ac Overlay
Dim Mrg4 As Byte At $01ad Overlay
Dim Mra As Single At $01ae
Dim Mra1 As Byte At $01ae Overlay
Dim Mra2 As Byte At $01af Overlay
Dim Mra3 As Byte At $01b0 Overlay
Dim Mra4 As Byte At $01b1 Overlay

Dim Kksum As Single At $01b2
Dim Kksum1 As Byte At $01b2 Overlay
Dim Kksum2 As Byte At $01b3 Overlay
Dim Kksum3 As Byte At $01b4 Overlay
Dim Kksum4 As Byte At $01b5 Overlay
Dim Sw As Single At $01b6
Dim Sw1 As Byte At $01b6 Overlay
Dim Sw2 As Byte At $01b7 Overlay
Dim Sw3 As Byte At $01b8 Overlay
Dim Sw4 As Byte At $01b9 Overlay
Dim Gyronick As Long At $01ba
Dim Gyronick1 As Byte At $01ba Overlay
Dim Gyronick2 As Byte At $01bb Overlay
Dim Gyronick3 As Byte At $01bc Overlay
Dim Gyronick4 As Byte At $01bd Overlay

Dim Mp_faktor As Single At $01be
Dim Mpf1 As Byte At $01be Overlay
Dim Mpf2 As Byte At $01bf Overlay
Dim Mpf3 As Byte At $01c0 Overlay
Dim Mpf4 As Byte At $01c1 Overlay
Dim Mv_faktor As Single At $01c2
Dim Mvf1 As Byte At $01c2 Overlay
Dim Mvf2 As Byte At $01c3 Overlay
Dim Mvf3 As Byte At $01c4 Overlay
Dim Mvf4 As Byte At $01c5 Overlay
Dim Mpl_faktor As Single At $01c6
Dim Mplf1 As Byte At $01c6 Overlay
Dim Mplf2 As Byte At $01c7 Overlay
Dim Mplf3 As Byte At $01c8 Overlay
Dim Mplf4 As Byte At $01c9 Overlay
Dim Mvl_faktor As Single At $01ca
Dim Mvlf1 As Byte At $01ca Overlay
Dim Mvlf2 As Byte At $01cb Overlay
Dim Mvlf3 As Byte At $01cc Overlay
Dim Mvlf4 As Byte At $01cd Overlay

Dim P_tacho As Single At $01ce
Dim I_tacho As Single At $01d2
Dim D_tacho As Single At $01d6
Dim Mp_tacho As Single At $01da
Dim Mv_tacho As Single At $01de
Dim Mpl_tacho As Single At $01e2
Dim Mvl_tacho As Single At $01e6

Dim Tdummy As Integer At $01ea
Dim Tachomeldung(30) As Byte At $01ce Overlay
Dim T1 As Byte At $01ce Overlay
Dim T2 As Byte At $01cf Overlay
Dim T3 As Byte At $01d0 Overlay
Dim T4 As Byte At $01d1 Overlay

Dim T5 As Byte At $01d2 Overlay
Dim T6 As Byte At $01d3 Overlay
Dim T7 As Byte At $01d4 Overlay
Dim T8 As Byte At $01d5 Overlay

Dim T9 As Byte At $01d6 Overlay
Dim T10 As Byte At $01d7 Overlay
Dim T11 As Byte At $01d8 Overlay
Dim T12 As Byte At $01d9 Overlay

Dim T13 As Byte At $01da Overlay
Dim T14 As Byte At $01db Overlay
Dim T15 As Byte At $01dc Overlay
Dim T16 As Byte At $01dd Overlay

Dim T17 As Byte At $01de Overlay
Dim T18 As Byte At $01df Overlay
Dim T19 As Byte At $01e0 Overlay
Dim T20 As Byte At $01e1 Overlay

Dim T21 As Byte At $01e2 Overlay
Dim T22 As Byte At $01e3 Overlay
Dim T23 As Byte At $01e4 Overlay
Dim T24 As Byte At $01e5 Overlay

Dim T25 As Byte At $01e6 Overlay
Dim T26 As Byte At $01e7 Overlay
Dim T27 As Byte At $01e8 Overlay
Dim T28 As Byte At $01e9 Overlay

Dim T29 As Byte At $01ea Overlay
Dim T30 As Byte At $01eb Overlay


Dim Gyro_gier As Word
Dim Gyro_gier_alt As Word
Dim Sum_gyro_gier As Long
Dim Mit_gyro_gier As Long
Dim Sig_gyro_gier As Long

Dim Werteneu As Byte
Dim Mit_acc_roll As Long
Dim Mit_acc_nick As Long
Dim Null_roll As Long
Dim Null_nick As Long
Dim Mit_regelzeit_ges As Single

Dim Lagefehler As Long
Dim Regelzeit As Word                                                           'Die Zeit in 1/100s die gebraucht wird bis die Nullage erreicht ist
Dim Regelzeitmax As Word
Dim Mit_acc_roll_max As Long
Dim Mit_acc_nick_max As Long
Dim Mit_acc_roll_min As Long
Dim Mit_acc_nick_min As Long


Dim Pwm_gesamt As Single
Dim Pwm_rechts_max As Byte
Dim Pwm_links_max As Byte
Dim Intcount As Byte
Dim K_diff As Single
Dim K_sum As Single
Dim K_diff_min As Single
Dim K_diff_max As Single
Dim Pwm_lenk_rechts As Integer
Dim Pwm_lenk_links As Integer

Dim Lenkvorgabe As Single
Dim Mp_anpassung As Single
Dim Mv_anpassung As Single
Dim Mpl_anpassung As Single
Dim Mvl_anpassung As Single
Dim K_sum_anpassung As Single


Dim Wdummy1 As Word
Dim Ldummyi As Long
Dim Ldummy1 As Long
Dim Ldummy2 As Long
Dim Bdummy1 As Byte
Dim Idummy1 As Integer
Dim Sdummy1 As Single


Dim Antriebaus As Byte
Dim Notaus As Byte
Dim Sensor_ok As Byte                                                           'Zeigt an ob die Mittelwerte der Gyros stabil stehen.
Dim Sensor_ok_count As Byte
Dim Ltest As Byte
Dim Ugemessen As Long                                                           'Batteriespannung in V*100
Dim Igemessen As Long                                                           'Gesamter Motorstrom in mA

Dim Imin As Long                                                                'Kleinster gemessener Motorstrom (größte Rückspeisung)
Dim Imax As Long                                                                'größter gemessener Motorstrom
Dim Summe_as_entladen As Long
Dim Summe_mah_entladen As Long
Dim Summe_as_laden As Long
Dim Summe_mah_laden As Long
Dim Akkukap As Long
Dim Akku1 As Long
Dim Akku2 As Long
Dim Akku3 As Long
Dim Akku4 As Long
Dim Akku5 As Long
Dim Erzeugt As Long
Dim Verbraucht As Long
'Single Variablen um die Prozente rechnen zu können
Dim Akkuinhalt As Single
Dim Akkuverbrauch As Single
Dim Akkuprozent As Single

Dim Adresse As Word
Dim Bzeit As Long
Dim Bstate As Byte
Dim Frei As Byte
Dim Timeout As Byte
Dim S_err As Byte
Dim Seriell_err_l As Word
Dim Seriell_err_r As Word
Dim Tamax As Byte
Dim Fusscount As Word
Dim Blinkcount As Byte
Dim Lenkerstellung As Single
Dim Lenkstellwert As Single
Dim Lenkfehler As Single
Dim N As Integer
Dim Utext As String * 32                                                        'Texte für den Unfalldatenschreiber (als Text im eeprom)
Dim Ugrund As String * 32
Dim Text As String * 16
Dim Min_v As Integer
Dim Max_v As Integer


Dim Tachocount As Byte
Led3 = 1
Led4 = 1
Led5 = 1
Led6 = 1
Led7 = 1
Wait 5                                                                          '5 Sekunden Warten
Antriebaus = 1
'Ein paar feste Werte vorab berechnen:
Di = Radumpfang / Kom_pro_u
Kmhfaktor = Di * 9                                                              'Eigentlich 3.6 aber bei der Mittelwertbildung entsteht der 4-Fache Wert. Darum 9, und es ergibt sich automateisch V in 0.1km/h
'Unbenutzte Pins auf Masse legen
Portb.7 = 0
Portb.5 = 0
'Input-Pins hochziehen
Portc.0 = 1
Portc.1 = 1
Portc.2 = 1
Portc.3 = 1
Portc.4 = 1
Portc.5 = 1
Portc.6 = 1

'Sensorboard schalten:
Sensor_aus = 0
Sensor_test = 0
'Hochziehwiderstände der Knöpfe einschalten
Button1 = 1
Fusstaster = 1

Config Adc = Single , Prescaler = Auto , Reference = Internal_2.56
Start Adc
'Wartezeit bis die Motorregler soweit sind
Readeeprom Max_v , $2f0
Readeeprom Min_v , $2f2
Readeeprom Summe_as_entladen , $300                                             'Entnommene Wh und Ws aus EEPROM lesen
Led3 = 0
Waitms 150
Readeeprom Summe_mah_entladen , $310
Led4 = 0
Waitms 150
Readeeprom Summe_as_laden , $320
Led5 = 0
Waitms 150
Readeeprom Summe_mah_laden , $330
Led6 = 0
Waitms 150
Readeeprom Akkukap , $340
Led7 = 0
Waitms 150
Readeeprom Kilometer , $350
Readeeprom Meter , $360
Readeeprom Zentimeter , $370

'<Faktoren für den PID-Regler einlesen>
      Readeeprom P_faktor , $380
      Readeeprom I_faktor , $384
      Readeeprom D_faktor , $388
      Readeeprom Mp_faktor , $38c
      Readeeprom Mv_faktor , $390
      Readeeprom Mpl_faktor , $394
      Readeeprom Mvl_faktor , $398

      If P_faktor < 0 Or P_faktor > 1 Then P_faktor = 0.0033                    'Halbwegs sinnvolle Werte vorbelegen
      If I_faktor < 0 Or I_faktor > 1 Then I_faktor = 0.00006
      If D_faktor < 0 Or D_faktor > 1 Then D_faktor = 0.08
      If Mp_faktor < 0 Or Mp_faktor > 1 Then Mp_faktor = 0.006

      If Mv_faktor < 0 Or Mv_faktor > 1 Then Mv_faktor = 0
      If Mpl_faktor < 0 Or Mpl_faktor > 1 Then Mpl_faktor = 0
      If Mvl_faktor < 0 Or Mvl_faktor > 1 Then Mvl_faktor = 0

'</Faktoren für den PID-Regler einlesen>
If Kilometer = $ffff Then                                                       'Kommt bei frischem EEPROM vor
Zentimeter = 0
Meter = 0
Kilometer = 0
Min_v = 0
Max_v = 0
End If
'Bei Fehlerhaften Werten aus dem EEPROM einfach 100mAh als Vorgabe
If Akkukap <= 0 Then Akkukap = 100
If Akkukap > 32000 Then Akkukap = 100
Akkugrenzen
Antriebaus = 1                                                                  'Beim Start ist immer alles aus
Notaus = 0
Readeeprom Max_v , $2f0
Readeeprom Min_v , $2f4
Config Watchdog = 128
'<Sicherheitstest 1>
      Start Watchdog
      If Button1 = 0 Then Wait 10
      If Antriebaus = 0 Then Wait 10
      If P_faktor > 1 Then Wait 10
      If I_faktor > 1 Then Wait 10
      If D_faktor > 1 Then Wait 10
      If P_faktor < 0 Then Wait 10
      If I_faktor < 0 Then Wait 10
      If D_faktor < 0 Then Wait 10
      If Fusstaster = 0 Then Wait 10
      Stop Watchdog
'</Sicherheitstest 1>
'Maximalwerte für Datenschreiber/Notauserkennung auf 0 setzen
Regelzeit = 0
Regelzeitmax = 0
Mit_acc_nick_max = 0
Mit_acc_roll_max = 0
Pwm_rechts_max = 0
Pwm_links_max = 0
K_diff_min = 0
K_diff_max = 0
Richtung_links = 2                                                              'Leerlauf
Richtung_rechts = 2                                                             'Leerlauf
Pwm_links = 0
Pwm_rechts = 0
Pwm_gesamt = 0
Mit_acc_nick_s = 0
Lenkerstellung = 0

K_rechts = 0
K_links = 0
Seriell_err_l = 0
Seriell_err_r = 0
Ugrund = "----------------"                                                     'Einfach mal vorbelegen um zu sehen obs auch einen Notaus ohne Grund gibt.
On Timer0 Regel_interrupt
Config Timer0 = Timer , Prescale = 1024
Enable Timer0
Enable Interrupts
Waitms 100
Toggle Led3
Toggle Led4
Toggle Led5
Toggle Led6
Toggle Led7
Waitms 100
Toggle Led3
Toggle Led4
Toggle Led5
Toggle Led6
Toggle Led7
Waitms 100
Toggle Led3
Toggle Led4
Toggle Led5
Toggle Led6
Toggle Led7
Waitms 100
Toggle Led3
Toggle Led4
Toggle Led5
Toggle Led6
Toggle Led7

'<Sicherheitstest 2>
      Start Watchdog
      If Button1 = 0 Then Wait 10
      If Antriebaus = 0 Then Wait 10
      If P_faktor > 1 Then Wait 10
      If I_faktor > 1 Then Wait 10
      If D_faktor > 1 Then Wait 10
      If P_faktor < 0 Then Wait 10
      If I_faktor < 0 Then Wait 10
      If D_faktor < 0 Then Wait 10
      If Ugemessen < 1000 Then Wait 10
      If Ugemessen > 4700 Then Wait 10
      If Fusstaster = 0 Then Wait 10
      Stop Watchdog
'</Sicherheitstest 2>
If Ugemessen < 2400 Then                                                        'Sensoren ausschalten wenn am Ladegerät oder zu Testzwecken
    Sensor_aus = 1
Else
    Sensor_aus = 0
End If


While Ugemessen < 2400                                                          'Unterspannung oder am Ladegerät!
      Led3 = 1
      Led4 = 1
      Led5 = 1
      Led6 = 0
      Led7 = 1
Wend

'Prüfung ob Akku gerade geladen:
If Ugemessen > 3950 Then                                                        'Der Akku wurde gerade geladen
   Disable Interrupts                                                           'Interruts abschalten damit die folgenden Zeilen nicht gerade jetzt vom Interrupt unterbrochen werden.
   Waitms 50
   Summe_as_entladen = 0
   Summe_mah_entladen = 0
   Summe_as_laden = 0
   Summe_mah_laden = 0
   Enable Interrupts
End If
Summen_vorbelegen:
Acc_roll = Getadc(5)                                                            'Roll
Acc_nick = Getadc(7)                                                            'Nick
Gyro_roll = Getadc(2)                                                           'Roll
Gyro_nick = Getadc(4)                                                           'Nick
Gyro_gier = Getadc(3)

Acc_roll = Acc_roll * 100
Acc_nick = Acc_nick * 100

Sum_gyro_roll = Gyro_roll * Gyro_anz
Sum_gyro_nick = Gyro_nick * Gyro_anz
Sum_gyro_gier = Gyro_gier * Gyro_anz

Gyro_roll_alt = Gyro_roll
Gyro_nick_alt = Gyro_nick
Gyro_gier_alt = Gyro_gier

Mit_gyro_roll = Gyro_roll
Mit_gyro_nick = Gyro_nick
Mit_gyro_gier = Gyro_gier

Sum_acc_roll = Acc_roll * Acc_anz
Sum_acc_nick = Acc_nick * Acc_anz

Waitms 100
Toggle Led3
Toggle Led4
Toggle Led5
Toggle Led6
Toggle Led7
Waitms 100
Toggle Led3
Toggle Led4
Toggle Led5
Toggle Led6
Toggle Led7

If Sig_gyro_nick < -3 Then Goto Summen_vorbelegen
If Sig_gyro_nick > 3 Then Goto Summen_vorbelegen
If Sig_gyro_roll < -3 Then Goto Summen_vorbelegen
If Sig_gyro_roll > 3 Then Goto Summen_vorbelegen
If Sig_gyro_gier < -3 Then Goto Summen_vorbelegen
If Sig_gyro_gier > 3 Then Goto Summen_vorbelegen

'<Sicherheitstest 3>
      Start Watchdog
      If Button1 = 0 Then Wait 10
      If Antriebaus = 0 Then Wait 10
      If P_faktor > 1 Then Wait 10
      If I_faktor > 1 Then Wait 10
      If D_faktor > 1 Then Wait 10
      If P_faktor < 0 Then Wait 10
      If I_faktor < 0 Then Wait 10
      If D_faktor < 0 Then Wait 10
      If Ugemessen < 1000 Then Wait 10
      If Ugemessen > 4700 Then Wait 10
      If Fusstaster = 0 Then Wait 10
      Stop Watchdog
'</Sicherheitstest 3>
'********************************************************************************************************************************************************
Do
'<Fusstaster abfragen>
If Fusscount = 0 Then
   If Antriebaus = 0 Then
      Antriebaus = 1
      Waitms 100                                                                'Damit der Regelinterrupt auch was davon mitbekommt
      Disable Interrupts
      Betriebsdatenschreiber
      Enable Interrupts
   End If
End If
'</Fusstaster abfragen>

'<Sicherheitstest Sensoren>
      If Antriebaus = 1 Then

         If Intcount <> Ltest Then
            Ltest = Intcount
            Sensor_ok = 0
            If Sensor_ok_count > 0 Then Decr Sensor_ok_count
            '1. Es darf keine nennenswerte Drehbewegung erkannt werden!

            If Sig_gyro_nick < -3 Then Sensor_ok_count = 50
            If Sig_gyro_nick > 3 Then Sensor_ok_count = 50
            If Sig_gyro_roll < -3 Then Sensor_ok_count = 50
            If Sig_gyro_roll > 3 Then Sensor_ok_count = 50
            If Sig_gyro_gier < -15 Then Sensor_ok_count = 50                    'Nach mehrfachen Drehungen könnte der Mittelwert etwas verschoben sein. Darum hier 12
            If Sig_gyro_gier > 15 Then Sensor_ok_count = 50

            '2. Die Werte dürfen nicht verrauscht sein
            Ldummy2 = Mit_acc_nick + 500
            If Acc_nick > Ldummy2 Then Sensor_ok_count = 50
            Ldummy2 = Mit_acc_nick - 500
            If Acc_nick < Ldummy2 Then Sensor_ok_count = 50
            Ldummy2 = Mit_acc_roll + 500
            If Acc_roll > Ldummy2 Then Sensor_ok_count = 50
            Ldummy2 = Mit_acc_roll - 500
            If Acc_roll < Ldummy2 Then Sensor_ok_count = 50

            '3. Die Werte müssen ungefähr in einer legalen Nullage sein.
            If Null_roll < O_roll_u Then Sensor_ok_count = 50
            If Null_roll > O_roll_o Then Sensor_ok_count = 50
            If Null_nick < O_nick_u Then Sensor_ok_count = 50
            If Null_nick > O_nick_o Then Sensor_ok_count = 50

            If Sensor_ok_count = 0 Then Sensor_ok = 1                           'Bedingungen während 50 Messungen (0.5s) eingehalten

         End If
      Else
         Sensor_ok = 0
      End If

 '</Sicherheitstest Sensoren>

'<Batterieanzeige>
      If Ugemessen > Akkufastleer Then                                          'alles unter 36V Leerlaufspannung ist LEER
               If Verbraucht > Akkukap Then
                  Akkukap = Verbraucht + 1                                      'Einfach mal annehmen daß noch mehr als angezeigt im Akku ist
                  Akkugrenzen
               End If
      End If
      'Mit Blinken wenn Gyros nicht bereit
      If Antriebaus = 1 Then
      If Ugemessen > Akkuleer Then                                              'alles unter 36V Leerlaufspannung ist LEER
               If Verbraucht > Akkukap Then
                  Akkukap = Verbraucht + 20                                     'Einfach mal annehmen daß noch mehr als angezeigt im Akku ist
                  Akkugrenzen
               End If
      End If
         If Sensor_ok = 1 Then
            If Verbraucht < Akku1 Then Led7 = 0 Else Led7 = 1
            If Verbraucht < Akku2 Then Led6 = 0 Else Led6 = 1
            If Verbraucht < Akku3 Then Led5 = 0 Else Led5 = 1
            If Verbraucht < Akku4 Then Led4 = 0 Else Led4 = 1
            If Verbraucht < Akku5 Then Led3 = 0 Else Led3 = 1
         Else
         If Frei = 1 Then                                                       'Schneller Blinken wenn Frei
            Blinkcount = Intcount
            If Blinkcount > 50 Then Blinkcount = Blinkcount - 50
         Else
            Blinkcount = Intcount / 2
         End If

            If Blinkcount > 40 Then
               If Verbraucht < Akku1 Then Led7 = 0 Else Led7 = 1
               If Verbraucht < Akku2 Then Led6 = 0 Else Led6 = 1
               If Verbraucht < Akku3 Then Led5 = 0 Else Led5 = 1
               If Verbraucht < Akku4 Then Led4 = 0 Else Led4 = 1
               If Verbraucht < Akku5 Then Led3 = 0 Else Led3 = 1
            Else
               Led7 = 1
               Led6 = 1
               Led5 = 1
               Led4 = 1
               Led3 = 1
            End If

         End If
      Else
         If Verbraucht < Akku1 Then Led7 = 0 Else Led7 = 1
         If Verbraucht < Akku2 Then Led6 = 0 Else Led6 = 1
         If Verbraucht < Akku3 Then Led5 = 0 Else Led5 = 1
         If Verbraucht < Akku4 Then Led4 = 0 Else Led4 = 1
         If Verbraucht < Akku5 Then Led3 = 0 Else Led3 = 1

      End If
'</Batterieanzeige>
'<Maximalwerte für Betriebsdatenschreiber>
      If Igemessen < Imin Then Imin = Igemessen                                 'Nur zur Information
      If Igemessen > Imax Then Imax = Igemessen                                 'Nur zur Information
      If Regelzeit > Regelzeitmax Then Regelzeitmax = Regelzeit
      If Mit_acc_nick > Mit_acc_nick_max Then Mit_acc_nick_max = Mit_acc_nick
      If Mit_acc_roll > Mit_acc_roll_max Then Mit_acc_roll_max = Mit_acc_roll
      If Mit_acc_nick < Mit_acc_nick_min Then Mit_acc_nick_min = Mit_acc_nick
      If Mit_acc_roll < Mit_acc_roll_min Then Mit_acc_roll_min = Mit_acc_roll
      If Pwm_links > Pwm_links_max Then Pwm_links_max = Pwm_links
      If Pwm_rechts > Pwm_rechts_max Then Pwm_rechts_max = Pwm_rechts
      If K_diff > K_diff_max Then K_diff_max = K_diff
      If K_diff < K_diff_min Then K_diff_min = K_diff
      If Ta > Tamax Then Tamax = Ta
'</Maximalwerte für Betriebsdatenschreiber>

If Intcount > 80 Then Led1 = 1 Else Led1 = 0                                    'Sekundenblinken
                                                            'Daran lässt sich leicht erkennen ob
                                                            'noch alles normal läuft.
Debounce Button1 , 0 , Onbutton1 , Sub


Loop
End
'**************************************************************************************************
'Ab hier kommen die Subroutinen und die Interrupts
'**************************************************************************************************
Sub Akkugrenzen
      'Grenzwerte für die Akku-LEDs berechnen
      Ldummy1 = Akkukap / 5
      Akku1 = Ldummy1
      Akku2 = 2 * Ldummy1
      Akku3 = 3 * Ldummy1
      Akku4 = 4 * Ldummy1
      Akku5 = 5 * Ldummy1
End Sub
'**************************************************************************************************
Sub Unfalldatenschreiber
Waitms 10
   Utext = Str(regelzeit)
   Utext = "Rz=" + Utext
   Logtext 0 , Utext
   Utext = Str(mit_acc_nick_max)
   Utext = "N max=" + Utext
   Logtext $10 , Utext
   Utext = Str(mit_acc_nick)
   Utext = "N=" + Utext
   Logtext $20 , Utext
   Utext = Str(mit_acc_nick_min)
   Utext = "N min=" + Utext
   Logtext $30 , Utext
   Utext = Str(mit_acc_roll_max)
   Utext = "R max=" + Utext
   Logtext $40 , Utext
   Utext = Str(mit_acc_roll)
   Utext = "R=" + Utext
   Logtext $50 , Utext
   Utext = Str(mit_acc_roll_min)
   Utext = "R min=" + Utext
   Logtext $60 , Utext
   Utext = Str(pwm_rechts)
   Utext = "PWM r=" + Utext
   Logtext $70 , Utext
   Utext = Str(pwm_links)
   Utext = "PWM l=" + Utext
   Logtext $80 , Utext
   Utext = Str(u_rechts)
   Utext = "U r=" + Utext
   Logtext $90 , Utext
   Utext = Str(u_links)
   Utext = "U l=" + Utext
   Logtext $a0 , Utext
   Utext = Str(ta)
   Utext = "Ta=" + Utext
   Logtext $b0 , Utext
   Utext = Str(seriell_err_l)
   Utext = Utext + "|"
   Utext = Utext + Str(seriell_err_r)
   Utext = "S =" + Utext
   Logtext $c0 , Utext
   Utext = Ugrund
   Logtext $d0 , Utext
End Sub

'**************************************************************************************************
Sub Betriebsdatenschreiber
'Ob die Wartezeiten beim EEprom schreiben nötig sind ist nicht sicher.
'Mit den Wartezeiten treten aber keine Fehler bei den Werten mehr auf.
'Möglich, daß dies auch durch eine andere Änderung bewirkt wurde.
Writeeeprom Max_v , $2f0
Writeeeprom Min_v , $2f2
Zentimeter = Int(zentimeter)                                                    'Die Millimeter sollen nicht geschrieben werden
Writeeeprom Summe_as_entladen , $300
Writeeeprom Summe_mah_entladen , $310
Writeeeprom Summe_as_laden , $320
Writeeeprom Summe_mah_laden , $330
Writeeeprom Akkukap , $340
Writeeeprom Kilometer , $350
Writeeeprom Meter , $360
Writeeeprom Zentimeter , $370
   If Werteneu = 1 Then                                                         'Nur wenn das Einstellklavier gesteckt ist.
      Writeeeprom P_faktor , $380
      Writeeeprom I_faktor , $384
      Writeeeprom D_faktor , $388
      Writeeeprom Mp_faktor , $38c
      Writeeeprom Mv_faktor , $390
      Writeeeprom Mpl_faktor , $394
      Writeeeprom Mvl_faktor , $398
      Werteneu = 0
   End If
   Utext = Str(pwm_rechts_max)
   Utext = "PWM r max=" + Utext
   Logtext $e0 , Utext
   Utext = Str(pwm_links_max)
   Utext = "PWM l max=" + Utext
   Logtext $f0 , Utext
   Utext = Str(ugemessen)
   Utext = "U=" + Utext
   Logtext $100 , Utext
   Utext = Str(imin)
   Utext = "Imin=" + Utext
   Logtext $110 , Utext
   Utext = Str(imax)
   Utext = "Imax=" + Utext
   Logtext $120 , Utext
   Utext = Str(summe_mah_entladen)
   Utext = "-mAh" + Utext
   Logtext $130 , Utext
   Utext = Str(summe_mah_laden)
   Utext = "+mAh=" + Utext
   Logtext $140 , Utext
   Utext = Str(verbraucht)
   Utext = "    =" + Utext
   Logtext $150 , Utext
   Utext = Str(akkukap)
   Utext = "Kap=" + Utext
   Logtext $160 , Utext
   Utext = Str(kilometer)
   Utext = "Km=" + Utext
   Logtext $170 , Utext
   Utext = Str(meter)
   Utext = "M=" + Utext
   Logtext $180 , Utext
   Utext = Str(zentimeter)
   Utext = "cm=" + Utext
   Logtext $190 , Utext
   Utext = Str(regelzeitmax)
   Utext = "Rz Max=" + Utext
   Logtext $1a0 , Utext
   Utext = Str(null_roll)
   Utext = "Null_R=" + Utext
   Logtext $1b0 , Utext
   Utext = Str(null_nick)
   Utext = "null_N=" + Utext
   Logtext $1c0 , Utext
   Utext = Str(p_faktor)
   Utext = "P=" + Utext
   Logtext $1d0 , Utext
   Utext = Str(i_faktor)
   Utext = "I=" + Utext
   Logtext $1e0 , Utext
   Utext = Str(d_faktor)
   Utext = "D=" + Utext
   Logtext $1f0 , Utext
   Utext = Str(mp_faktor)
   Utext = "M=" + Utext
   Logtext $200 , Utext
   Utext = Str(k_diff_min)
   Utext = "K min=" + Utext
   Logtext $210 , Utext
   Utext = Str(k_diff_max)
   Utext = "K max=" + Utext
   Logtext $220 , Utext
   Utext = Str(seriell_err_l)
   Utext = "S l=" + Utext
   Logtext $230 , Utext
   Utext = Str(seriell_err_r)
   Utext = "S r=" + Utext
   Logtext $240 , Utext
   Utext = Str(tamax)
   Utext = "Ta_Max=" + Utext
   Logtext $250 , Utext
   Utext = Str(max_v)
   Utext = "V_Max=" + Utext
   Logtext $260 , Utext
   Utext = Str(min_v)
   Utext = "V_Min=" + Utext
   Logtext $270 , Utext

End Sub
'**************************************************************************************************
Sub Onbutton1
If Antriebaus = 1 Then
   If Ugemessen > Akkuleer Then                                                 'alles unter 36V Leerlaufspannung ist LEER
      If Sensor_ok = 1 Then                                                     'Prüfen ob Gyros noch driften
         Disable Interrupts                                                     'Interupts abschalten damit Fusscount nicht gerade jetzt decrementiert wird
         Regelzeit = 0
         Regelzeitmax = 0
         Mit_acc_nick_max = 0
         Mit_acc_roll_max = 0
         Mit_acc_nick_min = 0
         Mit_acc_roll_min = 0
         Pwm_rechts_max = 0
         Pwm_links_max = 0
         Seriell_err_r = 0
         Seriell_err_l = 0
         K_diff_min = 0
         K_diff_max = 0
         Antriebaus = 0
         Fusscount = 50                                                         '0.5 Sekunden zeit zum Aufsteigen
         Enable Interrupts
      Else
         If Null_nick > O_nick_o And Null_nick < O_nick_oo Then
            If Bzeit = 500 Then
               Bstate = 1
               Bzeit = 0
            Else
               If Bstate = 1 Then
                  If Bzeit < 50 Then
                  Disable Interrupts
                  Utext = "                "
                  Logtext $00 , Utext
                  Logtext $10 , Utext
                  Logtext $20 , Utext
                  Logtext $30 , Utext
                  Logtext $40 , Utext
                  Logtext $50 , Utext
                  Logtext $60 , Utext
                  Logtext $70 , Utext
                  Logtext $80 , Utext
                  Logtext $90 , Utext
                  Logtext $a0 , Utext
                  Logtext $b0 , Utext
                  Logtext $c0 , Utext
                  Logtext $d0 , Utext
                  Logtext $e0 , Utext
                  Logtext $f0 , Utext
                  Logtext $100 , Utext
                  Logtext $110 , Utext
                  Logtext $120 , Utext
                  Logtext $130 , Utext
                  Logtext $140 , Utext
                  Logtext $150 , Utext
                  Logtext $160 , Utext
                  Logtext $170 , Utext
                  Logtext $180 , Utext
                  Logtext $190 , Utext
                  Logtext $1a0 , Utext
                  Logtext $1b0 , Utext
                  Logtext $1c0 , Utext
                  Logtext $1d0 , Utext
                  Logtext $1e0 , Utext
                  Logtext $1f0 , Utext
                  Logtext $200 , Utext
                  Logtext $210 , Utext
                  Logtext $220 , Utext
                  Logtext $230 , Utext
                  Logtext $240 , Utext
                  Logtext $250 , Utext
                  Logtext $260 , Utext
                  Summe_as_entladen = 0
                  Summe_mah_entladen = 0
                  Summe_as_laden = 0
                  Summe_mah_laden = 0
                  Writeeeprom Summe_as_entladen , $300
                  Writeeeprom Summe_mah_entladen , $310
                  Writeeeprom Summe_as_laden , $320
                  Writeeeprom Summe_mah_laden , $330
                  Frei = 0
                  For Bzeit = 1 To 10
                     Led3 = 1
                     Led4 = 1
                     Led5 = 1
                     Led6 = 1
                     Led7 = 1
                     Waitms 100
                     Led3 = 0
                     Led4 = 0
                     Led5 = 0
                     Led6 = 0
                     Led7 = 0
                     Waitms 100
                  Next
                  Bzeit = 500
                  Enable Interrupts
                  Waitms 100

                  End If
                  If Bzeit > 250 And Bzeit < 350 Then
                     Frei = 1
                     Bzeit = 500
                  Else
                     Frei = 0
                     Bzeit = 500

                  End If
               End If
            End If
         Else
         Frei = 0
         Bzeit = 500
         End If
      End If
   Else
      If Verbraucht < Akkukap Then                                              'Akku ist laut Spannung leer!
         Akkukap = Verbraucht * 0.95                                            'Einfach mal annehmen daß zuviel entnommen wurde
         Akkugrenzen
      End If
      Disable Interrupts
      Betriebsdatenschreiber
      Enable Interrupts
   End If
Else
   Antriebaus = 1
   Waitms 100
   Frei = 0
   Disable Interrupts
   Betriebsdatenschreiber
   Enable Interrupts
End If
End Sub
'**************************************************************************************************
Sub Logtext(adresse , Text)
   If Len(text) > 16 Then Text = Mid(text , 1 , 16)
   While Len(text) < 16
      Text = Text + " "
   Wend
   Writeeeprom Text , Adresse

End Sub
'**************************************************************************************************

Regel_interrupt:
'**************************************************************************************************
'Eigentlich ist das kein Typischer Interrupt. Der Interrupt wird hier nur verwendet um der
'Regelung einen 100Hz Takt zu geben. Normalerweise sollte kein Interrupt so lange sein.
'Dadurch werden andere Interrupts für diese Dauer blockiert.
'**************************************************************************************************
Timer0 = 61                                                                     'Sollte ca. 100Hz ergeben
Led2 = 1
If Fusscount > 0 Then Decr Fusscount
If Fusstaster = 0 Then Fusscount = 25                                           '0.25 Sekunden ohne Fusstaster
Bdummy1 = Udr
Gyro_gier = Getadc(3)                                                           'Gier
Acc_roll = Getadc(5)                                                            'Roll
Acc_nick = Getadc(7)                                                            'Nick
Gyro_roll = Getadc(2)                                                           'Roll
Gyro_nick = Getadc(4)                                                           'Nick

Acc_roll = Acc_roll * 100
Acc_nick = Acc_nick * 100


'<Mittellage ausgleichen>
   'Die Offsetwerte werden langsam an eine früher ermittelte Waagerechte Positoion angeglichen.
   Null_roll = Null_roll * 99
   Null_nick = Null_nick * 99
   Null_roll = Null_roll + Offset_roll                                          'Mittellage
   Null_nick = Null_nick + Offset_nick                                          'Mittellage
   Null_roll = Null_roll / 100
   Null_nick = Null_nick / 100
'</Mittellage ausgleichen>

'Offsetwerte der Beschleunigungssensoren sofort abziehen
'Diese werden nicht laufend ermittelt
Acc_roll = Acc_roll - Null_roll
Acc_nick = Acc_nick - Null_nick
'<Geschwindikgeitsbegrenzung>
      'Bei der Geschwindigkeitsbegrenzung wird einfach die Nullage der Plattform verändert.
      'Beim Vorwärtsbremsen wird nach hinten geneigt.
      'Beim Rückwärtsbremsen nach vorne.
      'Höchstgeschwindigkeit im Gesetzlichen Rahmen unter 6Km/h (Ausserhalb der KFZ Zulassungsordnung)
      Vbrems = 0
      Lbrems = 0
      If Frei = 0 Then
         If Kmh > Vmax Then Vbrems = Vmax - Kmh
         If Kmh < Vmaxr Then Vbrems = Vmaxr - Kmh
      End If

      'Lastabhängige Bremse
      'Es muss immer genug Reserve sein um noch zu Bremsen oder zu Balancieren
      Ldummyi = Int(pwm_gesamt)
      If Ldummyi > Spwm Then Lbrems = Spwm - Ldummyi
      If Ldummyi < Spwmr Then Lbrems = Spwmr - Ldummyi
      Brems = Lbrems + Vbrems
      Brems = Brems * 50
      'Nicht zu heftig Bremsen
      If Brems < -2000 Then Brems = -2000
      If Brems > 2000 Then Brems = 2000
      Acc_nick = Acc_nick + Brems
'</Geschwindikgeitsbegrenzung>

'<Signalaufbereitung der Sensorsignale>
      K_diff = K_rechts - K_links
      K_sum = K_sum * 0.84
      K_sum = K_sum + K_diff
      'Gleitender Mittelwert der Gyros berechnen um damit den Offset bei Stillstand
      'zu haben. Dieser ist leider Temperaturabhängig.
      Sum_gyro_gier = Sum_gyro_gier - Mit_gyro_gier
      Sum_gyro_gier = Sum_gyro_gier + Gyro_gier
      Mit_gyro_gier = Sum_gyro_gier / Gyro_anz
      Sig_gyro_gier = Gyro_gier - Mit_gyro_gier

      Sum_gyro_roll = Sum_gyro_roll - Mit_gyro_roll
      Sum_gyro_roll = Sum_gyro_roll + Gyro_roll
      Mit_gyro_roll = Sum_gyro_roll / Gyro_anz
      Sig_gyro_roll = Gyro_roll - Mit_gyro_roll
      Korr_acc_roll = Sig_gyro_roll * Gyro_faktor
      Sum_acc_roll = Sum_acc_roll - Mit_acc_roll
      Sum_acc_roll = Sum_acc_roll + Acc_roll
      Sum_acc_roll = Sum_acc_roll + Korr_acc_roll                               'Gyrowert hinzu
      Mit_acc_roll = Sum_acc_roll / Acc_anz
      '************************************************************************************************************************************************************************************
      Sdummy1 = Mit_acc_roll * Sig_gyro_gier                                    'Das soll ausgleichen, daß die Gyros bei gekippten Sensoren eine Drehung um die falsche Achse registrieren.
      Sdummy1 = Sdummy1 / 20000                                                 'Die Sensorplatine muss dazu absolut Waagerecht montiert sein.
      Lagefehler = Int(sdummy1)
      Gyro_nick = Gyro_nick + Lagefehler                                        'Das soll den Fehler ausgleichen, der durch die geänderte Lage des Gyros auftritt.
      '************************************************************************************************************************************************************************************
      Sum_gyro_nick = Sum_gyro_nick - Mit_gyro_nick
      Sum_gyro_nick = Sum_gyro_nick + Gyro_nick
      Mit_gyro_nick = Sum_gyro_nick / Gyro_anz
      Sig_gyro_nick = Gyro_nick - Mit_gyro_nick
      Korr_acc_nick = Sig_gyro_nick * Gyro_faktor
      Sum_acc_nick = Sum_acc_nick - Mit_acc_nick
      Sum_acc_nick = Sum_acc_nick + Acc_nick
      Sum_acc_nick = Sum_acc_nick - Korr_acc_nick                               'Gyrowert hinzu
      Mit_acc_nick = Sum_acc_nick / Acc_anz
'</Signalaufbereitung der Sensorsignale>

'<PID-Regler>
   Stellwert = 0
   '<P-Regler>
      Sdummy1 = Mit_acc_nick
      Sdummy1 = Sdummy1 * P_faktor
      Stellwert = Stellwert - Sdummy1
   '</P-Regler>
   '<I-Regler>
      Mit_acc_nick_s = Mit_acc_nick_s + Mit_acc_nick
      'Überlauf/unsinnige Werte verhindern
      If Mit_acc_nick_s > Max_mit_acc_nick_s Then Mit_acc_nick_s = Max_mit_acc_nick_s
      If Mit_acc_nick_s < Min_mit_acc_nick_s Then Mit_acc_nick_s = Min_mit_acc_nick_s
      Sdummy1 = Mit_acc_nick_s
      Sdummy1 = Sdummy1 * I_faktor
      Stellwert = Stellwert - Sdummy1
   '</I-Regler>
   '<D-Regeler>
      Sdummy1 = Sig_gyro_nick
      Sdummy1 = Sdummy1 * D_faktor
      Stellwert = Stellwert + Sdummy1
   '</D-Regler>

   '<Anpassungsfaktoren berechnen>
   K_sum_anpassung = K_rechts + K_links
   K_sum_anpassung = Abs(k_sum_anpassung)

   Mp_anpassung = Pwm_gesamt * Mp_faktor
   Mp_anpassung = Abs(mp_anpassung)
   Mp_anpassung = Mp_anpassung + 0.95

   Mv_anpassung = K_sum_anpassung * Mv_faktor
   Mv_anpassung = Abs(mv_anpassung)
   Mv_anpassung = Mv_anpassung + 1

   Mpl_anpassung = Pwm_gesamt * Mpl_faktor
   Mpl_anpassung = Abs(mpl_anpassung)
   Mpl_anpassung = Mpl_anpassung + 1

   Mvl_anpassung = K_sum_anpassung * Mvl_faktor
   Mvl_anpassung = Abs(mvl_anpassung)
   Mvl_anpassung = Mvl_anpassung + 1
   '</Anpassungsfaktoren berechnen>


   '<Verstärkte Regelung bei hoher Leistung>
   Stellwert = Stellwert * Mp_anpassung
   Stellwert = Stellwert * Mv_anpassung
   '<Verstärkte Regelung bei hoher Leistung>


   '<Abgeschwächte Regelung in Stand>

   If K_sum_anpassung < 8 Then Stellwert = Stellwert * 0.8
   If K_sum_anpassung < 6 Then Stellwert = Stellwert * 0.8
   If K_sum_anpassung < 4 Then Stellwert = Stellwert * 0.8

   '</Abgeschwächte Regelung in Stand>
   Pwm_gesamt = Pwm_gesamt + Stellwert

'</PID-Regler>

'<Bremsvibrator einmischen>
   'Der Bremsvibrator signalisiert dem Fahrer, daß wegen irgend einem Grenzwert gebremst wird.
   'Das kann V-Max oder P-Max sein.
    If Brems <> 0 Then
      Bdummy1 = Intcount / 2
      Bdummy1 = Bdummy1 Mod 2
      If Bdummy1 = 0 Then
         Pwm_gesamt = Pwm_gesamt + 5
      Else
         Pwm_gesamt = Pwm_gesamt - 5
      End If
    End If
'<Bremsvibrator einmischen>
'<Lenkung>
   Lenkvorgabe = Mit_acc_roll / 80

   Sdummy1 = Abs(lenkvorgabe)
   If Sdummy1 <= 6 Then
      Sdummy1 = Sdummy1 / 12
      Lenkerstellung = Lenkvorgabe * Sdummy1
   Else
      If Lenkvorgabe < 0 Then
         Lenkerstellung = Lenkvorgabe + 3
      Else
         Lenkerstellung = Lenkvorgabe - 3
      End If
   End If

   'Lenkfehler berechnen und zum Stellwert hinzu.
   Sdummy1 = Sig_gyro_gier
   Sdummy1 = Sdummy1 / 5
   Lenkfehler = Lenkerstellung - Sdummy1
   Lenkfehler = Lenkfehler * Mpl_anpassung
   Lenkfehler = Lenkfehler * Mvl_anpassung

   Lenkstellwert = Lenkerstellung + Lenkfehler

   If Lenkstellwert > Max_lenk Then Lenkstellwert = Max_lenk
   If Lenkstellwert < Min_lenk Then Lenkstellwert = Min_lenk

   Pwm_lenk_links = Pwm_gesamt + Lenkstellwert
   Pwm_lenk_rechts = Pwm_gesamt - Lenkstellwert

   If Pwm_lenk_links > 0 Then Richtung_links = 1 Else Richtung_links = 0
   If Pwm_lenk_rechts > 0 Then Richtung_rechts = 1 Else Richtung_rechts = 0

   Pwm_lenk_links = Abs(pwm_lenk_links)
   Pwm_lenk_rechts = Abs(pwm_lenk_rechts)

   If Pwm_lenk_links > Maxpwm Then Pwm_lenk_links = Maxpwm
   If Pwm_lenk_rechts > Maxpwm Then Pwm_lenk_rechts = Maxpwm

   If Pwm_lenk_links < Minpwm Then Pwm_lenk_links = Minpwm
   If Pwm_lenk_rechts < Minpwm Then Pwm_lenk_rechts = Minpwm

   Pwm_links = Pwm_lenk_links
   Pwm_rechts = Pwm_lenk_rechts
'</Lenkung>
If Antriebaus = 1 Then
   If Notaus = 1 Then
      'Unfalldatenschreiber: *********************************************************
      'Dauert zwar Lange, macht aber nix. Bei Notaus muss nix geregelt werden.
      Unfalldatenschreiber
      Betriebsdatenschreiber
      Notaus = 0
      '*******************************************************************************
   End If
'Auf 0 setzen was sonst geregelt wird
   Richtung_links = 2                                                           'Leerlauf
   Richtung_rechts = 2                                                          'Leerlauf
   Pwm_links = 0
   Pwm_rechts = 0
   Pwm_gesamt = 0
   Mit_acc_nick_s = 0
   Lenkerstellung = 0
   K_rechts = 0
   K_links = 0
   'Waagerechte Positoion setzen wenn Zustand Antriebaus:
   Wdummy1 = Getadc(5)
   Null_roll = Wdummy1 * 100
   Wdummy1 = Getadc(7)
   Null_nick = Wdummy1 * 100
   'Mittelwert der Beschleunigungssensoren auf aktuellen Wert stellen
   'Der Mittelwert braucht sonst zu lange bis
   'er sich eingeregelt hat
   'Sum_acc_roll = Acc_roll * Acc_anz
   'Sum_acc_nick = Acc_nick * Acc_anz
   If Intcount = 0 Then T30 = 1                                                 'Regelparameter vom Tacho anfordern
   If Intcount = 50 Then T30 = 1                                                'Regelparameter vom Tacho anfordern
End If
'*******************************************************************************
   If T30 = 1 Then                                                              'Regelparameter vom Tacho anfordern
      K_links = 0
      K_rechts = 0
      Zeiger_meldung = 1
      Bdummy1 = Udr
      T30 = 0                                                                   'Damit keine alte Rückmeldung ausgewertet wird
      Timeout = Timer0
      Timeout = Timeout + 100
      Print Chr(255) ; Chr(0) ; Chr(0) ; Chr(255) ; Chr(13)

      While Zeiger_meldung < 31
         If Ucsr0a.rxc0 = 1 Then                                                'Warten bis ein Byte eingetroffen ist
            Tachomeldung(zeiger_meldung) = Udr
            Incr Zeiger_meldung
            Timeout = Timer0
            Timeout = Timeout + 20
         End If
         If Timer0 > Timeout Then
         T30 = 0
         Exit While                                                             'Timeout
         End If
      Wend
      If T30 = 13 Then
         $asm
            lds R24,{t1}
            LDs R25,{t2}
            add R24,R25
            LDs R25,{t3}
            add R24,R25
            LDs R25,{t4}
            add R24,R25
            LDs R25,{t5}
            add R24,R25
            LDs R25,{t6}
            add R24,R25
            LDs R25,{t7}
            add R24,R25
            LDs R25,{t8}
            add R24,R25
            LDs R25,{t9}
            add R24,R25
            LDs R25,{t10}
            add R24,R25
            LDs R25,{t11}
            add R24,R25
            LDs R25,{t12}
            add R24,R25
            LDs R25,{t13}
            add R24,R25
            LDs R25,{t14}
            add R24,R25
            LDs R25,{t15}
            add R24,R25
            LDs R25,{t16}
            add R24,R25
            LDs R25,{t17}
            add R24,R25
            LDs R25,{t18}
            add R24,R25
            LDs R25,{t19}
            add R24,R25
            LDs R25,{t20}
            add R24,R25
            LDs R25,{t21}
            add R24,R25
            LDs R25,{t22}
            add R24,R25
            LDs R25,{t23}
            add R24,R25
            LDs R25,{t24}
            add R24,R25
            LDs R25,{t25}
            add R24,R25
            LDs R25,{t26}
            add R24,R25
            LDs R25,{t27}
            add R24,R25
            LDs R25,{t28}
            add R24,R25
            STS {Bdummy1},R24
         $end Asm

         If Bdummy1 = T29 Then
            P_faktor = P_tacho
            I_faktor = I_tacho
            D_faktor = D_tacho
            Mp_faktor = Mp_tacho
            Mv_faktor = Mv_tacho
            Mpl_faktor = Mpl_tacho
            Mvl_faktor = Mvl_tacho
            Werteneu = 1
         End If
      End If

   Else
   'Errechnete Geschwindigkeiten an Motoren Senden:
   'Rechter Motor:
   Checksum_rechts = 82
   Checksum_rechts = Checksum_rechts + Richtung_rechts
   Checksum_rechts = Checksum_rechts + Pwm_rechts
   'Geschwindigkeit an Motorregler senden
   Zeiger_meldung = 1
   Bdummy1 = Udr                                                                'Puffer leeren
   'Print Chr(82) ; Chr(richtung_rechts) ; Chr(pwm_rechts) ; Chr(checksum_rechts) ; Chr(13);
   'In Assembler geht das so:
   $asm
   Ldi R24 , 82
   STS     udr,R24
   Warten1:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten1
   LDS     R24,{Richtung_rechts}
   STS     udr,R24
   Warten2:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten2
   lds     R24,{Pwm_rechts}
   STS     udr,R24
   Warten3:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten3
   LDS     R24,{Checksum_rechts}
   STS     udr,R24
   Warten4:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten4
   LDI     R24,13
   STS     udr,R24
   Warten5:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten5
   $end Asm
   Motormeldung(8) = 0                                                          'Damit keine alte Rückmeldung ausgewertet wird
   Timeout = Timer0
   Timeout = Timeout + 80
   While Zeiger_meldung < 9
      If Ucsr0a.rxc0 = 1 Then                                                   'Warten bis ein Byte eingetroffen ist
         Motormeldung(zeiger_meldung) = Udr
         Incr Zeiger_meldung
      End If
      If Timer0 > Timeout Then
      Ende = 0
      Exit While                                                                'Timeout
      End If
   Wend
   $asm
               lds R24,{M1}
               LDs R25,{M2}
               ADD R24,R25
               LDs R25,{M3}
               ADD R24,R25
               LDs R25,{M4}
               ADD R24,R25
               LDs R25,{M5}
               ADD R24,R25
               LDs R25,{M6}
               ADD R24,R25
               STS {Bdummy1},R24
   $end Asm
   If Bdummy1 <> Checksumme Then Ende = 0
   If Ende = 13 Then
      U_rechts = U
      I_rechts = I
      K_rechts = K
   Else
      Incr Seriell_err_r
      S_err = 1
   End If
   'Linker Motor:
   Checksum_links = 76
   Checksum_links = Checksum_links + Richtung_links
   Checksum_links = Checksum_links + Pwm_links
   Zeiger_meldung = 1
   Bdummy1 = Udr                                                                'Puffer leeren
   'Print Chr(76) ; Chr(richtung_links) ; Chr(pwm_links) ; Chr(checksum_links) ; Chr(13);
   'In Assembler geht das so:
   $asm
   Ldi     R24 , 76
   STS     udr,R24
   Warten6:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten6
   LDS     R24,{Richtung_links}
   STS     udr,R24
   Warten7:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten7
   lds     R24,{Pwm_links}
   STS     udr,R24
   Warten8:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten8
   LDS     R24,{Checksum_links}
   STS     udr,R24
   Warten9:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten9
   LDI     R24,13
   STS     udr,R24
   Warten10:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten10
   $end Asm
   Motormeldung(8) = 0                                                          'Damit keine alte Rückmeldung ausgewertet wird
   Timeout = Timer0
   Timeout = Timeout + 80
   While Zeiger_meldung < 9
      If Ucsr0a.rxc0 = 1 Then                                                   'Warten bis ein Byte eingetroffen ist
         Motormeldung(zeiger_meldung) = Udr
         Incr Zeiger_meldung
      End If
      If Timer0 > Timeout Then
      Ende = 0
      Exit While                                                                'Timeout
      End If
   Wend
   $asm
               lds R24,{M1}
               LDs R25,{M2}
               ADD R24,R25
               LDs R25,{M3}
               ADD R24,R25
               LDs R25,{M4}
               ADD R24,R25
               LDs R25,{M5}
               ADD R24,R25
               LDs R25,{M6}
               ADD R24,R25
               STS {Bdummy1},R24
   $end Asm
   If Bdummy1 <> Checksumme Then Ende = 0
   If Ende = 13 Then
      U_links = U
      I_links = I
      K_links = K
   Else
      Incr Seriell_err_l
      S_err = 1
   End If
If S_err = 1 Then                                                               'Sicherheitshalber, damit der Lenkausgleich nicht überreagiert.
  K_links = 0
  K_rechts = 0
  S_err = 0
End If
End If
 If Antriebaus = 0 Then
'<Notauskriterien ermitteln>
      If Mit_acc_nick > -300 Then
         If Mit_acc_nick < 300 Then

            Regelzeit = 0
         End If
      End If
   Incr Regelzeit
'</Notauskriterien ermitteln>
'<Notauskriterien abprüfen>
   If Regelzeit > 250 Then                                                      'Wenn nach 2,5s waagerechte Lage nicht erreicht
      Notaus = 1
      Antriebaus = 1
      Ugrund = Str(regelzeit)
      Ugrund = "*Rz " + Ugrund
   End If
   Ldummyi = 4000 + Brems
   If Mit_acc_nick > Ldummyi Then                                               'zu weit nach hinten gekippt
      Notaus = 1
      Antriebaus = 1
      Ugrund = Str(mit_acc_nick)
      Ugrund = "*Nick " + Ugrund
   End If
   Ldummyi = -4000 + Brems
   If Mit_acc_nick < Ldummyi Then                                               'zu weit nach vorn gekippt
      Notaus = 1
      Antriebaus = 1
      Ugrund = Str(mit_acc_nick)
      Ugrund = "*Nick " + Ugrund
   End If
   If Mit_acc_roll > 5000 Then                                                  'Lenker zu weit
      Notaus = 1
      Antriebaus = 1
      Ugrund = Str(mit_acc_roll)
      Ugrund = "*Roll " + Ugrund
   End If
   If Mit_acc_roll < -5000 Then                                                 'Lenker zu weit
      Notaus = 1
      Antriebaus = 1
       Ugrund = Str(mit_acc_roll)
      Ugrund = "*Roll " + Ugrund
   End If
   If K_diff > 10 Then                                                          'Plattform rotiert zu schnell
      Notaus = 1
      Antriebaus = 1
      Ugrund = Str(k_diff)
      Ugrund = "*Gier " + Ugrund
   End If

   If K_diff < -10 Then                                                         'Plattform rotiert zu schnell
      Notaus = 1
      Antriebaus = 1
      Ugrund = Str(k_diff)
      Ugrund = "*Gier " + Ugrund
   End If
'</Notauskriterien abprüfen>
End If
'<Akkuspannung berechnen>
   Ugemessen = U_links + U_rechts
   Shift Ugemessen , Right , 1 , Signed
'</Akkuspannung berechnen>
'<Gesamtstrom berechnen>
   Igemessen = I_links + I_rechts
'</Gesamtstrom berechnen>
'<verbrauchte As berechnen>
   If Igemessen > 0 Then
      Summe_as_entladen = Summe_as_entladen + Igemessen                         'Verbrauchte
   End If
   If Igemessen < 0 Then
      Summe_as_laden = Summe_as_laden - Igemessen                               'Rückgespeiste
   End If
   'mAh aus den As rausholen
   'um überläufe zu verhindern
   'Die 360000 kommen weil:
   'Igemessen in mA -> Faktor 1
   '100 Messungen pro Sekunde -> mal 100
   '3600 mAmperesekunden=1 mAh -> also nochmal mal 3600
   If Summe_as_entladen > 360000 Then
      Summe_mah_entladen = Summe_mah_entladen + 1
      Summe_as_entladen = Summe_as_entladen - 360000
   End If
   If Summe_as_laden > 360000 Then                                              'Das kann passieren wenns den Berg runter geht
      Summe_mah_laden = Summe_mah_laden + 1
      Summe_as_laden = Summe_as_laden - 360000
   End If
      Erzeugt = Summe_mah_laden
      Verbraucht = Summe_mah_entladen
      Erzeugt = Erzeugt * 0.7                                                   'Das ist der Akku-Wirkungsgrad 0.7 (0.85) bezogen auf den Stom
      Verbraucht = Verbraucht - Erzeugt
      Akkuinhalt = Akkukap
      Akkuverbrauch = Verbraucht
      Akkuprozent = Akkuverbrauch / Akkuinhalt
      Akkuprozent = Akkuprozent * 1000
      Akkuprozent = Int(akkuprozent)
      Akkustand = Akkuprozent
'</verbrauchte As berechnen>
'<Strecke berechnen>
   Kx = K_links + K_rechts
   Kx = Kx / 2
   Strecke = Kx * Di
   Zentimeter = Zentimeter + Strecke
   If Zentimeter >= 100 Then
      Meter = Meter + 1
      Zentimeter = Zentimeter - 100
   End If
   If Zentimeter < 0 Then
      Meter = Meter - 1
      Zentimeter = Zentimeter + 100
   End If
   If Meter >= 1000 Then
      Kilometer = Kilometer + 1
      Meter = Meter - 1000
   End If
   If Meter < 0 Then
      Kilometer = Kilometer - 1
      Meter = Meter + 1000
   End If
'</Strecke berechnen>
'<Geschwindigkeit berechnen>
Kv = Kv + Kx
Kmh = Kv * Kmhfaktor
Kv = Kv * 0.75                                                                  'Bei Änderung auch den kmhfaktor neu berechnen
If Kmh > Max_v Then Max_v = Kmh
If Kmh < Min_v Then Min_v = Kmh
'</Geschwindigkeit berechnen>
If Bzeit < 500 Then Incr Bzeit
Incr Intcount
Incr Tachocount
Select Case Tachocount
'*******************************************************************************
'Alle möglichen Werte werden an den Tacho geschickt. Manche für die Funktion
'des Tachos, andere um die Steuerung zu optimieren.
'*******************************************************************************
Case 0                                                                          'U ausgeben
     B(1) = 1
     U = Ugemessen
     'U = Sig_Gyro_Nick
     B(2) = Motormeldung(1)
     B(3) = Motormeldung(2)
     B(5) = 1
Case 1                                                                          'I ausgeben
     B(1) = 2
     I = Igemessen / 10
     B(2) = Motormeldung(3)
     B(3) = Motormeldung(4)
     B(5) = 1
Case 2                                                                          'Km/h
     B(1) = 3
     B(2) = Kmhmeldung(1)
     B(3) = Kmhmeldung(2)
     B(5) = 1
Case 3                                                                          'Akkuprozente
     B(1) = 5
     B(2) = Akkumeldung(1)
     B(3) = Akkumeldung(2)
     B(5) = 1
Case 4                                                                          'Meter
     B(1) = 7
     B(2) = Metermeldung(1)
     B(3) = Metermeldung(2)
     B(5) = 1
Case 5                                                                          'Kilometer
     B(1) = 6
     B(2) = Kilometermeldung(1)
     B(3) = Kilometermeldung(2)
     B(5) = 1
Case 6
      If Antriebaus = 0 Then
         Maccroll = Mit_acc_roll
         B(1) = 8
         B(2) = Maccroll1
         B(3) = Maccroll2
         B(4) = 1
      Else
         Nullroll = Null_roll
         B(1) = 12
         B(2) = Nullroll1
         B(3) = Nullroll2
         B(4) = 1
      End If
Case 7
      If Antriebaus = 0 Then
         B(1) = 9
         B(2) = Maccroll3
         B(3) = Maccroll4
         B(4) = 1
      Else
         B(1) = 13
         B(2) = Nullroll3
         B(3) = Nullroll4
         B(4) = 1
      End If
Case 8
      B(1) = 4
      B(2) = Antriebaus
      B(3) = Ta
      B(4) = 1
Case 9
      If Antriebaus = 0 Then
         Maccnick = Mit_acc_nick
         B(1) = 10
         B(2) = Maccnick1
         B(3) = Maccnick2
         B(4) = 1
      Else
         Nullnick = Null_nick
         B(1) = 14
         B(2) = Nullnick1
         B(3) = Nullnick2
         B(4) = 1
      End If
Case 10
      If Antriebaus = 0 Then
         B(1) = 11
         B(2) = Maccnick3
         B(3) = Maccnick4
         B(4) = 1
      Else
         B(1) = 15
         B(2) = Nullnick3
         B(3) = Nullnick4
         B(4) = 1
      End If
Case 11
      Sw = Stellwert
      B(1) = 16
      B(2) = Sw1
      B(3) = Sw2
      B(4) = 1
Case 12
      B(1) = 17
      B(2) = Sw3
      B(3) = Sw4
      B(4) = 1
Case 13
      Gyronick = Sig_gyro_nick
      B(1) = 18
      B(2) = Gyronick1
      B(3) = Gyronick2
      B(4) = 1
Case 14
      B(1) = 19
      B(2) = Gyronick3
      B(3) = Gyronick4
      B(4) = 1
Case 15
      Kksum = K_sum
      B(1) = 20
      B(2) = Kksum1
      B(3) = Kksum2
      B(4) = 1
Case 16
      B(1) = 21
      B(2) = Kksum3
      B(3) = Kksum4
      B(4) = 1
If Antriebaus = 0 Then Tachocount = 255                                         'Gleich zu 0 überlaufen lassen
Case 17
      B(1) = 22
      B(2) = Pf1
      B(3) = Pf2
      B(4) = 1
Case 18
      B(1) = 23
      B(2) = Pf3
      B(3) = Pf4
      B(4) = 1
Case 19
      B(1) = 24
      B(2) = If1
      B(3) = If2
      B(4) = 1
Case 20
      B(1) = 25
      B(2) = If3
      B(3) = If4
      B(4) = 1
Case 21
      B(1) = 26
      B(2) = Df1
      B(3) = Df2
      B(4) = 1
Case 22
      B(1) = 27
      B(2) = Df3
      B(3) = Df4
      B(4) = 1
Case 23
      B(1) = 28
      B(2) = Mpf1
      B(3) = Mpf2
      B(4) = 1
Case 24
      B(1) = 29
      B(2) = Mpf3
      B(3) = Mpf4
      B(4) = 1
Case 25
      B(1) = 30
      B(2) = Mvf1
      B(3) = Mvf2
      B(4) = 1
Case 26
      B(1) = 31
      B(2) = Mvf3
      B(3) = Mvf4
      B(4) = 1
Case 27
      B(1) = 32
      B(2) = Mplf1
      B(3) = Mplf2
      B(4) = 1
Case 28
      B(1) = 33
      B(2) = Mplf3
      B(3) = Mplf4
      B(4) = 1
Case 29
      B(1) = 34
      B(2) = Mvlf1
      B(3) = Mvlf2
      B(4) = 1
Case 30
      B(1) = 35
      B(2) = Mvlf3
      B(3) = Mvlf4
      B(4) = 1

Tachocount = 255
Case Else
B(5) = 0                                                                        'Nix ausgeben
End Select
If Intcount = 99 Then Intcount = 255
If B(5) = 1 Then
   'Checksumme berechnen
   B(4) = B(1)
   B(4) = B(4) + B(2)
   B(4) = B(4) + B(3)

'Print Chr(b(1)) ; Chr(b(2)) ; Chr(b(3)) ; Chr(b(4)) ; Chr(13);
'Print in Assembler:
$asm
   LDS     R24,285                                                              'b(1)
   STS     udr,R24
   Warten11:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten11
   LDS     R24,286                                                              'b(2)
   STS     udr,R24
   Warten12:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten12
   lds     R24,287                                                              'B(3)
   STS     udr,R24
   Warten13:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten13
   LDS     R24,288                                                              'B(4)
   STS     udr,R24
   Warten14:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten14
   LDI     R24,13
   STS     udr,R24
   Warten15:
   LDS     R24,UCSR0A
   BST     R24,5
   Brtc warten15
$end Asm
   Ta = Timer0                                                                  'Um festzustellen ob der Interrupt zu lange dauert
End If
Led2 = 0
Return

Tacho

Als Tacho wird ein fertiges Display von Display3000.de mit dem optionalen SD-Katen Modul verwendet. Das Display ist für diese Zwecke gerade brauchbar. Die mitgelieferten Bibliotheken könnten etwas besser, und vor allem schneller sein. Neben der normalen Geschwindigkeitsanzeige können über den Tacho auch die PID-Parameter sowie die diversen Parameter zur Anpassung der Motorkennlinie eingestellt werden. Wenn diese Parameter einmal eingestellt sind ist zum Betrieb der Tacho nicht zwingend erforderlich. Das ganze läuft auch ohne. Wenn eine SD-Karte gesteckt ist können mit dem Tacho auch alle Telegramme, die über die Serielle Schnittstelle gehen, mitgeloggt werden. In diesem Modus muss wegen der langsamen Bibliotheken auf die Aktualisierung des Display verzichtet werden. Bei dem Display muss vor der Verwendung ein Lötjumper geändert werden. Der Taster rechts aussen muss auf D0 gelegt werden. In der Anleitung zum Display ist gut beschrieben wie das geht. Bei früheren Versuchen wurde auch versucht einen PDA als Tacho zu verwenden. Dies funktioierte sogar, ein mitloggen war jedoch nicht möglich. Windows-Mobile ist bei 600Mhz langsamer als ein Atmel mit 16Mhz. Versuche mit einem aktuellen Android-Handy stehen noch aus.


Software

Die Software des Tachos wurde nicht ganz so sorgfältig erstellt wie die des Motorreglers oder der Lageregelung. Hier war der Leitgedanke "Haupsache es tut".

$hwstack = 256
$swstack = 256
$framesize = 64
$regfile = "m2561def.dat"
$crystal = 16000000                                                             'enter the used clock of your actual microcontroller
$baud1 = 57600




'############################################################################################
'4 - Definition of used ports and pull up resistors
                                                            'At our boards we are using Port B for the SPI-communication to the LCD.
                                                            'Now we need to select Port B as to an output port (data output to the display)
Ddrb = &B01110110                                                               'DDR = Data direction register; Port B1, B2, B4, B5, B6 switched to output (1) as needed by the display ....
Portb = &B10001001                                                              '... the other ports of Port B are inputs with switched on pull up resistors

Ddra = &B00000000                                                               'switch all 8 Ports of Port A to input (0), Pin (PA.0 - PA.7)
Porta = &B11111111                                                              'All port pins have individually selectable pull-up resistors. Here we enable these pull-up-resisitors, so these Pins are always at logical 1
                                                                                 'You need to pull these Pins against ground (GND)
Ddrc = &B00000000                                                               'switch all Ports of Port C to input
Portc = &B11111111                                                              'all pull-up-Resistors turned on

Ddrd = &B00000000                                                               'switch all Ports of Port D to input
Portd = &B11110011                                                              'all pull-up-Resistors turned on

Ddre = &B10000000                                                               'E7=Output (SD card activate), all other Input
Porte = &B11111111                                                              'all pull-up-Resistors turned on

Ddrf = &B00000000                                                               'switch all Ports of Port F to input
Portf = &B11111111                                                              'all pull-up-Resistors turned on

Ddrg = &B00000000                                                               'switch all Ports of Port G to input
Portg = &B11111111                                                              'all pull-up-Resistors turned on




Declare Sub Activate_display()
Declare Sub Activate_sd()
Declare Sub Minmax()
Declare Function Trimtext(t As String) As String * 22







Const Titel = "Tacho 0.1             "

Dim Ep0 As Byte At $0201
Dim Ep1 As Byte At $0202
Dim Ep2 As Byte At $0203
Dim Ep3 As Byte At $0204
Dim Ep4 As Byte At $0205
Dim Ep5 As Byte At $0206
Dim Iwert As Integer At $0203 Overlay
Dim Wwert As Integer At $0203 Overlay
Dim Index As Byte At $202 Overlay


Dim Maccx As Long At $0207
Dim Maccx1 As Byte At $0207 Overlay
Dim Maccx2 As Byte At $0208 Overlay
Dim Maccx3 As Byte At $0209 Overlay
Dim Maccx4 As Byte At $020a Overlay
Dim Maccy As Long At $020b
Dim Maccy1 As Byte At $020b Overlay
Dim Maccy2 As Byte At $020c Overlay
Dim Maccy3 As Byte At $020d Overlay
Dim Maccy4 As Byte At $020e Overlay

Dim Nullx As Long At $020f
Dim Nullx1 As Byte At $020f Overlay
Dim Nullx2 As Byte At $0210 Overlay
Dim Nullx3 As Byte At $0211 Overlay
Dim Nullx4 As Byte At $0212 Overlay
Dim Nully As Long At $0213
Dim Nully1 As Byte At $0213 Overlay
Dim Nully2 As Byte At $0214 Overlay
Dim Nully3 As Byte At $0215 Overlay
Dim Nully4 As Byte At $0216 Overlay
Dim Sw As Single At $00217

Dim Sw1 As Byte At $0217 Overlay
Dim Sw2 As Byte At $0218 Overlay
Dim Sw3 As Byte At $0219 Overlay
Dim Sw4 As Byte At $021a Overlay

Dim Gyronick As Long At $0021b
Dim Gyronick1 As Byte At $021b Overlay
Dim Gyronick2 As Byte At $021c Overlay
Dim Gyronick3 As Byte At $021d Overlay
Dim Gyronick4 As Byte At $021e Overlay

Dim Kksum As Single At $0021f
Dim Kksum1 As Byte At $021f Overlay
Dim Kksum2 As Byte At $0220 Overlay
Dim Kksum3 As Byte At $0221 Overlay
Dim Kksum4 As Byte At $0222 Overlay

Dim P_faktor As Single At $0223
Dim P_f1 As Byte At $0223 Overlay
Dim P_f2 As Byte At $0224 Overlay
Dim P_f3 As Byte At $0225 Overlay
Dim P_f4 As Byte At $0226 Overlay
Dim I_faktor As Single At $0227
Dim I_f1 As Byte At $0227 Overlay
Dim I_f2 As Byte At $0228 Overlay
Dim I_f3 As Byte At $0229 Overlay
Dim I_f4 As Byte At $022a Overlay
Dim D_faktor As Single At $022b
Dim D_f1 As Byte At $022b Overlay
Dim D_f2 As Byte At $022c Overlay
Dim D_f3 As Byte At $022d Overlay
Dim D_f4 As Byte At $022e Overlay
Dim Mp_faktor As Single At $022f
Dim Mp_f1 As Byte At $022f Overlay
Dim Mp_f2 As Byte At $0230 Overlay
Dim Mp_f3 As Byte At $0231 Overlay
Dim Mp_f4 As Byte At $0232 Overlay

Dim Mv_faktor As Single At $0233
Dim Mv_f1 As Byte At $0233 Overlay
Dim Mv_f2 As Byte At $0234 Overlay
Dim Mv_f3 As Byte At $0235 Overlay
Dim Mv_f4 As Byte At $0236 Overlay

Dim Mpl_faktor As Single At $0237
Dim Mpl_f1 As Byte At $0237 Overlay
Dim Mpl_f2 As Byte At $0238 Overlay
Dim Mpl_f3 As Byte At $0239 Overlay
Dim Mpl_f4 As Byte At $023a Overlay

Dim Mvl_faktor As Single At $023b
Dim Mvl_f1 As Byte At $023b Overlay
Dim Mvl_f2 As Byte At $023c Overlay
Dim Mvl_f3 As Byte At $023d Overlay
Dim Mvl_f4 As Byte At $023e Overlay

Dim Pfaktor As Long
Dim Ifaktor As Long
Dim Dfaktor As Long
Dim Mpfaktor As Long
Dim Mvfaktor As Long
Dim Mplfaktor As Long
Dim Mvlfaktor As Long


Dim Stellwert As Single
Dim Gyro_nick As Long
Dim Null_y As Long
Dim Null_x As Long
Dim Mit_acc_y As Long
Dim Mit_acc_x As Long
Dim K_sum As Single
Dim K_summax As Single
Dim K_summin As Single

Dim Bdummy As Byte
Dim Sdummy As Single
Dim Wdummy As Word
Dim Ldummy As Long
Dim Idummy As Integer
Dim Kilometer As Word
Dim Meter As Integer
Dim Akkuprozente As Integer
Dim Kmh As Integer
Dim Kmhmax As Integer
Dim Kmhmin As Integer

Dim I As Integer
Dim Imax As Integer
Dim Imin As Integer

Dim U As Integer
Dim Umax As Integer
Dim Umin As Integer
Dim Ml As Integer
Dim Mlmax As Integer
Dim Mrmax As Integer
Dim Mr As Integer
Dim Mlmin As Integer
Dim Mrmin As Integer
Dim Modus As Byte
Dim Editzeile As Byte
Dim Utext As String * 22
Dim Itext As String * 22
Dim Ltext As String * 22
Dim L1 As Byte
Dim L2 As Byte
Dim Text As String * 22
Dim Farbe As Word
Dim X1 As Byte
Dim Y1 As Byte
Dim X2 As Byte
Dim Y2 As Byte
Dim Startkilometer As Single
Dim Ta As Byte
Dim Tamax As Byte

Dim Antriebaus As Byte

Dim Xoffset As Long
Dim Yoffset As Long
Dim Ya As Byte
Dim Xa As Byte

Dim Lname As Long
Dim Dateiname As String * 12
Dim Dateilaenge As Long
Dim Dateiaktuell As Byte
Dim Ulog As Single
Dim Ilog As Single
Dim Kmhlog As Single
Dim Kmlog As Single
Dim Akkuprozentelog As Single
Dim Loopcount As Word
Dim Aloopcount As Word
Dim Logtext As String * 255
Dim Zeit As String * 8
Dim Sichern As Byte
Dim Transferok As Byte

Config Clock = User
Config Date = Dmy , Separator = .


$include "Init21_display3000.bas"
$include "Config_mmc.bas"

If Porte.7 = 1 Then Activate_display
Gosub Lcd_init
Wait 1


Orientation = Portrait180

If Porte.7 = 0 Then Activate_sd
Gbdriveerror = Driveinit()                                                      ' Init MMC/SD Card
   If Gbdriveerror = 0 Then
      $include "Config_AVR-DOS.BAS"                                             ' Include AVR-DOS Configuration and library
      Bdummy = Initfilesystem(1)                                                ' Partition 1
      Ldummy = Disksize()
      Ldummy = Ldummy / 1024
      Utext = Str(ldummy)
      Utext = "Size: " + Utext
      Utext = Utext + " mb"
      Ldummy = Diskfree()
      Ldummy = Ldummy / 1024
      Itext = Str(ldummy)
      Itext = "Free: " + Itext
      Itext = Itext + " mb"
      If Porte.7 = 1 Then Activate_display
      Lcd_cls
      Lcd_print Titel , 0 , 0 , 1 , 1 , 1 , Yellow , Blue
      Lcd_print "SD-Karte init" , 0 , 18 , 1 , 1 , 1 , Black , White
      Lcd_print Utext , 0 , 27 , 1 , 1 , 1 , Black , White
      Lcd_print Itext , 0 , 36 , 1 , 1 , 1 , Black , White
      Wait 7
   Else
   If Porte.7 = 1 Then Activate_display
   Ltext = Str(gbdriveerror)
   Ltext = "Fehler: " + Ltext
   Lcd_cls
   Lcd_print Titel , 0 , 0 , 1 , 1 , 1 , Yellow , Blue
   Lcd_print "SD-Karte init" , 0 , 18 , 1 , 1 , 1 , Black , White
   Lcd_print Ltext , 0 , 27 , 1 , 1 , 1 , Black , White
   Wait 7
  End If

Readeeprom Startkilometer , 0
Readeeprom Xoffset , 4
Readeeprom Yoffset , 8
'Einige Werte vorbelegen um Fehlerhafte Grafik
'zu vermeiden wenn keine Verbindung besteht
Xa = 74
Ya = 130
Antriebaus = 1
Null_x = Xoffset
Null_y = Yoffset





If Porte.7 = 1 Then Activate_display
Lcd_cls
Lcd_print Titel , 0 , 0 , 1 , 1 , 1 , Yellow , Blue
If Modus = 0 Then
   Lcd_rect 8 , 84 , 124 , 95 , 0 , Black
   Lcd_rect 24 , 125 , 124 , 135 , 0 , Black
   Lcd_rect 8 , 100 , 19 , 160 , 0 , Black
   Lcd_draw 20 , 130 , 22 , 130 , 0 , Black
   Lcd_draw 74 , 136 , 74 , 138 , 0 , Black
   Lcd_draw 0 , 167 , 132 , 167 , 0 , Black
End If

Config Timer2 = Timer , Prescale = 128
Assr.exclk = 0
Assr.as2 = 1

On Timer2 Tick
Enable Timer2
Start Timer2


On Urxc1 Datenempfang
Enable Urxc1
Enable Interrupts
Minmax
Do

If Pind.1 = 0 Then
Waitms 3
   If Pind.1 = 0 Then
      While Pind.1 = 0
      Wend
      Modus = Modus + 1
      If Gbdriveerror <> 0 Then                                                 'Logging nicht zulassen wenn DriveError
            If Modus > 3 Then Modus = 0
      End If

      If Modus <> 3 Then                                                        'nur berechnen wenn die Werte nicht editiert werden

         Sdummy = P_faktor * 10000
         Sdummy = Round(sdummy)
         Pfaktor = Sdummy

         Sdummy = I_faktor * 1000000
         Sdummy = Round(sdummy)
         Ifaktor = Sdummy

         Sdummy = D_faktor * 1000
         Sdummy = Round(sdummy)
         Dfaktor = Sdummy

         Sdummy = Mp_faktor * 10000
         Sdummy = Round(sdummy)
         Mpfaktor = Sdummy

         Sdummy = Mv_faktor * 1000
         Sdummy = Round(sdummy)
         Mvfaktor = Sdummy

         Sdummy = Mpl_faktor * 10000
         Sdummy = Round(sdummy)
         Mplfaktor = Sdummy

         Sdummy = Mvl_faktor * 1000
         Sdummy = Round(sdummy)
         Mvlfaktor = Sdummy

      End If



      If Modus > 4 Then Modus = 0
      If Porte.7 = 1 Then Activate_display
      Lcd_cls
      Lcd_print Titel , 0 , 0 , 1 , 1 , 1 , Yellow , Blue


      If Modus = 4 Then
         Lcd_print "LOGGING" , 30 , 40 , 2 , 1 , 2 , Black , White
         Dateiaktuell = 0
      Else
         If Modus = 0 Then
            Lcd_rect 8 , 84 , 124 , 95 , 0 , Black
            Lcd_rect 24 , 125 , 124 , 135 , 0 , Black
            Lcd_rect 8 , 100 , 19 , 160 , 0 , Black
            Lcd_draw 20 , 130 , 22 , 130 , 0 , Black
            Lcd_draw 74 , 136 , 74 , 138 , 0 , Black
            Lcd_draw 0 , 167 , 132 , 167 , 0 , Black
         End If
      End If
   End If
End If
If Modus <> 4 Then                                                              'Nur wenn nicht geloggt wird
   If U <> 0 Then                                                               'Nur wenn auch schon eine Spannung übertragen wurde
      If Ta <> 0 Then
         Idummy = U                                                             'Nur wenn auch schon ein Ta übertragen wurde
         If Idummy > Umax Then Umax = Idummy
         If Idummy < Umin Then Umin = Idummy
         Idummy = I
         If Idummy > Imax Then Imax = Idummy
         If Idummy < Imin Then Imin = Idummy
         Idummy = Kmh
         If Idummy > Kmhmax Then Kmhmax = Idummy
         If Idummy < Kmhmin Then Kmhmin = Idummy
         Bdummy = Ta
         If Bdummy > Tamax Then Tamax = Bdummy
         Idummy = Ml
         If Idummy > Mlmax Then Mlmax = Idummy
         If Idummy < Mlmin Then Mlmin = Idummy
         Idummy = Mr
         If Idummy > Mrmax Then Mrmax = Idummy
         If Idummy < Mrmin Then Mrmin = Idummy
      End If
   End If
End If


Select Case Modus
Case 0
   If Dateiaktuell = 1 Then
         If Porte.7 = 0 Then Activate_sd
         Close #2
         Dateiaktuell = 0
   End If
   If Porte.7 = 1 Then Activate_display                                         'Standart-Anzeige
   If Pind.0 = 0 Then
      Waitms 3
      If Pind.0 = 0 Then
         While Pind.0 = 0
         Wend
         Sdummy = Meter / 1000
         Sdummy = Sdummy + Kilometer
         Startkilometer = Sdummy
         Writeeeprom Startkilometer , 0
      End If
   End If
   'Spannung und Strom
   Sdummy = U / 100
   Utext = Fusing(sdummy , "#.#")
   Utext = Utext + "V"
   Utext = " " + Utext
   Sdummy = I / 100
   Itext = Fusing(sdummy , "#.#")
   Itext = Itext + "A "
   L1 = Len(utext)
   L2 = Len(itext)
   L1 = L1 + L2
   L2 = 22 - L1
   Ltext = String(l2 , 32)
   Text = Utext + Ltext
   Text = Text + Itext
   Lcd_print Text , 0 , 75 , 1 , 1 , 1 , Black , White
   'Akkuprozente
   Sdummy = Akkuprozente / 10
   Sdummy = 100 - Sdummy
   Text = Fusing(sdummy , "#.#" )
   Text = Text + "% "
   Text = " " + Text
   Farbe = Red
   If Sdummy > 33 Then Farbe = Yellow
   If Sdummy > 66 Then Farbe = Green
   Sdummy = Sdummy * 1.14
   X2 = Sdummy
   X2 = X2 + 10
   X1 = X2 + 1
   Lcd_box X1 , 85 , 123 , 94 , White
   Lcd_box 9 , 85 , X2 , 94 , Farbe
   L1 = Len(text)
   L1 = L1 * 5
   L1 = L1 / 2
   X1 = 66 - L1
   Lcd_print Text , X1 , 98 , 1 , 1 , 1 , Black , White
   'km/h
   Sdummy = Kmh / 10
   Text = Fusing(sdummy , "#.#")
   Text = Text + "km/h "
   Text = " " + Text
   L1 = Len(text)
   Text = Text + " "
   L1 = L1 * 4
   X1 = 66 - L1
   Lcd_print Text , X1 , 12 , 2 , 1 , 2 , Black , White
   'km
   Sdummy = Meter / 1000
   Sdummy = Sdummy + Kilometer
   Text = Fusing(sdummy , "#.#")
   Text = Text + "km  "
   Text = " " + Text
   L1 = Len(text)
   L1 = L1 * 5
   L1 = L1 / 2
   X1 = 66 - L1
   Lcd_print Text , X1 , 46 , 1 , 1 , 1 , Black , White
   'Tageskilometerzähler
   Sdummy = Sdummy - Startkilometer
   Text = Fusing(sdummy , "#.###")
   Text = Text + "km  "
   Text = " " + Text
   L1 = Len(text)
   L1 = L1 * 5
   L1 = L1 / 2
   X1 = 66 - L1
   Lcd_print Text , X1 , 60 , 1 , 1 , 1 , Black , White
   'Ta
   Text = Str(ta)
   Lcd_print Text , 0 , 168 , 1 , 1 , 1 , White , Black
   'Motorleistung
   Utext = Str(ml)
   Utext = Utext + " "
   Utext = " " + Utext
   Itext = Str(mr)
   Itext = Itext + " "
   L1 = Len(utext)
   L2 = Len(itext)
   L1 = L1 + L2
   L2 = 19 - L1
   Ltext = String(l2 , 32)
   Text = Utext + Ltext
   Text = Text + Itext
   Lcd_print Text , 18 , 168 , 1 , 1 , 1 , White , Black
   If Antriebaus = 1 Then
      Sdummy = Null_x
      Sdummy = Sdummy - Xoffset
      Sdummy = Sdummy / 50
      If Sdummy > 49 Then Sdummy = 49
      If Sdummy < -49 Then Sdummy = -49
      Sdummy = Sdummy + 74
      X1 = Int(sdummy)
      Lcd_draw Xa , 126 , Xa , 134 , 0 , White
      Lcd_draw X1 , 126 , X1 , 134 , 0 , Black
      Xa = X1
      Sdummy = Null_y
      Sdummy = Sdummy - Yoffset
      Sdummy = Sdummy / 50
      If Sdummy > 29 Then Sdummy = 29
      If Sdummy < -29 Then Sdummy = -29
      Sdummy = Sdummy + 130
      Y1 = Int(sdummy)
      Lcd_draw 9 , Ya , 18 , Ya , 0 , White
      Lcd_draw 9 , Y1 , 18 , Y1 , 0 , Black
      Ya = Y1
   Else
      Sdummy = Mit_acc_x
      Sdummy = Sdummy / 50
      If Sdummy > 49 Then Sdummy = 49
      If Sdummy < -49 Then Sdummy = -49
      Sdummy = Sdummy + 74
      X1 = Int(sdummy)
      Lcd_draw Xa , 126 , Xa , 134 , 0 , White
      Lcd_draw X1 , 126 , X1 , 134 , 0 , Black
      Xa = X1
      Sdummy = Mit_acc_y
      Sdummy = Sdummy / 50
      If Sdummy > 29 Then Sdummy = 29
      If Sdummy < -29 Then Sdummy = -29
      Sdummy = Sdummy + 130
      Y1 = Int(sdummy)
      Lcd_draw 9 , Ya , 18 , Ya , 0 , White
      Lcd_draw 9 , Y1 , 18 , Y1 , 0 , Black
      Ya = Y1
   End If
Case 1                                                                          'Details
   If Dateiaktuell = 1 Then
         If Porte.7 = 0 Then Activate_sd
         Close #2
         Dateiaktuell = 0
   End If
      If Porte.7 = 1 Then Activate_display
      If Pind.0 = 0 Then
         Waitms 3
            If Pind.0 = 0 Then
            While Pind.0 = 0
            Wend
            If Antriebaus = 1 Then
               Yoffset = Null_y
               Xoffset = Null_x

               Writeeeprom Xoffset , 4
               Writeeeprom Yoffset , 8
            End If
         End If
      End If
      Sdummy = U / 100
      Text = Fusing(sdummy , "#.#")
      Text = "   U= " + Text
      Text = Text + "V"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 9 , 1 , 1 , 1 , Black , White

      Sdummy = I / 100
      Text = Fusing(sdummy , "#.#")
      Text = "   I= " + Text
      Text = Text + "A"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 18 , 1 , 1 , 1 , Black , White

      Sdummy = Kmh / 10
      Text = Fusing(sdummy , "#.#")
      Text = "   V= " + Text
      Text = Text + "km/h"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 27 , 1 , 1 , 1 , Black , White

      Sdummy = Meter / 1000
      Sdummy = Sdummy + Kilometer
      Text = Fusing(sdummy , "#.###")
      Text = "   S= " + Text
      Text = Text + "km  "
      Text = Trimtext(text)
      Lcd_print Text , 0 , 36 , 1 , 1 , 1 , Black , White

      Sdummy = Ml
      Text = Str(ml )
      Text = "  Pl= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 45 , 1 , 1 , 1 , Black , White

      Sdummy = Mr
      Text = Str(mr )
      Text = "  Pr= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 54 , 1 , 1 , 1 , Black , White

      Sdummy = Akkuprozente / 10
      Sdummy = 100 - Sdummy
      Text = Fusing(sdummy , "#.#")
      Text = "Akku= " + Text
      Text = Text + "%"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 63 , 1 , 1 , 1 , Black , White

      Text = Str(mit_acc_x)
      Text = "Roll= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 72 , 1 , 1 , 1 , Black , White

      Text = Str(mit_acc_y )
      Text = "Nick= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 81 , 1 , 1 , 1 , Black , White

      Text = Str(null_x )
      Text = "  R0= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 90 , 1 , 1 , 1 , Black , White

      Text = Str(null_y )
      Text = "  N0= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 99 , 1 , 1 , 1 , Black , White

      Text = Fusing(stellwert , "#.###")
      Text = "  SW= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 108 , 1 , 1 , 1 , Black , White

      Text = Str(gyro_nick )
      Text = "  GN= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 117 , 1 , 1 , 1 , Black , White

      Text = Str(ta )
      Text = "  TA= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 126 , 1 , 1 , 1 , Black , White

      Text = Str(k_sum)
      Text = "Ksum= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 135 , 1 , 1 , 1 , Black , White



Case 2                                                                          'minmax

If Dateiaktuell = 1 Then
         If Porte.7 = 0 Then Activate_sd
         Close #2
         Dateiaktuell = 0
   End If
      If Porte.7 = 1 Then Activate_display
      If Pind.0 = 0 Then
         Waitms 3
            If Pind.0 = 0 Then
            While Pind.0 = 0
            Wend
            Minmax                                                              'Min-Maxwerte auf 0 setzen
         End If
      End If
      Sdummy = Umin / 100
      Text = Fusing(sdummy , "#.#")
      Text = "   Umin= " + Text
      Text = Text + "V"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 9 , 1 , 1 , 1 , Black , White
      Sdummy = Umax / 100
      Text = Fusing(sdummy , "#.#")
      Text = "   Umax= " + Text
      Text = Text + "V"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 18 , 1 , 1 , 1 , Black , White
      '**************************************************
      Sdummy = Imin / 100
      Text = Fusing(sdummy , "#.#")
      Text = "   Imin= " + Text
      Text = Text + "A"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 27 , 1 , 1 , 1 , Black , White
      Sdummy = Imax / 100
      Text = Fusing(sdummy , "#.#")
      Text = "   Imax= " + Text
      Text = Text + "A"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 36 , 1 , 1 , 1 , Black , White
      '**************************************************
      Sdummy = Kmhmin / 10
      Text = Fusing(sdummy , "#.#")
      Text = "   Vmin= " + Text
      Text = Text + "km/h"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 45 , 1 , 1 , 1 , Black , White
      Sdummy = Kmhmax / 10
      Text = Fusing(sdummy , "#.#")
      Text = "   Vmax= " + Text
      Text = Text + "km/h"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 54 , 1 , 1 , 1 , Black , White
      '**************************************************


      Text = Str(mlmin )
      Text = "  Plmin= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 63 , 1 , 1 , 1 , Black , White

      Text = Str(mlmax )
      Text = "  Plmax= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 72 , 1 , 1 , 1 , Black , White
      '**************************************************

      Text = Str(mlmin )
      Text = "  Prmin= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 81 , 1 , 1 , 1 , Black , White

      Text = Str(mrmax )
      Text = "  Prmax= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 90 , 1 , 1 , 1 , Black , White
      '**************************************************


      Text = Str(tamax )
      Text = "  TAmax= " + Text
      Text = Trimtext(text)
      Lcd_print Text , 0 , 99 , 1 , 1 , 1 , Black , White

Case 3
If Dateiaktuell = 1 Then
         If Porte.7 = 0 Then Activate_sd
         Close #2
         Dateiaktuell = 0
   End If
      If Porte.7 = 1 Then Activate_display



      If Pind.4 = 0 Then
         Waitms 3
            If Pind.4 = 0 Then
            While Pind.4 = 0
            Wend
            If Editzeile > 1 Then Decr Editzeile
         End If
      End If




      If Pind.7 = 0 Then
         Waitms 3
            If Pind.7 = 0 Then
            While Pind.7 = 0
            Wend
            If Editzeile < 7 Then Incr Editzeile
         End If
      End If


       If Pind.5 = 0 Then
         Waitms 3
            If Pind.5 = 0 Then
            While Pind.5 = 0
            Wend
            Select Case Editzeile
            Case 1
            Decr Pfaktor
            Case 2
            Decr Ifaktor
            Case 3
            Decr Dfaktor
            Case 4
            Decr Mpfaktor
            Case 5
            Decr Mvfaktor
            Case 6
            Decr Mplfaktor
            Case 7
            Decr Mvlfaktor
            End Select
         End If
      End If


      If Pind.6 = 0 Then
         Waitms 3
            If Pind.6 = 0 Then
            While Pind.6 = 0
            Wend
            Select Case Editzeile
            Case 1
            Incr Pfaktor
            Case 2
            Incr Ifaktor
            Case 3
            Incr Dfaktor
            Case 4
            Incr Mpfaktor
            Case 5
            Incr Mvfaktor
            Case 6
            Incr Mplfaktor
            Case 7
            Incr Mvlfaktor
            End Select
         End If
      End If

      If Pind.0 = 0 Then
         Waitms 3
            If Pind.0 = 0 Then
            While Pind.0 = 0
            Wend
            P_faktor = Pfaktor
            P_faktor = P_faktor / 10000

            I_faktor = Ifaktor
            I_faktor = I_faktor / 1000000

            D_faktor = Dfaktor
            D_faktor = D_faktor / 1000

            Mp_faktor = Mpfaktor
            Mp_faktor = Mp_faktor / 10000

            Mv_faktor = Mvfaktor
            Mv_faktor = Mv_faktor / 1000

            Mpl_faktor = Mplfaktor
            Mpl_faktor = Mpl_faktor / 10000

            Mvl_faktor = Mvlfaktor
            Mvl_faktor = Mvl_faktor / 1000

            Sichern = 1
            Lcd_cls
            Lcd_print Titel , 0 , 0 , 1 , 1 , 1 , Yellow , Blue
            Wait 2
         End If
      End If



      Text = "   Regelparameter:"
      Text = Trimtext(text)
      Lcd_print Text , 0 , 18 , 1 , 1 , 1 , Black , White
      '**************************************************



      Text = Str(pfaktor)
      Text = "   P = " + Text
      Text = Trimtext(text)
      If Editzeile = 1 Then
      Lcd_print Text , 0 , 36 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
      Lcd_print Text , 0 , 36 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If


      Text = Str(ifaktor)
      Text = "   I = " + Text
      Text = Trimtext(text)
      If Editzeile = 2 Then
      Lcd_print Text , 0 , 46 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
       Lcd_print Text , 0 , 46 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If


      Text = Str(dfaktor)
      Text = "   D = " + Text
      Text = Trimtext(text)
      If Editzeile = 3 Then
      Lcd_print Text , 0 , 56 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
      Lcd_print Text , 0 , 56 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If



      Text = Str(mpfaktor)
      Text = "  MP = " + Text
      Text = Trimtext(text)
      If Editzeile = 4 Then
      Lcd_print Text , 0 , 66 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
      Lcd_print Text , 0 , 66 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If


      Text = Str(mvfaktor)
      Text = "  MV = " + Text
      Text = Trimtext(text)
      If Editzeile = 5 Then
      Lcd_print Text , 0 , 76 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
      Lcd_print Text , 0 , 76 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If


      Text = Str(mplfaktor)
      Text = " MPL = " + Text
      Text = Trimtext(text)
      If Editzeile = 6 Then
      Lcd_print Text , 0 , 86 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
      Lcd_print Text , 0 , 86 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If


      Text = Str(mvlfaktor)
      Text = " MVL = " + Text
      Text = Trimtext(text)
      If Editzeile = 7 Then
      Lcd_print Text , 0 , 96 , 1 , 1 , 1 , Black , Green
      '**************************************************
      Else
      Lcd_print Text , 0 , 96 , 1 , 1 , 1 , Black , White
      '**************************************************
      End If

      If Transferok = 1 Then
      Lcd_print "   Transfer OK" , 0 , 106 , 1 , 1 , 1 , Black , White
      Transferok = 0
      End If


Case 4
If Porte.7 = 0 Then Activate_sd
If Antriebaus = 0 Then                                                          'Motoren laufen ->Werte Loggen
   If Dateiaktuell = 0 Then                                                     'Keine Datei geöffnet

      Lname = 1
      Dateilaenge = 1                                                           'Nächste Schleife einmal erzwingen
      While Dateilaenge > 0
         Dateiname = Str(lname)
         Dateiname = Dateiname + ".log"
         '0 voranstellen, damit sich die Dateien besser sortieren lassen.
         While Len(dateiname) < 12
            Dateiname = "0" + Dateiname
         Wend
         Dateilaenge = Filelen(dateiname)

         Incr Lname
      Wend
      If Porte.7 = 1 Then Activate_display
      Lcd_print Dateiname , 25 , 75 , 1 , 1 , 1 , Black , White
      If Porte.7 = 0 Then Activate_sd
      Dateiaktuell = 1
      Loopcount = 0
      _sec = 0
      _min = 0
      _hour = 0
      Zeit = "00:00:00"
      Open Dateiname For Append As #2
      Logtext = "Zeit;Loopcount;U;I;Nick;GyroNick;Stellwert;Roll;Pl;Pr;Ksum;V"
      Print #2 , Logtext
   End If

   If Loopcount <> Aloopcount Then
         Ulog = U / 100
         Ilog = I / 100
         Kmhlog = Kmh / 10
         Logtext = Zeit
         Logtext = Logtext + ";"
         Logtext = Logtext + Str(loopcount)
         Logtext = Logtext + ";"
         Logtext = Logtext + Fusing(ulog , "#.#")
         Logtext = Logtext + ";"
         Logtext = Logtext + Fusing(ilog , "#.#")
         Logtext = Logtext + ";"
         Logtext = Logtext + Str(mit_acc_y)
         Logtext = Logtext + ";"
         Logtext = Logtext + Str(gyro_nick)
         Logtext = Logtext + ";"
         Logtext = Logtext + Fusing(stellwert , "#.###")
         Logtext = Logtext + ";"
         Logtext = Logtext + Str(mit_acc_x)
         Logtext = Logtext + ";"
         Logtext = Logtext + Str(ml)
         Logtext = Logtext + ";"
         Logtext = Logtext + Str(mr)
         Logtext = Logtext + ";"
         Logtext = Logtext + Fusing(k_sum , "#.##")
         Logtext = Logtext + ";"
         Logtext = Logtext + Fusing(kmhlog , "#.#")

         Print #2 , Logtext

         Aloopcount = Loopcount
   End If


Else

      Close #2
      Dateiaktuell = 0


End If


End Select

Loop



'********************************************************************************************
Datenempfang:
$asm
PUSH R24;
IN R24, SREG;
PUSH R24;
LDS R24,{EP1}
STS {EP0},R24
LDS R24,{EP2}
STS {EP1},R24
LDS R24,{EP3}
STS {EP2},R24
LDS R24,{EP4}
STS {EP3},R24
LDS R24,{EP5}
STS {EP4},R24
IN  R24,UDR1
STs {EP5},R24
POP R24;
Out Sreg , R24;
POP R24;
$end Asm
'<Serielle Telegramme abarbeiten>
If Ep0 = 13 Then
   If Ep5 = 13 Then
         'Disable Urxc1
         $asm
         lds R24,{EP1}
         LDs R25,{EP2}
         add R24,R25
         LDs R25,{EP3}
         add R24,R25
         STS {Bdummy},R24
         $end Asm
         If Bdummy = Ep4 Then                                                   'Die Prüfsumme stimmt
            Select Case Index
            Case 1                                                              'Spannung
               U = Iwert

            Case 2                                                              'Strom
               I = Iwert
            Case 3                                                              'km/h
               Kmh = Iwert
            Case 4                                                              'Ta und Antriebaus
               Antriebaus = Ep2
               Ta = Ep3
            Case 5                                                              'Akkuprozente
               Akkuprozente = Iwert
            Case 6                                                              'Kilometer
               Kilometer = Wwert
            Case 7                                                              'Meter
               Meter = Iwert
            Case 8
               Maccx1 = Ep2
               Maccx2 = Ep3
            Case 9
               Maccx3 = Ep2
               Maccx4 = Ep3
               Mit_acc_x = Maccx
            Case 10
               Maccy1 = Ep2
               Maccy2 = Ep3
            Case 11
               Maccy3 = Ep2
               Maccy4 = Ep3
               Mit_acc_y = Maccy
            Case 12
               Nullx1 = Ep2
               Nullx2 = Ep3
            Case 13
               Nullx3 = Ep2
               Nullx4 = Ep3
               Null_x = Nullx
            Case 14
               Nully1 = Ep2
               Nully2 = Ep3
            Case 15
               Nully3 = Ep2
               Nully4 = Ep3
               Null_y = Nully
            Case 16
               Sw1 = Ep2
               Sw2 = Ep3
            Case 17
               Sw3 = Ep2
               Sw4 = Ep3
               Stellwert = Sw
            Case 18
               Gyronick1 = Ep2
               Gyronick2 = Ep3
            Case 19
               Gyronick3 = Ep2
               Gyronick4 = Ep3
               Gyro_nick = Gyronick
            Case 20
               Kksum1 = Ep2
               Kksum2 = Ep3
            Case 21
               Kksum3 = Ep2
               Kksum4 = Ep3
               K_sum = Kksum
            Case 255
               If Sichern = 1 Then
                  $asm
                  lds R24,{P_f1}
                  LDs R25,{P_f2}
                  add R24,R25
                  LDs R25,{P_f3}
                  add R24,R25
                  LDs R25,{P_f4}
                  add R24,R25
                  LDs R25,{I_f1}
                  add R24,R25
                  LDs R25,{I_f2}
                  add R24,R25
                  LDs R25,{I_f3}
                  add R24,R25
                  LDs R25,{I_f4}
                  add R24,R25
                  LDs R25,{D_f1}
                  add R24,R25
                  LDs R25,{D_f2}
                  add R24,R25
                  LDs R25,{D_f3}
                  add R24,R25
                  LDs R25,{D_f4}
                  add R24,R25
                  LDs R25,{Mp_f1}
                  add R24,R25
                  LDs R25,{Mp_f2}
                  add R24,R25
                  LDs R25,{Mp_f3}
                  add R24,R25
                  LDs R25,{Mp_f4}
                  add R24,R25

                  LDs R25,{Mv_f1}
                  add R24,R25
                  LDs R25,{Mv_f2}
                  add R24,R25
                  LDs R25,{Mv_f3}
                  add R24,R25
                  LDs R25,{Mv_f4}
                  add R24,R25

                  LDs R25,{Mpl_f1}
                  add R24,R25
                  LDs R25,{Mpl_f2}
                  add R24,R25
                  LDs R25,{Mpl_f3}
                  add R24,R25
                  LDs R25,{Mpl_f4}
                  add R24,R25

                  LDs R25,{Mvl_f1}
                  add R24,R25
                  LDs R25,{Mvl_f2}
                  add R24,R25
                  LDs R25,{Mvl_f3}
                  add R24,R25
                  LDs R25,{Mvl_f4}
                  add R24,R25

                  STS {Bdummy},R24
                  $end Asm
                  Open "COM2:" For Binary As #1
                  Print #1 , Chr(p_f1) ; Chr(p_f2) ; Chr(p_f3) ; Chr(p_f4) ;
                  Print #1 , Chr(i_f1) ; Chr(i_f2) ; Chr(i_f3) ; Chr(i_f4) ;
                  Print #1 , Chr(d_f1) ; Chr(d_f2) ; Chr(d_f3) ; Chr(d_f4) ;
                  Print #1 , Chr(mp_f1) ; Chr(mp_f2) ; Chr(mp_f3) ; Chr(mp_f4) ;
                  Print #1 , Chr(mv_f1) ; Chr(mv_f2) ; Chr(mv_f3) ; Chr(mv_f4) ;
                  Print #1 , Chr(mpl_f1) ; Chr(mpl_f2) ; Chr(mpl_f3) ; Chr(mpl_f4) ;
                  Print #1 , Chr(mvl_f1) ; Chr(mvl_f2) ; Chr(mvl_f3) ; Chr(mvl_f4) ;

                  Print #1 , Chr(bdummy) ; Chr(13) ;
                  Close #1
                  Transferok = 1
                  Sichern = 0
               End If
            Case 76                                                             'Linker Motor
               Ml = Ep3
               If Ep2 = 0 Then Ml = Ml * -1
               Incr Loopcount
            Case 82                                                             'Rechter Motor
               Mr = Ep3
               If Ep2 = 0 Then Mr = Mr * -1

            End Select

            If Modus < 3 Then
               Select Case Index
                Case 22
                  P_f1 = Ep2
                  P_f2 = Ep3
               Case 23
                  P_f3 = Ep2
                  P_f4 = Ep3
               Case 24
                  I_f1 = Ep2
                  I_f2 = Ep3
               Case 25
                  I_f3 = Ep2
                  I_f4 = Ep3
               Case 26
                  D_f1 = Ep2
                  D_f2 = Ep3
               Case 27
                  D_f3 = Ep2
                  D_f4 = Ep3
               Case 28
                  Mp_f1 = Ep2
                  Mp_f2 = Ep3
               Case 29
                  Mp_f3 = Ep2
                  Mp_f4 = Ep3

               Case 30
                  Mv_f1 = Ep2
                  Mv_f2 = Ep3
               Case 31
                  Mv_f3 = Ep2
                  Mv_f4 = Ep3

               Case 32
                  Mpl_f1 = Ep2
                  Mpl_f2 = Ep3
               Case 33
                  Mpl_f3 = Ep2
                  Mpl_f4 = Ep3

               Case 34
                  Mvl_f1 = Ep2
                  Mvl_f2 = Ep3
               Case 35
                  Mvl_f3 = Ep2
                  Mvl_f4 = Ep3







               End Select
            End If
         End If
         'Enable Urxc1
   End If
End If
'</Serielle Telegramme abarbeiten>
Return
'********************************************************************************************


Sub Activate_display
   Reset Porte.7                                                                'remove SD Card from bus, need to be "Reset PortF.7" at board D071x
   Config Spi = Hard , Interrupt = Off , Data Order = Msb , Master = Yes , Polarity = Low , Phase = 0 , Clockrate = 4 , Noss = 1       'different to what the SD card needs
   Set Spsr.spi2x                                                               'hidden parameter: doubles the speed of the SPI output
   Spiinit

End Sub



Sub Activate_sd
    Set Lcd_port.lcd_cs                                                         'Deactivate Display
    Config Spi = Hard , Interrupt = Off , Data Order = Msb , Master = Yes , Polarity = High , Phase = 1 , Clockrate = 4 , Noss = 1       'different to what the display needs
    Reset Spsr.spi2x                                                            'no double speed. You may remove this line to try out with your card. It may work.
    Spiinit
    Set Porte.7                                                                 'connects SD Card to bus, need to be "Set PortF.7" at board D071x
End Sub

Function Trimtext(t As String) As String
If Len(t) > 22 Then T = Left(t , 22)
While Len(t) < 22
T = T + " "
Wend
End Function

Sub Minmax
Umin = 10000
Umax = -10000
Imin = 0
Imax = 0
Kmhmin = 0
Kmhmax = 0
Mlmax = 0
Mlmin = 0
Mrmax = 0
Mrmin = 0
Tamax = 0
End Sub






Tick:
Incr _sec
If _sec > 59 Then
_sec = 0
Incr _min
End If
If _min > 59 Then
_min = 0
Incr _hour
End If
Zeit = Time$
Return
Getdatetime:

Return



$include "Glcd21_display3000.bas"
$include "Glcd21_fonts.bas"
'Dummy Data um Fehlermeldungen bei der Kompilierung der Standardroutinen zu vermeiden
'Die Tabelle wird dann bei Nutzung eines indizierten Grafikdatei mit "echten" Daten ausgetauscht
Colortable:
Data 0

Akkuladestecker

Um die 4 12V Akkus zu laden wird ein spezieller Stecker verwendet. Durch diesen werden die 4 Akkus paralell geschaltet und können danach mit einem normalen KFZ-Ladegerät geladen werden. Für den Fahrbetrieb wird der Ladestecker abgezogen und der Fahrstecker gesteckt. Dadurch werden 3 der Akkus in Reihe geschaltet für die Versorgung der Motoren mit 36V. Einer der Akkus bleibt alleine, nur am Minuspol mit den anderen verbunden. Aus diesem Akku werden die 5V für die Steuerung gewonnen. Ein extra Akku nur für die 5V-Seite wurde vor allem aus Angst vor EMV-Problemen gewählt. Auserdem war aus Symetriegründen sowiso noch Platz für diesen Akku.

Mechanik

Wie oben erwähnt musste die Mechanik mehrere Kriterien erfüllen. Zum einen sollte fast nichts hinter den Rädern hervorstehen, zum anderen sollte alles noch mit den vorhandenen Werkzeugen herstellbar sein. Das gerade letzteres bei jedem anders ist, wird sich jeder seinen eigenen mechanischen Aufbau konstruieren müssen. Ein Styropormodell oder ein 3D-Modell im Rechner sollte man auf jeden Fall erstellen bevor man sich Teile zusammenschweisst, die hinterher in keine Mülltonne passen. Unten gezeigte Lösung muss noch lange nicht das beste sein. Sie stellt nur den idealen Kompromiss zwischen den von mir aufgestellten Anforderungen und meinem vorhandenen Maschinenpark dar.

Um die Mobilitätshilfenverordnung einzuhalten wurde noch eine Beleuchtung, Reflektoren, Klingel und ein äusseres Alublech (EMV) nachgerüstet.


Weitere Bilder folgen.

Motorumbau

Achtung! Bauen Sie sich zum Zerlegen der Motoren unbedingt eine Hilfsvorrichtung. Vor allem beim Zusammenbauen wird das Blechpaket mit der Wicklung derart stark von den Magneten in den Läufer hineingezogen, daß es im ungünstigen Fall einen oder mehrere Finger abtrennen könnte. Auch der Motor kann beschädigt werden wenn die Teile plötzlich zusammenpatschen.

Die verwendeten Motoren können im Anlieferzustand nicht verwendet werden. Mit den 3 verbauten Hallsensoren ist nur eine normale Blockkommutierung möglich. Diese hat jedoch den Nachteil, daß im Moment des Kommutierens das Drehmoment stark einbricht. Beim Anfahren gegen ein kleines Hindernis hat das den Effekt, daß der Motor immer wenn er Kommutieren sollte wieder leicht zurückrollt und dadurch erneut kommutieren muss. Das Ergebnis ist ein Quietschen ohne nennenswertes Drehmoment. Als Abhilfe werden drei weitere Hallsensoren verbaut, die genau die Zwischenpositionen der Bisherigen erfassen. Die drei Sensoren werden mit einer kleinen Platine im Motor zu einem Signal zusammengefasst. Es gibt jetzt nicht nur 6 Positionen pro elektrischer Umdrehung sondern 12. (30° el.) Dadurch kann der Motor mit einer Sinusspannung angesteuert werden. Der Drehmomentripple ist soweit reduziert, daß er sich nicht mehr negativ auswirkt. Als netter Nebeneffekt wird der Motor auch noch deutlich leiser.

In den beiden Grafiken ist die Spannungsvorgabe an den Motor dargestellt. X-Achse sind die 12 Positionen pro elektrischer Umdrehung, Y-Achse die Spannung in Prozent. Wenn jetzt zum Beispiel die 4 Sensorsignale besagen, daß der Motor in Position 7 steht, dann werden die einzelnen Wicklungen wie folgt versorgt:

Blau=100% von der PWM-Vorgabe

Lila=37% von der PWM-Vorgabe

Gelb=13% von der PWM-Vorgabe


Da sich der magnetische Fluss beim kommutieren jetzt nicht mehr so extem ändern muss wie bei normaler Blockkommutierung fällt auch der Drehmomenteinbruch entsprechend geringer aus. In der Praxis hat sich gezeigt, daß das Ergebnis der obigen Rechnung bei kleiner PWM-Vorgabe oft 0 ergibt. Darum wird in der Praxis die etwas eckigere Kuve verwendet.

Oft wird angemerkt, daß diese Vorgehensweise sehr ungewöhnlich und nicht "Standart" sei. Macht aber nix. Es funktioniert prächtig! Ohne diesen Trick sind solche Motoren für den Zweck unbrauchbar oder nur mit Getriebe verwendbar. Ein Getriebe benötigt jedoch Platz und macht Geräusche. Auserdem sind BLDC-Motoren mit passendem Getriebe noch schwerer zu bekommen oder für Normalverdiener unbezahlbar. Drei zusätzliche Hallsensoren und etwas Hirnschmalz sind da wesentlich billiger.
Hier ein paar Bilder vom Umbau:

Rechtliches

Wie der Segway unterliegt auch dieses Fahrzeug der Mobilitätshilfenverordnung. Bei der Entwicklung wurde darauf geachtet, daß alle Forderungen der Verordnung eingehalten werden. Bei fast allen Anforderungen konnte das auch selbst nachgeprüft werden. Der einzige Punkt, der nicht selbst geprüft werden konnte, ist die Prüfung auf elektromagnetische Verträglichkeit. Da sich aber alle elektrischen Vorgänge in einem mittlerweile komplett geschlossenen Aluminiumgehäuse abspielen, sind hier keine Probleme zu erwarten. Eine Einzelbetriebserlaubnis wurde bisher noch nicht beantragt.

Bezugsquellen

Da die Beschaffung geeigneter Motoren für einige Leser offensichtlich ein Problem darstellt möchte ich hier meine Quelle nennen. Hier soll keine Aussage über die Qualität der angebotenen Motoren getroffen werden. Die könnte von "gut" bis "bescheiden" variieren.

Yongkang Success Permanent Magnetic Motor Manufactory
DeVi-Comfort BV

Weitere Quellen werden hinzugefügt wenn eine erfolgreiche Bestellung gemeldet wird.

Ausblick

Am derzeitigen Design wird nicht mehr weiterentwickelt. Das ist gut, so wie es ist. Dennoch ist dies kein totes Projekt. Derzeit laufen die Tests der nächsten Generation. Diese wird technisch um einiges aufwendiger sein als die bisherige.

Hier einige Eckpunkte zur nächsten Generation:

1. 2kW Motoren

2. Lifepo-Akkus mit ca. 50V,10Ah

3. Ladegerät fest eingebaut

4. Akku-Balancer mit Schnittstelle zur Hauptsteuerung

5. BMS über die Hauptsteuerung

6. Tacho mit Touchscreen

7. Hauptsteuerung mit xmega

8. Digitale Sensoren (MPU6050)

9. Neues Gehäuse, weniger als 10cm hoch.

10. Höhere Maximalgeschwindigkeit. (ca. 30km/h)

Hier ein Video von den ersten Tests: Video

Weblinks


LiFePO4 Speicher Test