本文所使用的方法与代码参考自正点原子,如果想要详细了解这方面的知识,请阅读正点原子官方提供的文档。
一、背景
在开发STM32应用时,将一些信息通过串口打印到电脑上是常用的调试手段。C语言标准库中的printf函数是我们常用的打印函数。但是在STM32应用下一般无法直接使用这个函数,正点原子给出的解释如下,有兴趣可以详细了解一下。
标准库下的 printf 为调试属性的函数,如果直接使用,会使单片机进入半主机模式(semihosting),这是一种调试模式,直接下载代码后出现程序无法运行,但是在连接调试器进行 Debug 时程序反而能正常工作的情况。半主机是 ARM 目标的一种机制,用于将输入/输出请求从应用程序代码通信到运行调试器的主机。例如,此机制可用于允许 C 库中的函数(如 printf()和 scanf())使用主机的屏幕和键盘,而不是在目标系统上设置屏幕和键盘。这很有用,因为开发硬件通常不具有最终系统的所有输入和输出设备,如屏幕、键盘等。半主机是通过一组定义好的软件指令(如 SVC)SVC 指令(以前称为 SWI 指令)来实现的,这些指令通过程序控制生成异常。应用程序调用相应的半主机调用,然后调试代理处理该异常。调试代理(这里的调试代理是仿真器)提供与主机之间的必需通信。也就是说使用半主机模式必须使用仿真器调试。
如果想在独立环境下运行调试功能的函数,我们这里是 printf,printf 对字符 ch 处理后写入文件 f,最后使用 fputc 将文件 f 输出到显示设备。对于 PC 端的设备,fputc 通过复杂的源码,最终把字符显示到屏幕上。那我们需要做的,就是把 printf 调用的 fputc 函数重新实现,重定向fputc 的输出,同时避免进入半主模式。
目前想要在SMT32上使用printf有两种方法:
- 通过代码取消ARM的半主机工作模式,并重定向printf函数
- 使用微库MicroLib,并重定向printf函数。
由于微库裁剪了许多标准库的功能,如果注重功能完整性建议使用第一种方法。
二、取消ARM的半主机工作模式
添加stdio.h头文件,并在程序中加入以下代码段即可(代码引自正点原子)
#include <stdio.h>
#if 1
#if (__ARMCC_VERSION >= 6010050)
__asm(".global __use_no_semihosting\n\t");
__asm(".global __ARM_use_no_argv \n\t");
#else
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
#endif
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
FILE __stdout;
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0);
USART1->DR = (uint8_t)ch;
return ch;
}
#endif
上面代码段使用的是串口1(USART1),可根据实际使用情况调整。
三、使用微库MicroLib
直接在Keil中的如下界面勾选使用微库
并添加如下代码段重定向fputc
#include <stdio.h>
int fputc(int ch, FILE *f)
{
while ((USART1->ISR & 0X40) == 0);
USART1->TDR = (uint8_t)ch;
return ch;
}
微库由于裁剪掉了一些功能,有着如下特点:
- 微库会优化代码空间,但会降低某些程序的执行效率(比如: memcpy()),效率换空间
- 微库不支持浮点运算,所以在有FPU单元的MCU上,使用MicroLIB并开启FPU会让程序死机或跑飞
- 微库不支持C++,在使用C++开发MCU时不能使用MicroLib
- 微库不支持操作系统函数
更详细的讲解可参见博文STM32程序不运行与MicroLIB讲解
四、应用
采用了上面任意一种方法设置后,我们便可在程序中使用printf,并通过串口打印在电脑端的串口助手上。
printf("123\r\n");
HAL_Delay(500);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)