cancel
Showing results for 
Search instead for 
Did you mean: 

How to generate PWM using timers working with PLL

DJ1
Associate III

Here, i am working on STM32F401 controller, where i am using PLL with HSI working at 72MHz freq. The timer is generating a PWM output but instead of generating 24 pulses as per my requirement it is only generating 6 pulses. Hence 2 pulses for every 8 inputs. Where can it be wrong, as i am newly working on high frequencies i am not sure.

 

uint8_t pwmData[24]={61,61,61,61,61,61,61,61, 39,39,39,39,39,39,39,39, 39,39,39,39,39,39,39,39};   

 

int main()
{
RCC_Init();
 
GPIOA_PCLK_EN;
 
// PWM Alternate Functionality
pwm.pGPIOx = GPIOA;
pwm.GPIO_PinConfig.GPIO_PinMode = GPIO_MODE_AF;
pwm.GPIO_PinConfig.GPIO_PinAltFunMode = 1;
pwm.GPIO_PinConfig.GPIO_PinNumber = 0; // Timer 1 Channel 1
GPIO_Init(&pwm);
 
 
// Enable clock for timer 2
TIM2_EN;
// Timer CCER bit enable
TIM2->TIMx_CCER |= (1<<0);
// ARPE bit enable
TIM2->TIMx_CR1 |= (1 << 7);
// Enable OC1PE bit and OC1M bit
TIM2->TIMx_CCMR1 |= ((6 << 4)|(1 << 3));
 
// Generate 1ms delay from 16MHz clock, pre-scalar value is 16000-1
TIM2->TIMx_PSC = 1 - 1;
// Store the count in ARR
TIM2->TIMx_ARR = 90-1; // 100 milliseconds
 
 
// Enable UG bit in EGR
TIM2->TIMx_EGR |= (1 << 0);
// Timer Count = 0
TIM2->TIMx_CNT = 0; // counter reset
// Timer enable
TIM2->TIMx_CR1 |= (1 << 0); // Timer2
 
while(1)
{
for(int i=0;i<24;i++)
// Set duty cycle
TIM2->TIMx_CCR1 = pwmData[i]; 
}
}
 
 
DJ1_0-1693282429611.png

 

4 REPLIES 4
LCE
Principal

There might be several problems.

0) In general, for better readability use bit defines instead of "(1 << 0)", these should be in some "*f401*.h" file (and there is a better forum option to show source code, it's in the "..." menu)

1) Your timer settings are strange, you write the timer gets 16 MHz, then set PSC to 0, and ARR to 89 -> that will not give you 100 ms

2) In the while loop, think about what's going on there:
the for loop is called all the time, almost at CPU clock frequency, and each time CCR1 is set with a new value.
So you better use DMA to feed the CCR register, thus only when the current pwmData[i] "time" has elapsed, a new value is given to CCR.

DJ1
Associate III

Well the thing is i am not trying to generate a PWM wave of 100ms. The HSI (16MHz) is used to supply clock to PLL which generates 72MHz. Now i have taken a pre-scaler of 1 which when multiplied by the ARR of 90 gives me an output wave with a time period of 1.25 micro-seconds. Here also i am using NRZ protocol based concept as my LED's work on it, meaning the PWM pulse will never become 0, hence the changing CCR1 value. The for loop is just for testing.

The ultimate idea is to use DMA only but my concern is related to whether it is possible to use this way at high speeds or not at all possible.

Also about the bits shifting, the thing is we have made our own header from scratch for better understanding thus we have not made macros for every position.

Also i will attach my RCC_Init function which enables PLL.

void RCC_Init()
{
/*
* RCC Settings for setting upto 72MHz freq using HSI based PLL
*/
 
// Set PLL clock for MCO output
//RCC->CFGR |= (3 << 21);
// Set pre-scaler for MCO1 bcoz of Logic Analyzer
//RCC->CFGR |= (4 << 24);
 
 
// Program number of wait states to the Latency bits in ACR
FLASH->FLASH_ACR |= (2 << 0);
// Enable Pre-fetch
FLASH->FLASH_ACR |= (1 << 8);
 
#ifdef HSE
 
// Turn ON HSE
RCC->CR |= (1 << 16);
// Wait till ready
while(!(RCC->CR >> 17 & 1));
// Set HSE as PLL source
RCC->CFGR |= (1 << 22);
// Turn OFF HSI
RCC->CR &= ~(1 << 0);
// Enable Clock security CSS
RCC->CR |= (1 << 19);
 
 
#else
 
// Turn on HSI ON
RCC->CR |= (1 << 0);
// wait until HSI is ready
while(!(RCC->CR >> 1 & 1));
 
#endif
 
// Changing HSITRIM Calculation for HSI clock to work precisely
// Clear the default 16 value
RCC->CR &= ~(16 << 3);
// Change the value to 12
RCC->CR |= (12 << 3);
 
 
// Clear the default 192 setting
RCC->PLLCFGR &= ~(192 << 6);
// Set 200 to PLLN
RCC->PLLCFGR |= (216 << 6);
// Clear the default 16 setting
RCC->PLLCFGR &= ~(16 << 0);
// Set 8 to PLLM
RCC->PLLCFGR |= (8 << 0);
// Set PLLP to 6
RCC->PLLCFGR |= (2 << 16);
// Clear PLLQ
RCC->PLLCFGR &= ~(4 << 24);
// Set PLLQ to 9
RCC->PLLCFGR |= (9 << 24);
 
// Set Pre-scaler 2 for APB1 bus within 42MHz
RCC->CFGR |= (4 << 10);
 
// Turn on PLL from RCC CR Register
RCC->CR |= (1 << 24);
// Wait till PLL is ready
while(!(RCC->CR >> 25 & 1));
 
// SET PLL as system clock
RCC->CFGR |= (2 << 0);
// Wait until PLL Switch Status is SET
while(!(RCC->CFGR >> 2 & 2));
}

This has nothing to do with PLL.

As @LCE  said above, in the for() loop, before storing the new value to TIM2_CCR1, you have to wait until the previous period elapsed, i.e. you have to wait for TIM2_SR.UIF going high, clear it, and then write the next CCR1 value.

JW

I tried that but it is jumbling my output on LED. Actually i am using Addressable RGB LED, for which 1 LED takes 24 bit/pulse (NRZ) PWM data (1 pulse may it be 0 or 1 has a time period of 1.25 micro sec). I have generated a precise time period as required by its datasheet still the overall issue is i am not able to completely control them. Also i am working on Timer DMA parallelly but facing few issues in it. One of the reason for working with PLL also was due to these LED's only as they need high freq. Please do suggest something efficient.

I am able to get primary colors on the LED but the issue starts with different color patterns. Also without PLL, i am getting 24 pulses.