With this tutorial we’re going to start using the I/O pins of the esp32 chip. The chip features 40 General Purpose I/O pads (GPIO), that are 40 “contacts” you can use in different ways, both for input and output functions.
The setup of those pads is quite complex: the esp32 chip allows indeed to “map” the internal peripherals (UART, SPI…) to different pads, via matrices that can be configured in your firmware. In the technical reference manual you can find a drawing that explains how it works:
In this first post about I/O I’m not going to explain in details the different registers involved… it’s however important that you understand by now that an initial configuration is always required to use a pin, even for basic I/O functionalities. The driver you have to include in your code to use all the functions described below is:
As previously said, before using a pin you need to make a proper configuration of the internal registers. After the chip reset, most pins are automatically configured as basic I/O pins; this is not true for all the pins. You can configure a pin as a general purpose I/O pin with the function:
void gpio_pad_select_gpio(uint8_t gpio_num);
The GPIO pins can work in 3 different “directions”:
- open drain
The corresponding constants are defined in the gpio.h file:
There are some exceptions, in particular pins > 33 can only work as input pins.
You can configure the direction of a pin with the function:
esp_err_t gpio_set_direction(gpio_num_t gpio_num, gpio_mode_t mode);
gpio_num is the number of the pin to be configured, while mode is one of the three constants shown above. For example if you need to use pin 13 as output, the code is:
You can read the pin status, if configured as input, with the function:
int gpio_get_level(gpio_num_t gpio_num);
while if the pin is configured as output or open drain, you can change its status with:
esp_err_t gpio_set_level(gpio_num_t gpio_num, uint32_t level);
Pull-up and pull-down
Every pin also includes pull-up and pull-down resistors, you can enable them with the function:
esp_err_t gpio_set_pull_mode(gpio_num_t gpio_num, gpio_pull_mode_t pull);
the possible constants are:
Alternatively, you can use the dedicated functions to enable/disable a resistor on a pin:
esp_err_t gpio_pullup_en(gpio_num_t gpio_num); esp_err_t gpio_pullup_dis(gpio_num_t gpio_num); esp_err_t gpio_pulldown_en(gpio_num_t gpio_num); esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num);
So far, you learned how to configure a single pin. You can also configure some pins at the same time, applying to them the same configuration template.
First you have to declare a gpio_config_t struct:
The parameters that can be configured are:
The pins you want to configure must be specified in the pin_bit_mask variable as a “bit mask”. You can create it concatenating the different GPIO_SEL_x constants with the OR (|) operator. Let’s see a couple of examples (I’ll blog about interrupts in a future post, for now let’s keep them disabled):
- pin 2, 3 and 7 configured as output, pull-up and down resistors disabled:
pin_config.pin_bit_mask = GPIO_SEL_2 | GPIO_SEL_3 | GPIO_SEL_7; pin_config.mode = GPIO_MODE_OUTPUT; pin_config.pull_up_en = GPIO_PULLUP_DISABLE; pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE; pin_config.intr_type = GPIO_PIN_INTR_DISABLE;
- pin 6 and 8 configured as input, with pull-up resistor:
pin_config.pin_bit_mask = GPIO_SEL_6 | GPIO_SEL_8; pin_config.mode = GPIO_MODE_INPUT; pin_config.pull_up_en = GPIO_PULLUP_ENABLE; pin_config.pull_down_en = GPIO_PULLDOWN_DISABLE; pin_config.intr_type = GPIO_PIN_INTR_DISABLE;
The configuration template is finally applied with the function gpio_config(&pin_config).
Because this tutorial is about I/O basic concepts, I didn’t prepare dedicated examples… with the framework are indeed shipped some programs that show what I explained above: