Hello,
I'm tyring to program a STM32F4 (with ARM F4) board to generate a sinusoidal PWM, that is a sequence of pulses with variable width that, once filtered by a LPF, gives a sinusoid.
I was told the best way to get it is by generating a sequence of single pulses synchronized to timer by interrupts.
I've tried several solutions but I'm not good at managing interrupts.
Do you have any idea how to achieve this goal? Can you attach a piece of code where it's explained how to do it?
Thanks in advance! ;)
Mara
You can't manage them, or you don't understand the concept? Can your teachers/instructors not take responsibility for adequately explaining things?
Yes, you can use PWM mode, you can create a table of values to program into the CCRx for the channel you're using at each Update interrupt of the timer.
If the value of the Period is 10000-1, then the sine table you create will need to be 5000 + 5000*sin(angle), a table with 10 degree increments would consist of 36 entries. At each interrupt you'd output the next value, incrementing a counter, and wrapping it based on the size of the table.
ie
void TIMx_IRQHandler(void) // Adapt for timer and channel under consideration { static int index = 0; if (TIM_GetITStatus(TIMx, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIMx, TIM_IT_Update); TIMx->CCRx = sinetbl[index]; index = (index + 1) % 36; } }
Thank you for your answer!
Well, I know the theory about interrupts, but I've never used them before so I don't know how to manage them in my code.
I have two more questions for you:
1) Which function do I have to call to generate the sequence of pulses?
(HAL_TIM_OnePulse_Start, HAL_TIM_Base_Start_IT, HAL_TIM_PWM_Start)
2) How can I connect the execution of the function to the interrupt?
Forgive my incompetence, but that's my first time of hardware programming.
Thanks, Mara
What board are you using?
STM32F401RE
It looks like you have a STM32F401RE-Nucleo board. You can download at:
www.st.com/.../PF259243
at the bottom of the page is a read button. It provides a few projects for your board as well as other STMF4 Boards. The more expensive boards have the most project examples. I am looking at a Nucleo board the STM32F153RE. It has one project called TIM_DMA which creates a output PWM signal that varies its pulses since the DMA continuously writes to TIM1 Capture Compare Register 3.
Description in readme file This example provides a description of how to use DMA with TIM1 Update request to transfer Data from memory to TIM1 Capture Compare Register 3 (CCR3).
The following configuration values are used in this example:
- TIM1CLK = SystemCoreClock - Counter repetition = 3 - Prescaler = 0 - TIM1 counter clock = SystemCoreClock - SystemCoreClock is set to 64 MHz for STM32F1xx
The objective is to configure TIM1 channel 3 to generate complementary PWM (Pulse Width Modulation) signal with a frequency equal to 17.57 KHz, and a variable duty cycle that is changed by the DMA after a specific number of Update DMA request.
The number of this repetitive requests is defined by the TIM1 Repetition counter, each 4 Update Requests, the TIM1 Channel 3 Duty Cycle changes to the next new value defined by the aCCValue_Buffer.
The PWM waveform can be displayed using an oscilloscope.
The examples provided for the STM32F401RE-Nucleo do not include a PWM project. But if you download the STM32F4 Cube software there are also examples for the Other STM32F4 boards included. You can browse the other projects and take a look at PWM examples. Especially look at the EVAL board projects.
With DMA
// NUCLEO-F401RE DMA Driven PWM - Not good for school or work assignments. // Copyright (C) 2015 sourcer32@gmail.com, All Rights Reserved #include "stm32f4xx.h" #define PWM_ELEMENTS 100 const u32 PWM_Buffer[PWM_ELEMENTS] = { // Sine Table 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 50, 53, 56, 58, 61, 63, 66, 68, 70, 72, 75, 77, 79, 80, 82, 84, 86, 87, 89, 90, 91, 92, 94, 95, 96, 96, 97, 98, 98, 99, 99, 99, 99,100, 99, 99, 99, 99, 98, 98, 97, 96, 96, 95, 94, 92, 91, 90, 89, 87, 86, 84, 82, 80, 79, 77, 75, 72, 70, 68, 66, 63, 61, 58, 56, 53, 50, 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3 }; /**************************************************************************************/ void RCC_Configuration(void) { /* GPIOA clock enable */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); /* DMA1 clock enable */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); /* TIM2 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); } /**************************************************************************************/ void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; /* GPIO Configuration */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Connect TIM2 pins to AF */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_TIM2); // PA5 TIM2_CH1 } /**************************************************************************************/ void DMA_Configuration(void) { DMA_InitTypeDef DMA_InitStruct; /* TIM2_UP - DMA1, Channel 3, Stream 1 */ DMA_InitStruct.DMA_Channel = DMA_Channel_3; DMA_InitStruct.DMA_BufferSize = PWM_ELEMENTS; DMA_InitStruct.DMA_DIR = DMA_DIR_MemoryToPeripheral; DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable; DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&PWM_Buffer[0]; DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word; DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&TIM2->CCR1; DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; DMA_Init(DMA1_Stream1, &DMA_InitStruct); /* Enabling DMA */ DMA_Cmd(DMA1_Stream1, ENABLE); } /**************************************************************************************/ void TIM2_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 10000) - 1; // 10 KHz tick TIM_TimeBaseStructure.TIM_Period = 100-1; // 0..99, 100 Hz period TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 1; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* Channel 1 configuration (PA.5) */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 50; // 50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OC1Init(TIM2, &TIM_OCInitStructure); /* Associating DMA and TIM2 Update (DMA1 Channel 3, Stream 1) */ TIM_DMACmd(TIM2, TIM_DMA_Update, ENABLE); /* Turning on TIM2 and PWM outputs */ TIM_Cmd(TIM2, ENABLE); } /**************************************************************************************/ int main(void) { RCC_Configuration(); GPIO_Configuration(); DMA_Configuration(); TIM2_Configuration(); while(1); } /**************************************************************************************/ #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { } } #endif /**************************************************************************************/
With IRQ
// NUCLEO-F401RE IRQ Driven PWM - Not good for school or work assignments. // Copyright (C) 2015 sourcer32@gmail.com, All Rights Reserved #include "stm32f4xx.h" #define PWM_ELEMENTS 100 const u32 PWM_Buffer[PWM_ELEMENTS] = { // Sine Table 0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 50, 53, 56, 58, 61, 63, 66, 68, 70, 72, 75, 77, 79, 80, 82, 84, 86, 87, 89, 90, 91, 92, 94, 95, 96, 96, 97, 98, 98, 99, 99, 99, 99,100, 99, 99, 99, 99, 98, 98, 97, 96, 96, 95, 94, 92, 91, 90, 89, 87, 86, 84, 82, 80, 79, 77, 75, 72, 70, 68, 66, 63, 61, 58, 56, 53, 50, 48, 45, 42, 39, 36, 33, 30, 27, 24, 21, 18, 15, 12, 9, 6, 3 }; /**************************************************************************************/ void RCC_Configuration(void) { /* GPIOA clock enable */ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); /* TIM2 clock enable */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); } /**************************************************************************************/ void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; /* GPIO Configuration */ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); /* Connect TIM2 pins to AF */ GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_TIM2); // PA5 TIM2_CH1 } /**************************************************************************************/ void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&NVIC_InitStructure); } /**************************************************************************************/ void TIM2_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_TimeBaseStructure.TIM_Prescaler = (SystemCoreClock / 10000) - 1; // 10 KHz tick TIM_TimeBaseStructure.TIM_Period = 100-1; // 0..99, 100 Hz period TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseStructure.TIM_RepetitionCounter = 1; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); /* Channel 1 configuration (PA.5) */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 50; // 50% TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; TIM_OC1Init(TIM2, &TIM_OCInitStructure); /* Enable TIM2 Update Interrupt */ TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); /* Turning on TIM2 and PWM outputs */ TIM_Cmd(TIM2, ENABLE); } /**************************************************************************************/ void TIM2_IRQHandler(void) // Per Keil forum example, modified to suit { static int index = 0; if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); TIM2->CCR1 = PWM_Buffer[index]; index = (index + 1) % PWM_ELEMENTS; } } /**************************************************************************************/ int main(void) { RCC_Configuration(); GPIO_Configuration(); NVIC_Configuration(); TIM2_Configuration(); while(1); } /**************************************************************************************/ #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { } } #endif /**************************************************************************************/
Thanks for your help!
I'm using the second code, the one with IRQ, but I got 26 errors because a lot of functions and structures you've used are unknown to Keil, like:
TIM_IT_Update TIM_Cmd RCC_AHB1PeriphClockCmd NVIC_InitStructure ... and many more
What library did you use? I can't find none of these functions. Did you program the code for the STM32F401RE?
Just another question ... how can I modify that code if I want to introduce another two phases at 120 and 240 degrees to drive a brushless motor?
View all questions in Keil forum