Hello everyone.
In this article, we will try to send LL UART communication using interrupts.
What is LL? If you are wondering what LL is, please refer to the article HAL and LL.
The following is the development environment at the time of posting.
PC: Windows 10 OS
IDE: STM32CubeIDE Version1.5.0
Configurator: STM32CubeMX Version6.1.0
Board: STM32Nucleo-F401RE
Create a project
Start the IDE, select File- New – STM32 Project, when the Target Selection window comes up, select the Board Selector tab, select NUCLEO-F401RE from the Boards List and press the Next button. Click the Next button.
Enter F401UartLLTxInt as the Project Name and press the Finish button.
It will ask “Initialize all peripherals with their default Mode ? Press Yes.
Preparing to use the UART with LL
Select the Project Manager tab and change the USART in Advanced Settings from HAL (default) to LL. (Use the combo box to select)
Click on the HAL section to display a list of combo boxes, and select LL.
Select Project – Build All.
It will ask “Do you want Generate Code ? Check the Remember my decision checkbox and press OK.
This action can be associated with C/C++ perspective.
This action can be associated with C/C++ perspective.
Modify UART parameters
Double-click F401UartLL.ioc from the Project Explorer to open the GUI window.
Select Pinout & Configuration – Connectivity – USART2.
In the Mode section, Asynchronus is selected and ready to use. (Disable if you don’t want to use it.)
We will use parity again.
In the Configuration – Parameter Settings section, make the following settings.
(The only change from the default is to change Parity from None to Even)
Baud Rate : 115200
Word Length : 8bits
Parity : Even
Stop Bits : 1
Parity, data length and STM32
There are a few differences between the STM32 and the general communication specifications that you should be aware of.
This diagram shows the format of the serial data flowing through the TX and RX lines of the microcontroller.
The data flows from left to right.
S : Start bit Logical 0
D0-D7 : Data bits
P : Parity bit
ST : Stop bit Logical 0
In the case of asynchronous communication by UART, it is called LSB first, and it starts sending from D0 bit.

In the above figure, the data length is 7, but on the STM32, it should be 8.
In the figure below, the data length is 8, but for the STM32, it should be 9.
(Parity is included in the data length for STM32)
If the parity is set to ODD or EVEN in the microcontroller USART parameter settings, the data length is 8.
If the parity is set to ODD or EVEN in the microcontroller USART parameter setting, the microcontroller will calculate the parity and send the data.
If parity is set to ODD or EVEN in the microcontroller USART parameter settings, the microcontroller will calculate the parity and add it after the data bits, so the user does not need to code it.
When receiving
The microcontroller calculates the parity from the sent serial data and compares it with the parity bit to detect an error.
The result can be checked with the PE bit in the status register.
The user only needs to write the judgment and processing.
What is even parity?
Even: Parity is created so that the sum of the data bits (7 or 8) and the parity bit (1 bit) is 0.
Odd: Parity is created so that the sum of the data bits (7 or 8) and the parity bit (1 bit) is 1.
Example of even parity for 8-bit data.
When the data is 0b00000000, the parity is 0
When the data is 0b00000001, the parity is 1
When the data is 0b00000010, the parity is 1
When the data is 0b00000110, the parity is 0
When data is 0b11111111, parity is 0
Example of odd parity for 8-bit data.
When the data is 0b00000000, the parity is 1
When the data is 0b00001000, the parity is 0
Parity is 1 when the data is 0b11111111
Configuring Interrupt Settings
Check the Enabled checkbox for USART2 global interrupt in Configuration – NVIC Settings.
Now let’s build it and check the code.
Select Project – Build All.
Coretex-M and Interrupts
In the Coretex-M microcontroller, the address of the handler is stored in the interrupt vector table.
The vector table is described in the file startup_stm32f401retx.s located in F401UartLL – Core – Startup in Project Explorer.
This is the assembler source file.
The following description was found on line 181.
.word USART2_IRQHandler /* USART2 */
When the USART2 interrupt is enabled and an interrupt factor related to USART2 is detected, it will jump to this function.
USART2_IRQHandler() is written in Core – Src – stm32f4xx_it.c, so write the interrupt handling in this part.
Coding it
The point to note was to code between /* USER CODE BEGIN xxx */ and /* USER CODE END xxx */, right?
If you don’t do so, the code you have written will be erased when you use the configurator.
First, main.c
Near the beginning
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes
/* USER CODE BEGIN Includes */ /* USER CODE BEGIN Includes
void TransmitData(void);
#define _CR 0x0d
#define _LF 0x0a
uint8_t buffer[] =
{
'#', 'a', 'b', 'c', 'd', 'e', _CR, _LF
};
bufInfo bi;
/* USER CODE END Includes */
Write the implementation of TransmitData() around the front of the originally written prototype of SystemClock_Config.
/* USER CODE BEGIN PV */
void TransmitData(void)
{
bi.ptr = &buffer[1]; // start from the second byte in the interrupt
bi.size = sizeof(buffer);
bi.counter = 0;
LL_USART_TransmitData9(USART2, (uint16_t)buffer[0]);
LL_USART_EnableIT_TXE(USART2);
}
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
While() in loop
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */ /* USER CODE END WHILE
TransmitData();
HAL_Delay(3000);
/* USER CODE BEGIN 3 */ TransmitData()
}
/* USER CODE END 3 */ }
main.h
main.h is located in Core – Inc.
Now that we have created a structure of type bufInfo, let’s define it.
/* USER CODE BEGIN Includes */
typedef struct
{ ptr
uint8_t *ptr;
uint16_t size;
uint16_t counter;
} bufInfo;
/* USER CODE END Includes */
stm32f4xx_it.c
Write a description that references a variable near the beginning.
/* USER CODE BEGIN Includes */
extern bufInfo bi;
/* USER CODE END Includes */
Write the following code in the interrupt handler function at line 207 (nearby).
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
uint8_t d;
if (LL_USART_IsActiveFlag_TXE(USART2))
{
bi.counter++;
if (bi.counter >= bi.size)
{
LL_USART_DisableIT_TXE(USART2);
return;
}
d = *(bi.ptr++);
LL_USART_TransmitData9(USART2, (uint16_t)d);
}
/* USER CODE END USART2_IRQn 0 */ /* USER CODE BEGIN USART2_IRQn 0
/* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 2
/* USER CODE END USART2_IRQn 1 */ /* USER CODE BEGIN USART2_IRQn 1 */
}
Build it
Build and make sure there are no errors.
Connect the board to the PC with a USB cable because the board will communicate with Tera Term.
Configuration of Tera Term
The image is omitted because it has been shown many times.
Set up in the following way.
COM port : Select Serial in New Connection and select the STLink Virtual COM port.
From Settings – Serial Port
Speed : 115200
Data : 7bit
Parity : even
Stop bit : 1bit
Flow control : none
Check the Local Echo checkbox in the terminal settings.
Outline of the program
This time, we added CR LF to the data to be sent to make it easier to understand that data is being sent one after another.
In TransmitData(), the
LL_USART_TransmitData9(USART2, (uint16_t)buffer[0]) to send the first byte (#).
Enable transmit data empty interrupt with LL_USART_EnableIT_TXE(USART2).
Now when the first byte of data is sent, the interrupt is enabled and the transmission of the second and subsequent bytes is written within the interrupt process.
The second argument of LL_USART_TransmitData9() is of type uint16_t, so we cast it.
In case of parity, this function is used, right?
HAL_Delay() and send the data every 3 seconds.
USART2_IRQHandler() in the interrupt handler.
LL_USART_IsActiveFlag_TXE() to check if the send data is empty, and if so, send the next data.
If all 8 bytes have been sent, disable the interrupt with LL_USART_DisableIT_TXE().
Try to run the program
In the IDE, Run – Debug to Run – Resume to run the program.
If you see #abcde every 3 seconds on Teram Term, you have succeeded.
How was it? Did it work?
In the next article, we will look at sending data using DMA, which is more efficient in using the CPU.
Thank you for your time.





