STM32——关于printf重定向到串口的问题

本文介绍了在MDK环境中如何正确使用printf函数的方法,包括重定义fputc函数以实现输出重定向,以及如何避免使用semihosting模式。此外,还提供了具体的代码示例。
简单地说:想在mdk 中用printf,需要同时重定义fputc函数和避免使用semihosting(半主机模式), 
标准库函数的默认输出设备是显示器,要实现在串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数. 
例如:printf输出到串口,需要将fputc里面的输出指向串口(重定向),方法如下: 
#ifdef __GNUC__ 
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf 
     set to 'Yes') calls __io_putchar() */ 
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch) 
#else 
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) 
#endif /* __GNUC__ */ 
PUTCHAR_PROTOTYPE 

 /* Place your implementation of fputc here */ 
 /* e.g. write a character to the USART */ 
 USART_SendData(USART1, (uint8_t) ch); 
 /* Loop until the end of transmission */ 
 while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); 
 return ch; 

因printf()之类的函数,使用了半主机模式。使用标准库会导致程序无法运行,以下是解决方法: 
方法1.使用微库,因为使用微库的话,不会使用半主机模式. 
方法2.仍然使用标准库,在主程序添加下面代码: 
#pragma import(__use_no_semihosting)  
_sys_exit(int x)  
{  
x = x;  
}  
struct __FILE  
{  
int handle;  
/* Whatever you require here. If the only file you are using is */  
/* standard output using printf() for debugging, no file handling */  
/* is required. */  
};  
/* FILE is typedef’ d in stdio.h. */  
FILE __stdout; 
如果使用的是MDK,请在工程属性的“Target“-》”Code Generation“中勾选”Use MicroLIB;今天参考了一下论坛,使用微库可以很好的解决这个问题。 
2.另一种方法:(其实大同小异)   
需要添加以下代码  
(论坛里应该有完整介绍这个的帖子,但是我没搜到,也许是沉了。) 
#pragma import(__use_no_semihosting)   
/******************************************************************************   
*标准库需要的支持函数   
******************************************************************************/   
struct __FILE   
{   
int handle;   
/* Whatever you require here. If the only file you are using is */   
/* standard output using printf() for debugging, no file handling */   
/* is required. */   
};   
/* FILE is typedef’ d in stdio.h. */   
FILE __stdout;  
/// <summary>   
/// 定义_sys_exit()以避免使用半主机模式   
/// </summary>   
/// <param name="x"></param>   
/// <returns></returns>   
_sys_exit(int x)   
{   
x = x;   
}  
 
int fputc(int ch, FILE *f)  
{  
    //USART_SendData(USART1, (u8) ch);  
    USART1->DR = (u8) ch;  
      
    /* Loop until the end of transmission */  
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) 
    {  
    } 
    return ch;  
}  
semihosting的作用,介绍如下  
Semihosting is a mechanism for ARM targets to communicate input/output requests  
from application code to a host computer running a debugger. This mechanism could be  
used, for example, to allow functions in the C library, such as printf() and scanf(), to use the screen and keyboard of the host rather than having a screen and keyboard on the target system.  
This is useful because development hardware often does not have all the input and  
output facilities of the final system. Semihosting allows the host computer to provide these facilities.  
Semihosting is implemented by a set of defined software interrupt (SWI) operations.  
The application invokes the appropriate SWI and the debug agent then handles the SWI  
exception. The debug agent provides the required communication with the host.  
In many cases, the semihosting SWI will be invoked by code within library functions. The application can also invoke the semihosting SWI directly. Refer to the C library descriptions in the ADS Compilers and Libraries Guide for more information on support for semihosting in the ARM C library.  
 按我的理解,这个模式是用来调试的,通过仿真器,使用主机的输入输出代替单片机自己的,也就是说即便单片机没有输出口也能printf到电脑上。反过来,由于这个模式更改了printf()等的实现方式,输入输出就不走单片机的外设了,所以只重定义fputc不起作用。 
用代码关闭此模式后,需要同时更新一下__stdout 和__stdin 的定义,所以有后面的语句。 
以上仅为个人理解,如有错误请指正。 


另外,勾选microlib之后,也许编译的时候就不把开启semihosting的文件包进去了,所以没事。
C库函数重定向: 
用户能定义自己的C语言库函数,连接器在连接时自动使用这些新的功能函数。这个过程叫做重定向C语言库函数,如下图所示。 
举例来说,用户有一个I/O设备(如UART)。本来库函数fputc()是把字符输出到调试器控制窗口中去的,但用户把输出设备改成了UART端口,这样一来,所有基于fputc()函数的printf()系列函数输出都被重定向到UART端口上去了。 
下面是实现fputc()重定向的一个例子: 
externvoidsendchar(char*ch); 
intfputc(intch,FILE*f) 
{/*e.g.writeacharactertoanUART*/ 
chartempch=ch; 
sendchar(&tempch); 
returnch; 
} 
这个例子简单地将输入字符重新定向到另一个函数sendchar(),sendchar()假定是个另外定义的串口输出函数。在这里,fputc()就似乎目标硬件和标准C库函数之间的一个抽象层。
 
第二个问题,路径:D:\Keil3.80\ARM\Examples\ST\STM32F10xFWLib\Examples
STM32中实现printf重定向串口,主要是通过重定向`fputc`函数来实现,以下为具体的实现方法: ### 实现思路 在C语言标准库中,`printf`函数最终会调用`fputc`函数将字符输出到标准输出设备。在STM32中,要将`printf`的输出重定向串口,就需要重新实现`fputc`函数,让其通过串口发送字符。 ### 代码示例 以下是一个基于STM32的示例代码: ```c #include "stm32f10x.h" // Device header #include <stdio.h> // 串口初始化函数 void Serial_Init(void) { // 使能GPIOA和USART1时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); // 配置USART1 Tx (PA9)为复用推挽输出 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置USART1 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); // 使能USART1 USART_Cmd(USART1, ENABLE); } // 重定向fputc函数 int fputc(int ch, FILE *f) { // 等待发送缓冲区为空 while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 发送字符 USART_SendData(USART1, (uint8_t)ch); return ch; } // 主函数 int main(void) { Serial_Init(); printf("串口打印数据\r\n"); // 串口发送printf打印的格式化字符串 while (1) { } } ``` ### 代码解释 1. **串口初始化**:在`Serial_Init`函数中,对串口的GPIO引脚和串口本身进行初始化配置,包括时钟使能、引脚模式设置、波特率设置等。 2. **重定向`fputc`函数**:在`fputc`函数中,通过`USART_GetFlagStatus`函数等待串口发送缓冲区为空,然后使用`USART_SendData`函数将字符发送出去。 3. **主函数**:在`main`函数中,调用`Serial_Init`函数初始化串口,然后使用`printf`函数输出格式化字符串。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值