Wie füge ich eine μs-Verzögerung in der for-Schleife für diesen STM32-Code hinzu?

Ich verwende die HAL-Bibliothek und die Funktion namens HAL_TIM_PWM_PulseFinishedCallback, um die DAC-Ausgabe am Ende jedes Impulses zu aktualisieren. Hier ist der zugehörige Abschnitt des Codes:

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{

   if(htim -> Instance == TIM3)
   {
      if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
     {
          ValDAC1 = ValDAC1 + step_DAC1;

          if(ValDAC1 > 4095)
          {
              ValDAC1 = 4095;
          }
          DAC1->DHR12R1 = currValDAC1;
          pulsCount++;
          if(pulsCount > numP)
          {
              HAL_TIM_PWM_Stop_IT(&htim3, TIM_CHANNEL_1);
          }
     }
   }

   if(htim -> Instance == TIM8)
   {
      if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
     {
          if(pulsCount > numP)
          {
              HAL_TIM_PWM_Stop_IT(&htim8, TIM_CHANNEL_2);

              for(int i = 0; i<ValDAC1; i++)
              {
                  DAC1->DHR12R1 = ValDAC1 - i;
                  //Need some uS delay here 
              }
              
              DAC1->DHR12R1 = 0;

          }
     }
   }
}

Und nach einer gewissen Anzahl von Impulsen geht der DAC wie in der obigen for-Schleife auf Null. Aber ich brauche eine Verzögerung zwischen jedem für die Iteration, wie im Kommentar oben als "Benötigen Sie hier eine US-Verzögerung" gezeigt. Ich brauche dort keine Präzision, aber einige Mikrosekunden Verzögerung. Wie könnte das möglich sein?

Beachten Sie, dass einige us je nach Gerät eine beträchtliche Anzahl von CPU-Takten sein können. Manchmal kann es vorteilhaft sein, einen Timer und/oder irgendeinen Ereignismechanismus zu verwenden, um die CPU von unnötigen Verzögerungen zu entlasten.

Antworten (2)

Wenn es Ihnen egal ist, ob es blockiert (OP sagt es nicht), können Sie eine weitere for-Schleife mit einem nopInnere einfügen.

Fügen Sie diese Zeile irgendwo in die Datei ein (compilerspezifisch).
asm volatile("nop");
oder
__asm__("nop");
Dieses ST-Forum sagt, dass die letztere Syntax verwendet werden soll

Fügen Sie dann in Ihrer for-Schleife diese Schleife hinzu
for( int idx = MAX_LOOP; idx != 0; idx-- )
{
nop;
}

"nop" bedeutet nichts in der for-Schleife? Wenn ja, muss ich die Ausgabe versucht haben, aber es hat keinen Unterschied gemacht. Aber ich werde es nochmal prüfen und euch Bescheid geben.
@ cm64 Der Compiler kann es wegoptimieren. Sie können auch einige temporäre Variablen berechnen. Knirschen Sie im Grunde ein bisschen mit Zahlen.
Ich benutze normalerweise asm volatile("nop");, um Inklusion zu erzwingen.
@cm64 Siehe die Aktualisierungen
Aber wo fügen Sie hinzu: asm volatile("nop"); ? Ich habe es innen und außen versucht, es hat nicht funktioniert. Ich erhalte einen Compiler-Fehler. Wo soll diese Linie sein? Was meinst du mit "irgendwo in der Datei"?
@cm64 Die asmSyntax ist Compiler-spezifisch. Ich habe der Antwort ein weiteres Formular hinzugefügt. Sie sollten in der Dokumentation Ihres Compilers/Ihrer IDE nachsehen.
@ cm64 Was das "Wo lege ich es an?" Es ist eine Art Definition, daher wird es normalerweise am besten am Anfang der Datei platziert, nicht innerhalb einer Funktion. Sie könnten es auch in einer Header-Datei platzieren.

DWT_Initialization()-Funktion

uint32_t DWT_Delay_Init(void)
{
    /* Disable TRC */
    CoreDebug->DEMCR &= ~CoreDebug_DEMCR_TRCENA_Msk; // ~0x01000000;
    /* Enable TRC */
    CoreDebug->DEMCR |=  CoreDebug_DEMCR_TRCENA_Msk; // 0x01000000;
 
    /* Disable clock cycle counter */
    DWT->CTRL &= ~DWT_CTRL_CYCCNTENA_Msk; //~0x00000001;
    /* Enable  clock cycle counter */
    DWT->CTRL |=  DWT_CTRL_CYCCNTENA_Msk; //0x00000001;
 
    /* Reset the clock cycle counter value */
    DWT->CYCCNT = 0;
 
    /* 3 NO OPERATION instructions */
    __ASM volatile ("NOP");
    __ASM volatile ("NOP");
    __ASM volatile ("NOP");
 
    /* Check if clock cycle counter has started */
    if(DWT->CYCCNT)
    {
       return 0; /*clock cycle counter started*/
    }
    else
    {
      return 1; /*clock cycle counter not started*/
    }
}

DWT_Delay_us() Funktion

// This Function Provides Delay In Microseconds Using DWT
 
__STATIC_INLINE void DWT_Delay_us(volatile uint32_t au32_microseconds)
{
  uint32_t au32_initial_ticks = DWT->CYCCNT;
  uint32_t au32_ticks = (HAL_RCC_GetHCLKFreq() / 1000000);
  au32_microseconds *= au32_ticks;
  while ((DWT->CYCCNT - au32_initial_ticks) < au32_microseconds-au32_ticks);
}

DWT_Delay_ms() Funktion

// This Function Provides Delay In Milliseconds Using DWT
 
__STATIC_INLINE void DWT_Delay_ms(volatile uint32_t au32_milliseconds)
{
  uint32_t au32_initial_ticks = DWT->CYCCNT;
  uint32_t au32_ticks = (HAL_RCC_GetHCLKFreq() / 1000);
  au32_milliseconds *= au32_ticks;
  while ((DWT->CYCCNT - au32_initial_ticks) < au32_milliseconds);
}

Quellen:

Welchen Timer verwendet dieser?
Es verwendet die DWT-Register (Data Watchpoint Trigger).
Die Funktion DWT_Delay_Init (void) ist in main.c in Ordnung, aber für die Funktion DWT_Delay_us musste ich __STATIC_INLINE entfernen, wenn ich mich in main.c befand. Wo sollte eine Funktion mit dem Präfix __STATIC_INLINE bleiben?
Sie müssen entweder (a) einen Header einfügen, der __STATIC_INLINE, (b) selbst definiert #define __STATIC_INLINE static inline, oder (c) einfach __STATIC_INLINE durch ersetzen static inline.
Welchen Zweck hat die -au32_ticksBedingung in der Schleife: while ((DWT->CYCCNT - au32_initial_ticks) < au32_microseconds-au32_ticks);? Würde dies nicht zu um 1µs zu kurzen Verzögerungen führen? Und der volatileQualifizierer für das Argument erscheint unnötig, da CYCCNT selbst bereits volatil sein sollte?
@radioflash Der volatileQualifizierer ist notwendig, um zu verhindern, dass der Compiler "kluge" Optimierungen am Argument selbst vornimmt. Ich kann nur vermuten, dass der Zweck darin -au32_ticksbesteht, die verlorene Zeit vor dem Eintritt in die Schleife aufzuholen while. Wenn ich einen STM32 zur Hand hätte, würde ich einige Messungen mit einem Oszilloskop machen.
@Seir Ich verstehe, was der flüchtige Qualifizierer tut, aber mein Punkt ist, dass er für das Argument nicht notwendig ist, nur der Registerzugriff selbst. Sie wollen es NICHT auf dem Argument, weil das den Compiler nur dazu zwingt, unnötig komplizierten Code auszugeben. Überzeugen Sie sich selbst: godbolt.org/z/sPKd97fdv (Sie würden nur ein Problem mit dem Optimierer bekommen, wenn Sie auch den flüchtigen Qualifizierer aus dem CYCCNT-Zugriff entfernen würden).
@radioflash Du hast recht. Es reicht aus, dass CYCCNTals deklariert wird volatile. Übrigens, danke für den Link zum 'Compiler Explorer'. Scheint ein ziemlich nützliches Online-Tool zu sein.