Forum: Mikrocontroller und Digitale Elektronik ATmega168A Counter streikt bei Frequenz > 41kHz


von Matthias (Gast)


Lesenswert?

Hallo,

ich hab ein kleines Problem mit dem Conuter eines ATmega 168A-PU.

Die Clocksource des Timer0 wurde auf Extern und fallende Flanke 
eingestellt. Damit sollen die von einem Encoder produzierten Flanken 
detektiert werden.
Timer2 stellt den Zeitbezug her um auf eine Drehzahl schließen zu 
können.

Prinziepiell funktioniert das auch. Allerdings nur solange der Motor 
nicht mit "Vollgas" fährt.

Sobal die Encoderpulsfrequenz einen Grenzwert (ca 41kHz) überschreitet 
verweigert der Counter vollständig seinen Dienst. Die über die 
Serielleschnittstelle eingelesenen Variablen bleiben 0.
Unterhalb der genannten Frequenz liefert der Counter zwar Werte, 
allerdings sehen diese eher nach einer Alias-Frequenz aus. Das heisst 
bei geringerer Geschwindigkeit zählt der Counter mehr Pulse als bei 
höheren.

Soweit ich das im Datenblatt nachlesen konnte sollte die Grenzfrequenz 
bei dem 1/2,5 fachen des CPU-Taktes liegen. Da die CPU über einen 
externen Quarz mit 20MHz läuft, kann ich da kein Problem erkennen. Und 
auch bei 8MHz wären 42kHz auch noch unter dem geforderten Wert.

Deshalb verstehe ich nicht wirklich wo das Problem liegt. Hat jemand 
dazu eine Idee?

Quelltext(Auszug):
1
#define EXTCLK_FALING_EDGE    0x06
2
#define TIMER0_OVINT_ENABLED    TIMSK0 |= (1<<TOIE0)
3
#define TIMER2_OVINT_ENABLED    TIMSK2 |= (1<<TOIE2)
4
#define START_TIMER0      TCCR0B  = EXTCLK_FALING_EDGE
5
#define START_TIMER2      TCCR2B  = 0x07  
6
7
int main(void)
8
{
9
  setup(); //ruft unter Anderem timer() und counter() auf.
10
  OVERFLOW=0;
11
  OVERFLOW2=0;
12
13
    while(1)
14
    {
15
      sei();
16
    }
17
}
18
19
void timer()
20
{
21
  START_TIMER2;       
22
TIMER2_OVINT_ENABLED;            
23
}
24
25
void counter()
26
{
27
  START_TIMER0;  
28
TIMER0_OVINT_ENABLED;
29
}
30
31
32
ISR(TIMER0_OVF_vect)
33
{
34
  cli();
35
  overflow_flag++;
36
  if (overflow_flag==255)
37
  {
38
    overflow2_flag=1;
39
    overflow_flag=0;
40
  }
41
}
42
43
ISR(TIMER2_OVF_vect)
44
{
45
  cli();
46
  TICK_COUNT=TCNT0;
47
  tick_sum=tick_sum+TICK_COUNT;
48
  OVERFLOW=overflow_flag;
49
  OVERFLOW2=overflow2_flag;
50
  overflow_flag=0;
51
  overflow2_flag=0;
52
}

Der gesamte Quelltext ist etwas zu umfangreich um ihn hier einzustellen. 
(380 Zeilen + Header) Daher nur ein Auszug.

Um feinheiten, wie exakte Werte durch stoppen und wieder starten der 
Timer und den genauen Prescaler des Timer2 habe ich mich noch nicht 
gekümmert. Ich wollte erst einmal einen prinziepiell funktionierenden 
Code.

von Peter D. (peda)


Lesenswert?

Matthias schrieb:
> Der gesamte Quelltext ist etwas zu umfangreich um ihn hier einzustellen.

Dann häng ihn eben an.

Schnipsel bringen garnichts.
Es werden eh nur immer die Schnipsel gepostet, an denen es garantiert 
nicht liegt.
Oder die nichtmal dem Original entsprechen!

Und ja, man darf auch eigene Programme mit Kommentaren versehen, die die 
Funktion beschreiben.
Nicht jeder kann in Deinen Kopf schauen.
Und Du hast nach einem Jahr vieles vergessen.

von Uwe (de0508)


Lesenswert?

Sag mal was soll das cli(); / sei(); ?

ist doch dort wo es steht unnütz.

Auch die Angabe es AVR µC wäre hilfreich.

Einmal sei(); reicht.

Ich habe einen Frequenzzähler am laufen, der bei F_CPU = 20MHz noch bis 
9,625MHz sicher zählt.

von Matthias (Gast)


Lesenswert?

Das wäre der Wert für die Frequenzmessung, den ich ebenfalls (in etwa) 
erwartet hätte.

Das cli(); steht da um eine Rekursivität auszuschlißen.
Da das ganze Programm über ISRs läuft war es zumindest denkbar das es zu 
einem Stackoverflow kommt und so die seltsamen Werte produziert werden.

Deshalb werden erst nach beenden einer ISR mit sei(); die Interrups 
wieder freigegeben.

von Uwe (de0508)


Lesenswert?

Hallo,

Nun eine Interrupt kann ohne weiters eine schon laufende 
Interruptbearbeitung unterbrechen.

Das geht erst nach dem RETI.

von Pickett (Gast)


Lesenswert?

Wohl richtig gemeint :-)
> Nun ein Interrupt kann NICHT ohne weiters eine schon laufende
> Interruptbearbeitung unterbrechen.

darum ist cli(); unsinnig.

von Peter D. (peda)


Lesenswert?

Matthias schrieb:
> Deshalb werden erst nach beenden einer ISR mit sei(); die Interrups
> wieder freigegeben.

Lies Dir nochmal im Datenblatt den Abschnitt Interrupthandling in Ruhe 
durch.

von Matthias (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Lies Dir nochmal im Datenblatt den Abschnitt Interrupthandling in Ruhe
> durch.

Eventuell habe ich mich etwas undeutlich ausgedrückt.

Sei und Cli wurden erst eingefügt, nachdem ich merkte, dass Etwas nicht 
stimmen kann. --> Ausschlussverfahren. Ich habe lieber eine, oder zwei 
Assembleranweisungen zuviel im Code stehen als mir nicht 100% sicher zu 
sein, dass es nicht doch daran liegen könnte.

Falls das ein Hinweis auf einen Fehler sein sollte kann ich ihn nicht 
erkennen. Anderenfalls verstehe ich nicht, was es mit dem eigentlichen 
Problem zu tun hat.

von Pickett (Gast)


Lesenswert?

Matthias schrieb:
> Anderenfalls verstehe ich nicht, was es mit dem eigentlichen
> Problem zu tun hat.

Da wir nur deinen Quellcode(Auszug) kennen, sind vermutlich noch viel 
mehr Maden im Speck.

Matthias schrieb:
> Der gesamte Quelltext ist etwas zu umfangreich um ihn hier einzustellen.
> (380 Zeilen + Header) Daher nur ein Auszug.

Nur 380 Zeilen? Da hast du vermutlich die ganzen Initialisierungen 
vergessen?
Als Dateianhang fällt das nicht ins Gewicht.

von Peter D. (peda)


Lesenswert?

Matthias schrieb:
> Sei und Cli wurden erst eingefügt, nachdem ich merkte, dass Etwas nicht
> stimmen kann.

Dann hast Du gemerkt, daran liegt es nicht, aber uns kannst Du die Gurke 
ja ruhig andrehen?

Es gehört sich einfach nicht, überflüssigen Schrunz, der nichts mit dem 
Problem zu tun hat, im Code drin zu lassen.
Damit schafft man sich keine Freunde.

Man sieht es leider oft, daß bei Problemen man irgendwas verwurschteltes 
mit allen möglichen Trial&Error Codefragmenten unkommentiert vorgesetzt 
kriegt.
Ich gebe Dir mein Wort, das hebt in keinster Weise die Motivation, zu 
helfen.

Poste die erste Version, wo der Fehler zuerst aufgetreten ist und 
kommentiere, welche Funktion Du von welchem Code erwartest.

von Matthias (Gast)


Lesenswert?

Okay, entschuldigung.

Hier der Code in der Ursprünglichen Fassung:
1
#define F_CPU 20000000UL
2
3
4
#define EXTCLK_FALING_EDGE      0x06
5
#define TIMER0_OVINT_ENABLED    TIMSK0 |= (1<<TOIE0)
6
#define TIMER2_OVINT_ENABLED    TIMSK2 |= (1<<TOIE2)
7
#define START_TIMER0            TCCR0B  = EXTCLK_FALING_EDGE
8
#define START_TIMER2            TCCR2B  = 0x07  
9
#define TICK_COUNT              data
10
#define OVERFLOW                data0
11
12
#include <avr/io.h>
13
#include <avr/interrupt.h>
14
15
volatile uint8_t data=0,data0=0,overflow_flag=0;
16
17
int main(void)
18
{
19
  setup(); //ruft unter Anderem timer() und counter() auf.
20
    while(1) //Warte auf die ISRs
21
    {
22
    }
23
}
24
25
void timer()
26
{
27
  START_TIMER2;    //Prescaler 1024 entspricht ca 52ms Messzeit    
28
  TIMER2_OVINT_ENABLED;      //Gibt den Timer Overfl-IR frei      
29
}
30
31
void counter()
32
{
33
  START_TIMER0;  //Setze Clock auf extern fallende Flanke
34
  TIMER0_OVINT_ENABLED; //Gibt den Timer Overfl-IR frei 
35
}
36
37
38
// Timer 0 Zählt die vom Encoder ausgegebenen Pulse bis Timer 2 //"überläuft" 
39
ISR(TIMER0_OVF_vect)
40
{
41
  overflow_flag++; //Zeigt wie oft der Timer0 übergelaufen ist
42
}
43
44
ISR(TIMER2_OVF_vect)
45
{
46
  TICK_COUNT=TCNT0; //Schreibt den Wert des Counters in eine Variable
47
  OVERFLOW=overflow_flag; //Gibt an wie oft der Timer0 überlief
48
  overflow_flag=0;  //Flag zurücksetzen
49
50
}

So sollten alle 52ms die bis dahin gezählten Pulse übergeben werden, um 
die Drehzahl zu ermitteln.

von Moritz A. (moritz_a)


Lesenswert?

1
  setup(); //ruft unter Anderem timer() und counter() auf.

Und wo finde ich die setup(){…}?

von Spess53 (Gast)


Lesenswert?

Hi

>  START_TIMER2;    //Prescaler 1024 entspricht ca 52ms Messzeit

Nö, 13,107 ms.

MfG Spess

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.