mikrocontroller.net

Forum: Compiler & IDEs Cortex-M3: delay_ms(), delay_us() ?


Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
p.s. Es handelt sich um einen STM32F103

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für längere Delays empfiehlt sich der Systick Interrupt.

Für kürzere Delays kann man Systick verwenden, um eine passend 
programmierte Warteschleife zu kalibrieren. Alternativ kann man direkt 
den Systick-Wert nutzen.

Autor: Patrick B. (p51d)
Datum:

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


int main(void){

  _delay_ms(100);      // Delay von 100ms
  _delay_us(100);      // Delay von 100us

return 0;
}

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).

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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>

Autor: Achim (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In der ZIP Datei der StdPeriph_Lib ist ein Examples Verzeichnis 
enthalten, darin gibt es auch ein SysTick Beispiel.

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
p.s. die Delays müssen nicht sonderlich genau sein. Ob delay_us (50) 
50us oder 55us dauert wäre nicht so wichtig.

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Achim
Jau, hab ich auch gesehen, aber ist das wirklich so aufwändig ?

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ein Beispiel für eine kalibrierte Delayloop wäre
#define DELAY_OVERHEAD (18 * (1000000000 / F_HCLK)) // Daumenpeilung

static unsigned delay_ns_per_tick = 1;

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

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Achim, tschuldigung, hatte was verwechselt.
Hm.. sieht ja ganz gut aus :-)

Autor: doc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@A.K.
wow.. DANKESCHÖN erstmal.

Ich verstehe zwar noch nicht viel davon, aber das sieht klasse aus :-)
Wie würde ich für den Fall des Falles Interrupts enablen/disablen ?

Autor: Selso (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :
typedef struct __libclk_systickInfo
{
  unsigned long ulRes_ns ; //!< cpt resolution
  unsigned long ulMax_ns ; //!< delay max appliable NS
} LIBCLK_SYSTICKINFO;

static LIBCLK_SYSTICKINFO g_systickInfo = {0};

void libclk_dlyInit( void )
{
  // Compute the max appliable

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

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

}

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

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

  if( systick_val >= dly_cnt )
  {
    dly_val = systick_val - dly_cnt;
    // wait underflow with 1 countdown detection
    while( (SysTick->VAL > dly_val) && (SysTick->VAL < systick_val) ){};
  }
  else
  {
    dly_val = SysTick->LOAD - ( dly_cnt - systick_val ) ;
    // wait underflow
    while(SysTick->VAL < systick_val);
    // wait target underflowed
    while( SysTick->VAL > dly_val ) {};
  }
}

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.

: Bearbeitet durch Moderator
Autor: Adib (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hello all,

Regarding the delay_us i would recommend to use a timer unit.

My 2 ct.

Adib.

Autor: aSma>> (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

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.