1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
对于 printf 函数相信大家都不陌生,第一个C语言程序就是使用 printf 函数在屏幕上的控制台打印出Hello World,之后使用 printf 函数输出各种类型的数据,使用格式控制输出各种长度的字符,甚至输出各种各样的图案。 除此之外,在程序出错的时候,懒得调试,直接简单粗暴的加个 printf 找bug,有时候也不失为一种有效的方法。 对于已经习惯的 printf 函数,你了解多少呢? 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重定向。 2.在MDK中使用MicroLib重定向printf 勾选Use MicroLib MicroLib是对标准C库进行了高度优化之后的库,供MDK默认使用,相比之下,MicroLIB的代码更少,资源占用更少: 重定义fputc到串口 重新实现fputc函数,编写代码将这个字符通过串口发送,因为发送每个字符时都会调用该函数,所以为了效率,不再调用库函数 HAL_UART_Transmit 发送,而是直接操作寄存器发送。
其中 BIT6 TC用来指示当前串口是否发送完成,如图: 可以通过判断该位来判断串口当前是否处于发送状态,代码如下: while((USART1->ISR & 0X40) == 0);
USART1->TDR = (uint8_t) ch; 最后实现fputc函数就变的非常简单了,这里我放在usart.c文件的末尾: /* USER CODE BEGIN 1 */ #if 1 #include int fputc(int ch, FILE *stream) { /* 堵塞判断串口是否发送完成 */ while((USART1->ISR & 0X40) == 0); /* 串口发送完成,将该字符发送 */ USART1->TDR = (uint8_t) ch; return ch; } #endif /* USER CODE END 1 */ 测试printf 在main函数中测试一下printf函数是否可以正常使用: /* USER CODE BEGIN 2 */ printf("Hello, i am %sn", "mculover666"); printf("Test int: i = %d", 100); printf("Test float: i = %f", 1.234); printf("Test hex: i = 0x%2x",100); /* USER CODE END 2 */ 结果如下: 3.在MDK中使用标准库重定向printf printf 函数使用了半主机模式,所以直接使用标准库会导致程序无法运行,因此必须提前告知编译器不使用半主机模式:
/* 告知连接器不从C库链接使用半主机的函数 */ #pragma import(__use_no_semihosting) /* 定义 _sys_exit() 以避免使用半主机模式 */ void _sys_exit(int x) { x = x; } 所以,重定向fputc()函数完整的代码如下: #if 1 #include /* 告知连接器不从C库链接使用半主机的函数 */ #pragma import(__use_no_semihosting) /* 定义 _sys_exit() 以避免使用半主机模式 */ void _sys_exit(int x) { x = x; } /* 标准库需要的支持类型 */ struct __FILE { int handle; }; FILE __stdout; /* */ int fputc(int ch, FILE *stream) { /* 堵塞判断串口是否发送完成 */ while((USART1->ISR & 0X40) == 0); /* 串口发送完成,将该字符发送 */ USART1->TDR = (uint8_t) ch; return ch; } #endif 测试printf 测试printf函数的代码不变,在MDK设置中取消勾选USE MICROLIB,然后重新编译,下载代码后试验现象如下: 4.在GCC中使用标准库重定向printf 不同的编译器对于C库的底层实现机制是不同的,所以上面两种在MDK中的实现方法,在使用Gcc编译器的时候是不可行的。 在Gcc中重定向printf函数时注意两个关键点:
/* USER CODE BEGIN 1 */ #if 1 #include int _write(int fd, char *ptr, int len) { HAL_UART_Transmit(&huart1, (uint8_t*)ptr, len, 0xFFFF); return len; } #endif /* USER CODE END 1 */ 使用STM32CubeMX生成makefile,然后使用arm-none-eabi-gcc编译没有问题,再使用STM32 ST-LINK utility 下载后实验现象如下: 至此,我们已经学会实现printf()函数的多种方法,下一节将讲述如何使用ADC读取MQ-2气体传感器的值。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1820 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1634 浏览 1 评论
1104 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
740 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1692 浏览 2 评论
1951浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
756浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
587浏览 3评论
605浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
570浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-31 02:11 , Processed in 0.877443 second(s), Total 77, Slave 61 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号