Use UART/printf on STM BlackPill with stm32cube framework

As good as debugging is via the ST-LINK, sometimes it’s nice to be able to print some logging to help follow the code. From what I understand, you can’t get any UART through the ST-LINK and instead you need a USB to serial adapter which I have.

I have the USB to serial adapter RX connected to A9 (TX) and TX of the adapter to A10 (RX). I can confirm this works as I am able to upload the program through the serial connection. However, I can’t seem to send anything from the blackpill so I’m guessing my code is wrong, but debugging isn’t telling me much and without being able to print anything it’s hard to know whats wrong. I’ve based my code off examples I could find online:


#include <stdio.h>
#include <stm32f4xx.h>
#include <stm32f4xx_hal.h>

#define USARTx USART1
#define USARTx_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE();
#define USARTx_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define USARTx_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()

#define USARTx_FORCE_RESET() __HAL_RCC_USART1_FORCE_RESET()
#define USARTx_RELEASE_RESET() __HAL_RCC_USART1_RELEASE_RESET()

#define USARTx_TX_PIN GPIO_PIN_9
#define USARTx_TX_GPIO_PORT GPIOA
#define USARTx_TX_AF GPIO_AF7_USART1
#define USARTx_RX_PIN GPIO_PIN_10
#define USARTx_RX_GPIO_PORT GPIOA
#define USARTx_RX_AF GPIO_AF7_USART1


UART_HandleTypeDef UartHandle;

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

PUTCHAR_PROTOTYPE {
    HAL_UART_Transmit(&UartHandle, (uint8_t *)&ch, 1, 0xFFFF);

    return ch;
}

void UART_Init(void) {
    UartHandle.Instance = USARTx;

    UartHandle.Init.BaudRate = 9600;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits = UART_STOPBITS_1;
    UartHandle.Init.Parity = UART_PARITY_ODD;
    UartHandle.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;

    if (HAL_UART_Init(&UartHandle) != HAL_OK) {
        Error_Handler();
    }
}

void HAL_UART_MspInit(UART_HandleTypeDef *huart) {
    GPIO_InitTypeDef GPIO_InitStruct;

    USARTx_TX_GPIO_CLK_ENABLE();
    USARTx_RX_GPIO_CLK_ENABLE();

    USARTx_CLK_ENABLE();

    GPIO_InitStruct.Pin = USARTx_TX_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
    GPIO_InitStruct.Alternate = USARTx_TX_AF;

    HAL_GPIO_Init(USARTx_TX_GPIO_PORT, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = USARTx_RX_PIN;
    GPIO_InitStruct.Alternate = USARTx_RX_AF;

    HAL_GPIO_Init(USARTx_RX_GPIO_PORT, &GPIO_InitStruct);
}

void HAL_UART_MspDeInit(UART_HandleTypeDef *huart) {
    USARTx_FORCE_RESET();
    USARTx_RELEASE_RESET();

    HAL_GPIO_DeInit(USARTx_TX_GPIO_PORT, USARTx_TX_PIN);
    HAL_GPIO_DeInit(USARTx_RX_GPIO_PORT, USARTx_RX_PIN);
}

int main(void) {
    HAL_Init();
    UART_Init();

    printf("Testing!\n");

    while (1) {
    }
}

/* excluded methods below like Error_Handler and HardFault_Handler */

I know it successfully initialises UART but I just don’t see anything received when I am monitoring it (whether that’s in vscode or something like putty).

Does anyone know what could be wrong?

P.S. Is it required that I have to put it into DFU mode every time I want to upload via serial, or is there some pin/config I can set so I can program it without that? Not a big issue, just curious.

Are you sure you don’t want “UART_PARITY_NONE”? Odd / even is not widely used. If you don’t set monitor_parity to also O, you won’t get the correct output, but garbled stuff.

I’ve more often than not seen the _write() function getting implemented, not __io_putchar. Per this, try this instead

int _write(int file, char *ptr, int len) {
   (void) file; // can be 0 for stdout or 1 for stderr, unused.
   HAL_UART_Transmit(&UartHandle, (uint8_t *)ptr, len, 0xFFFF);
   return len;
}

Also if you put it like this, make sure you’re pressing the reset button of the board. If you just use “Uplaod and Monitor”, the output may have already been gone by unnoticed.

Thank you so much! I did make the changes all in one, but I think what probably fixed it was using _write instead. This is odd because as far as I can remember, all the examples I saw reimplemented putchar.
The reason I had parity as ODD is because that’s what the stm32cubeprogrammer set it to.

Also, yeah, I have been making sure to press reset in case it was printed before connections.

Thanks again!