Getting Started with STM32: Difference between revisions

From Embedded Workshop
Jump to navigation Jump to search
No edit summary
 
(51 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Although this getting started class will work well for many of the STM32 processors and boards, we will focus on the NUCLEO-F103RB board with its STM32-F103RB processor.<br>
Although this getting started class will work well for many of the STM32 processors and boards, we will focus on the NUCLEO-F103RB board with its STM32-F103RB processor.<br>
[[Getting Started with STM32 - class description|Class Description]]
<br>
<br>
==64-Pin NUCLEO Board==
[[File:NUCLEO_large.jpg|right|thumb]]
[[File:NUCLEO_large.jpg|right|thumb]]
<strong>64-Pin NUCLEO Board</strong><br>
* Green LED, LD2, connected to PA5, illuminates when driven high
* Green LED, LD2, connected to PA5, illuminates when driven high
* Blue PushButton, B1, connects to PC13, 4.7K external pullup, signal connects to ground when pressed
* Blue PushButton, B1, connects to PC13, grounding the signal when pressed
* 32,768Hz crystal oscillator, LSE, used for Real Time Clock (RTC)
* 32,768Hz crystal oscillator, LSE, used for Real Time Clock (RTC)
* 8MHz HSE, is provided by the 8MHz crystal oscillator from the attached ST-Link, into PD0
* 8MHz HSE, is provided by the 8MHz crystal oscillator from the attached ST-Link, into PD0
* UART2 connects the target processor to the ST-Link, providing a USB COM port connection on the host computer. Uses PA2 for TX, and PA3 for RX.
* UART2 connects the target processor to the ST-Link, providing a USB COM port connection on the host computer. Uses PA2 for TX, and PA3 for RX.
* ST-Link connects to TCK (PA14), TMS (PA13)
* ST-Link connects to TCK (PA14), TMS (PA13)
'''Board:''' https://www.st.com/en/evaluation-tools/nucleo-f103rb.html
'''Board:''' https://www.st.com/en/evaluation-tools/nucleo-f103rb.html<br>
'''Manual:''' https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf
'''Manual:''' https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf<br>
'''Schematic:''' https://www.st.com/resource/en/schematic_pack/nucleo_64pins_sch.zip
'''Schematic:''' https://www.st.com/resource/en/schematic_pack/nucleo_64pins_sch.zip<br>


<strong>Processor: STM32F103RBT6</strong>
==Processor: STM32F103RBT6==
  * 128KB Flash
  * 128KB Flash, organized as 128 - 1K sectors
  * 20KB SRAM
  * 20KB SRAM
  * 72MHz clock
  * 72MHz clock
Line 28: Line 29:
     "High-density device" has more peripherals.
     "High-density device" has more peripherals.


<strong>Software - Development Tools - IDE</strong> (I recommend creating an STMicro account)
==Software - Development Tools - IDE==
I recommend creating an STMicro account to make multiple downloads easier.
  Download the following tool(s) from STMicro:
  Download the following tool(s) from STMicro:
  https://www.st.com/en/development-tools/stm32cubeide.html
  '''https://www.st.com/en/development-tools/stm32cubeide.html'''  (Required)
'''Optional tools''' (not necessary for the class)
https://www.st.com/en/development-tools/stm32-software-development-tools.html
STM32CubeProg - Programming tool to write/read firmware to/from the target processor
STSW-Link007 - ST-Link Firmware Upgrade tool
[https://www.st.com/en/development-tools/stsw-link009.html STSW-Link009 - Windows Device Drivers for ST-Link adapters]


<strong>Installing STM32CubeIDE_1.9.0</strong>
==Installing STM32CubeIDE_1.10.1==
  * Allow the software to install at C:/ST/STM32CubeIDE_1.9.0
  * Allow the software to install at C:/ST/STM32CubeIDE_1.10.1
  * When asked where to store project files, I recommend creating a path off of C:/Users/YourName/Documents
  * When asked where to store project files, I recommend creating a path off of C:/Users/YourName/Documents
     Something like: '''C:/Users/YourName/Documents/STM32CubeIDE_1.9.0'''
     Something like: '''C:/Users/YourName/Documents/STM32_Projects'''
     This allows you to find your projects and makes them easy to backup.
     This makes your projects easy to find and backup.


<strong>Create a New STM32CubeIDE_1.9.0 Project</strong>
==Create a New STM32CubeIDE_1.10.1 Project - Blinky==
  1 Click "Start new STM32 project", then select the "Board Selector" tab
  1 Click '''"Start new STM32 project"''', then select the '''"Board Selector"''' tab
  2 In the Commercial Part Number box, type "NUCLEO-F103RB", select the board, and then click "Next>" at the bottom
  2 In the Commercial Part Number box, type '''"NUCLEO-F103RB"''', select the board, and then click '''"Next>"''' at the bottom
  3 Since I have several STM32 boards, I like to have the board name in the name of the project
  3 Since I have several STM32 boards, I like to have the board name in the name of the project
   I used: "NUCLEO-F103RB_Blinky" as the project name
   I used: "NUCLEO-F103RB_Blinky" as the project name
Line 48: Line 55:
     Do you want to open this perspective now?  (Select '''Yes''' to see what's configured)
     Do you want to open this perspective now?  (Select '''Yes''' to see what's configured)
   
   
  7 Under "Project Manager Tab", Linker Settings, I set both Minimum Heap Size and Minimum Stack Size to 0x400 (1K bytes each)
  7 Under '''"Project Manager Tab"''', Linker Settings, I set both Minimum Heap Size and Minimum Stack Size to 0x800 (2K bytes each)
  8 On the left, Project Explorer window, Open Core->Src->main.c
  8 On the left, Project Explorer window, Open Core->Src->main.c
  Note: If you examine the source code, you will see that the following functions get called:
  Note: If you examine the source code, you will see that the following functions get called:
Line 57: Line 64:
   but it will be rather boring...
   but it will be rather boring...
   
   
  9 Find the infinite loop inside the main() function, and add the following two {{Highlight|highlighted lines of code:}}
  9 Find the infinite loop inside the main() function, and add the following two bold lines of code:
    
    
   /* Infinite loop */
   /* Infinite loop */
Line 63: Line 70:
   while (1)
   while (1)
   {
   {
{{Highlight|  HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin);
    '''HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin);'''
     HAL_Delay(500);}}
     '''HAL_Delay(500);'''
     /* USER CODE END WHILE */
     /* USER CODE END WHILE */
   
   
Line 79: Line 86:
   The program will be downloaded to the device and begin execution.
   The program will be downloaded to the device and begin execution.
   You should see LED2 blinking at this point.
   You should see LED2 blinking at this point.
<br>
<strong>Fix Eclipse's Small Icon issue</strong>
1 Find the installation folder for your STM32CubeIDE_1.9.0.
    For me, this is C:\ST\STM32CubeIDE_1.9.0\STM32CubeIDE
2 Open stm32cubeide.ini in your favorite text editor
3 Add the following bolded line of text in the -vmargs section:
  -vmargs
  '''-Dswt.autoScale=quarter'''
  -Dosgi.requiredJavaVersion=1.8
  -Dosgi.instance.area.default=


 
==Create a New STM32CubeIDE_1.10.1 Project - Hello-World==
 
Follow the example above to create a new project.
<strong>printf()</strong>
  The printf() function is commonly used for "C" programming to output formatted text to a terminal.
  The printf() function is commonly used for "C" programming to output formatted text to a terminal.
  This allows easy debugging in that variables, intermediate values, entering/leaving functions can
  This allows easy debugging in that variables, intermediate values, entering/leaving functions can
Line 104: Line 101:
  Redefining either function to write characters to a serial port will produce a functioning
  Redefining either function to write characters to a serial port will produce a functioning
  printf().
  printf().
<br>
'''Important note when using printf():'''
<strong>I2C</strong><br>
The standard stdio library used to implement the printf() function will buffer the stdout
[[File:Nucleo f103rb 2018 07 06 arduino right.jpg|right|thumb]]
stream data before it is sent to the lower level output function.
  We will use the upper right two pins on the Arduino header.
This results in non-full lines not printing until a line feed character is sent.
  These are mapped as follows: D15: P8_8 (SCL) and D14: P8_9 (SDA)
To avoid this problem, add the following line to request the stdout stream to be '''unbuffered:'''
  By default, STM's pin selector will map I2C1 to different pins.
'''setvbuf(stdout, NULL, _IONBF, 0);'''
  To change this, open the project's .ico file, select "Pinout & Configuration" tab
  Select '''Connectivity''' category, and then '''I2C1'''
====Add putchar() and getchar() Functions:====
  If not yet enabled, enable it in the "Mode" section as "I2C"
Add the following serial output and serial input code to main.c, in the specified sections:
  In the "GPIO Settings" section, we want it to display '''PB8''' for I2C_SCL and '''PB9''' for I2C_SDA.
  If it doesn't, select the pin in the "Pin Name" field for I2C_SCL, and then select '''PB8''' in the Pinout view
/* Private user code ---------------------------------------------------------*/
  Doing this will usually define both pinsIf not, repeat the process for I2C_SDA and PB9.
/* USER CODE BEGIN 0 */
#define HAL_SMALL_WAIT  40
// Define serial input and output functions using UART2
int __io_putchar(int ch)
{
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_SMALL_WAIT);
    return 1;
}
// Read a character from the UART with small timeout
// This function works well if called within a 50ms or shorter loop, but may lose characters if
// data is received with any real speed.
// Use DMA for receive data if trying to perform higher speed communication (anything faster than typing).
// If HAL_UART_Receive() returns error, return EOF (-1), else return the character read
int __io_getchar(void)
{
    uint8_t byte;
    HAL_StatusTypeDef hal_status = HAL_UART_Receive(&huart2, &byte, sizeof(byte), HAL_SMALL_WAIT);
    if(HAL_OK == hal_status)
    {
        return byte;
    }
    else
        return EOF;
}
/* USER CODE END 0 */
 
====Add stdio.h include:====
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h> // printf()
 
/* USER CODE END Includes */
 
====Add printf() into your code:====
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
    '''printf("Hello World\n");'''
    HAL_Delay(500);
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  } // while(1)
  /* USER CODE END 3 */
} // main()
 
At this point, you should be able to build, download, and run, with a flashing LED<br>
and "Hello World" repeatedly displayed in a terminal window.
==Create a New STM32CubeIDE_1.10.1 Project - Command_Line==
On this website, '''Command Line''', has it's own web page.
Feel free to read the page, OR, maybe you just want to watch it work...
Either way, scroll down to the '''Source Code''' section and get  "command_line.c" and "command_line.h"
onto your host computer.
[[Command_Line_Interface]]
  Then, copy the files into the <project>/Core/Src folder.
'''Do this by selecting the file(s), right click <copy> and then right clicking on the '''Src''' folder
and using the <paste> function.'''
  Once you have the files in your project, add the function calls as documented in the following
two sections.
At this point, you should be able to build, download, and run a command line interface using<br>
a serial terminal program like TeraTerm or Putty.
 
==Using LittleFS==
  On this website, '''LittleFS Flash File System''', has it's own web page.
Feel free to read the page, OR, maybe you just want to watch it work...
  Either way, scroll down to the '''My Source Code''' section and get  "littlefs_interface.c" and "littlefs_interface.h"
  onto your host computer.
[[STM32 - LittleFS Flash File System Interfacing]]
Then, copy the files into the <project>/Core/Src folder.
'''Do this by selecting the file(s), right click <copy> and then right clicking on the '''Src''' folder
and using the <paste> function.'''
  Once you have the files in your project, modify the original code as follows to use the new functionality:
  1a) In command_line.c, add '''#include "littlefs_interface.h"''' with the other #includes.
1b) Within cmd_table[], add '''LITTLEFS_COMMANDS,  /* set of commands from littlefs_interface.h */'''
    at the bottom of the table, but above the '''{NULL,NULL,0,NULL}, /* end of table */'''
    This includes a whole set of file commands into the command table.
2) The source code for LittleFS needs to be added into the project
    Download the LittleFS source code from https://github.com/littlefs-project/littlefs
    Unzip the folder into the project such that the directory structure looks something like:
    Core
      Inc
      LittleFS
      Src
    There are plenty of files within this package, but only the core source / header files are needed:
    lfs_util.c  <- this file provides a software CRC implementation for LittleFS
    lfs_util.h
    lfs.c  <- this file provides all the essential functionality of LittleFS
    lfs.h
 
At this point, you should be able to build, download, and run a command line interface using<br>
with a new set of file functions added, allowing files to be created, renamed, copied, and displayed.<br>
See: "STM32-Using DMA for Serial Receive" to improve serial reception and to add X-Modem commands.
 
==Interface a rotary encoder==
The STM32 family provides timers to support a rotary encoder, reading "CH A", and "CH B" signals,
  debouncing the switches, creating a counter that counts up when the encoder rotates one way,
and counts down when the encoder rotates the other way.
[[File:RotaryEncoder.jpg|800px]]
The vertical dashed line in the diagram above represents the current position of the encoder.
As the dash line is "moved" along the 'CH A'/'CH B' waveforms, the corresponding high/low signals
will be sent to the STM32.
 
===Wiring Diagram===
Encoder Signal  Wire Color  NUCLEO Pin STM32 Signal
    CLK          Orange      CN7-28      PA0 (TIM2_CH1)
    DT            Brown        CN7-30      PA1 (TIM2_CH2)
    SW            Yellow      CN7-38      PC0
    +            Red          CN7-16      3V3
    GND          Black        CN7-20      GND
 
[[File:RotaryEncoderModule.jpg|right|thumb]]
KY-040 Rotary Encoder Module
 
===Example STM32 Project using NUCLEO-F103RB===
https://github.com/JimMerkle/NUCLEO-F103RB_Rotary_Encoder
 
==Miscellaneous==
===Using the JTAG (SWD) Debugger===
This assumes you have a project that builds and maybe runs to some degree, but something is broken...
1) Instead of the "Run" button, click on the "Run->Debug As..."
2) Using the JTAD (SWD) debugger, the application will launch, and stop at a breakpoint set at HAL_Init(),
    allowing you to begin debugging from there.
3) Set a breakpoint where you want to begin debugging, example - at the MX_GPIO_Init() call.
4) Click the "Resume" button, allowing the processor to run until it hits the breakpoint
5) You can "Step Into" (F5), or "Step Over" (F6) lines of code.
    You can also step out of a function using "Step Return" (F7)
'''STM32Cube Monitor:'''
https://www.st.com/en/development-tools/stm32cubemonitor.html
 
===Where is everything?===
Once an STM32 processor or development board is selected, STM32CubeIDE downloads and installs
a firmware package for the device selected.
I found the install location for the “F1” firmware here:
'''C:\Users\JimMerkle\STM32Cube\Repository\STM32Cube_FW_F1_V1.8.4'''
Within this folder are header files, HAL source code, middleware, driver code,
example code, and documentation for the firmware package.
  Each project includes a copy of the header files needed for a build.
(Just drill down inside the project's '''Driver''' folder.)  Example:
C:\Users\JimMerkle\Documents\STM32_Projects\NUCLEO-F103RB_Blinky\Drivers\STM32F1xx_HAL_Driver\Inc\stm32f1xx_hal_gpio.h


<strong>Additional Resources:</strong>
===Additional Resources:===
  [https://shawnhymel.com/1873/how-to-use-printf-on-stm32/#:~:text=We%20can%20call%20the%20STM32,re%2Dwrite%20the%20underlying%20functions Shawn Hymel - How to use printf() on stm32]
  [https://shawnhymel.com/1873/how-to-use-printf-on-stm32/#:~:text=We%20can%20call%20the%20STM32,re%2Dwrite%20the%20underlying%20functions Shawn Hymel - How to use printf() on stm32]
  [https://forum.digikey.com/t/easily-use-printf-on-stm32/20157 Digikey Forum: Easily Use Printf On STM32]
  [https://forum.digikey.com/t/easily-use-printf-on-stm32/20157 Digikey Forum: Easily Use Printf On STM32]
  [https://os.mbed.com/platforms/ST-Nucleo-F103RB/ NUCLEO-F103RB Documentation - Mbed]
  [https://os.mbed.com/platforms/ST-Nucleo-F103RB/ NUCLEO-F103RB Documentation - Mbed]
[https://www.waveshare.com/wiki/XNUCLEO-F411RE '''X'''NUCLEO boards - Pinouts]

Latest revision as of 14:44, 18 June 2024

Although this getting started class will work well for many of the STM32 processors and boards, we will focus on the NUCLEO-F103RB board with its STM32-F103RB processor.
Class Description

64-Pin NUCLEO Board

* Green LED, LD2, connected to PA5, illuminates when driven high
* Blue PushButton, B1, connects to PC13, 4.7K external pullup, signal connects to ground when pressed
* 32,768Hz crystal oscillator, LSE, used for Real Time Clock (RTC)
* 8MHz HSE, is provided by the 8MHz crystal oscillator from the attached ST-Link, into PD0
* UART2 connects the target processor to the ST-Link, providing a USB COM port connection on the host computer. Uses PA2 for TX, and PA3 for RX.
* ST-Link connects to TCK (PA14), TMS (PA13)
Board: https://www.st.com/en/evaluation-tools/nucleo-f103rb.html
Manual: https://www.st.com/resource/en/user_manual/um1724-stm32-nucleo64-boards-mb1136-stmicroelectronics.pdf
Schematic: https://www.st.com/resource/en/schematic_pack/nucleo_64pins_sch.zip

Processor: STM32F103RBT6

* 128KB Flash, organized as 128 - 1K sectors
* 20KB SRAM
* 72MHz clock
* Single-cycle multiplication and hardware division
* 2.0 to 3.6 V application supply and I/Os
* Within this part series, this is considered a "Medium-density device",
  and as such, has the following peripherals:
* 3 USARTs
* 3 16-bit timers
* 2 SPIs, 2 I2Cs, USB, CAN, 1 PWM Timer, 2 ADCs
Notes:
   "Low-density device" has fewer peripherals.
   "High-density device" has more peripherals.

Software - Development Tools - IDE

I recommend creating an STMicro account to make multiple downloads easier.
Download the following tool(s) from STMicro:
https://www.st.com/en/development-tools/stm32cubeide.html  (Required)
Optional tools (not necessary for the class)
https://www.st.com/en/development-tools/stm32-software-development-tools.html
STM32CubeProg - Programming tool to write/read firmware to/from the target processor
STSW-Link007 - ST-Link Firmware Upgrade tool
STSW-Link009 - Windows Device Drivers for ST-Link adapters

Installing STM32CubeIDE_1.10.1

* Allow the software to install at C:/ST/STM32CubeIDE_1.10.1
* When asked where to store project files, I recommend creating a path off of C:/Users/YourName/Documents
   Something like: C:/Users/YourName/Documents/STM32_Projects
   This makes your projects easy to find and backup.

Create a New STM32CubeIDE_1.10.1 Project - Blinky

1 Click "Start new STM32 project", then select the "Board Selector" tab
2 In the Commercial Part Number box, type "NUCLEO-F103RB", select the board, and then click "Next>" at the bottom
3 Since I have several STM32 boards, I like to have the board name in the name of the project
  I used: "NUCLEO-F103RB_Blinky" as the project name
4 Select "C++" (This allows both C and C++), Executable, STM32Cube Proect Type, Finish
5 Initialize all peripherals with their default Mode ? - Yes
6 Device Configuration Tool editor is associated with Device Configuration Tool perspective.
   Do you want to open this perspective now?  (Select Yes to see what's configured)

7 Under "Project Manager Tab", Linker Settings, I set both Minimum Heap Size and Minimum Stack Size to 0x800 (2K bytes each)
8 On the left, Project Explorer window, Open Core->Src->main.c
Note: If you examine the source code, you will see that the following functions get called:
 * HAL_Init()
 * SystemClockConfig()
 * MX_GPIO_Init()
 Then, the code enters an empty while loop.  Using the JTAG/SWD debugger, we can build, run, and debug the code,
 but it will be rather boring...

9 Find the infinite loop inside the main() function, and add the following two bold lines of code:
 
 /* Infinite loop */
 /* USER CODE BEGIN WHILE */
 while (1)
 {
   HAL_GPIO_TogglePin(LD2_GPIO_Port,LD2_Pin);
   HAL_Delay(500);
   /* USER CODE END WHILE */

   /* USER CODE BEGIN 3 */
 }
 
 NOTE:
 Be sure to always place your code between the USER CODE BEGIN and USER CODE END comments
 This will protect your code from being removed should you make changes using the STM32CubeMX again.

10 Click the Hammer Icon for "Build"
   The program will be compiled, linked, and located to run on your NUCLEO-F103RB
11 Click the Right Pointing Green Arrow Icon for "Run"
  The program will be downloaded to the device and begin execution.
  You should see LED2 blinking at this point.

Create a New STM32CubeIDE_1.10.1 Project - Hello-World

Follow the example above to create a new project.

The printf() function is commonly used for "C" programming to output formatted text to a terminal.
This allows easy debugging in that variables, intermediate values, entering/leaving functions can
be monitored and status displayed with the printf() stdio library function.
The STM32 development environment provides an stdio printf() library function, but some
"connecting glue" is required to provide the desired output.
The printf() function calls _write() to perform an output function.
See the _write() function in syscalls.c.  This function was given the (weak) attribute, allowing
the function to be over-written (without causing a warning or error message.)
This default _write() function calls __io_putchar() to output one character at a time.
Redefining either function to write characters to a serial port will produce a functioning
printf().
Important note when using printf():
The standard stdio library used to implement the printf() function will buffer the stdout
stream data before it is sent to the lower level output function.
This results in non-full lines not printing until a line feed character is sent.
To avoid this problem, add the following line to request the stdout stream to be unbuffered:
setvbuf(stdout, NULL, _IONBF, 0);

Add putchar() and getchar() Functions:

Add the following serial output and serial input code to main.c, in the specified sections:

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define HAL_SMALL_WAIT  40
// Define serial input and output functions using UART2
int __io_putchar(int ch)
{
    HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, HAL_SMALL_WAIT);
    return 1;
}

// Read a character from the UART with small timeout
// This function works well if called within a 50ms or shorter loop, but may lose characters if
// data is received with any real speed.
// Use DMA for receive data if trying to perform higher speed communication (anything faster than typing).
// If HAL_UART_Receive() returns error, return EOF (-1), else return the character read
int __io_getchar(void)
{
    uint8_t byte;
    HAL_StatusTypeDef hal_status = HAL_UART_Receive(&huart2, &byte, sizeof(byte), HAL_SMALL_WAIT);
    if(HAL_OK == hal_status)
    {
        return byte;
    }
    else
        return EOF;
}
/* USER CODE END 0 */

Add stdio.h include:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h> // printf()
 
/* USER CODE END Includes */

Add printf() into your code:

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
    printf("Hello World\n");
    HAL_Delay(500);

    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  } // while(1)
  /* USER CODE END 3 */
} // main()

At this point, you should be able to build, download, and run, with a flashing LED
and "Hello World" repeatedly displayed in a terminal window.

Create a New STM32CubeIDE_1.10.1 Project - Command_Line

On this website, Command Line, has it's own web page.
Feel free to read the page, OR, maybe you just want to watch it work...
Either way, scroll down to the Source Code section and get  "command_line.c" and "command_line.h"
onto your host computer.
Command_Line_Interface
Then, copy the files into the <project>/Core/Src folder.
Do this by selecting the file(s), right click <copy> and then right clicking on the Src folder
and using the <paste> function.
Once you have the files in your project, add the function calls as documented in the following
two sections.

At this point, you should be able to build, download, and run a command line interface using
a serial terminal program like TeraTerm or Putty.

Using LittleFS

On this website, LittleFS Flash File System, has it's own web page.
Feel free to read the page, OR, maybe you just want to watch it work...
Either way, scroll down to the My Source Code section and get  "littlefs_interface.c" and "littlefs_interface.h"
onto your host computer.
STM32 - LittleFS Flash File System Interfacing
Then, copy the files into the <project>/Core/Src folder.
Do this by selecting the file(s), right click <copy> and then right clicking on the Src folder
and using the <paste> function.
Once you have the files in your project, modify the original code as follows to use the new functionality:
1a) In command_line.c, add #include "littlefs_interface.h" with the other #includes.
1b) Within cmd_table[], add LITTLEFS_COMMANDS,   /* set of commands from littlefs_interface.h */
    at the bottom of the table, but above the {NULL,NULL,0,NULL}, /* end of table */
    This includes a whole set of file commands into the command table.
2) The source code for LittleFS needs to be added into the project
   Download the LittleFS source code from https://github.com/littlefs-project/littlefs
   Unzip the folder into the project such that the directory structure looks something like:
   Core
     Inc
     LittleFS
     Src

   There are plenty of files within this package, but only the core source / header files are needed:
   lfs_util.c  <- this file provides a software CRC implementation for LittleFS
   lfs_util.h
   lfs.c   <- this file provides all the essential functionality of LittleFS
   lfs.h

At this point, you should be able to build, download, and run a command line interface using
with a new set of file functions added, allowing files to be created, renamed, copied, and displayed.
See: "STM32-Using DMA for Serial Receive" to improve serial reception and to add X-Modem commands.

Interface a rotary encoder

The STM32 family provides timers to support a rotary encoder, reading "CH A", and "CH B" signals,
debouncing the switches, creating a counter that counts up when the encoder rotates one way,
and counts down when the encoder rotates the other way.

The vertical dashed line in the diagram above represents the current position of the encoder.
As the dash line is "moved" along the 'CH A'/'CH B' waveforms, the corresponding high/low signals
will be sent to the STM32.

Wiring Diagram

Encoder Signal  Wire Color  NUCLEO Pin  STM32 Signal
    CLK           Orange       CN7-28      PA0 (TIM2_CH1)
    DT            Brown        CN7-30      PA1 (TIM2_CH2)
    SW            Yellow       CN7-38      PC0
    +             Red          CN7-16      3V3
    GND           Black        CN7-20      GND
KY-040 Rotary Encoder Module

Example STM32 Project using NUCLEO-F103RB

https://github.com/JimMerkle/NUCLEO-F103RB_Rotary_Encoder

Miscellaneous

Using the JTAG (SWD) Debugger

This assumes you have a project that builds and maybe runs to some degree, but something is broken...

1) Instead of the "Run" button, click on the "Run->Debug As..."
2) Using the JTAD (SWD) debugger, the application will launch, and stop at a breakpoint set at HAL_Init(),
   allowing you to begin debugging from there.
3) Set a breakpoint where you want to begin debugging, example - at the MX_GPIO_Init() call.
4) Click the "Resume" button, allowing the processor to run until it hits the breakpoint
5) You can "Step Into" (F5), or "Step Over" (F6) lines of code.
   You can also step out of a function using "Step Return" (F7)

STM32Cube Monitor:
https://www.st.com/en/development-tools/stm32cubemonitor.html

Where is everything?

Once an STM32 processor or development board is selected, STM32CubeIDE downloads and installs
a firmware package for the device selected.
I found the install location for the “F1” firmware here:
C:\Users\JimMerkle\STM32Cube\Repository\STM32Cube_FW_F1_V1.8.4
Within this folder are header files, HAL source code, middleware, driver code,
example code, and documentation for the firmware package.

Each project includes a copy of the header files needed for a build.
(Just drill down inside the project's Driver folder.)  Example:
C:\Users\JimMerkle\Documents\STM32_Projects\NUCLEO-F103RB_Blinky\Drivers\STM32F1xx_HAL_Driver\Inc\stm32f1xx_hal_gpio.h

Additional Resources:

Shawn Hymel - How to use printf() on stm32
Digikey Forum: Easily Use Printf On STM32
NUCLEO-F103RB Documentation - Mbed