Communicate between microcontroller and PC using UART. With and without HAL driver library.
Unlike SPI which is a communication protocol, the UART is a physical circuit inside the STM32 microcontroller. UART allows for asynchronous communication between two devices using two wires. In this project, we cover UART via polling, interrupt, and DMA.
Source: Scott Campbell -- https://www.circuitbasics.com/basics-uart-communication/
Data from the UART is sent and recieved as a packet.
In STM32CubeMX, enable USART2. Set buad rate to 9600 bit/s, 8 data bits, no parity bit, and 1 stop bit.
uint8_t transmit[] = "Hello!";
uint8_t receive[];
uint8_t Rx_count = 0;
uint8_t Tx_count = 0;
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
while(1){
}
}
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 9600;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
USART2->CR1 |= USART_CR1_RXNEIE | USART_CR1_TXEIE; // enable RX/TX interrupt
/* USER CODE END USART2_Init 2 */
}
void USART2_IRQHandler(void)
{
// if receive buffer ready to read
if( USART2->ISR & USART_ISR_RXNE ){
// move byte from buffer to message
if(Rx_count < MAX){
receive[Rx_count] = USART2->RDR & 0x0FF; // reading RDR clears RXNE flag
Rx_count++;
}
}
// if byte ready to transfer
if(USART2->ISR & USART_ISR_TC){
// send byte to TDR
if(Tx_count < MAX){
USART2->TDR = transmit[Tx_count]; // write to TDR (clears TC bit)
Tx_count++;
}
}
}
Board transmits "Hello!" and recieves "Return".
The simplest but least efficient method for UART. Polling blocks the CPU until the UART is finished recieving or transmitting data.
Polling method code:
uint8_t TX_Buffer[] = "Hello World!\r\n";
uint8_t RX_Buffer[8];
HAL_UART_Transmit(&huart2,TX_Buffer,sizeof(TX_Buffer),1000); // "Hello World!"
HAL_UART_Receive(&huart2, RX_Buffer, 8, 5000); // "Goodbye!"
Purpose:
MCU sends "Hello World!" to PC. PC returns "Goodbye!" to MCU. Hercules SETUP is used to handle PC UART.
Both HAL polling transmit and recieve functions use similar arguments. First, address variable &huart2 is the UART handle for our enabled USART2. Then, the buffer, size of the buffer in bytes, and the allowed blocking time of the function in ms.
To further understand these two functions read the STM32F4 HAL User Manual: https://www.st.com/resource/en/user_manual/dm00105879-description-of-stm32f4-hal-and-ll-drivers-stmicroelectronics.pdf. Or, highlight the function inside STM32Cube IDE and right-click to open-declaration. which will bring you to the UART HAL module driver.
The interrupt method is non-blocking, meaning that recieve/transmit completion will be indicated through interrupt. Since we are using interrupts, the NVIC must be configured.
Interrupt method code:
HAL_UART_Receive_IT(&huart2, RX_Buffer, sizeof(RX_Buffer)); // Recieve data from PC
HAL_UART_Transmit_IT(&huart2, TX_Buffer, sizeof(TX_Buffer)); // Transmit data to PC
When data the size of RX_Buffer is recieved via UART, the HAL_UART_RxCpltCallback interrupt is called by the HAL_UART_IRQ_Handler.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
__NOP(); // for debugging
}
Completion of data transmit is handled in similar fashion.
The DMA method is the most efficient method for saving CPU cycles. This method offloads the data transfer process to the device's DMA controller instead of the CPU. The DMA is especially useful when data is transferred in quick bursts which would otherwise be handled by rapid interrupt calls.
Source: Yasen Stoyanov -- https://open4tech.com/direct-memory-access-dma-in-embedded-systems/
To configure the DMA, enable USART2_RX and USART2_TX in STMCubeMX
DMA method code:
HAL_UART_Receive_DMA(&huart2, RX_Buffer, sizeof(RX_Buffer)); // Recieve data from PC
HAL_UART_Transmit_DMA(&huart2, TX_Buffer, sizeof(TX_Buffer)); // Transmit data to PC