• STC15单片机-串口打印printf重定向


    printf函数重定向为往串口打印信息

    在UART.h、UART1.h和UART.c文件的内容编写好后,在main函数中实现通过串口发送一串字符串和一个不断加1的变量

    方法一:

    使用sprintf函数,组装字符串,然后通过串口发送数组的函数进行串口发送

    /* Includes ------------------------------------------------------------------*/
    #include 
    
    /* Private define-------------------------------------------------------------*/
    
    /* Private variables----------------------------------------------------------*/
    uint16_t Cnt = 0;		//初始化自动加1的变量
    /* Public variables-----------------------------------------------------------*/
    
    /* Private function prototypes------------------------------------------------*/
    
    /*
    * @name   main
    * @brief  主函数
    * @param  void	
    * @retval int      
    */
    int main(void)
    {
    	//系统初始化
    	Hradware.Sys_Init();
    
    	//串口1发送初始化信息
    	UART1.UART_SendString("Initialization completed,system startup!\r\n");
    	//系统主循环
    	while(1)
    	{
    		//按键检测
    		//KEY1.KEY_Detect();
    		//KEY2.KEY_Detect();
    		//PWM.PWM_LED_Adjust_Brightness();
    		
    		//通过串口发送字符串和自动加1的变量
    		//方法一
            /*sprintf函数将Cnt按照格式组装成一个字符串,然后放入到UART1.pucSend_Buffer指向的发送数组中*/
    		sprintf(UART1.pucSend_Buffer,"hello:%u\r\n",Cnt++);
    		Public.Delay_ms(300);
    		UART1.UART_SendArray(UART1.pucSend_Buffer,20);
    		Run_LED.Run_LED_Flip();
    	}
    }
    /********************************************************
      End Of File
    ********************************************************/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    在这里插入图片描述

    注意Cnt变量的类型以及组装字符串时的格式化%方式不对的话,都会造成不同的效果

    注意一:
    /* Private variables----------------------------------------------------------*/
    uint8_t Cnt = 0;	//初始化自动加1的变量
    
    • 1
    • 2

    如果Cnt的类型是uint8_t的话,下面的格式化不变,还是%u,则串口输出的不是加1,而是加256了

    在这里插入图片描述

    如果Cnt类型非得写成 uint8_t 的话,那么格式化前要强制类型转换才会正常加1,并从0加到255,然后又从0开始

    sprintf(UART1.pucSend_Buffer,"hello:%u\r\n",(uint16_t)Cnt++);
    
    • 1

    在这里插入图片描述

    注意二:
    sprintf(UART1.pucSend_Buffer,"hello:%c\r\n",Cnt++);
    
    • 1

    如果格式化时,写成了%c,Cnt的类型还是uint16_t,则串口打印不了Cnt变量
    在这里插入图片描述

    如果把Cnt类型改为uint8_t,则Cnt被打印成了一个圈,而且还不连续,明显是错误的

    在这里插入图片描述

    方法二:

    使用printf重定向来做,本次实验对putchar函数进行修改,需要在main.h头文件中引入< stdio.h >头文件

    UART1.c:

    /*
    * @name   putchar
    * @brief  字符发送函数重定向
    * @param  c:发送的字符
    * @retval char   
    */
    extern char putchar(char ch)
    {
      UART1.UART_SendData((uint8_t)ch);		//在putchar函数内直接调用串口发送字符函数
      return ch;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    main.c:

    /* Includes ------------------------------------------------------------------*/
    #include 
    
    /* Private define-------------------------------------------------------------*/
    
    /* Private variables----------------------------------------------------------*/
    uint16_t Cnt = 0;	//初始化自动加1的变量
    /* Public variables-----------------------------------------------------------*/
    
    /* Private function prototypes------------------------------------------------*/
    
    /*
    * @name   main
    * @brief  主函数
    * @param  void	
    * @retval int      
    */
    int main(void)
    {
    	//系统初始化
    	Hradware.Sys_Init();
    
    	//串口1发送初始化信息
    	UART1.UART_SendString("Initialization completed,system startup!\r\n");
    	//系统主循环
    	while(1)
    	{
    		//按键检测
    		//KEY1.KEY_Detect();
    		//KEY2.KEY_Detect();
    		//PWM.PWM_LED_Adjust_Brightness();
    		
    		//通过串口发送字符串和自动加1的变量
    		//方法一
    		/*sprintf函数将Cnt按照格式组装成一个字符串,然后放入到UART1.pucSend_Buffer指向的发送数组中*/
    		// sprintf(UART1.pucSend_Buffer,"hello:%u\r\n",Cnt++);
    		// Public.Delay_ms(100);
    		// UART1.UART_SendArray(UART1.pucSend_Buffer,20);
    		// Run_LED.Run_LED_Flip();
    
    		//方法二
    		//printf函数重定向
    		printf("hello:%u\r\n",Cnt++);
    		Public.Delay_ms(100);
    		Run_LED.Run_LED_Flip();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在这里插入图片描述

    重定向后,也可以使用printf函数打印浮点数

    float f = 3.14;
    。。。
    printf("hello:%u%f\r\n",Cnt++,f);
    
    • 1
    • 2
    • 3

    在这里插入图片描述

    注意:

    网上文章许多都是说改写fputc函数,但我在这个程序中试了是不行的,会编译出错,定位到函数的FILE类型上,因为C51是没有FILE类型的,所以这里是使用不了fputc函数的

    int fputc(int ch,FILE*stream)	//编译时在FILE处报错
    {
      UART1.UART_SendData((uint8_t)ch);
      return ch;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    那为什么网上还有那么多说改fputc函数呢?因为那是在编写STM32单片机程序的时候,可以在keil软件的设置里,勾选Use MicroLIB选项,就可以使用fputc函数来实现重定向了

    打开STM32工程文件时keil设置界面:

    在这里插入图片描述

    为什么使用microlib库

    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 只提供双区内存模型,即单独的堆栈和堆区。

    (13)、可以合理地将 microlib 与 --fpmode=std 或 --fpmode=fast 配合使用。

    勾选微库之后,重写fputc函数和fgetc函数就可以支持printf函数和scanf函数。

    但打开编写51单片机的程序时,keil软件设置里是没有这个选项的,所以51的程序没法使用fputc函数

    打开51工程文件时keil设置界面:

    在这里插入图片描述

    而从keil的帮助文档里可以知道, printf 也是基于putchar实现的,所以本次重新实现putchar,就可以实现printf的重定向了

    至于重定向的方法网上有很多,但为什么可以重定向,就比较少文章介绍,这里看到有一篇文章写的不错:https://zhuanlan.zhihu.com/p/133460085

    文章中介绍的weak的属性,相信以后在STM32中会经常遇到

    fputc

    语法:

    #include   int fputc( int ch, FILE *stream ); 
    
    • 1

    函数fputc()把给出的字符ch写到给出的输出流. 返回值是字符, 发生错误时返回值是EOF.

    putchar

    语法:

    #include   int putchar( int ch ); 
    
    • 1

    putchar()函数把 ch 写到STDOUT(标准输出). 代码

    putchar( ch );
    
    • 1

    putc( ch, STDOUT );
    
    • 1

    一样.

    putchar()的返回值是被写的字符, 发生错误时返回EOF

  • 相关阅读:
    Spring boot+Spring security+JWT实现前后端分离登录认证及权限控制
    Elasticsearch 索引数据多了怎么办,如何调优,部署?
    不会写代码同学的福音——AI 代码生成器 Amazon CodeWhisperer(通过注释写代码)
    Linux安装配置awscli命令行接口工具及其从aws上传下载数据
    并行算法优化(1)
    中国DevOps平台市场,华为云再次位居领导者位置
    赶紧进来看看---C语言实现学生信息管理系统(3.0文件存储版)
    POJ3984迷宫问题——题解
    微信小程序--注册时获取微信头像
    【Java】面向对象思想
  • 原文地址:https://blog.csdn.net/weixin_46251230/article/details/126674934