This commit is contained in:
parent
e8528d3739
commit
b8f9d2ebcd
@ -8,15 +8,13 @@ series: ["STM32 For Beginners"]
|
||||
tags: ["Embedded", "STM32"]
|
||||
---
|
||||
|
||||
# Printouts and "Hello World"
|
||||
|
||||
Our first goal will be writing doing a simple printout. A classical "hello world". Well, given our restrictions, this is going to take a bit of doing.
|
||||
|
||||
Typically, in C, we'd accomplish this by using `printf()`. However, with a microcontroller, printf goes nowhere by default. So our first goal is to redirect that printf command into something we can pick up with our PCs. We're going to use UART for this.
|
||||
|
||||
We've already enabled UART in our cube with the VCP.
|
||||
|
||||
## Writing into UART
|
||||
# Writing into UART
|
||||
|
||||
For writing into UART, we'd use the `HAL_UART_Transmit` function. The function requires the following arguments:
|
||||
* `huart`, which is a pointer to the UART hardware instance we're going to use,
|
||||
@ -67,14 +65,14 @@ the green play button:
|
||||
|
||||
{{< video src="/media/stm32begin-001-006.mp4" type="video/mp4" preload="auto" caption="Don't mind the lack of an STM32 being connected...">}}
|
||||
|
||||
## Viewing the Output
|
||||
# Viewing the Output
|
||||
|
||||
Now with that, we've got our microcontroller sending data to our PC via UART. The next question is: how do we
|
||||
pick that data up and read it? For that, we need a piece of software capable of reading a virtual COM port (VCP).
|
||||
On Windows, Linux, and MacOS, probably one of the most common software for this purpose is [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html).
|
||||
If you're more commandline inclined and on Linux or MacOS, you may also be interested in [minicom](https://wiki.emacinc.com/wiki/Getting_Started_With_Minicom).
|
||||
|
||||
### PuTTY
|
||||
## PuTTY
|
||||
|
||||
If you're on Windows, follow the link in the previous section to download PuTTY, and then install it. For Linux
|
||||
users, I would suggest flatpak: `flatpak install uk.org.greenend.chiark.sgtatham.putty`. Once installed, run it.
|
||||
@ -89,7 +87,7 @@ On Linux, you'll be looking for a `ttyACM0` style entry in your `/dev/` folder.
|
||||
|
||||
{{< figure src="/media/stm32begin-001-008.png" >}}
|
||||
|
||||
### minicom
|
||||
## minicom
|
||||
|
||||
For minicom, consult your local distro package repository for the relevant package. `dnf search minicom` or `apt
|
||||
search minicom` should provide the desired results.
|
||||
@ -137,7 +135,7 @@ force a new line on most systems. With that, the updated code looks like the fol
|
||||
Notice how we only needed to modify the string here, and the length in the `length` variable was adjusted
|
||||
accordingly.
|
||||
|
||||
## Writing with printf
|
||||
# Writing with printf
|
||||
|
||||
So, we have a way to transmit strings using UART. What we want to do is pipe the output of `printf` into UART.
|
||||
For this, we need to provide our own definition for an existing C library implementation function. For the C
|
||||
|
||||
201
content/posts/004-stm32begin-003-leds.md
Normal file
201
content/posts/004-stm32begin-003-leds.md
Normal file
@ -0,0 +1,201 @@
|
||||
---
|
||||
title: "STM32 For Beginners [3]: LEDs and inputs"
|
||||
date: 2024-10-02T00:36:00+03:00
|
||||
draft: false
|
||||
summary: "..."
|
||||
author: "Rusted Skull"
|
||||
series: ["STM32 For Beginners"]
|
||||
tags: ["Embedded", "STM32"]
|
||||
---
|
||||
|
||||
In the previous part we got our STM to print stuff out via UART and figured out how view this printout
|
||||
on our PC. Using this, we did the typical "Hello world" example of programming. Well, while a printout
|
||||
is indeed the common first step for setting up software development enviornments, there's another,
|
||||
more simple "Hello world" that's common in the embedded world: flashing a LED. We'll be doing that now.
|
||||
|
||||
# GPIO - General Purpose Input-Output
|
||||
|
||||
On an MCU, one of the easiest ways to interact with the world (and other devices) is via General
|
||||
Purpose Input-Output pins. A GPIO pin can be set as an input or as an output. For the input, the
|
||||
line will translate the voltage that's _being applied to it_ into a true or false (1 or 0) value that
|
||||
we can read and respond to with our code. For the output, we can write a true or false value from
|
||||
our code onto the line, and in response to that, the MCU will either put a voltage onto that line or
|
||||
leave it connected to ground.
|
||||
|
||||
For the STM32 series of MCUs, the digital logic voltage is 3.3 volts. This, _in principle_, that a
|
||||
true value will correspond to a voltage of 3.3 volts, and a false value will correspond to a value of
|
||||
0 volts. In reality, there's a bit more to consider. First, some pins on the STM32 controllers
|
||||
capable of handling up to 5 volts of input. These pins are marked in the respective controller's
|
||||
datasheet as "FT" - "Five volt Tolerant". The other exception is that there's some tolerances built
|
||||
into differentiating between true and false: we could say that a true value is a voltage between 2.3
|
||||
and 3.3 volts. Again, exact specifications can be found in the datasheet.
|
||||
|
||||
|
||||
# Writing into GPIO Output
|
||||
|
||||
We previously configured PB3 as our digital output. Now's the time to use it. Specifically, we named
|
||||
the pin as "OUT_LED". With this, STM32CubeMX has generated the following defines:
|
||||
|
||||
* `OUT_LED_GPIO_Port` - this is an alias for PORTB of the MCU,
|
||||
* `OUT_LED_Pin` - this an alias for pin number.
|
||||
|
||||
On most MCUs, pins are divided into different ports. Each port has a specific amount of pins, and to
|
||||
address a specific pin, you must know both the pin's port and the pin's number in that port to control
|
||||
it. For PB3, the port is PORTB, and the pin itself is numbered as 3 in that port. These defines give
|
||||
us a more meaningful alias via which to access these specifiers.
|
||||
|
||||
For writing into the pin, ST's HAL provides us with the following functions:
|
||||
|
||||
* `HAL_GPIO_WritePin(GPIOx, GPIO_Pin, PinState)` - for specifically turning the line high or low,
|
||||
* `HAL_GPIO_TogglePin(GPIOx, GPIO_Pin)` - for toggling the pin from its previous state.
|
||||
|
||||
In both cases, `GPIOx` is the argument for the port, and `GPIO_Pin` is the argument for the pin
|
||||
number. `PinState` is either `GPIO_PIN_SET` for writing a 1, or `GPIO_PIN_RESET` for writing a 0 onto
|
||||
the line.
|
||||
|
||||
So, if we wanted to flash the LED, we would write this into our `main()`:
|
||||
|
||||
```cpp
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
while (1)
|
||||
{
|
||||
HAL_GPIO_WritePin(OUT_LED_GPIO_Port, OUT_LED_Pin, GPIO_PIN_SET); // LED will turn on.
|
||||
HAL_Delay(1000); // Keep the LED on for a second.
|
||||
|
||||
HAL_GPIO_WritePin(OUT_LED_GPIO_Port, OUT_LED_Pin, GPIO_PIN_RESET); // LED will turn off.
|
||||
HAL_Delay(1000); // Keep the LED off for a second.
|
||||
/* USER CODE END WHILE */
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
```
|
||||
|
||||
Build and run the program, and you will see LD3 on the Nucleo board (assuming STM32F303k8 Nucleo)
|
||||
start flashing.
|
||||
|
||||
Obviously, with the knowledge that we also have `HAL_GPIO_TogglePin`, this code could be written a bit
|
||||
shorter as well:
|
||||
|
||||
```cpp
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
while (1)
|
||||
{
|
||||
HAL_GPIO_TogglePin(OUT_LED_GPIO_Port, OUT_LED_Pin); // LED will be toggled from its previous state.
|
||||
HAL_Delay(1000); // Maintain state for a second.
|
||||
/* USER CODE END WHILE */
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
```
|
||||
|
||||
Congratulations, you've flashed a LED! You've now done your second "Hello world" exercise!
|
||||
Now let's make the LED actually respond to some external input.
|
||||
|
||||
# Reading GPIO Inputs (and buttons!)
|
||||
|
||||
Let's say that now we want to drive our LED based on the state of another digital input. We designated
|
||||
PB0 as a GPIO Input, `IN_BTN`, so let's use it!
|
||||
|
||||
To read a GPIO, we have a single function: `HAL_GPIO_ReadPin(GPIOx, GPIO_Pin)`. It will return a
|
||||
`GPIO_PinState` value, which is the same type we used as an input before. So it's either a value of
|
||||
`GPIO_PIN_SET` for when the line is high, or `GPIO_PIN_RESET` for when the piin is set low. We'll
|
||||
review this a bit more indepth shortly.
|
||||
|
||||
First, we must also figure out how to manipulate the pin. If you don't have a push button or something
|
||||
else on hand, then all you need is a jumper wire: connect one end of the jumper wire to PB0 (marked
|
||||
D3 on the board itself) and the other end either to a GND or 3V3 pin.
|
||||
|
||||
One thought exercise to consider here is: "What happens when we leave the wire disconnected?" The
|
||||
answer is: your pin will float. It may take a high value or a low value, due to inductance. This is
|
||||
why we configured the pin with a pull-down in the beginning: the pull-down resistor will assert a low
|
||||
state on the pin if it's not connected to a stronger source. This will negate the floating.
|
||||
|
||||
So, we can bring the line high or low by connecting the wire to 3V3 or GND. Now, onto reading it. To
|
||||
read it, we must call the function and save the result into a variable, for example. And then, make
|
||||
our logic respond to this value: for example, when the pin is at a state of `GPIO_PIN_SET`, we turn
|
||||
the LED on, else, we turn it off.
|
||||
|
||||
An example code for this is:
|
||||
|
||||
```cpp
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
while (1)
|
||||
{
|
||||
GPIO_PinState button_state = HAL_GPIO_ReadPin(IN_BTN_GPIO_Port, IN_BTN_Pin); // declare a new variable and read the pin state into it.
|
||||
if (button_state == GPIO_PIN_SET) // Check if the button is being pressed.
|
||||
{
|
||||
// Write the LED on.
|
||||
HAL_GPIO_WritePin(OUT_LED_GPIO_Port, OUT_LED_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write the LED off.
|
||||
HAL_GPIO_WritePin(OUT_LED_GPIO_Port, OUT_LED_Pin, GPIO_PIN_RESET);
|
||||
}
|
||||
/* USER CODE END WHILE */
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
```
|
||||
|
||||
You will notice that we defined a variable here as well. We made it of type `GPIO_PinState`, as that
|
||||
is what `HAL_GPIO_ReadPin` returns. And then we can respond to this using the if statements. Obviously
|
||||
you can also write some printf's in or around those statements to see some more of the action.
|
||||
|
||||
For example, if we wanted to also print out the state of the pin:
|
||||
|
||||
```cpp
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
while (1)
|
||||
{
|
||||
GPIO_PinState button_state = HAL_GPIO_ReadPin(IN_BTN_GPIO_Port, IN_BTN_Pin); // declare a new variable and read the pin state into it.
|
||||
|
||||
printf("The button state is: %d\n\r", button_state);
|
||||
|
||||
if (button_state == GPIO_PIN_SET) // Check if the button is being pressed.
|
||||
{
|
||||
// Write the LED on.
|
||||
HAL_GPIO_WritePin(OUT_LED_GPIO_Port, OUT_LED_Pin, GPIO_PIN_SET);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write the LED off.
|
||||
HAL_GPIO_WritePin(OUT_LED_GPIO_Port, OUT_LED_Pin, GPIO_PIN_RESET);
|
||||
}
|
||||
|
||||
HAL_Delay(250); // Delay to stop printout flooding.
|
||||
/* USER CODE END WHILE */
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
```
|
||||
|
||||
This will work by printing the button state out as a 1 or 0. Note the necessity for a delay, this will
|
||||
make the logic slower as well, obviously.
|
||||
|
||||
But, that's it for this lesson. Next time we'll review motors and PWM.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user