STM32--对原子哥USART实验中printf重定向进行分析

原子哥的USART代码中,有一部分感觉看得不是很懂

#if 1
#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;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
    x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
    return ch;
}
#endif 

参考了相关资料:
RealView 编译工具 开发指南
RealView 编译工具 库和浮点支持指南
下面分析代码:

1. #if 1

相当于一段废话,就是永远判断为正

2. #pragma import(__use_no_semihosting)

要理解这一句话,要先了解半主机的概念以及如何构建用于非半主机环境的应用程序

2.1 半主机的概念

半主机是用于 ARM 目标的一种机制,可将来自应用程序代码的输入/输出请求
传送至运行调试器的主机。 例如,使用此机制可以启用 C 库中的函数,如
printf() 和 scanf(),来使用主机的屏幕和键盘,而不是在目标系统上配备屏幕
和键盘。

这种机制很有用,因为开发时使用的硬件通常没有最终系统的所有输入和输出
设备。 半主机可让主机来提供这些设备。

2.2构建用于非半主机环境的应用程序

如果不想使用任何半主机功能,则必须删除对半主机函数的所有调用,或者使
用非半主机函数重新实现它们。
要构建不使用半主机功能的应用程序,请执行以下操作:

2.2.1 创建源文件以实现与目标相关的功能。

例如,使用半主机调用或依赖于目标内存映射的函数。

2.2.2 将 __use_no_semihosting 符号添加到源文件中。

要确保应用程序中不包含任何使用半主机的函数,请使用以下任一方法:
• 汇编语言中的 IMPORT __use_no_semihosting
• C 中的 #pragma import(__use_no_semihosting)。



IMPORT __use_no_semihosting 只需添加到一个汇编源文件中。 同样, #pragma
import(__use_no_semihosting) 只需添加到一个 C 源文件中。 不需要将这些插入添
加到每个源文件中。

2.2.3 将新对象与应用程序进行链接。

2.2.4 在创建与目标相关的应用程序时使用新配置。

从2.2.2可以看出,这句话确保应用程序不包含任何使用半主机的函数

3. 重新实现printf()

printf()这一高级函数通过调用与目标相关的函数来执行,重新实现printf(),需要重新定义低级函数

重新实现有此需要的函

__FILE: 文件结构

__stdout: __FILE类型的标准输出对象

fputc():将一个字符输出到文件中

ferror():返回在文件 I/O 期间累积的错误状态

3.1 demo()

《RealView编译工具-库和浮点支持指南》中的demo

#include <stdio.h>
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;
int fputc(int ch, FILE *f)
{
/* Your implementation of fputc(). */
return ch;
}
int ferror(FILE *f)
{
/* Your implementation of ferror(). */
return 0;
}
void test(void)
{
printf( “Hello world\n” );
}

3.2 __FILE; __stdout; fputc()

通过之前的分析,可以理解原子哥关于 __FILE; __stdout的代码是什么意思了

重定义了 __FILE; __stdout;

3.3 重定义fputc()

3.3.1 状态寄存器(USART_SR)

位7:

TXE:发送数据寄存器空 (Transmit data register empty)

当TDR寄存器中的数据被硬件转移到移位寄存器的时候,该位被硬件置位。如果USART_CR1
寄存器中的TXEIE为1,则产生中断。对USART_DR的写操作,将该位清零。

0:数据还没有被转移到移位寄存器;

1:数据已经被转移到移位寄存器。

3.3.2 数据寄存器(USART_DR)

位31:9:

保留位,硬件强制为0

位8:0

DR[8:0]:数据值 (Data value)

包含了发送或接收的数据。由于它是由两个寄存器组成的,一个给发送用(TDR),一个给接收
用(RDR),该寄存器兼具读和写的功能。

3.3.3 fputc()

int fputc(int ch, FILE *f)
{      
    while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
    USART1->DR = (u8) ch;      
    return ch;
}

相当于先等待之前的字符通过USART1发送完成后,通过USART1将一个字符发出

3.3 __sys_exit()

__sys_exit()是库函数退出函数,所有从库中的退出最后都会调用__sys_exit()

在这里重新定义,避免使用半主机模式

  • 9
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值