Makefile 如何使printf在STM32F103上工作?

Makefile 如何使printf在STM32F103上工作?,makefile,microcontroller,stm32,newlib,Makefile,Microcontroller,Stm32,Newlib,我对STM32F103的世界是陌生的。我有一个STM32F103的演示代码,我正在使用arm none eabi编译它 我试着在谷歌上找到我能找到的,但到目前为止没有任何效果。我已经花了三天时间研究这个问题了 任何人都可以给我一个printf的演示代码,效果很好 我的makefile的一部分: CFLAG = -mcpu=$(CPU) -mthumb -Wall -fdump-rtl-expand -specs=nano.specs --specs=rdimon.specs -Wl,--

我对STM32F103的世界是陌生的。我有一个STM32F103的演示代码,我正在使用arm none eabi编译它

我试着在谷歌上找到我能找到的,但到目前为止没有任何效果。我已经花了三天时间研究这个问题了

任何人都可以给我一个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
。这是
glib
中的
printf
。但你有微控制器。所以您应该编写自己的
printf
,其中
vfprintf
将结果返回到缓冲区,然后将数据从缓冲区发送到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
功能的一部分。

。这是
glib
中的
printf
。但你有微控制器。所以您应该编写自己的
printf
,其中
vfprintf
将结果返回到缓冲区,然后将数据从缓冲区发送到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
功能的一小部分。

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

最后,您可以找到一些轻量级的
printf
实现。这些轻量级实现提供的代码大小和功能集介于自定义编写的
printf
函数和库存标准
printf
函数(也称beast)之间。我最近尝试过这一点,并且非常满意它在ARM内核上的性能,包括内存占用和所需的执行周期数

-PS


从我自己的某个时候复制的。

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

最后,您可以找到一些轻量级的
printf
实现。这些轻量级实现提供的代码大小和功能集介于自定义编写的
printf
函数和库存标准
printf
函数(也称beast)之间。我最近尝试过这一点,并且非常满意它在ARM内核上的性能,包括内存占用和所需的执行周期数

-PS

从我自己的网站上复制回来。

链接:

尝试劫持_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将执行此功能(当然取决于您使用的libs)。

链接:

尝试劫持_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将执行此功能(当然取决于您使用的lib)。

包括以下链接器标志:

LDFLAGS   += --specs=rdimon.specs -lc -lrdimon
看起来您正在尝试使用所谓的半托管。您正在告诉链接器包含系统调用库

半宿主是一种机制,使ARM目标上运行的代码能够在运行调试器的主机上通信和使用输入/输出设施

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

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

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


这里提到的将
fputc()
函数重定位到UART接口的其他解决方案也可以工作,并且可能会成功。半托管将在所有最新的ARM Cortex-M上工作,但需要一些编译器和调试器配置(见上文)。将
fputc()
函数重新定位到UART接口将适用于任何编译器,但您必须检查每个电路板的pin配置。

包括以下链接器标志:

LDFLAGS   += --specs=rdimon.specs -lc -lrdimon
看起来您正在尝试使用所谓的半托管。您正在告诉链接器包含系统调用库

半宿主是一种机制,使ARM目标上运行的代码能够在运行调试器的主机上通信和使用输入/输出设施

这些功能的示例包括键盘输入、屏幕输出和磁盘I/O。例如,您可以使用此机制启用C库中的函数,如printf()和scanf(),以使用主机的屏幕和键盘,而不是在目标计算机上使用屏幕和键盘