7 Keil C51 使用_getkey对scanf输入重定向,实现标准输入输出,getchar

Keil C51 使用_getkey对scanf输入重定向,实现标准输入输出

作者 将狼才鲸
创建时间 2022-12-04

    1. 使用Keil C51:
    • 当前使用Keil c51v960a + Keil uVision5,采用Keil的模拟器运行,不用接板子
    • 注意!文件使用了UTF-8编码,需要在Keil中进行设置才能正常显示中文,否则会显示乱码:Edit–>Configuation–>Encoding–>Encoding in UTF-8 without signature;因为GB2312在Git中会显示乱码,并且在Linux中使用GB2312会很不方便,从Linux和Windows中来回转时一不留神会用错误的编码报错导致中文丢失。
    • 进入cj-player-camera/samples/01_helloworld/keil.c51目录
    • 工程源码路径:https://gitee.com/langcai1943/cj-player-camera/tree/develop/samples/01_helloworld/keil.c51
    • 双击打开c51.uvproj
    • 然后编译:点击软件上面状态栏第三排左侧两个向下小箭头的图标
    • 然后开始运行:点击软件上面状态栏第二排右侧黑色放大镜+红色“d”的小图标;如果你的软件是官网下载的未破解的评估版,会出现一个弹窗,关掉那个弹窗不管它;程序会停在main()函数的第一行,先不要继续运行
    • 输出的结果会在Keil Debug状态下的UART #1窗口中;这个窗口Keil不会主动为你打开,你需要点击在软件上面第三排图标中的小串口带一个黑色串口的图标旁边的小三角形,选中里面的UART #1,然后软件右下角就会出现UART #1窗口
    • 继续运行:点击软件左上角一个向下箭头的图标,UART #1窗口中出现了输出
    • 运行效果:
Is Keil C51
Is standard ANSI C protocol
Hello world

! Please input your string (maybe need to end with a 'Enter' key) !
cdThis is your input result:
dddddddaabbccddeeffgg
  • 源码展示:
/*******************************************************************************
 * \brief	打印hello world
 * \details 支持GCC、Qt、VS、Keil C51、Keil MDK
 * \note	File format: UTF-8
 * \author	将狼才鲸
 * \date	2022-12-04
 * \remark 	参考网址
 *			[keil C51 重定向printf到串口](https://blog.csdn.net/Wekic/article/details/77418443)
 *			[C51中预定义宏](https://blog.csdn.net/chminlan/article/details/9334017)
 *			[c51语言 库函数,C51库函数](https://blog.csdn.net/weixin_42513038/article/details/117162958)
 *          [51单片机实现scanf和printf函数](https://blog.csdn.net/CSDN4646/article/details/80893142)
 *			[Error: L6218E: Undefined symbol Image$$ARM_LIB_STACK$$ZI$$Limit Not enough information to list image](https://blog.csdn.net/qq_41200467/article/details/124958685)
 *          [visual c++中预定义的宏](https://blog.csdn.net/chiqi9480/article/details/100606870)
 ******************************************************************************/

/********************************** 头文件 ************************************/
#ifdef __C51__
#	include <reg51.h>	/* 8051通用寄存器 */
#endif

#include <stdio.h>		/* printf */

/******************************** 函数声明 ************************************/
#ifdef __C51__
static void c51_uart0_init(void);
#endif

/******************************** 接口函数 ************************************/
/**
 * \brief   主函数
 */
int main()
{
    
    
	int input_flag = 1;
	char i_c;

    /** 1. 一些C语言编译器区分 */
#ifdef __C51__
    printf("Is Keil C51\n");
    c51_uart0_init();
#endif

#ifdef __STDC__ /* __STDC__是编译器自带的宏定义 */
	printf("Is standard ANSI C protocol\n");	/* 是否是标准的C语言协议 */
#else
	printf("Is not standard ANSI C protocol\n");
#endif

    /** 2. 打印Helloworld */
    printf("Hello world\n");
#ifndef __C51__
    fflush(stdout); /* 立即输出到终端,清除缓存,防止有些终端上看不到输出 */
#endif

	/** 3. 输入测试 */
	printf("\n! Please input your string (maybe need to end with a 'Enter' key) !\n");
#ifndef __C51__
    fflush(stdout);
#endif

	/*
	 * 死循环的作用:	1. 防止有些终端执行完后会闪退
	 *					2. 测试输入输出回环
	 */
	while (1)
	{
    
    
		scanf("%c", &i_c);	/* 用阻塞的方式读 */

		if (input_flag)
		{
    
    
            printf("This is your input result:\n\n");
#ifndef __C51__
            fflush(stdout);
#endif
            input_flag = 0;
		}

        printf("%c", i_c);
#ifndef __C51__
        fflush(stdout); /* 立即输出到终端,清除缓存 */
#endif
	}

    return 0;
}

#ifdef __C51__
/******************************** 私有函数 ************************************/
/**
 * \brief		C51串口0初始化
 * \attention	Keil模拟器模式时可以不初始化,即使配置了参数,也始终以115200波特率输出
 * \note		不初始化的话能输出数据,但是进不了接收中断,接收不了信息
 */
static void c51_uart0_init(void)
{
    
    
	/* 串口参数配置函数,这里配置为9600波特率,1位停止位,8位数据位,无校验 */
	TMOD = 0x20;  /* Timer1以定时模式工作在方式2:8位常数自动装入定时器/计数器 */
	SCON = 0x40;  /* SM0 = 0,SM1 = 1,方式1,10位UART "0 D0~D7 1",波特率可变 */
	REN  = 1;     /* 允许串口接收数据位 */
	/** \warning 不管设置多少波特率,Keil模拟器在UART #1中的收发始终是115200 */
	TH1  = 0xFD;  /* 9600波特率:晶振的频率/(12 * (256 - 初值)) = 波特率 * 32 */
	TL1  = 0xFD;  /* 方式2的TH1,TL1是相等的,TL1自动重装TH1初值 */
	PCON = 0x00;  /* SMOD = 0波特率不加倍 */
	IE   = 0x90;  /* 允许总中断,允许串口中断,禁止其他中断 */
	PS   = 0;     /* 设置串行口中断优先级 */
	TR1  = 1;     /* 当GATE=0,TR1置“1” 启动定时器1 */
}

/******************************* 重定向函数 ***********************************/
/**
 * \brief		printf重定向的函数,这里重定向到串口0
 * \param[in]	c	提供printf中需要获取到的一个字符
 * \return		正常时返回c
 */
char putchar(char c)
{
    
    
	/* 空格和行尾注释使用Linux风格 */
	ES = 0;				/* 关串口中断 */
	SBUF = c;
	while (TI != 1);	/* 等待发送成功 */
	TI = 0;				/* 清除发送中断标志 */
	ES = 1;				/* 开串口中断 */

	return c;
}

/**
 * \brief	scanf输入重定向
 */
char _getkey(void)
{
    
    
	char c;

	while (RI != 1); /* 阻塞住,等待查询到接收中端状态 */

	/* RI置位表示一帧数据接收完毕,中断处理后RI必须用软件清0 */
	RI = 0; /* 中断标志位清零 */
	ES = 0; /* 关闭串口中断,防止下面程序执行时被打断,防止中断嵌套 */

	c = SBUF; /* 存放1个字节 */

	ES = 1;	/* 中断处理完重新打开串口中断 */

	return c;
}
#endif /* __C51__ */

猜你喜欢

转载自blog.csdn.net/qq582880551/article/details/128178463