Skip to main content
Fede Rico
Associate III
October 12, 2017
Question

Efficient way to fade a LED

  • October 12, 2017
  • 24 replies
  • 5228 views
Posted on October 12, 2017 at 09:36

Hi there,

I'm trying to Fade (In and Out) a LED with TIM5.

This is my configuration (Just a recap of the configuration):

1) Configure TIM5 in order to generate a signal with 100 Hz as period with no pulse.

htim5.Instance = TIM5;

htim5.Init.Prescaler = 9;

htim5.Init.CounterMode = TIM_COUNTERMODE_UP;

htim5.Init.Period = 63999;

htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

sConfigOC.OCMode = TIM_OCMODE_PWM1;

sConfigOC.Pulse = 0;

sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;

sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;

2) I write this function to vary the duty cycle:

StatusCode_e PWM_Configure( PWM_TimerId_e timerId, PWM_ChannelId_e channelId, uint8_t dutyCyclePercent )

{

TIM_OC_InitTypeDef outputConfiguration;

outputConfiguration.OCMode = TIM_OCMODE_PWM1;

outputConfiguration.Pulse = (((PWM_TimerHandler[PWM_TIMER_ID_1].timPeriod + 1) * dutyCyclePercent) / 100) + 1;

outputConfiguration.OCPolarity = TIM_OCPOLARITY_HIGH;

outputConfiguration.OCFastMode = TIM_OCFAST_DISABLE;

HAL_TIM_PWM_ConfigChannel( PWM_TimerHandler[timerId].handler, &outputConfiguration, PWM_ChannelHandler[channelId] );

return StatusCode_OK;

}

3) I realise the fade effects with this function:

StatusCode_e LED_FADE_FadeIn( LED_FADE_Id_e ledFadeId, uint16_t fadeMs )

{

uint8_t dutyCycleCnt;

for( dutyCycleCnt = 1; dutyCycleCnt <= 100; dutyCycleCnt++ )

{

PWM_Off( LED_FADE_Handler[ledFadeId].timerId, LED_FADE_Handler[ledFadeId].channelId );

PWM_Configure( LED_FADE_Handler[ledFadeId].timerId, LED_FADE_Handler[ledFadeId].channelId, dutyCycleCnt );

PWM_On( LED_FADE_Handler[ledFadeId].timerId, LED_FADE_Handler[ledFadeId].channelId );

OS_Delay(LED_FADE_DUTY_CYCLE_STEP_MS);

}

return StatusCode_OK;

}

This method works well but I think that is not efficient. In fact every time I want to increase the Duty Cycle, the timer must be stopped, reconfigure the output channel and start the timer.

These action are repeated for 100 times to fade the led.

Is there a best way to change the CCR without stop the timer?

Thanks for the help.

Federico

#fade #timer #pwm #led

Note: this post was migrated and contained many threaded conversations, some content may be missing.
This topic has been closed for replies.

24 replies

waclawek.jan
Super User
October 12, 2017
Posted on October 12, 2017 at 09:58

Is there a best way to change the CCR without stop the timer?

Yes. Simply write to it.

TIM5->CCR1 = current_value;

They may be some LL incantation for this if you insist on using a 'library'.

You may want to have TIMx_CCMRy.OCzPE set.

JW

waclawek.jan
Super User
October 12, 2017
Posted on October 12, 2017 at 10:04

The next step would be to leave DMA (driven by another timer) to pick values from a table and write to the target PWM CCR. That then runs with no involvement of the processor after initialization.

<shameless self-advertisement> An example for the DISCO-L4

http://www.efton.sk/STM32/breathl4.zip

</shameless self-advertisement>

JW

Fede Rico
Fede RicoAuthor
Associate III
October 12, 2017
Posted on October 12, 2017 at 10:22

Thanks for the example. It's very useful. I try this implementation as next step.

Fede Rico
Fede RicoAuthor
Associate III
October 12, 2017
Posted on October 12, 2017 at 10:06

Hello JW,

thanks for the reply.

Regarding

TIM5->CCR1 = current_value;

TIM5 is a definition or I should use the Timer Handler (TIM_HandleTypeDef)?

I'm using the hal library in my application.

Could you provide an example please?

BR

waclawek.jan
Super User
October 12, 2017
Posted on October 12, 2017 at 10:22

TIM5 is a definition

Yes, from the CMSIS-mandated device header. You did not tell us your STM32 model, but e.g. for STM32F407 it's at [STM32Cube_FW_F4_V1.16.0]\Drivers\CMSIS\Device\ST\STM32F4xx\Include\stm32f407xx.h to be #include-d indirectly through stm32f4xx.h in the same directory (and you probably have the path added in the compiling environment (IDE or whatever)).

Iit's basically a pointer to a structure which matches the layout of hardware registers of the timer. Your 'handler' will contain it as one of its fields, so you can indirectly use it from there, too. Look at the HAL sources, how they access the registers.

JW

Fede Rico
Fede RicoAuthor
Associate III
October 12, 2017
Posted on October 12, 2017 at 10:25

Ok! I undestand! TIM5 map directly the timer register.

So in this way I don't need to stop the timer. Am I right?

Thanks!

waclawek.jan
Super User
October 12, 2017
Posted on October 12, 2017 at 10:33

Yes.

Maybe you didn't have to stop it with using the HAL either, but I don't know for sure, I don't Cube.

JW

Fede Rico
Fede RicoAuthor
Associate III
October 12, 2017
Posted on October 12, 2017 at 10:36

Using HAL you must stop the timer. I tried.

I don't Cube.    

  

Ben K
Senior III
October 12, 2017
Posted on October 12, 2017 at 11:44

With HAL you can use this macro to alias the register access without having to stop the timer or anything (using HAL PWM channel config preload is always enabled):

__HAL_TIM_SET_COMPARE(

PWM_TimerHandler[timerId].handler, /* TIM handle pointer */

PWM_ChannelHandler[channelId], /* TIM channel */

newCompVal /* Compare register value to set */ );

HAL is nothing more than an abstraction library, therefore anything can be done with it, the only limitation is whether there is an abstraction method/macro or not. And if there isn't, you can just use handle->Instance to perform register access.

John Craven
Senior
October 12, 2017
Fede Rico
Fede RicoAuthor
Associate III
October 13, 2017
Posted on October 13, 2017 at 09:25

Good Morning John!

Thanks a lot for the provided example! I will try to use the DMA!

John Craven
Senior
October 13, 2017
Posted on October 13, 2017 at 14:43

I didn't mention it, because the cube and source is there, but the demo is running on F7 at 216mhz. The timer sees 108mhz, and uses pre scaler of 108 for 1us ticks. The ARR is 10000 for 10ms (100Hz). 100 fade steps from 0 to 10000 (100%). Fade takes 1sec.

S.Ma
Principal
October 13, 2017
Posted on October 13, 2017 at 04:24

As dimming is to the human eye, use HW assist only for sub milisecond HW updates.

A TIM usually has up to 4 channels = 4 LED driving. Why 100Hz? Make the LED toogle at say 10kHz and have timer running 128 times this speed (your dim table has ~100 discrete values) hence ~1.28 MHz.

If the compare value is higher than the possible swept timer values, the output won't trigger. Choose the right OC polarity to decide if in this condition the output is supposed to be high or low.

Normally no need to stop or reset the timer to change dimming. As dimming is relatively slow, no DMA HW assist needed here.

Fede Rico
Fede RicoAuthor
Associate III
October 13, 2017
Posted on October 13, 2017 at 09:30

Hi KIC8462852 EPIC204278916,

Why 100 Hz is not enough? Why I should rum the timer at 10 kHz (Just for my understanding)?

Regarding the problem with my table, I solved the problem. It's related to a wrong index during the fade . My bad.

BR

AvaTar
Senior III
October 13, 2017
Posted on October 13, 2017 at 10:29

Why 100 Hz is not enough? Why I should rum the timer at 10 kHz (Just for my understanding)?

Some people still seing a flicker, especially when looking out of the eye's corner (i.e. not focussing on the lighting).

And, be aware of superposition effects.

Two/more PWM controlled LED lights might mix into a visible flicker, making it annoying to look at.