No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Control PWM con ESP32 y LEDC

22/30
Recursos

Aportes 5

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Compre el osciloscopio para armar junto con el esp32 ❤️

Para responder unas preguntas en los otros comentarios dejo una foto del mismo ejercicio pero manipulando la velocidad de giro de un motor.

El único cambio es que uso el pin 15 de salida porque es el que tengo en la tarjeta con PWM.

El osciloscopio es de bolsillo, es un DSO v3 de la empresa seed studio.

No tengo potenciometro pero lo simule con una funcion.
.
Segui el ejemplo del manual
.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/adc.h"
#include "esp_adc_cal.h"
#include "driver/ledc.h"

#define LED_PIN       25
#define LED_RED_PIN   4
#define LED_GREEN_PIN 0
#define LED_BLUE_PIN  12

#define SW_KY040_PIN  34 

bool state_led   = false;
bool state_red   = false;
bool state_green = false;
bool state_blue  = false;


const TickType_t blink_delay    = 1000 / portTICK_PERIOD_MS;
const TickType_t debounce_delay =   50 / portTICK_PERIOD_MS;
const TickType_t digital_delay  =  100 / portTICK_PERIOD_MS;
const TickType_t battVolt_delay = 2000 / portTICK_PERIOD_MS;
const TickType_t pwm_delay =       500 / portTICK_PERIOD_MS;

TaskHandle_t ledHandle;
TaskHandle_t swky040Handle;
TaskHandle_t battVoltHandle;
TaskHandle_t pwmHandle;

uint32_t raw_value;
uint32_t milivolts;
static esp_adc_cal_characteristics_t adc1_chars;

//en el cado del gpio35 el divisor de voltaje esta compuesto por dos resistencias de 100k
// Esto quiere decir que llega mitad del voltaje de la bateria.
// Para obtener el valor correcto hay que multiplicar el valor medido por dos
// como el valor del voltaje medido es milivoltios  para obtener los voltios hay que
// dividir por 1000. De hay el factor de conversion de 0.002 
double voltage_divider_factor = 0.002;
double battery_volt;

int32_t duty_cycle   = 0;
int32_t dc_increment = 64;

void refresh_led(void *args)
{
    while (true)
    {
        state_led = !state_led;
        gpio_set_level(LED_PIN, state_led);
        vTaskDelay(blink_delay);
    }     
}

void refresh_swky040(void *args) 
{
    while (true)
    {    
        //El switch esta conectado en pull up
        //su estado normal es alto
        //cuando se oprime su estado es bajo    
        state_red = gpio_get_level(SW_KY040_PIN);
        gpio_set_level(LED_RED_PIN, !state_red);
        if (!state_red) vTaskDelay(debounce_delay);
        vTaskDelay(digital_delay);
    }
}

void refresh_battVolt(void *args) 
{
    while (true)
    {    
        raw_value = adc1_get_raw(ADC1_CHANNEL_7);
        milivolts = esp_adc_cal_raw_to_voltage(raw_value, &adc1_chars);
        battery_volt = milivolts;
        battery_volt = battery_volt * voltage_divider_factor;
        printf("raw value: %ld milivolts: %ld battery: %f\n",raw_value, milivolts, battery_volt);
        vTaskDelay(battVolt_delay);
    }
}

void refresh_pwm_led(void *args)
{
    while (true)
    {
        duty_cycle = duty_cycle + dc_increment;
        if (duty_cycle > 4096)
        {
            duty_cycle = 4095;
            dc_increment = - dc_increment;
        }
        if (duty_cycle < 0)
        {
            duty_cycle = 0;
            dc_increment = - dc_increment;
        }
        ESP_ERROR_CHECK(ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, duty_cycle));
        ESP_ERROR_CHECK(ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0));       
        vTaskDelay(pwm_delay);
    }
}


void app_main(void)
{
    //reset pines de salida
    gpio_reset_pin(LED_PIN);

    gpio_reset_pin(LED_RED_PIN);
    //gpio_reset_pin(LED_GREEN_PIN);
    gpio_reset_pin(LED_BLUE_PIN);
    
    // coloca los pines en estado de salida o entrada
    gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
    
    gpio_set_direction(LED_RED_PIN, GPIO_MODE_OUTPUT);
    //gpio_set_direction(LED_GREEN_PIN, GPIO_MODE_OUTPUT);
    gpio_set_direction(LED_BLUE_PIN, GPIO_MODE_OUTPUT);
    
    gpio_set_direction(SW_KY040_PIN, GPIO_MODE_INPUT);

    //coloca los leds rgb en apagado
    gpio_set_level(LED_RED_PIN,   state_red);
    //gpio_set_level(LED_GREEN_PIN, state_green);
    gpio_set_level(LED_BLUE_PIN,  state_blue);


    //En el chip Lora32, esp32 con radio LORA, OLED Display, cargador bateria LIPO:
    //El gpio35 lee a traves de un divisor de 2 resistencias de 100k el voltaje de la
    //   bateria.
    //El gpio35 corresponde al ADC1_CHANNEL7

    //calibramos el adc1
    esp_adc_cal_characterize(
        ADC_UNIT_1,             //apuntamos al adc 1
        ADC_ATTEN_DB_11,        //Rango lineal entre 150-2450 mv
        ADC_WIDTH_BIT_12,       //0 a 4095
        0,                      //
        &adc1_chars);           //resultado de la calibracion 

    ESP_ERROR_CHECK(adc1_config_width(ADC_WIDTH_BIT_12));  
    ESP_ERROR_CHECK(adc1_config_channel_atten(ADC1_CHANNEL_7, ADC_ATTEN_DB_11));  
    

    //pwm timer config 
    ledc_timer_config_t ledc_timer = {
        .speed_mode       = LEDC_HIGH_SPEED_MODE,
        .duty_resolution  = LEDC_TIMER_12_BIT,
        .timer_num        = LEDC_TIMER_0,
        .freq_hz          = 4000,  // Set output frequency at 4 kHz
        .clk_cfg          = LEDC_AUTO_CLK
    };
    ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));

    // Prepare and then apply the LEDC PWM channel configuration
    ledc_channel_config_t ledc_channel = {
        .speed_mode     = LEDC_HIGH_SPEED_MODE,
        .channel        = LEDC_CHANNEL_0,
        .timer_sel      = LEDC_TIMER_0,
        .intr_type      = LEDC_INTR_DISABLE,
        .gpio_num       = LED_GREEN_PIN,
        .duty           = 0, // Set duty to 0%
        .hpoint         = 0
    };
    ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));

    //creamos la tarea asociandola al segundo core
    xTaskCreatePinnedToCore(
        refresh_led,    //Nombre de la funcion que implementa la tarea
        "refresh_led",  //Nombre descriptivo de la tarea con fines de debugging
        2048,           //El tamaño del task stack en bytes
        NULL,           //pointer a un parametro cuando la tarea esta siendo creada
        5,              //Prioridad de la tarea un numero
        &ledHandle,     //apuntador para referenciar la tarea. 
        1               // core 0 y core 1
    );

    xTaskCreatePinnedToCore(
        refresh_swky040,   //Nombre de la funcion que implementa la tarea
        "refresh_swky040", //Nombre descriptivo de la tarea con fines de debugging
        2048,              //El tamaño del task stack en bytes
        NULL,              //pointer a un parametro cuando la tarea esta siendo creada
        10,                //Prioridad de la tarea un numero
        &swky040Handle,    //apuntador para referenciar la tarea. 
        1                  // core 0 y core 1
    );
    
    xTaskCreatePinnedToCore(
        refresh_battVolt,   //Nombre de la funcion que implementa la tarea
        "refresh_battVolt", //Nombre descriptivo de la tarea con fines de debugging
        2048,              //El tamaño del task stack en bytes
        NULL,              //pointer a un parametro cuando la tarea esta siendo creada
        5,                 //Prioridad de la tarea un numero
        &battVoltHandle,        //apuntador para referenciar la tarea. 
        1                  // core 0 y core 1
    );
   
   xTaskCreatePinnedToCore(
        refresh_pwm_led,   //Nombre de la funcion que implementa la tarea
        "refresh_pwm_led", //Nombre descriptivo de la tarea con fines de debugging
        2048,              //El tamaño del task stack en bytes
        NULL,              //pointer a un parametro cuando la tarea esta siendo creada
        5,                 //Prioridad de la tarea un numero
        &pwmHandle,        //apuntador para referenciar la tarea. 
        1                  // core 0 y core 1
    );
   
}

Definitivamente con un oscilador se puede apreciar mucho mejor lo interesante de esta práctica. Los osciladores profesionales son bastante caros pero uno como este (tipo mini para principiantes) se puede conseguir en Amazon en un rango de precios entre $50 y $150 bajo la descripción de pocket Digital Storage Oscillator (DSO).

<img src=“https://drive.google.com/file/d/1FaupW7f4dYdz9C9GvhFT9GiJClAmhlE9/view?usp=sharing” alt=“Osciloscopio”>

comparto el codigo con algunas optimizaciones, ya que no comprendo para que definio la estructura de ledc\_channel si en los parametros de las funciones no le pasa las variables de esa estructura sino puras macros de IDF. ```js //incluimos las librerias necesarias #include <stdio.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_adc/adc_oneshot.h" #include "driver/ledc.h" #include "esp_log.h" //defines #define LEDR_PIN 17 #define LEDG_PIN 18 #define LEDB_PIN 5 //variables globales adc_oneshot_unit_handle_t adc1_handle; adc_oneshot_unit_init_cfg_t adc1_config = { .unit_id = ADC_UNIT_1, .ulp_mode = ADC_ULP_MODE_DISABLE, }; adc_oneshot_chan_cfg_t config = { .bitwidth = ADC_BITWIDTH_12, .atten = ADC_ATTEN_DB_11, }; void app_main(void) { ledc_timer_config_t ledc_timer = { .speed_mode = LEDC_HIGH_SPEED_MODE, .timer_num = LEDC_TIMER_0, .duty_resolution = LEDC_TIMER_12_BIT, .freq_hz = 5000, .clk_cfg = LEDC_AUTO_CLK }; ledc_channel_config_t ledr_chnl = { .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, .intr_type = LEDC_INTR_DISABLE, //interrupciones deshabilitadas .gpio_num = LEDR_PIN, //pin de salida PWM .duty = 0, //ciclo util inicial .hpoint = 0 //ajuste de fase }; ledc_channel_config_t ledg_chnl = { .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, .intr_type = LEDC_INTR_DISABLE, //interrupciones deshabilitadas .gpio_num = LEDG_PIN, //pin de salida PWM .duty = 0, //ciclo util inicial .hpoint = 0 //ajuste de fase }; ledc_channel_config_t ledb_chnl = { .speed_mode = LEDC_HIGH_SPEED_MODE, .channel = LEDC_CHANNEL_0, .timer_sel = LEDC_TIMER_0, .intr_type = LEDC_INTR_DISABLE, //interrupciones deshabilitadas .gpio_num = LEDB_PIN, //pin de salida PWM .duty = 0, //ciclo util inicial .hpoint = 0 //ajuste de fase }; int adc_value; uint32_t duty_red = 0; uint32_t duty_green = 0; uint32_t duty_blue = 0; //configuramos el timer para el PWM del LED ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); //configuramos el canal PWM para el led rojo, verde y azul ESP_ERROR_CHECK(ledc_channel_config(&ledr_chnl)); ESP_ERROR_CHECK(ledc_channel_config(&ledg_chnl)); ESP_ERROR_CHECK(ledc_channel_config(&ledb_chnl)); //init ADC1 ESP_ERROR_CHECK(adc_oneshot_new_unit(&adc1_config, &adc1_handle)); // config ADC1 ESP_ERROR_CHECK(adc_oneshot_config_channel(adc1_handle, ADC_CHANNEL_4, &config)); while (true) { //leemos el canal del ADC ESP_ERROR_CHECK(adc_oneshot_read(adc1_handle, ADC_CHANNEL_4, &adc_value)); printf("ADC read value: %d\n", adc_value); //ajustamos el ciclo util proporcional al valor analogico leido duty_red = 4095 - adc_value; duty_green = 4095 - adc_value / 3; duty_blue = 4095 - adc_value / 2; //actualizamos el ciclo util de los 3 canales PWM ledc_set_duty(ledr_chnl.speed_mode, ledr_chnl.channel, duty_red); ledc_update_duty(ledr_chnl.speed_mode, ledr_chnl.channel); ledc_set_duty(ledg_chnl.speed_mode, ledg_chnl.channel, duty_green); ledc_update_duty(ledg_chnl.speed_mode, ledg_chnl.channel); ledc_set_duty(ledb_chnl.speed_mode, ledb_chnl.channel, duty_blue); ledc_update_duty(ledb_chnl.speed_mode, ledb_chnl.channel); vTaskDelay(pdMS_TO_TICKS(1000)); } } //------------------------------- ```