C Attiny13 Frequenzmessung

flecralf

Mitglied
25. Juli 2013
194
2
18
Sprachen
  1. ANSI C
Hallo alle zusammen,
seit gestern Abend beschäftige ich mich mit der Realisierung einer Frequenz oder genauer Drehzahlmessung anhand eines Attiny13. Fragen zum Timer und externem Interrupt hatte ich bereits schon mal an dieser Stelle gestellt und soweit verstanden, denke ich ;-). Aber bei der Frequenzmessung benötigt man beides, oder?

Externer Trigger INT0, positive Flanke -> Zeit zählen -> Externer Trigger --> Timer anhalten.... Ergebnis wäre dann T, die Periodendauer
oder....

Timer starten, Dauer 1 s -> positive Flanken zählen -> Timer Ende -> Ergebnis wäre dann die Frequenz f....

Mit ISR(TIM0_OVF_vect) und ISR(INT0_vect) müsste das doch machbar sein, oder?
Aber wie kann man diese verbinden?
In ISR(TIM0_OVF_vect) den ISR(INT0_vect) aufrufen?


Abweichend von der Drehzahlmessung (Drehzahl = 5000 -> 5000 U/min : 60 s = 83 Hz ) rechne ich mal mit einer maximal zu messenden Frequenz von 500 Hz.

Sind die Ideen umsetzbar und bin ich auf dem falschen Weg?
Oder wäre eine anderer Weg von vorne rein besser?
Den Code, wenn auch recht wenig, lade ich heute Abend hoch....

Viele Grüsse
Ralf
 
Hallo Ralf!

Leider hat der tiny13 nur einen 8bit-Timer und auch kein Capture Input (damit wäre es eleganter), du kannst es aber auch mit externem Interrupt machen.

Das könnte prinzipiell (!) so aussehen:



CodeBox C
uint16_t volatile timer0_msb; // für timer0 weitere 16bit.
uint32_t volatile periodendauer_isr;

ISR(TIM0_OVF_vect)
{

  // timer0_msb inkrementieren
  if (timer0_msb < 0xFFFF) timer0_msb += 1;
}


ISR(INT0_vect)
{
  // Timer0 Stop (prescaler bits löschen)

  // Timer0 und timer0_msb in volatile Variablen merken
  periodendauer_isr = TCNT0;
  periodendauer_isr |= (timer0_msb<<8);

  // ggf. hier noch für das Hauptprogramm ein Flag setzen, dass neue Werte vorhanden sind.

  // Timer0 und timer0_msb auf Null setzen
  TCNT0 = 0;
  timer0_msb = 0;

  // Timer0 Interrupt Anforderungsflag löschen

  // Timer0 Overflow Interrupt freigeben (ggf. auch einmal im Hauptprogramm machen)

  // Timer0 starten (prescaler bits so setzen, dass
  // ausreichend Auflösung für deine Anwendung, aber
  // auch so, dass der Timer nicht zu schnell läuft)

}

int main (void)
{
  uint32_t meine_periodendauer;

  // Externen Interrupt einstellen, flankengetriggert

  sei();

  while (1)
  {


   uint8_t int_reg = SREG;
   cli();
   meine_periodendauer = periodendauer_isr;
   SREG = int_reg;

   // meine_periodendauer umrechnen, auswerten, ausgeben

  }

}
 
Hallo Ralf!

Leider hat der tiny13 nur einen 8bit-Timer und auch kein Capture Input (damit wäre es eleganter), du kannst es aber auch mit externem Interrupt machen.

Das könnte prinzipiell (!) so aussehen:



CodeBox C
uint16_t volatile timer0_msb; // für timer0 weitere 16bit.
uint32_t volatile periodendauer_isr;

ISR(TIM0_OVF_vect)
{

  // timer0_msb inkrementieren
  if (timer0_msb < 0xFFFF) timer0_msb += 1;
}


ISR(INT0_vect)
{
  // Timer0 Stop (prescaler bits löschen)

  // Timer0 und timer0_msb in volatile Variablen merken
  periodendauer_isr = TCNT0;
  periodendauer_isr |= (timer0_msb<<8);

  // ggf. hier noch für das Hauptprogramm ein Flag setzen, dass neue Werte vorhanden sind.

  // Timer0 und timer0_msb auf Null setzen
  TCNT0 = 0;
  timer0_msb = 0;

  // Timer0 Interrupt Anforderungsflag löschen

  // Timer0 Overflow Interrupt freigeben (ggf. auch einmal im Hauptprogramm machen)

  // Timer0 starten (prescaler bits so setzen, dass
  // ausreichend Auflösung für deine Anwendung, aber
  // auch so, dass der Timer nicht zu schnell läuft)

}

int main (void)
{
  uint32_t meine_periodendauer;

  // Externen Interrupt einstellen, flankengetriggert

  sei();

  while (1)
  {


   uint8_t int_reg = SREG;
   cli();
   meine_periodendauer = periodendauer_isr;
   SREG = int_reg;

   // meine_periodendauer umrechnen, auswerten, ausgeben

  }

}
 
Hallo Dirk,
Danke für die Hilfe !
Bin jetzt erst mal am "Software-Basteln".... melde mich dann später wieder....
Gruß
Ralf
 
Leider hat der tiny13 nur einen 8bit-Timer
Jain - der Watchdog Timer ist quasi sowas wie ein 11bit-Timer, dessen Überläufe zur Generierung von Interrupts verwendet werden können. Der WDT ist generell an den Watchdog-Oszillator gekoppelt (mit diversen möglichen Vorteilern). Auf den eigentlichen Zähler hat man keinen Zugriff (außer daß man ihn mit einem WatchdogReset gezielt Nullsetzen kann).
Prinzipiell könnte(!) man mit dem Watchdog also eine Zeitbasis generieren - mit Genauigkeit und Möglichkeiten des WD-Oszillators, klar...

Aber was zum Thema:
Wie Du selbst bereits geschrieben hast, gibts zwei Wege. Entweder man mißt die Zeit zwischen zwei Ereignissen (zB in Form von verstrichenen Takten (zB eines Timers, ggf mit Einbeziehung eines Prescalers) -> Dirks Programm), oder man zählt die in einer festgelegten Zeit auftretenden Ereignisse (wäre bei höheren Frequenzen ggf besser umsetzbar, insbesondere wenn man zum zählen einen (zweiten) Timer als Counter verwenden kann.)
 
Jain - der Watchdog Timer ist quasi sowas wie ein 11bit-Timer, dessen Überläufe zur Generierung von Interrupts verwendet werden können. Der WDT ist generell an den Watchdog-Oszillator gekoppelt (mit diversen möglichen Vorteilern). Auf den eigentlichen Zähler hat man keinen Zugriff (außer daß man ihn mit einem WatchdogReset gezielt Nullsetzen kann).
Prinzipiell könnte(!) man mit dem Watchdog also eine Zeitbasis generieren - mit Genauigkeit und Möglichkeiten des WD-Oszillators, klar...

Also an den Watchdog hätte ich nie gedacht. Aber damit Messungen zu machen ...:confused: Da wäre der ADC Int als Taktquelle schon genauer, aber der tiny13 hat ja eh keinen :D Maximal 500Hz mit einem "erweiterten" Timer0 zu messen, ist ja eigentlich kein Problem, man muss hier nur den Prescaler gut einstellen.
 
Ich hoffe, Ralf ist jetzt nicht verunsichert und versucht nicht mit dem Watchdog die Periodendauer zu messen :confused:
 
Ich hoffe, Ralf ist jetzt nicht verunsichert und versucht nicht mit dem Watchdog die Periodendauer zu messen :confused:
Hi Dirk, hi LotadaC,
leider bekomme ich Dirks Beispiel nicht zum laufen....
für mein Verständnis..... der Weg über die Frequenz...

Sagen wir mal ich aktiviere den Timer wie folgt:


CodeBox C
uint16_t volatile impulse_p_s; // Impulse pro Sekunde
main()
{
DDRB = 1<<DDB0;
TCCR0B |= (1<<CS02);
TIMSK0 |=1<<TOIE0;
sei();
disp( impulse_p_s); //Ausgabe auf die 7-Segment-Anzeige
}

ISR(TIM0_OVF_vect) // irgendwie zurecht gewurschtelt... :)
{
  if (++timer_overflow_count > 8)
  {
  PORTB ^= 1<<PB0;
  timer_overflow_count = 0;
  impulse_p_s=0;
}

}



Damit habe ich laut Frequenzzähler eine Frequenz von 1 Hz.
Jetzt mein Verständnisproblem:
Wie kann ich den externen Interrupt einbauen?


CodeBox C
ISR(INT0_vect)
{
impulse_p_s++;
}

Wenn ich statt des Toggeln von PB0(diente nur dem Test zur Frequenzmessung) nun den externen Pin abfrage (wie in INT0) und die Impulse innerhalb der Timerperiode zählen möchte?
Auf welchen Interrupt bezieht sich sei() ?
Wo muss ich den INT0 nun aktivieren?
Gruß
Ralf
 
Hallo Ralf,

halte dich einfach an mein Beispiel (ist allerdings nur ein prinzipielles Beispiel). Nach den Kommentaren kommt in der Regel ein bisschen Code.

Zunächst musst du erst einmal den Externen Interrupt einstellen. Das passiert irgendwo nach main().

sei() aktiviert global die Interrupts.

Deine Displayausgabe müsste in eine while(1) Schleife.

Leider habe ich jetzt nicht so die Zeit, genauer drauf einzugehen. Vielleicht kann ja jemand anderes mal helfen, den Ext Int einzustellen (welche Bits werden gesetzt).

Dirk :ciao:
 
Wo muss ich den INT0 nun aktivieren?
Vielleicht kann ja jemand anderes mal helfen, den Ext Int einzustellen (welche Bits werden gesetzt).
Ich versuchs mal etwas, allerdings hab ich von C keine Ahnung. Prinzipiell mußt Du den Interrupt nur irgendwann scharfmachen bevor er das erste mal erfaßt werden soll. Außerdem mußt Du einstellen, auf welche Flanke/n der Interrupt ausgelöst werden soll.
Blick ins Datenblatt -> 9.Interrupts -> 9.3 Register Description. Seite 46.
Im MCU Control Register (MCUCR) wird mit den beiden Interrupt 0 Sense Control Bits (ISC01 und ISC00) der Trigger eingestellt.
Im General Interrupt Mask Register (GIMSK) wird der Interrupt durch setzen des External Interrupt Request 0 Bits (INT0) scharfgemacht.
Wenn I im SREG gesetzt ist (-> SEI() ), springt der Controller nun bei diesem Interrupt automatisch den Int0-Interruptvektor (Program-Flash Adresse 0x0001) an, dort muß also der Sprung in die korrespondierende Interrupt-Service-Routine stehen. Wie das in C geht, muß ein C'ler sagen (in BASCOM würde ich das zusammenbekommen, in ASM ist das eh klar).

Möglicherweise macht C das selbst für Dich ???

CodeBox C
ISR(INT0_vect){...}
 
Zuletzt bearbeitet:
Ich versuchs mal etwas, allerdings hab ich von C keine Ahnung. Prinzipiell mußt Du den Interrupt nur irgendwann scharfmachen bevor er das erste mal erfaßt werden soll. Außerdem mußt Du einstellen, auf welche Flanke/n der Interrupt ausgelöst werden soll.
Blick ins Datenblatt -> 9.Interrupts -> 9.3 Register Description. Seite 46.
Im MCU Control Register (MCUCR) wird mit den beiden Interrupt 0 Sense Control Bits (ISC01 und ISC00) der Trigger eingestellt.
Im General Interrupt Mask Register (GIMSK) wird der Interrupt durch setzen des External Interrupt Request 0 Bits (INT0) scharfgemacht.
Wenn I im SREG gesetzt ist (-> SEI() ), springt der Controller nun bei diesem Interrupt automatisch den Int0-Interruptvektor (Program-Flash Adresse 0x0001) an, dort muß also der Sprung in die korrespondierende Interrupt-Service-Routine stehen. Wie das in C geht, muß ein C'ler sagen (in BASCOM würde ich das zusammenbekommen, in ASM ist das eh klar).

Möglicherweise macht C das selbst für Dich ???

CodeBox C
ISR(INT0_vect){...}
Hi LotadaC,
ds hatte ich vergessen...
Du meinst das hier:


CodeBox C
GIMSK  |= (1<<INT0); 
 MCUCR |=(1<<ISC01) | (1<<ISC00); 



das funktioniert alleine auch... aber die Kombination mit dem Timer.... verstehe ich nicht .
 
Außerdem mußt Du einstellen, auf welche Flanke/n der Interrupt ausgelöst werden soll.
Blick ins Datenblatt -> 9.Interrupts -> 9.3 Register Description. Seite 46.
Im MCU Control Register (MCUCR) wird mit den beiden Interrupt 0 Sense Control Bits (ISC01 und ISC00) der Trigger eingestellt.
Im General Interrupt Mask Register (GIMSK) wird der Interrupt durch setzen des External Interrupt Request 0 Bits (INT0) scharfgemacht.

Gut. Danke für den Link zum Datenblatt.



CodeBox C
int main (void)
{

  // Initialisierung Ext Int
  MCUCR |= (1<<ISC01) | (0<<ISC00);  // Beispiel: ISC01=1, ISC00=0 -> The falling edge of INT0 generates an interrupt request.
  GIMSK |= (1<<INT0);  // External Interrupt Request 0 Enable
  GIFR |= (1<<INTF0);  // Das ist optional, braucht man hier eigentlich nicht, tut aber auch nicht weh: Interruptanforderungsflag löschen

  sei();  // Interrupts global freigeben.

  while (1)
  {


  }
}


Wegen dem Timer:

Dieser läuft zwischen zwei ExtInt0 Ereignissen. Du musst hier einfach nur den Wert des Timers lesen. Da du die Timerfrequenz kennst (stellst du ja ein) hast du die Periodendauer zwischen den zwei ExtInt0 Ereignissen. Im Hauptprogramm kannst du diese Periodendauer dann in Ruhe verarbeiten.
 
Gut. Danke für den Link zum Datenblatt.



CodeBox C
int main (void)
{

  // Initialisierung Ext Int
  MCUCR |= (1<<ISC01) | (0<<ISC00);  // Beispiel: ISC01=1, ISC00=0 -> The falling edge of INT0 generates an interrupt request.
  GIMSK |= (1<<INT0);  // External Interrupt Request 0 Enable
  GIFR |= (1<<INTF0);  // Das ist optional, braucht man hier eigentlich nicht, tut aber auch nicht weh: Interruptanforderungsflag löschen

  sei();  // Interrupts global freigeben.

  while (1)
  {


  }
}


Wegen dem Timer:

Dieser läuft zwischen zwei ExtInt0 Ereignissen. Du musst hier einfach nur den Wert des Timers lesen. Da du die Timerfrequenz kennst (stellst du ja ein) hast du die Periodendauer zwischen den zwei ExtInt0 Ereignissen. Im Hauptprogramm kannst du diese Periodendauer dann in Ruhe verarbeiten.

Die Timer-Zähl-Variable ist dann TCNT0 ,oder ?
 
Jain - Dirk hatte den ja mithilfe einer anderen Variable auf sechzehn Bit gestreckt. Also eher diese zusammengesetzte Variable "Periodendauer_ISR".

Der andere Weg (->Frequenz) wäre, in der INT0-ISR eine Zählvariable zu inkrementieren, und die in der Timerüberlauf-ISR auszulesen und zurückzusetzen.
 
Jain - Dirk hatte den ja mithilfe einer anderen Variable auf sechzehn Bit gestreckt. Also eher diese zusammengesetzte Variable "Periodendauer_ISR".

Der andere Weg (->Frequenz) wäre, in der INT0-ISR eine Zählvariable zu inkrementieren, und die in der Timerüberlauf-ISR auszulesen und zurückzusetzen.
Jain - Dirk hatte den ja mithilfe einer anderen Variable auf sechzehn Bit gestreckt. Also eher diese zusammengesetzte Variable "Periodendauer_ISR".

Der andere Weg (->Frequenz) wäre, in der INT0-ISR eine Zählvariable zu inkrementieren, und die in der Timerüberlauf-ISR auszulesen und zurückzusetzen.
So... ich probier' jetzt mal ein bißchen... das mit dem sei() ist mir zwar immer noch ganz klar, wann und wo man setzen muss... zumindest bei 2 Interrupts...... bei einem ist klar....
Gruß & Danke schomal und bis später Ralf
 
So... ich probier' jetzt mal ein bißchen... das mit dem sei() ist mir zwar immer noch ganz klar, wann und wo man setzen muss... zumindest bei 2 Interrupts...... bei einem ist klar....
Gruß & Danke schomal und bis später Ralf

Moin Dirk, moin LotadaC,
so... bin auf den ATMEGA8 umgestiegen......
hier mal der Code....
Jetzt nervt nur noch die 7-Segment-Anzeige :)
mit flackern, ungleichmäßigen Leuchtstärken einzelner Stellen, die Einerstelle ist taghell, die Zehnerstelle auf Sparflamme und der Rest irgendwo dazwischen......
Aber, die Frequenz wird bis ca. 50 Khz korrekt gemessen.... 500 Hz wären schon ausreichend......
Als Ersatz für die 7-Segment habe ich hier eine LCD 1602 mit I^2C-Bus Ansteuerung liegen und keine Ahnung wie das schon wieder funktioniert.....
Schon mal und nochmal Euch vielen Dank !!!!!



CodeBox C
#include <avr/io.h>
#define F_CPU 8000000UL // 8 MHz


#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>
#include <inttypes.h>
#include<util/delay.h>
#include <avr/io.h>
#include <string.h>
#include <stdio.h>
#include "lcd-routines.h"
int i;
int16_t z;
int16_t drehzahl;
int16_t x;




//#########   Drehzahlmesser ###########

#define EINGANG_PIN     PD2
#define EINGANG_DIR     DDRD
#define EINGANG_PORT   PORTD
int volatile timer_overflow_count = 0;
uint16_t volatile impulse_p_s;
uint16_t volatile meine_frequenz;



ISR(TIMER0_OVF_vect) {
GICR  |= (1<<INT0);
MCUCR |=(1<<ISC01) | (0<<ISC00);

  if (++timer_overflow_count > 30) // 8 MHz/1024 -> 7813 Hz-> 31 Overflows / Sekunde(8-Bit; 255)
  {
   timer_overflow_count = 0;
   meine_frequenz=impulse_p_s;
   impulse_p_s=0;
   GIMSK  &= ~(1 << INT0);
}

}

ISR(INT0_vect)
{
impulse_p_s++;
}

int main (void)
  {
_delay_ms(100);
disp_init();

  TCCR0 |= (1<<CS00)|(1<<CS02);
  TIMSK |= (1<<TOIE0);
  TIFR |= (1<<TOV0);

sei();

z=0;


while(1)
{

EINGANG_DIR &= ~(1 << EINGANG_PIN);

z++;

if(z > 100)
{

z=0;
disp_zaehler(meine_frequenz*60); // *60 -> U/min
}



}

}

 
Zuletzt bearbeitet:
Jetzt nervt nur noch die 7-Segment-Anzeige :)
mit flackern, ungleichmäßigen Leuchtstärken einzelner Stellen, die Einerstelle ist taghell, die Zehnerstelle auf Sparflamme und der Rest irgendwo dazwischen.....

Möglicherweise rufst du die Routine disp_zaehler zu schnell hintereinander auf. Da die Interrupts das Hauptprogramm fast garnicht "belasten" wirkt dein z nicht als Pause, grob vielleicht 15 bis 20us.
Probiere testweise einfach mal _delay_ms(500).
 
das mit dem sei() ist mir zwar immer noch ganz klar, wann und wo man setzen muss
Mir ist nicht klar, wo Du ein Verständnisproblem hast. Deswegen schieb ich jetzt einfach mal ein paar Grundlagen dazwischen.
Die AVR besitzen einen seperaten Programmspeicher (Harvard-Architektur). Dieser ist linear, also eine Liste Words. In jedem Word steht eine Maschinencode-/Assemblerinstruktion (bis auf ganz wenige Ausnahmen, die mehr als ein Word benötigen).
Außerdem gibts einen Programmzähler, der auf das Word mit der nächsten abzuarbeitenden Instruktion zeigt.

Beim Reset wird (unter anderem) dieser Programmzähler auf Null gesetzt (Ausnahme Bootloader?), der Controller beginnt also bei Adresse Null mit der Abarbeitung des Program-Flash-Inhaltes. Die Instruktion wird Verarbeitet, der Programmzähler dabei (automatisch) inkrementiert.
Bei einem Sprung (im Sinne von Goto) wird einfach der entsprechende Wert in den Programmzähler geschrieben.
Bei einem Funktionsaufruf (Call Subroutine/Gosub/...), also wo ein Rücksprung erfolgen muß, muß die Rücksprungadresse gesichert werden. Dazu wird am Ende des SRAM ein Stapel eingerichtet, der von hinten nach vorn wächst (und von vorn nach hinten schrumpft). Beim Aufruf der Funktion wird also die Adresse der nächsten Instruktion auf diesen Stapel gelegt, und die Adresse der Funktion in den Programmzähler geladen. Beim Rücksprung aus der Funktion (Return) wird die Adresse vom Stapel genommen, und in den Programmzähler geladen. Es ist also möglich, mehrere Funktionsaufrufe (auch derselben Funktion) ineinander zu schachteln - solange der Speicher für den Stapel reicht.

So, nach einem Reset beginnt der Controller wie gesagt mit Flash-Adresse Null. Die folgenden Adressen sind mit dem Interrupt-Mechanismus verbunden, deswegen steht in Adresse Null meist ein Sprung in das "Hauptprogramm".
Der Controller arbeitet den Flash-Inhalt also eigentlich linear (mit etwaigen Verzweigungen und Sprüngen) nacheinander ab. Ein Thread. Periphäre Hardware kann aber parallel dazu selbständig arbeiten (die Timer laufen im Hintergrund, und lassen ggf bei PWM die Beine Zappeln, der UART sendet und empfängt bitweise Bytes, der ADC mißt Spannungen, SPI und TWI pumpen/saugen Bytes durch ein Bein usw).
Alle diese Hardware-Module hinterlegen Dir für Dein Programm Informationen in ihren I/O-Registern (manchmal auch Special Function Register oder so genannt).
Du kannst diese Informationen also im Hauptprogramm zyklisch abfragen (pollen).
Oder eben Interrupts nutzen. Was passiert dabei konkret?
Oben hatte ich das Datenblatt des Tiny13 verlinkt, schau mal Kapitel 9 (S. 44) an.
Jede Interrupt-Quelle (beim Tiny13 sinds neun) ist fest mit einer Flash-Adresse verbunden.
Jede Quelle muß erstmal mit einem eigenen Enable-Bit scharfgemacht werden (für den externen Interrupt hast Du das mit "INT0" im "GIMSK" gemacht, für den Timerüberlauf mit "TOIE0" im "TIMSK").
Tritt ein interruptauslösendes Event ein (Timerüberlauf/Flankenwechsel usw) wird automatisch ein entsprechendes Interruptanforderungsflag gesetzt. Ist der Interrupt scharf, und sind Interrupts global enabled, werden automatisch die Interrupts global gesperrt, Flashadresse der eigentlichen nächsten Instruktion auf den Stapel gepackt und die fest mit dem Interrupt verbundene Flash-Adresse angesprungen (also sozusagen ein call subroutine auf diese Adresse mit gleichzeitigem Unterdrücken/Zurückhalten aller Interrupts - außerdem wird dabei meist das Interrupt-Anforderungsflag gelöscht).
Da eine ISR im allgemeinen nicht in diese fest verbundene Adresse paßt, steht dort üblicherweise ein Sprung in die eigentliche Service-Routine, ein Vektor. Deswegen nennt man diesen Flash-Bereich auch InterruptVectorTabelle.
Em Ende der ISR steht dann ein Return from Interrupt, welches die Rücksprungadresse vom Stapel nimmt und anspringt, und gleichzeitig die Interrupts global wieder scharfmacht.
Ein Interrupt kann also normalerweise nicht von einem anderen Interrupt unterbrochen werden.
Bei etwaigen zurückgehaltenen Interrupts, von inzwischen eingetretenen Interruptereignissen, ist ja immer noch das Anforderungsflag gesetzt, sobald die Interrupts global wieder freigegeben werden, werden die wartenden Interrupts wirksam. Die Priorität entspricht der Reihenfolge in der Interruptvektortabelle.
Ob die Interrupts global scharf sind oder nicht, legt das "I"-Bit im Statusregister fest. Dieses kann (neben der automatischen Manipulation durch den Interruptmechanismus) mit SEI (set I) gesetzt werden, mit CLI (clear I) gelöscht werden.
Das macht zB Sinn, wenn eine Operation mit einer Variable mit mehr als einem Byte durch einen Interrupt unterbrochen werden könnte, welcher die Variable zwischendurch verändert. Die AVR arbeiten ja mit einer 8bit-Aritmetik. Um also zwei Words zu addieren, werden zwei Byteadditionen benötigt. Zwischen den beiden Byteadditionen könnte ein Interrupt zuschlagen, der eine der Variablen verändert.
Um so eine Operation unteilbar - atomar - zu bekommen, unterdrückt man also vorher global alle Interrupts (CLI) und macht sie ggf hinterher wieder scharf (SEI).

Also zurück zur Frage, wann die Interrupts global scharfzumachen sind: dann, wenn Du sie das erste mal brauchst, im allgemeinen also erst, wenn alle verwendeten Interruptquellen initialisiert sind.

In dem Zusammenhang fällt mir bei Deinem Code (#18) auf, daß Du den externen Interrupt in der Timerüberlaufs-ISR initialisierst, und zwar bei jedem Aufruf (also bei jedem Überlauf). Da der Timer mindestens einmal überläuft, funktioniert das natürlich, da die Werte der drei Bits in GICR und MCUCR sich nicht ändern stört das ständige Überschreiben (mit denselben Werten) auch nicht weiter - es kostet nur unnötig Zeit.
Es wäre sinniger, das einmal wie beim Timer in der main zu machen.
Ebenso das setzen des Beines als Eingang - das muß auch nicht ständig in der while(true)-Schleife wiederholt werden...

Was soll eigentlich Zeile 42? Der Mega8 hat doch gar kein GIMSK. INT0 bis zum nächsten Timerüberlauf abschalten (eigentlich in GICR)?

Zur Displayausgabe kann ich erstmal nichts weiter sagen, bezüglich der Siebensegmente sehe ich keinen Code, ein TWI-LCD wäre komplexer, wobei Du sicher vorgefertigte C-Routinen verwenden würdest.
Aber nochwas zu Deiner Warteschleife mit "z":
Der Controller rennt mit 8MHz.
Das setzen des Beines als Eingang sollte C mit drei Takten umsetzen (oder gut optimiert mit einem Takt - @Dirk macht C daraus "IN->ANDI->OUT" oder "CBI"?)
Das "z++" sollten fünf weitere Takte sein ("LDS->INC->STS")
Das "If..." schlägt mMn mit vier Takten zu ("LDS->CPI->BRanch")
Also über den Daumen 10-20 Takte, das ganze hundertmal - dann würde disp_Zähler alle 1000-2000 Takte aufgerufen werden, also mit 4-8kHz.
Wie von Dirk angedeutet, verzögern die Interrupts das ganze nochmal ein wenig.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)