STM32F4 USART tutorial

This example code shows how to use the STM32F407′s USART. It initializes the USART1 in receive and transmit mode an receives a data string via an interrupt.

The USART allows us to send data to a PC or other device via the serial interface (RS232). We can also receive data from a PC or another device. However please note that the STM32F407 works at 3.3V and most other devices operate at 5V or even the standard +-12V for RS232.

In order to connect to these devices (particularly the RS232 devices) you need to use a level shifter like the MAX232 or a serial to USB converter like the FT232.

If you wonder where to find all the values and properties for the InitStruct’s have a look at the include files located in the STM32F4 library by ST under Libraries/STM32F4xx_StdPeriph_Driver/ in the inc and src folders

Everything is explained in the comments. Enjoy the code!

#include <stm32f4xx.h>
#include <misc.h>			 // I recommend you have a look at these in the ST firmware folder
#include <stm32f4xx_usart.h> // under Libraries/STM32F4xx_StdPeriph_Driver/inc and src

#define MAX_STRLEN 12 // this is the maximum string length of our string in characters
volatile char received_string[MAX_STRLEN+1]; // this will hold the recieved string

void Delay(__IO uint32_t nCount) {
  while(nCount--) {
  }
}

/* This funcion initializes the USART1 peripheral
 *
 * Arguments: baudrate --> the baudrate at which the USART is
 * 						   supposed to operate
 */
void init_USART1(uint32_t baudrate){

	/* This is a concept that has to do with the libraries provided by ST
	 * to make development easier the have made up something similar to
	 * classes, called TypeDefs, which actually just define the common
	 * parameters that every peripheral needs to work correctly
	 *
	 * They make our life easier because we don't have to mess around with
	 * the low level stuff of setting bits in the correct registers
	 */
	GPIO_InitTypeDef GPIO_InitStruct; // this is for the GPIO pins used as TX and RX
	USART_InitTypeDef USART_InitStruct; // this is for the USART1 initilization
	NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

	/* enable APB2 peripheral clock for USART1
	 * note that only USART1 and USART6 are connected to APB2
	 * the other USARTs are connected to APB1
	 */
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

	/* enable the peripheral clock for the pins used by
	 * USART1, PB6 for TX and PB7 for RX
	 */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

	/* This sequence sets up the TX and RX pins
	 * so they work correctly with the USART1 peripheral
	 */
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // Pins 6 (TX) and 7 (RX) are used
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; 			// the pins are configured as alternate function so the USART peripheral has access to them
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;		// this defines the IO speed and has nothing to do with the baudrate!
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;			// this defines the output type as push pull mode (as opposed to open drain)
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;			// this activates the pullup resistors on the IO pins
	GPIO_Init(GPIOB, &GPIO_InitStruct);					// now all the values are passed to the GPIO_Init() function which sets the GPIO registers

	/* The RX and TX pins are now connected to their AF
	 * so that the USART1 can take over control of the
	 * pins
	 */
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); //
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

	/* Now the USART_InitStruct is used to define the
	 * properties of USART1
	 */
	USART_InitStruct.USART_BaudRate = baudrate;				// the baudrate is set to the value we passed into this init function
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard)
	USART_InitStruct.USART_StopBits = USART_StopBits_1;		// we want 1 stop bit (standard)
	USART_InitStruct.USART_Parity = USART_Parity_No;		// we don't want a parity bit (standard)
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard)
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
	USART_Init(USART1, &USART_InitStruct);					// again all the properties are passed to the USART_Init function which takes care of all the bit setting


	/* Here the USART1 receive interrupt is enabled
	 * and the interrupt controller is configured
	 * to jump to the USART1_IRQHandler() function
	 * if the USART1 receive interrupt occurs
	 */
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt

	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		 // we want to configure the USART1 interrupts
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// this sets the priority group of the USART1 interrupts
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		 // this sets the subpriority inside the group
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			 // the USART1 interrupts are globally enabled
	NVIC_Init(&NVIC_InitStructure);							 // the properties are passed to the NVIC_Init function which takes care of the low level stuff

	// finally this enables the complete USART1 peripheral
	USART_Cmd(USART1, ENABLE);
}

/* This function is used to transmit a string of characters via
 * the USART specified in USARTx.
 *
 * It takes two arguments: USARTx --> can be any of the USARTs e.g. USART1, USART2 etc.
 * 						   (volatile) char *s is the string you want to send
 *
 * Note: The string has to be passed to the function as a pointer because
 * 		 the compiler doesn't know the 'string' data type. In standard
 * 		 C a string is just an array of characters
 *
 * Note 2: At the moment it takes a volatile char because the received_string variable
 * 		   declared as volatile char --> otherwise the compiler will spit out warnings
 * */
void USART_puts(USART_TypeDef* USARTx, volatile char *s){

	while(*s){
		// wait until data register is empty
		while( !(USARTx->SR & 0x00000040) );
		USART_SendData(USARTx, *s);
		*s++;
	}
}

int main(void) {

  init_USART1(9600); // initialize USART1 @ 9600 baud

  USART_puts(USART1, "Init complete! Hello World!rn"); // just send a message to indicate that it works

  while (1){
    /*
     * You can do whatever you want in here
     */
  }
}

// this is the interrupt request handler (IRQ) for ALL USART1 interrupts
void USART1_IRQHandler(void){

	// check if the USART1 receive interrupt flag was set
	if( USART_GetITStatus(USART1, USART_IT_RXNE) ){

		static uint8_t cnt = 0; // this counter is used to determine the string length
		char t = USART1->DR; // the character from the USART1 data register is saved in t

		/* check if the received character is not the LF character (used to determine end of string)
		 * or the if the maximum string length has been been reached
		 */
		if( (t != 'n') && (cnt < MAX_STRLEN) ){
			received_string[cnt] = t;
			cnt++;
		}
		else{ // otherwise reset the character counter and print the received string
			cnt = 0;
			USART_puts(USART1, received_string);
		}
	}
}

26 thoughts on “STM32F4 USART tutorial

    1. Sorrry, unfortunately I don’t use an IDE like Keil, I just make a new folder for a new project and copy and paste the necessary files :/

  1. Thank you for this code, it is very useful, but I was trying to send pin bit level. Here is the code:
    USART_puts(USART1, GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_1));

    But I get error:

    1. That is not going to work because the function expects a full byte as a data value. Then second if you read ’1′ from the pin then you will need to make it an ASCII standard character by adding 48 to it. This new value has to be saved as a char and then you can send it via serial and you will see a 1 or 0 appear, depending on the pin state.

  2. Elia,
    I have successfully used Arduino/SoftwareSerial to run your USART, sending ‘h’ to Arduino->serial monitor but not successful with the above code which sends string.
    What does function: “Init complete! Hello World!”
    Also: if I replace your Interrupt handler code with simple LED blink(PD14) it proves that the interrupt is configured properly, but there is no echo of what I type into the serial monitor, even though the led blink does respond as I mentioned. All baud rates are 9600, for SoftwareSerial as well as Serial as well as the STM.
    Is there some code update/correction needed?

    1. From your other comments I guess you should check your pin connections between the STM32F4 and the Arduino.
      What I have done to test the serial communication I connected the STM32F4 pins directly to the RX/TX pins of the Arduino (make sure you’re Arduino has no program running on it!).
      This would be a simple test to check if the STM32F4 is working corretly, the Arduino software serial might not work properly.

      1. Elia,
        Thank you for your kind response, and I must apologize for my oversight…….I needed to set the serial monitor to feed New Line…..so, the string was not sent. I thought I had that set long ago for other projects.
        Only think, though: if the typed string has more than max length you get gibberish in return.
        Example:
        So: 12345678901 for eleven characters plus return
        But: É6FVfv†–¦,3ffÍ5678901 if I type 123456789012

  3. If you have problem with the Event Handler never being called it could be because you are using C++ instead of C. This gave me some problems for a while. just wrap it in an extern “C” like this:

    extern “C” {
    void USART1_IRQHandler(void){

    //Do something
    }
    }

    1. As I recall the interrupt flag is cleared by reading it from the USART status register as the code works properly without having to clear it explicitly.

      You can certainly clear that bit if you like to.

  4. I am using STM32F407 Discovery kit with motherboard of open407V-D. I have tested USART example coding . its working fine.I want to get the inputs from keyboard and that should display in hyperterminal and TFT LCD also through the controller. Kindly Can u give me the source code or any guidance ?

  5. Hi,
    great job commenting the lines of code. I modified this code for UART3 and works. Thank you
    Just want to point out the backslashes in the code:

    \r for CR and \n for LF

  6. Thank you so much for this code! it’s helpfull in my project ! but i have a problem because i want to send a number between 0..3.3 so can you help me to realize this!
    Thanks!

  7. Hi. I’m using your code, but i have problems with it. This is my first application with STM32F4, so I’m beginner with this board. I create new cler project in Atollic for STM32F4, replace main.c with your code, connect board to PC via this stick http://www.gme.cz/prevodnik-z-usb2-0-na-seriovy-port-com-premiumcord-ku2-232a-p759-971, program board, run terminal and reset board. Instead of “Init complete! Hello World!” I can only see something like this “[00]¶#Ząň“[12]I’äB“‘MożŢj9r[12]É[13]%’#’“/#$[00]“. Terminal is configured to BR9600, DB8, SB1, parity and flow control none.
    Where is the mistake? In some forums i read something about HSE and HSI, but it’s not helping, when I change HSE from default 25M to 8M value. HSI is 16M.

    Can you help me with this problem?

  8. void USART1_IRQHandler(void){

    // check if the USART1 receive interrupt flag was set
    if( USART_GetITStatus(USART1, USART_IT_RXNE) ){

    static uint8_t cnt = 0; // this counter is used to determine the string length
    char t = USART1->DR; // the character from the USART1 data register is saved in t

    /* check if the received character is not the LF character (used to determine end of string)
    * or the if the maximum string length has been been reached
    */
    if( (t != ‘n’) && (cnt < MAX_STRLEN) ){
    received_string[cnt] = t;
    cnt++;
    }
    else{ // otherwise reset the character counter and print the received string
    cnt = 0;
    USART_puts(USART1, received_string);
    }
    }

    this received function of you perhap have problem, it not true when receive folowwing index variable "cnt "..because each performing this function variable "cnt " = 0; it not increasing index in function received_tring[cnt]…???

  9. Hi Elia,

    Your work has to b really appreciated…..Was able to understand many things about the STM32F407.i need an help of yours.Is it possible for you to post the interfacing code between STM32F407 and mpu6050 and explain as you did for sample examples….

    Regards,
    sam123

    1. Hi Sam!

      I have the MPU6050 interface code on my GitHub look for rover2.

      It seems like the you aren’t including the stm32f4xx.h file and that’s why you’re getting those build errors.

      Cheers,
      Elia

  10. Hello Gents, I’ve got those errs:

    Type ‘uint32_t’ could not be resolved
    Multiple markers at this line
    - Symbol ‘RCC_APB2Periph_USART1′ could not be resolved
    Multiple markers at this line
    - Symbol ‘ENABLE’ could not be resolved
    - Symbol ‘RCC_AHB1Periph_GPIOB’ could not be resolved
    Multiple markers at this line
    - Symbol ‘GPIO_Pin_6′ could not be resolved
    - Symbol ‘GPIO_Pin_7′ could not be resolved
    Multiple markers at this line
    - Symbol ‘GPIO_AF_USART1′ could not be resolved
    - Type ‘GPIOB’ could not be resolved
    - Symbol ‘GPIO_PinSource6′ could not be resolved
    Type ‘GPIOB’ could not be resolved
    Multiple markers at this line
    - Symbol ‘GPIO_AF_USART1′ could not be resolved
    - Type ‘GPIOB’ could not be resolved
    - Symbol ‘GPIO_PinSource6′ could not be resolved
    Multiple markers at this line
    - Symbol ‘GPIO_AF_USART1′ could not be resolved
    - Type ‘GPIOB’ could not be resolved
    - Symbol ‘GPIO_PinSource7′ could not be resolved
    Type ‘USART_WordLength_8b’ could not be resolved
    Type ‘USART_StopBits_1′ could not be resolved
    Type ‘USART_Parity_No’ could not be resolved
    Type ‘USART_Mode_Tx’ could not be resolved
    Type ‘USART_Mode_Rx’ could not be resolved

    It seems that I missed one of the header files !!!
    Any idea will be appreciated …
    Regards …

    BTW;
    - I am using STM32F4 – DISCOVERY board.
    - Eclipse IDE

  11. HI guys..
    Nice efforts with STM32f4xx.. !!!!!!!.
    i have little problem with STM discovery board . I configured USART1 for sending string to PC. Program is working correct(No compilation errors / no calculation errors). but on the hyper terminal i am not getting proper string . Garbage values are echoing . please guide me. where i am wrong. …

    my code is..

    void init_USART1(uint32_t baudrate){

    GPIO_InitTypeDef GPIO_InitStruct; // this is for the GPIO pins used as TX and RX

    USART_InitTypeDef USART_InitStruct; // this is for the USART1 initilization

    NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // Pins 6 (TX) and 7 (RX) are used
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; // the pins are configured as alternate function so the USART peripheral has access to them
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // this defines the IO speed and has nothing to do with the baudrate!
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; // this defines the output type as push pull mode (as opposed to open drain)
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // this activates the pullup resistors on the IO pins
    GPIO_Init(GPIOB, &GPIO_InitStruct); // now all the values are passed to the GPIO_Init() function which sets the GPIO registers

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); //
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

    USART_InitStruct.USART_BaudRate = baudrate; // the baudrate is set to the value we passed into this init function
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard)
    USART_InitStruct.USART_StopBits = USART_StopBits_1; // we want 1 stop bit (standard)
    USART_InitStruct.USART_Parity = USART_Parity_No; // we don’t want a parity bit (standard)
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don’t want flow control (standard)
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
    USART_Init(USART1, &USART_InitStruct); // again all the properties are passed to the USART_Init function which takes care of all the bit setting

    // finally this enables the complete USART1 peripheral
    USART_Cmd(USART1, ENABLE);
    }

    void USART_puts(USART_TypeDef* USARTx, volatile char *s){

    while(*s){
    // wait until data register is empty
    while( !(USARTx->SR & 0×00000040) );
    USART_SendData(USARTx, *s);
    *s++;
    }
    }

  12. Hi
    i want to thank you for your tutorial :)

    i m working on a projet where i use the STM32F4 board as an interface between a PC (through RS232) and a numeric potentiometr (through I2C).
    in fact i will make an interface with LABVIEW which send the tension and the fréquency value to the STM32f4 and then the STM32 Send these values to a numeric potentiometr (I2C)
    PS : the trasmission of the tension value and the frequency is’nt at the same time.

    it ‘s my first experience with STM32 , and i really need help :)

    thank you :)

    1. That should be rather simple.

      First configure your USART so that you can send and receive data to/from the computer. Note that you will either have to use a level converter from RS232 to the 3V3 logic level of the STM32F4. I’d suggest using a USB to serial converter like an FT232 by FTDI (or a complete cable like this).

      Then configure one of the STM32F4′s I2C peripherals as an I2C master (look at my other tutorial). Then you should be able to process the messages from the PC and convert them to values that the potentiometer understands.

      Cheers,
      Elia

    1. I have never tried to use those high baudrates with the STM32F4, you might want to check the STM32F4 reference manual RM0090 from ST.

      I can certainly imagine that this is possible based on the fact that the APB2 frequency is 84MHz.

      Cheers,
      Elia

Leave a Reply

Your email address will not be published. Required fields are marked *


three × = 24