ST UART Polling and Interrupt
Universal asynchronous receive transmit (UART) is one of the most commonly used communication protocols for hobbyist components and uCs. For example, the radios that are used in my balloon projects (RFD900) and the GPS I used (Adarfuit/uBlox) are all based on serial (UART) communications. Arduino serial ports can also use UART to communicate with these devices. For this reason, serial (UART) ports are often in high demand on uCs. One of the benefits of ST uC is that some can support as many as 16 UART ports.
​
Like in all my ST posts I will be using STM32CubeMX to generate initialization code and I will be using Visual Studio Code to add to the initialization code. There are also a couple of other intricacies in setting up the tools to program the ST Discovery boards. Before starting ensure your programming environment is set up correctly by visiting my tutorial on setting up Visual Studio for ST programming.
Polling Mode Getting Started
Before code can be written for the ST uC, it is necessary to first begin by configuring the ports on the uC for UART communications. This is done through STM32CubeMX. Start by opening up a project with the discovery board you are using. Once the project menu is open, notice that there should be a menu on the left of the screen. One of the selections in this menu is connectivity, pull this menu down to see the various options. Among other options there should be at least 1-2 UART ports available depending on the uC there could be many more. Note that these communication protocols often require additional hardware to work efficiently. What this means is that each of the protocols can only be used on 1 or 2 pins of the uC, the other pins will not have this hardware installed. For this reason, if there is a pink circle with a line through it, the pins needed for this protocol are already in use.
​
Now once you find a USART port that is available you need to enable it and assign its pins. There are two ways to do this. First, from the side menu, you can select the protocol you are interested in and enable it. STM32CubeMX will automatically select the ports needed to use the communications. Alternatively, you can select pins one by one and find which pins are available for assignment to the port you are interested in. See the video below for additional details.

Once the UART is turned on the baud rate, message size and parity of the UART need to be assigned. These parameters are usually determined by what you are wanting to connect to the UART. In my case I will be connecting the RFD900 radio which operates at a baud rate of 56700, has a message size of 8 bits and no parity. To give some background baud rate determines how quickly messages can be sent via the UART port, the message size determines how many bits are set in each message and parity is a technique that can be used to detect errors in the message transmission. These parameters can be set from the UART menu, see the video below for details.
Polling Mode: Coding in VSCode
To use the uC in polling mode there are a few HAL commands that we need to utilize. These commands and their purposes are as follows.
​
HAL_UART_Receive_IT(*huart, *rx_buffer, int numBytes);
HAL_UART_Transmit_IT(*huart *tx_buffer, int numBytes);
​
The UART object is a pointer to the huart structure. If you are interested this structure is declared in a file located in the drivers directory, with the name "stm32f334x8.h". What matters though is that for each of the UARTs available on your uC there is a variable huart<x> which will automatically be generated. For example, if I am using the USART2 port then my variable name is HUART2.
​
These commands are used to transmit and receive data to and from the UART. Additional information can be found for these interrupts in the HAL Programming manual.
​
Now note that when the HAL commands are used to read the UARTs a polling type UART is being used, even though some aspects may appear similar to an interrupt based communications. In addition to the commands above, it is also necessary to declare a function named
​
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart<x>)
​
For receiving messages and
​
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart<x>)
​
for transmitting messages.
​
These callback functions are called after the functions HAL_UART_Transmit_IT and HAL_UART_Receive_IT, successfully transmit or receive the number of bytes specified. Now it is time to use these programs to create something useful.
​
Start by declaring an RX_Buffer object. Remember that this buffer should be uint8_t since the UART is configured for an 8 bit (1 byte) word size.

Once this is complete, place the HAL receive function in the while loop which is found inside void main(). This will allow the data to be received.

Finally, the last part of the process is to define the callback function. This is the point in the code where you might have the program check the validity of the received data or maybe do a short calculation of the data etc. In the example below I just use the UART to receive to 8 bit unsigned integers. I combine these two integers together and then check if they are equal to a specific number.

The same process could be followed to transmit data. A more applicable use for the callback might be the following.
​
Below is attached an image of the UBX packet structure. This structure describes how bytes aer sent in a UBX message. UBX messages are used by the Ublox GPS to send GPS data. See my tutorial on GPS for additional information.
​
​

So now let us say that I wanted to receive a UBX message from a Ublox GPS module. I would need to set up a structure to store each part of the message. Then I could use this structure to direct each piece of incoming data into the proper variable within the structure. This would make the data more easy to access at the end of the callback function. To see a similar algorithm executed using an Arduino visit my tutorial on GPS for the High Altitude Balloon.
Interrupt Mode Getting Started
To begin using the interrupts, set up the UART as in the polling method, but DO NOT generate the scaffolding code. Before you generate code follow the video below to make two additional changes to the settings. Once these changes are made generate the code as you normally would.
​
When the code has finished generating open up the main.c file in VSCode. Scroll down until you have located the function labeled
​
static void MX_USART2_UART_INIT(void)
​
Note that USART2 could be a number (USART<x>) depending on which port you are using. At the end of this function insert the following code.

What this code, does is enable the Transmit Empty interrupt (TXE) and the Receive Not empty interrupt (RXNE). There are buffers built into the ST uC that allow it to temporarily store data transmitted and received by the UART. The data will remain in these buffers until in the case of receive the data is read, or in the case of transmit the data is sent. Essentially, then the RXNE is triggered whenever there is data available to be read. Similarly, whenever data has finished transmitting the Transmit Empty interrupt is triggered. Typically to prevent the TXE interrupt from constantly being called the interrupt is switched on only when the uC is ready to transmit the data. Following data transmission, the TXE interrupt can be used to disable the TXE interrupt and maybe do some additional processing.
Note that in the case of interrupts I find that it is beneficial to just create a separate file to hold headers and functions. To do this start by creating two files. The first file should be named USART2_IT.c and should be in the src directory of your project. The second file should be named USART2_IT.h and should be placed in the Inc directory of your project. Now open the Makefile in your project. Search the file for the variable C_SOURCES. This variable stores all the source files that need to be compiled for the program to work correctly. Add the "USART2_IT.c to this list, or if you are not using USART2 add USART_IT.c.
Now we will need to code our files so that they can transmit and receive data as needed by the interrupts. I wanted to make this tutorial useful, so I have included the source code that I use for my USART on my balloon on Github. This GitHub will just contain the code in the files USART2_IT.c and USART2_IT.h. I have been sure to thoroughly comment the data to aid in comprehension of these files. In these files, I use a circular buffer. This means that the same buffer is used to store data that needs to be transmitted and data the has been received. To do this it is necessary to use two "pointer like" variables which just keep track of where received data starts/ends and where transmit data starts/ends. In addition to this, the file sets up functions to read received data from this buffer and also to send data to be transmitted from this buffer. Note also that the name USART2_IRQHandler(void) came from the file stm32f3xx_it.h which can be found in the inc directory.
​
The link to the library can be found below