STM32 - GPIO, Delay and Debugger === ###### tags: `firmware_hardware` `electrical_system` `NTURT` #### Author: @andre-liang ![reference link: https://www.leagueoflegends.com/en-us/champions/gangplank/](https://i.imgur.com/glhxuNG.jpg) ### Preface General-purpose input/output (GPIO) is the easiest way for a microcontroller to communicate with outside world using digital signal. ### Create a New Project Open up STM32CubeIDE, click on File -> New -> STM32 Project. On the target selection screen, type in **STM32F103C8T6** as commercial part number. ![](https://i.imgur.com/wgCfjMP.png) Then choose a project name. ![](https://i.imgur.com/QcTbtxu.png) Then you should see the project window. ![](https://i.imgur.com/ARDLBLJ.png) ### Configure Pins In order for the GPIO functionality to work properly, we must tell the microcontroller we want each pin to be a input pin or a output pin, also the input/output mode. There are two output mode and three input mode. #### Output Mode * Output Push-Pull * Output Open-Drain ![reference link: https://circuitdigest.com/forums/embedded/difference-between-open-drain-and-push-pull](https://i.imgur.com/vttX2O5.png) As the name implies, the push-pull mode is capable of driving two output levels: pull to ground and push to power supply voltage. It is implemented with a pair of switch. This configuration is most commonly used in interfaces that have unidirectional lines. While in open-drain mode, the pin can drive it only to ground. The other possible state is high impedance (Hi-Z), which means the voltage depends on external voltage. This configuration most commonly used in communication interfaces where multiple devices are connected on the same line. We usually use push-pull mode as default. Further reading: [link](https://open4tech.com/open-drain-output-vs-push-pull-output/#:~:text=Push%2Dpull%20output%20is%20best,%2C%20One%2DWire%20etc.) #### Input Mode * None * Pull-Up * Pull-Down ![reference link: https://www.mlmvlab.bime.ntu.edu.tw/%E5%BE%AE%E6%8E%A7%E5%88%B6%E5%99%A8%E5%8E%9F%E7%90%86%E8%88%87%E6%87%89%E7%94%A8](https://i.imgur.com/Fjyu9t6.png) The pull-up/pull-down resistor is used to prevent reading floating value when connected to open-circuit. In pull-up mode, the resistor keeps the input at HIGH, while in pull-down mode the resistor keeps the input at LOW. To configure each pin to specific pin mode, open the `<project-name>.ioc` in the root of the project folder, right-click on the target pin and toggle the pin mode. ![](https://i.imgur.com/kbdXPpI.png) Click on Pinout & Configuration -> System Core -> GPIO to switch between input/output mode or play around with other GPIO features. ![](https://i.imgur.com/QjVDxtq.png) *Note: All the formentioned settings could theoretically be set by manually changing the value of different registers, but we usually let the IDE to do that tedious job for us* **Save the `<project-name>.ioc` and let the code generation tool to apply the settings for you.** ### Code Structure When you are learning C/C++, the first program you write probably looks like this: ```cpp #include <iostream> int main() { std::cout<<"Hello World!"<<std::endl; } ``` The `main()` function is the entry point of the program, i.e. the program execution starts from that function call. In a STM32CubeIDE project, the entry point is the `<project-name>/Core/Src/main.c`. ```cpp! int main(void) { /* Settings for HAL library */ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); /* Only executed once */ while (1) { /* Executed indefinitely*/ } } ``` You can modify the file to make the microcontroller to do what you want. ### Writing/Reading Digital Signal #### Write Signal To alter the digital signal output of certain pin, we use the following function: ```c HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState); ``` ![](https://i.imgur.com/3sK02ws.png) A pin is defined as `P<GPIOx><GPIO_Pin>`, and PinState could be either `GPIO_PIN_SET` for output HIGH-Voltage or `GPIO_PIN_RESET` for LOW-Voltage. For example: ```cpp! HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); ``` Means to output HIGH at pin PA0. You can type `<space>+<control>` to activate auto-complete. #### Read Signal To read the digital signal of certain pin, we use: ```c var = HAL_GPIO_ReadPin(GPIOx, GPIO_Pin); ``` ### Debugger First declare a variable outside `main()` function scope, then read the pin value to that variable. ```cpp! int buttonState; int main(void){ // HAL init functions ommited; while(1){ buttonState = HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0); } } ``` For convenience, I use a button module to generate a input signal. When pressed down, the OUT pin will output a HIGH signal.Connect the OUT pin to PA0. ![](https://i.imgur.com/hb6VoG6.jpg) Since STM-32 don't have a screen, you can't output variables to standard output like we normally do in a console application. But we can use a dubugger to see the value of variables directly. To use the debugger, click `Debug <project-name> Debug` (with bug icon) to start debugging. ![](https://i.imgur.com/YY79LVx.png) Then click the search icon on the top-right corner, search for Live Expressions and enter the variable name you want to observe. ![](https://i.imgur.com/cQSvbwV.gif) You should see the value changes as you press down the button. ### Delay We can use `HAL_Delay(delayTime);` to suspends (waits) execution of the microcontroller for a given number of micro-seconds. ```cpp! while (1){ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); HAL_Delay(200); // Stop for 0.2 second HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); HAL_Delay(200); } ``` Then connect a LED to pin A0, remember to attach a resistor to limit the current flow. ![](https://i.imgur.com/W9cEN1s.jpg) Then you should see the LED blinks every 0.2 second. ![](https://i.imgur.com/NnVkv9x.gif)