cancel
Showing results for 
Search instead for 
Did you mean: 

LPTimer counts only to 1 if running on LSE

Felix_livetec
Associate II

Hi,

 

I'm using on a STM32L4R9 a LPTimer1 in interrupt mode. If i use it with the PCLK1, HSI or LSI as clocksource it works as expected. It counts to the ARR value and then the interrupt occours. If i use the LSE as clocksource the LPTimer seems only to count to 1 and the interrupt happens way to often (propably also at 1).

The LSE is also used for the RTC, which works fine.

 

 

 

1 ACCEPTED SOLUTION

Accepted Solutions

This doesn't look right:

while ((!(LPTIM1->ISR, (HAL_LPTIM_FLAG_ARROK))) && (count != 0UL));

JW

View solution in original post

10 REPLIES 10
Sarra.S
ST Employee

Hello @Felix_livetec

Could you specify the freq of LSE? 

You can check inside the STM32Cube_FW_L4, there is an example demonstrating the PWM generation from LPTIM clocked from LSE following this path : STM32Cube_FW_L4_V1.18.0\Projects\32L4R9IDISCOVERY\Examples\LPTIM\LPTIM_Timeout

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Read out and check/post content of LPTIM registers.

JW

The LSE Fequency is 32768 Hz. I checked the ferquency also over the LSCO Pin and it was OK.

The RTC is also working fine with the LSE

 Here are the register values left with HSI16 were everything is fine, right side with LSE were it only counts to 1. Both should count to 0.5s. why the ARR value is different. I think it has something to do with the ISR/ICR Flags, I also read about in the errata es0393. But even if i don't use the STM32CubeMX code, I do pretty much the same thing to initialize and start the timer. And the interupt handler is also pretty much the same.

I've also tried different Timer Modes (Counter, Timeout, PWM), the behaviour is always the same.

 

2023-11-16_08h45_09.png

Devil is in the details, here, the particular way how you've set up the timer in code. As you've shown us no code, here's some theory.

LPTIM is a dual-clock module - registers are accessed from the system (including processor) through the APB bus, and that domain is clocked by the APB clock; and the real working counters are clocked by the asynchronous clock, in your case LSE.

ARR (and CMP) registers have in fact two copies, one is in the APB domain (let's denote it ARRapb) and other in the asynchronous domain (let's denote it ARRasync). Readback comes from ARRapb, while the actual counter works with ARRasync.

Values written to ARRapb are copied through a hardware mechanism, which works only if the async clock is present This demonstrates itself also in the fact that RM requires LPTIM_CR.ENABLE to be set before ARR is written.

So, what I think happens in your case is, that you perform such sequence of operations, which write into ARR while the asynchronous clock - in your case from LSE - is not present. Maybe it's not stable yet, or maybe there should be delays introduced in some point in your program.

At any case, after the write, you should check if ARRapb has been copied into ARRasync, by checking LPTIM_ISR.ARROK.

JW

Felix_livetec
Associate II

I think your right, it must be some little detail I'm missing. Though, I was still not able to find the mistake.

I am absolutly sure the LSE is running since it is the pretty first thing I do after the startup code. (it is also checked if it is stable via the RCC_BDCR_LSERDY Flag).

Also the LPTIM_CR.ENABLE to be set before ARR is written and the LPTIM_ISR.ARROK is checked and OK.

 

I've added the code to start the timer below. It should be pretty much the same as in the cube code but just all the different HAL_LPTIM_Start (PWM_Start, PWM_Start_ISR, OnePulse_Start, ...) function merged into one.

Felix

 

/**
 * @brief  Start the Timer in the given mode.
 * @PAram  self LPTIM handle
 * @PAram  mode Specifies the mode and values needed for this mode.
 * @retval HAL status
 */
HAL_RetStatus_e HAL_LPTIM_start (HAL_LPTIM_t self, HAL_LPTIM_Mode_t *mode)
{
  /* Check the parameters */
  ASSERT (self != NULL);
  ASSERT (self->lptim_instance != NULL);
  ASSERT (mode != NULL);

  self->lptimer_callback = mode->callback;
  self->user_data = mode->user_data;
  self->mode = mode->count_mode;

  switch (mode->count_mode)
  {
    case HAL_LPTIM_MODE_PWM:
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_WAVE);   /* Reset WAVE bit */
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_TIMOUT); /* Reset TIMOUT bit */
      break;
    case HAL_LPTIM_MODE_ONEPULSE:
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_WAVE);   /* Reset WAVE bit */
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_TIMOUT); /* Reset TIMOUT bit  */
      break;
    case HAL_LPTIM_MODE_SETONCE:
      self->lptim_instance->CFGR |= LPTIM_CFGR_WAVE;      /* Set WAVE bit */
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_TIMOUT); /* Reset TIMOUT bit  */
      break;
    case HAL_LPTIM_MODE_TIMEOUT:
      if (mode->int_en)
      {
        /* Enable EXTI Line interrupt on the LPTIM Wake-up Timer */
        HAL_EXTI_Line_enableInterrupt (self->exti_line);
      }
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_WAVE); /* Reset WAVE bit  */
      self->lptim_instance->CFGR |= LPTIM_CFGR_TIMOUT;  /* Set TIMOUT bit */
      break;
    case HAL_LPTIM_MODE_COUNTER:
    default:
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_WAVE);   /* Reset WAVE bit */
      self->lptim_instance->CFGR &= (~LPTIM_CFGR_TIMOUT); /* Reset TIMOUT bit  */
      if (mode->int_en)
      {
        /* Enable EXTI Line interrupt on the LPTIM Wake-up Timer */
        HAL_EXTI_Line_enableInterrupt (self->exti_line);
      }
      break;
  }

  /* Enable the Peripheral */
  self->lptim_instance->CR |= LPTIM_CR_ENABLE;

  /* Clear flag */
  self->lptim_instance->ICR = HAL_LPTIM_FLAG_ARROK;

  /* Load the period value in the autoreload register */
  self->lptim_instance->ARR = mode->period;

  /* Wait for the completion of the write operation to the LPTIM_ARR register */
  if (HAL_LPTIM_waitForFlag (self, HAL_LPTIM_FLAG_ARROK) == HAL_RETSTATUS_TIMEOUT)
  {
    return HAL_RETSTATUS_TIMEOUT;
  }
  /* Load the pulse value in the compare register */
  if (mode->count_mode != HAL_LPTIM_MODE_COUNTER)
  {
    /* Clear flag */
    self->lptim_instance->ICR = HAL_LPTIM_FLAG_CMPOK;

    /* Load the pulse value in the compare register */
    if (mode->count_mode != HAL_LPTIM_MODE_COUNTER)
      self->lptim_instance->CMP = mode->pulse;

    /* Wait for the completion of the write operation to the LPTIM_CMP register */
    if (HAL_LPTIM_waitForFlag (self, HAL_LPTIM_FLAG_CMPOK) == HAL_RETSTATUS_TIMEOUT)
    {
      return HAL_RETSTATUS_TIMEOUT;
    }
  }
  if (mode->int_en)
  {
    /* Disable the Peripheral */
    if (HAL_LPTIM_disable (self) == HAL_RETSTATUS_TIMEOUT)
    {
      return HAL_RETSTATUS_TIMEOUT;
    }

    switch (mode->count_mode)
    {
      case HAL_LPTIM_MODE_PWM:
      case HAL_LPTIM_MODE_ONEPULSE:
      case HAL_LPTIM_MODE_SETONCE:
        /* Enable Autoreload write complete interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_ARROK;

        /* Enable Compare write complete interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_CMPOK;

        /* Enable Autoreload match interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_ARRM;

        /* Enable Compare match interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_CMPM;

        /* If external trigger source is used, then enable external trigger interrupt */
        if (self->extTrigger_active)
        {
          /* Enable external trigger interrupt */
          self->lptim_instance->IER |= HAL_LPTIM_FLAG_EXTTRIG;
        }
        break;

      case HAL_LPTIM_MODE_TIMEOUT:
        /* Enable Compare match interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_CMPM;
        break;
      case HAL_LPTIM_MODE_COUNTER:
      default:
        /* Enable Autoreload write complete interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_ARROK;

        /* Enable Autoreload match interrupt */
        self->lptim_instance->IER |= HAL_LPTIM_FLAG_ARRM;
        break;
    }
  }
  /* Enable the Peripheral */
  self->lptim_instance->CR |= LPTIM_CR_ENABLE;

  /* Start timer in continuous mode */
  switch (mode->count_mode)
  {
    case HAL_LPTIM_MODE_ONEPULSE:
    case HAL_LPTIM_MODE_SETONCE:
      self->lptim_instance->CR &= (~LPTIM_CR_CNTSTRT);
      self->lptim_instance->CR |= LPTIM_CR_SNGSTRT;
      break;
    default:
    case HAL_LPTIM_MODE_PWM:
    case HAL_LPTIM_MODE_TIMEOUT:
    case HAL_LPTIM_MODE_COUNTER:
      self->lptim_instance->CR &= (~LPTIM_CR_SNGSTRT);
      self->lptim_instance->CR |= LPTIM_CR_CNTSTRT;
      break;
  }
  /* Return function status */
  return HAL_RETSTATUS_OK;
}

 

I can't judge what this does, as there are many externalities to it.

Reduce this code to bare minimum which performs the action used in your program, while excluding any calls to any external functions and excluding usage of externally defined and filled-in structs. In other words, write the absolute minimum code containing only symbols defined in the CMSIS-mandated device header and explicit writes to registers. Check if it still exhibits the problem, and post.

JW

Felix_livetec
Associate II

I was trying to change the could so you can read it and thought about were the problem could be. i thought it might have somthing to do with the disable function, since this is very special for the LPTimer. So I stepped through the code and afterwards the timer was running correct. Then I tested with some different breakpoints with the same results depending were I set the breakpoint. In the next step I added a 1ms timeout to different parts of the disable function and checked if the timer works correct or not. In the code below you can see in which range of code the timeout has to be added to get the timer working properly.

 

In my disable function I do the same things as the CubeMX HAL does and I can't find a similar timeout there, which is why I'm a bit surprised by this, and also not very comfortable that this "fix" will always work.

If you have any thoughts to this I would be happy to hear.

Felix

 

 

/**
 * @brief  Disable LPTIM HW instance.
 * @PAram  hlptim pointer to a LPTIM_HandleTypeDef structure that contains
 *                the configuration information for LPTIM module.
 * @note   The following sequence is required to solve LPTIM disable HW limitation.
 *         Please check Errata Sheet ES0335 for more details under "MCU may remain
 *         stuck in LPTIM interrupt when entering Stop mode" section.
 * @retval None
 */
void LPTIM_DisableTest ()
{
  uint32_t tmpclksource = 0;
  uint32_t tmpIER;
  uint32_t tmpCFGR;
  uint32_t tmpCMP;
  uint32_t tmpARR;
  uint32_t primask_bit;
  uint32_t tmpOR;

  /* Enter critical section */
  primask_bit = __get_PRIMASK ();
  __set_PRIMASK (1);

  /*********** Save LPTIM Config ***********/
  /* Save LPTIM source clock */
  tmpclksource = READ_BIT (RCC->CCIPR, RCC_CCIPR_LPTIM1SEL);

  /* Save LPTIM configuration registers */
  tmpIER = LPTIM1->IER;
  tmpCFGR = LPTIM1->CFGR;
  tmpCMP = LPTIM1->CMP;
  tmpARR = LPTIM1->ARR;
  tmpOR = LPTIM1->OR;

  /*********** Reset LPTIM ***********/

  SET_BIT (RCC->APB1RSTR1, RCC_APB1RSTR1_LPTIM1RST);
  CLEAR_BIT (RCC->APB1RSTR1, RCC_APB1RSTR1_LPTIM1RST);


  //!!!!!!!!!!!! 1ms Timeout here or later and the timer works fine !!!!!!!!!!


  /*********** Restore LPTIM Config ***********/
  if ((tmpCMP != 0UL) || (tmpARR != 0UL))
  {

    /* Force LPTIM source kernel clock from APB */

    __IO uint32_t *reg;
    uint32_t clearMask;
    uint32_t setMask;

    uint32_t pos;

    for (pos = 0; ((RCC_PERIPH_CLOCK_LPTIM1 & (1 << pos)) == 0); pos++)
      ;

    if ((RCC_PERIPH_CLOCK_LPTIM1 & CCIPR2_FLAG))
    {
      reg = &RCC->CCIPR2;
      clearMask = RCC_PERIPH_CLOCK_LPTIM1 & (~CCIPR2_FLAG);
      setMask = RCC_LPTIM_CLOCK_SOURCE_PCLK1 << pos;
    }
    else
    {
      reg = &RCC->CCIPR;
      clearMask = RCC_PERIPH_CLOCK_LPTIM1;
      setMask = RCC_LPTIM_CLOCK_SOURCE_PCLK1 << pos;
    }

    MODIFY_REG (*reg, clearMask, setMask);

    if (tmpCMP != 0UL)
    {
      /* Restore CMP register (LPTIM should be enabled first) */
      LPTIM1->CR |= LPTIM_CR_ENABLE;
      LPTIM1->CMP = tmpCMP;

      uint32_t count = HAL_LPTIMER_TIMEOUT * (120000000 / 20UL / 1000UL);
      do
      {
        count--;
        if (count == 0UL)
        {
        }
      }
      while ((!(LPTIM1->ISR, (HAL_LPTIM_FLAG_CMPOK))) && (count != 0UL));

      LPTIM1->ICR = HAL_LPTIM_FLAG_CMPOK;
    }

    if (tmpARR != 0UL)
    {
      /* Restore ARR register (LPTIM should be enabled first) */
      LPTIM1->CR |= LPTIM_CR_ENABLE;
      LPTIM1->ARR = tmpARR;

      /* Wait for the completion of the write operation to the LPTIM_ARR register */
      uint32_t count = HAL_LPTIMER_TIMEOUT * (120000000 / 20UL / 1000UL);
      do
      {
        count--;
        if (count == 0UL)
        {
        }
      }
      while ((!(LPTIM1->ISR, (HAL_LPTIM_FLAG_ARROK))) && (count != 0UL));

      LPTIM1->ICR = HAL_LPTIM_FLAG_ARROK;
    }

    /* Restore LPTIM source kernel clock */

    MODIFY_REG (RCC->CCIPR, RCC_CCIPR_LPTIM1SEL, tmpclksource);
  }


  //!!!!!!!!!!!! 1ms Timeout here or before and the timer works fine !!!!!!!!!!



  /* Restore configuration registers (LPTIM should be disabled first) */
  LPTIM1->CR &= ~(LPTIM_CR_ENABLE);
  LPTIM1->IER = tmpIER;
  LPTIM1->CFGR = tmpCFGR;
  LPTIM1->OR = tmpOR;

  /* Exit critical section: restore previous priority mask */
  __set_PRIMASK (primask_bit);
}

 

 

This doesn't look right:

while ((!(LPTIM1->ISR, (HAL_LPTIM_FLAG_ARROK))) && (count != 0UL));

JW