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 its library functions that allows sending formatted data streams.

Arm GCC toolchain comes with newlib C library from Redhat, and so it isn’t specially designed for 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, os 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. Named it as newlib_stubs.c and placed in 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 reads 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 for 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 PA9 pin used and PA10 as Rx. Tx pin has to be configured as alternative function push-pull while Rx as 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 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 string message and echoes back chars sent through the terminal program. STM32F103ZET6USART

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.