使用串口重定向实现printf和scanf的格式化输入输出

一、实验设计效果

通过重定向C标准库的printf和scanf函数,实现串口的格式化输出;

调用MDK微库(MicroLib)的方法和调用标准库的方法。

二、硬件工作原理和原理图

我们使用的正点原子STM32F103RB NANO开发板上将USART1(引脚为PA9,PA10)接出

USART1并没有在PCB上连接在一起,需要通过跳线帽来连接一下。这里我们把 P5 的 RXD 和 TXD 用跳线帽与 PA9 和 PA10 连接起来。

信号传输:串口 —— 调试器 —— USB ——电脑

三、实验记录

完成 CubeMX初始化配置 → 生成初始化HAL库工程 →在keli中编写串口程序

1. 完成 CubeMX初始化配置

1.1 利用CubeMX完成HAL库工程模板和初始化:

通过选择芯片型号创建CubeMX工程

在弹出的对话框中输入开发板上的芯片型号,STM32F103RB

在右侧筛选栏中选择Tx型,即开发板上芯片所用的LQFP64型封装,双击建立工程

1.2 RCC模块引脚的配置

在弹出的工程配置对话框中的第一个引脚配置选项卡下,先完成RCC时钟模块引脚配置:

选择启用外部的高速和低速时钟源,HSE和LSE,配置为晶振连接;

配置完成后,对应时钟引脚变绿,同时旁边出现其将要配置模式的文字说明;

1.3 串口的配置

  1. 在侧边栏的通信外设中选择USART1
  2. 在串口下拉框出选择异步(Asynchronous),成功后可以看到对应的串口引脚RX(接收引脚),TX(发送引脚)变绿。
  3. 选择下方的Parameter Settings选项卡确认串口参数。设置波特率为115200,传输数据长度为8 Bit,无奇偶校验位,1个停止位,数据方向(Data Direction)为接收和发送(Receive and Transmit)

  1. 在NVIC Settings选项卡下,开启串口中断,如需调整中断优先级可在右侧修改

1.4 完成时钟配置:

我们前面启用了RCC时钟模块的外部时钟引脚,这里我们需要将外部时钟源配置为实际使用的频率;

查看手册可知:LSE为32.768KHz,HSE为8MHz;

点击上方的时钟配置选项卡,进入时钟配置界面;

前面启用了外部晶振源,这里检查修改HSE\LSE的频率,和实际一致;

在主频选择框中输入72MHz,然后按回车OK;

系统将自动将时钟源、时钟流向、PLL等配置好,以产生相应的主频;

1.5 创建生成工程

 

 

2、printf 和 fputc

对于 printf 函数相信大家都不陌生,第一个C语言程序就是使用 printf 函数在屏幕上的控制台打印出Hello World,之后使用 printf 函数输出各种类型的数据,使用格式控制输出各种长度的字符,甚至输出各种各样的图案。

除此之外,在程序出错的时候,懒得调试,直接简单粗暴的加个 printf 找bug,有时候也不失为一种有效的方法。

对于已经习惯的 printf 函数,你了解多少呢?

printf 定义在 <stdio.h> 头文件中,如下:

int printf(const char *format, ...);

printf 函数根据 format 字符串给出的格式打印输出到 stdout(标准输出)中,当然,printf 函数是不会一个字符一个字符去输出,它会调用更底层的 I/O 函数:fputc去逐个字符打印。

fputc 也定义于头文件 <stdio.h>中,如下:

int fputc(int ch, FILE *stream);

fputc 函数写入字符 ch 到给定输出流 stream,printf函数在调用该函数时,会向stream参数传入stdout从而打印数据到标准输出。

那么,要实现printf打印到串口就变得非常简单了,只需要重新定义fputc函数,在fputc的函数中将数据通过串口发送,称之为:fputc重定向或者printf重定向。

同理,只需要重新定义fputc函数,在scanf的函数中将数据通过串口接收,称之为:scanf重定向。

3. printf,scanf的重定向方法介绍

3.1 方法一 使用HAL库函数实现串口重定向(勾选Use MicroLib)

MicroLib是对标准C库进行了高度优化之后的库,供MDK默认使用,相比之下,MicroLIB的代码更少,资源占用更少,在MDK编译环境下可以使用其MicroLib,快速实现printf重定向;

1)勾选Use MicroLib,使用半主机模式

2)重定义fputc到串口

在MicroLib的stdio.h中,fputc()函数的原型为:int fputc(int ch, FILE* stream)

此函数原本是将 字符ch 打印到文件指针 stream 所指向的文件流去的,现在我们不需要打印到文件流,而是打印到串口1。

在 usart.c 中,重写 fputc 函数,利用HAL_UART_Transmit打印到串口1,相应的,fgets也可以HAL_UART_Receive重定向;

int fputc(int ch, FILE *f)
{
    uint8_t temp = ch;
    HAL_UART_Transmit(&huart1, &temp, 1, 0xffff);//huart1需要根据你的配置修改
    return ch;
}

int fgetc(FILE *f)
{
    uint8_t ch = 0;
    HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
    return ch;
}

 别忘了在usart.h 中添加 stdio.h,这样在需要使用串口的文件中添加 usart.h 就可以使用串口HAL库和printf重定向了。

#include <stdio.h>

3.2 在MDK中使用标准库重定向printf

MicroLib很好,但在其他编译环境下不可用,有的MCU也不支持,这里再介绍使用标准库重定向printf,可以在各种编译环境下使用;

1)注意,不要勾选Use MicroLib使用半主机模式

2)重定向fputc()函数完整的代码

一样写在 usart.c文件中即可,这里使用了寄存器操作,运行效率更高;

#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;  
FILE __stdin;   
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (uint8_t) ch;      
	return ch;
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
    uint8_t ch = 0;
	while((USART1->SR&0X20)==0);//循环发送,直到发送完毕   
	ch = (uint8_t) USART1->DR;   
    return ch;
}

别忘了在usart.h 中添加 stdio.h,这样在需要使用串口的文件中添加 usart.h 就可以使用串口HAL库和printf重定向了。

#include <stdio.h>

在所给的例程中,我使用了条件编译 #ifdef USE_PRINT…#endif、#ifndef USE_MICROLIB… #else…#endif来裁剪和选择这部分功能,对于大段代码的启用/弃用也可使用 #if 0/1 …#endif 来裁剪。

4. 编写main.c测试printf、scanf重定向

1)按前述方法,完成printf、scanf重定向

2)添加printf、scanf的测试程序

在main.c 中写一个简单的测试程序,测试printf和scanf的整数、浮点数、字符和字符串的格式化输入输出。

  /* USER CODE BEGIN 2 */
	uint8_t name[10];
	int32_t age;
	float score;
	int8_t level;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		printf("Enter your  name: \r\n");
		scanf ("%10s",name);  
		printf("Enter your age: \r\n");
		scanf ("%d",&age);
		printf("Enter your score: \r\n");
		scanf ("%f",&score);
		printf("Enter your level: \r\n");
		scanf ("%c",&level);
		printf("\r\nHi, %s, %d years old. \r\n", name, age);
		printf("Your score is %2.2f. Your level is %c.\r\n\r\n", score, level);		
    /* USER CODE END WHILE */
  }

3)串口测试

a) 使用 XCOM 串口助手测试

打开串口助手,按下图提示进行测试,可以看到例程可以正常工作;

注意烧录时最好要关闭串口,烧录完成后再重新打开;

每次烧录后,都要重新关闭/打开一次串口助手,串口助手XCOM 才能正常工作;

b) 使用远程串行终端软件 PuTTY 测试

PuTTY 是一个 Telnet/SSH/rlogin/ 纯 TCP 以及串行阜连线软件。常用于远程连接控制台调试,使用非常方便。这里我们串口的简单输入输出测试,也可以使用它。

①先进行终端设置,勾选输入的回显和换行,方便观察输入信息;

②点击串口,配置串口参数后即可打开;

③像控制台一样进行访问,可以回删修改输入,按回车键确认输入,使用非常方便;

物联沃分享整理
物联沃-IOTWORD物联网 » 使用串口重定向实现printf和scanf的格式化输入输出

发表回复