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);
}
PWM sine wave program using GPTimer to give high resolution output
-
- 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
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.
-
- 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
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.
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