Forum: Compiler & IDEs Denkanstoß beim Zählen eines externes Interrupts/ Atmega 16


von Mapa M. (mapamann)


Lesenswert?

Hi...

Seit meinem ersten Beitrag hier (hier zu finden:
Beitrag "Hilfe bei ATmega 16" ),
habe ich schon einiges dazu gelernt bzw. verstehen können.

So funktioniert in dem Projekt aus o.g. Link inzwischen die 
Empfängerauswertung aller Kanäle, Ansteuerung der Servos per Soft 
Drive(Servo läuft ganz langsam an die Enposition, trotz des es mit einem 
Kippschalter bedient wird), Akkuspannungs- bzw. 
Akkuunterspannungserkennung via ADC, Motoransteuerung mittels H-Brücke 
und PWM und noch paar kleine Spielereien mehr...

Natürlich dank der vielen Hilfen hier:)

Motoransteuerung mittels Controller bzw. H-Brücke klappt ja.

Nun soll besagter Motor einen kolbentauchtank in einem Modelluboot 
steuern.
War eigentlich auch kein Problem das zu realisieren.
Schiebepoti Mitte an der Ferbedienung --> Motor stop
Schiebepoti nach unten bzw. oben Motor in entsprechende Richtung an 
(dabei noch unterscheidung wie weit Poti geschoben --> mittels PWM 
konnte ich dann ja noch die Drehzahl regeln, jeweils auch in beide 
Richtungen.)
Wenn Tank voll bzw. leer --> schalten Endschalter Motor ab und es kann 
dann nur erstmal in die andere Richtung der Motor angesteuert werden.

Soweit so Gut.

Nun möchte ich folgendes realisieren:

Das Schiebepoti soll nun proportional zum Tauchtank funktionieren, d.h. 
z.B poti ganz unten --> Tank leer oder Poti mitte --> Tank halb voll 
usw.

Dazu befindet sich im tank ein Hall Sensor und 2 Magneten.
Hall Sensor ist hier der auf Seite 5 beschriebene, also der Bipolare.

http://www.reichelt.de/?;ACTION=7;LA=6;OPEN=0;INDEX=0;FILENAME=B400%252FTLE4905L_TLE4935L_TLE4945L_TLE4935-2L%2523SIE.pdf;SID=32zWLsRawQASAAAD4s-4Q60c11f46858999dab241819a9820fcfb


Bekomme mit meinem Oszi auch das entsprechende Impulsdiagramm am Ausgang 
des Hall IC.
Nun möchte ich die Flankenwechsel mit dem Int0 zählen und auswerten, 
bzw. mit der aktuellen Position des Potis vergleichen, damit der Tank an 
diese Position fährt.

Dabei komme ich aber auf keinen so rechten Ansatz.
Probleme die ich dabei habe sind z.b.:

- es liegen ja nur Flanken an die ich zählen kann, wenn der Motor schon 
läuft (Wollte die Auswertung bzw. den vergleich in der ISR machen, aber 
wenn kein Interrupt kommt, läft der Motor ja nie...
)
- Vergleich aktueller Flankenwert mit Schiebepoti
(Wenn ich nur die steigenden Flanken zähle, sind es 205 von Tank leer 
bis Tank voll)

- ist es besser nur die steigenden Flanken zu zählen? oder auch die 
fallenden?(könnte ja somit halbe Umdrehungen zählen --> Auflösung 
genauer)

Meinem Empfängerimpuls kann ich mit 125 Schritten Genauigkeit erkennen 
(1-2ms mit 8µs Abtastung)

- Wäre das somit auch meine maximale Auflöung für die Ansteuerung des 
Tanks?

Ich erwarte keine fertigen Codeschnipsel, darum poste ich auch selbst 
keine.

Lediglich ein paar Denkanstöße wären schön, wie ich den Einstieg finde.

Vielen Dank fürs Erste.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bei 2 freien Timer könnte Timer A die Zeitbasis für die Abfragen liegern 
und Timer B über einen I/O-Pin von extern getaktet werden. Damit erzeugt 
das eigentliche Zählen keine IRQ-Last. In der Timer A ISR wird dann der 
Zählwert von B abgefragt un rückgesetzt.

Wie bei jeder Messung unterliegt auch diese der Unschärferelation, d.h 
für die Fehler bei der Frequenzmessung bzw. deren zeitliche Verortung 
gilt
und du musst festlegen, was du wie (un)genau brauchst.

Johann

von STK500-Besitzer (Gast)


Lesenswert?

Das ist doch ein relativ einfacher Regelkreis:
Du weisst, welchen Zustand der Tank zuletzt hatte und du weisst, wie 
lang der letzte Impuls vom Sender ist.
Wenn sich also die Impulslänge ändet, musst der Motor X Schritte in die 
entsprechende Richtung drehen. Dann merkst du dir, die neue Impulslänge 
und vergleichst sie mit der aktuellen.
Dass die beiden Werte nicht wirklich vergleichbare Skalierungen haben 
ist unglücklich, sollte aber durch ein wenige Rechnerei zu beheben sein.
Die Auflösung des Tanks zu verdoppeln sehe ich als nicht so sinnvoll an, 
da dein Sollwert ja sowieso schon eine kleinere Auflösung hat.
Sinnvoller wäre aus meiner Sicht ein oder zwei Endlagenschalter.

von Mapa M. (mapamann)


Lesenswert?

> Sinnvoller wäre aus meiner Sicht ein oder zwei Endlagenschalter.

Hi...

erstmal danke für die Antworten, bin auch inzwischen selbst schon etwas 
weiter gekommen.

Endlagenschalter besitzt der Tank sowieso, man kann ja nie wissen;)

Werde, wenn das Grundprinizp funktioniert, vielleicht versuchen das 
Enpfängersignal noch etwas höher aufzulösen. Da es aber etwas Arbeit 
ist(zumindest für mich, weil ich dann mit jedem Impuls in einen Überlauf 
rein komme und nicht wie jetzt nur in der Kanalpause --> und somit ja 
"relativ" einfach die Pause erkennen kann)

@ Johann:

Leider kein andere Timer mehr frei...max wenn ich vieleicht mein Servo 
über eine Soft PWM realisiere...damit habe ich mich allerdings noch 
nicht beschäftigt und daher keine Ahnung über Aufwand, Sinn bzw. 
Realisierung...Trotzdem danke für den Tip:)

von Mapa M. (mapamann)


Lesenswert?

Hi...

also, läuft erstmal soweit...

Sollvariable wird aus aktueller Kanallänge gebildet.

Istvariable in der ISR hoch- bzw. runtergezählt.

Da der Getriebemotor aber gut gelagert ist, hatte ich um ist == soll 
leichte Probleme den Motor zum Stillstand zu bringen. Da der Motor noch 
leicht nachlief nach dem Motor Stop Befehl. Somit gab es wieder einen 
Interrupt und ist != soll.

Habe es erstmal so gelöst, dass der Motor kurz vor ist==soll stopt, 
quasie ein kleines Intervall für ist==soll.

Nun meine Idee:

Hab leider keinen Timer mehr an meinem Atmega16 frei.
Der Hall IC gibt mir ja einen Rechteckimpuls aus.

Könnte ich diesen nicht auf einen normalen Portpin geben und so quasi 
auch zählen?(Pin auf Masse oder nicht). Es geht dabei maximal um eine 
Frequenz von 3-4Hz die der Hall IC bei maximaler Drehzahl erzeugt.
Damit hätte ich wieder einen timer frei und könnte somit den Tauchtank 
im Bereich ist==soll mit PWM ansteuern um in diesem Bereich genauer 
regeln zu können.

Meint Ihr, dass wäre möglich bzw. ein brauchbarer Ansatz?

von Karl H. (kbuchegg)


Lesenswert?

Mapa Mann schrieb:

> Nun meine Idee:
>
> Hab leider keinen Timer mehr an meinem Atmega16 frei.

Wofür benutzt du deine Timer zur Zeit?

Was sich in vielen Programmen bewährt hat:
Einen Timer exklusiv für eine 'Systemuhr' abzustellen.
Aufgabe des Timers ist es einfach nur eine Stelle zu haben, an der man 
regelmässig notwendige Dinge erledigen kann. Die Timer ISR wird so in 
etwa im Millisekunden-Takt oder auch 10 Millisekunden oder was für deine 
Applikation praktisch ist, aufgerufen und in dieser ISR werden dann all 
die Dinge erledigt, die einfach nur periodisch gemacht werden müssen. 
Die Erkennung eines 3 oder 4Hz Signals würde da zb locker drunterfallen. 
Die 5 Instruktionen um den Pegelwechsel zu detektieren und einen Zähler 
rauf/runter zu zählen, hat man in dieser ISR immer noch. So voll kann 
die ISR gar nicht sein.
Auf diese Art kann man sich dann mit einem Hardwaretimer fast beliebig 
viele 'virtuelle Timer-Zähler' bauen, die dann auch noch auf beliebige 
Port Pins gelegt werden können.

von Mapa M. (mapamann)


Lesenswert?

Karl heinz Buchegger schrieb:
> Wofür benutzt du deine Timer zur Zeit?

Hallo,

Timer 1 zur Ansteuerung meies Soft Drive für Servos
Timer 2 liest meine Empfängersignale ein
Timer 0 bis eben Flankenerkennung vom Hall IC

Dein Ansatz ist sicher super, nur leider fehlt mir gerade der Überblick 
das alles abzuschätzen bzw. in der ISR zu trennen bzw. Unterzubringen:(
Wie kann ich mehrere Dinge in der ISR trennen?
Es gibt ja nur "eine" für den entsprechenden timer.

von Mapa M. (mapamann)


Lesenswert?

Hmm...

wieso ist der Edit Button nach ner weile eigentlich weg?

Hab mir mal paar Gedanken dazu gemacht:

Könnte ja den Timmer z.B. immer überlaufen lassen um mir so einen 
Interrupt zu generieren.
Dann in der ISR die Dinge erledigen die ich möchte.
Pins toggln oder Ports abfragen ob high oder low.

Meinst du das so in etwa?

von Karl H. (kbuchegg)


Lesenswert?

Mapa Mann schrieb:

> Dein Ansatz ist sicher super, nur leider fehlt mir gerade der Überblick
> das alles abzuschätzen bzw. in der ISR zu trennen bzw. Unterzubringen:(
> Wie kann ich mehrere Dinge in der ISR trennen?
> Es gibt ja nur "eine" für den entsprechenden timer.

Macht ja nichts.
Du kannst ja in einer ISR einfach mehrere Funktionsblöcke hintereinander 
ausführen lassen.
1
ISR( ... )
2
{
3
   kümmere dich um dieses
4
5
   kümmere dich um jenes
6
7
   ach ja, und jetzt shcaust du auch noch am Pin nach, ob sich
8
   der Zustand geändert hat. Wenn ja -> Zähler erhöhen
9
}

Das große Problem ist eher, die unterschiedlichen Zeitanforderungen der 
einzelnen, in der ISR zu erledigenden Dinge, unter einen gemeinsamen Hut 
zu bringen. Wenn 'kümmere dich um dieses' alle 5ms passieren soll, 
'kümmere dich um jenes' aber alle 3ms, und der 'Pin-Zähler' alle 2ms, 
dann musst du die ISR so einstellen, dass sie zb alle 1ms aufgerufen 
wird. Bei jedem 5.ten Aufruf wird 'kümmere dich um dieses' gemacht, bei 
jedem 3ten 'kümmere dich um jenes' und bei jedem 2ten Aufruf wird der 
Pin untersucht.

1
uint8_t diesesCnt;
2
uint8_t jenesCnt;
3
uint8_t PinCnt;
4
5
ISR( ... )
6
{
7
  diesesCnt++;
8
  if( diesesCnt == 5 ) {
9
    kümmere dich um dieses
10
    diesesCnt = 0;
11
  }
12
13
  jenesCnt++;
14
  if( jenesCnt == 3 ) {
15
    kümmere dich um jenes
16
    diesesCnt = 0;
17
  }
18
19
  pinCnt++;
20
  if( pinCnt == 2 ) {
21
    ach ja, und jetzt shcaust du auch noch am Pin nach, ob sich
22
    der Zustand geändert hat. Wenn ja -> Zähler erhöhen
23
24
    pinCnt = 0;
25
  }
26
}

Jetzt hast du in einer ISR 3 unterschiedliche Aktionen untergebracht, 
die mit unterschiedlichen Timings regelmässig abgearbeitet werden. Du 
hast aus dem einen vorhandenen Hardwaretimer, der alle 1ms eine Funktion 
aufruft, 3 virtuelle Timer gemacht, die alle 5, 3 und 2ms ihre Aktion 
ausführen.

von Mapa M. (mapamann)


Lesenswert?

Hallo,

also, hab nun versucht das Prinzip des "Virtuellen Timers" umzusetzten 
und es klappt wunderbar.

Danke nochmal an Karl Heinz.

Das Problem, das der Motor nicht still stehen will um den Wert ist==soll 
habe ich aber nach wie vor.

Setze ich mit hingegen einen festen soll Wert (zu Testzwecken) steht der 
Motor sofort bei ist==soll. Generiere ich mir jedoch den Sollwert wieder 
aus meinem RC Signal geht das "Gewackel" wieder los.

Dabei hab ich nun schon wieder die Auflösung des Signals grober 
gewählt(Abtastung mit 16µs), damit eventuelle kleine Tolleranzen nicht 
so ins Gewicht fallen. Hat aber nix gebacht, der Motor will einfach 
nicht still stehen.

Hier die ISR (Aufruf im ms takt):

ISR(TIMER0_COMP_vect)
{

  if (!(PIND & (1<<PIND2)))
    {
    ihall_cnt=0;               // Pegel auf Low
    PORTC &= ~( 1 << PC4 );    //Led an PC4 aus
    }
    else
      {
      ihall_cnt=1;             // Pegel auf high
      PORTC |= (1 << PC4);     // Led an PC4 an
      }

  if (ihall_cnt!=ihall_cnt_tmp)   //fals Pegel geändert
    {
    if(ihall<isoll)
     {
      ihall++;
     }
     else
       {
      ihall--;
      }
    }


  ihall_cnt_tmp=ihall_cnt;  //Speichern des aktuellen Pegels in tmp 
variable

         isoll = ( iSumSig[4]-67)*4); //berechnung aktueller soll wert

}

Ist die Berechnung von isoll aus dem Arraywert so korrekt oder muss ich 
dabei noch etwas anderes beachten?

von Karl H. (kbuchegg)


Lesenswert?

Mapa Mann schrieb:

> Setze ich mit hingegen einen festen soll Wert (zu Testzwecken) steht der
> Motor sofort bei ist==soll. Generiere ich mir jedoch den Sollwert wieder
> aus meinem RC Signal geht das "Gewackel" wieder los.

Hast du schon nachgesehen, ob nicht das 'ist' das Problem darstellt. 
Sprich: Ob dieses wackelt.


>     if(ihall<isoll)
>      {
>       ihall++;
>      }
>      else
>        {
>       ihall--;
>       }
>     }

So wirst du ihall aber nie auf einen einzigen Wert einpendeln lassen 
können.
Wenn ihall kleiner isoll ist, erhöhst du ihall.
Irgendwann wird es größer als isoll. Woraufhin du es gleich wieder um 1 
verminderst. -> ihall pendelt immer um -1 um isoll herum. In einem ISR 
Aufruf ist es noch isoll, im nächsten wieder um 1 weniger um dann für 
den nächsten wieder auf isoll zu gehen etc. Selbst wenn sich isoll in 
der Zwischenzeit gar nicht geändert hat.

>          isoll = ( iSumSig[4]-67)*4); //berechnung aktueller soll wert
>
> }
>
> Ist die Berechnung von isoll aus dem Arraywert so korrekt oder muss ich
> dabei noch etwas anderes beachten?

Das weiß ich nicht. Ich weiß ja nicht, was in sSumSig[4] so für Werte 
drinnen stehen.

von Mapa M. (mapamann)


Lesenswert?

Hi,

der Vergleich ob größer oder kleiner wird doch nur aufgerufen wenn

if (ihall_cnt!=ihall_cnt_tmp) ist, ansonsten zählt da nix hoch oder 
runter.
Zumal ja alles klappt, wenn ich isoll z.B. auf 100 setze.Dann steht der 
Motor sofort.
Ansteuerung des Motors erfolgt ja in einer Extra Funktion, daher 
vielleicht nicht so ganz klar hier.

Aber werd mir das trotzdem nochmal genauer anschauen, danke für den Tip.

In iSumSig[4] steht die jeweilige Kanallänge des Rc Signals drin.

1100ms bis 1900ms abgetastet mit 16µs, also Werte zwischen 69 und 119. 
(denke ich zumindest)

von Michael U. (amiga)


Lesenswert?

Hallo,

ich habe mir das nicht alles angesehen, aber kann
if (ihall_cnt!=ihall_cnt_tmp)

überhaupt gleich werden (Rundungsfehler in den Berechnungen, 
Typkonvertierungen usw.)?

Gruß aus Berlin
Michael

von Mapa M. (mapamann)


Lesenswert?

Michael U. schrieb:
> Hallo,
>
> ich habe mir das nicht alles angesehen, aber kann
> if (ihall_cnt!=ihall_cnt_tmp)
>
> überhaupt gleich werden (Rundungsfehler in den Berechnungen,
> Typkonvertierungen usw.)?
>
> Gruß aus Berlin
> Michael

Hi,

ihall_cnt kann nur 0 oder 1 sein und soll nur feststellen ob sich seit 
dem letzen aufruf der ISR der Pegel vom Hall Sensor geändert hat.

von Karl H. (kbuchegg)


Lesenswert?

Mapa Mann schrieb:
> Hi,
>
> der Vergleich ob größer oder kleiner wird doch nur aufgerufen wenn
>
> if (ihall_cnt!=ihall_cnt_tmp) ist, ansonsten zählt da nix hoch oder
> runter.

Gut.
Dann wird ihall eben nicht bei jedem ISR Aufruf nachgeführt, sondern bei 
jedem Pegelwechsel am Hallsensor.
Einpegeln auf isoll wird er sich trotzdem nicht.


> der Vergleich ob größer oder kleiner wird doch nur aufgerufen wenn

"nicht kleiner" ist nicht automatisch "größer". "gleich" qualifiziert 
ebenfalls als "nicht kleiner".

Wo liegt das Problem in ...
1
     if( ihall < isoll )
2
       ihall++;
3
     else if( ihall > isoll )
4
       ihall--;

... Das ist doch kein Hexenwerk und egal was passiert hast du die 
Garantie dass ihall auf isoll nachgeführt wird und dort auch bleibt und 
nicht rumzappelt.
Auch wenn dieses Detail bei dir jetzt anscheinend keine große Rolle 
spielt, sauberer ist es trotzdem.

> 1100ms bis 1900ms abgetastet mit 16µs, also Werte zwischen 69 und 119.
> (denke ich zumindest)

Das würde ich erst mal kontrollieren.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.