2025-08-04 6:30 AM - edited 2025-08-04 6:51 AM
I'm trying to set up a simple UART debug just for transmission from the the NUCLEO board.
uart.c
#include "uart.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 uart2_set_baudrate(uint32_t periph_clk, uint32_t baudrate);
static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate);
static void uart2_write(int ch);
int __io_putchar(int ch)
{
uart2_write(ch);
return ch;
}
void uart2_tx_init(void)
{
/*** Configure UART GPIO pin ***/
/* Enable clock access to GPIOA */
RCC->AHB1ENR |= GPIOAEN;
/* Set PA2 mode to alternate function mode */
GPIOA->MODER &=~ (1U<<4);
GPIOA->MODER |= (1U<<5);
/* Set PA2 alt function to UART tx (AF07) */
GPIOA->AFR[0] |= (1U<<8);
GPIOA->AFR[0] |= (1U<<9);
GPIOA->AFR[0] |= (1U<<10);
GPIOA->AFR[0] &=~ (1U<<11);
/*** Configure the UART ***/
/* Enable clock access to UART2 */
RCC->AHB1ENR |= UART2EN;
/* Configure baudrate */
uart2_set_baudrate(APB1_CLK, UART_BAUDRATE);
/* Configure transfer direction */
USART2->CR1 = CR1_TE;
/* Enable UART module */
USART2->CR1 |= CR1_UE;
}
static void uart2_write(int ch)
{
/* Ensure transmit data register empty */
while(!(USART2->SR & SR_TXE)){}
/* Write to the transmit data register */
USART2->DR = (ch & 0xFF);
}
static void uart2_set_baudrate(uint32_t periph_clk, uint32_t baudrate)
{
USART2->BRR = compute_uart_bd(periph_clk, baudrate);
}
static uint16_t compute_uart_bd(uint32_t periph_clk, uint32_t baudrate)
{
return ((periph_clk + (baudrate / 2U)) / baudrate);
}
uart.h
#ifndef __UART_H__
#define __UART_H__
#include "stm32f4xx.h"
#include <stdint.h>
void uart2_tx_init(void);
#endif
main.c
#include "stm32f4xx.h"
#include <stdio.h>
#include "uart.h"
static void psuedo_delay(int dly);
int main(void)
{
/* Initialize UART2 */
uart2_tx_init();
while(1)
{
printf("hi there!\n\r");
psuedo_delay(9000);
}
}
static void psuedo_delay(int dly)
{
for(int i = 0; i < dly; i++)
{
// DO NOTHING
}
}
If I'm right then PA2 has been set up as a UART TX with a Baud rate of 115200 & this is connected to the USB on the board.
But when I open a TeraTerm window & connect to the board I get nothing just a blank terminal window. What am I missing here?
2025-08-04 7:03 AM - edited 2025-08-04 7:04 AM
@NicRoberts wrote:If I'm right then PA2 has been set up as a UART TX
You can confirm that in the board's User Manual:
via: https://www.st.com/en/evaluation-tools/nucleo-f411re.html#documentation
You could also check the schematics:
via https://www.st.com/en/evaluation-tools/nucleo-f411re.html#cad-resources
@NicRoberts wrote:with a Baud rate of 115200
The baud rate can be whatever you like - it just has to match what you terminal is set to.
@NicRoberts wrote:But when I open a TeraTerm window & connect to the board I get nothing
Are you sure you're connecting to the correct COM port?
Do you have an Arduino shield (or any other hardware) connected which could interfere?
The manual describes various configuration options - has your board been modified?
Before adding the complications of printf, get it working with just direct UART output.
Have you tried one of the ready-made examples?
2025-08-04 8:09 AM
OK so the board is set up correctly for use of USART2 over the ST-Link/USB connection, I have used this before with no issue with the board but just double checked & it all looks good.
No other hardware connected.
Baud rate on the board & the terminal match (115200),
COM5: STMicroelectronics STLink Virtual COM Port selected from Tera Term pull down
.....and nothing.....
2025-08-04 8:22 AM
Flow control set to 'None' ?
Have you tried one of the ready-made examples?
Do you have a scope to see if anything is actually coming out of the TX pin?
2025-08-12 6:25 AM
Yes, flow control set to 'None'
Settings,
Baud : 115200
Data: 8 bit
Parity: none
Stop bits: 1
Flow control: none
Same config on TeraTerm
I'm trying to run some of the examples but the example selector keeps crashing CubeIDE
Got a simple HAL_UART_Transmit() code to work using printf() though, so I know the board is working now.
Still cant figure out why my bare metal implementation isn't working though.
2025-08-12 6:49 AM - edited 2025-08-12 6:50 AM
@NicRoberts wrote:Still cant figure out why my bare metal implementation isn't working though.
Step through the two examples; compare & contrast ...
Alternatively, start with the working HAL code, and pare that back to the minimum register operations ...
PS:
Or, of course, just use the HAL version as-is...
2025-08-12 9:44 AM - edited 2025-08-12 2:57 PM
Are we sure APB1 is DIV2 and not DIV1 ?
Perhaps send 'U' patterns and scope the PA2 pin to confirm activity and bit-timing
If you're using bare-metal registers for efficiency, this is NOT efficient, and the compiler can't optimize / fold this because the registers are deemed volatile. So 4x load-store operations, when one load and one store would be the goal.
GPIOA->AFR[0] |= (1U<<8);
GPIOA->AFR[0] |= (1U<<9);
GPIOA->AFR[0] |= (1U<<10);
GPIOA->AFR[0] &=~ (1U<<11);
2025-08-13 7:12 AM
Hi,
I'm relatively new to STM32 so I'm doing everything long hand to get a better understanding but I get the sense in what you're are saying.
As for APB1, when I check against the auto-generated code the RCC initialiser uses DIV1. I thought I'd configured for DIV1?