1. Open block diagram in Datasheet
We will use USART2 interface because it is connected to the same bus (APB1) with the power interface. That means we can communicate directly with the computer via USB without having a chip to convert from UART to USB.
2. Write UART send function to computer USB
Open Alternate function mapping in datasheet. We can see that creating UART TX involving PA2 and AF07 (will mention later)
- First we need to setup PA2 port to alternate function type as table above
- Enable clock to GPIOA
RCC->AHB1ENR |= GPIOAEN;
- Set PA2 to alternate mode As the table, we need to set bit 5 and 4 of pin 2 to value 10 If we want to receive UART, as the alternate table, we need to set PA3 to alternate mode
GPIOA->MODER &=~(1U<<4);
GPIOA->MODER |= (1U<<5);
GPIOA->MODER &=~(1U<<6);
GPIOA->MODER |= (1U<<7);
- Set PA2 alternate type to UART_TX As mentioned above, besides PA2, we need to modify AF07. AFRL is a low register for alternate function There are 2 alternate function registers, and we are dealing with PA2, so we use AFR[0] for low to locate to AFRL2, and we will modify bit 8, 9, 10, 11. To set to AF7, use value 0111
GPIOA->AFR[0] |= (1U<<8);
GPIOA->AFR[0] |= (1U<<9);
GPIOA->AFR[0] |= (1U<<10);
GPIOA->AFR[0] &= ~(1U<<11);
Set PA3
GPIOA->AFR[0] |= (1U<<12);
GPIOA->AFR[0] |= (1U<<13);
GPIOA->AFR[0] |= (1U<<14);
GPIOA->AFR[0] &= ~(1U<<15);
- Set clock to USART2 port
#define UART2EN (1U<<17)
RCC->APB1ENR |= UART2EN;
- Set direction of UART to sending We need to set TE mode
#define CR1_TE (1U<<3)
USART2->CR1 = CR1_TE;
Then we need to enable USART in bit 13
Remember that we only enable after we set up all the configuration.
#define CR1_UE (1U<<13)
USART2->CR1 |= CR1_UE;
USART2->CR1 = CR1_TE | CR1_RE; //if we want to receive UART
Set baudrate
We will set frequency as 16Mhz and baudrate as 115200.
And calculate as formula below
Then we need to set value to BRR register
Write data to pin
Before sending, we need to check if send status is ok or not
#define SR_TXE (1U<<7)
USART2->SR & SR_TXE
And we can only write one character per write
USART2->DR = (character & 0xFF);
Source code
#include "stm32f4xx.h"
#include <stdint.h>
#include <stdio.h>
#define GPIOAEN (1U<<0)
#define UART2EN (1U<<17)
#define CR1_TE (1U<<3)
#define CR1_UE (1U<<13)
#define SR_TXE (1U<<7)
#define SYS_FREQ 16000000
#define APB1_CLK SYS_FREQ
#define UART_BAUDRATE 115200
static void uart_set_baudrate(USART_TypeDef *USARTx, uint32_t PeriphClk, uint32_t BaudRate);
static uint16_t compute_uart_bd(uint32_t PeriphClk,uint32_t BaudRate);
void uart2_tx_init(void);
void uart2_write(int ch);
int __io_putchar(int ch){
uart2_write(ch);
return ch;
}
int main(void){
uart2_tx_init();
while(1){
uart2_write('O');
//printf("hello\n\r");
}
return 0;
}
void uart2_tx_init(void){
//Enable GPIOA
RCC->AHB1ENR |= GPIOAEN;
//Set PA2 to alternate function mode
GPIOA->MODER &=~(1U<<4);
GPIOA->MODER |= (1U<<5);
//Set PA2 alternate function type to UART_TX (AF07)
GPIOA->AFR[0] |= (1U<<8);
GPIOA->AFR[0] |= (1U<<9);
GPIOA->AFR[0] |= (1U<<10);
GPIOA->AFR[0] &= ~(1U<<11);
//Enable clock access to uart2
RCC->APB1ENR |= UART2EN;
//Configure baudrate
uart_set_baudrate(USART2, APB1_CLK, UART_BAUDRATE);
//Configure transfer direct
USART2->CR1 = CR1_TE;
//Enable uart module
USART2->CR1 |= CR1_UE;
}
void uart2_write(int ch){
while( !(USART2->SR & SR_TXE)){}
USART2->DR = (ch & 0xFF);
}
static void uart_set_baudrate(USART_TypeDef *USARTx, uint32_t PeriphClk, uint32_t BaudRate){
USARTx->BRR |= compute_uart_bd(PeriphClk, BaudRate);
}
static uint16_t compute_uart_bd(uint32_t PeriphClk,uint32_t BaudRate){
return ((PeriphClk + (BaudRate/2U))/BaudRate);
}