STM32 Programming. Part 16: USART
Posted: 17 Oct 2023, 07:09
Introduction
There are 3 USART modules built into the stm32f103c8 microcontroller, which can be configured quite flexibly to suit your needs. Among the features are the following:
- full duplex;
- the ability to work in half-duplex mode on a single wire;
- fractional setting of the USART rate divider. This allows you to adjust the transmission rate with zero error on "inconvenient" for USART quartz resonators;
- customizable transmit data size of 8 or 9 bits;
- configurable number of stop bits;
- possibility to work with DMA;
- various accessories for working with IR port (IrDA), with smart cards, etc.
Within the framework of this article we will touch only on such concepts as the format of transmitted data (number of bits, parity, stop bits) and interrupts USART. Everything related to hardware flow control, IR port, smart cards, synchronous mode, etc. we will omit. Also we will not study the operation of USART together with DMA.
First, let's take a look at the USART block diagram
Does it look terrifying? Let's go over the basics.
Shift registers and data registers
The main part of any serial data interface is the shift register. In USARTs there are 2 of them: one for transmitting (Transmit Shift Register) and one for receiving (Receive Shift Register). Each of these shift registers has its own data buffer register: Transmit Data Register (TDR) and Receive Data Register (RDR).
In order to send a word of data to the USART (intentionally I say "word" rather than "byte" because the word size can be 8 or 9 bits), it must be loaded into the Transmit Data Register TDR. Once written to the TDR, this value will "fail" into the transmitter shift register and the transmit process will be started. It is worth noting that once the value from the TDR has been sent to the shift register, more data can be loaded into the TDR, which will be there waiting for the end of the transfer from the shift register. Thus, we have a buffer for 2 words: one is in the shift register, the other in the TDR, which allows you to transmit data on USART a continuous stream without pauses between neighboring transmissions.
In a similar way is performed and the reception of data. After the data is received by the shift register of the receiver, they fall into the RDR register, and the receiver is immediately ready to receive the next word of data. Here we also have a buffer for 2 words, one in the shift register, the other in the RDR. Thus, we have the ability to receive a continuous stream of data without pauses between neighboring transmissions.
In STM32F103C8 registers TDR and RDR are not directly accessible programmatically. For this purpose, the DR (Data register) is used. During a write operation to the DR, the written value falls into the TDR register, and when reading from the DR will read the value of RDR. In other words, receiving and transmitting data from the firmware side will look like accessing the same DR register.
Flags and interrupts
In the module USART microcontrollers STM32 microcontrollers have a sufficient number of various flags and interrupts, with which we can very conveniently realize the process of data exchange both on interrupts and the method of polling registers.
Let's briefly familiarize ourselves with some interesting flags. Let's consider the process of data transfer. If the transmitter register TDR is empty and the next word can be written into it, the special flag TXE (Transmit data register empty) will be set to 1 in the status register. It should be noted that setting the TXE flag to 1 does not mean that the data transfer process has ended. TXE only indicates that the next value can be written to the transmitter register.
In order to make sure that the data transmission on the Tx leg is complete, there is another flag in the status register: TC (Transmission complete). It is set only if the data transmission is complete and there is no next data in the transmitter register to load into the shift register (TXE flag is set). The TC flag can be useful when implementing an RS485 interface where the direction of the interface driver can only be switched after the data transfer is complete.
Let's move on to receiving data. There is a flag RXNE (Read data register not empty) in the status register. It is set to 1 if there is new data in the receiver buffer.
In addition, by setting one of the above flags to 1, it is possible to enable USART interrupt generation, which is very useful when transmitting data through interrupts.
Data transmission formats
The USART supports setting the following baud rates:
- number of data bits (8 or 9)
- parity (none, even, odd)
- number of stop bits (0.5, 1, 1.5, 2).
- start bit; 8 data bits; stop bit
- start bit; 7 data bits; 1 parity bit; stop bit
Transmission speed
Those who have worked with AVR microcontrollers, know about special resonator frequencies, convenient for working with UART-om. In microcontrollers STM32 USART has a trickier implementation, which allows you to get zero deviation of the baud rate from standard values when working from the most common quartz, for example, 8 MHz (the table below shows the bus frequencies using PLL):
This is achieved by setting the baud rate division ratio fractionally. The BRR division ratio setting register consists of 2 parts: DIV_Mantissa and DIV_Fraction. Both of these values form a fixed point number: VAL = DIV_Mantissa,DIV_Fraction. However, the formulas and rules for calculating the DIV_Mantissa and DIV_Fraction values presented in the manual are very confusing, so simplifying these calculations we get the following formula for calculating the BRR value:
BRR = (uint16_t)(BUS_FREQ / BAUD)
BUS_FREQ - USART module clocking frequency (bus frequency)
BAUD - baud rate.
By the way, in the manual for other STM32 microcontrollers, for example, STM32F030, the formula for calculation is given immediately in a digestible form:
I/O ports
Let's find out which GPIO pins the USARTs in the STM32F103C8 microcontroller are connected to. We open the Datasheet and find Table 5: Medium-density STM32F103xx pin definitions. It contains the following:
For USART1:
TX: PA9, Remap PB6
RX: PA10, Remap PB7
For USART2:
- TX: PA2
- RX: PA3
- TX: PB10
- RX: PB11
Let us now refer to section 9.1.11 GPIO configurations for device peripherals in the Reference manual to understand how to configure the I/O ports for USART operation:
For the simplest full duplex mode without hardware flow control we need only TX and RX pins. The other pins can be used as normal GPIO ports.
So, the setup will be as follows:
TX: alternate function mode, push-pull or open collector output type
RX: input with no pull-up or pull-up type
USART registers
Status register (USART_SR) - status register
TXE: Transmitter register empty. This bit is set in hardware when the contents of the TDR transmitter register (TDR is not directly accessible from the program, but it is accessed when writing to USART_DR) have been transferred to the shift register. If the TXEIE interrupt enable bit was set in USART_CR1, a USART interrupt request is generated at this point. TXE is reset when the value is written to the USART_DR data register.
TC: transmission complete. This bit is set by hardware if the UART has completed data transfer, with the TXE bit set to one. This bit can be useful for RS485 interface implementations to switch the direction of the RS485 driver. If the TCIE bit is set in the USART_CR1 register, a USART interrupt is generated when the TC bit is set. The TC bit is reset by the following program sequence: reading the USART_SR register followed by a write to the USART_DR register. In addition, the TC bit can be reset by writing to it the value 0, but it is recommended to do this only in the mode of joint work with DMA.
RXNE: the receiver register is not empty. This bit is set to one when the contents of the receiver shift register are transferred to the USART data register. If the RXNEIE bit in the USART_CR1 register is set, a USART interrupt request is generated. The RXNE bit is reset when the USART_DR data register is read. In addition, RXNE can be reset by writing a 0 to it, but it is recommended to do this only in DMA co-operative mode.
ORE: overflow error. Set to 1 if the data in the receiver shift register is ready to be transferred to the data register, but the RXNE bit is set. In other words, we have already received the next byte on USART, but have not yet read the previous byte. If the RXNEIE flag is set in the USART_CR1 register, a USART interrupt request is generated. The ORE bit is reset by the following program sequence: reading the USART_SR register followed by reading the USART_DR register.
Data register (USART_DR) - data register
DR[8:0]: data. This register contains 2 shadow registers: TDR and RDR. When reading from DR the value of the receiver data register RDR will be read, when writing to DR the value will be written to the transmitter data register TDR. If parity control is used (the PCE bit in the USART_CR1 register is set to 1), then when writing to the DR the value of the high bit will be ignored, as it will be replaced by the parity bit during transmission. When receiving with parity enabled, the high bit will contain the parity bit.
Baud rate register (USART_BRR) - USART baud rate register
The BRR register contains a division factor that sets the baud rate of USART.
BRR = (uint16_t)(BUS_FREQ / BAUD)
where BUS_FREQ is the frequency of the bus, on which hangs this USART
BAUD - the desired baud rate.
Control register 1 (USART_CR1) - configuration register 1
UE: enable USART.
- 0: USART predividers and outputs are disabled
- 1: USART enabled
- 0: 1 start bit, 8 data bits, n stop bits
- 1: 1 start bit, 9 data bits, n stop bits
PS: parity check type selection. This bit selects the parity option when the PCE bit is set. Set and cleared by software.
- 0: Even
- 1: Odd
TCIE: enable transmit end interrupts. If 1, a USART interrupt request is generated when the TC flag in the USART_SR register is set.
RXNEIE: enable interrupts when data appears in the receiver register. If 1, a USART interrupt request is generated when the RXNE or ORE flag is set in the USART_SR register.
TE: enable the USART transmitter
RE: enable USART receiver Control register 2 (USART_CR2) - configuration register 2
STOP: number of STOP bits
- 00: 1 stop bit
- 01: 0.5 stop bits
- 10: 2 stop bits
- 11: 1.5 stop bits
Conclusion
In order not to clutter the reader's head with unnecessary information, I did not give a description of each flag and each register USART, as for most tasks it is not necessary. Perhaps, in the future I will add descriptions of bits responsible for joint work with DMA.