Let’s talk about GPIO (Part 4)

The world belongs to those who think and act with it, who keep a finger on its pulse.

William Ralph Inge

Now that we can use basic digital GPIO functionality, let us look at using one of the SoC functions which can be connected to the GPIO via the IOMUX. In this case, we’re going to use the LED controller, LEDC .

LEDC is a Pulse-width modulation (PWM) controller which can set LED brightness to various levels using a train of LOW to HIGH pulses with a varying duty-cycle which is the ratio of the LOW to the HIGH part of each pulse. In addition to just setting the LED brightness, the controller can also fade it in and out by adjusting the duty cycle!

Setup for this example is similar to the previous one, and we are going to the use Sparkfun ESP32 Thing+, so set the PWM_GPIO config option to `13` and set the fade period to 300000 :

jcrisp@embedded-dev:~/workspace/pwm$ idf.py --version
ESP-IDF v5.0
jcrisp@embedded-dev:~/workspace/pwm$ idf.py set-target ESP32
Adding "set-target"'s dependency "fullclean" to list of commands with default set of options.
Executing action: fullclean
Executing action: set-target
Set Target to: esp32, new sdkconfig created. Existing sdkconfig renamed to sdkconfig.old.
 ... compile output ...
-- Generating done
-- Build files have been written to: /home/jcrisp/workspace/pwm/build
jcrisp@embedded-dev:~/workspace/pwm$ idf.py menuconfig
Executing action: menuconfig
Running make in directory /home/jcrisp/workspace/pwm/build
Executing "make -j 18 menuconfig"...
TERM environment variable is set to "xterm-256color"
Loaded configuration '/home/jcrisp/workspace/pwm/sdkconfig'
No changes to save (for '/home/jcrisp/workspace/pwm/sdkconfig')
Built target menuconfig

Our files are kconfig.projbuild

menu "Example Configuration"

    orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"

    config PWM_GPIO
        int "Blink GPIO number"
        range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
        default 8 if IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32H2 || IDF_TARGET_ESP32C2
        default 18 if IDF_TARGET_ESP32S2
        default 48 if IDF_TARGET_ESP32S3
        default 5
        help
            GPIO number (IOxx) to blink on and off or the RMT signal for the addressable LED.
            Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.

    config PWM_FADE_PERIOD
        int "Pule width modulation fade period in ms"
        range 10 3600000
        default 1000
        help
            Define the fade period in milliseconds.

endmenu

and our pwm_main.c (don’t forget to set the name in CMakeLists.txt) which in this case just fades the LED in from off to 100% in our chosen 30000 miliseconds. We are choosing not to use the “fade end” interrupt, but it is possible to ask the LED controller to trigger an interrupt when the fade is complete.

/* LED Fade Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "driver/ledc.h"
#include "sdkconfig.h"

static const char *TAG = "example";

ledc_timer_config_t timer_cfg = {
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_13_BIT,
    .timer_num = 0,
    .freq_hz = 5000,
    .clk_cfg = LEDC_AUTO_CLK
};

ledc_channel_config_t ledc_channel = {
    .channel    = 0,
    .duty       = 0, // This field determines the ratio of HIGH to LOW
    .gpio_num   = CONFIG_PWM_GPIO,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .hpoint     = 0,
    .timer_sel  = 0,
    .flags.output_invert = 0,
    .intr_type = LEDC_INTR_DISABLE // Can be LEDC_INTR_FADE_END
};

static void configure_led(void)
{
    ESP_LOGI(TAG, "Example configured to PWM Fade GPIO LED %d!", CONFIG_PWM_GPIO);
    gpio_reset_pin(CONFIG_PWM_GPIO);
    /* Set the GPIO as a push/pull output */
    gpio_set_direction(CONFIG_PWM_GPIO, GPIO_MODE_OUTPUT);
    ledc_timer_config(&timer_cfg);
    ledc_channel_config(&ledc_channel);
    ledc_fade_func_install(0);
}

void app_main(void)
{

    /* Configure the peripheral according to the LED type */
    configure_led();

    ledc_set_fade_with_time(ledc_channel.speed_mode, ledc_channel.channel, 8192, CONFIG_PWM_FADE_PERIOD);
    ledc_fade_start(ledc_channel.speed_mode,
                    ledc_channel.channel, LEDC_FADE_NO_WAIT);

    while (1) {
        vTaskDelay(CONFIG_PWM_FADE_PERIOD / portTICK_PERIOD_MS);
    }
}