All you need to know about the GPIO
GPIO or General Purpose Input Output is one of the most frequent term which you might have come across with. When you start your career in an embedded system, this is the first word you must have heard of. But do you think you have mastered all the concepts related to GPIO? In the end, its the signal on the GPIO pins which lets us achieve our end functionality and enable us to talk to the external world. In this article, I will go in-depth about various concepts related to GPIO's. At the end of this post, you will be able to answer what a gpio is, what are different types in which GPIO can be used, how gpio should be configured in order to save power, the effect of GPIO speed on EMI emission of the board, locking gpio to prevent accidental reconfiguration, etc. As a firmware engineer, you must master these concepts as your most of the work will be writing bare metal code or driver code and as an embedded application developer, you should know these concepts in order to understand the core functionalities provided by the GPIO API'S and if something is broken, you can quickly go and fix at the driver level.
In this post, I would explain the GPIO concepts specific to the STM32F446RE microcontroller board. However, the GPIO concepts would be the same for all the other boards with little difference here and there. Excited! Let's dive into the exciting world of GPIO's.
What is GPIO?
General Purpose Input Output is a digital signal pin on an integrated circuit whose behavior (input or output) is controlled by the application software. A GPIO is basically a pin that can be configured as input or output. If we configure the pin as an output, we can write 0 (LOW) or 3.3/5 V (VDD) to that pin. When configured as input, we can read the signal on that pin. GPIO is the standard interface through which a microcontroller can communicate with the external world. It can be used to read values from analog or digital sensors, drive a led, drives clock for I2C communication, etc.
Memory map of GPIO peripheral
As we already know that modern microcontroller uses the memory map technique in order to map all the peripherals in one unified memory space. Since STM32F446RE controller is a ARM Cortex M4 based microcontroller, the memory space for the peripherals are reserved by the ARM as shown in the figure:
The memory address between 0x40000000 to 0x600000000 can be used by microcontroller vendor to map their different peripherals. This region is further divided into several sub-regions, each one mapped to a specific peripheral, as shown in the figure below. From a programmer perspective, we need to find out the address where a particular peripheral is mapped in this address range. The datasheet of the microcontroller will provide the address where a particular peripheral is mapped. In our case, as we can see in the image below, GPIO peripheral is mapped in the address range 0x40020000 to 0x40021FFF. This also happened to be the bit band region (we will learn about this later).
As we can see, GPIO peripheral is further split into GPIO A, GPIO B, GPIO C, GPIO D, GPIO E, GPIO F, GPIO G, GPIO H, and each port contains a maximum of 16 pins. GPIOA peripheral is mapped from address 0x40020000 to 0x400203FF, and it manages all the pins connected to PORT-A.
ARM Cortex M-4 processor model has a 32-bit wide data bus, address bus, and 32-bit wide register set. In fact, each word is 32-bits in size. Knowing the data bus, address bus, and register width is important as it will help in understanding things at a very low (basic) level i.e. driver level. Since we know that word size is 4 bytes, hence we can say that every register of GPIO peripheral is 4 bytes long.
Till now, we have figured out the base address of the GPIO peripherals. That is the first thing you would do as a programmer whenever you are going to program any peripheral - finding the base address of the peripheral. Now, we need to know the exact register address in order to program the registers. Before getting into programming the registers, we must understand the purpose of these registers. Let's dive into the GPIO register set!
GPIO Registers
All the GPIOs present in the microcontroller are grouped as Port X where X is A, B, C, D ...
Each port in STM32F446RE consists of 16 pins. Each pin has various registers associated with it, by changing the contents of the registers, we can control the behavior of a particular pin.
In STM32F446RE, the behavior of each pin can be controlled using:
- GPIO Mode Register
- GPIO Output Type Register
- GPIO Speed Register
- GPIO Pull-up/Pull-down Register
- GPIO Input data Register
- GPIO Output data Register
- GPIO bit set/reset Register
- GPIO Configuration Lock Register
- GPIO Alternate Functionality Register
We will look into the functions served by each of these registers in this post. Understanding the functionality of these registers is very important as it will help you in controlling the pin as per your requirements. Also, if you want to write your own GPIO driver, you will mostly write core functions to program these registers and provide abstracted API to the user applications using your driver.
GPIO Registers in detail...
- GPIO Mode Register - This register is used to select the mode of the pin. There are four modes that can be programmed into this register - Input mode, General purpose output mode, Alternate function mode, and Analog mode. When the software wants to read any data from the external world like sensors, it configured the pin as an input. Each pin has a corresponding input buffer that can be read by the software when the pin is operating in INPUT Mode. General Purpose Output Mode is used when we want to write a LOW or HIGH value to the pin. For example: if we connect a LED to a pin and we want to turn on the led, the software configures the pin as output and writes HIGH (VDD) to the pin. Each pin has a corresponding output buffer which can be written by the software, in this case, we are writing 1 to the output buffer. Alternate function mode is used when we want to assign a particular pin to any other peripheral. Confused! Lets break this down into simple easy to understand manner with a simple example. For example: If you want to use I2C communication, you will need two pins namely SDA and SCL. You need to choose some specific pins provided by the microcontroller vendor which can be used as I2C SDA and SCL lines. You can get this information from the device datasheet. In the table given below, you can see that for I2C1 peripheral SCL, you can use Port B Pin 6 in Alternate Function (AF) 4.
From the above diagram, you can see that the device manufacturer has mapped certain specific pins to be used by different peripherals. Hence, in our example, Port B Pin 6 should be configured as Alternate function mode so that it can be used by I2C1 peripheral. If you want to assign the pins to ADC (Analog to Digital Converter) peripheral or DAC (Digital to Analog convertor) peripheral, you should configure the mode as Analog mode.
- GPIO Output Type Register - This register is written by the software to configure the output type of the pin. There are two output types possible: Output Push-pull and Output open-drain. In order to understand the difference between output push-pull and output open-drain, we need to understand how the gpio pin is actually implemented in terms of transistor gate. As an embedded engineer, you should be clear in terms of difference between these two configurations. Let's recharge our minds with some electronics stuff!
Lets learn how the input and output mode works at the circuit level. Below picture shows the simple implementation of GPIO pin in a microcontroller.
Each pin consists of two buffers - Input and Output buffer along with one enable line. When the enable line is 0, the output buffer gets activated and input buffer will be deactivated. An output buffer is basically two CMOS transistors connected in the below fashion.
When we write 1 from software, the first inverter will make it as 0 and PMOS circuitry (T1) will be active (NMOS (T2)will be deactivated) due to which the pin will be pulled up to VCC. When we write 0 from software, the first inverter will make it as 1 and NMOS will be activated due to which the pin will be pulled down to 0. This is the default configuration of GPIO pin and also known as Output push-pull.
If we make enable line as 1, then the pin will be configured in input mode by activating the input buffer. An input buffer consists of two CMOS transistors connected in the below fashion:
When pin reads 1 (HIGH), the inverter will invert this high logic to 0 and T1 PMOS transistor will be ON. Since it is pulled up to VCC, the software read the logic as 1. When pin reads 0 (LOW), the inverter inverts this low logic to 1 and T2 NMOS will be activated and it will be pulled down to 0, hence software reads 0. This enable line is configured by the software.
In the output open-drain mode, the top PMOS transistor is not present. The diagram looks something like this:
When T2 is on, the pin will be pulled down to low (gnd). When T2 is off, the drain (D) of the transistor will be floating or open, hence output will be floating. Hence open-drain configuration can only pull down the pin, it can't pull up the pin. Since open drain has only two logic levels, logic 0 or floating, you need to provide the pull-up capabilities to this pin either by activating internal pull-up resistor (can be done via GPIO registers) or introducing external pull up resistor. In some peripherals, the GPIO is configured as open-drain, for example, the I2C peripheral configures the SDA and SCL pins in open drain configuration.
- GPIO Speed Register - This speed has nothing to do with the switching frequency. Faster speed doesn't mean that the GPIO switch rate is higher (number of times a pin goes from ON to OFF in unit time). This speed parameter simply defines the slew rate of the GPIO, i.e. how fast it can go from 0V level to VDD level and vice-versa.
The red wave is the ideal switching curve from 0 to 1 but in reality, we only get the green wave. Increasing the speed parameter will move the green curve left towards the red one. Note: Driving the pin at higher speed also impacts the overall EMI emission of your board.
- GPIO Pull-up/Pull-down Register - Using this register, we can enable/disable the pull-up and pull-down resistor of a given pin.
- GPIO Input data Register - We can use this register to read the input status (logic) on the pin. However, it can only be read as 4 bytes, hence we need to mask off all other bits to extract the bit of our interest.
- GPIO Output data Register - This register is used to set/reset the logic on the output pin. To drive the pin high, the software writes 1 to the particular bit of this register corresponding to the particular pin.
- GPIO Bit Set/Reset Register - This register allows the application to set and reset each individual bit in the GPIO Output data register. Using this register to change the values of individual bits in output data register is a "one-shot" effect i.e. it provides a way of performing atomic bitwise handling.
- GPIO configuration lock register - This register is used to lock the configuration of the port pin until the next reset. There is a particular sequence in which this register needs to be written in order to activate the lock.
- GPIO alternate functionality register - This register is used to configure the gpio pins as alternate functions i.e. these pins can be used by the other peripherals within the microcontroller. However, a particular pin can be associated to only one peripheral at a time. Most of the I/O pins are connected to the onboard peripherals through a multiplexer which allows only one peripheral's alternate function connected to an I/O pin at a time which ensures that there can be no conflict between peripherals sharing the same I/O pin. Each I/O pin has a multiplexer with 16 alternate function inputs (AF0 to AF15) which can be selected using GPIO alternate functionality register. After reset, all I/Os are connected to the system's alternate function AF0 which is the default behavior. Peripherals alternate functions are mapped from AF1 to AF13.
So, if you want to use a particular pin as I2C1 peripheral (provided that pin supports I2C1 functionality), you would choose AF4 by writing this gpio alternate function register with the corresponding value.
How to access the GPIO register in the program?
Once you identified the base address of the given GPIO peripheral and understood the various registers functionality in order to configure the pins, we can now go ahead and understand how to access and program these registers. Please note that every port will have a set of all the 9 registers which we discussed above.
Suppose you want to program the mode register of GPIOA. One way to program these register is to find out the exact address of the mode register of GPIOA and take a pointer to that actual address, dereference this pointer, edit values and write back into this location using the same pointer. This is a lengthy approach as you need to figure out the actual address of every register in order to read/write from it.
The other simpler approach is to abstract the specific peripheral mapping. This is done by defining several handlers for each peripheral. A handler is nothing but a C structure, whose references are used to point to real peripheral addresses.
The above code snippet is a C structure and each field is 32-bit wide which corresponds to the registers which we saw above. If we point GPIO_TypeDef structure to the base address of the GPIOA peripheral, and since each of this field is 32-bit wide, each field can point to the corresponding register address in the memory.
Hence by writing something like this GPIOA ((GPIO_TypeDef *) GPIOA_BASE) where GPIOA_BASE corresponds to the actual GPIOA address which is 0x40020000, each of the fields inside this structure points to the actual corresponding register address in the memory. Hence by writing something like GPIOA->MODER, we can access the contents of the mode register of the GPIOA peripheral.
What is the state of GPIO immediately after Power-On Reset?
During and just after power-on reset, most of the I/O (Input/Output) GPIO pins are configured in input floating mode. However, the gpio pins dedicated to debug circuitry i.e. debug pins are configured in alternate functionality mode. After each device reset, these JTAG/SWD (debug circuitry) pins are assigned as dedicated pins immediately usable by the debugger host. This makes sense because you want the debug circuit to be up during and just after the reset.
In this post, I have covered the basics of GPIO peripheral and registers related to GPIO. In the upcoming post, I will cover the basics of interrupts and review the STM32 as well as NVIC interrupt controller.
Engineer Technician en Visteon Technical Center Querétaro
12 个月how to take care of the integrity of a GPIO?
2yrs in EPC, Power Systems Design, AutoCAD, ETAP, Passionate about Substation, Energy and Solar PV.
1 年excellent
Electronics Design and Product Development
4 年Excellent! Very well depicted.
15K+ Connection ?? Embedded Software Engineer C| C++| Linux Internal| Data Structure| UART| I2C| SPI| CAN| TCP/IP| Shell Scripting| QT C++| SQLLite| Socket Programming| Embedded Yocto Linux
4 年?? !!
Embedded Software Engineer | C | Developer | R&D | Learner | Git | IoT | MCU | SoC | Agile
4 年output push pul and open drain explained very clearly in Embedded programming perspective