Skip to content

Communicate between microcontroller and PC using UART polling, interrupt, and DMA.

Notifications You must be signed in to change notification settings

Ltran0325/STM32-UART-Communication

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

64 Commits
 
 
 
 
 
 

Repository files navigation

STM32-UART-Communication

Communicate between microcontroller and PC using UART. With and without HAL driver library.

Universal Asynchronous Reciever-Transmitter (UART):

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.

image

Source: Scott Campbell -- https://www.circuitbasics.com/basics-uart-communication/

Data from the UART is sent and recieved as a packet.

STM32CubeMX (Initialization Code Generator GUI):

In STM32CubeMX, enable USART2. Set buad rate to 9600 bit/s, 8 data bits, no parity bit, and 1 stop bit.

UART Interrupt Method Without HAL UART Module Driver:

1) Recieve and return message via UART interrupt.

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){

	}

}

2) USART2 initialization. Enable USART2 receive/transmit interrupts.

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 */

}

3) Program USART2 interrupt request handler.

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++;
		}

	}

}

4) Run the program.

Board transmits "Hello!" and recieves "Return".

image

UART With HAL UART Module Driver:

Polling Method:

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.

Interrupt Method:

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.

DMA Method:

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.

image

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

About

Communicate between microcontroller and PC using UART polling, interrupt, and DMA.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages