How to make printf work on STM32F103?

2019-03-06 10:38发布

问题:

I am new to the world of STM32F103. I have a demo code for STM32F103 and I am using arm-none-eabi to compile it.

I tried what I could find on Google, but nothing worked so far. I have already spent three days on the problem.

Anyone can give me a demo code for printf which works well?

Part of my makefile:

CFLAG   = -mcpu=$(CPU) -mthumb -Wall -fdump-rtl-expand -specs=nano.specs --specs=rdimon.specs   -Wl,--start-group -lgcc -lc -lm -lrdimon -Wl,--end-group
LDFLAG  = -mcpu=$(CPU) -T ./stm32_flash.ld -specs=nano.specs --specs=rdimon.specs   -Wl,--start-group -lgcc -lc -lm -lrdimon -Wl,--end-group

回答1:

Writing an own printf implementation is an option, and probably the most recommended option according to me. Get some inspiration from the standard library implementation and write your own version, only to cater your requirements. In general, what you have to do is, first retarget a putc function to send char s through your serial interface. Then override the printf method by using the putc custom implementation. Perhaps, a very simple approach is sending the string character-wise by recursive calls for putc function.

Last but not least, you can find some lightweight printf implementations. The code size and the set of features offered by these lightweight implementations lie in between the custom written printf function and the stock standard printf function (aka the beast). I have recently tried this Tiny Printf and very pleased with its performance on an ARM core in terms of memory footprint and the number of execution cycles required.

-PS

Copied from my own writings sometime back.



回答2:

Link: How to retarget printf() on an STM32F10x?

Try hijacking the _write function like so:

#define STDOUT_FILENO   1
#define STDERR_FILENO   2

int _write(int file, char *ptr, int len)
{
    switch (file)
    {
    case STDOUT_FILENO: /*stdout*/
        // Send the string somewhere
        break;
    case STDERR_FILENO: /* stderr */
        // Send the string somewhere
        break;
    default:
        return -1;
    }
    return len;
}

The original printf will go through this function (depending on what libs you use of course).



回答3:

Look there. This is printf from glib. But you have microcontroller. So you sould write own printf, where vfprintf will return result into buffer and next you will send data from buffer to UART. Kind of

void printf( const char * format, ... )
{
  char buffer[256];
  va_list args;
  va_start (args, format);
  vsprintf (buffer,format, args);
  send_via_USART1 (buffer);
  va_end (args);
}

Also you can write own vsprintf. Standart vsprintf is very heavy. Usually little part of vsprintf features is used.



回答4:

By including the following linker flags:

LDFLAGS   += --specs=rdimon.specs -lc -lrdimon

it looks like you are trying to use what is called semihosting. You are telling the linker to include system call libraries.

Semihosting is a mechanism that enables code running on an ARM target to communicate and use the Input/Output facilities on a host computer that is running a debugger.

Examples of these facilities include keyboard input, screen output, and disk I/O. For example, you can use this mechanism to enable functions in the C library, such as printf() and scanf(), to use the screen and keyboard of the host instead of having a screen and keyboard on the target system.

Since you are using openSource tools for your STM32 development (Makefile and arm-none-eabi), I am assuming you are also using openOCD to program your microcontroller. openOCD requires you to enable semihosting as well using the following command:

arm semihosting enable

You can at the command to your openOCD script making sure you terminate the configuration stage and enter the run stage with the 'init' command. Below is an example of an openOCD script (adapted for STM32F103):

 source [find target/stm32f1x.cfg]
 init
 arm semihosting enable

Other solutions mentioned here where your retarget the fputc() function to a UART interface will also work and might. Semihosting will work on all recent ARM Cortex-M but will require some compiler & debugger configuration (see above). Retargeting the fputc() function to a UART interface will work with any compiler but you will have to check your pin configurations for every board.