cancel
Showing results for 
Search instead for 
Did you mean: 

pwm duty cycle measurement for 0% duty cycle

saurabhkore
Associate III

I am working on STM32L476RG mcu for duty cycle measurement of PWM signal. I share my code with you. it working fine but when i switch off pwm input coded stuck there and it shows previous reading .when i again given input then it measure the duty cycle correctly. so my question is that why code stuck at 0% duty cycle or when no PWM input provide to it. please check my code and share any solution .

 

 

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
   if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) // If the interrupt is triggered by channel 1
   {
      // Read the IC value
      ICValue = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
      if (ICValue != 0)
      {
      // calculate the Duty Cycle
      Duty = (HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2) *100)/ICValue;
      Frequency = 90000000/ICValue;
      }
   }

}

Int main(void)
{
  HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
  HAL_TIM_IC_Start(&htim2, TIM_CHANNEL_2);

  while(1)
  {

  }
}

 

 

24 REPLIES 24
Techn
Senior III

What I suggest is to add a flag as volatile unit8_t dutyCycleData = 0 ; make it 1 when you get a new calculation time, copy the duty cycle value to a volatile variable say back_dutycycle.  Read the backed duty cycle in the while(1) loop, clear the flag. only side effect is that you may get just the previous data, which many not be that bad since the duty cycle is measured in every cycle.

 

If you feel a post has answered your question, please click "Accept as Solution".

And it's still getting "stuck" with that addition?

So what debugging have you done to see what's happening?

Is your "no edges" timer ever triggering?

Please re-post the code legibly.

sir i am trying to implement you're suggestion but i am unable to do it. can you edit your suggestion in code or can you share any data, document regarding to that.

I can't help you more than give the suggestion above.

JW

hii sir ,as per youre suggestion i implement my code.

#include "stm32l4xx_hal.h"

// Global variables
volatile uint32_t t1 = 0, t2 = 0;         // Captured timer values
volatile float duty_c = 0;                // Calculated duty cycle
volatile uint32_t timeout_flag = 0;       // Timeout flag
uint32_t timeout_threshold_ms = 500;      // Timeout threshold in milliseconds

TIM_HandleTypeDef htim2;                  // Timer 2 handle

// Function prototypes
void SystemClock_Config(void);
void Error_Handler(void);
void TIM2_Init(void);
void handle_signal_loss(void);

int main(void) {
    HAL_Init();
    SystemClock_Config();

    // Initialize Timer 2 for input capture
    TIM2_Init();

    // Start input capture for both channels
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // Rising edge
    HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); // Falling edge

    while (1) {
        if (timeout_flag) {
            timeout_flag = 0; // Reset the flag
            handle_signal_loss();
        }

        // Main application logic
    }
}

// Timer 2 initialization
void TIM2_Init(void) {
    __HAL_RCC_TIM2_CLK_ENABLE();

    TIM_IC_InitTypeDef sConfigIC;

    htim2.Instance = TIM2;
    htim2.Init.Prescaler = 79;           // Timer clock = 1 MHz (assuming 80 MHz system clock)
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim2.Init.Period = 0xFFFF;          // Maximum count value
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    HAL_TIM_IC_Init(&htim2);

    // Configure Channel 1 for rising edge detection
    sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING;
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    sConfigIC.ICFilter = 0;
    HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);

    // Configure Channel 2 for falling edge detection
    sConfigIC.ICPolarity = TIM_ICPOLARITY_FALLING;
    HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2);
}

// Input capture callback
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
    static uint32_t last_edge_time = 0;

    if (htim->Instance == TIM2) {
        if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
            // Rising edge captured: Period start
            t1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);

            if (t1 != 0) {
                t2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); // Falling edge captured
                duty_c = ((float)t2 / t1) * 100.0;                  // Calculate duty cycle

  

                // Reset the timeout mechanism
                __HAL_TIM_SET_COUNTER(&htim2, 0);
                timeout_flag = 0;
                last_edge_time = HAL_GetTick(); // Update last active time
            }
        }
    }
}

// Timer overflow callback (timeout detection)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if (htim->Instance == TIM2) {
        // Check for timeout condition
        if ((HAL_GetTick() - last_edge_time) > timeout_threshold_ms) {
            timeout_flag = 1; // Set the timeout flag
        }
    }
}

// Handle signal loss
void handle_signal_loss(void) {
    printf("Signal lost! Timeout occurred.\n");

    // Handle signal loss condition here (e.g., reset variables, trigger an error state)
    duty_c = 0;

}

// System clock configuration (implement as needed for your MCU)
void SystemClock_Config(void) {
    // Configure system clock here (use CubeMX for guidance)
}

// Error handler
void Error_Handler(void) {
    while (1) {
        // Stay here for debugging
    }
}

sir but still it not work.as per my knowledge for the measurement of duty cycle we totally relies on capture callback function ,and this function only trigger when edge is detected in input. For 0 and 100% duty cycle there are no edges therefore this function never activated and measure the duty cycle. I guess when we gives 0% or 100% duty cycle mcu go in hang mode therefore it is not able to measure 0% or 100% duty cycle.   suggest any other solution sir.

Thankyou.


@saurabhkore wrote:

this function only trigger when edge is detected in input. For 0 and 100% duty cycle there are no edges therefore this function never activated and measure the duty cycle. .


Exactly - that's why you need a separate timer that times-out when there are no edges.

sir can you tell me where we implement this timer inside capture callback function or outside somewhere. because if we implement this timer inside the capture call back then defiantly it will not allow to another timer to execute it work.

because condition is very straight forward ,if there is edge means capture callback is active and we get output else all things are hanged.

Think of it like a "Watchdog" timer: each time you get a capture callback, you restart the "watchdog".

Therefore it will only time-out after there have been no capture callbacks for the timeout time.

 

The idea is that you set the pwm variable to be set to zero when no edge is detected. You can either reset it to zero in your main or in your timer2. How are you going to use this pwm value? Is it going to be used for display or some other purpose. If you do the resetting using timer2,  I  feel you have to re-trigger every time an edge is detected, so that it will be in sync. So my suggestion is to reset the pwm value to zero, after you copy it to another variable. You can set a flag, newData, when you get edge and reset in main.

If you feel a post has answered your question, please click "Accept as Solution".

I don't use Cube/HAL, but in the above code - which to me looks to be AI-generated - I don't see how the Update interrupt to be enabled.

Also, there would need to be some mechanism to reset the timeout-timer (which in this case is apparently the same TIM2), maybe using Reset mode of the slave-mode controller, as in the "PWM input" described in RM.

JW