STM32工程上printf()函数的方法实现

针对stm32的串口编程,可以通过usart1向计算机的串口调试助手打印数据,或者接收计算机串口调试助手的数据。
下面,我们可以实现stm32工程上的printf()函数了,方便用于程序开发中调试信息的打印。
方法1:使用microlib库
1.1 keil-mdk中的use microlib选项
在mdk开发环境中,
microlib是缺省c库的备选库,它可装入少量内存中,与嵌入式应用程序配合使用,且这些应用程序不在操作系统中运行。microlib进行了高度优化以使代码变得很小,功能比缺省c库少,不具备某些iso c特性,部分库函数的运行速度也比较慢,如内存拷贝函数memcpy()。   microlib与缺省c库之间的主要差异如下:
(1) microlib不符合 iso c库标准。不支持某些iso特性,并且其他特性具有的功能也较少。
(2) microlib不符合ieee 754二进制浮点算法标准。
(3) microlib进行了高度优化以使代码变得很小。
(4) 无法对区域设置进行配置。缺省c区域设置是唯一可用的区域设置。
(5) 不能将main() 声明为使用参数,并且不能返回内容。
(6) 不支持stdio,但未缓冲的stdin、stdout和stderr除外。
(7) microlib对c99函数提供有限的支持。 (8) microlib不支持操作系统函数。 (9) microlib不支持与位置无关的代码。
(10) microlib不提供互斥锁来防止非线程安全的代码。 (11) microlib不支持宽字符或多字节字符串。
(12) 与stdlib不同,microlib不支持可选择的单或双区内存模型。microlib只提供双区内存模型,即单独的堆栈和堆区。
microlib提供了一个有限的stdio子系统,它仅支持未缓冲的stdin、stdout和stderr,那么也就是说勾选了use microlib选项后,在代码工程中就可以使用printf()函数咯?然而事实并非如此,这样直接使用printf()函数,其打印的字符串最终不知道打印到何处。我们要做的是将调试信息打印到usart1中,所以需要对printf()函数所依赖的打印输出函数fputc()重定向(microlib中的printf()函数打印操作依赖fputc() )。  
1.2 重定向fputc函数
在microlib的stdio.h中,fputc()函数的原型为:
int fputc(int ch, file* stream)
此函数原本是将字符ch打印到文件指针stream所指向的文件流去的,现在我们不需要打印到文件流,而是打印到串口1。基于前面的代码:
#include int fputc(int ch, file* stream){//usart_senddata(usart1, (unsigned char) ch);//while (!(usart1->sr & usart_flag_txe));usart_sendchar(usart1, (uint8_t)ch);return ch;}  注意:需要包含头文件stdio.h,否则file类型未定义。
勾选了use microlib选项,重定向fputc()函数后,我们就可以在工程代码中使用printf()函数了: int main(void){usart_configuration();printf(stm32f103rct6);printf(cortex-m3);while (1);return 0;}  printf()函数的使用方法跟之前一样,运行结果:
方法2:不使用microlib库
2.1 半主机模式
半主机模式是arm的一种机制,实现将来arm应用程序代码的输入/输出请求传送至运行着调试器的主机。例如,设置使用半主机模式下的arm应用程序,可以使用printf()和scanf()来使用主机的显示器和键盘,而不需要在arm系统上搭配显示器和键盘。
半主机通过一组定义好的软件指令(如svc)来实现的,这些指令在程序控制下产生异常,arm应用程序调用半主机对应的异常处理函数,然后调试代理处理该异常。
第二段话感觉理解起来有点模糊,但是第一段还是懂它在讲什么的。一般的arm应用程序中并不需要半主机操作,在这里为确保arm应用程序中没有链接microlib的半主机相关函数,我们要取消arm的半主机工作模式。
2.2 实现代码
在工程中加上如下代码:
#pragma import(__use_no_semihosting)struct __file {int handle;};file __stdout;_sys_exit(int x){x = x;}int fputc(int ch, file *f){while((usart1->sr&0x40)==0);usart1->dr = (u8) ch;return ch;}  上面的代码摘自正点原子的范例程序,具体每一行的意义目前也不大清楚。这样操作后,在不使用microlib的前提下,仍能使用printf()函数将调试信息打印到usart1上了。


华为海思自主设计的ARM芯片已经流片归来,那么能技压群雄么?
德国电信携手OPPO积极引领欧洲5G网络部署
智慧能耗监测管理系统的功能和特点说明
安霸与Applied合作提供CV3-AD域控制器SoC的可扩展HIL测试
安全关键时代的遗留代码
STM32工程上printf()函数的方法实现
Vivado硬件平台更新后Vitis工程要如何快捷更新
常熟积极构建人工智能生态圈 上海人工智能技术协会入驻虹桥商务区
人工智能大会齐聚众多大咖,探索AI下一步突破的方向大会齐聚众多大咖,探索AI下一步突破的方向
i2c详解+送apb_i2c工程+送中英文协议
名门锁业室内静音指纹锁EM-S015003简介
2019车市开始走合作路线 整合资源又扬长避短
电子纹身植入体内 大脑控制想变就变
太阳能自动伸缩充电车衣亮相 可让汽车在停车过程中自动充电
智慧楼宇综合管理系统物联网方案
为数字健康应用揭开人工智能和机器学习的神秘面纱
飞兆半导体开发出高集成度PWM控制器FAN6863
分体式电磁流量计安装注意事项
一位工作三年左右的Java程序员的从业心得
三相三线和三相四线电能表的区别