Forum: Compiler & IDEs delay us: Wie reparieren?


von Joachim .. (joachim_01)


Lesenswert?

Hab hier nen SAM3X8E mit f_cpu = 84MHz. Bei 1000us messe ich auf dem 
Oszi aber um die 1920us (nicht Faktor 2) am Port.

das hier steht in cycle_counter.h (ich benutze gcc):
1
void portable_delay_cycles(unsigned long n);
2
3
#if (defined __GNUC__)
4
#  define cpu_ms_2_cy(ms, f_cpu)  \
5
  (((uint64_t)(ms) * (f_cpu) + (uint64_t)(57e2-1ul)) / (uint64_t)57e2)
6
#  define cpu_us_2_cy(us, f_cpu)  \
7
  (((uint64_t)(us) * (f_cpu) + (uint64_t)(57e5-1ul)) / (uint64_t)57e5)
8
9
10
#elif (defined __ICCARM__)
11
#  define cpu_ms_2_cy(ms, f_cpu)  \
12
  (((uint64_t)(ms) * (f_cpu) + (uint64_t)(14e3-1ul)) / (uint64_t)14e3)
13
#  define cpu_us_2_cy(us, f_cpu)  \
14
  (((uint64_t)(us) * (f_cpu) + (uint64_t)(14e6-1ul)) / (uint64_t)14e6)
15
#endif
16
17
#define delay_cycles               portable_delay_cycles
18
19
#define cpu_delay_ms(delay, f_cpu) delay_cycles(cpu_ms_2_cy(delay, f_cpu))
20
#define cpu_delay_us(delay, f_cpu) delay_cycles(cpu_us_2_cy(delay, f_cpu))


Die Datei ist aus dem Atmel Framework. Scheinbar gehört der Code aber zu 
einem anderen uC. Wie kann ich die Berechnungen korrigieren damit ich
cpu_delay_us(delay, f_cpu)
vernünftig benutzen kann?
Und was hat's mit den unterschiedlichen Anhängseln für gcc und IAR auf 
sich?

von Uwe Bonnes (Gast)


Lesenswert?

Laeuft der Systick Timer? Dann haengt Dich an den ran. Etwa wie in 
Nut/Os
1
void NutMicroDelay(uint32_t us)
2
{
3
    int32_t start_ticks;
4
    int32_t current_ticks, summed_ticks=0;
5
    int32_t end_ticks;
6
7
    start_ticks = SysTick->VAL;
8
    end_ticks = (us * (SysTick->LOAD +1))/SYS_TICK_FREQ;
9
/* Systick counts backwards! */
10
    while (summed_ticks < end_ticks)
11
    {
12
        current_ticks = SysTick->VAL;
13
        summed_ticks += start_ticks - current_ticks ;
14
        if (current_ticks > start_ticks)
15
            summed_ticks += (SysTick->LOAD +1);
16
        start_ticks = current_ticks;
17
    }

von Joachim .. (joachim_01)


Lesenswert?

>Laeuft der Systick Timer? Dann haengt Dich an den ran.

Ja, das hab ich auch schon überlegt.
Das freundlicherweise mitgelieferte mdelay() liefert Mllisekunden, ich 
hab die Timer samt ISR aber bis jetzt immer zu umgehen versucht ;-) 
Wobei der System-Timer wohl einfacher ist. Ich werd's in den nächsten 
Tagen angehen.

Danke.

von Peter D. (peda)


Lesenswert?

Uwe Bonnes schrieb:
> void NutMicroDelay(uint32_t us)
> ...

Das ist ja ganz schön viel Holz.
Es geht auch deutlich einfacher.

Hier ein Delay mit Timer auf dem AVR:
1
void delay( int16_t d )          // d = 0 ... 32000 ticks
2
{
3
  d += TCNT1;
4
  while( (TCNT1 - d) & 0x8000 ); // until positive
5
}

Der Trick dabei ist, daß es überlauffest ist (Differenzen stimmen 
immer).

Bei 32Bit wird der Überlauf zwar seltener sein, muß aber trotzdem 
berücksichtigt werden.

Uwe Bonnes schrieb:
> end_ticks = (us * (SysTick->LOAD +1))/SYS_TICK_FREQ;

Erfordert aber zwingend einen MC mit Divisionsbefehl, sonst kommt da 
noch massig Delay hinzu.

von Uwe Bonnes (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Das ist ja ganz schön viel Holz.
> Es geht auch deutlich einfacher.
>
> Hier ein Delay mit Timer auf dem AVR:void delay( int16_t d )          //
> d = 0 ... 32000 ticks
> {
>   d += TCNT1;
>   while( (TCNT1 - d) & 0x8000 ); // until positive
> }
>
> Der Trick dabei ist, daß es überlauffest ist (Differenzen stimmen
> immer).
>

Peter, der SysTick Timer beim Cortex dient meist Zeitbasis und laeuft 
daher sowieso. Ausserdem ist er bei allen Cortex Type vorhanden, 
waehrend die anderen Timer herstellerspezifisch sind. Allerdings zaehlt 
der Systick im Betrieb als Zeitbasis halt nur von 0 bis x und nicht bis 
zum Ueberlauf, daher die komplexere Arithmetik.

> Bei 32Bit wird der Überlauf zwar seltener sein, muß aber trotzdem
> berücksichtigt werden.
>
> Uwe Bonnes schrieb:
>> end_ticks = (us * (SysTick->LOAD +1))/SYS_TICK_FREQ;
>
> Erfordert aber zwingend einen MC mit Divisionsbefehl, sonst kommt da
> noch massig Delay hinzu.

Irgendwo hatte ich (SysTick->LOAD +1))/SYS_TICK_FREQ auch schon mal 
waehrend des Startups berechnet und dann in NutMicroDelay nur noch 
multipliziert. Der Code ist aber irgendwie nicht im Repsoitory 
angekommen. Danke fuer die Codereview!

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.