3
\$\begingroup\$

I'm using HAL library and the finction called HAL_TIM_PWM_PulseFinishedCallback to update DAC output at the end of each pulse. Here is the related section of the code:

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;

          }
     }
   }
}

And after some number of pulses the DAC goes to zero as in the for loop above. But I need a delay between each for iteration as shown in the comment as "Need some uS delay here" above. I dont need precision there but some microseconds of delay. How could that be possible?

\$\endgroup\$
1
  • \$\begingroup\$ Note that a few us might be a significant number of cpu clocks depending on the actual device. Sometimes it might be advantageous to utilise a timer and/or any event mechanism to relieve the cpu of wasteful delays. \$\endgroup\$
    – Kartman
    Feb 17 at 3:36

2 Answers 2

5
\$\begingroup\$

If you don't care if it blocks (OP doesn't say) You can insert another for loop with a nop inside.

Put this line somewhere in the file (compiler specific).
asm volatile("nop");
or
__asm__("nop");
This ST Forum says to use the latter syntax

Then inside your for loop add this loop
for( int idx = MAX_LOOP; idx != 0; idx-- )
{
nop;
}

\$\endgroup\$
7
  • \$\begingroup\$ "nop" means nothing inside the for loop? If so I must have tried output but didnt make difference. But I will double check it and let you know. \$\endgroup\$
    – cm64
    Feb 16 at 18:14
  • 1
    \$\begingroup\$ @cm64 the compiler may optimize it away. You can do some temp variable calculation too. Basically crunch numbers for a bit. \$\endgroup\$
    – Ilya
    Feb 16 at 18:17
  • 4
    \$\begingroup\$ I usually use asm volatile("nop"); to force inclusion. \$\endgroup\$
    – Majenko
    Feb 16 at 18:21
  • \$\begingroup\$ @cm64 See the updates \$\endgroup\$
    – Aaron
    Feb 17 at 0:16
  • \$\begingroup\$ But where do you add: asm volatile("nop"); ? I tried inside main and outside it didnt work. I get compiler error. Where should that line be? What do you mean by "somewhere in the file"? \$\endgroup\$
    – cm64
    Feb 17 at 10:42
4
\$\begingroup\$

DWT_Initialization() Function

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() Function

// 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() Function

// 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);
}

Sources:

\$\endgroup\$
8
  • \$\begingroup\$ Which timer does this use? \$\endgroup\$
    – cm64
    Feb 16 at 18:13
  • \$\begingroup\$ It uses the DWT (data watchpoint trigger) registers. \$\endgroup\$
    – Seir
    Feb 16 at 18:17
  • \$\begingroup\$ DWT_Delay_Init(void) function is fine in main.c but for DWT_Delay_us fucntion I had to remove __STATIC_INLINE when inside main.c. Where should a function with __STATIC_INLINE prefix stay? \$\endgroup\$
    – cm64
    Feb 17 at 10:53
  • \$\begingroup\$ You either have to (a) include a header which specifies __STATIC_INLINE, (b) define it yourself #define __STATIC_INLINE static inline, or (c) simply replace __STATIC_INLINE with static inline. \$\endgroup\$
    – Seir
    Feb 17 at 13:17
  • 1
    \$\begingroup\$ @Seir I understand what the volatile qualifier does, but my point is that it's not necessary on the argument, only the register access itself. You DONT want it on the argument because that just forces the compiler to emit needlessly convoluted code. Just see for yourself: godbolt.org/z/sPKd97fdv (you'd only get a problem with the optimizer if you'd remove the volatile qualifier from the CYCCNT access, too). \$\endgroup\$
    – radioflash
    Feb 17 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.