PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Sensorecho an Atmega328P am D6 - PCINT22 abfragen ?



JoeM1978
29.09.2013, 18:55
Hey... folgende sache...

Ich habe bereits timer für servos und Motor-PWM in verwendung und möchte deshalb einen
Ultraschallsensor (SRF04) am D6 (PCINT22) abfragen.
Da nurnoch timer 2 dafür zur verfügung steht hatte ich mir überlegt, den wert einfach mit den Überläufen hochzurechnen.

Nun hab ich noch herausgefunden, das der gesamte D-Port reagiert, wenn man es nicht über Pcmsk beschränkt.
Ich hoffe das ist soweit richtig gelöst.

soweit so gut...
allerdings habe ich hier ein kleines Testprogramm... das trotzdem recht
verwirrende Werte ausgibt.


$regfile = "m328pdef.dat" 'eingesetzter Mikrocontroller
$crystal = 8000000 'eingestellte Taktfrequenz (8MHz)
$hwstack = 100 'Standardwert
$swstack = 100 'Standardwert
$framesize = 100 'Standardwert
$baud = 9600

'-------------------------------------------------------------------------------
'Ports/Pins/Configs
'-------------------------------------------------------------------------------
Config Portb.0 = Output 'Funkmodul ENABLE
Funk_enable Alias Portb.0
Config Portb.1 = Output 'Motor2 Geschwindigkeit (PWM)
Motor2speed Alias Portb.1
Config Portb.2 = Output 'Motor1 Geschwindigkeit (PWM)
Motor1speed Alias Portb.2
Config Portb.3 = Output 'Servo1 (PWM)
Servo1 Alias Portb.3
Config Portb.4 = Output 'Motor2a
Motor2b Alias Portb.4
Config Portb.5 = Output 'Motor2b
Motor2a Alias Portb.5

Config Portc.0 = Output 'Motor1b
Motor1b Alias Portc.0
Config Portc.1 = Output 'Motor1a
Motor1a Alias Portc.1
'Config Portc.2 = Output
'Xx Alias Portc.2
'Config Portc.3 = Output
'Xx Alias Portc.3
'Config Portc.4 = Output
'Xx Alias Portc.4
'Config Portc.5 = Output
'Xx Alias Portc.5

'Config Portd.2 = Output
'Xx Alias Pind.2
'Config Portd.3 = Output
'Xx Alias Portd.3
'Config Portd.4 = Output
'Xx Alias Portd.4
'Config Portd.5 = Output
'Xx Alias Portd.5
Config Pind.6 = Input 'Ultraschall Echo
Us_echo Alias Pind.6
Config Portd.7 = Output 'Ultraschall Trigger
Us_trigger Alias Portd.7

'-------------------------------------------------------------------------------
'Timer/OCR/PWM/ISR usw. setzen
'-------------------------------------------------------------------------------

Pcicr = &B100
Pcmsk2 = &B01000000
On Pcint2 Echowechsel
Enable Pcint2

Config Timer2 = Timer , Prescale = 1 ' Echo vom Ultraschallsensor zählen
On Timer2 Isr_timer2
Enable Timer2

Enable Interrupts 'Interrupts global aktivieren

'-------------------------------------------------------------------------------
'Variablen
'-------------------------------------------------------------------------------

Dim Temp_string As String * 10
Dim Temp_long As Long

Dim Timerueberlauf As Long
Timerueberlauf = 0
Dim Messung_starten As Bit ' Zeigt an, ob Messung gestartet werden soll
Messung_starten = 1
Dim Messung_beendet As Bit ' Zeigt an, ob Messung beendet ist
Messung_beendet = 0
Dim Messergebniss As Long ' Ergebniss der Messung in us
Messergebniss = 0
Dim Entfernung As Long ' Entfernung in mm
Entfernung = 0
Dim Position_x_soll As Word
Dim Start_pause As Single
Start_pause = 1000
Dim Triggersignal_laenge As Single
Triggersignal_laenge = 10
Dim Timer_ueberlauf As Word
Timer_ueberlauf = 0


Declare Sub Triggersignal_senden(byval Start_pause As Single , Byval Triggersignal_laenge As Single)
'-------------------------------------------------------------------------------
'Hauptprogramm
'-------------------------------------------------------------------------------

Hauptprogramm:
Do

For Position_x_soll = 8 To 22 Step 2
Timerueberlauf = 0
Messergebniss = 0
Temp_long = 0
Entfernung = 0
Messung_beendet = 0
Call Triggersignal_senden(start_pause , Triggersignal_laenge)
Do
Waitus 1
Loop Until Messung_beendet = 1
If Messung_beendet = 1 Then
Print "'t=" ; Timerueberlauf

Print "_T=" ; Temp_long
Messergebniss = Messergebniss + Temp_long
Print "_M=" ; Messergebniss
Entfernung = Messergebniss * 172
Entfernung = Entfernung / 10000 ' Ergebniss in mm ?
Temp_string = Str(entfernung)
Temp_string = Format(temp_string , "000000")

Waitms 500
Print "==" ; Temp_string ; "mm" ' Ausgabe des Ergebniss
End If
Next

Loop

End



'-------------------------------------------------------------------------------
'Subs
'-------------------------------------------------------------------------------


Sub Triggersignal_senden(byval Start_pause As Single , Byval Triggersignal_laenge As Single)
Messung_beendet = 0
Waitms Start_pause ' minimale Wartezeit um "Fehlechos" auszuschliessen
Portd.7 = 1 ' Impuls an Triggerport senden
Waitus Triggersignal_laenge ' Impulsdauer 10us
Portd.7 = 0
End Sub

'-------------------------------------------------------------------------------
'Interrupt
'-------------------------------------------------------------------------------

Isr_timer2:
Timerueberlauf = Timerueberlauf + 1 'Timerüberläufe zählen
Return


Echowechsel: ' Interrupt startet bei Signalwechsel am Echopin
If Pind.6 = 1 Then ' Wenn Pin zu High wechselt ...
Timer2 = 0

Else ' wenn Pin zu LOW wechselt....
Messergebniss = Timer2 ' Messergebniss in Variable speichern
Temp_long = Timerueberlauf * 255 ' aktuelle Timerüberläufe übernehmen
Messung_beendet = 1 ' Anzeigen, das Messung beendet is
End If
Return

nichtmal der wert "Temp_long" wird korrekt berechnet...
kann es sein, das der 328p mir da irgendwie rechenfehler reinbaut ? o.O

NACHTRAG:
Es werden z.b. solche (nur mit geringen Schwankungen) Werte ausgegeben:
t=5241 _T=1327530 _M1327777 ==022837mm

und als Quarz ist ein 8mhz-Quarz verbaut und als fuse ist eingestellt:
Ext.Crystal Osc.; Frequ. 3,0-8,0MHz; ... 16/14/65ms


Kann bitte jemand meinen blinden Augen auf die sprünge helfen ?

Sauerbruch
30.09.2013, 14:02
kann es sein, das der 328p mir da irgendwie rechenfehler reinbaut ? o.O


Hier kann ein Taschenrechner äußerst wertvolle Dienste leisten! Wenn man damit Deine angegebenen Werte mal so verrechnet wie es der Mega328P tun soll, kommt man zu überraschend ähnlichen Ergebnissen. Rechenfehler seitens des Controllers sind es also schon mal nicht (und wären auch absolut nicht zu erwarten).

Erstmal zwei "Schönheitsfragen":

Was ist der Sinn der For-Next-Schleife?
Wieso fragst Du nach dem Befehl "Loop Until Messung_beendet = 1" nochmal "If Messung_beendet = 1" ab?

Das erklärt die seltsamen Werte aber zugegebenermaßen nicht. Was mir insgesamt auffällt ist die extrem hohe Anzahl an Timer-Überläufen ( > 5000...).
Bei einer Taktfrequenz von 8 MHz und einem Prescaler von 1 läuft der 8-Bit-Timer alle 32µS über. 5000 Überläufe während des auszumessenden Impulses würden also bedeuten, dass der Impuls eine Größenordnung von ca. 160mS hat - und das ist zu viel. Der SRF04 soll einen Impuls von maximal 32ms erzeugen (wenn kein reflektiertes Echo erkannt wurde), und ansonsten zwischen 100µs und 18mS. Mit dieser Größenordnung ist also schon mal was faul.

Den Timer ohne jeden Prescaler zu betreiben, ist möglicherweise auch etwas problematisch: Nach dem Auslösen einer ISR braucht der Controller schon mal weit mehr als 50 Takte um alle Register zu "retten" - und nach dem Return nochmal so viele, um die Registerinhalte alle wieder zurückzuschreiben. Wenn also alle 256 Takte in eine Timer-ISR gesprungen wird, ist der Controller schon mal mindestens die Hälfte der Zeit mit nix anderem beschäftigt, als Registerinhalte hin- und herzuschieben. Und dabei könnte vielleicht einiges auf der Strecke bleiben.

Wenn Du den Timer mit einem Prescaler von 64 betreibst, ist die Auflösung jedes Zählschrittes immer noch 8µs, d.h. die relevante Pulsbreite von 100µs bis 16ms könnte damit in knapp 2000 Stufen aufgelöst werden. Bei einer maximaler Impulslänge von 32 ms (d.h. kein Echoreflex) würde es übersichtliche 15 Timer-Überläufe geben, d.h. man könnte das Ganze auch mit Word-Variablen berechnen.

Die konkrete Berechnung müsste dann natürlich mit etwas anderen Zahlen ausgeführt werden, was ja aber kein Problem sein sollte wenn man weiß, wie der Zusammenhang zwischen Pulsdauer und Entfernung ist.

JoeM1978
30.09.2013, 17:29
Also diese For-Next Schleife ist drinn, da der Sensor auf einem Servo sitzt und bei jeder angefahrenen Position x eine Messung durchführen soll.
Vorbereitet bzw Restbestände aus dem Test mit dem Servo sozusagen.

Das mit dem Taschenrechner... mhm... du sagst ja selber "... ähnliche Werte"

bei : t=5241 _T=1327530 _M1327777 ==022837mm

rechne ich aber _T=5241*255 = 1336455
was eine Differenz zum tatsächlich angezeigten ergibt von 8925 ... also 35 Timerüberläufe.

eben mit ...

Messergebniss = Timer2
Temp_long = Timerueberlauf * 255
Messung_beendet = 1

...wollte ich erreichen, das die Werte gesichert sind, und nicht während des abarbeitens verändert werden.

Ja... mit Word-Variablen ,oder sogar kleiner, rechnen wäre tatsächlich besser... allerdings wollte ich zum testen erstmal ausschliessen, das nicht hier irgendwo was hängen bleibt am Wertebereich.
Das ändern des Prescaler hab ich bereits versucht...

Wenn ich den auf 64 habe... und extra noch direkt beim senden des Triggersignals den Timerueberlauf auf 0 stelle...
Bekomme ich bei einem Test-Abstand von 5cm rund 33 Timerüberläufe.
Abstand mit rund 2m ergibt etwa 40 Timerüberläufe.

Das ist deutlich zu viel... nun ist die Frage woher nimmt er diese "überschüssigen" 15-20 Timerüberläufe?

NACHTRAG:

Könnte es sein, das er während der Sub "Echowechsel" mehrmals in die ISR des Timers springt ?
Dann müsste ich den Timer sofort als erstes stoppen wenn der Pin zu Low wechselt.... das teste ich gleich mal.

- - - Aktualisiert - - -

Aktueller Versuch...


$regfile = "m328pdef.dat" 'eingesetzter Mikrocontroller
$crystal = 8000000 'eingestellte Taktfrequenz (8MHz)
$hwstack = 100 'Standardwert
$swstack = 100 'Standardwert
$framesize = 100 'Standardwert
$baud = 9600

'-------------------------------------------------------------------------------
'Ports/Pins/Configs
'-------------------------------------------------------------------------------
Config Portb.0 = Output 'Funkmodul ENABLE
Funk_enable Alias Portb.0
Config Portb.1 = Output 'Motor2 Geschwindigkeit (PWM)
Motor2speed Alias Portb.1
Config Portb.2 = Output 'Motor1 Geschwindigkeit (PWM)
Motor1speed Alias Portb.2
Config Portb.3 = Output 'Servo1 (PWM)
Servo1 Alias Portb.3
Config Portb.4 = Output 'Motor2a
Motor2b Alias Portb.4
Config Portb.5 = Output 'Motor2b
Motor2a Alias Portb.5

Config Portc.0 = Output 'Motor1b
Motor1b Alias Portc.0
Config Portc.1 = Output 'Motor1a
Motor1a Alias Portc.1
'Config Portc.2 = Output
'Xx Alias Portc.2
'Config Portc.3 = Output
'Xx Alias Portc.3
'Config Portc.4 = Output
'Xx Alias Portc.4
'Config Portc.5 = Output
'Xx Alias Portc.5

'Config Portd.2 = Output
'Xx Alias Pind.2
'Config Portd.3 = Output
'Xx Alias Portd.3
'Config Portd.4 = Output
'Xx Alias Portd.4
'Config Portd.5 = Output
'Xx Alias Portd.5
Config Pind.6 = Input 'Ultraschall Echo
Us_echo Alias Pind.6
Config Portd.7 = Output 'Ultraschall Trigger
Us_trigger Alias Portd.7

'-------------------------------------------------------------------------------
'Timer/OCR/PWM/ISR usw. setzen
'-------------------------------------------------------------------------------

Pcicr = &B100
Pcmsk2 = &B01000000
On Pcint2 Echowechsel
Enable Pcint2

Config Timer2 = Timer , Prescale = 64 ' Echo vom Ultraschallsensor zählen
On Timer2 Isr_timer2
Enable Timer2

Enable Interrupts 'Interrupts global aktivieren

'-------------------------------------------------------------------------------
'Variablen
'-------------------------------------------------------------------------------

Dim Temp_string As String * 10
Dim Temp_long As Long

Dim Timerueberlauf As Long
Timerueberlauf = 0
Dim Messung_starten As Bit ' Zeigt an, ob Messung gestartet werden soll
Messung_starten = 1
Dim Messung_beendet As Bit ' Zeigt an, ob Messung beendet ist
Messung_beendet = 0
Dim Messergebniss As Long ' Ergebniss der Messung in us
Messergebniss = 0
Dim Entfernung As Long ' Entfernung in mm
Entfernung = 0
Dim Position_x_soll As Word
Dim Start_pause As Single
Start_pause = 1000
Dim Triggersignal_laenge As Single
Triggersignal_laenge = 10
Dim Timer_ueberlauf As Word
Timer_ueberlauf = 0


Declare Sub Triggersignal_senden(byval Start_pause As Single , Byval Triggersignal_laenge As Single)
'-------------------------------------------------------------------------------
'Hauptprogramm
'-------------------------------------------------------------------------------

Hauptprogramm:
Do

For Position_x_soll = 8 To 22 Step 2
Timerueberlauf = 0
Messergebniss = 0
Temp_long = 0
Entfernung = 0
Messung_beendet = 0
Call Triggersignal_senden(start_pause , Triggersignal_laenge)
Do
Waitus 1
Loop Until Messung_beendet = 1

Print "'t=" ; Timerueberlauf

Print "_T=" ; Temp_long
Messergebniss = Messergebniss + Temp_long
Print "_M=" ; Messergebniss
Entfernung = Messergebniss * 172
Entfernung = Entfernung / 10000 ' Ergebniss in mm ?
Temp_string = Str(entfernung)
Temp_string = Format(temp_string , "000000")

Waitms 500
Print "==" ; Temp_string ; "mm" ' Ausgabe des Ergebniss

Next

Loop

End



'-------------------------------------------------------------------------------
'Subs
'-------------------------------------------------------------------------------


Sub Triggersignal_senden(byval Start_pause As Single , Byval Triggersignal_laenge As Single)
Messung_beendet = 0
Timerueberlauf = 0
Enable Interrupts
Waitms Start_pause ' minimale Wartezeit um "Fehlechos" auszuschliessen
Portd.7 = 1 ' Impuls an Triggerport senden
Waitus Triggersignal_laenge ' Impulsdauer 10us
Portd.7 = 0
End Sub

'-------------------------------------------------------------------------------
'Interrupt
'-------------------------------------------------------------------------------

Isr_timer2:
Timerueberlauf = Timerueberlauf + 1 'Timerüberläufe zählen
Return


Echowechsel: ' Interrupt startet bei Signalwechsel am Echopin
If Pind.6 = 1 Then ' Wenn Pin zu High wechselt ...
Timer2 = 0

Else ' wenn Pin zu LOW wechselt....
Disable Interrupts
Messergebniss = Timer2 ' Messergebniss in Variable speichern
Temp_long = Timerueberlauf * 255 ' aktuelle Timerüberläufe übernehmen
Messung_beendet = 1 ' Anzeigen, das Messung beendet is
End If
Return ' Zurück zum Hauptprogramm

Werte bei 5cm:
't=33 _T=8415 _M=8456 ==000145mm
't=33 _T=8415 _M=8456 ==000145mm
't=33 _T=8415 _M=8456 ==000145mm

Werte bei ca. 2m:
't=33 _T=8415 _M=8492 ==000146mm
't=33 _T=8415 _M=8489 ==000146mm
't=33 _T=8415 _M=8491 ==000146mm

:roll:

NACHTRAG:

Nachdem ich nun so einiges vergeblich versucht habe bin ich zumindest soweit, das ich sagen kann:
Es muss mit dem PCINT22 / PCMSK / PCICR zu tun haben

Sogar bei abgestecktem sensor springt der PCINT22 an.
Also hab ich mal den Triggerport "nicht triggern lassen" ... dann springt der PCINT22 nicht an.

... nun ist zumindest der fehler eingegrenzt.

Das der Trigerport auch am PCINT2 liegt ist mir bewusst... aber ich war/bin der Meinung, das man ihn
durch die PCMSK2 auf eben diesen einen Pin beschränken kann. ?
Kann mir da wer verraten was an der Einstellung für den PCINT22 falsch sein kann ?

for_ro
30.09.2013, 22:23
Hallo Joe,
hier ein paar Kommentare zu deinem Programm.
Diese beiden Befehle bewirken bzgl. PCINT2 das Gleiche
Pcicr = &B100
Enable Pcint2

Disable Interrupts
während einer Interrupt Bearbeitung ist sinnlos, da die Interrupts zu diesem Zeitpunkt schon global abgeschaltet sind.
Nach der ISR werden sie dann automatisch wieder global eingeschaltet, sodass weitere Überläufe des Timers stattfinden.
Wenn du das verhindern willst, dann schreibe
Disable Timer2
Konsequenterweise dann
Enable Timer2
unmittelbar vor oder nach dem Triggerimpuls. Bei dir läuft der Timer auch schon während der Start_pause.
Und mache Start_pause und Triggersignal_laenge zu Word Variablen. Wait funktioniert nicht mit Single.
Du solltest die Variablen im Declare und im Call unterschiedlich benennen.

Da der Timer tatsächlich 256 Schritte bis zum Überlauf macht, muss diese Rechnung
Temp_long = Timerueberlauf * 255 ' aktuelle Timerüberläufe übernehmen
so lauten
Temp_long = Timerueberlauf *256

Die Diskrepanz der Werte kommt übrigens, weil du zwar Messergebnis und Temp_long abspeicherst, der Timer aber die Variable Timerueberlauf fleissig hochzählt. Bei Prescale=64 schafft der Timer meistens keinen Überlauf, bei Prescale=1 schafft er 35 Überläufe während des Print "'t=";

Wechselt eigentlich dein Eingangssignal richtig zwischen Low und High? Da ist kein PullUp im Programm aktiviert.

JoeM1978
30.09.2013, 23:23
Oh wei...
OK. ich geb zu... hin und wieder bin ich mit Blindheit geschlagen. :Haue

Hab auch soweit alles bereits umgesetzt... wobei mir nicht ganz klar ist warum
die Variablen unterschiedlich benannt sein sollen.
Um sicher zu gehen, das sie nicht während einer Rechenoperation im
Main, "gleichzeitig" auch in einer Sub/ISR verändert werden ?

Dabei fällt mir auf, das ich die Variablen beim aufruf der Sub "Triggersignal_senden" an sich nicht benötige,
da ich diese nur als "Konstante" im Kopf hinterlege.

Mit dem Eingangssignal hab ich an sich keine Probleme. das kommt sauber an.
zumindest jetzt in diesem Testprogramm noch.

Aber auf alle Fälle danke für eure "Anschubser".

for_ro
01.10.2013, 19:26
Hab auch soweit alles bereits umgesetzt... wobei mir nicht ganz klar ist warum
die Variablen unterschiedlich benannt sein sollen.
Hiermit erzeugst du globale Variablen, die überall in deinem Programm bekannt sind. Deren Werte kannst du auch überall ändern:
Dim Start_pause As Single
Dim Triggersignal_laenge As Single

Dagegen erzeugst du hiermit lokale Variablen der Sub, die nur in deren Körper bekannt sind und nur dort geändert werden können:
Declare Sub Triggersignal_senden(byval Start_pause As Single , Byval Triggersignal_laenge As Single)

Eigentlich sollte der Compiler meckern, vielleicht löst er es auch richtig auf. Da man das nie weiß, nimmt man verschiedene Namen. Namen sind normalerweise nicht knapp.