cancel
Showing results for 
Search instead for 
Did you mean: 

Efficient way to fade a LED

Fede Rico
Associate III
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.
24 REPLIES 24
Posted on October 12, 2017 at 15:05

Another alternative is to use DMA write burst method.

Doesn't require a second timer.

Also fire and forgot.

See section 5 of 

http://www.st.com/content/ccc/resource/technical/document/application_note/group0/91/01/84/3f/7c/67/41/3f/DM00236305/files/DM00236305.pdf/jcr:content/translations/en.DM00236305.pdf

Posted on October 12, 2017 at 17:56

Hi Ben,

I followed your suggestion and I built this table with the CCR values for DC from 0 to 100.

This is the formula that i used:  CCR = ((Timer Period + 1) * dutyCyclePercent) / 100) + 1 . Timer Period = 63999.

static const uint16_t LED_FADE_PwmCompareList[101] = { 1, 641, 1281, 1921, 2561, 3201, 3841, 4481, 5121, 5761,

6401, 7041, 7681, 8321, 8961, 9601, 10241, 10881, 11521, 12161,

12801, 13441, 14081, 14721, 15361, 16001, 16641, 17281, 17921, 18561,

19201, 19841, 20481, 21121, 21761, 22401, 23041, 23681, 24321, 24961,

25601, 26241, 26881, 27521, 28161, 28801, 29441, 30081, 30721, 31361,

32001, 32641, 33281, 33921, 34561, 35201, 35841, 36481, 37121, 37761,

38401, 39041, 39681, 40321, 40961, 41601, 42241, 42881, 43521, 44161,

44801, 45441, 46081, 46721, 47361, 48001, 48641, 49281, 49921, 50561,

51201, 51841, 52481, 53121, 53761, 54401, 55041, 55681, 56321, 56961,

57601, 58241, 58881, 59521, 60161, 60801, 61441, 62081, 62721, 63361,

64001 };

I use a Software time interrupt to change the CCR value according to the above table. Starting from 0 to 101.

The table works well, but when i reach the last value of the table the LED switch to OFF.

Is possible that the value 64001 ( grater than the timer period) reset the timer?

Thanks!

John Craven
Senior
S.Ma
Principal
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.

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!

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

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.

Posted on October 13, 2017 at 10:36

Addon:

Application specific MCUs for lighting applications often have PWM units with a hardware dither, to avoid/reduce such superpositions.

Other applications (switched PS) use dither to 'smear' EMI over a broader band, and avoid violating legal limits.

Posted on October 13, 2017 at 13:48

If the Compare register value is higher than the Reload register's, then the compare event will never occur (the relation is always CCR > ARR), and depending on PWM1 or PWM2 mode the output will always be 0 or 1.

Posted on October 13, 2017 at 14:22

In my case, with this table:

static const uint16_t LED_FADE_PwmCompareList[101] = { 1, 641, 1281, 1921, 2561, 3201, 3841, 4481, 5121, 5761, 

6401, 7041, 7681, 8321, 8961, 9601, 10241, 10881, 11521, 12161,

12801, 13441, 14081, 14721, 15361, 16001, 16641, 17281, 17921, 18561,

19201, 19841, 20481, 21121, 21761, 22401, 23041, 23681, 24321, 24961,

25601, 26241, 26881, 27521, 28161, 28801, 29441, 30081, 30721, 31361,

32001, 32641, 33281, 33921, 34561, 35201, 35841, 36481, 37121, 37761,

38401, 39041, 39681, 40321, 40961, 41601, 42241, 42881, 43521, 44161,

44801, 45441, 46081, 46721, 47361, 48001, 48641, 49281, 49921, 50561,

51201, 51841, 52481, 53121, 53761, 54401, 55041, 55681, 56321, 56961,

57601, 58241, 58881, 59521, 60161, 60801, 61441, 62081, 62721, 63361,

64001 };

When the code set the CCR to 64001, what's happen? ( CCR (64001) > ARR (63999).

Thanks