【问题标题】:How to make printf work on STM32F103?如何让 printf 在 STM32F103 上工作?
【发布时间】:2017-02-01 12:12:50
【问题描述】:

我是 STM32F103 世界的新手。我有一个 STM32F103 的演示代码,我正在使用 arm-none-eabi 来编译它。

我尝试了可以​​在 Google 上找到的内容,但到目前为止没有任何效果。我已经花了三天时间解决这个问题。

谁能给我一个运行良好的 printf 演示代码?

我的 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

【问题讨论】:

  • “它不起作用”是什么意思?它是一个微控制器,您希望在哪里打印文本?通常在为嵌入式目标编程时,您会创建自己的print 使用 UART(或其他通信协议)的函数集。查找一些 UART 库,或者一些使用您的调试器的调试库(例如,如果您有 Segger 调试器,则在您的 PC 上使用带有 JLink Viewer 的 Segger RTT)
  • 我使用 arm-none-eabi- 来编译。那就是问题所在。我知道如何在 keil 项目中使用 printf。我使用 USART1 输出。您是否尝试使用此编译器构建 bin 文件。

标签: makefile microcontroller stm32 newlib


【解决方案1】:

通过包含以下链接器标志:

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

您似乎正在尝试使用所谓的 semihosting。您是在告诉链接器包含系统调用库。

Semihosting 是一种机制,它使运行在 ARM 目标上的代码能够通信并使用运行调试器的主机上的输入/输出工具。

这些工具的示例包括键盘输入、屏幕输出和磁盘 I/O。例如,您可以使用这种机制来启用 C 库中的函数,例如 printf() 和 scanf(),以使用主机的屏幕和键盘,而不是目标系统上的屏幕和键盘。

由于您使用开源工具进行 STM32 开发(Makefile 和 arm-none-eabi),我假设您也使用 openOCD 对微控制器进行编程。 openOCD 要求您也使用以下命令启用半主机:

arm semihosting enable

您可以在 openOCD 脚本的命令中确保终止配置阶段并使用“init”命令进入运行阶段。下面是一个 openOCD 脚本示例(适用于 STM32F103):

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

此处提到的将fputc() 函数重新定位到UART 接口的其他解决方案也将起作用并且可能会起作用。半主机将适用于所有最新的 ARM Cortex-M,但需要一些编译器和调试器配置(见上文)。将fputc() 函数重定向到 UART 接口将适用于任何编译器,但您必须检查每个板的引脚配置。

【讨论】:

    【解决方案2】:

    编写自己的printf 实现是一种选择,在我看来,这可能是最推荐的选择。从标准库实现中获得一些灵感并编写您自己的版本,以满足您的要求。通常,您要做的是,首先重新定位putc 函数以通过您的串行接口发送 char 。然后使用putc 自定义实现覆盖printf 方法。也许,一种非常简单的方法是通过递归调用putc 函数来逐字符发送字符串。

    最后但同样重要的是,您可以找到一些轻量级的printf 实现。这些轻量级实现提供的代码大小和功能集介于自定义编写的printf 函数和标准的printf 函数(又名野兽)之间。我最近尝试了这个Tiny Printf,对它在 ARM 内核上的内存占用和所需执行周期数方面的性能非常满意。

    -PS

    从我自己的writings 复制过来。

    【讨论】:

      【解决方案3】:

      Look there。这是来自glibprintf。但是你有微控制器。所以你应该写自己的printfvfprintf 会将结果返回到缓冲区,接下来你将从缓冲区发送数据到 UART。种

      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);
      }
      

      你也可以写自己的vsprintf。 Standart vsprintf 很重。通常会使用一小部分 vsprintf 功能。

      【讨论】:

        【解决方案4】:

        链接:How to retarget printf() on an STM32F10x?

        尝试像这样劫持 _write 函数:

        #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;
        }
        

        原来的 printf 会经过这个函数(当然取决于你使用什么库)。

        【讨论】:

        猜你喜欢
        • 2015-03-31
        • 2017-09-06
        • 2019-05-11
        • 1970-01-01
        • 2019-12-02
        • 2013-09-15
        • 2013-05-19
        • 2016-09-19
        • 2011-12-02
        相关资源
        最近更新 更多