How to program an STM32
Part 4: Reading the input from the IO port
Introduction
In this episode, we will continue our journey with the GPIO by reading the input from the IO pin. As mentioned from the previous part, there are two ways to archive that goal, by polling the pin and by an interrupt. Before digging into detail, we will recap what happened in the last episode.
In part 3, we have discussed how to use the toolchains to generating, writing, flashing and debugging the code. Microcontroller-wise, to start up a microcontroller, we have learned that, we need to set up the clock for the microcontroller, then provide the clock to the peripherals. We also have known that to let a GPIO working as an output, we have to provide it the clock, set up the direction (input/output), setup output mode, and setup output speed.
At this point, you could have guessed that if you want to make the pin as input pin i.e. the other way around of the output pin, you have to, more or less, change the direction of the pin. So, no more blah blah, we will, again, get down to business.
Reading input from the IO
Polling method
To give you an idea of setting the pin as input is just an opposite direction of setting pin as an output, we will start from the previous project in part 3, where we write our own GPIO configuration. If you accidentally skip the previous part, here is where to download it (link). The final piece, which is missing, is which pin the button is connected to. To find that, we can open the schematic of the board on the board manual. At page 23 of the board manual, it is stated that:
B1 USER: the user button is connected to the I/O PC13 (pin 2) of the STM32 microcontroller.
Therefore, we have to configure pin PC13 as an input pin. We open file main.c and add the following snippet at line 113 (right after the GPIO output configuration).
/*PC13 as input*/
/*enable clock for GPIO PortC*/
LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOC);/*set pin as input pin*/
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_13, LL_GPIO_MODE_INPUT);/*let our pin floating*/
LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_13, LL_GPIO_PULL_NO);
The code is more or less self-explained. It enabled the clock, configured pin PC13 as input pin with no pull-up or pull-down.
Next, in the while(1) loop, we replace the old code with the following snippet:
if(LL_GPIO_IsInputPinSet(GPIOC, LL_GPIO_PIN_13))
{
LL_GPIO_SetOutputPin(LD2_PIN_GPIO_Port, LD2_PIN_Pin);
}
else
{
LL_GPIO_ResetOutputPin(LD2_PIN_GPIO_Port, LD2_PIN_Pin);
}It did nothing else than checking the status of pin PC13. If the state of the pin is high or 1, we will turn on the LED and vice versa. Here is the result of our application:

This is the method “Polling” I have mentioned on the section’s title. This method involves in constantly checking for the status of something and set the action according to that. You can also imagine, this method is like driving the car with an annoying kid to the theme park and the kid is constantly asking you “are we there yet? are we there yet?” and you have to answer every time, “yes” or “no”.
This method is pretty straight forward, easy to understand and implement. But for every quick and dirty trick, there is always drawbacks. Firstly, we will waste our CPU power just to poll for the status. Secondly, while checking for the desired state, we may miss something more important, for example, some message on the communication bus. Let’s go back to out annoying kid, meanwhile answering to him “yes” or “no”, you could have missed a red light 😜. You may ask yourself, wait, is there something to let the peripherals, kind of, checking the states itself and only inform you when it reaches the some defined states?. Yes, there is something like that, and it is called interrupt.
Interrupt method
As mentioned above, the savior to our problem is called interrupt. Shortly explain, interrupt is a signal that let the CPU inside the microcontroller know that, there is something happening and the CPU should stop what it is doing (normally code section in the while(1))to handle the signal. Whenever an interrupt happens (interrupt request), as soon as the CPU finishes its current instruction, it saves the address of the next instruction, status register, etc. and jumps to a section of code that handles the interrupt request (this section is called interrupt service routine or ISR). The transition from the current code section to the interrupt code section is called interrupt latency. Of course, the shorter the latency, the better it is. After finishing the ISR, the CPU jumps back to where it left and continues from there. Rule of thumb for the ISR is, the code should be short and clean.
This is only a very brief explanation of how interrupt works. Each MCU architecture, each MCU vendor has a different way to handle the interrupt so please take a look in the datasheet to understand more. I have found this article about interrupt pretty useful, please have a look if you are…really fancy about interrupt 😆 link (in fact, most of my explanation is based on it).
Back to our STM32 controller, most of the work is taking care by CubeMX, so the thing we have to do is knowing how to configure interrupt, filling the right code at the right place, and that’s it. Let’s create a new project with CubeMX, I hope you have already known the basic step how to do that.
For this application, we need to GPIO, PA5 to drive the LED and PC13 for the button.

From the figure, you can see that PA5 is initialized as GPIO output and it has an alternative name LED.

On the other hand, PC13 is not initialized as GPIO Input but GPIO_EXTI13. In addition, it is given a name BUTTON.

In the Clock configuration tab, we will give it to the maximum value of 32MHz.

Although we have configured the PC13 as an external interrupt, we have to activate that function in the configuration tab. Therefore, in the Configuration tab, click on NVIC (Nested Vector Interrupt Controller) and tick at the EXTI line 4 to 15 interrupts. Why do I know that it is line 4 to 15 I have to activate? The answer is in the reference manual, page 289.

Everything is set and done, let’s generate our code and jump to TrueStudio to write some lines of code (actually one line of code 😄 ). Before filling the code let’s go to MX_GPIO_Init function to check what is the difference between using the polling method and interrupt method. The noticeable thing is that PC13 is configured as GPIO Input, exactly the same as what we have done in the previous section. Additionally, externally interrupt line 13 is activated and configured as rising edge triggering (code below).
/**/
LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTC,
LL_SYSCFG_EXTI_LINE13);EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_13;
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);/* EXTI interrupt init*/
NVIC_SetPriority(EXTI4_15_IRQn, 0);
NVIC_EnableIRQ(EXTI4_15_IRQn);
So, we have only to look for the ISR, fill in the code and the job is done. the ISR is located in the file stm32l0xx_it.c. Open that file, look for the function void EXTI4_15_IRQHandler(void) and fill in the code as below to toggle the LED:
if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13) != RESET)
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13);
/* USER CODE BEGIN LL_EXTI_LINE_13 */
LL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
/* USER CODE END LL_EXTI_LINE_13 */
}So…that’s it to toggle LED whenever we press the button, no more polling for the status of the pin connected to the button. Actually, our while(1) loop is empty. Let’s compile and run our code, here is the result:

Conclusion
we are pretty much finished with this episode. In this part, we know how to configure a GPIO to work as an input pin. We have learned 2 ways to use the input pin, one is using the polling method and the other is using the interrupt. The projects in this episode can be found in the git repository.
For the next episode, we will do something different, UART communication. So stay tuned, happy coding and peace 😉
Other parts
Part 2: Toolchains and software installation
Part 3: How to use the toolchains. Writing our first application — Blinky LED
