STM32
直播中

刘悌耀

7年用户 1069经验值
私信 关注
[问答]

怎样去解决STM32中不能使用printf()函数的问题呢

为什么STM32中不能使用printf()函数n?
怎样去解决STM32中不能使用printf()函数的问题呢?

回帖(1)

马聪

2021-12-1 15:53:55
简单地说:想在mdk中用printf,同时重定义fputc函数和避免使用半主机模式,
标准库函数的默认输出是显示器,要实现在串口设备或LCD输出,必须重定义库函数的与输出设备相关的函数.
例如:printf输出到串口,需要将fputc里面的输出接口调用(主机),方法如下:
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker) ->Libraries->Small printf
     设置为 'Yes') 调用 __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
/* 将你的 fputc 实现放在这里 */
/* 例如写一个字符到 USART */
USART_SendData(USART1, (uint8_t) ch);
/* 循环直到传输结束 */
而 (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
返回 ch;
}
因printf()之类的函数,使用了半主机模式。使用标准库会导致程序无法运行,以下是解决方法:
方法1.使用微库,因为使用微库的话,不会使用半主机模式。
方法2.仍可使用标准库,在主程序添加如下代码:
#pragma import(__use_no_semihosting)
_sys_exit(int x)
{
x = x;
}
struct __FILE
{
int 句柄;
/* 无论你在这里需要什么。如果您使用的唯一文件是 */
/* 使用 printf() 进行调试的标准输出,则不需要文件处理 */
/*。*/
};
/* FILE 在 stdio.h 中是 typedef' d。*/
FILE __stdout;
如果使用的是MDK,请在工程属性的“Target“-》”Code Generation“中勾选”Use MicroLIB;今天参考william hill官网 ,微库可以很好的解决这个问题。
2.另一种方法:(其实是大同小异)  
需要添加以下代码
(william hill官网 里应该有完整介绍这个帖子的,但是我没搜到,可能是沉了。)
#pragma import(__use_no_semihosting)  
/***** ****************************************************** ************************  
*标准库需要的支持函数  
******************** ****************************************************** ********/  
struct __FILE  
{  
int 句柄;  
/* 无论你在这里需要什么。如果您使用的唯一文件是 */  
/* 使用 printf() 进行调试的标准输出,则不需要文件处理 */  
/*。*/  
};  
/* FILE 在 stdio.h 中是 typedef' d。*/  
文件 __stdout;  

///   
///定义_sys_exit()定义使用半主机模式  
////
  
///   
///   
_sys_exit(int x)  
{  
x = x;  
}  



int fputc(int ch, FILE *f)
{
    //USART_SendData(USART1, (u8) ch);
    USART1->DR = (u8) 通道;
     
    /* 循环直到传输结束 */
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
    {
    }

    return ch;
}
semihosting 的作用,介绍如下
Semihosting 是ARM 目标将输入/输出请求
从应用程序代码传送到运行调试器的主机的一种机制 。这种机制可能是
例如,用于允许 C 库中的函数(如 printf() 和 scanf())使用主机的屏幕和键盘,而不是在目标系统上使用屏幕和键盘。
这很有用,因为开发硬件通常不具备
最终系统的所有输入和 输出设施。半主机允许主机提供这些功能。
半主机由一组定义的软件中断 (SWI) 操作实现。
应用程序调用适当的 SWI,然后调试代理处理 SWI
异常。调试代理提供与主机所需的通信。
在许多情况下,半主机 SWI 将由库函数中的代码调用。应用程序还可以直接调用半主机 SWI。有关在 ARM C 库中支持半主机的更多信息,请参阅 ADS 编译器和库指南中的 C 库描述。
按我的理解,这个模式是分解的,通过仿真器,使用主机的输入输出转换转换自己的,想要自己的目标没有输出口也能把printf传到电脑上。由于这个模式改变了printf()等的实现方式,输入输出输出驱动的外设了,所以只重定义fputc不起作用。

用代码关闭此模式后,需要同时更新一下__stdout和__stdin的定义,所以有后面的语句。

以上仅为个人理解,如有错误请指正。


另外,勾选microlib中之后,也许编译的时候就不把开启半主机的文件包进去了,所以没事。

ç库函数重定向:
。用户能定义自己的ç语言库函数,连接器在连接时自动使用这些新的功能函数这个过程叫做重定向C语言库,如下图所示。
举例,用户有一个I/O设备(如UART)。本来库函数fputc()是把字符输出到调试器控制窗口中去的,但把输出设备改成UART端口,这样你,所有基于fputc()函数的printf()系列函数输出都被成功到UART端口上去了。
下面是实现fputc()生成的一个例子:
externvoidsendchar(char*ch);
intfputc(intch,FILE*f)
{/*egwriteacharactertoanUART*/
chartempch=ch;
发送字符(&tempch);
返回;
}
这个例子简单地将输入字符重新定向到另一个函数sendchar(),sendchar()假定是个另外定义的串口输出函数。在这里,fputc()函数就似乎目标硬件和标准Ç库函数之间的一个抽象层。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分