Using STM32 HAL Library to Drive UART in RT-Thread

Introduction

Previously, I had been trying to develop STM32 with RT-Thread under Linux. While this approach is feasible, it comes with various inconveniences. Using VSCode + Scons + openocd for editing, compiling/linking, and flashing is possible for development, but there’s no complete solution for debugging in this environment, requiring Eclipse for debugging. Although converting RT-Thread to an Eclipse project is possible, at that point it might be better to directly use Keil 5 for development under Windows.

Since the STM32F103’s onboard flash is too small and its stack is very limited, I switched to STM32F411. The board is from Waveshare’s XNUCLEO, which is compatible with STM’s official NUCLEO boards.

Installing Keil 5.23

I first installed Keil 5.11 and cracked it, then uninstalled it and installed Keil 5.23, which automatically showed as successfully cracked. The cracking tool is the registration code generator that has been popular for many years. I’m using Keil 5.23 because RT-Thread projects can only be opened with this version.

Installing Libraries with Pack Installer

You need to install Keil::STM32F4xx_DFP to open the nucleo project in RT-Thread’s bps branch.

Installing STM32CubeMX

With STM32CubeMX, we can easily generate initialization code by clicking with the mouse. However, we still need to write function calls ourselves, which is not as convenient as NPX’s PE.

Waveshare’s 100 yuan board includes an ST-Link debugger, so we’ll use ST-Link for debugging.

Downloading the Nucleo Project

Open the Keil 5 project in rt-thread-master\bsp\stm32f411-nucleo, try to compile it, and after it passes, set up the project’s debug configuration to use ST-Link for debugging, then download the program to the board. Although the program is downloaded, there’s no visible effect because the main function is empty.

Let’s add the following code to the main function:

int main(void)
{
  /* user app entry */
	MX_GPIO_Init();
	while(1){
		rt_thread_delay(5);
		HAL_GPIO_WritePin (GPIOC, GPIO_PIN_5, GPIO_PIN_SET);
		rt_thread_delay(5);
		HAL_GPIO_WritePin (GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);
	}
}

And add the LED initialization function:

static void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC5 */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

Now we can see the onboard LED4 light up and blink.

Where do these initialization codes come from? The code in the main function is found online, a simple function to operate GPIO. The MX_GPIO_Init code segment comes from the code automatically generated by STM32CubeMX. Below I’ll explain how to generate UART initialization code using STM32CubeMX.

Generating UART Initialization Code with STM32CubeMX

  • First, create a project and set the chip model. Here’s a demonstration:

2017-04-06_16-41-06.gif

  • Then set up the serial port pins. Since RT-Thread has already initialized UART1, we can’t use this one. We can use UART6.

2017-04-06_16-57-29.gif

  • Open the project and extract the code. The blue part is the code we need to take:

2017-04-06_17-01-11.gif

  • Port the code In the void HAL_UART_MspInit(UART_HandleTypeDef *huart) function, add the code for USART6:
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    if (huart->Instance == USART2)
    {
        /*##-1- Enable peripherals and GPIO Clocks #################################*/
        /* Enable GPIO TX/RX clock */
        USARTx_TX_GPIO_CLK_ENABLE();
        USARTx_RX_GPIO_CLK_ENABLE();

        /* Enable USARTx clock */
        USARTx_CLK_ENABLE(); 

        /*##-2- Configure peripheral GPIO ##########################################*/  
        /* UART TX GPIO pin configuration  */
        GPIO_InitStruct.Pin       = USARTx_TX_PIN;
        GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull      = GPIO_PULLUP;
        GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;
        GPIO_InitStruct.Alternate = USARTx_TX_AF;

        HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);
        
        /* UART RX GPIO pin configuration  */
        GPIO_InitStruct.Pin = USARTx_RX_PIN;
        GPIO_InitStruct.Alternate = USARTx_RX_AF;
        
        HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
        
        HAL_NVIC_SetPriority(USART2_IRQn, 0, 1);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
    }
		if (huart->Instance == USART6)
		{
		/* USER CODE BEGIN USART6_MspInit 0 */

		/* USER CODE END USART6_MspInit 0 */
			/* Peripheral clock enable */
			__HAL_RCC_USART6_CLK_ENABLE();
		
			/**USART6 GPIO Configuration    
			PC6     ------> USART6_TX
			PC7     ------> USART6_RX 
			*/
			GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
			GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
			GPIO_InitStruct.Pull = GPIO_PULLUP;
			GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
			GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
			HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

			/* Peripheral interrupt init */
			HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
			HAL_NVIC_EnableIRQ(USART6_IRQn);
		/* USER CODE BEGIN USART6_MspInit 1 */

		/* USER CODE END USART6_MspInit 1 */
		}
}

In the main.c file, add the MX_USART6_UART_Init(); function content, and call MX_USART6_UART_Init(); in the main function to initialize.

Then, test it in the main() function:

		uint8_t TxData[10]= "01234abcde";
		HAL_UART_Transmit(&huart6,TxData,10,0xf);

We’ll place the Waveshare board’s JP4 jumper on the right side so that the USB TO UART can use PC6 and PC7 as the serial interface. Then, connect the USB interface to the PC, and we can view the character string received by the PC in a serial terminal like XSHELL.

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy