Cortex-M3: delay_ms(), delay_us() ?

Gast #1509785
Lesenswert?

Hallo Gemeinde,

meine LED blinkt nun mit Hilfe von Millionen von Transtitoren eines 
Cortex-M3 :-)) Einer der aufwändigsten Blinker aller Zeiten :-)

Da stellt sich mir aber eine doofe Anfänger Frage:

Wo finde ich Funktionen wie _delay_ms() und _delay_us() (anlog wie beim 
avr) ?

Oder.. wie programmiere ich sowas halbwegs portabel ?
Ich nehme an, man kann den SysTick benutzen ?

thanks in advance,
doc.
#1509808
Lesenswert?

also bei avr gcc gibts die angesprochenen Funktionen schon als Header:
1
#define F_CPU   16000000
2
#include     <util/delay.h>
3

4

5
int main(void){
6

7
  _delay_ms(100);      // Delay von 100ms
8
  _delay_us(100);      // Delay von 100us
9

10
return 0;
11
}

oder du baust dir dein Delay selber mittels einem Timer und den 
entsprechenden Interrupts / Flags (je nach Wunsch).
Dann inkrementierst du dir halt eine Variable, jedesmal wenn der Timer 
einen bestimmten Wert erreicht hat (CTC).
#1509813
Lesenswert?

Nur ist ein STM32 kein AVR. Die AVR Versionen hängen von bekannten 
Befehlslaufzeiten ab. Bei komplexeren Prozessoren mit Waitstates, 
Pefetch-Puffern usw. ist das schwer portabel in den Griff zu bekommen.

Wenn man bei solchen Prozessoren mit Delayloops arbeitet, dann sollte 
man die anfangs mit einer bekannter Referenz kalibireren, um nicht von 
unbekannten Laufzeiten abhängig zu sein.
Gast #1509817
Lesenswert?

@Patrick:
Jo, für den AVR kenn ich das ja..

nützt mir nur nix, weil ich noch keine Bibliothek gefunden habe, wo das 
eingebaut wäre.
Die vom AVR funktioniert natürlich nicht.

@A.K.
Ähmm kannst Du mich in Form von ein bischen Code mal ein wenig auf den 
richtigen Weg schubsen ?

a) für Delays im us-Bereich
b) für Delays im ms-Bereich

Ich möchte die STM StdPeriph_Lib benutzen.

Außer PC9 Toggeln kann ich noch garnix auf Cortex <g>
#1509844
Lesenswert?

Ein Beispiel für eine kalibrierte Delayloop wäre
1
#define DELAY_OVERHEAD (18 * (1000000000 / F_HCLK)) // Daumenpeilung
2

3
static unsigned delay_ns_per_tick = 1;
4

5
void
6
delay_ns(unsigned ns)
7
{
8
    unsigned t = DELAY_OVERHEAD;
9
    __asm volatile(".balign 8\n"
10
       "1: add %0, %1\n"
11
       "   cmp %0, %2\n"
12
       "   blo 1b" :: "r"(t),"r"(delay_ns_per_tick),"r"(ns));
13
}
wobei ich das eher aus Gewohnheit in Assembler implentiert habe, in C 
ginge das genauso. Die Kalibierung über Systick sieht so aus:
1
    SysTick->LOAD = SYSTICK_MAXCOUNT;
2
    SysTick->VAL  = 0;
3
    SysTick->CTRL = 1<<SYSTICK_CLKSOURCE | 1<<SYSTICK_ENABLE; // use core clock (HCLK)
4
    __DSB();
5
    unsigned t1 = SysTick->VAL;
6
    delay_ns(1000 + DELAY_OVERHEAD);
7
    unsigned t2 = SysTick->VAL;
8
    __asm volatile (""::"r"(t2)); // GCC-spezifische Vorsichtsmassnahme
9
    delay_ns_per_tick = 1000000 / (F_HCLK / (t1 - t2));
Gast #4927880
Lesenswert?

Sorry I don't speak german, but the topic is interesting even it is old 
this is the only one I found that talks about short delay on cortex.

I wanted u delay based on the systick.
I am coding with ST HAL for the STM32F3xx
I did not understand the asm code above, so I wrote my own solution that 
I share here :
1
typedef struct __libclk_systickInfo
2
{
3
  unsigned long ulRes_ns ; //!< cpt resolution
4
  unsigned long ulMax_ns ; //!< delay max appliable NS
5
} LIBCLK_SYSTICKINFO;
6

7
static LIBCLK_SYSTICKINFO g_systickInfo = {0};
8

9
void libclk_dlyInit( void )
10
{
11
  // Compute the max appliable
12

13
  /*
14
   * Compute troncate to int but this does not matter, we'll                         
15
           compensate later "SystemCoreClock "
16
   */
17
  g_systickInfo.ulRes_ns = (1000000000/SystemCoreClock);
18

19
  /*
20
   * Delay on a full counwtown result in impossible thresold detection : we give 100 us margin to CPU (@72MHz) 
21
   */
22
  g_systickInfo.ulMax_ns = ((SysTick->LOAD ) *  
23
        g_systickInfo.ulRes_ns)  - 100000 ;
24

25
}
26

27
void libclk_delayNs( unsigned long dlyNs )
28
{
29
  // add 1 count to compensate division truncation
30
  unsigned long dly_cnt = dlyNs / g_systickInfo.ulRes_ns  + 1;
31
  // systick execute a countdown
32
  unsigned long dly_val =  0;
33
  unsigned long systick_val = SysTick->VAL;
34

35
  // deactivate assert when benchmarking
36
  assert( g_systickInfo.ulRes_ns && g_systickInfo.ulMax_ns);
37
  assert( dly_cnt <=  g_systickInfo.ulMax_ns);
38

39
  if( systick_val >= dly_cnt )
40
  {
41
    dly_val = systick_val - dly_cnt;
42
    // wait underflow with 1 countdown detection
43
    while( (SysTick->VAL > dly_val) && (SysTick->VAL < systick_val) ){};
44
  }
45
  else
46
  {
47
    dly_val = SysTick->LOAD - ( dly_cnt - systick_val ) ;
48
    // wait underflow
49
    while(SysTick->VAL < systick_val);
50
    // wait target underflowed
51
    while( SysTick->VAL > dly_val ) {};
52
  }
53
}

I just did test in debug mode with a gcc toolchain, the minimum I could 
do was 1,35 us. I bet the result is better when optimising but my target 
was to have us delays.
It would be better if this code was written in disassembly as the 
compilation option would not influence the result, but I am not used to 
assembly code.
Regards.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren