Reputation: 2214
I built a small board with a STM32L011D3 and an LED. I want to use the LED at 38kHz with a duty cycle of 50%. Now I connected the LED to the wrong pin on the mcu so I can not use the timer as a PWM generator directly. So I was trying to generate the PWM signal manually in the main loop. I thought this would work since I am actually not doing anything else on the mcu.
I configured my clock like the image shows below:
The timer TIM2 I configured like the image shows below:
So this means I should have 4MHz / 4 / 26 = 38.461kHz
The main loop currently looks like this:
With this setup I get something around 37us on / 37us off. The timings are not very stable.
Could it be, that my instructions to enable/disable the led are too long to be executed all 25us, to get a stable pwm output?
Upvotes: 0
Views: 226
Reputation: 93446
You are wasting time reading the output - you set it, so you already know what it is:
for(;;)
{
int out = (timer2Value >= 13) ? GPIO_PIN_SET : GPIO_PIN_RESET ;
HAL_GPIO_WritePin( GPIOA, GPIO_PIN_7, out ) ;
}
If the timing does not then change, then the problem is more likely that your clock is not running at the frequency you think it is.
A more deterministic solution (short of correcting the hardware design), is to set the timer to generate an interrupt at 38000-1 intervals, and toggle the output in the interrupt handler:
void TIM2_IRQHandler()
{
static int out = 0 ;
out = out ? 0 : 1 ;
HAL_GPIO_WritePin( GPIOA, GPIO_PIN_7, out ) ;
}
Or even just:
void TIM2_IRQHandler()
{
HAL_GPIO_TogglePin( GPIOA, GPIO_PIN_7 ) ;
}
If you really want the fastest possible output switching, then you might determine the bitband address of the pin and and toggle that directly:
Given:
__IO uint32_t* getBitBandAddress( volatile const void* address, int bit )
{
__IO uint32_t* bit_address = 0;
uint32_t addr = reinterpret_cast<uint32_t>(address);
// This bit maniplation makes the function valid for RAM
// and Peripheral bitband regions
uint32_t word_band_base = addr & 0xf0000000;
uint32_t bit_band_base = word_band_base | 0x02000000;
uint32_t offset = addr - word_band_base;
// Calculate bit band address
bit_address = reinterpret_cast<__IO uint32_t*>(bit_band_base + (offset * 32u) + (static_cast<uint32_t>(bit) * 4u));
return bit_address ;
}
Then:
__IO uint32_t* gpioA7 = getBitBandAddress( &GPIOA->ODR, 7 ) ;
for(;;)
{
*gpioA7 = (timer2Value >= 13) ;
}
Similarly in an interrupt:
void TIM2_IRQHandler()
{
static __IO uint32_t* gpioA7 = getBitBandAddress( &GPIOA->ODR, 7 ) ;
*gpioA7 = !*gpioA7 ;
}
Though you might choose to initialise gpioA7
externally in the interrupt case.
Upvotes: 2