I have a STM32F446RE on a Nucleo F446RE, and I'm trying to learn how to program the UART to write over the Virtual COM Port (connected to USART2).
I'm able to download someone else's code, modify it to use USART2, and see the messages in my Linux console. This shows that the Virtual COM Port is set up properly on the device, ST-LINK, and Linux.
Yet, my code writing to it doesn't work. My code does not freeze, because the LED continues to blink. But no chars appear on the serial port, and the stats (provided by tio
) say 0 bytes received
.
I've tried opening it in gdb, but then it does appear to freeze.
How can I debug this? Key portions of the code are below.
nt uart_init() {
// This Nucleo board wires USART2 to PA2 (TX) and PA3 (RX)
RCC->AHB1ENR |= BIT(0); // Enable GPIOA
RCC->APB1ENR |= BIT(17); // Enable USART2
GPIOA->MODER |= 2*BIT(2) + 1; // PA2 to AF7
GPIOA->MODER &= ~(2*BIT(2));
GPIOA->AFR[0] |= 0b0111 << (2*4); // Each pin's AF is controlled by a nibble
GPIOA->AFR[0] &= ~(0b1000UL << (2*4));
GPIOA->MODER |= 2*BIT(3) + 1; // PA3 to AF7
GPIOA->MODER &= ~(2*BIT(3));
GPIOA->AFR[0] |= 0b0111 << (3*4);
GPIOA->AFR[0] &= ~(0b1000UL << (3*4));
// We want 115,200 baud, and since CPU Hz is 16*10^6, and we sample each bit 16 times (default),
// we need to sample approximately every 8.68 cycles (16*10^6 / 115200 = 8.68...)
// The BRR is a fixed point num, with lowest 4 bits fractional
USART2->BRR = 0b10001011; // 0b1000 = 8, 0b1011 = 0.68
USART2->CR1 |= 0b1100; // Enable TX and RX, leave other defaults
USART2->CR1 |= BIT(13); // Enable the USART
return 0;
}
int dbg(char* msg) {
int cnt = 0;
for (char* p = msg; *p; ++p) {
while (!(USART2->SR & BIT(7))) asm(""); // Spin until the char buffer is empty
USART2->DR= *p;
++cnt;
}
return cnt;
}
int main(void) {
...
uart_init();
for (;;) {
dbg("Strobe!\r\n");
GPIOA->ODR |= BIT(5);
spin(100);
dbg("Clear.\r\n");
GPIOA->ODR &= ~BIT(5);
spin(1000);
}
...
Update
#define BIT(x) (1UL << (x))
GPIOA->MODER |= 2*BIT(2) + 1;
is absolutely horrible at expressing your intent in human-readable terms. Use the manufacturer-provided C header files which #define macros for all of these peripheral registers. \$\endgroup\$