STM32使用printf重定向

最近用STM32CubeMX创建了一个demo工程,在调试过程中,printf打印功能一直不能正常打印,检查工程中也已将fputc函数进行了实现。奇怪的是用JTAG进行调试时打印恢复了正常。最后发现问题的原因是没有勾选MDK使用微库的配置,使用微库的话 ,不会使用半主机模式。
printf之类的函数,使用了半主机模式,MDK上开启半主机模式-需要SWO线(换言之,需要使用JTAG接线)当目标板脱离仿真器(jlink/ulink)单独运行时,不能使用半主机模式。

半主机是ARM的一种目标机制,它使得在ARM目标上跑的代码,如果主机电脑运行了调试器,那么该代码可以使用该主机电脑的输入输出设备。
这点非常重要,因为开发初期,可能开发者根本不知道该 ARM 器件上有什么输入输出设备,而半主基机制使得你不用知道ARM器件的外设,利用主机电脑的外设就可以实现输入输出调试。
一般单片机需要独立运行,使用时应去掉仿真器,把printf函数通过单片机的外设来实现,例如通过开发板的串口,lcd或者sd卡。首先要关掉半主机机制。然后再将输入输出重定向到ARM器件上,如 printf 和 scanf,需要重写fputc和fgetc函数。

printf 定义在 头文件中,如下:

int printf(const char *format, ...);

printf 函数根据 format 字符串给出的格式打印输出到 stdout(标准输出)中,当然,printf 函数是不会一个字符一个字符去输出,它会调用更底层的 I/O 函数:fputc去逐个字符打印
fputc 也定义于头文件 中,如下:

int fputc(int ch, FILE *stream);

fputc 函数写入字符 ch 到给定输出流 stream,printf函数在调用该函数时,会向stream参数传入stdout从而打印数据到标准输出。
那么,要实现printf打印到串口就变得非常简单了,只需要重新定义fputc函数,在fputc的函数中将数据通过串口发送,称之为:fputc重定向或者printf重定向。

在MDK中使用重定向的方式:

使用微库

  • 勾选Use MicroLib
  • 重定义fputc到串口
#include 

int fputc(int ch, FILE *stream)
{
    while((USART1->ISR & UART_FLAG_TC) == 0);
    USART1->TDR = (uint8_t) ch;
    return ch;
}

使用标准库

系统 IO 一般指的是 Linux/Unix 系统调用中关于 I/O 操作的统称,其中包括 open、read、write、close 等操作。
与系统 IO 对应还有标准 IO,标准 IO 是 ISO 标准中 C 语言标准定义的 IO 访问接口,例如 fprintf/fgets 等 C 语言标准中定义的文件访问接口。

在 Linux 系统中 open/read/write 等函数的底层实现是通过系统调用访问的,在 STM32 的裸机中没有操作系统,更没有这些系统调用。
但是我们可以用一种其他的方式去实现这些系统 IO,而不需要操作系统。

/* 为确保没有从 C 库链接使用半主机的函数,如果仍然链接了使用半主机的函数,则链接器会报告错误 */   
#pragma import(__use_no_semihosting) 

/* 标准库需要的支持函数 */        
struct __FILE {
    int handle; 
}; 

/* FILE 在stdio.h文件 */    
FILE __stdout;    

/* 定义_sys_exit()以避免使用半主机模式 */                       
void _sys_exit(int x)                    
{  
    x = x;
}         

int fputc(int ch, FILE *stream)
{
    while((USART1->ISR & UART_FLAG_TC) == 0);
    USART1->TDR = (uint8_t) ch;
    return ch;
}

microlib 进行了高度优化以使代码变得很小。 它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。某些库函数的运行速度也比较慢,例如,memcpy()。

不同的编译器对于C库的底层实现机制是不同的,所以上面两种在MDK中的实现方法,在使用Gcc编译器的时候是不可行的。

GCC使用标准库重定向

#include 

int _write(int fd, char *ptr, int len)  
{  
    HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF);
    return len;
}

与microlib 方式不同的是,GCC重定向是一次写入多个字符,而fputc 中是一次写入一个字符。

在需要使用printf 频繁写入大量字符时,建议使用半主机模式或者使用GCC 编译器。GCC使用打印时要注意行缓冲。

你可能感兴趣的:(STM32,stm32,单片机,arm)