PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] Hallsensoren und Atmega2560 - Richtungs- und Längenmessung mittels Interrupts



plastikboot
08.06.2013, 13:04
Hallo AVRler,

ich habe ein mittlerweile sehr nerviges Problem und komme nicht weiter: ich will mittels zweier Hallsensoren TLE4905L (Unipolar) eine Seilrolle abfragen nach Richtung und Länge, d.h. über die Rolle läuft ein Kabel und ich will immer sehen wieviel Kabel ist gerade draußen.

Die Schaltung an sich funktioniert sehr gut - die Hallsensoren sind nach Datenblatt mit Widerständen und Kondensatoren abgefedert und das Oszi, sowie die angeschlossenen LEDs zeigen mir sauber an, wie die Zustände wechseln. Mit dem angehängten Code soll nun bei jedem CHANGE je ein Interrupt für den einen Sensor und ein Interrupt für den anderen Sensor ausgelöst werden. Damit kann ich dann die Richtung und die Länge bestimmen - der Magnet spricht zuerst den ersten, dann beide und zum Schluss des Durchgangs nur den zweiten an. Die LEDs zeigen das auch. Ein Durchgang wäre also 00 - 10 - 11 - 01 - 00 - in die andere Richtung dann eben umgekehrt.

Das Problem ist nun, das das mit den Interrupts schon klappt, aber leider in keiner Weise kalkulierbar - einmal löst er zweimal aus, dann wieder nur einmal, dann nur steigend und nicht fallend. Beim Durchmessen mittels Oszi siehen die Signaleingänge aber perfekt aus - beide Hallsensoren wechseln brav von 0 auf 4,94V und wieder zurück.
Die beiden Printbefehle machen den Interrupt natürlich langsamer (sind auch nur fürs debugging drin). Ich kann auch ganz langsam drehen und es klappt irgendwie nicht. Ich habe auch schon mal die typischen Changeinterrupts PCINT ausprobiert - gleiches Phänomen.

Ich habe auch schon überlegt das ganze über Timer zu machen und einfach den Zustand der beiden Sensoren abzufragen. Die Trommel sollte nachher jedoch auch noch per PWM angesteuert werden und mit einem Display über RS485 kommunizieren - irgendwie wären mir die Interrupts aber lieber, da so weit weniger Last anfällt - die Rolle dreht mit maximal 180 Umdrehungen pro Minute.


Hoffe jemand kann helfen. Der Code ist auch als Datei angehängt.

Gruss

Plastikboot


CODE:

'################################################# ##
'Modul Drehgeber Starfishsidescanwinde
'mit Modul RN-Mega2560
'für
'RoboterNetz Board RN-Mega2560 ab Version 1.0
'
'################################################# #############



'###################### Standards ##############################################
'Diese Anweisung setzt die Fusebits automatisch korrekt für Atmega 2560
' Syntax $PROG LB, FB , FBH , FBX
$prog , 255 , &B11011001 , 'Quarz an / Teiler aus / Jtag aus
$regfile = "m2560def.dat" 'Atmega 2560 einstellen
$loadersize = 512
$hwstack = 128
$framesize = 128
$swstack = 128
$crystal = 16000000 'Quarzfrequenz
$baud = 57600
Config Single = Scientific , Digits = 2


'######################ENDE STANDARDS ##########################################


'-------------------------------------------------------------------------------


'###################### Variablendefinitionen ##################################

Dim Zustandhallsensor1 As Bit ' Erfasst den Zustand 0/1 des Hallsensors1
Dim Zustandhallsensor2 As Bit ' Erfasst den Zustand 0/1 des Hallsensors2
Dim Hallsensordurchlauf As String * 2 ' Gibt Auskunft über den aktuellen Stand der Hallsensoren

'Diese Variablen dienen zur Erkennung eines vollen Durchlaufs
Dim Durchgang1 As Bit ' Wenn Hallsensor 1 auslöst, wird hier vermerkt mit 1
Dim Durchgang2 As Bit ' Wenn Hallsensor 2 auslöst, wird hier vermerkt mit 1
Dim Umdrehungen As Integer ' Anzahl der Drehungen (geteilt durch 3 sind die Volldrehungen)

Dim Umlenkrollenumfang As Word ' Rollenumfang in mm!
Dim Kabelmeter As Single ' Ausgegebene Kabelmeter gerundet

'##################### ENDE Variabelndefinitionen ##############################


'-------------------------------------------------------------------------------


'###################### DEBUG-Variablen ########################################

Dim Vorher As Integer ' Um RS232 im Debug zu entlasten

Vorher = 0

'##################### ENDE DEBUGVariablen####### ##############################

'-------------------------------------------------------------------------------

'##################### DEBUGGING einschalten ###################################
' dieser Bereich kann bei funktionierendem Programm auskommentiert werden
' nicht löschen, dann man weiß ja nie

'Debuggervariablen
Dim Hallsensor1interrupts As Word
Dim Hallsensor2interrupts As Word


' LED auf Platine definieren
Config Pind.5 = Output ' Schaltet die LED auf der Atmega Platine ein!
Led Alias Portd.5 ' Gibt dem Port D5 den Aliasnamen LED
' Die LED kann mit Led = 1 eingeschaltet werden und mit Led = 0 aus

'USB anschluss auf Platine als COM4 definieren für DEBUGGING
Config Com4 = 9600 , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0
Open "com4:" For Binary As #4 'USB Buchse

'##################### ENDE DEBUGGING einschalten ##############################


'-------------------------------------------------------------------------------


'-------------------------------------------------------------------------------


'##################### CONFIG Interrupts für Hallsensoren ######################

Hallsensor1 Alias Porte.4 ' Hallsensor 1 definieren an PortE4 JP1Pin6 Int4
Config Hallsensor1 = Input
On Int4 Hallsensor1change ' Bei Change auf Routine Hallsensor1change springen
Hallsensor1 = 1
Config Int4 = Change ' Change um beide Flanken zu erkennen!!!!
Enable Int4

Hallsensor2 Alias Porte.6 ' Hallsensor 2 definieren an PortE6 JP1Pin8 Int6
Config Hallsensor2 = Input ' Bei Change auf Routine Hallsensor1change springen
On Int6 Hallsensor2change
Hallsensor2 = 1
Config Int6 = Change
Enable Int6

'Interrupts einschalten

Enable Interrupts

'##################### ENDE Config Interrupts für Hallsensoren #################


'-------------------------------------------------------------------------------

'##################### Preprogramm Parameter einstellen ########################
Print #4 , "Programmstart"
Print #4 , ""
Print #4 , ""

Gosub Checkhallsensorstate

Umdrehungen = 0 ' Anzahl der Umdrehungen auf 0 setzen
Umlenkrollenumfang = 546 'ACHTUNG WERT in mm ohne Komma


'##################### ENDE Preprogramm Parameter einstellen ###################


'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------


'##################### PROGRAMMSTART ###########################################

Do

If Umdrehungen <> Vorher Then

Kabelmeter = Umlenkrollenumfang * Umdrehungen
Kabelmeter = Kabelmeter / 1000
Vorher = Umdrehungen
End If

Loop

End

'#################### PROGRAMMENDE #############################################


'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------
'-------------------------------------------------------------------------------


'#################### Hallsensoren Interruptbehandlung #########################
Hallsensor1change: ' Hallsensor 1 Auswertung

Select Case Hallsensordurchlauf

'Drehung nach rechts anfang
Case "00" :
Hallsensordurchlauf = "10"
Durchgang1 = 1

'Drehung nach links
Case "10" :
Hallsensordurchlauf = "00"
Durchgang1 = 0
If Durchgang2 = 1 Then
Umdrehungen = Umdrehungen - 1
Durchgang2 = 0
End If

'Drehung über die Mitte hinaus
Case "11" :
Hallsensordurchlauf = "01"

'Drehung auf Mitte
Case "01" :
Hallsensordurchlauf = "11"
End Select
Print #4 , "1 - " ; Pine.4 ; " " ; Pine.6 ; " " ; Hallsensordurchlauf
Return



Hallsensor2change: ' Hallsensor 2 Auswertung


Select Case Hallsensordurchlauf
'Drehung nach links anfang
Case "00" :
Hallsensordurchlauf = "01"
Durchgang2 = 1

'Drehung nach rechts
Case "01" :
Hallsensordurchlauf = "00"
Durchgang2 = 0
If Durchgang1 = 1 Then
Umdrehungen = Umdrehungen + 1
Durchgang1 = 0
End If

'Drehung über die Mitte hinaus
Case "11" :
Hallsensordurchlauf = "10"

'Drehung auf Mitte
Case "10" :
Hallsensordurchlauf = "11"

End Select
Print #4 , "2 - " ; Pine.4 ; " " ; Pine.6 ; " " ; Hallsensordurchlauf
Return

'################################################# ##############################

'Checkhallsensorstate um Ungenauigkeiten wieder auszubügeln

'################################################# ##############################

Checkhallsensorstate:

' Abfrage des aktuellen Zustandes der Hallsensoren
' und setzen der Hallsensordurchlauf Variable auf den aktuellen Zustand

Zustandhallsensor1 = Pine.4
Zustandhallsensor2 = Pine.6

Print #4 , Pine.4 ; " " ; Pine.6
Print #4 , ""

If Zustandhallsensor1 = 1 And Zustandhallsensor1 = 1 Then
Hallsensordurchlauf = "00"
End If
If Zustandhallsensor1 = 0 And Zustandhallsensor2 = 1 Then
Hallsensordurchlauf = "10"
End If
If Zustandhallsensor1 = 1 And Zustandhallsensor2 = 0 Then
Hallsensordurchlauf = "01"
End If
If Zustandhallsensor1 = 0 And Zustandhallsensor2 = 0 Then
Hallsensordurchlauf = "11"
End If

Return

Besserwessi
08.06.2013, 17:04
Die Lösung mit dem Interrupt von Timer, also mit einer festen Zeit zum Abtasten würde ich vorziehen. Das mit dem Pinchange Interrupt geht auch, aber bei kleinen Störungen bekommt man da ggf. schnell hintereinander mehrere Interrupts - für den kurzen Zeitraum sind dann auch schon mal 3 Interrupts in kurzer Zeit möglich wo der Timer nur 1 hätte. Falls die 3 Interrupts sehr schnell kommen, gibt es dabei ggf. sogar Zählfehler. Damit ist der worst case mit dem Pinchange Interrupt ungünstiger als mit dem Timer.

Die Programmierung mit einem Case über einen String ist auch eher wenig Effektiv. Günstiger wäre es da 2 Bits in einem Byte zu nutzen. Wenn die Eingänge günstig liegen, geht es z.B. mit Zahlen von 0 bis 3 und dann mit einer konstanten Tabelle statt dem Case Statement. Die jetzige Lösung geht auch deshalb nicht gut weil das Auslesen der Sensoren noch im Hauptpogramm stattfindet, und nicht im Interrupt.

plastikboot
08.06.2013, 21:39
Vielen Dank für die Einschätzung! Ich dachte die Interrupts wären eine 100%ige Sache, aber da habe ich mich wohl getäuscht. Ich kann immer noch nicht einschätzen wie schnell so ein Atmega wirklich ist, da er ja sobald man etwas über RS232 anschaut gleich richtig langsam wird. Würde denn ein Timer, plus PWM und einer Übertragung von 3x RS232 noch hinhauen oder bin ich da schon eher an der Grenze des machbaren? Die RS232 übermitteln nicht viel - lediglich 6 Zeichen pro Sekunde und das eben 3x.

Besserwessi
08.06.2013, 22:53
Mit der Print-ausgabe in der ISR wird er µC hier extrem gebremst. Sonst sind die AVRs schon recht schnell, allerdings ist der BASCOM Compiler bei den Interrupts relativ langsam - trotzdem sollte es für den Decoder noch locker ausreichen, auch mit 1 MHz Takt. So als grober Vergleichswert bei 10 MHz Takt etwa 20 mal schneller als z.B. die alten Homecomputer wie C64, Apple 2, oder etwa die Geschwindigkeit der ersten PCs, oder fast wie damals ein Amiga, Atrai ST oder die ersten Macs. Im Simulator von BASCOM wird die Laufzeit mit angezeigt, wenn man wissen will wie lange der Code wirklich braucht.

Etwas schneller als 6 Zeichen je sekunde sollten mit der RS232 schon drin sein, vor allem mit einer so hohen Baudrate. Mit 9600 Baud rund 900 Bytes je Sekunde drin. Ein gerne gemachter Fehler ist es aber den Takt auf 1 MHz zu lassen und nur 16 MHz im Programm einzutragen. Dann läuft das Programm mit 1/16 der erwarteten Geschwindigkeit.

Das PWM Signal braucht mit HW Unterstützung gar keine extra Rechenzeit - das geht in der Regel für 2-6 Kanäle. Was etwas kritisch werden könnte sind 3 RS232 Kanäle, sofern der µC nicht schon 2 mal UART in Hardware bietet. Zumindest wenn man 2 mal ohne die HW-Unterstützung empfangen soll, wird es kompliziert. Der Mega2560 bietet da schon reichlich Hardware Unterstützung an. Allerdings kann eine hohe Baudrate ein Problem werden wenn die Quarzfrequenz nicht passt - mit einem passenden Quarz geht es dann aber.

plastikboot
09.06.2013, 10:32
Da die Winde ja fast die ganze Zeit steht und nur selten die Interrupts benötigt werden, wollte ich noch nachfragen, ob es wie beim Tastenentprellen möglich ist auch den Interrupt zu entprellen? Oder führt das auch nur zu falschen Ergebnissen? Was für einen Sinn macht denn dann eigentlich die Interruptsteuerung, wenn man falsche Werte bekommt. Mit meinem bisherigen Verständnis war das sozusagen die sichere Variante.

Besserwessi
09.06.2013, 13:18
Es kommt ein wenig darauf an wie man die Abfrage mit den Interrupts realisiert. Richtig gemacht geht es auch sicher mit den Interrupts. Der kritische Punkt ist wenn sich das Rad langsam dreht und dann gerade nach dem Umschalten eine Störung kommt. Gerade da ist die Empfindlichkeit auf Störungen am höchsten, vor allem wenn die Hardware keine merkliche Hysterese hat. Da hat man dann neben dem gewollten Pegelwechsel noch 2 zusätzliche Wechsel und damit halt ggf. 3 gleiche Interrupts (Pin Change) in sehr kurzer Zeit, wo es eigentlich nur hätte einen geben sollen. Wenn man es richtig macht, ist die Auswertung im Pin-change Interrupts fast genau so wie mit dem Timer, also mit merken des alten (d.h. zuletzt berücksichtigten) Zustandes und Auslesen des neuen. Dann geht es auch zuverlässig im Interrupt, selbst wenn man im Einzelfall mal einen Interrupt verpasst. Fehleranfällig ist dagegen wenn man das Auftreten des Interrupts schon als Ersatz für das Auslesen des Zustandes nutzt.

Im Prinzip macht man mit der Variante den Decoder nach dem Timer abzufragen auch nichts anderes als ein Entprellen, einfach durch abtasten zu festen Zeiten.

plastikboot
09.06.2013, 14:31
So langsam lichtet sich mein Denknebel... eine letzte Frage bezüglich der Umsetzung ist jetzt noch aufgetaucht: Ist es genauso schnell die beiden Portstati mehrmals abzufragen wie diese in einer Bitvariablen zu speichern und mit der Variablen zu arbeiten? Mit dem Simulator stehe ich irgendwie auf Kriegsfuss - zumindest sobald ich mit externen Signalen arbeiten will ;)

Besserwessi
09.06.2013, 17:41
Für den Drehgeber sollte man die Portwerte nur jeweils einmal in der ISR abfragen. Das kann man zusammen als Byte für einen Port, oder etwas langsamer als Bitwerte machen. Wirklich groß ist da der Unterschied nicht, das geht beides schnell.

plastikboot
09.06.2013, 17:58
So Besserwessi, die Abfrage der Sensoren funktioniert jetzt tadellos!!! Ich danke Dir vielmals für die Mühe und Deine Zeit! Mein Projekt kann jetzt gleich in die nächsten Probleme weitergehen ;))) Die ISR sieht jetzt so aus:

Irq_hallsensoren:

Timer1 = Timervorgabe ' Timervorgabe für die Frequenz
Zustandhallsensor1 = Pine.4
Zustandhallsensor2 = Pine.6
If Zustandhallsensor2alt <> Zustandhallsensor2 Then
If Zustandhallsensor1 = 0 Then
'Wechsel 00 auf 01 oder 10 auf 00
If Zustandhallsensor2 = 1 Then
Durchgang2 = 1
Zustandhallsensor2alt = 1
Else
If Durchgang1 = 1 Then
Umdrehungen = Umdrehungen + 1
Durchgang1 = 0
Print #4 , Umdrehungen ; " - " ; Zustandhallsensor1 ; " - " ; Zustandhallsensor2 ' DEBUG
Print #4 , "D2"
Else
End If
Durchgang2 = 0
Zustandhallsensor2alt = 0
End If
End If
End If

If Zustandhallsensor1alt <> Zustandhallsensor1 Then
If Zustandhallsensor2 = 0 Then
'Wechsel 00 auf 10 oder 01 auf 00
If Zustandhallsensor1 = 1 Then
Durchgang1 = 1
Zustandhallsensor1alt = 1
Else
If Durchgang2 = 1 Then
Umdrehungen = Umdrehungen - 1
Durchgang2 = 0
Print #4 , Umdrehungen ; " - " ; Zustandhallsensor1 ; " - " ; Zustandhallsensor2 ' DEBUG
Print #4 , "D1"

End If
Durchgang1 = 0
Zustandhallsensor1alt = 0
End If
End If
End If
Return

Besserwessi
09.06.2013, 18:37
Der Code ist noch reichlich unübersichtlich, und auch noch nicht ganz Fehlerfrei. Bis jetzt geht es mit dem Umschalten von Sensor 2 vor und beim Umschalten von Sensor1 nur zurück. Das kann Fehler geben, wenn das Rad etwas vor und zurück geht, um den einen Übergang.

Es muss also bei einer Änderung von Sensor1 entweder vor oder zurück gehen, abhängig davon ob die beiden Sensoren 1 und 2 gleich sind oder nicht. So ähnlich dann auch mit einer Änderung an Sensor2 (wobei das ggf. optional ist wenn weniger Auflösung reicht).

Das speichern der alten Zustände kann man direkt durch Zuweisung machen, also etwas als
Zustandhallsensor1alt = Zustandhallsensor1 am Ende der ISR.

Timer 1 erlaubt auch eine variable Interruptsfrequenz ohne nachladen von Hand.

plastikboot
09.06.2013, 19:50
Ja, da hatte ich mich zu früh gefreut, der Code lief bis auf eine Kleinigkeit, die dann natürlich zum größeren Ärgernis wurde: nested If-Then-If then Else funktionieren nicht ,) Danke für den Timerhinweis - wieder eine Zeile gespart ,)
Die Drehung erfasse ich über Durchgang1 und Durchgang2 - nur wenn vorher die "andere" Seite durchlaufen ist, kommt es zu einer Zählung (so ist zumindest der Plan)... so jetzt suche ich mal nach einer anderen Schreibweise für meine If thens.....

- - - Aktualisiert - - -

ES GEHT ;)
Das mit der variablen Interruptsfrequenz habe ich nicht gefunden - nur viel über CTC Modi - ich werde also einfach weiter per Hand nachladen ;)

Für alle Interessierten - hier der funktionierende Code....

'#################### Hallsensoren Interruptbehandlung #########################
Irq_hallsensoren:
Timer1 = 51647 ' Timer1 nachladen für ca. 18 Hz

Zustandhallsensor1 = Pine.4
Zustandhallsensor2 = Pine.6


If Zustandhallsensor1alt <> Zustandhallsensor1 Then
If Zustandhallsensor2 = 0 And Zustandhallsensor1 = 1 Then
Durchgang1 = 1
Elseif Zustandhallsensor2 = 0 And Zustandhallsensor1 = 0 And Durchgang2 = 1 Then
Umdrehungen = Umdrehungen - 1
Durchgang2 = 0

'Print #4 , Umdrehungen ; " - " ; Zustandhallsensor1 ; " - " ; Zustandhallsensor2 ' DEBUG
'Print #4 , "D1"

Elseif Zustandhallsensor2 = 0 And Zustandhallsensor1 = 0 And Durchgang2 = 0 Then
Durchgang1 = 0
End If
End If

If Zustandhallsensor2alt <> Zustandhallsensor2 Then
If Zustandhallsensor1 = 0 And Zustandhallsensor2 = 1 Then
Durchgang2 = 1
Elseif Zustandhallsensor1 = 0 And Zustandhallsensor2 = 0 And Durchgang1 = 1 Then
Umdrehungen = Umdrehungen + 1
Durchgang1 = 0

'Print #4 , Umdrehungen ; " - " ; Zustandhallsensor1 ; " - " ; Zustandhallsensor2 ' DEBUG
'Print #4 , "D2"

Elseif Zustandhallsensor1 = 0 And Zustandhallsensor2 = 0 And Durchgang1 = 0 Then
Durchgang2 = 0
End If
End If

Zustandhallsensor1alt = Zustandhallsensor1
Zustandhallsensor2alt = Zustandhallsensor2
Return