Difference between revisions of "STM32 Crazy Mystery"

From Stm32World Wiki
Jump to navigation Jump to search
Line 4: Line 4:
 
Since PC13 is commonly used to drive an on-board [[LED]] on [[STM32 Development Boards|Development Boards]] and since PC13 is not connected to any timer channel, usually one can only toggle that [[LED]] on or off.  A while back I was experimenting with bit banging PWM on this pin to be able to dim and pulse the led.
 
Since PC13 is commonly used to drive an on-board [[LED]] on [[STM32 Development Boards|Development Boards]] and since PC13 is not connected to any timer channel, usually one can only toggle that [[LED]] on or off.  A while back I was experimenting with bit banging PWM on this pin to be able to dim and pulse the led.
  
{{#ev:youtube|ZsHR1bQOmzk}}
+
 
  
 
== What works (STM32F411 - [[Black Pill]]) ==
 
== What works (STM32F411 - [[Black Pill]]) ==
Line 26: Line 26:
  
 
The working example can be found here: [https://github.com/lbthomsen/blackpill/tree/master/bitbang_pwm https://github.com/lbthomsen/blackpill/tree/master/bitbang_pwm]
 
The working example can be found here: [https://github.com/lbthomsen/blackpill/tree/master/bitbang_pwm https://github.com/lbthomsen/blackpill/tree/master/bitbang_pwm]
 +
 +
On the oscilloscope it looks like this:
 +
 +
{{#ev:youtube|ZsHR1bQOmzk}}
  
 
== What does '''not''' work (STM32F405 - [[STM32World]]) ==
 
== What does '''not''' work (STM32F405 - [[STM32World]]) ==

Revision as of 07:19, 16 October 2024

This page documents a bit of a mystery that I - so far - have been unable to solve.

Since PC13 is commonly used to drive an on-board LED on Development Boards and since PC13 is not connected to any timer channel, usually one can only toggle that LED on or off. A while back I was experimenting with bit banging PWM on this pin to be able to dim and pulse the led.


What works (STM32F411 - Black Pill)

The first attempt was done on a STM32F411 based Black Pill board and it worked flawlessly. The idea is to run a repeating timer, run an 8-bit counter in the timer callback and then toggle the GPIO on and off at an appropriate level within that range. In the working code that looks like:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

    if (htim->Instance == LED_PWM_TIM) {
        ++led_pwm_cnt; // The counter is 8 bit so will wrap around after 255

        // Use BSRR to set or reset bit 13 of the LED GPIO port
        LED_GPIO_Port->BSRR = led_pwm_cnt >= led_pwm_val ? GPIO_BSRR_BS13 : GPIO_BSRR_BR13;
    }

}

Extremely simple really. First check for the right timer, then increase a counter (which will roll around automatically as it is only an 8 bit variable) and finally set the BSRR register (set/reset register) of the appropriate port to an appropriate BS13 (set) or BR13 (reset) value.

The working example can be found here: https://github.com/lbthomsen/blackpill/tree/master/bitbang_pwm

On the oscilloscope it looks like this:

What does not work (STM32F405 - STM32World)

Exactly the same (I think) was later implemented on one of our own STM32World boards, which are based on STM32F405 MCU. However, this did not work. The PC13 remains at whatever value it was on when initialized.

The first thought was obviously if the callback was ever executed (as a result of timer not running proper or interrupt not configured correctly), so an alternative implementation was tried using the bit-banding feature of the STM32F405. Using the following:

#define BITBAND_BIT_ADDR(src_byte_addr, bit)  (((((uint32_t)(src_byte_addr) & 0x000fffff) << 5) | ((uint32_t)(src_byte_addr) & 0xfff00000) | 0x02000000) + (((uint32_t)(bit)) << 2))

uint8_t *led_bb_bit = (uint8_t*) BITBAND_BIT_ADDR(&LED_GPIO_Port->ODR, 13);

And then the following callback:

// Callback which runs the PWM
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

    if (htim->Instance == TIM10) {

        // Increase the counter - it will roll over automatically every 256 bit
        ++led_pwm_cnt;

        // Switch LED on off or on depending on value of led_pwm_cnt.
        *led_bb_bit = (uint8_t) led_pwm_cnt >= led_pwm_val ? 1 : 0;

    }

}

It is working flawlessly - exactly as the STM32F411, but any attempt to toggle the bit using the BSRR register failed. Here is the callback as committed with three failed attempts commented out:

// Callback which runs the PWM
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

    if (htim->Instance == TIM10) {

        // Increase the counter - it will roll over automatically every 256 bit
        ++led_pwm_cnt;

        // Switch LED on off or on depending on value of led_pwm_cnt.
        *led_bb_bit = (uint8_t) led_pwm_cnt >= led_pwm_val ? 1 : 0;

        // NOTICE!  Huge unsolved mystery
        // Use BSRR to set or reset bit 13 of the LED GPIO port.  This works perfectly on
        // STM32F411 but for some bizarre reason it does not work on STM32F405.
//        LED_GPIO_Port->BSRR = led_pwm_cnt >= led_pwm_val ? GPIO_BSRR_BS13 : GPIO_BSRR_BR13;
//        LED_GPIO_Port->BSRR = led_pwm_cnt >= led_pwm_val ? LED_Pin : (LED_Pin << 16);
//        if (led_pwm_cnt >= led_pwm_val) {
//            LED_GPIO_Port->BSRR = GPIO_BSRR_BS13;
//        } else {
//            LED_GPIO_Port->BSRR = GPIO_BSRR_BR13;
//        }
    }

}

The non-working example is here: https://github.com/STM32World/firmware/tree/master/mcustm32f405_bitbang_pwm