Connecting STM32 USART to standard I/O streams in GCC

In many situations, when working with STM32 microcontrollers, you will want to output text strings. There is no need to write specialized functions that output specially formatted strings as it is hard to keep up with various cases. It is convenient to use standard I/O streams and their library functions that allow sending formatted data streams.

Arm GCC toolchain comes with the newlib C library from Redhat, so it isn’t specially designed for the embedded toolchain. To use stdio functions, we have to take care of several syscals so-called “stub functions.” These functions usually are provided by operating systems like you would write C programs in Windows or Linux. In our case, we aren’t using any OS, so to avoid error messages while compiling, we have to provide these function declarations where most of them are dummy implementations. It’s not something new pick one that you find on the internet. I noticed that it was written for STM32 Discovery. I named it newlib_stubs.c and placed it in the startup directory. Among system functions implementations like _write(), _fstat(), etc., there are also USARTs assigned to standard streams:

#ifndef STDOUT_USART
#define STDOUT_USART 1
#endif
#ifndef STDERR_USART
#define STDERR_USART 2
#endif
#ifndef STDIN_USART
#define STDIN_USART 1
#endif

Here stdout/stdin streams are mapped to USART1, stderr to USART2. If you need them to be different,

this is a place to do so. Also, there are USART routines that write or read char.

Initializing USART

This is easy by using CMSIS and StdPeriph library. Several things have to be taken care of. We are going to use USART as our example. First of all, we need to enable clocks for USART1, GPIOA, and AFIO with the command:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

Then we need to set up port pins that are used as USART1 pins. As Tx pins, there is a PA9 pin used and PA10 as Rx. The tx pin has to be configured as an alternative function push-pull while Rx a floating input.

void USART1Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	USART_ClockInitTypeDef  USART_ClockInitStructure;
	//enable bus clocks
	/* Enable USART1 and GPIOA clock */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
	//Configure USART1 Tx (PA.09) as alternate function push-pull
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//Configure USART1 Rx (PA.10) as input floating
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	USART_ClockStructInit(&USART_ClockInitStructure);
	USART_ClockInit(USART1, &USART_ClockInitStructure);
	USART_StructInit(&USART_InitStructure);
	//Configure USART1 basic and asynchronous paramters
	USART_Init(USART1, &USART_InitStructure);
	//Enable USART1
	USART_Cmd(USART1, ENABLE);
}

To initialize USART1, we are going to use a couple of handy functions from the stm32f10x_usart.h library.

USART_ClockStructInit(&USART_ClockInitStructure);
USART_StructInit(&USART_InitStructure);

Instead of writing all parameters like baud rate, parity, and mode, we can call these StructInit functions that fill structure variables with default parameters (9600 baud, 8-bit, 1 stop, no parity, no HV flow control, and enable Tx/Rx).

Then we can pass these structures to initialization functions that take care of proper writing to registers:

USART_ClockInit(USART1, &USART_ClockInitStructure);
USART_Init(USART1, &USART_InitStructure);

And the last command is to enable USART1:

USART_Cmd(USART1, ENABLE);

This is it – we set up USART1 that can be used in the main program:

//STM32F103ZET6 Usart Test
#include "stm32f10x.h"
#include "leds.h"
#include "buttons.h"
#include "usart.h"
#include "stm32f10x_it.h"
int main(void)
{
  char i;
	//init leds
  LEDsInit();
  //init buttons t ogenerate interrupts
  ButtonsInitEXTI();
  //initialize USART
  USART1Init();
  //start sys tick timer that also generates interrupts
  SysTick_Config(15000000);
  printf("\r\n USART1 Test \r\n");
while(1	)
	{
	if(USART_GetFlagStatus(USART1,USART_IT_RXNE)==SET)
		{
			i = USART_ReceiveData(USART1);
			printf("  %c",i&0xFF);	   /* print the input char */
		}
	}
}

It simply outputs a string message and echoes back chars sent through the terminal program. STM32F103ZET6USART

Leave a Reply