mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Timer tickt durcheinander (AVR-C)


Autor: Hegy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Club,

ich habe ein Problem mit einen 16-Bit Timer beim ATmega48 mit 4Mhz 
Quarz. Alle 2 bis 3 Sek. hat der Timer einen "Schluckauf", sodaß eine 
normalerweise gleichmäßig blinkende LED unregelmäßig blinkt (Periode zu 
kurz). Auch eine Periodenmessung bestätigt das.

Ersma der Code:
// Variablendefinition, Bits und Flags
union Bitfield
{
  unsigned int allbits;
  struct
  {
    unsigned char sleep:1;                 // 1 = sleep()-Fkt. aktiv
    [weitere 15 Variablen, Typ char]
  };
};

static volatile union Bitfield flag;

static volatile unsigned int waittime;


// 16-Bit Timer Compare Match mit quasi Autoreload, wird alle 0.5 ms aufgerufen
ISR(SIG_OUTPUT_COMPARE1A)
{
  if(flag.sleep) // wird in sleep()-Fkt. (rück)gesetzt
    waittime++;
}


// 16-Bit Timer1, 0.5 ms Wartezeit
void sleep(unsigned int wait)
{  
  waittime = 0;
  flag.sleep = 1;
  
  do
  {
    asm volatile("NOP");
  }while(waittime < wait);

  flag.sleep = 0;
  //TCNT1 = 0;
}


int main(void)
{
  // Initialisierung des 16-Bit Timers, Interrupt alle 0,5 ms bei 4MHz Quarz
  TCNT1 = 0;  // Timer Counter Register initialisieren
  TCCR1B = (1<<WGM12) | (1<<CS10);  // Prescaler = 1, CTC Modus
  OCR1A  = 1999;    // Compare Match Register, gemessene Zeit: 0,5ms @4MHz
  TIFR1  = 1<<ICF1;
  TIMSK1 = 1<<OCIE1A; // Interruptfreigabe bei Compare Match

  flag.allbits = 0;
  
  wdt_disable();

  // zur Vollständigkeit halber
  UART_init(UART_BAUD_SELECT(4800, F_CPU));
  TWI_SlaveInit(SLAVE_ADR);

  sei();
  
  while(1)
  {
    TWI_Start();
    
    UCSR0B = 0; // disable UART
    UCSR0C = 0;
    DDRD = 0xFF;
    PORTD &= 0xFD;
    PORTC = 0xF7;
    while(1)
    {
      sleep(400);
      PORTD |= 0x02; // PORTD1 auf 1
      PORTC = 0xFF;
      sleep(400);
      PORTD &= 0xFD; // PORTD1 für 200ms auf 0
      PORTC = 0xF7;
    }
  }
}

Diese Proggi sollte nichts weiters machen als eine LED an PORTC blinken 
zu lassen, gleichzeitig wird auch PORTD1 geändert, an dem ich gemessen 
habe.

Was mir bei der Fehlersuche aufgefallen ist, daß die Periodendauer sich 
sehr stark ändert, wenn ich OCR1A um 1 ändere. Hier mal die Meßwerte.
Deutlich zu sehen an den OCR1A-Werten von 1998 bis 2000, abweichende 
Zeilen habe ich mal mit nem '?' markiert.
/*
           ist      soll
 OCR1A |  t(ms)  |  t(ms)
-------+---------+-------
 1000    200,235    200
 1400    229,796 ?  280
 1500    300,228    300
 1998    327,861 ?  399
 1999    400,023    400
 2000    238,189 ?  400
 2999    492,015 ?  600
 3000    600,211    600
*/

Woher kommt das?
Ich zum messen in den Code nur den OCR1A-Wert geändert und gemessen. 
Irgendwas ist da mit der sleep()-Geschichte in der inneren 
while(1)-Schleife faul, meine ich, aber finde nicht raus, was es ist.

Wenn ich in der sleep()-Fkt. die auskommentierte Zeile TCNT0=0 
aktiviere, ist der "Schluckauf" weg, aber in die Tabelle oben ändert 
sich nichts an den Meßwerten.

Ratlos
 Hegy

Autor: antworter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich würde testweise JEDEN Zugriff auf waittime mit cli() sei() 
einschliessen (nur im INT nicht)



und nebenbei

  do
  {
    asm volatile("NOP");
  }while(waittime < wait);

verkürzen zu

  while(waittime < wait);

Autor: antworter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach ja.. die Schleife wird natürlich zu

while(1)
{
  cli();
  if (waittime >= wait) break;
  sei();
}
sei(); // nachdem Schleife durch "break" verlassen

Autor: Hegy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
supi, so gehtz!

Nur warum müssen unbedingt alle Interrupts gesperrt werden bei diesem 
Vergleich? waittime wird 'nur' alle 0,5ms hochgezählt, der Vergleich in 
der do...while-Schleife ist wesentlich schneller und selbst wenn während 
des Vergleichens waittime wiederrum eins hochgezählt werden sollte, ist 
das Limit erreicht und die do...while-Schleife wird verlassen, das Bit 
flag.sleep wird auf 0 gesetzt und damit wird waittime in der ISR nicht 
mehr hochgezählt.

Wenn ich in der Schleife das cli() auskommentiere und OCR1A mit 1998 
initialisiere erhalte ich eine Periodendauer von 327ms (soll 399ms), bei 
aktiviertem cli() sind's 399,8ms, was rein rechnerisch auch past.

Nur den Sinn von cli() an dieser Stelle verstehe ich nicht.

Autor: antworter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich sag es mal kurz:

nichtsequenzieller/überschneidender Zugriff auf eine 16Bit-Variable auf 
einer 8Bit-Architektur

(die lange Version mußt Du Dir ergrübeln)

Autor: Hegy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nee du, ergrübel mir nix, ist schon klar, auf einem 8-Bitter mit 16 Bit 
rumzukacheln ist schon etwas Hallas (=Aufwand), schlimmer wirds bei 
Fließkommazahlen. Wer es mal in plain Assembler gemacht hat.....

Oder steckt da noch mehr hinter?


Mal die Assembler Listings zum Vergleich dazu.
    neue Version           Buggy-Version

    movw    r18, r24       movw    r18, r24
    sts     0x0113, r1     sts     0x0113, r1
    sts     0x0112, r1     sts     0x0112, r1
    lds     r24, 0x0102    lds     r24, 0x0102 
    ori     r24, 0x02      ori     r24, 0x02
    sts     0x0102, r24    sts     0x0102, r24
    cli                    nop
    lds     r24, 0x0112    lds     r24, 0x0112
    lds     r25, 0x0113    lds     r25, 0x0113
    cp      r24, r18       cp      r24, r18
    cpc     r25, r19       cpc     r25, r19
    brcc    .+4            brcs    .-16
    sei                    lds     r24, 0x0102
    rjmp    .-20           andi    r24, 0xFD
    sei                    sts     0x0102, r24
    lds     r24, 0x0102    sts     0x0085, r1
    andi    r24, 0xFD      sts     0x0084, r1
    sts     0x0102, r24    ret
    ret

    56 Bytes lang          54 Bytes lang
Eigentlich kein Riesenunterschied, aber der Unterschied von 
alle-0,5ms-Variable-hochzählen und der Ausführungszeit von 54/56 Bytes 
Assemblercode ohne zeitaufwendige Befehle (z.B. div, mul beim 8086) ist 
groß und immernoch ist mir nicht klar, warum genau hier der Hund 
begraben liegt.

Autor: antworter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Nee du, ergrübel mir nix, ist schon klar.."

"...immernoch ist mir nicht klar, warum genau hier der Hund begraben 
liegt"


ohne worte

Autor: Hegy (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hättest du den ersten Satz vollständig hingeschrieben, dann würde man 
sehen, daß das eine Unterthema nichts mit dem anderen Unterthema zu tun 
hat. Zuerst ging es um 16-Bit Operationen auf 8-Bit Prozessoren und 
unten dann darum, warum eine im us Bereich dauerne Operation 
(sleep-Funktion einmalig durchlaufen) durch einen wesentlich längeren 
Vorgang (hochzählen einer Variable im 0,5ms Takt) derart behindert wird, 
daß es hier zu Fehlfunktionen kommen kann.

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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