Forum: Mikrocontroller und Digitale Elektronik SYSTICK Timer Overflow Cortex M4


von StackOverflower (Gast)


Lesenswert?

Hallo,

habe einen STM32F4 und eine Frage zum systick-Timer.
Mein Code ist folgender:
1
volatile uint8_t msTicks;                                 // counts 1ms timeTicks
2
/*----------------------------------------------------------------------------
3
 * SysTick_Handler:
4
 *----------------------------------------------------------------------------*/
5
extern "C" {
6
  void SysTick_Handler(void) {
7
    msTicks++;    
8
  }
9
}
10
11
void Delay_ms(uint32_t dlyTicks) {
12
  uint32_t curTicks;
13
14
  curTicks = msTicks;
15
  while ((msTicks - curTicks) < dlyTicks) {
16
    __NOP(); 
17
  }
18
}

Beispielhaft habe ich den msTicks als char gewählt.
Wie löst man die Problematik am besten, wenn der (msTicks - curTicks) 
durch einen Überlauf (255 + 1 = 0) niemals größer dlyTicks wird?

Bsp: curTicks = 250 und dlyTicks = 10...

Müsste man eine If-Abfrage in den SysTickHandler schreiben?

Gruß

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mit einem nur 8 bit großen msTicks wirst du schwerlich Delays von
32 bit Größe erreichen können.

Andersrum wäre es kein Problem, denn das Überlaufverhalten ist bei
vorzeichenloser Arithmetik definiert.

von cfgardiner (Gast)


Lesenswert?

Der ARM v6 (Cortex M) Systick Zähler ist nach ARM ARM als 24-Bit Zähler 
definiert, der abwärts zählt.

Normalerweise, rechne ich den Startwert anhand der gewünschten Periode. 
Nach einem Systick Interrupt wird der Timer neu geladen/gestartet und 
die gewünschte periodische Tasks bzw. Task Kette gestartet.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

cfgardiner schrieb:
> Der ARM v6 (Cortex M) Systick Zähler ist nach ARM ARM als 24-Bit Zähler
> definiert, der abwärts zählt.

Das war doch gar nicht die Frage bzw. sein Problem: er hat den
SysTick (offenbar) so konfiguriert, dass er jede Millisekunde
überläuft.  Im zugehörigen Interrupt zählt er dann die Millisekunden
und will abhängig davon ein Delay steuern.  (Inwiefern ein
millisekundenlanges Delay auf einem ARM sinnvoll ist, darüber kann
man sich gewiss vortrefflich streiten …)

von Eric B. (beric)


Lesenswert?

StackOverflower schrieb:

> Wie löst man die Problematik am besten, wenn der (msTicks - curTicks)
> durch einen Überlauf (255 + 1 = 0) niemals größer dlyTicks wird?

Indem man alle beteiligte Variablen (msTick, curTicks und ldyTicks) den 
gleichen Typ gibt und diesen Typ groß genug wählt.

von W.S. (Gast)


Lesenswert?

StackOverflower schrieb:
> Wie löst man die Problematik am besten, wenn der (msTicks - curTicks)
> durch einen Überlauf (255 + 1 = 0) niemals größer dlyTicks wird?

Wenn man so an die Sache herangeht wie du, dann kann man das Problem 
eigentlich garnicht lösen. Die Möglichkeit des Überlaufes bringt das 
eben mit sich.

Du mußt es eben anders machen.

Ich halte das bei einfachen Anwendungen so, daß ich als Zählvariable 
eben ein dword nehme ("unsigned long"), welches alle 10 ms vom 
Uhrtick-Handler inkrementiert wird und dreisterweise es drauf ankommen 
lasse, daß wohl niemand die Kiste SOOO lange eingeschaltet läßt.

Bei "besseren" Anwendungen mache ich das anders: Der Uhrtick-Handler 
führt eine Systemzeit in Millisekunden, diesmal in long (also mit 
Vorzeichen) und ein Datum. Obendrein kennt er ein kleines Array von 
"delayed Events", also Ereigniscodes, wo Routinen aus der 
Anwendungsebene ein Ereignis ihrer Wahl nebst einer Uhrzeit eintragen 
lassen können. Bei jedem Uhrtick sieht der Uhrtick-Handler diese Liste 
durch und wenn er einen Eintrag findet, der abgelaufen ist, läßt er den 
zugehörigen Ereigniscode in den Event-Puffer eintragen und löscht den 
Eintrag in seiner Liste. Und damit sowas nicht überlaufen kann, wird um 
Mitternacht sowohl die Uhrzeit um genau 1 Tag zurückgestellt als auch 
alle etwaigen Einträge in der Liste um denselben Betrag rückgestellt. 
Somit kommt es prinzipiell nicht zu einem Überlaufs-Fehler.

Ja, auch wenn jetzt wieder Leute mit den Augen rollen.. nachzulesen ist 
das u.a. eben auch in den Quellen der Lernbetty. Ich hab euch ne Menge 
nützlicher Dinge vorgeturnt und wer dort seine Nase reingesteckt hat, 
braucht hier nicht händeringend nach Lösungen nachzufrragen.

W.S.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

W.S. schrieb:
> daß wohl niemand die Kiste SOOO lange eingeschaltet läßt.

Kommt mir bekannt vor.  Windows 95 hörte nach 49,7 Tagen auf, die
Maus zu bewegen …

Dabei ist unsigned overflow sowas von sauber definiert, dass man mit
ein wenig Nachdenken das völlig problemlos in den Griff bekommt.

von Fritz (Gast)


Lesenswert?

Warum verwendest du ein 8-bit unsigned für den timer?
Aus Sicht des darunterliegenden Assemblers gibt es keinen Unterschied in 
der Laufzeit ob 8 oder 32-bit! Einzig der RAM-Verbrauch ist 
möglicherweise um 1, oder 3 byte größer, wobei das aber auch 
möglicherweise durch alignments vom Compiler auch noch hinfällig sein 
kann!

von grundschüler (Gast)


Lesenswert?

vorschlag für ARM/AVR-Universal-Timer - frei nach elm chan:
1
//smh - Timer
2
3
#if zucAVR
4
5
init_timer_smh() {
6
/*
7
cs02+01+00:
8
0 no clock
9
1 no presc
10
2 /8
11
3 /64
12
4 /256
13
5 /1024
14
*/
15
   TCCR0A |= (1<<WGM01);
16
    TCCR0B |= ((1<<CS02)|(1<<CS00));//1024
17
    // 1; Timer0 Vorteiler: 64 -> bei 16 MHz: 
18
    // 16.000.000 / 64 = 250000 Mal pro Sek gibts einen Takt an den Timer
19
    
20
    OCR0A = 78;
21
    // Timer0 soll bei 250 einen Output Compare Interrupt auslösen ->
22
    // 250.000 / 250 = 1000 mal pro Sek gibts einen Interrupt
23
24
    //Compare Interrupt aktivieren
25
    TIMSK0 |= (1<<OCIE0A);
26
27
    //Globale Interrupts aktivieren
28
    sei();
29
30
}
31
32
33
ISR(TIMER0_COMPA_vect)
34
{
35
timer_proc_10ms();
36
}
37
#endif
38
39
#if zucLPC176x
40
//FEHLENDE LPC176x.h
41
#define NVIC_SYSTICK_CLK    0x00000004
42
#define NVIC_SYSTICK_INT    0x00000002
43
#define NVIC_SYSTICK_ENABLE    0x00000001
44
#define NVIC_ST_RELOAD (*(volatile uint32_t*)0xE000E014)
45
#define NVIC_ST_CTRL (*(volatile uint32_t*)0xE000E010)
46
void vF_SysTickInit(int i_CPUClockHz, int i_SystickHz)
47
{
48
  NVIC_ST_RELOAD = (( i_CPUClockHz / i_SystickHz )/10) - 1UL;
49
  NVIC_ST_CTRL = NVIC_SYSTICK_CLK | NVIC_SYSTICK_INT | NVIC_SYSTICK_ENABLE;
50
}
51
52
void SysTick_Handler (void){
53
timer_proc_10ms();
54
}
55
#endif
56
//=========================================
57
58
void timer_proc_10ms(void){
59
timer0_neu++;
60
#if USE_MMC 
61
if(Timer1)Timer1--;//100Hz f�r sd-karte
62
#endif
63
64
if(timer0_neu==100){//1sec
65
led1_tog;
66
67
if(sec==60){sec=0;  min++;
68
if(min==60){min=0; hou++;
69
if(hou==24){hou=0;}}}
70
71
timer0_neu=0;
72
}//-1sec
73
}

und für delay-Funktion:
1
#if AVR==0
2
void _delay_ms(volatile uint32_t ms) {
3
uint32_t tmp=ms*F_DELAY;
4
while(tmp--)  {  }}
5
6
void _delay_us(volatile uint32_t us) {
7
uint32_t tmp=us*F_DELAY/1000;
8
while(tmp--)  {  }}
9
#endif

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Fritz schrieb:
> Aus Sicht des darunterliegenden Assemblers gibt es keinen Unterschied in
> der Laufzeit ob 8 oder 32-bit!

Das stimmt nicht. 8-Bit-Variablen machen das Programm auf einem STM32 
wesentlich langsamer(!) als 32-Bit-Variablen, welche die "natürliche 
Breite" des Prozessor verwenden. Warum? Bei 8-Bit muss der Prozessor 
sämtliche Operationen auf 8-Bit zusätzlich maskieren.

Wenn ich auf einem STM32 tatsächlich nur eine Variable mit dem 
Wertebereich 0..255 brauche, verwende ich uint_fast8_t, um auszudrücken, 
dass ein uint8_t eigentlich reichen würde. uint_fast8_t ist aber auf dem 
STM32 32-Bit breit (identisch mit uint32_t) und läuft daher mit voller 
Geschwindigkeit. So kann ich µC-unspezifische Programmteile sowohl für 
einen ATmega (uint_fast8_t ist hier identisch mit uint8_t) als auch für 
einen STM32 kompatibel halten - bei voller Geschwindigkeit.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

grundschüler schrieb:
> vorschlag für ARM/AVR-Universal-Timer - frei nach elm chan:

Eine Zumutung. Als Einrückung werden hier 3, 4 und 0 Blanks verwendet. 
Das kann man nicht lesen.

Die Preprocessor-Konstante "zucAVR" gibts nicht. Verwende besser eine, 
die auf AVRs immer definiert ist. OCR0A = 78 ist abhängig von F_CPU. 
Also benutze das auch.

Zu timer_proc_10ms: Warum brauchst Du hier Stunden und Minuten?

Das sieht mir hier sehr nach zusammengeklaubtem Code aus, den Du selbst 
nicht verstanden hast.

: Bearbeitet durch Moderator
von grundschüler (Gast)


Lesenswert?

Frank M. schrieb:
> zusammengeklaubtem Code

Das ist zusammengeklaubter Code, den ich offensichtlich verstanden habe, 
weil ich ihn mit STMs, AVRs und LPCs zum Laufen gebracht habe.

Deine Arroganz geht mir auf den Senkel.

von Dr. Sommer (Gast)


Lesenswert?

Frank M. schrieb:
> Das stimmt nicht. 8-Bit-Variablen machen das Programm auf einem STM32
> wesentlich langsamer(!) als 32-Bit-Variablen,
Völlig übertrieben. Das Ausmaskieren ist nur nötig wenn das Ergebnis 
direkt aus dem Register weiterverarbeitet wird, denn wird es in den 
Speicher geschrieben (wie hier im SysTick_Handler), geschieht dies 
"gratis" automatisch durch die STRB Instruktion. Selbst dann wird nur 
ein einziger zusätzlicher Takt benötigt (für UXTB/SXTB). Das macht sich 
also nur dann bemerkbar, wenn das Programm ausschließlich aus 
8-Bit-Rechnungen besteht, die nicht auf den Speicher zugreifen...

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Völlig übertrieben.

Lass mal die diese Schleife auf einem STM32 laufen und miss die Zeiten.
1
volatile uint32_t m;
2
volatile uint8_t i;
3
volatile uint8_t j;
4
5
for (m = 0; m < 1000000; m++)
6
{
7
    for (i = 0; i < 255; i++)
8
    {
9
        for (j = 0; j < 255; j++)
10
        {
11
            ;
12
        }
13
    }
14
}

Anschließend setzt Du den Typ von i und j auf uint_fast8_t und misst 
nochmal. Du wirst feststellen, dass die Schleifen nun ca. 3mal so 
schnell abgearbeitet weden. Genau das hatte ich mal vor ein paar Monaten 
getestet. Leider habe ich den genauen Faktor nicht mehr im Kopf. Schlag 
mich nicht, wenn's nur Faktor 2,5 ist. Ähnlich verhält es sich übrigens 
bei uint16_t.

Okay, im konkreten Fall mag dies nicht so gravierend sein.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

grundschüler schrieb:
> Das ist zusammengeklaubter Code, den ich offensichtlich verstanden habe,
> weil ich ihn mit STMs, AVRs und LPCs zum Laufen gebracht habe.

Dein Code ist nicht übersetzbar, denn es fehlen u.a. die Definitionen 
für sec, min, hou und timer0_neu. Welchen Typ haben sie? volatile oder 
nicht volatile? 8-Bit, 16-Bit oder 32-Bit?

Nochmal: Was machen die Minuten und Stunden dort im Code? Die haben für 
Deinen Timer nichts beizutragen. Zeig doch mal ein (übersetzbares!) 
Beispiel, wie man Deinen Code plattformübergreifend nutzen soll.

> Deine Arroganz geht mir auf den Senkel.

Deine Art, wie Du überall im Forum Code hinrotzt, geht mir ebenso auf 
den Senkel und zeugt von einer ganz anderen Art von Arroganz. Man kann 
in dieser Form nämlich leider mit Deinen Codefetzen, die so gar nicht 
laufen, wenig bis gar nichts anfangen.

Wenn Du den Code aufarbeitest und in eine brauchbare Form bringst, 
muss die Arbeit nur einmal gemacht werden. Wenn aber 1000 Leser die 
Arbeit machen müssen, dann ist das 999 mal zuviel. Also zeige bitte 
etwas mehr Respekt vor Deinen Lesern und gib Dir mehr Mühe.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

StackOverflower schrieb:
> Beispielhaft habe ich den msTicks als char gewählt.
> Wie löst man die Problematik am besten, wenn der (msTicks - curTicks)
> durch einen Überlauf (255 + 1 = 0) niemals größer dlyTicks wird?
>
> Bsp: curTicks = 250 und dlyTicks = 10...
>
> Müsste man eine If-Abfrage in den SysTickHandler schreiben?

Die Frage ist, warum Du überhaupt einen Zähler msTicks die ganze Zeit 
durchlaufen lässt. Eigentlich muss der nur "laufen", wenn auch die Zeit 
gestoppt werden soll. Damit vermeidest Du das Überlauf-Problem.

Hier eine delay-Funktion mit SysTick_Handler, wo ein Überlauf überhaupt 
gar nicht erst entstehen kann:
1
#if defined (STM32F10X)
2
#include "stm32f10x.h"
3
#include "stm32f10x_rcc.h"
4
#elif defined (STM32F4XX)
5
#include "stm32f4xx.h"
6
#include "stm32f4xx_rcc.h"
7
#endif
8
9
#include <stdint.h>
10
11
static volatile uint32_t delay_counter;
12
13
void
14
SysTick_Handler(void)
15
{
16
    if (delay_counter > 0)
17
    {
18
        delay_counter--;
19
    }
20
}
21
22
void
23
delay_msec (uint32_t msec)
24
{
25
    delay_counter = msec;
26
27
    while (delay_counter != 0)
28
    {
29
        ;
30
    }
31
}
32
33
void
34
delay_init (void)
35
{
36
    SysTick_Config(SystemCoreClock / 1000);
37
}

Das Beispiel arbeitet mit einer Auflösung von 1ms und ist leicht an 
andere Auflösungen (z.B. 100us oder gar 1us) in delay_init() anpassbar. 
Läuft "Out-of-the-Box" für STM32F1xx und STM32F4xx - vorausgesetzt, die 
STM32-PLLs wurden mit SystemInit() und SystemCoreClockUpdate() vorab 
korrekt initialisiert.

Beispiel:
1
int
2
main ()
3
{
4
    SystemInit ();
5
    SystemCoreClockUpdate ();
6
    delay_init ();
7
8
    while (1)
9
    {
10
         delay_msec (1000);
11
         // do something
12
    }
13
    return 0;
14
}

: Bearbeitet durch Moderator
von Dr. Sommer (Gast)


Lesenswert?

Frank M. schrieb:
> Du wirst feststellen, dass die Schleifen nun ca. 3mal so
> schnell abgearbeitet weden. Genau das hatte ich mal vor ein paar Monaten
> getestet.
Liegt vermutlich daran, dass deine Schleifenzähler "volatile" sind 
und/oder du ohne Optimierungen kompiliert hast. Ich habe es auch mal 
ausprobiert, und uint8_t braucht ca. 25% länger als uint_fast8_t. Das 
ist auch viel realistischer, denn die meiste Zeit wird mit addieren und 
springen verbracht, der eine Zyklus mehr zum Ausmaskieren macht da 
vergleichsweise wenig aus.

Hier mein Test-Code:
1
void test1 () {
2
  uint32_t m;
3
  uint8_t i, j;
4
  for (m = 0; m < 1000000; m++) {
5
    for (i = 0; i < 255; i++) {
6
      for (j = 0; j < 255; j++) {
7
        asm volatile ("nop");
8
      }
9
    }
10
  }
11
}
12
void test2 () {
13
  uint32_t m;
14
  uint_fast8_t i, j;
15
  for (m = 0; m < 1000000; m++) {
16
    for (i = 0; i < 255; i++) {
17
      for (j = 0; j < 255; j++) {
18
        asm volatile ("nop");
19
      }
20
    }
21
  }
22
}
Das "nop" (=1 Takt) verhindert unerwünschte Compiler-Optimierungen, und 
liefert dennoch realistische Ergebnisse da eine Schleife mit 0 Takten im 
Körper unsinnig ist...

Hier trifft übrigens genau meine Bedingung von oben zu:
Dr. Sommer schrieb:
> Das macht sich
> also nur dann bemerkbar, wenn das Programm ausschließlich aus
> 8-Bit-Rechnungen besteht, die nicht auf den Speicher zugreifen...

Selbst in diesem konstruierten Extremfall ist der Unterschied "nur" 25%, 
daher würde ich "wesentlich langsamer(!)" immer noch als stark 
übertrieben ansehen. Je mehr sinnvoller Code im Schleifencode steckt, 
desto insignifikanter wird der uint8_t / uint_fast8_t Unterschied.

Frank M. schrieb:
> while (delay_counter != 0)
>     {
>         ;
>     }
Zu Stromsparzwecken könnte man in die while-Schleife noch ein asm 
volatile("wfi") einfügen, um den Core beim Warten schlafen zu legen. Das 
Aufwachen geschieht automatisch durch den Interrupt.

Hier noch der Code vom Beispiel, die markierten Zeilen entfallen in der 
uint_fast8_t Version:
1
<test1()>:
2
ldr  r1, [pc, #28]  ; (20010d84 <test1()+0x20>)
3
movs    r2, #255  ; 0xff
4
movs    r3, #255  ; 0xff
5
nop
6
subs    r3, #1
7
ands.w  r3, r3, #255  ; 0xff      <---- Hier
8
bne.n   20010d6a <test1()+0x6>
9
subs    r3, r2, #1
10
ands.w  r2, r3, #255  ; 0xff      <---- Hier
11
bne.n   20010d68 <test1()+0x4>
12
subs    r1, #1
13
bne.n   20010d66 <test1()+0x2>
14
bx      lr
15
nop
16
.word  0x000f4240

von grundschüler (Gast)


Lesenswert?

Frank M. schrieb:
> Nochmal: Was machen die Minuten und Stunden dort im Code? Die haben für
> Deinen Timer nichts beizutragen. Zeig doch mal ein (übersetzbares!)
> Beispiel, wie man Deinen Code plattformübergreifend nutzen soll.

Das ist ein code-Ausschnitt und kein vollständiges Programm. Es zeigt, 
wie man -uc-abhängig- einen systick von 10 ms definiert und dann 
uc-unabhängig mit einem Timer_process auswertet. Für M328 etc, LPC1768 
kann man den Systick direkt verwenden. In den Timer_Process muss 
anwendungsabhängig alles rein, was dort gebraucht wird. Wenn man z.B. 
eine Uhrzeit braucht, sec, min, hou für die Uhrzeit. Wenn man RTC-Zeit 
hat , lässt man das natürlich weg. Gezeigt werden sollte wie z.B. ein 
Timer  für die sd-Karte funktioniert:
#if USE_MMC
if(Timer1)Timer1--;//100Hz f�r sd-karte
#endif
Das versteht sich wohl von selbst ohne dass man USE_MMC (geklaubt von 
U.Radig) noch einmal erläutert.

Was ich deinem Beitrag immerhin entnommen habe ist, dass der code besser 
wird, wenn man einen systemabhängigen Datentyp ux für Zählvariablen 
definiert, der beim AVR ein uint8_t ist und beim ARM ein u32:

#if AVR
#define ux  uint8_t
#else
#define ux  uint32_t
#endif

von grundschüler (Gast)


Lesenswert?

Frank M. schrieb:
> OCR0A = 78 ist abhängig von F_CPU.
> Also benutze das auch.

Der Timer muss sinnvollerweise kalibriert werden. F_CPU ist ungefähr - 
bei meinen AVRs - 8MHz. Ich ermittle einmal die Konstante für den Timer 
und für das delay. Den weiteren Schritt - Rückrechnung der genauen 
CPU-Frequenz - spare ich mir, weil ich die dann nicht mehr brauche. Ich 
kann, da die genau CPU-Frequenz nicht bekannt ist, OCR0A nicht mit 
ausreichender Genauigkeit aus F_CPU ableiten.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> Liegt vermutlich daran, dass deine Schleifenzähler "volatile" sind
> und/oder du ohne Optimierungen kompiliert hast.

Ja/nein.

> Ich habe es auch mal
> ausprobiert, und uint8_t braucht ca. 25% länger als uint_fast8_t.

Auch das wäre für mich schon ein Grund, uint_fast8_t zu verwenden. Auch 
wenn die STM32 schon ziemlich flott sind, muss man die Ressourcen ja 
nicht zum Fenster rauswerfen ;-)

> Zu Stromsparzwecken könnte man in die while-Schleife noch ein asm
> volatile("wfi") einfügen, um den Core beim Warten schlafen zu legen. Das
> Aufwachen geschieht automatisch durch den Interrupt.

Das ist ein guter Tipp, danke dafür.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

grundschüler schrieb:
> Der Timer muss sinnvollerweise kalibriert werden.

Das ist aber für den Leser, dem Du die Brocken vorwirfst, überhaupt 
nicht verständlich. Zumindest solltest Du die Stelle dokumentieren. Aber 
zu Deinem Kommentar komme ich noch weiter unten.

Da die AVRs auch die Möglichkeit anbieten, einen Quarz anzuschließen, 
solltest Du für einen allgemeingültigen Code, den Du der Allgemeinheit 
zur Verfügung stellst, F_CPU verwenden. Diejenigen, die Deinen Source 
dann verwenden, müssen halt dafür sorgen, dass F_CPU annähernd korrekt 
ist - sei es durch einen Quarz oder Kalibrierung des internen 
Oszillators.

> F_CPU ist ungefähr - bei meinen AVRs - 8MHz.

Wenn Du hier Code veröffentlichst, dann spielen deine AVRs keine 
Rolle. Denn dann geht es um die AVRs des Lesers.

Deshalb ist ein
1
   OCR0A = 78;

überhaupt nicht angebracht. Was hilft dieser Wert, wenn er doch nur 
Deine AVRs betrifft?

Und jetzt kommt der Witz des Ganzen. Hättest Du geschrieben:
1
   OCR0A = F_CPU / 1024 / 100;    // prescaler is 1024, interrupt every 10msec

Dann kommt heraus:
1
8000000 / 1024 / 100 = 78

Also exakt Dein Wert! Verblüffend, oder? Der Source ist nun abhängig vom 
gewählten CPU-Takt des Users und nicht mehr von Deinen AVRs! :-)

P.S.
Dein Kommentar:
1
    OCR0A = 78;
2
    // Timer0 soll bei 250 einen Output Compare Interrupt auslösen ->
3
    // 250.000 / 250 = 1000 mal pro Sek gibts einen Interrupt

ist übrigens komplett unsinnig: Der Output Compare Interrupt wird hier 
überhaupt nicht bei 250 ausgelöst. Und es gibt auch nicht 1000mal pro 
Sekunde einen Interrupt, sondern nur 100mal pro Sekunde.

Also nochmal: hättest Du geschrieben:
1
   OCR0A = F_CPU / 1024 / 100;    // prescaler is 1024, interrupt every 10msec

Dann sieht man ganz klar die 100 Calls pro Sekunde - einfach, weil es da 
steht.

> Ich ermittle einmal die Konstante für den Timer
> und für das delay. Den weiteren Schritt - Rückrechnung der genauen
> CPU-Frequenz - spare ich mir, weil ich die dann nicht mehr brauche. Ich
> kann, da die genau CPU-Frequenz nicht bekannt ist, OCR0A nicht mit
> ausreichender Genauigkeit aus F_CPU ableiten.

Das ist Unsinn. Natürlich willst Du Deinen Oszillator möglichst genau 
laufen lassen. Aber Du willst ihn möglichst genau mit 8MHz laufen 
lassen, denn darauf kalibrierst Du ihn!

Fazit: Die Anwendung von F_CPU wäre immer der korrekte Weg gewesen.

von Dr. Sommer (Gast)


Lesenswert?

grundschüler schrieb:
> #if AVR
> #define ux  uint8_t
> #else
> #define ux  uint32_t
> #endif
Wenn schon eigene Datentypen definieren dann bitte mit typedef und nicht 
mit der Textersetzung "#define", erst recht wenn man so einen kurzen 
Namen verwendet. Stell dir mal vor jemand kommt auf die Idee eine lokale 
Variable mit dem Namen "ux" zu definieren...
Außerdem gibt es für diesen Fall eben uint_fast8_t, kein Grund was 
eigenes zu machen.

Frank M. schrieb:
> Auch das wäre für mich schon ein Grund, uint_fast8_t zu verwenden. Auch
> wenn die STM32 schon ziemlich flott sind, muss man die Ressourcen ja
> nicht zum Fenster rauswerfen ;-)
Schon, aber gerade beim Fall mit dem Delay ist der eine Takt völlig 
wurst. Da gibt es noch viel bessere Optimierungsmöglichkeiten, zum 
Beispiel gar kein Delay zu verwenden sondern "richtige" Timer... Solche 
Mikrooptimierungen kommen erst wenn man alle Algorithmen und 
Datenstrukturen schon perfekt hat :-)

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.