GPIO - theory version
Contains lots of ELEC 3300 materials that you might not understand yet
What is GPIO?
A general-purpose input/output (GPIO) is an digital signal pin on an integrated circuit or electronic circuit board whose behavior — including whether it acts as an input or output — is controllable by the user at run time.
In short, GPIO is for outputting and reading HIGH [1] and LOW [0] values. (Sometimes we will use GPIO for analogue values but don't worry about that for now)
GPIO Configuration
Every pin on the MCU can be used as GPIO, they are divided into a blocks of 16 pins due to internal structure
Each block is named by letters:
GPIOA
,GPIOB
,GPIOC
...Each pin within the block is numbered from 0:
GPIO_PIN_0
,GPIO_PIN_1
,GPIO_PIN_2
...
So every pin is referred to by a pair of values: the port and the pin:
GPIOE, GPIO_PIN_3
or when humans are talking we sayPE3
Macro
s for GPIO
Macro
s for GPIOWe will often use #define
to give more meaningful names to the ports and pins, for ease of use:
/* main.h */
#define UP_BTN_GPIO_Port GPIOE
#define UP_BTN_Pin GPIO_PIN_3
#define LED1_GPIO_Port GPIOE
#define LED1_Pin GPIO_PIN_4
#define LED2_GPIO_Port GPIOE
#define LED2_PIN GPIO_PIN_5
#define LED3_GPIO_Port GPIOE
#define LED3_PIN GPIO_PIN_6
These defines are generated when you generate the HAL library from STM32CubeMX. You may change it at anytime to suit your need.
Initialization of GPIO (HAL Library Style)
Any hardware that you want to use must be first initialized. This is basically setting it up so it can work they way you want it to.
HAL library help you initialize the GPIO when the code is generated from STM32CudeMX. Inside the function
MX_GPIO_Init
/* gpio.c */
// ...
/*Configure GPIO pin Output Level */
/*
* This initialized 4 LED pins to LOW 0
*
* Notice that all pins need to be in the SAME port in order
* to use the | bitwise-or operator to chain up the pins.
*/
HAL_GPIO_WritePin(GPIOA, LED4_Pin|LED3_Pin|LED2_Pin|LED1_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PAPin PAPin PAPin PAPin */
GPIO_InitStruct.Pin = LED4_Pin|LED3_Pin|LED2_Pin|LED1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // Output Push-pull mode
GPIO_InitStruct.Pull = GPIO_NOPULL; // No pull-up or pull-down
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // Let's not worry that for now
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : PAPin PAPin */
GPIO_InitStruct.Pin = UP_BTN_Pin|DOWN_BTN_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // Input mode
GPIO_InitStruct.Pull = GPIO_PULLUP; // Pull-up
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
* Various GPIO Mode
and GPIO Pull
options
GPIO Mode
and GPIO Pull
options/* stm32f4xx_hal_gpio.h */
// Modes
#define GPIO_MODE_INPUT MODE_INPUT /*!< Input Floating Mode */
#define GPIO_MODE_OUTPUT_PP (MODE_OUTPUT | OUTPUT_PP) /*!< Output Push Pull Mode */
#define GPIO_MODE_OUTPUT_OD (MODE_OUTPUT | OUTPUT_OD) /*!< Output Open Drain Mode */
#define GPIO_MODE_AF_PP (MODE_AF | OUTPUT_PP) /*!< Alternate Function Push Pull Mode */
#define GPIO_MODE_AF_OD (MODE_AF | OUTPUT_OD) /*!< Alternate Function Open Drain Mode */
#define GPIO_MODE_ANALOG MODE_ANALOG
// Pull
#define GPIO_NOPULL 0x00000000U /*!< No Pull-up or Pull-down activation */
#define GPIO_PULLUP 0x00000001U /*!< Pull-up activation */
#define GPIO_PULLDOWN 0x00000002U /*!< Pull-down activation */
It seems there are various modes and pulls in the initialization, right? In Gerneal, you actually only need to know 4 of them -
GPIO_MODE_INPUT
,GPIO_MODE_OUTPUT_PP
,GPIO_PULLUP
,GPIO_PULLDOWN
.
Input : GPIO_MODE_INPUT
GPIO_MODE_INPUT
GPIO_PULLUP
andGPIO_PULLDOWN
- The GPIO Pin can be used to read the logical value of the pinThe point is to deal with the secret 3rd state of binary digital signals: floating pins.
What happens when a pin is connected to nothing at all?
Noise will cause you to read a mostly random value
Therefore, The pull-up or pull-down gives a "weak" connection from the pin to either a high or low voltage. It gives a defined value to a floating pin while being weak enough to be easily overridden by any external signal.
Pull-up & Pull-down
]
One of the two set-ups here will read a high voltage when the button is on and low voltage when the button is off, while the other one is the opposite, can you tell which is which and why?
keywords : short circuit, potential divider
Output : GPIO_MODE_OUPUT_OD
and GPIO_MODE_OUTPUT_PP
GPIO_MODE_OUPUT_OD
and GPIO_MODE_OUTPUT_PP
GPIO_MODE_OUPUT_OD
andGPIO_MODE_OUTPUT_PP
- The GPIO Pin can be used to output a digital signal using a pair of switches (see figure below)Push-pull(PP) uses the 2 switches to connect the pin to either high voltage or low voltage, it pushes or pulls the voltage to the level assigned
Open-drain(OD) is similar but does not use the upper switch, thus it outputs a low voltage or completely disconnects the pin
GPIO_MODE_AF_PP & OD
are the same but are for when the pin output is to be controlled by another bit of hardware, don't worry about it for now
Inside the MCU lives a pair of switches, ie.transistors
:
Reading GPIO Input in the Program
The HAL_GPIO_ReadPin
function reads the GPIO input.
/* stm32f4xx_hal_gpio.h */
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
// usage
uint8_t state = HAL_GPIO_ReadPin(BTN1_GPIO_Port, BTN1_Pin); // returns 0 or 1
The
HAL_GPIO_ReadPin
functions returns 1 or 0 of that respective pin of the parameter.
Readability:
Writing
HAL_GPIO_ReadPin(XXX_GPIO_Port, XXX_Pin)
every time may seem tidious. However with the help of magical c prepocessor, we can make it easier
/* main.h */
#define gpio_read(gpio) HAL_GPIO_ReadPin(gpio##_GPIO_Port, gpio##_Pin)
// usage
uint8_t state = gpio_read(BTN1);
Writing GPIO Output in the Program
The following macros can be found in main.h
The
gpio_set(gpio)
macro sets the GPIO pin to be 1.The
gpio_reset(gpio)
macro resets the GPIO pin to 0.The
gpio_toggle(gpio)
macro toggles the GPIO pin. (i.e. changes the GPIO pin state to 1 if it was originally 0 and vice versa)
Examples:
/* main.h */
#define gpio_set(gpio) HAL_GPIO_WritePin(gpio##_GPIO_Port, gpio##_Pin, GPIO_PIN_SET)
#define gpio_reset(gpio) HAL_GPIO_WritePin(gpio##_GPIO_Port, gpio##_Pin, GPIO_PIN_RESET)
#define gpio_toggle(gpio) HAL_GPIO_TogglePin(gpio##_GPIO_Port, gpio##_Pin)
// usage
// Turns off the LED
gpio_set(LED1);
// Turns on the LED
gpio_reset(LED1);
// Toggles the LED
gpio_toggle(LED1);
Full Example (Flickering the LED1 for every 500 ticks)
/* main.c */
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
volatile uint32_t last_ticks = HAL_GetTick();
while (1) {
if (HAL_GetTick() - last_ticks > 500) {
last_ticks = HAL_GetTick();
gpio_toggle(LED1); // flickering the LED1
}
}
return 0;
}
Even More Readability:
Writing gpio_set(LED1)
is still not very readable because you don't know if it is turning on/off the LED1.
Writing gpio_read(BTN1)
is also not very readable because you don't know if gpio_read(BTN1) == 1
is pressed or gpio_read(BTN1) == 0
is pressed.
So, we defined some more macro functions in main.h for more readability:
/* main.h */
#define led_on(led) gpio_reset(led) // notice that reset the pin turns the led on
#define led_off(led) gpio_set(led)
#define led_toggle(led) gpio_toggle(led)
#define btn_read(btn) gpio_read(btn)
If one day your hardware groupmate accidentally swapped the wires of all LEDs or buttons, you can simply change the defines above. You don't need to read through your hundreds/thousands of lines of code and change every gpio_set() gpio_reset() you wrote.
Simplest uses for GPIO


Thinking Time:
What should the GPIO Mode and Pull be for each of the above and why?
There are few step before using GPIO pins in your program:
Find which GPIO you want to use in
main.h
(if you can't find one, define it yourself)Initialize the GPIO Pin once in
gpio.h
. Remember which mode and pull you need for the pinUse the GPIO Functions to read or write the digital signals
Button Example
// Initialising all gpio
MX_GPIO_Init();
// Remember the onboard button must be used with a pull up resistor
uint8_t pressed = btn_read(BTN1);
// Remember the onboard button will give a low signal when clicked, and a high signal otherwise
Last updated