Skip to main content
xpp07
Associate III
October 23, 2018
Question

How to fade PWM without Delay?

  • October 23, 2018
  • 8 replies
  • 1616 views

I have L432KC nucleo. I’ve set Timer 1 for PWM generation. I’m controlling the duty cycle via the CCR register. When increasing o decreasing the duty cycle, I want it to be slow. It works with a for loop and delay. But HAL_Delay prevents from doing another thing in the loop. Is there another way of doing this?

    This topic has been closed for replies.

    8 replies

    T J
    Senior III
    October 23, 2018

    can you show your code ?

    you could set a flag to decrease in steps...

    you could set the PWM timer over flow interrupt ON

    and if the flag is set decrement/increment the PWM dutyCycle if your conditions are right...

    Alan Chambers
    Associate III
    October 23, 2018

    Perhaps use a second timer to manage the duty cycle. So TIM1 does PWM at 10kHz (say), and TIM2 changes the duty at 100Hz (say). Fewer interrupts that way, but at the cost of a timer. I generally use a software timer implementation based around SysTick for this sort of thing.

    xpp07
    xpp07Author
    Associate III
    October 23, 2018

    This is what I have: two functions, one to go up and another to fade out.

    The problem is I don't want to use HAL_Delay, as I can't do anything else until the for loop is done.

    void ramp_down(){
     
    	int real_dutyCycle = 8*command_dutyC;
    	for (int fadeValue = actual_dutyC;fadeValue == real_dutyCycle;fadeValue -= 1) {
    			 		htim1.Instance->CCR1=fadeValue;
    			 	 // wait for 30 milliseconds to see the dimming effect
    			 	 HAL_Delay(30);
    	}
    	actual_dutyC = real_dutyCycle;
    }
     
    void ramp_up(){
     
    	int real_dutyCycle = 8*command_dutyC;
    	for (int fadeValue = actual_dutyC;fadeValue == real_dutyCycle;fadeValue += 1) {
    			 		htim1.Instance->CCR1=fadeValue;
    			 	 // wait for 30 milliseconds to see the dimming effect
    			 	 HAL_Delay(30);
    	}
    actual_dutyC = real_dutyCycle;
    }

    T J
    Senior III
    October 23, 2018

    Yes, you need a state machine, easy enough..

    are you running RTOS ?

    I use a single thread and check all my state machines every mS

    In your case you would initialise the CCR1 in Start_Ramp_Down();

    htim1.Instance->CCR1=fadeValue;

    and then set a flag like this...

    set some global vars; 
     
    char mSint,secondInt,seconds,minuteInt;
    char resetActualDutyCycle_mS,rampingDown;
    int real_dutyCycle,actual_dutyC,fadeValue;
    int16_t mS;
     
    in Systick(){ // add these lines
    	msint = true;
    	mS++;
    	if(mS>=1000){
    	mS=0;
    	secondInt=true;
    	seconds++;
    	if(seconds>=60){
    	seconds=0;
    	minuteInt = true;
    	}
    }
     
    void start_ramp_down(){ 
     real_dutyCycle = 8*command_dutyC;
     rampingDown = true; 
     fadeValue = actual_dutyC;
     htim1.Instance->CCR1=fadeValue;
     // wait for 30 milliseconds to see the dimming effect
     //HAL_Delay(30);
     resetActualDutyCycle_mS = 30; //mS
     CurrentDutyCycle_mS = resetActualDutyCycle_mS;
    }
     
    under while(1){
     
    checkStateMachines();
    }
     
    checkStateMachines(){ //every mS
     if(msInt){		// this remains running forever
     mSint = false;
     if(rampingDown ) // state machine flag
     if(!CurrentDutyCycle_mS --){ //this checks if the 30mS is over.
     CurrentDutyCycle_mS = resetActualDutyCycle_mS; // this resets the 30mS counter
     
     if(fadeValue > real_dutyCycle){
     fadeValue -=1;
     htim1.Instance->CCR1=fadeValue;
     }else //reached end
     {
     rampingDown = false; // this will stop the statemachine
     actual_dutyC = real_dutyCycle;
     }
     
    // check other states/flags here every mS
     }
    }

    well that's the general idea

    Jeroen3
    Senior
    October 23, 2018

    A different approach to loops is to use a moving average filter on the PWM value.

    Changing the PWM setpoint has to pass through the moving average filter, that you can make as slow as you'd like by changing the calculation interval or by cascading multiple.

    https://dsp.stackexchange.com/questions/20333/how-to-implement-a-moving-average-in-c-without-a-buffer

    xpp07
    xpp07Author
    Associate III
    October 23, 2018

    Thank you. I'm not running RTOS actually. I will review your ideas. It would take me a while to get it working as I'm not a software-oriented guy.

    Tesla DeLorean
    Guru
    October 23, 2018

    Put the code in the TIM update interrupt, rather than waiting.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    xpp07
    xpp07Author
    Associate III
    October 23, 2018

    Do you mean setting another timer interrupt and put the code in it?