As a part of my project I needed a PWM source and since hardware PWM was not accessible before OS 19.09, the only other option was to do it old school: by bit-banging GPIO pins. Turns out it wasn't that difficult and since this technique might sometimes be useful even with HW PWM present, I'd like to share my code for others to use.
PWM (or Pulse Width Modulation) is basically a clock signal with a programmable duty cycle. PWM generator could then be decribed by using two values: clock frequency (or period) and current duty cycle percentage. My implementation uses real-time M4 core GPT timers for generating clock signal where timer duration is set depending on duty cycle value. GPT timer after running out generates interrupt and the interrupt handling routine flips GPIO state.
Since the original GPT timer setup routine supports only 1 kHz timer clock, which is probably too slow for PWM generating purposes, I have tweaked this routine a bit (pun intended ) and switched GPT timer clock to maximum supported 32 kHz. See mt3620-timer-user.c where I also added basic routines for previously unsupported GPT2 timer. GPT2 is a free-running timer and cannot be used for interrupts, but it can be very useful for time keeping as an Arduino's millis() and delay() function replacement. With this setup we can reach maximum software PWM frequencies around 5 kHz.
The repository contains complete real-time core application which demonstrates PWM function by dimming RGB LED after repeated button1 presses. You can find the complete code here: https://github.com/jgroman/azsphere_rtcore_softpwm_example
PWM generating function is basically this short interrupt handling routine, which is called when pwm timer runs out. Clock period is set by COUNTER_PWM_MAX and the current ducty cycle value is stored in counter_pwm_on array.
static void handle_irq_pwm_timer(void) { static bool b_is_pwm_gpio_high = true; // Calculate this pulse duration counter uint32_t counter = (b_is_pwm_gpio_high) ? counter_pwm_on[index_counter_pwm] : COUNTER_PWM_MAX - counter_pwm_on[index_counter_pwm]; if (counter > 0) { // Do not flip output GPIO if requested pulse duration is 0 Mt3620_Gpio_Write(GPIO_RGBLED_RED, b_is_pwm_gpio_high); Mt3620_Gpio_Write(GPIO0, b_is_pwm_gpio_high); } b_is_pwm_gpio_high = !b_is_pwm_gpio_high; start_pwm_timer(counter); }
Top Comments