cancel
Showing results for 
Search instead for 
Did you mean: 

Cannot get printf via USART to work on STM32H745

AnaJade
Associate II

Hello!

As the title says, I haven't managed to get the printf function to work on my STM32H7 MCU. I am using the Nucleo-H745ZI-Q with STM32Cube MX and Keil. Here is the code I'm using:

#include "stdio.h"
 
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/* USER CODE END 0 */
 
/* USER CODE BEGIN 4 */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
	/* Place your implementation of fputc here */
	/* e.g. write a character to the USART3 and Loop until the end of transmission */
	HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
/* USER CODE END 4 */

When running the code in debug mode, I keep getting stuck on this line in the disassembly:

0x080006XX BEAB     BKPT         0xAB

After looking it up online, the most common answer I found was that I needed to add a retarget.c and retarget.h to my project. I tried looking for those files (specific for the H7) online but came up empty-handed. I think it is also worth mentioning that when I comment the line with the printf command, the rest of the code works fine.

I have used the exact same code (except with USART 2 instead of 3) on a Nucleo F446RE board and it worked perfectly fine. I have also read the printf via UART section in the STM32 debug application note (https://www.st.com/resource/en/application_note/dm00354244-stm32-microcontroller-debug-toolbox-stmicroelectronics.pdf) and can't find what I'm missing. Finally, I also tried to find an example code in the STM32H745 github repo (https://github.com/STMicroelectronics/STM32CubeH7/tree/master/Projects/NUCLEO-H745ZI-Q) but there wasn't one for the printf.

Any help would be greatly appreciated!

7 REPLIES 7
//****************************************************************************
// Hosting of stdio functionality through USART
//****************************************************************************
 
/* Implementation of putchar (also used by printf function to output data)    */
int SendChar(int ch)                    /* Write character to Serial Port     */
{
  ITM_SendChar(ch); // From core_cm4.c (Via SWO/SWV)
 
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF); 
 
  return(ch);
}
 
//****************************************************************************
 
#include <rt_misc.h>
 
#ifdef __ARM_CC // Only for v5 compiler, skip with v6
#pragma import(__use_no_semihosting_swi)
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
#endif // _ARM_CC
 
int fputc(int ch, FILE *f) { return (SendChar(ch)); }
 
int ferror(FILE *f)
{
  /* Your implementation of ferror */
  return EOF;
}
 
void _ttywrch(int ch) { SendChar(ch); }
 
void _sys_exit(int return_code)
{
label:  goto label;  /* endless loop */
}
 
//******************************************************************************

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Pavel A.
Evangelist III

> BKPT 0xAB

IIRC this means that you need to implement _write for GNU & libc_nano toolchain like CubeIDE.

The Cube should have generated syscalls.c file for you.

In Keil it relates to the "Semi-Hosting" interface, ie where the debugger spoofs Target side interfaces on the Host, say file access, or stdio type stuff.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Thank you for your reply! If I understand correctly, I would have to put the SendChar function in the User Code Begin 4 section in the main.c files for both cores. Where would the bottom portion of the code in your reply go? In a separate file or somewhere else in the main.c file? Do I need to put it in the main file for both cores?

I would generally put this at the end of main.c for the core(s) I want to debug. Sharing a single USART might work, content might interleave.

Each of the H7 cores has it's own debug logic, the SWV data has to go through a funnel. Not played with it in a while.

It would take some time to come up with a thoroughly tested example, but the BKPT issue you have is due to semi-hosting software interrupt code.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Alright! Thank you so much for your help, I really appreciate it :)

AnaJade
Associate II

I forgot about this post for a while, but I just thought I would share what I did to solve my problem!

The first thing I did was switch from Keil to STM32CubeIDE. I think the root of my problem was that I wasn't generating the code properly in CubeMX for Keil. Here are the main reasons why I think that was the case:

  • When loading up one of the examples provided by Keil, I noticed the project/file structure was different from what I got when generating my code with Cube MX
  • I also had to comment out the code that would boot one core after the other because I would just end up in the error handler every time
  • I could only use the CM7 core, and I wasn't able to access the CM4 core no matter what I did.

I'm by no means an expert on STM32 MCUs, so it was probably a user error on my part.

Now onto my solution for the printf:

I found this video on YouTube that was extremely helpful in setting everything up: https://youtu.be/jI1k6p-fduE. Once I was able to flash my code onto both cores, I simply followed the instructions on p. 70-71 of the debug application note (https://www.st.com/resource/en/application_note/dm00354244-stm32-microcontroller-debug-toolbox-stmicroelectronics.pdf). Here is my final code:

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
.
.
.
/* USER CODE BEGIN 4 */
/*
 * Function: putchar
 * Description:
 * Function to enable printf
 * To enable float print:
 * Project -> Properties -> C/C++ Build -> Settings
 * Tool Settings -> MCU GCC Linker -> Miscellaneous -> Other flags
 * Add flag: -u _printf_float
 */
int __io_putchar(int ch) {
	HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}

Hope this helps anyone reading this in the future!