Makefile 如何使printf在STM32F103上工作?
我对STM32F103的世界是陌生的。我有一个STM32F103的演示代码,我正在使用arm none eabi编译它 我试着在谷歌上找到我能找到的,但到目前为止没有任何效果。我已经花了三天时间研究这个问题了 任何人都可以给我一个printf的演示代码,效果很好 我的makefile的一部分: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,--
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
。Standartvsprintf
非常重。通常很少使用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
。Standartvsprintf
非常重。通常使用的是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(),以使用主机的屏幕和键盘,而不是在目标计算机上使用屏幕和键盘