The STM32 Discovery is equipped with three USARTs. USART stands for Universal Synchronous Asynchronous Receiver Transmitter. The USARTs on the STM32 support a wide range of serial protocols, the usual asynchronous ones, plus things like IrDA, SPI etc.
In this article I am going to describe how the STM32 Discovery could be connected to the serial port (RS232) of a PC, and how the STM32 has to be programmed to communicate with the PC. We will cover both: sending and receiving.
Since the STM32 works on 3.3V levels, a level shifting component is needed to connect the USART of the STM32 to a PC serial port. Most PCs today come without an native RS232 port, thus an USB to serial converter is also needed. For the level shifting, and the USB to serial conversation, we use the FTDI based FT232RL in its breadboard variant from sparkfun. For more details and a very basic usage example see this article.
- Toolchain for building binaries for the STM32 is in place
- Toolchain for flashing binaries to the STM32 is in place
- Basic understanding of the STM32 (GPIOs)
- Basic understanding of the FT232RL
- Basic understanding of how to program the STM32 in C
3. Parts Needed
- STM32 Discovery board
- FT232RL (breadboard version from sparkfun)
- A breadboard plus some breadboard wires
4. The Setup
In this example, we simply connect the RX of the STM32 USART1 to the TX of the FT232RL, and the TX of the USART1 to the RX of the FT232RL. The program we load onto the STM32 then waits until a character is received, takes that character, wraps square braces around it, and echoes back the received char in braces. E.g. if the character “A” is received, “[A]” is send back. Thus sending “Hello” will echo “[H][e][l][l][o]”. Every time a character is received, the build in green LED on the STM32 is flashed, every time the echo is send, the build in blue LED is flashed. On the PC side we use a serial terminal program (with local echo turned off) to send and receive characters to and from the STM32.
5. Wiring the Hardware
The wiring we do here is pretty simple. Since we do not use hardware flow control or any other fancy stuff, only the RX/TX pins of the STM32 and the FT232RL are needed. The table below shows the pin outs of the three USARTS available on the STM32 Discovery:
As one can see in the figure above, all the USARTs are fully equipped. The main difference is, that only USART1 is connected to the high-speed bus APB2. All the other USARTs are connected to APB1.
In this tutorial, we are going to use USART1. Thus we wire up the following:
- PA9 from the STM32 (USART1_TX) gets connected to pin RXD of the FT232RL
- PA10 from the STM32 (USART1_RX) gets connected to pin TXD of the FT232RL
- GND of the STM32 gets connected to GND of the FT232RL
Thats it! Double check your wiring, connect the SMT32 and the FT232RL to your USB bus, and as always, be sensitive about funny smells comming from your components.
6. Writing the Software
For this tutorial we are going to program the STM32 on the “bare metal”. The only things we use are some header files from the STM32 library to give us the needed register definitions.
To use an USART, the board has to be initialized properly first. In detail, we need to setup the following:
- Enable the system clock for USART1
- Setup the RX/TX pins for USART1
- Set the baudrate for USART1
- Enable USART1 and its RX- and TX-component
Since the presets for flow-control and stop-bit are already set to N and 1 by default, we don’t need to configure them for this example.
To send/receive data, the DR (Data Register) register of the USART is used. If you read the register, you get the last byte received. If you write to the register, the byte written is transmitted. Now the tricky part is to know, if new data could be written to the register, or if new data could be read.
One (bad) solution for the sending thing is to use a delay after writing to the register, and if the delay is big enough, we can be sure that the data is transmitted. the bad solution for the receiving part is to poll the register until the byte read differs from the last byte read.
A better solution is to use the SR (Status Register) register of the USART1. The status register has a bit indicating if the data is transmitted as well as a bit indicating if new data was received. As soon one reads the DR register, the data received flag is automatically reset. This is exactly the solution we are going to use in out code.
An alternative would be to enable the USART1 interrupt for data received. By doing so, an interrupt handler is called each time new data was received.
In the next sub paragraphs I am going to detail how the setup, the sending and the receiving could be programmed in C. We will therefore look at the following operations:
- board_init: all the initialization is done her
- usart_rec: receive data blocking
- usart_snd: send data blocking
At least we need to enable the system clock for USART1 and the GPIOA block (since RX/TX are on that block). For using the build in LEDs, we also enable the GPIOC. This is done by setting the RCC register for the APB2 bus (both, GPIOA and USART1 are connected to this bus):
RCC->APB2ENR = 0 // Turn on USART1 | RCC_APB2ENR_USART1EN // Turn on IO Port A | RCC_APB2ENR_IOPAEN;
Now we are going to setup the GPIOs for PA9 (TX) and PA10 (RX). The TX pin must be setup as an output-pin with alternate function (USART1_TX). We configure this pin as output pin in push-pull mode at a frequency of 50 Mhz. Puzzling all the bits for the config register together, we end up in 0xB for this configuration. P10 (RX) must be setup as input. We use floating input for that since the connected FT232RL takes care of the pull-up/down stuff. To enable floating input for a pin, 0x4 must be written to the config register. In the code this would look like this:
// Put PA9 (TX) to alternate function output push-pull at 50 MHz // Put PA10 (RX) to floating input GPIOA->CRH = 0x000004B0;
Note: we use CHR since the pins 8-15 are in the H register, where as pins 0-7 are in the L part.
Next we need to setup the USART. First we are going to define the baud rate to use. This is done by using the USARTs BRR register. Since the STM32 has a fractional generator any baud rate must be derived from the systems bus clock. The value to store in the BRR register simply could by calculated by this simple formula:
USART_BRR = [BUS_FREQUENCY_in_Hz] / [BAUD_RATE]
To setup the USART for 38400 bauds, we do the following in our code:
// Configure BRR by deviding the bus clock with the baud rate USART1->BRR = 8000000/38400;
The last thing we have to do is to enable the UART, the TX unit of the URAT and the RX unit of the USART. This is done by setting the corresponding flags on th config register 1 of the UART1:
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
Thats it. Your USART is now ready to communicate with 38400 bauds at 8 data bits, no flow control and one stop-bit.
To read in received data, all we have to do is to wait until RXNE (RX not empty) bit is set in the status register SR of the USART1. As soon as we realized, this bit is set, we read out the data from the DR (data register) of the USART. Since the DR register is 32 bit, but only 9 Bits are used, we apply the mask 0x1FF to the incoming data to make sure we receive no additional garbage. As soon as we read the DR register, the USART resets the RXNE flag automatically. In our program it would look like this:
/* Wait until the data is ready to be received. */ while ((USART1->SR & USART_SR_RXNE) == 0); // read RX data, combine with DR mask (we only accept a max of 9 Bits) return USART1->DR & 0x1FF;
To send data, we simply assign it to the DR register. Then we wait until the TXE (TX empty) flag in the SR register is set to be sure the data is transmitted. In the code it looks like this:
USART1->DR = data; // wait for TX while ((USART1->SR & USART_SR_TXE) == 0);
- Example source code, including schematic: stm32_usart_loopback_example.tar.gz