STM32 bit bang PWM

From Stm32World Wiki
Revision as of 03:15, 7 May 2024 by Lth (talk | contribs) (Lth moved page STM32 Bitbang PWM to STM32 Bit bang PWM)
Jump to navigation Jump to search

On many of the cheaper STM32 Development Boards there is a LED attached to PC13. This is perfectly ok if you want to switch it on or off, but PC13 is not attached to any of the timer channels, so it will not be possible to control the brightness using PWM.

Fortunately, while not ideal, it is possible to bitbang the PWM in a manner which doesn't require too much computation. Contrary to PWM using a Timer channel, it does require some computation in the MCU.

Bit-banding

Bit-banding is a feature of all STM32 MCUs. Because STM32 are 32-bit MCUs they got a huge address space, much bigger than what is needed to address available memory. Each byte of memory has got a dedicated address for each bit, so if a single bit needs to be set or reset, a value of 0 or 1 can be written to that address. Without digging into details, the following macro will calculate the necessary address.

#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))

Code

Timer Settings

Firstly configure some timer (we use TIM10 here):

Bitbang PWM Timer Setting.png

We also need to enable the global timer 10 interrupt:

Bitbang PWM Timer Interrupt Setting.png

Implementation

We use bitbanding to address the bit used for GPIO:

// The led_bb_bit points to the bitband address for controlling the PC13
uint8_t *led_bb_bit = (uint8_t*) BITBAND_BIT_ADDR(&LED_GPIO_Port->ODR, 13);

A few more global variables keep track of the counter and pwm value:

// Variables to run the pwm.  led_pwm_cnt goes from 0-255 and then roll back over to 0.
// led_pwm_val determines the ratio between on and off status of the LED.
uint32_t led_pwm_cnt;
uint8_t led_pwm_val = 0x00;

Now the actual PWM work can be handled in the interrupt callback:

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

    if (htim->Instance == LED_PWM_TIM) {

        // 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;

    }

}

Result

Miscellaneous Links