PWM sine wave program using GPTimer to give high resolution output

andyjay_1
Posts: 1
Joined: Thu Oct 13, 2022 9:03 pm

PWM sine wave program using GPTimer to give high resolution output

Postby andyjay_1 » Tue Jul 25, 2023 8:59 am

This code should give a high resolution sine wave output for driving a low voltage synchronous motor. In due course it will be modified to give the two required out-of-phase signals and adjustment for fine control of motor speed.
It compiles but does not give a sine wave output on pin 25.
Any help debugging it would be greatly appreciated!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <esp_timer.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gptimer.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "freertos/semphr.h"
#include "rom/ets_sys.h"

#define PWM_PIN GPIO_NUM_25
#define SINE_WAVE_FREQUENCY_MIN 39.5 //automatically double as decimal
#define SINE_WAVE_FREQUENCY_MAX 40.5 //ditto
#define SINE_WAVE_FREQUENCY_STEP 0.001
#define PWM_FREQ_TARGET ((SINE_WAVE_FREQUENCY_MIN + SINE_WAVE_FREQUENCY_MAX) / 2.0) //auto double
#define PWM_STEPS 32

const uint16_t sineWaveTable[PWM_STEPS] = {
2048, 2447, 2831, 3185, 3495, 3750, 3939, 4056,
4095, 4056, 3939, 3750, 3495, 3185, 2831, 2447,
2048, 1649, 1265, 911, 601, 346, 157, 40,
0, 40, 157, 346, 601, 911, 1265, 1649
};

typedef struct {
uint32_t dutyCyclePeriod;
} TimerUserData;

TimerUserData timerUserData; // Global instance of TimerUserData

gptimer_handle_t gptimer = NULL; // Declare the gptimer variable

TaskHandle_t pinOffTask; // Task handle for pinOffnotificationFunction

static bool IRAM_ATTR timer_on_alarm_cb(gptimer_handle_t timer, const gptimer_alarm_event_data_t *edata, void *user_ctx)
{
(void)timer;
(void)edata;
TimerUserData* userData = (TimerUserData*)user_ctx;
uint32_t dutyCyclePeriod = userData->dutyCyclePeriod;
(void)dutyCyclePeriod; // Suppress the unused variable warning
BaseType_t higherPriorityTaskWoken = pdFALSE;
vTaskGenericNotifyGiveFromISR(pinOffTask, 0, &higherPriorityTaskWoken); // Notify the pinOffnotificationFunction task to turn off the PWM pin
portYIELD_FROM_ISR(higherPriorityTaskWoken); // Perform any required context switch if xHigherPriorityTaskWoken is set to pdTRUE
return false;
}
void pinOffnotificationFunction(void *pvParameters) {
while (1) {
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); // Wait for the notification from timer_on_alarm_cb
gpio_set_level(PWM_PIN, 0); // Set PWM pin to LOW
//vTaskDelay(pdMS_TO_TICKS(1)); // Delay for a short period to avoid immediate re-notification
// note! 1ms = 4000ticks!
ets_delay_us(100); //Stalls execution for #uS
}
}

void generateSineWaveTask(void *pvParameters) {

uint32_t pwmStepPeriod;
uint32_t refFreq = 4 * 1000 * 1000; //Hz
uint8_t tableIndex = 0; // Declare and initialize the tableIndex variable

gptimer_alarm_config_t alarm_config = {
.alarm_count = 0,
.reload_count = 0,
.flags.auto_reload_on_alarm = false,
};

gptimer_start(gptimer);


while (1) {
gpio_set_level(PWM_PIN, 1); // Set PWM pin to HIGH

pwmStepPeriod = refFreq / (PWM_FREQ_TARGET * 32); //ticks

uint16_t tableValue = sineWaveTable[tableIndex];

uint32_t dutyCyclePeriod = (tableValue * pwmStepPeriod) / 65535; // ticks Calculate the pwm duty cycle based on the sine wave table

timerUserData.dutyCyclePeriod = dutyCyclePeriod; // Pass dutyCyclePeriod to timerUserData

uint32_t reloadCount = pwmStepPeriod - dutyCyclePeriod; //ticks

alarm_config.alarm_count = dutyCyclePeriod; //ticks
alarm_config.reload_count = reloadCount; //ticks
alarm_config.flags.auto_reload_on_alarm = true;
ESP_ERROR_CHECK(gptimer_set_alarm_action(gptimer, &alarm_config)); // Set the alarm action with the updated alarm_config

tableIndex = (tableIndex + 1) % PWM_STEPS; // Increment the tableIndex
}
}

void app_main() {
gpio_config_t io_conf = {}; //zero-initialize the config structure.
io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = (1ULL << PWM_PIN);
io_conf.pull_down_en = 0; //disable pull-down mode
io_conf.pull_up_en = 0; //disable pull-up mode
gpio_config(&io_conf); //configure GPIO with the given settings

gptimer_config_t timer_config = { // Define the desired timer configuration
.clk_src = GPTIMER_CLK_SRC_DEFAULT,
.direction = GPTIMER_COUNT_UP,
.resolution_hz = 4 * 1000 * 1000, // 4MHz, 1 tick = 0.25us
};

gptimer_new_timer(&timer_config, &gptimer);

gptimer_event_callbacks_t cbs = {.on_alarm = timer_on_alarm_cb,};

gptimer_register_event_callbacks(gptimer, &cbs, &timerUserData);

gptimer_enable(gptimer);

xTaskCreate(pinOffnotificationFunction, "pinOffTask", configMINIMAL_STACK_SIZE, NULL, 1, &pinOffTask);
// Create the generateSineWaveTask as a FreeRTOS task
xTaskCreate(generateSineWaveTask, "SineWaveTask", configMINIMAL_STACK_SIZE, NULL, 1, NULL);

}

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: PWM sine wave program using GPTimer to give high resolution output

Postby MicroController » Wed Jul 26, 2023 5:57 pm

generateSineWaveTask(...) keeps setting the PWM_PIN output to 1 in a loop, as fast as possible; and pinOffnotificationFunction(...) sets it to 0, but that is only effective briefly until generateSineWaveTask(...) sets it back to 1.

MicroController
Posts: 1708
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: PWM sine wave program using GPTimer to give high resolution output

Postby MicroController » Thu Jul 27, 2023 8:46 am

Btw, instead of generating PWM output in software, you should look into the Motor Control Pulse Width Modulator or the LED Controller.
With those peripherals, outputting a certain waveform boils down to just updating the duty cycle at regular intervals.

Who is online

Users browsing this forum: No registered users and 128 guests