-------------------------------------------------------------------------------------------------------------------
一、MDK中使用printf出问题的原因
如在MDK中使用printf,需要同时重定义fputc函数和避免使用semihosting(半主机模式), 标准库函数的默认输出设备是电脑显示器,要实现在串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数。
例如:printf输出到串口,需要将fputc里面的输出指向串口(重定向),方法如下:
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
return ch;
}
-------------------------------------------------------------------------------------------------------------------
二、解决方法
因为printf()之类的函数,使用了半主机模式。使用标准库会导致程序无法运行,以下是解决方法:
1、使用微库
因为使用微库的话,不会使用半主机模式。
MDK在工程属性的“Target“-》”Code Generation“中勾选” Use MicroLIB。
-----------------------------------------------------------
2、添加代码
在MDK中用printf,需要同时重定义fputc函数和避免使用semihosting(半主机模式)。
#pragma import(__use_no_semihosting)
struct __FILE
{undefined
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;
// 定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{undefined
x = x;
}
//定义了“stdio.h”文件,下面的语句不需要
int fputc(int ch, FILE *f)
{undefined
//USART_SendData(USART1, (u8) ch);
USART1->DR = (u8) ch;
// Loop until the end of transmission
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET)
{undefined
}
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()重定向的一个例子:
extern void sendchar(char*ch);
intfputc(intch,FILE*f)
{
chartempch=ch;
sendchar(&tempch);
returnch;
}
这个例子简单地将输入字符重新定向到另一个函数sendchar(),sendchar()假定是个另外定义的串口输出函数。在这里,fputc()就似乎是目标硬件和标准C库函数之间的一个抽象层。
第二个问题,路径:D:Keil3.80ARMExamplesSTSTM32F10xFWLibExamples
-------------------------------------------------------------------------------------------------------------------
四、ARM调用printf()总结
为了实现ARM能调用输出函数,把fputc重新定向到ARM的串口了(在标准C库中printf 和scanf是被默认定向到电脑显示器的),禁止使用半主机模式,就是在Link的时候不调用标准C库的函数,但是可能由于在自己的子C文件中用到了某个函数,又没有重定义,它就会去标准库里调用,这样就造成了矛盾,一边禁止使用半主机模式,一边去C库里调用函数,或者如下例中输出到了USART3中, 实际是需要输出到USART1,所以会输出死机。
执行printf就会出错,如下:
-------------------------------------------------------------------------------------------------------------------
上一篇:STM32以太网程序解析一
下一篇:STM32使用systick实现精确延时
设计资源 培训 开发板 精华推荐
- 具有 400mA 突发钳位、fSW = 1MHz 同步降压型稳压器的 LTC3621EDCB 2.5V Vout 的典型应用
- AD5330 并行接口、单电压输出、8 位 DAC 的典型应用
- 具有过压关断功能的 LT1161CSW 受保护四通道 1A 汽车电磁阀驱动器的典型应用电路
- 使用 TC7117 模数转换器获得 2V 满量程推荐组件值的典型应用
- 使用 Analog Devices 的 LTC1149 的参考设计
- MC33071DR2G 基本同相放大器典型应用
- NCP451FCT2GEVB:具有自动放电路径评估板的 3.0 A 受控负载开关
- 使用 ROHM Semiconductor 的 BD5336 的参考设计
- EVAL-RS485FDEBZ,用于 ADM489 全双工 RS-485 收发器的评估板,采用 14 引脚 SOIC 封装
- 用于无线基站的 3.3V DC 至 DC 单路输出电源