The STM32F407VG contains 4 USARTS and 2 UARTS.
USART
The USART can be programmed in asynchronous mode to work as a UART.
In this case lets use USART3.
Pins
USART3_TX is connected to PD8
USART3_RX is connected to PD9
These are defined as the alternate functions of those pins.
To setup these pins in USART mode, we must first program the I/O pin multiplexer.
GPIO I/O pin multiplexer
Only one peripheral’s alternate function can be connected to an I/O pin at any time, so that peripherals aren’t in conflict. That means that pins can only perform one function.
There are the GPIOx_AFRL registers for pins 0-7 and GPIOx_AFRH for pins 8-15.
Since we want PD8 and PD9, need to program only on GPIOD_AFRH.
When the board resets, all I/Os are connected to system’s alternate function by default (AF0).
The peripheral alternate functions are mapped from AF1 to AF13.
The steps are:
- Configure the desired I/O as an alternate function in the GPIOx_MODER register
- Select the type, pull-up/pull-down and output speed via the GPIOx_OTYPER, GPIOx_PUPDR and GPIOx_OSPEEDR registers
- Connect the I/O to the desired AFx in the GPIOx_AFRL or GPIOx_AFRH
For PD8 to be in USART3_TX mode, need to set AF7 on PortD.
Similarly, for PD9 to be in USART3_RX mode, need to set AF7 on PortD.
Clock Enable
To use the USART peripheral, we must connect the clock to it.
The Reset and clock control registers can be written to to enable it.
We are interested in the AHB1 clock control registers since USART3 is connected to the AHB1 bus.
The RCC APB1 peripheral reset register (RCC_APB1RSTR) is used to reset the peripheral back to its power on state and is fully controlled by software. We don’t need to touch this probably.
The RCC APB1 peripheral clock enable register (RCC_APB1ENR) needs to have bit 18 to be set to 1 to enable USART3 (USART3EN).
Oversampling
For Rx, USART uses a technique called oversampling. For a single received bit, the USART can sample either 8 or 16 times. There is a tradeoff between speed and accuracy, which is to be decided depending on the clock source.
To detect the level of a bit, 8 or 16 samples are taken. Then the middle 3 bits are evaluated.
To then evaluate the logic level, there are two options.
Majority vote – take the majority vote out of the 3 bits and if any of the bits disagree with each other, then noise has been detected and the noise found (NF) bit is set.
Single sample – just take the middle bit (this increases the tolerance of the receiver?)
When OVER8 = 0, then oversampling by 16
When OVER8 = 1, then oversampling by 8
Baud rate setup
To program the baud rate for both Tx and Rx, we need to program the correct values of the mantissa and fraction values of USARTDIV. Then we program the baud rate register (USART_BRR).
The formulae is given below.
USARTDIV = (f_ck * 10 ^ 6) / (8 * baud)
Suppose we want to use 9600 baud rate with clock speed at 16MHz and oversampling by 8.
Then USARTDIV = (16 * 10 ^ 6) / (8 * 9600) = 208.3333
Since the fractional part is represented by 4 bits for a value between 0 and 1, we must find the closest multiple of 1/16 for the fractional part.
The closest value is 208.375 which gives an actual baud rate of 9598 which has an error of 0.02%.
The mantissa would be 208 (0xD0) in this case. The fractional is 0.375 *16 = 6 (0x6).
Transmission
The data format for transmission can either by 8 or 9 bits, with the last bit possibly being a parity bit.
To configure 8 bits, M bit is set to 0 and 9 bits M bit is set to 1, in the USART control register 1 (USART_CR1).
Lets use 8 bits for this application.
We also need to program the number of stop bits, by default this is 1 stop bit and should be left as is for this application.
The procedure to send data is (without DMA):
1. Enable the USART by writing UE bit in USART_CR1
2. Program the M bit in USART_CR1 to define the word length
3. Program the number of stop bits in USART_CR2
4. Select the desire baud rate using the USART_BRR register
5. Set the TE bit in USART_CR1 to send an idle frame as first transmission
6. Write the data to send in the USART_DR register and repeat for each data. The TXE bit will be cleared until the data in USART_DR has been moved to the shift register and is in transmission. The TXE bit will be set to notify software that it is able to write to USART_DR for the next data.
The TXE bit can also be set to raise an interrupt. This can be looked into later.
7. After the last frame is sent, the TC flag will be set by hardware to indicate that the transmission is complete.
Steps 1, 2, 3 and 4 can be done once at setup.
Steps 5, 6, 7 can be done each time we want to send some data.
The code for these so far can be found at https://github.com/bdelta/stm32f407-uart-driver