6 8051使用Keil模拟器和调试输出窗口实现串口收发回环
迪丽瓦拉
2024-04-02 01:31:19
0
  • 本仓库相关网址:
    CSDN文章地址
    Gitee工程和源码地址

  • 相关仓库:
    嵌入式整体介绍,里面也描述了部分8051的内容:
    才鲸嵌入式 / 嵌入式知识图谱WiKi
    C语言框架讲解,让你对C语言要学哪些东西有一个完整的了解:
    embedded_programming_skills/ 0_doc / 02-C语言框架讲解.md

6)Keil调试输出窗口串口回环

  • 本工程主要演示使用Keil创建一个默认工程,然后添加串口0的回环收发用例,在Keil的调试串口进行串口收发,并且可以使用虚拟串口,通过SSOM32与Keil通信来进行串口收发。

  • 工程文件:本文档同级目录/06_uart0_loopback/06_uart0_loopback.uvproj

  • 将Keil的UART #1调试输出串口与SSCOM32等串口软件绑定的话,需要用到VSPD软件。

  • 参考网址:

    • Keil实例仿真AT89C51串口UART收发数据(附程序)
    • keil MDK 中使用虚拟串口调试串口
    • 虚拟串口 VSPD 的使用
  • 代码展示:

/******************************************************************************** \brief	进行串口0的回环输入输出* \details	可以通过Keil的UART #1调试串口直接输出串口信息和接收串口的输入,*			也可以通过VSPD虚拟串口软件与PC上的SSCOM32等串口软件进行绑定,通过*			串口软件收发信息。* \attention	Keil UART #1和SSCOM32窗口输入的行尾是\r,不是\r\n,*				但是输出又是以\r\n来处理的。Keil模拟器模式下,*				晶振不管设置为多少,UART0不管是否初始化,设置多少波特率,Keil*				UART #1调试串口的收发总是115200的波特率。* \author	将狼才鲸* \date	2022-10-30* \note	参考网址:*			[Keil实例仿真AT89C51串口UART收发数据(附程序)]*			(https://blog.csdn.net/tj_nonstoper/article/details/124271543)*			[keil MDK 中使用虚拟串口调试串口]*			(https://blog.csdn.net/NICHUN12345/article/details/124423615)*			[虚拟串口 VSPD 的使用]*			(https://blog.csdn.net/qq_17351161/article/details/89607458)******************************************************************************//********************************** 头文件 ************************************/
#include 	/* 8051通用的寄存器定义 *//********************************** 宏定义 ************************************/
#define CHAR_LOOPBACK		/* 收到一个字符就返回一个字符 */
//#define STRING_LOOPBACK	/* 收到一行后再返回一行,如果接收buffer满则先返回整个buffer数据 */
/* Keil使用自带的模拟器运行时,输入的换行是\r,但输出换行却是\r\n */
#define FIX_KEIL_EMULATOR_AND_SSCOM32_UART_LINE_FEED_ISSUE#define BUF_MAX_SIZE 16		/* 设置短一点,即使没输入正确的换行符,也能及时有数据返回 *//********************************* 全局变量 ***********************************/
static char rcv_buf[BUF_MAX_SIZE] = {0};	/* 完整的一行接收缓存 */
static int rcv_cnt = 0;	/* 接收的字节数 */
static char rcv_str_end_flag = 0; /* 0 is false, !0 is true */static void uart0_init(void);
static void send_str(char *str);
static void send_char(char c);//如果要使用printf,需要进行putchar输出重定义
//不使用printf,直接往串口写数据也能输出到UART #1
///**
// * \brief		printf重定向的函数,这里重定向到串口0
// * \param[in]	c	承接printf中传入的一个字符
// * \note		这里的函数注释格式是Doxygen,有兴趣的可以自行去了解
// * \return		正常时返回c,错误时返回错误码
// */
//char putchar(char c)
//{
//	ES = 0;				/* 关串口中断 */
//	SBUF = c;           
//	while (TI != 1);	/* 等待发送成功,产生发送中断 */
//	TI = 0;				/* 清除发送中断标志 */
//	ES = 1;				/* 开串口中断 *///	return c;
//}/********************************* 接口函数 ***********************************/
/*** \brief	入口函数*/
int main()
{/* 1. 串口初始化 */uart0_init();/* 2. 打印提示信息 */send_str("====\r\n");send_str("| Check your terminal line end format.\r\n");send_str("| \r \\r \r");send_str("| \n \\n \n");send_str("| \n\r \\n\\r \n\r");send_str("| \r\n \\r\\n \r\n");send_str("====\r\n\r\n");send_str("====\r\n");
#ifdef STRING_LOOPBACKsend_str("Now is string loopback mode\r\n");send_str("Please input string, and end with \\r or \\n.\r\n");
#elsesend_str("Now is char loopback mode\r\n");send_str("Please input char\r\n");
#endif/* 3. 打印从串口中断收到的信息 */while (1) {  	 if (rcv_str_end_flag) {/* 如果收到数据,则进行回环输出 */rcv_str_end_flag = 0;
#ifdef STRING_LOOPBACKsend_str("Your Input: ");send_str(rcv_buf);  /* 把接收到的字符串发送回去 */
#else/* 防止有人不知道串口中断输入是没有回显的,原样输出会以为只是自己的输入 */send_char('>');send_char(rcv_buf[0]);
#	ifdef FIX_KEIL_EMULATOR_AND_SSCOM32_UART_LINE_FEED_ISSUEif (rcv_buf[0] == '\r')send_char('\n');
#	endifsend_char(' ');
#endifrcv_buf[0] = '\0'; /* 清空字符串,直接写个字符串结尾 */}}
}	/********************************* 私有函数 ***********************************/
/*** \brief		串口0初始化* \attention	Keil模拟器模式时可以不初始化,即使配置了参数,也始终以115200波特率输出* \note		不初始化的话能输出数据,但是进不了接收中断,接收不了信息*/
static void 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		发送一个字符* \param[in]	c	发送的字符*/
static void send_char(char c)
{SBUF = c;		/* SBUF是指串行口同地址的两个缓冲寄存器,一个是发送寄存器,一个是接收寄存器,通过读和写来区分 */       while (!TI);		/* 等待一个字符发送完毕 */TI = 0;			/* TI软件清零,等待下一次发送后的置位 */
}/*** \brief		发送一个字符串* \param[in]	str	要发送的字符串指针*/
static void send_str(char *str)
{ES = 0; /* 发送前关闭串口中断,以防发送完每个字符都没必要地进入中断程序 */while (*str != '\0')		/* 持续发送直到字符串结尾 */send_char(*str++);	/* 先取值再自增 */ES = 1; /* 发送完打开串口中断 */
}/*** \brief	串口0中断处理函数* \note	中断默认优先级:0外部中断0 > 1定时/计数器0 > 2外部中断1 > 3定时/计数器1 > 4串行中断*/
void uart0_irq(void) interrupt 4 
{if (RI == 1) {/* RI置位表示一帧数据接收完毕,中断处理后RI必须用软件清0 */RI = 0; /* 中断标志位清零 */ES = 0; /* 关闭串口中断,防止下面程序执行时被打断,防止中断嵌套 */
#ifdef STRING_LOOPBACKif (rcv_cnt < BUF_MAX_SIZE - 2) {/* 接收缓冲区未溢出 */rcv_buf[rcv_cnt] = SBUF; /* 存放1个字节 */if (rcv_buf[rcv_cnt] == '\r' || rcv_buf[rcv_cnt] == '\n') {/* 如果接收到结束符(当前Keil UART #1和SSCOM都是\r) */rcv_str_end_flag = 1;	/* 接收了完整的字符串 */
#ifdef FIX_KEIL_EMULATOR_AND_SSCOM32_UART_LINE_FEED_ISSUErcv_buf[rcv_cnt - 1] = '\n';
#endifrcv_buf[rcv_cnt] = '\0';	/* 在字符串末尾补字符串结束 */rcv_cnt = 0; /* 接收长度清0 */} else {rcv_cnt++;}} else {/* 接收缓冲区溢出 */rcv_str_end_flag = 1;rcv_buf[rcv_cnt] = '\0';rcv_cnt = 0;}
#elsercv_buf[0] = SBUF; /* 存放1个字节 */rcv_str_end_flag = 1;
#endifES = 1;	/* 中断处理完重新打开串口中断 */}
}

相关内容