×
单片机 > 单片机程序设计 > 详情

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

发布时间:2020-08-24 发布时间:
|

原子哥的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

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()


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


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM 汇编的必知必会