cancel
Showing results for 
Search instead for 
Did you mean: 

Example: PWM on STM32-Discovery Blue LED

Posted on February 12, 2011 at 21:39

Hey,

I just spent a bunch of time sorting out how to get the darn PWM output on the blue LED on the STM32-Discovery board.  After a lot of time pouring over the datasheets and forum posts I was finally able to work out the following code.  Seems to work very well.

    RCC_APB2PeriphClockCmd(    RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |

            RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE |

            RCC_APB2Periph_AFIO, ENABLE );

 

    RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE );

 

    // Set the Vector Table base address at 0x08000000.

    NVIC_SetVectorTable( NVIC_VectTab_FLASH, 0x0 );

 

    NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );

 

    // Configure HCLK clock as SysTick clock source.

    SysTick_CLKSourceConfig( SysTick_CLKSource_HCLK );

    // Setup Blue LED on STM32-Discovery Board to use PWM.

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;            // Alt Function - Push Pull

    GPIO_Init( GPIOC, &GPIO_InitStructure );

    GPIO_PinRemapConfig( GPIO_FullRemap_TIM3, ENABLE );        // Map TIM3_CH3 to GPIOC.Pin8

 

    // Let PWM frequency equal 100Hz.

    // Let period equal 1000. Therefore, timer runs from zero to 1000. Gives 0.1Hz resolution.

    // Solving for prescaler gives 240.

    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;

    TIM_TimeBaseStructInit( &TIM_TimeBaseInitStruct );

    TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV4;

    TIM_TimeBaseInitStruct.TIM_Period = 1000;

    TIM_TimeBaseInitStruct.TIM_Prescaler = 240;

    TIM_TimeBaseInit( TIM3, &TIM_TimeBaseInitStruct );

 

    TIM_OCInitTypeDef TIM_OCInitStruct;

    TIM_OCStructInit( &TIM_OCInitStruct );

    TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;

    // Initial duty cycle equals 0%. Value can range from zero to 1000.

    TIM_OCInitStruct.TIM_Pulse = 0;

    TIM_OC3Init( TIM3, &TIM_OCInitStruct );

    TIM_Cmd( TIM3, ENABLE );

Also, below is a useful macro that sets the PWM duty cycle once the output is running.

// Set duty cycle when using TIM3 as a PWM output.

&sharpdefine SetTIM3Duty( val )    TIM3->CCR3 = val

It's neat to see the blue LED ramping and fading smoothly.

#stm32 #pwm #pwm #discovery #stm32f100rb #output
49 REPLIES 49
jurriaan
Associate II
Posted on October 22, 2012 at 12:41

Actually I have and used my scope and the code works correctly. Only the speed of the PWM is so low that I can clearly see the the on/off action from the LED. How do I set the core speed exactly?

Posted on October 22, 2012 at 13:56

PWMFreq = SystemCoreClock / ((TIM_Prescaler + 1) * (TIM_Period + 1));

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
crt2
Associate II
Posted on October 22, 2012 at 14:02

Check FWLib example for SysTick, RCC and you will see examples for HSE - you should probably use HSI or LSI, if you do not have external oscillators on board.

    // use HSI2 with PLLCLK = 4MHz (HSI=8) * 16 = 64 MHz.

    RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_16);

    /* Enable PLL. */

    RCC_PLLCmd(ENABLE);

    /* Wait till PLL is ready. */

    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)

        ;

    /* Select PLL as system clock source. */

    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

    /* Wait till PLL is used as system clock source. */

    while (RCC_GetSYSCLKSource() != SOURCE_PLL)

        ;

cooltalk2vinay
Associate
Posted on November 08, 2012 at 10:16

Hello, Mr. Clive!

I am new to STM32L152 programming and I am learning to program it. Initially I want to generate a PWM signal with varying duty caycle on only one channel.

I am actually getting  a square wave but how do i write the routine for varying the duty cycle?? Do i have to vary the CCRx value in the ISR or in the infinite while loop of the main program??

Any suggestions would be of great help!

Thankyou!

Posted on November 08, 2012 at 13:15

Ideally you want to change it in the Update interrupt, as it will latch in a predictable way. You program the width (duty) in the CCRx register in ticks of the timebase counter.

I posted some servo examples which might be illustrative.

[DEAD LINK /public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Servo control using STM32VLDiscovery&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000491D59B8574F8049B5DFA3E8B21CBA51&TopicsView=https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/AllItems.aspx&currentviews=397]https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/Servo%20control%20using%20STM32VLDiscovery&FolderCTID=0x01200200770978C69A1141439FE559EB459D758000491D59B8574F8049B5DFA3E8B21CBA51&TopicsView=https://my.st.com/public/STe2ecommunities/mcu/Lists/STM32VLDiscovery/AllItems.aspx¤tviews=397

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
naroyce
Associate
Posted on March 22, 2013 at 08:28

The default pin mapping, and remapped options, are clearly defined in the Data Sheet. The timer, channel, and remapping were chosen explicitly because they got the job done, and not picked randomly.

That was an invaluable tidbit that I needed to find. Thank you for pointing it out Clive. I was having issues with my LEDs. I don't have a Discovery board and the embedded LEDs use PE3,4,5,6. The doc shows that they has no remap. The AF is for TRACE/FSMC. It sounds like Timers provide great resource value for IO signalling by, what I assume is taking load off of the controller. It also seems that my only option with those LEDs is to hop on the SysTick handler and do my lighting changes there. My question is about threads or how the controller works with Timers. If I do a loop to change the brightness of the LED in the SysTick event handler, is the next Tick event put on hold while the function is running through its paces? This is my first foray into microcontrollers and I find the last infinite loop in a program odd because it makes me think it is just going to choke the UC. Even though it is a loop that may do ''nothing'', I'd think it is still going to perform a comparison instruction and max out the controller's power usage and occupy all of the instruction availability (obviously it doesn't though). If I were to try a threading attempt, would I try something like this (pseudo):

int
_curThread;
void
SysTick()
{
if
(_curThread != 5)
{_curThread++;}
else
{_curThread = 0;}
if
(_curThread == 1)
{
//loop to fade LED PE3}
elseif (_curThread == 2)
{
//loop to fade LED PE4}
elseif (_curThread == 3)
{
//loop to fade LED PE5}
elseif (_curThread == 4)
{
//loop to fade LED PE6}
}
void
main()
{
//initialize LEDs and system clock
while
(1); 
//do nothing
}

What I don't know is if this would be the most proper way to handle it. I don't know if one LED would fade and the others would be put on hold or if the SysTick event is fired like a thread. And if it is a thread, I think I might run into a racing problem where the _curThread isn't incremented in time for the next event firing.

Posted on March 22, 2013 at 14:33

The while(1) serves two purposes, it spins the processor in the foreground state so interrupts can happen in the background, and second it prevents the processor dropping out the bottom of main() and returning. Returning where? There will be no comparison, the compiler creates code that just jumps on the spot, and adding an __WFI(); would cause less power to be drawn.

The SysTick is an interrupt, you get called do your job and leave, and get called again 1 ms (or whatever the periodicity) later. A thread kind of assumes you sit in that code, for threads you'll need some sort of RTOS implementation.

You can use the SysTick interrupt to turn LEDs on and off, advance a software timer/count, and modulate the LEDs however you want. Just assume the same code is called 1000 times each second. What a 4 Hz LED, toggle the state every 125th entry.
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
saadeldine
Associate
Posted on May 11, 2013 at 17:02

hi

can you have a code variation voltage with

potentiometer

of stm32lxx

thank you

Posted on May 11, 2013 at 18:27

can you have a code variation voltage with

potentiometer

of stm32lxx

 

On what board, and what purpose?
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
nishantnain14
Associate
Posted on June 03, 2014 at 09:01

Hello,

I'm trying to use the above code and modified it a little to glow an LED, but I get an error-

 #268: declaration may not appear after executable statement in block

TIM_OCInitTypeDef TIM_OCInitStruct

GPIO_InitTypeDef

GPIO_InitStructure

;

Any ideas why this is happening? Also, I'm still not sure I'm remapping the channel for timer 3 correctly. Here's my C file for the timer configuration I'm trying to use in my main function -

#include ''stm32f10x_tim.h''

#define Timer_Used TIM2

#define Timer_Used_Sec TIM3

#define Timer_Used_IRQn TIM2_IRQn

#define Timer_Used_IRQn_Sec TIM3_IRQn

#define Timer_Prescaler_Value 384

#define Timer_Prescaler_Value_Sec 239

#define Timer_Period_Value 999

// Set duty cycle when using TIM3 as a PWM output. 

#define SetTIM3Duty(val)    TIM3->CCR3 = val 

void Timer_Initialization(void);

void Timer_RCC_Configuration(void);

void Timer_Configuration(void);

void Timer_Configuration_Sec(void);

void Timer_NVIC_Configuration(void);

void Timer_Initialization(void)

{

Timer_RCC_Configuration();

//Timer_Configuration();

Timer_Configuration_Sec();

Timer_NVIC_Configuration();

}

//TIM 2 Config 

/*

void Timer_Configuration(void)

{

TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;

TIM_TimeBaseInitStruct.TIM_Prescaler = Timer_Prescaler_Value;

  TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInitStruct.TIM_Period = Timer_Period_Value;

TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;

  TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;

  TIM_TimeBaseInit(Timer_Used,&TIM_TimeBaseInitStruct);

TIM_ClearITPendingBit(Timer_Used,TIM_IT_Update);

TIM_ITConfig(Timer_Used,TIM_IT_Update,ENABLE);

TIM_Cmd(Timer_Used,ENABLE);

}

//////////////////////////////////////////////////////////////////

*/ 

//TIM 3 Config 

void Timer_Configuration_Sec(void)

{

GPIO_InitTypeDef  GPIO_InitStructure;

// Setup LED as PWM output

    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

    GPIO_Init( GPIOC, &GPIO_InitStructure );

    GPIO_PinRemapConfig( GPIO_FullRemap_TIM3, ENABLE ); //LED connected to Pin 6, Port C

TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;

  TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInitStruct.TIM_Period = Timer_Period_Value;

TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;

  //TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;

TIM_TimeBaseInitStruct.TIM_Prescaler = Timer_Prescaler_Value_Sec;

  TIM_TimeBaseInit(Timer_Used_Sec,&TIM_TimeBaseInitStruct);

  TIM_OCInitTypeDef  TIM_OCInitStruct; 

  TIM_OCStructInit( &TIM_OCInitStruct );

  TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; 

  TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; 

// Initial duty cycle equals 0%. Value can range from zero to 1000. 

  TIM_OCInitStruct.TIM_Pulse = SetTIM3Duty(0); 

TIM_OC3Init( TIM3, &TIM_OCInitStruct ); // Initialize Timer Channel 3 setup

TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);

// TIM_ClearITPendingBit(Timer_Used_Sec,TIM_IT_Update);

// TIM_ITConfig(Timer_Used_Sec,TIM_IT_Update,ENABLE);

// TIM_Cmd(Timer_Used_Sec,ENABLE);

}

void Timer_RCC_Configuration(void)

{

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

  

  /* this will reduce the power consumption and increase the device

     immunity against  */

  // RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

//RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM2,DISABLE);

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

}

void Timer_NVIC_Configuration(void)

{

NVIC_InitTypeDef NVIC_InitStructure;

// NVIC_InitStructure.NVIC_IRQChannel = Timer_Used_IRQn;

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = Timer_Used_IRQn_Sec;

NVIC_Init(&NVIC_InitStructure);

}