Forum: Mikrocontroller und Digitale Elektronik Fehler mit ISR bei Laufzeitmessung


von C. M. (chrisso1986)


Lesenswert?

Hallo,

ich habe ein Problem mit meinem Programm welches die Laufzeit zwischen 
zwei Impulsen messen soll. Ich sende einen Impuls über die Funktion void 
senden und aktivieren den Overflow von Timer1 und den CAPT_VECT.

Wenn nun der ein Impuls am PIN0 detektiert wird der ISR CAPT_VECT 
aufgerufen und mir der Zählerwert per UART gesendet: Funktioniert auch. 
Falls jedoch kein Impuls innerhalb des ersten Overflows detektiert wird 
soll mit ein Fehler ausgegeben werden.

Wenn ich nun den Timer1 Overflow aktivieren springt mein Programm auch 
in den Routine rein jedoch kommt es da nicht mehr heraus und sendet mir 
nun immer den Fehler auch wenn wieder Impulse am PINB ankommen.

Anbei sende ich euch mal den Code da ich nun seid 2h nicht mehr weiter 
komme. Wenn ich den Timer1 Overflow nicht mehr aktiviere kommen 
ordentliche Wert. Wenn keine Impulse mehr ankommen steht dann halt immer 
die 65536 da.


//-----------------------------LIBRARYS----
#include <avr/io.h>                // Standard AVR LIBRARY
#include <avr/interrupt.h>            // fuer Interrupt-Funktion
#include <stdlib.h>                // fuer UTOA-Funktion

//--------------------------VARIABLEN_FUER_INTERRUPT
volatile uint8_t  timer1_low, timer1_high;  // Speichervariable fuer 
Laufzeit
volatile uint16_t  num_ovf;          // Speichervariable fuer 
Impulsbreite
volatile uint16_t  num_ovf_t1;          // Speichervariable fuer
volatile uint16_t  send_complete;        // Speichervariable fuer
volatile uint8_t  pause_senden;        // Warten mit Messdatensenden
uint16_t      time;

//------------------------------DEFINES------
#define FOSC 16000000              // Clock Speed
#define BAUD 9600                // Uebertragungsrate
#define MYUBRR FOSC/16/BAUD-1          //

#define SENDEPIN    PINB4          // Sendepin
#define SENDEPORT    PORTB          // Sendeport
#define SENDEDAUER    0xf0          // Impulsbreite
#define PAUSE      100            // Programmpause
#define VERZOEGERUNG  3            // Interne Laufzeit herausrechnen

//------------------------------INTERRUPT-SERVICE-ROUTIN--
ISR(TIMER1_CAPT_vect)               // Interrupt wird bei steigender 
Flanke am PORTB0 ausgeloest
{
  timer1_low  =  ICR1L;             // Auslesen des Low-Registers
  timer1_high  =  ICR1H;             // Auslesen des High-Registers
  TIMSK1  &=  ~(1<<TOIE1);          // Timer1 Overflow deaktivieren
  TCNT1  =  0x00;
  //PORTB  |=  (1<<PORTB5);          // zum testen
}

ISR(TIMER1_OVF_vect)              // Ueberlauf für Laufzeitmessung
{
  num_ovf_t1=1;
  //PORTB  |=  (1<<PORTB5);          //Zum testen
}

ISR(TIMER0_COMPA_vect)              // Interrupt um den Sendevorgang 
abzuschließen
{
  SENDEPORT &= ~(1<<SENDEPIN);         // Sendepin wieder loeschen
  send_complete = 1;
}

ISR(TIMER2_OVF_vect)              // Interrupt fuer erneutes Senden des 
Impulses
{
  num_ovf++;                  // Variable wird mit jedem Ueberlaufen um 
eis erhoeht
}

//---------------------------------FUNKTIONEN-------------
void senden()                  // Sendeimpuls erzeugen und 
Laufzeitzaehler starten
{
  TCNT1  =  0;
  SENDEPORT |= 1<<SENDEPIN;          // Sendepin auf High setzen
  TCNT1  =  0x00;              // Timer1 zuruecksetzen
  TCNT0  =  0x00;              // Timer0 zuruecksetzen
  TIMSK0  |=   1<<OCIE0A;            // Interrupt fuer TIMER0A 
aktivieren
  TIMSK1  |=  1<<TOIE1;            // Overflow aktivieren fuer Timer1
  while(send_complete != 1)          // While-Schleife
  send_complete = 0;               // Zuruecksetzen des Flags 
send_complete
  TIMSK0  &= ~(1<<OCIE0A);          // Interrupt fuer TIMER0A 
deaktivieren
  pause_senden=1;
}

void timer0_init()                //Timer0 fuer die Impulsbreite 
Initialisieren
{
  send_complete = 0;              //Variable zum setzen eines Flags 
wodurch ein der Interrupt
  TCCR0A = 0x00;                 //Zur Sicherheit alle Bits des 
Timer-Control Registers B auf 0 setzen
  TCCR0B = 0x00;                 //Zur Sicherheit alle Bits des 
Timer-Control Registers A auf 0 setzen
  TCCR0B |= 1<<CS00;               //Vorteiler wird auf eins eingestellt
  TCNT0 = 0x00;                //Timer0 wird zurueckgesetzt
  OCR0A  =  SENDEDAUER;            //Interrupt wird beim ueberschreiten 
von SENDEDAUER ausgeloest
  return;
}

void timer1_init()                //Pin-Change am ICP aktueller 
Zaehlerstand wird ICR1 Register gesichert
{
  TCCR1A  =  0x00;              //Konfigurationsregister wird 
zurueckgesetzt
  TCCR1B  =  0x00;              //Konfigurationsregister wird 
zurueckgesetzt
  TCCR1B  |=  1<<ICES1;             //ICES1=1 gesetzt Reaktion auf 
steigende Flanke, ICES1=0 Reaktion auf fallende Flanke
  TCCR1B  |=  1<<CS10;             //Vorteiler wird auf eins eingestellt
  TIMSK1  =  1<<ICIE1;            //Interrupts fuer den Input-Capture 
aktivieren
}

void timer2_init()                //Timer2 zum Neustarten der 
Programmroutine
{
  num_ovf  =  0;                //Zaehlervariable fuer die Overflows
  TCCR2A  =  0x00;              //Konfigurationsregister wird 
zurueckgesetzt
  TCCR2B  =  0x00;              //Konfigurationsregister wird 
zurueckgesetzt
  TCCR2B  |=  1<<CS00 | 1<<CS01 | 1<<CS02;  //Vorteiler wird auf 1024 
eingestellt
  TCNT2  =  0x00;               //Timer2 wird zurueckgesetzt
  TIMSK2  |=  1<<TOIE2;             //Aktiviere Overflow fuer Timer2
}

//----------------------------MESSDATEN_SENDEN_UEBER_USART--
void uart_send_number(uint16_t number_to_send)    // Funktion UART_send 
fuer ein 8-Bit langes Datenwort
{
  char string_to_send[16];             //Array, welches Zeichenweise die 
einzelnen Ziffern sendet
  utoa(number_to_send,string_to_send,10);      //Umwandlung Zahl zu 
einem Array, ermöglicht einzelnes senden
  uart_send_string(string_to_send);        //erstelle einen Zeiger, 
welche auf das Array char_to_send zeigt
}

void uart_send_string(char *p_string_to_send)    // Funktion UART_send 
fuer ein 8-Bit langes Datenwort
{
  while(*p_string_to_send)
  {
    while (!(UCSR0A & (1<<UDRE0))) {}      // warten bis das 
Sende-Register leer ist
    UDR0 = *p_string_to_send;
    p_string_to_send++;
  }
}

void USART_Init( unsigned int ubrr)          // Konfiguration 
USART-Schnittstelle
{
  /*Set baud rate */
  UBRR0H = (uint_fast16_t)(ubrr>>8);
  UBRR0L = (uint_fast16_t)ubrr;
  /*Enable receiver and transmitter */
  UCSR0B = (1<<RXEN0)|(1<<TXEN0);
  /* Set frame format: 8data, 2stop bit */
  UCSR0C = (1<<USBS0)|(3<<UCSZ00);
}

//-------------------------HAUPTPROGRAMM------------------
int main(void)
{
  timer0_init();          // Funktionsaufruf
  timer1_init();          // Funktionsaufruf
  timer2_init();          // Funktionsaufruf
  USART_Init(MYUBRR);        // Funktionsaufruf

  timer1_low  =  0x00;      // Speicherwariable auf null setzen
  timer1_high  =  0x00;      // Speichervariable auf null setzen
  time    =  0;        // Uebergabevariable fuer den Laufzeitmesswert
  pause_senden=  0;        // Null setzen


  DDRB    =  0xf0;      // Def. Ein-/Ausgaenge
  SENDEPORT  =  0x00;      // Ausgaenge zuruecksetzen
  PINB &= ~(1<<PINB0);
  sei();              // Interrupts aktivieren

    while(1)
    {
    senden();
      while(pause_senden!=1){};        //Warte, so lange, bis die 
festgelegte Wartezeit vorbei ist


    if(num_ovf_t1==0)            //Laufzeit senden wenn Zähler nicht 
übergelaufen ist
    {
        uart_send_number(num_ovf_t1);
        uart_send_string("\n\r");
        time = timer1_high;
        time = time<<8;
        time |= timer1_low;
        time = time-VERZOEGERUNG;
        uart_send_string("Aktueller Stand: ");
        uart_send_number(time);
        uart_send_string("\n\r");

    }

    else
    {                    //Zähler übergelaufen keinen Impuls detektiert
      uart_send_string("Fehler");
      uart_send_string("\n\r");
    }

    while (num_ovf < PAUSE){}        //Warte, so lange, bis die 
festgelegte Wartezeit vorbei ist

    pause_senden=0;              //Zuruecksetzen
    time = 0;                //Zuruecksetzen
    timer1_high = 0;            //Zuruecksetzen
    timer1_low = 0;              //Zuruecksetzen
    num_ovf_t1 = 0;              //Zuruecksetzen
      num_ovf  = 0;              //Zuruecksetzen
    PORTB  &=  ~(1<<PORTB5);
    }
}

von Wolfgang H. (Firma: AknF) (wolfgang_horn)


Lesenswert?

Hi, Chrisso,

Deinen Code habe ich mit der Einstellung "AtMega328" ohne 
Fehlermeldungen kompilieren und linken können.
Mit "PIN0" meinst Du wohl den ICP1 auf PortB, Pin0.

Was sagt Atmel-Studio denn in seinem Debugging? Dafür sind debugWIRE und 
JTAG-ICE doch da!

Die sind billiger als mein Stundensatz, multipliert mit der Zeit, die 
ich bräuchte, um Deinen Code Schritt für Schritt im Geiste zu 
simulieren.

> Wenn ich nun den Timer1 Overflow aktivieren springt mein Programm auch
> in den Routine rein jedoch kommt es da nicht mehr heraus

Auf die Schnelle ist mir nur aufgefallen:  In der Routine "void 
timer1_init(void)" setzt Du "TIMSK1  =  1<<ICIE1;", aber ohne dabei den 
anderen Interrupt abzuschalten. Ist das Absicht?


Ciao
Wolfgang Horn

von C.M. (Gast)


Lesenswert?

hallo,

Vielen Dank für deinen Beitrag. Ja also ich aktiviere ja beide 
interrupts einmal für den timer1 ovf und capt_vect ovf falls kein Impuls 
innerhalb der vorgegebenen Zeit delektiert wurde und capt_vect für die 
detektion innerhalb der Zeit.

von Wolfgang H. (Firma: AknF) (wolfgang_horn)


Lesenswert?

Ja, C.M., so habe ich das verstanden,
>
> Ja also ich aktiviere ja beide
> interrupts einmal für den timer1 ovf und capt_vect ovf falls kein Impuls
> innerhalb der vorgegebenen Zeit delektiert wurde und capt_vect für die
> detektion innerhalb der Zeit.

So hast Du das Problem beschrieben:
"Wenn ich nun den Timer1 Overflow aktivieren springt mein Programm auch
in den Routine rein jedoch kommt es da nicht mehr heraus und sendet mir
nun immer den Fehler auch wenn wieder Impulse am PINB ankommen."

Der Verdacht: Dies "nicht mehr raus" hat etwas zu tun mit dem doppelten 
Interrupt.

So etwas würde ich mir gerne ansehen mit einem einfachen Pulsgenerator 
oder einem doppelten, das können auch zwei Tasten am STK500 sein, und 
einem Debugger, der Einzelschritte zulässt.

Das Ansehen der Mediziner, der Ärzte, kommt auch weniger von deren 
Spekulationen, als vielmehr von ihrem Stethoskop, ihrem Spatel, ihrem 
Augenspiegel und ihren Instrumenden für die komplizierteren Fälle.

Deshalb ist das Debugging der nächste sinnvolle Schritt.
Sollte ein Könner das rein mental in seinem Kopf nachvollziehen und 
einen Fehler so einkreisen und finden können, dann verschwendet er in 
diesem Forum seine Arbeitskraft, denn solche Diagnosefähigkeiten werden 
viel höhrer bezahlt als Programmierfähigkeiten.

Ciao
Wolfgang Horn

von C.M. (Gast)


Lesenswert?

Hallo Wolfgang,

Vielen Dank für deinen Beitrag wenn auch etwas abschweifend.
Was ein Programmierer die Stunde kosten kann ist mir bekannt jedoch habe 
ich mir durch das Forum erhofft dass ein langjähriger Programmierer 
zufällig meinen falschen Denkansatz bei dem Programmcode erkannt und mir 
einen Tipp geben kann.

von timer (Gast)


Lesenswert?

Setzt du den overflow überhaupt jemals zurück?

von C.M. (Gast)


Lesenswert?

Ja eigentlich am Ende des Programmes.

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.