mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung und senden über CAN


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: M. H. (maiggl)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen.

ich will die Drehzahl mit Hilfe eines Encoders (mit Stirnrad auf der 
Motorwelle) messen und die Drehzahlwerte über CAN senden.
Als Hardware habe ich ein Arduino uno board mit aufgesteckten CAN sgield 
genommen, weil das gerade verfügbar war.

Im Prinzip funktioniert die Messung und das Senden, allerdings gibt es 
zwei Probleme.

1)Ab und zu scheint die Messung nicht zu funktionieren. Bei einer festen 
Drehzahl von 500 rpm werden über CAN ab und zu mal falsche Werte 
übermittelt. Das peaks von etwa 525-535 rpm. Das passiert auch bei 
anderen Drehzahlen.

2)Bei einer Drehzahl < 60 rpm werden "Phantasiewerte", z.B. 1325, über 
CAN zurückgemeldet. Diese springen auch ziemlich.

Im Code nutze ich den input capture um die Zeitstempel für den Beginn 
und das Ende einer Umdrehung zu messen (24 Pulse/Umdrehung). Aus der 
Zeitdifferenz wird dann die Drehzahl berechnet. So der Plan.

Hat jemand eine Idee was da schief läuft?
Falls es auch andere Verbesserungsvorschläge für die Umsetzung und den 
Code gibt, wäre ich dankbar, wenn ihr es mich wissen lassen würdet.

: Verschoben durch Moderator
Autor: BerndB (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

ich hab mal kurz in den Code geschaut. Ich arbeite zwar nicht mit 
Arduion, aber mir ist aufgefallen, das Du an der Anzahl der Pulss sowohl 
im Interrupt als auch im Main Veränderungen (Pulse reset) vornimst. Dies 
kann dazu führen, das die Interruptroutine mehr pulse zählt als deine 
24.

Gruß Bernd

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ISR(TIMER1_OVF_vect)
{
}

Hier fehlt der entscheidene Inhalt.
Laß Timer1 mit voller Geschwindigkeit laufen und beachte die Überläufe. 
Dann klappt das auch.
Code für Arduino: http://mino-elektronik.de/fmeter/fm_software.htm#bsp7

Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank für die Rückmeldungen.

Ich weiß nicht so richtig, was ich in ISR(TIMER1_OVF_vect) machen soll, 
weil mich die Überläufe doch gar nicht interessieren. Oder sollten sie 
das?
Daher habe ich den ovf interrupt mal komplett ausgeschaltet.
Das ändert aber rein gar nichts, immer noch peaks.

Aufrund BerndBs Hinweis habe ich den code so geändert, dass nur noch in 
ISR(TIMER1_CAPT_vect) die Variable verändert wird und nicht zusätzlich 
noch im Main.

Das hat an der Peak Thematik auch nichts geändert.

Sollte ich das Ganze vll so aufziehen, dass ich einfach nach einem 
festen Zeintintervall die bis dahin gezählten Pulse auswerte und mit 
Bezug auf das Zeitintervall und den Pulsen/Umdrehung die Dehzahl 
berechne?

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. H. schrieb:
> Ich weiß nicht so richtig, was ich in ISR(TIMER1_OVF_vect) machen soll,
> weil mich die Überläufe doch gar nicht interessieren. Oder sollten sie
> das?

Willst Du nicht mal lesen, was ich Dir geschrieben habe?

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>  if (pulse==PulseProUmdrehung+1)

Da ist es reiner Zufall dass deine while()-Schleife diese Bedingung mal 
trifft. Was passiert wenn innerhalb eines Durchlaufs deiner 
while()-Schleife die ISR mehrmals zuschlägt?

Autor: Mario M. (thelonging)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. H. schrieb:
> 2)Bei einer Drehzahl < 60 rpm werden "Phantasiewerte", z.B. 1325, über
> CAN zurückgemeldet. Diese springen auch ziemlich.

TCNT1 läuft über. 16 MHz/256/65536 = 0,95 Hz -> 57 rpm

Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Hinweise. Ich versuche mal den code zu verbessern und 
melde mich wieder

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
M. H. schrieb:
> Im Code nutze ich den input capture um die Zeitstempel für den Beginn
> und das Ende einer Umdrehung zu messen (24 Pulse/Umdrehung).
Eine Umdrehung hat für mich prinzipiell keinen Anfang und kein Ende.
Und warum schaltest du die zu zählende Flanke um?
Ich würde immer nur von der selben Flanke zur selben Flanke messen 
(steigend oder fallend, je nach dem, was definierter ist).

Und du musst zur Auswertung nicht 24 Impulse abwarten. Im Prinzip weißt 
du schon nach der zweiten steigenden Flanke (und bei jeder einzelenen 
nachfolgenden Flanke) die Drehzahl: die ist 24mal niedriger als die per 
Capture gemessene Zeit und die daraus berechnete Frequenz.

Du musst also einfach immer nur den Zeitpunkt der letzten Flanke vom 
Zeitpunkt der aktuellen Flanke abziehen und hast damit ein 24stel der 
Zeit bestimmt, die eine Umdrehung braucht. Dann merkst du dir die 
aktuelle Zeit für die nächste Berechnung bei der nächsten Flanke.

>  rpm = 60000 / ((timeStamp2-timeStamp1)*0.016) ; // Prescaler 256
Abgesehen vom relativ nutzlosen Kommentar: lass den zeitaufwändigen 
Umweg über diese float-Berechnung. Schreib die Zeile besser so:
rpm = 3750000 / (long)(timeStamp2-timeStamp1); // Magic Number 3750000 = 60000/0.016

: Bearbeitet durch Moderator
Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das liest sich alles ziemlich einleuchtend. Danke für die Hinweise.
Die Umschaltung der zählenden Flanke war ein Versuch, da sich nichts 
geändert hat, habe ich im Code gelassen.

Ich habe den Code abgeändert, allerdings komme ich nicht zum richtigen 
Messwert.

/*--------------------------------------------------------------------------------------------------
 CAPTURE ISR
 ---------------------------------------------------------------------------------------------------*/
 ISR(TIMER1_CAPT_vect)
 {
   if (pulse == 0)
   {
      Capt1 = ICR1;
      pulse++;
   }
   else
   {
      Capt2 = ICR1;
      ueberlauf = ueberlauf_count;
      ueberlauf_count=0;
      timeStamp1 = Capt1;
      timeStamp2 = Capt2 + ueberlauf*256;         
      zeit_diff = timeStamp2-timeStamp1;
      rpm = 3750000 / (long)(timeStamp2-timeStamp1) * 24; // Magic Number 3750000 = 60000/0.016
      Capt1 = Capt2;
   }
   } 
 /*--------------------------------------------------------------------------------------------------
 OVF ISR
 ---------------------------------------------------------------------------------------------------*/
ISR(TIMER1_OVF_vect)        
{
  ueberlauf_count++;    
}

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
M. H. schrieb:
> Ich habe den Code abgeändert
Immer noch recht holprig...
Und hier:
timeStamp2 = Capt2 + ueberlauf*256;
Frage ich mich: ist der Timer 1 nicht ein 16 Bit-Zähler? Dann kommt ein 
Überlauf erst nach 65536 und nicht schon nach 256...

> allerdings komme ich nicht zum richtigen Messwert.
Du darfst die rpm auch nicht mit 24 mutliplizieren, denn die Überlaufe 
an sich kommen ja schon um das 24fache zu oft...


Ich würde an deiner Stelle aus dem Capture und dem Überlauf einen 32Bit 
Wert basteln und damit weiterrechnen:
unsigned long timeStampAkt, timeStampLast, zeit_diff;
 ISR(TIMER1_CAPT_vect)
 {
      timeStampAkt = (ueberlauf_count<<16) + ICR1; // aus ICP und Überlaufz. einen 32 Bit Zähler basteln
      rpm = 1234567 / (timeStampAkt-timeStampAlt); // Magic Number anpassen... ;-)
      timeStampAlt = timeStampAkt;                 // fürs nächste Mal merken
 }

: Bearbeitet durch Moderator
Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> rpm = 1234567 / (timeStampAkt-timeStampAlt); // Magic Number
> anpassen... ;-)

Nein, sondern die Auswertung zum einen nicht in der ISR erledigen und 
zum anderen mit float rechnen.

Lothar M. schrieb:
> lass den zeitaufwändigen
> Umweg über diese float-Berechnung.

Der Zeitaufwand ist minimal und kein Grund auf float zu verzichten.
Das muß wohl mal wieder gesagt werden ;-)

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
m.n. schrieb:
> Der Zeitaufwand ist minimal und kein Grund auf float zu verzichten.
> Das muß wohl mal wieder gesagt werden ;-)
Dann sage ich halt: "nimm einen 32Bit Integer weil der genauer ist!", 
denn ein 32-Bit-float hat nur 7 signifikante Stellen. Und die 
Dynamik, die ein float bietet, wird bei der Aufgabe hier nicht 
benötigt.

> Nein, sondern die Auswertung zum einen nicht in der ISR erledigen
Wenn die "Auswertung" nur noch die 3 verbliebenen Zeilen ist, dann kann 
man die auch in der ISR machen.

Aber soll jeder nach seiner Fasson selig werden. Über Software streite 
ich genausowenig wie über Schönheit...   ;-)

Autor: M. H. (maiggl)
Datum:
Angehängte Dateien:
  • 1.txt (7,76 KB, 2 Downloads)
  • 2.txt (7,88 KB, 1 Downloads)
  • preview image for 1.PNG
    1.PNG
    2,19 KB, 66 Downloads
  • preview image for 2.PNG
    2.PNG
    1,61 KB, 48 Downloads

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> timeStamp2 = Capt2 + ueberlauf*256;
> Frage ich mich: ist der Timer 1 nicht ein 16 Bit-Zähler? Dann kommt ein
> Überlauf erst nach 65536 und nicht schon nach 256...

Denkfehler, danke für die Aufklärung.

Ich habe mit dem Tipp von Ikmiller mal zwei Varianten versucht.
1) Auswertung der Zeit von zwei aufeinander folgenden Pulsen
void StartTimer1(void)
 {
 TCCR1B|=(1<<CS12); //STARTING TIMER WITH PRESCALER 256
 }
unsigned long timeStampAkt, timeStampLast;//, zeit_diff;
 ISR(TIMER1_CAPT_vect)
 {
      timeStampAkt = (ueberlauf_count<<16) + ICR1; // aus ICP und Überlaufz. einen 32 Bit Zähler basteln
      rpm = 3750000 / (timeStampAkt-timeStampLast)/24; // Magic Number 3750000 = 60000/0.016
      timeStampLast = timeStampAkt;                 // fürs nächste Mal merken
 } 
ISR(TIMER1_OVF_vect)        
{
  ueberlauf_count++;      // Ueberlaeufe von T1 ergeben obere 16bit der Messzeit
}

2) Auswertung erst, wenn Pulse = 24 (Stirnrad hat 24 Zähne)
void StartTimer1(void)
 {
 TCCR1B|=(1<<CS12); //STARTING TIMER WITH PRESCALER 256
 }
unsigned long timeStampAkt, timeStampLast;//, zeit_diff;
 ISR(TIMER1_CAPT_vect)
 {
   if (pulse == 0)
   {
      timeStampLast = ICR1;
   }
   pulse++; //INCREMENTING FLAG
   if (pulse == PulseProUmdrehung)
   {
      timeStampAkt = ICR1;
      timeStampAkt = (ueberlauf_count<<16) + ICR1; // aus ICP und Überlaufz. einen 32 Bit Zähler basteln
      rpm = 3750000 / (timeStampAkt-timeStampLast); // Magic Number 3750000 = 60000/0.016
      ueberlauf_count=0;
      pulse = 0;
   }
} 

ISR(TIMER1_OVF_vect)        
{
  ueberlauf_count++;      // Ueberlaeufe von T1 ergeben obere 16bit der Messzeit
}

Zusätzlich sind die jeweiligen Codevarianten komplett angehängt.
Außerdem noch die CAN plots der rpm Werte Bei einer Drehzahl von 647 
rpm.

Variante 2 läuft stabiler, allerdings springt die Drehzahl gelegntlich 
auf 671.
Ein ähnliches Problem hatte ich beim ursprünglichen code.

Bei beiden Varianten springt der rpm Wert regelmäßig auf 0.

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe zwar keine Ahnung, aber:
im Beispielcode unter 
https://rn-wissen.de/wiki/index.php/Timer/Counter_(Avr)#Input_Capture 
werden das high und low Byte von ICR1 separat ausgelesen und ausgewertet 
gerade um Overflow-situationen glatt zu bügeln.

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> werden das high und low Byte von ICR1 separat ausgelesen und ausgewertet
> gerade um Overflow-situationen glatt zu bügeln.

Das separate Auslesen ist nicht der Punkt. Der TO muß erst erkennen, daß 
ein Überlauf auch dann auftreten kann, wenn die TIMER1_CAPT-ISR gerade 
aktiv ist. Beispielcode hat er ja genug.

Lothar M. schrieb:
> Dann sage ich halt: "nimm einen 32Bit Integer weil der genauer ist!",
> denn ein 32-Bit-float hat nur 7 signifikante Stellen.

Nimm eine Divison x/y. x und y liegen im Bereich 1 - 999. Wo ist denn 
hier die Integer-Berechnung genauer als die mit float? Einfaches 
Beispiel: x = 10 und y = 3.
int-Ergebnis = 3
float-Ergebnis = 3.333333

> Und die
> Dynamik, die ein float bietet, wird bei der Aufgabe hier nicht
> benötigt.

Da irrst Du. Wenn man nicht aufpaßt und seine Wertebereiche strikt 
eingrenzt, hat man mit uin32_t Werten schnell Über- oder Unterläufe - 
selbst, wenn man nur 4-stellige Ergebnisse braucht.

Lothar M. schrieb:
> Wenn die "Auswertung" nur noch die 3 verbliebenen Zeilen ist, dann kann
> man die auch in der ISR machen.

Gut, aber zuvor bitte wieder mit sei(); andere ISRs zulassen, damit 
diese durch größere Ausführungszeiten nicht blockiert werden. Das 
Gesamtprogramm ist ja noch nicht fertig.
Es geht mir nicht um Schönheit sondern um Pferdefüße ;-)

Autor: Anton (Gast)
Datum:

Bewertung
-3 lesenswert
nicht lesenswert
M. H. schrieb:
> ich will die Drehzahl mit Hilfe eines Encoders (mit Stirnrad auf der
> Motorwelle) messen und die Drehzahlwerte über CAN senden.

Das würde ich bleiben lassen, denn jeder Eingriff in die Elektrik führt 
zum Erlöschen der Betriebserlaubnis! Zudem möchte ich nicht in deiner 
Haut stecken, wenn du wegen Signalmanipulation einen Unfall auslöst.

Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Anton schrieb:
> Das würde ich bleiben lassen, denn jeder Eingriff in die Elektrik führt
> zum Erlöschen der Betriebserlaubnis! Zudem möchte ich nicht in deiner
> Haut stecken, wenn du wegen Signalmanipulation einen Unfall auslöst.

Den einzigen "Unfall", den ich damit auslösen kann, ist ein falscher 
Messwert, der niemanden (außer mich) interessiert.
Ich will nur die Drehzahl überprüfen und über CAN ausgeben, damit dieser 
Wert im gleichen CAN plot landet (aus Dokumentationszwecken), wie alle 
anderen Werte eines Umrichters, der den Motor antreibt.

Autor: M. H. (maiggl)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> werden das high und low Byte von ICR1 separat ausgelesen und ausgewertet
> gerade um Overflow-situationen glatt zu bügeln.

Das habe ich auch ausprobiert.
sieht schon deutlich besser aus, aber ausreißer sind immer noch drin.
(Nicht wundern, Drehzahl wurde ein wenig erhöht)

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> im Beispielcode unter
> https://rn-wissen.de/wiki/index.php/Timer/Counter_(Avr)#Input_Capture
> werden das high und low Byte von ICR1 separat ausgelesen
Das macht der Compiler automatisch richtig.

M. H. schrieb:
> Variante 2 läuft stabiler
Vergiss hier, irgendwelche "stabilere" oder "bessere" Varianten 
hinzubasteln. Die Aufgabe muss einfach einmal "richtig" gelöst werden. 
Das geht. So wie es aussieht, sind die Probleme weitestgehend zyklisch 
und das hat dann mit irgendwelchen Überläufen und Datentransfers zu tun.

M. H. schrieb:
> Bei beiden Varianten springt der rpm Wert regelmäßig auf 0.
Das ist der eigentliche Witz hier: du hast irgendein Semaphorenproblem. 
Der Zähler verstolpert sich. Das könnte daher kommen, dass der ICP und 
der Überlauf-Zähler sich irgendwie "verheddern".

Und natürlich hast du das noch das übliche Semaphorenproblem, denn du 
liest hier:
message.data[0] = rpm;
message.data[1] = (rpm>>8);
einen 16 Bit Wert in 2 Schritten aus. Zwischen diesen 2 Schritten kann 
ein Interrupt kommen und einen neuen Wert in rpm schreiben. Dann geht 
auf den CAN zur Hälfte der Neue und zur Hälfte der alte Wert. Und wenn 
der alte Wert rpm = 256 war und der Neue rpm = 255 ist, was geht dann 
auf den Bus?

> Außerdem noch die CAN plots
Hast du auch qualifizierte Messgeräte (mehrkanaliges digitales 
Speicheroszilloskop oder Logicanalyzer) zur Hand? Denn du musst das 
Problem mal direkter an der Quelle anpacken und z.B. mal das 
Interrupt-Timing messen, indem du zu Beginn jeder ISR einen Ausgangspin 
setzt und am Ende wieder zurücksetzt (idealerweise aber nicht mit der 
schnarchlangsamen Arduino-Funktion, sondern durch direkten 
Registerzugriff).

M. H. schrieb:
> Eric B. schrieb:
>> werden das high und low Byte von ICR1 separat ausgelesen
> Das habe ich auch ausprobiert.
> sieht schon deutlich besser aus
Vermutlich sieht es deshalb "besser aus", weil du die Frequenz ein wenig 
hochgedreht hast. Dann ist der Zyklus der Ausreißer logischerweise 
anders. Mit ein wenig Glück findest du sogar eine Drehzahl, wo gar kein 
Fehler sichtbar ist.



EDIT:
m.n. schrieb:
> Gut, aber zuvor bitte wieder mit sei(); andere ISRs zulassen, damit
> diese durch größere Ausführungszeiten nicht blockiert werden. Das
> Gesamtprogramm ist ja noch nicht fertig.
Ja, aber dabei aufpassen, damit ein zwischendurch auftretender 
Timer-Overflow nicht wieder den 16 Bit Zugriff auf ueberlauf_count 
korrumpiert.

> Nimm eine Divison x/y. x und y liegen im Bereich 1 - 999. Wo ist denn
> hier die Integer-Berechnung genauer als die mit float? Einfaches
> Beispiel: x = 10 und y = 3.
> int-Ergebnis = 3
> float-Ergebnis = 3.333333
Ja klar, das ist jetzt aber auch nicht das Problem, das ich meinte.

> Wo ist denn hier die Integer-Berechnung genauer als die mit float?
Die Mantisse hat nur 24 Bits, also kann sie 0..16777215 darstellen. Das 
sind effektiv 7 Stellen. Und bei 32 Bit kann ich den Zahlenbereich 
0..429497295 abbilden. Das sind gut 2 Dezimalstellen mehr.

Also rechne einfach mal 4444444444+1 in 32-Bit-float und in 
32-Bit-Integer. Das ist mit "signifikanten Stellen" gemeint.

> Nimm eine Divison x/y. x und y liegen im Bereich 1 - 999. Wo ist denn
> hier die Integer-Berechnung genauer als die mit float?
> Beispiel: x = 10 und y = 3.
> int-Ergebnis = 3
> float-Ergebnis = 3.333333
Wenn ich die Nachkommastellen brauche, dann rechne ich einfach rechne 
also z.B. statt in Volt in µV oder statt in Metern in µm und damit 
10000000/3000000 und bekomme 3333333. Und dann denke ich mir den 
Dezimalpunkt an die passende Stelle und gut ist.
Und dann bekomme ich auch noch bei 1000000000/3000000 (entspricht 
1000.0/3.0) bis zur letzten "mikro-Nachkommastelle" das richtige 
Ergebnis.

>> Und die Dynamik, die ein float bietet, wird bei der Aufgabe hier nicht
>> benötigt.
> Da irrst Du
Nein, alle Zahlen, die bei der Aufgabe hier zu berechnen sind, passen 
wunderbar in den Zahlenbereich eines 32 Bit Integers.

: Bearbeitet durch Moderator
Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Die Aufgabe muss einfach einmal "richtig" gelöst werden.

da bin ich deiner Meinung.

Lothar M. schrieb:
> Und natürlich hast du das noch das übliche Semaphorenproblem, denn du
> liest hier:
> message.data[0] = rpm;
> message.data[1] = (rpm>>8);
> einen 16 Bit Wert in 2 Schritten aus. Zwischen diesen 2 Schritten kann
> ein Interrupt kommen und einen neuen Wert in rpm schreiben. Dann geht
> auf den CAN zur Hälfte der Neue und zur Hälfte der alte Wert. Und wenn
> der alte Wert rpm = 256 war und der Neue rpm = 255 ist, was geht dann
> auf den Bus?

das Problem habe ich auch schon gesehen. Das müsste aber doch weg sein, 
wen man die Interrupts vor dem ganzen Sendevorgang ausschalte und danach 
wieder einschalte, oder nicht?

Lothar M. schrieb:
> Hast du auch qualifizierte Messgeräte (mehrkanaliges digitales
> Speicheroszilloskop oder Logicanalyzer) zur Hand? Denn du musst das
> Problem mal direkter an der Quelle anpacken und z.B. mal das
> Interrupt-Timing messen

Ja, das werde ich machen.

Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Und natürlich hast du das noch das übliche Semaphorenproblem, denn du
> liest hier:
> message.data[0] = rpm;
> message.data[1] = (rpm>>8);
> einen 16 Bit Wert in 2 Schritten aus. Zwischen diesen 2 Schritten kann
> ein Interrupt kommen und einen neuen Wert in rpm schreiben. Dann geht
> auf den CAN zur Hälfte der Neue und zur Hälfte der alte Wert. Und wenn
> der alte Wert rpm = 256 war und der Neue rpm = 255 ist, was geht dann
> auf den Bus?

Wenn ich den sendevorgang in die ISR verlagere, sehe ich keine Ausreißer 
mehr im plot.
Ich weiß, man sollte möglichst wenig in der ISR tun, aber für meinen 
Fall ist das doch eigentlich nicht wichtig wie lange die ISR braucht, 
oder?
ISR(TIMER1_CAPT_vect)      //  Flanke an ICP pin
{ 
 if (pulse == 0)
   {
     cap.i8l = ICR1L;         // low Byte zuerst, high Byte wird gepuffert
     cap.i8m = ICR1H;
     timestamp = cap.i32;
   }
   pulse++; //INCREMENTING FLAG
  if (pulse == PulseProUmdrehung)
   {   
     cap.i8l = ICR1L;         // low Byte zuerst, high Byte wird gepuffert
     cap.i8m = ICR1H;  
      // overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt
        if ((cap.i8m < 128) && (TIFR1 & (1<<TOV1)))
        {   // wartenden timer overflow Interrupt vorziehen
          ++ueberlauf_count;         
           TIFR1 = (1<<TOV1);    // timer overflow int. löschen, da schon hier ausgeführt
         }
      cap.high = ueberlauf_count;    // obere 16 Bit aus Software Zähler
      zeitdifferenz = cap.i32 - timestamp;
      rpm = 3750000 / zeitdifferenz; // Magic Number 3750000 = 60000/0.016
    
      tCAN message;
      message.id = 0x631; //formatted in HEX
      message.header.rtr = 0;
      message.header.length = 2; //formatted in DEC
      message.data[0] = rpm;
      message.data[1] = (rpm>>8);
      mcp2515_bit_modify(CANCTRL, (1<<REQOP2)|(1<<REQOP1)|(1<<REQOP0), 0);
      mcp2515_send_message(&message); 
      pulse = 0;
   }
}

Autor: M. H. (maiggl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu früh gefreut. Das ändert auch nichts an der Thematik...

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
1 lesenswert
nicht lesenswert
M. H. schrieb:
> Wenn ich den sendevorgang in die ISR verlagere, sehe ich keine Ausreißer
> mehr im plot.
> Ich weiß, man sollte möglichst wenig in der ISR tun, aber für meinen
> Fall ist das doch eigentlich nicht wichtig wie lange die ISR braucht,
> oder?

M. H. schrieb:
> wen man die Interrupts vor dem ganzen Sendevorgang ausschalte und danach
> wieder einschalte
Man soll Interrupts nur ganz kurz sperren. Denn ein Interrupt ist wie 
der Postbote: der klingelt an der Haustelefonanlage und das Klingeln 
wird vom Haustelefon gespeichert. Wenn du jetzt zu lange auf dem Klo 
gesessen hast, ist der Postbote mitsamt dem wichtigen persönlich zu 
übergebenden Brief schon wieder weg, obwohl der "Interrupt" gespeichert 
wurde.

> wen man die Interrupts vor dem ganzen Sendevorgang ausschalte und danach
> wieder einschalte
Du musst ja nur diesen Zugriff auf die 16-Bit-Variable (und die Zugriffe 
auf andere Variablen breiter als 1 char, die in ISR verändert werden) 
atomar machen. Also reicht es, die Interrupts direkt vorher zu sperren 
und am Besten sofort danach wieder freizugeben:
cli();
message.data[0] = rpm;
message.data[1] = (rpm>>8);
sei();

M. H. schrieb:
> Wenn ich den sendevorgang in die ISR verlagere, sehe ich keine Ausreißer
> mehr im plot.
Messen, nicht basteln!
Mach das mit den Pins in deinen Code und schließ das Oszi an.

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Lothar M. schrieb:
> Eric B. schrieb:
>> im Beispielcode unter
>> https://rn-wissen.de/wiki/index.php/Timer/Counter_(Avr)#Input_Capture
>> werden das high und low Byte von ICR1 separat ausgelesen
> Das macht der Compiler automatisch richtig.

Es ging mir nicht so sehr um das Regster auslesen an sich, sondern um 
diese Auswertung:
      // overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt
        if ((cap.i8m < 128) && (TIFR1 & (1<<TOV1)))
        {   // wartenden timer overflow Interrupt vorziehen
          ++ueberlauf_count;         
           TIFR1 = (1<<TOV1);    // timer overflow int. löschen, da schon hier ausgeführt
         }
Und ich habe nicht umsonst den Ich-habe-keine-Ahnung-Disclaimer 
hingepackt ;-)

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> sondern um diese Auswertung:
An dieser Stelle hast du ggfs. sowieso schon Interrupts verpasst, weil 
das du ja 24 Pulse abwartest und in der Zwischenzeit das Überlaufflag 
schon "zweimal" gesetzt wurde. Das Hochzählen des Überlaufzählers gehört 
direkt in die Overflow-ISR...

Autor: m.n. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> ++ueberlauf_count;
> TIFR1 = (1<<TOV1);    // timer overflow int. löschen, da

Das ist riskant. Sobald in der ISR zu TOV1 noch mehr ausgeführt wird, 
gibt es neue Fehler.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.