13 Replies Latest reply on May 25, 2017 2:32 PM by jla

    Cube32MX - input capture overflow detection.

    jla

      Hi.

       

      I'm trying to use TIM1's channel 1 as a spindle RPM input capture. ( device is STM32F103C8 )
      Knowing that the spindle has a single pulse per revolution and is rated from 0 to 30K rpm, the frequency domain is 0 to 500Hz. Easy feat for a timer running at 48MHz.

       

      So I configured TIM1's prescaler to 480, its period to 0xFFFF, direct input, no prescaling nor filtering (sharp input signal)...
      So far so good, frequencies from 5Hz to 1KHz  are registering nicely...

       

      But here's my issue : I was planning to count the number of UPDATE interrupts to detemine when the signal's period dropped too much ( i.e. the spindle was idling ).

       

      BUT : it seems that I'm missing some UP events. The update interrupt isn't always firing.


      Here's my code. The first method is CubeMX's output.

       


      /* TIM1 init function */
      static void MX_TIM1_Init(void)
      {

       

        TIM_ClockConfigTypeDef sClockSourceConfig;
        TIM_MasterConfigTypeDef sMasterConfig;
        TIM_IC_InitTypeDef sConfigIC;

       

        htim1.Instance = TIM1;
        htim1.Init.Prescaler = 512;
        htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
        htim1.Init.Period = 0xFFFF;
        htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        htim1.Init.RepetitionCounter = 0;    
        if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
        {
          _Error_Handler(__FILE__, __LINE__);
        }

       

        sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
        if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
        {
          _Error_Handler(__FILE__, __LINE__);
        }

       

        if (HAL_TIM_IC_Init(&htim1) != HAL_OK)
        {
          _Error_Handler(__FILE__, __LINE__);
        }

       

        sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
        sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
        if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
        {
          _Error_Handler(__FILE__, __LINE__);
        }

       

        sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
        sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
        sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
        sConfigIC.ICFilter = 0;
        if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
        {
          _Error_Handler(__FILE__, __LINE__);
        }

       

          
      }

       

      // Some global variables

      volatile bool _toolRawRevValid;
      volatile uint32_t _lastTim1CC,_toolRawRev, _tim1Ovf ;

       


      void InitializeInputStage(void)
      {
          // prepare variables 

        _toolRawRev = 0;
          _toolRawRevValid = false;
          _tim1Ovf = 0;
          _lastTim1CC = 0;
          
          // Start TIM1 and its interrupts (as configured in MX_TIM1_Init() ).
          HAL_TIM_Base_Start_IT(&htim1); // start counting, enable UPDATE interrupt
          HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1); // enable IC interrupt (already counting now).
      }

       

      // Auto-reload (update) event interrupt handler

      void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
      {
          if(htim == &htim1)
          {
              _tim1Ovf++; // count roll-overs.
              if(_tim1Ovf >= 2)
                  _toolRawRevValid = false;
          }
      }

       

      // Timers Capture handler

      void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
      {
          register uint32_t currCap, ovf, tc;
          
          if(htim == &htim1){
              currCap = HAL_TIM_ReadCapturedValue(&htim1, TIM_CHANNEL_1);
              tc = TIM1->CNT;
              ovf    = _tim1Ovf; // number of TIM1-CNT rollovers
              _tim1Ovf = 0;
              
              if(currCap <= _lastTim1CC) {
                  if(!ovf){ // no roll-over occured ?
                      while(1)
                          __NOP; // the previously captured value is higher than the current one, but no timer overflow registered ?
                  }
                  _toolRawRev = ((TIM1->ARR * ovf) + currCap )-_lastTim1CC;
              }else{
              _toolRawRev = (TIM1->ARR * ovf) + (currCap - _lastTim1CC);
              }
              _toolRawRevValid = true;
              
              _lastTim1CC = currCap;
              }
          
      }

       

       

       

       

      So here's the strange fact : my program keeps hitting the __NOP statement in the HAL_TIM_IC_CaptureCallback(...) method.
      Is Timer1 gated by its input capture somehow ?

       

       

      PS: sorry for the repetitive edits, I'm discovering the community's new interface. =/

        • Re: Cube32MX - input capture overflow detection.
          jla

          What troubles me most is this : if the second capture sample is lower that the fist, there SHOULD be an overflow registered between the two edges we detected, right ?

            • Re: Cube32MX - input capture overflow detection.
              waclawek.jan

              Does the update interrupt fire at all?

               

              TIM1 in many STM32 has separate interrupts for the update and for CC. If your STM32 model has it so, does it have both enabled in NVIC and directed properly towards the Cube mess? (I don't Cube so don't know how to do that).

               

              JW

                • Re: Cube32MX - input capture overflow detection.
                  jla

                  Well, that's both an easy and a tricky question.

                   

                  Yes, the update interrupts occurs ( I do reach it if I set a breakpoint on the line

                   

                  "_tim1Ovf++; // count roll-overs."

                   

                  when the infinite NOP is commented out.

                   

                  Plus, it tried to diss the HAL methods to start timer1 directly : nothing changed.
                  As a matter of fact, I ensured to set the UP¨interrupt to a lower rank than the CC to be sure it would be fired first.

                   

                  I added the infinite NOP

                   

                   if(!ovf){ // no roll-over occured ?
                       while(1)
                       __NOP;
                   }

                   

                  sequence for debug purposes, since I wasn't conviced the MCU misfired its UP interrupt. But it looks like it does.

                   

                  But as soon as the signal frequency drops under 5Hz ( and TIM1->CNT reach a big value ), it all goes haywire and the UP isr doesn't fire anymore ( CC doesn't fire as well ).

              • Re: Cube32MX - input capture overflow detection.
                waclawek.jan

                Is _tim1Ovf declared with volatile qualifier?

                 

                I ensured to set the UP¨interrupt to a lower rank than the CC to be sure it would be fired first.

                You mean, the update interrupt having higher priority, thus being able to nest/interrupt/preempt the CC interrupt service routine? With the above code, that should avoid reading unupdated (unincremented) _tim1Ovf  after having read an overflown TIM1->CNT (provided that _tim1Ovf is volatile, TIM1->CNT is defined as volatile in the CMSIS-mandated device header). However, you might read TIM1->CNT not yet overflown, the overflow happening just after having read TIM1->CNT, and _tim1Ovf is then already incremented.

                 

                Draw diagrams with all possible sequence of events. This problem does not have a trivial solution.

                 

                JW

                  • Re: Cube32MX - input capture overflow detection.
                    jla

                    Yes, the UPDATE interrupt preempts the CC interrupt in my NVIC initialization , ( UPDT rank is 2 and CC is 3 ).

                     

                    /** NVIC Configuration */
                    static void MX_NVIC_Init(void)
                    {
                      /* USB_HP_CAN1_TX_IRQn interrupt configuration */
                      HAL_NVIC_SetPriority(USB_HP_CAN1_TX_IRQn, 1, 0);
                      HAL_NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
                      /* USB_LP_CAN1_RX0_IRQn interrupt configuration */
                      HAL_NVIC_SetPriority(USB_LP_CAN1_RX0_IRQn, 5, 0);
                      HAL_NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
                      /* TIM interrupts ranking */  
                      HAL_NVIC_SetPriority(TIM1_UP_IRQn, 2,0);
                      HAL_NVIC_SetPriority(TIM1_CC_IRQn, 3,0);
                    }

                     

                     

                    I simplified my code a bit to see if I could shift the problem and make it more obvious, but I still stumble upon strange results, relating to the previous capture being "bigger" than the new one.

                     

                    /* let's make a 32-bits counter out of TIM1->CNT by holding its overflows into the MSW of a DWORD (all unsigned ) */

                    #pragma anon_unions
                    typedef union{
                        __packed struct{
                            uint16_t word0;
                            uint16_t word1;
                        };        
                        uint32_t dword;    
                    }U32Composed_t;

                     

                    volatile U32Composed_t _currTim1CC, _lastTim1CC;
                    volatile uint16_t _tim1Ovf;
                    volatile uint32_t _toolRawRev;

                     

                    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
                    {    
                        if(htim == &htim1){
                            // we might get an spurious overflow here, but the bugs said otherwise.
                            _currTim1CC.word1 = _tim1Ovf;
                            
                    // get the captured count and concatenate as lower word.                 

                            _currTim1CC.word0 = TIM1->CCR1;
                            
                            if(__HAL_TIM_GET_FLAG(&htim1,TIM_FLAG_UPDATE))
                                _currTim1CC.word1++;

                            _toolRawRev = _currTim1CC.dword - _lastTim1CC.dword;
                            _toolRawRevValid = true;

                            _lastTim1CC.dword = _currTim1CC.dword;
                        }    
                    }

                    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
                    {
                        if(htim == &htim1)
                          _tim1Ovf++; // count roll-overs.           
                    }

                     

                    Despite this,  _currTim1CC.dword  manages to be LOWER than _lastTim1CC.dword on frequencies lower than 5Hz !

                    In other words, the only volatile value which MSW could be incremented in a nested UPDATE interrupt  still manages to be lower than its previously saved value.
                    I'm at a loss here.

                    It's as if TIM1 was gated by its own input capture channel somehow : as soon as I cut the input signal, I get no more _tim1Ovf increments ! No more UPDATE interrupts !

                      • Re: Cube32MX - input capture overflow detection.
                        waclawek.jan

                        Does the update interrupt fire at all?

                         

                        TIM1 in many STM32 has separate interrupts for the update and for CC. If your STM32 model has it so, does it have both enabled in NVIC and directed properly towards the Cube mess? (I don't Cube so don't know how to do that).

                         

                        [

                        Yes, I know I re-asked the same question. No, you don't prove update interrupts being fired by update, just by placing a breakpoint in the callback - the callback will get called back from a different interrupt.

                         

                        If I am right in this, this is a deserved punishment for using a "library".

                        ]

                         

                        JW

                         

                        1 of 1 people found this helpful
                          • Re: Cube32MX - input capture overflow detection.
                            jla

                            Right now, I'm with you on this.
                            Although each interrupt vector (UP and CC) only call one MX method, there's too much mess in those (and conditionals statements)  to call it clear code. There's too much symbols hiding simple registers affectations while others refer to full-fledged subroutines.

                            I've joined the Cube bandwagon since it was an "up-to-date" version of ST's framework, but their abstraction layer is either too abstracted or not enough. And very poorly documented at that.

                            I'll switch back to direct register manipulation for this. Gain of both clarity and code size (although small).

                      • Re: Cube32MX - input capture overflow detection.
                        Clive One

                        Prescaler is an N-1 value, assuming you don't want a division by 481 or 513?

                        • Re: Cube32MX - input capture overflow detection.
                          jla

                          Gosh, I'm so upset and ashamed of myself.

                          Turns out the culprit was the function generator I used on my test bench.
                          This horrible piece of chinesium won't output anything at 4hz, and creates high-piched harmonics at very low frequencies, around 1Hz and 2Hz.
                          But its signal is clean and spotless above 5Hz !

                           

                          The code was functional all along -_-.

                          Anayway, thanks you guys for your interest and support !

                          Guess I'll have to buy a new signal generator =/