Forum: Mikrocontroller und Digitale Elektronik _delay_ms(1000) dauert ca. 1,3 Sekunden


von Michael (Gast)


Lesenswert?

Hallo,

ich habe ein Problem mit dem Timer bzw. der Clock auf einem Atmel 
Evaluation Board 2. Die _delay_ms() Funktion wartet länger als erwartet.
Ich habe ein ATMega16PU angeschlossen und im AVR Studio die Option

SUT_CKSEL auf EXTHIFXTALRES_16KCK_64MS

gestellt. Bei der Option CKOPT bin ich mir nicht sicher, auf was ich das 
stellen muss, hatte aber sowohl mit einem Häkchen als auch ohne keinen 
Erfolg. In der Doku steht nämlich:

"For resonators, the maximum frequency is 8 MHz with CKOPT unprogrammed 
and 16 MHz with CKOPT programmed." -> Also dachte ich, mache ich ein 
Häkchen. In der auf der nächste Seite abgebildeten Tabelle steht aber 
CKOPT = 0 für Frequenzen ab 1 MHZ.

Des Weiteren habe ich die Optimierung auf -Os gestellt.

Ich wollte eigentlich eine Infrarot-Fernbedienung programmieren bis ich 
gemerkt habe, dass die Timings nicht stimmen. Daher habe ich mir erstmal 
ein Minimalbeispiel erstellt, in dem ich eine LED einfach nur im 1 
Sekunden Rhythmus toggle.
1
#ifndef F_CPU
2
#define F_CPU 16000000UL
3
#warning keine F_CPU definiert
4
#endif
5
6
#include <stdint.h>
7
#include <avr/io.h>
8
#include <avr/interrupt.h>
9
#include <util/delay.h>
10
11
int main(void)
12
{
13
  DDRD = 0xff;
14
  
15
  while(1)
16
  {
17
    _delay_ms(1000);
18
    PORTD ^= (1 << PD5);
19
  }
20
}

Innerhalb von 26 Sekunden blinkt die LED allerdings nur 10 Mal. Sie 
sollte ja eigentlich 13 mal blinken.

Kann mir jemand sagen, ob ich etwas vergessen habe?

Gruß
Michael

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michael schrieb:
> Kann mir jemand sagen, ob ich etwas vergessen habe?

Ja, du hast vergessen zu sagen, was du denn als frequenzbestimmendes 
Bauteil nimmst. Resonatoren sind etwas anderes als Quarze und welche 
Quarzfrequenz du benutzt, hast du uns nicht mitgeteilt.
Für den Fall, das du einen 'echten' Quarz und keinen Keramikresonator 
benutzt, kannst du alle Resonator Optionen abschalten. Lediglich die 
beiden 22pF Kondensatoren sollten wie immer nach Datenblatt am Quarz 
hängen.
Wenn du AVR Studio benutzt, solltest du die korrekte Frequenz auch in 
den Projektoptionen eintragen.

: Bearbeitet durch User
von Pete K. (pete77)


Lesenswert?

Schau mal in Deine delay.h, ob die 1000 als Argument akzeptiert werden:
"The maximal possible delay is 262.14 ms / F_CPU in MHz."

Das F_CPU wird in den Projekteigenschaften eingetragen.

: Bearbeitet durch User
von Rudolph (Gast)


Lesenswert?

Matthias Sch. schrieb:
> Lediglich die
> beiden 22pF Kondensatoren sollten wie immer nach Datenblatt am Quarz
> hängen.

Das war ja wohl nix.
Wenn man schon einen genauen Takt haben will, dann sollten die zum 
Quarz, dem Controller und der Platine passenden Kondensatoren richtig 
verbaut sein.

von Michael (Gast)


Lesenswert?

Hallo und danke soweit für die Antworten,

die Frequenz habe ich nun als neues Symbol in den Projekteigenschaften 
eingegeben. Leider hat sich nichts verändert.

Matthias Sch. schrieb:
> Ja, du hast vergessen zu sagen, was du denn als frequenzbestimmendes
> Bauteil nimmst.

Stimmt: Ich habe ein 16 MHz Quarz auf der Platine und direkt daneben 
befinden sich die beiden 22pF Kondensatoren.

Matthias Sch. schrieb:
> Für den Fall, das du einen 'echten' Quarz und keinen Keramikresonator
> benutzt, kannst du alle Resonator Optionen abschalten.

Welche Resonator-Optionen meinst du? Die SUT_CKSEL Option? Und was 
meinst du mit abschalten?

Pete K. schrieb:
> Schau mal in Deine delay.h, ob die 1000 als Argument akzeptiert werden

Die Funktion arbeitet bis zu "262.14 ms / F_CPU in MHz" genau, danach 
mit einer Genauigkeit von 1/10 s. Sollte also auch kein Problem sein. 
Trotzdem habe ich zur Sicherheit jetzt 5 mal hintereinander ein 
_delay_ms(200) statt dem einmaligen Aufruf von _delay_ms(1000). Leider 
auch hier keine Veränderung.

Gibt es sonst noch Vorschläge?

Gruß Michael

von Georg G. (df2au)


Lesenswert?

Welche Version des Compilers und welche Studio Version benutzt du?

Probier mal als erste Zeile in deinem Programm

#define __HAS_DELAY_CYCLES 0

von Kaj (Gast)


Lesenswert?

Michael schrieb:
> Gibt es sonst noch Vorschläge?
Schau ins Datenblatt, Kapitel Timer und schreib dir deine eigene 
Delay-Funktion.
Und nein, das ist kein Scherz, ist ja auch nicht sonderlich schwer...

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Michael schrieb:
> Gibt es sonst noch Vorschläge?

Mein nächster wäre jetzt, mal den internen Oszillator zu aktivieren mit 
seinen 8 Mhz, die Projekteinstellungen und F_CPU entsprechend anzupassen 
und dann nochmal zu probieren, ob _delay_ms(1000) weiterhin zu langsam 
ist.

Rein rechnerisch müsste dein 16Mhz Quarz nämlich im Moment mit ca. 12,3 
Mhz laufen und das geht einfach nicht (egal, welchen Wert die beiden C 
haben), es sei denn, die Hersteller haben da ne falsche Frequenz 
raufgedruckt. Hast du noch andere Quarze bekannter Frequenz da? Auch 
damit könntest du nochmal gegenchecken.
Ach, _delay_ms() funktioniert übrigens richtig mit der -Os Optimierung, 
da hätte ich ja mal früher drauf kommen können.

: Bearbeitet durch User
von Michael (Gast)


Lesenswert?

Hallo,

Georg G. schrieb:
> #define __HAS_DELAY_CYCLES 0

Habe ich ganz oben eingefügt, hat aber keinen Unterschied gemacht.

Matthias Sch. schrieb:
> Mein nächster wäre jetzt, mal den internen Oszillator zu aktivieren mit
> seinen 8 Mhz, die Projekteinstellungen und F_CPU entsprechend anzupassen
> und dann nochmal zu probieren, ob _delay_ms(1000) weiterhin zu langsam
> ist.

Habe ich ebenfalls mit allen Frequenzen des internen Oszillator 
ausprobiert, aber es hat sich auch hier nichts verändert.

Kaj schrieb:
> Schau ins Datenblatt, Kapitel Timer und schreib dir deine eigene
> Delay-Funktion.

Meinst du eine richtige Delay-Funktion oder einen Interrupt gesteuerten 
Ablauf? Letzteres habe ich implementiert und siehe da: Die LED blinkt im 
1 Sekunden-Takt.
1
#include <stdint.h>
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
6
volatile uint16_t counter;
7
8
ISR ( TIMER0_OVF_vect )
9
{  
10
  // F_CPU = 16 MHz, Prescaler 1024 -> 15625 clock steps / 256 = 61 interrupts in einer Sekunde
11
  if(counter++ >= 61)
12
  {
13
    counter = 0;
14
    PORTD ^= (1 << PD5);
15
  }
16
}
17
18
int main(void)
19
{
20
  counter = 0;
21
  DDRA = 0xff;
22
  
23
  // Prescaler 1024
24
  TCCR0 |= ((1<<CS02) | (1<<CS00));
25
  TCCR0 &= ~(1 << CS01);
26
27
  TCNT0 = 0;
28
  TIMSK |= (1 << TOIE0);
29
  
30
  sei();
31
  
32
  while(1)
33
  {
34
  }
35
}

Der Quarz schwingt also richtig, der Prozessor kann es auch verarbeiten. 
Anscheinend ist die _delay_ms() Funktion wirklich ziemlich ungenau.
Im Kommentar der Funktion steht zwar, dass die Funktion bei dieser 
Frequenz bis ca. 16 ms genau arbeitet, danach aber immer noch mit einer 
1/10 s Genauigkeit bis zu 6.5 Sekunden, was ich in meinem Fall nicht 
bestätigen kann.

Danke für eure Vorschläge
Michael

von g457 (Gast)


Lesenswert?

Mach doch mal testweise sowas wie
1
for (uint16_t ui = 0; ui < 1000; ++ui)
2
   _delay_ms(1);
antelle des _delay_ms(1000) und schaug was rauskommt.

von Pete K. (pete77)


Lesenswert?

Hast Du auch wirklich alle F_CPU Befehle aus den .c und .h Dateien 
gelöscht?
Gibt der Compiler irgendwelche Warnungen aus?

Poste mal Dein ganze Projekt als zip-File.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Matthias Sch. schrieb:
> Ach, _delay_ms() funktioniert übrigens richtig mit der -Os Optimierung,
> da hätte ich ja mal früher drauf kommen können.

Hast du nochmal geschaut, welche Einstellung du für die Optimierung 
nimmst? Wie erwähnt, gehts m.W. nur mit "-Os" richtig.

: Bearbeitet durch User
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.