如何让 printf 在 STM32F103 上工作?

Posted

技术标签:

【中文标题】如何让 printf 在 STM32F103 上工作?【英文标题】:How to make printf work on 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 文件。 【参考方案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 会经过这个函数(当然取决于你使用什么库)。

【讨论】:

我在 STM32F072 上使用这种方法。详情在这里。 electronics.stackexchange.com/questions/206113/…

以上是关于如何让 printf 在 STM32F103 上工作?的主要内容,如果未能解决你的问题,请参考以下文章

Arduino STM32F103如何让pa11输出高电平?

USART3 初始化 STM32F103RB

stm32F103RCT6概括

STM32F103C8项目-我无法让HAL在自己的函数中工作

STM32F103RB BxCAN通信[关闭]

如何在 STM32F103C8T6 上进行裸机 LED 闪烁?