STC89C52RC 之 RS232与电脑通讯
第十三节课,RS232与电脑通讯
1 概述
RS232(Recommended Standard 232)是一种常用的串行通信接口标准,用于在计算机和外部设备之间传输数据。RS232是由电子工业协会(Electronic Industries Association)制定的,最初是为了连接计算机和调制解调器之间进行数据通信而设计的。以下是关于RS232的一些基本介绍:
(1) 物理接口:RS232使用传统的串行通信方式,使用一对差分信号线进行数据传输。常见的RS232物理接口是使用DB-9或DB-25连接器,其中DB-9连接器具有9个引脚,而DB-25连接器具有25个引脚。
(2) TTL信号:RS232使用两个信号线(通常称为TX和RX)来传输数据。这些信号线与地之间的电压差表示二进制数据的逻辑状态。通常,高电平表示逻辑1,低电平表示逻辑0。RS232底层一般是由UART电路转化而来,如图所示。
(3) 通信速率:RS232支持多种通信速率(波特率),典型的速率包括2400、4800、9600、19200、38400、57600和115200等。通信双方必须在相同的波特率下进行通信才能正常传输数据。
(4) 数据格式:RS232通信使用异步串行传输方式,其中数据被分为连续的字节进行传输。每个字节包含起始位、数据位、可选的奇偶校验位和停止位。数据位的常见大小为8位。
(5) 控制信号:除了数据传输信号外,RS232还定义了一些控制信号,用于控制通信过程。其中包括RTS(请求发送)、CTS(发送就绪)、DTR(数据终端就绪)和DSR(数据设备就绪)等信号。
(6) 通信距离和线缆:由于RS232信号受到信号损耗和干扰的影响,其通信距离较短,通常不超过50英尺(15米)。常见的RS232线缆是使用三线链接,其中包括发送线、接收线和地线。
(7) 应用领域:RS232广泛应用于计算机和外部设备之间的串行通信,例如连接调制解调器、打印机、终端、传感器、工业设备和其他串口设备等。
需要注意的是,随着技术的发展,RS232逐渐被更快速、更先进的通信接口(如USB、Ethernet等)所取代,但在某些特定的应用场景中仍然广泛使用。
2 Uart介绍
2.1 概述
UART(Universal Asynchronous Receiver-Transmitter)是一种常见的串行通信接口,用于在计算机系统或微控制器与外部设备之间进行数据传输。UART是一个硬件模块,用于将并行数据转换为串行数据以进行传输,并将接收到的串行数据转换为并行数据。
以下是关于UART的一些基本信息:
(1) 功能:UART负责将并行数据转换为串行数据并发送,同时接收串行数据并将其转换为并行数据。它提供了一种异步通信的方式,其中数据被分为连续的字节进行传输。
(2) 数据传输:UART使用两个信号线进行数据传输:一个用于发送数据(TX,传输线路),另一个用于接收数据(RX,接收线路)。发送方将并行数据转换为连续的位流,并通过发送线路将其发送给接收方。接收方通过接收线路接收位流,并将其重新转换为并行数据。
(3) 波特率:UART通信使用波特率来定义数据传输的速率。波特率是指每秒传输的位数。通信双方必须在相同的波特率下进行通信才能正确传输和接收数据。
(4) 数据格式:UART通信中的数据帧通常由起始位、数据位、可选的奇偶校验位和停止位组成。起始位用于指示数据传输的开始,停止位用于指示数据传输的结束。数据位的大小一般为5、6、7或8位。
(5) 逻辑电平:UART通信使用逻辑电平来表示数据的逻辑状态。通常,高电平表示逻辑1,低电平表示逻辑0。逻辑电平的具体电压取决于特定的电平标准,如TTL(Transistor-Transistor Logic)或CMOS(Complementary Metal-Oxide-Semiconductor)。
(6) 缓冲区:UART通信中,发送方和接收方通常都具有缓冲区用于临时存储数据。发送方将数据写入发送缓冲区,而接收方从接收缓冲区读取接收到的数据。
(7) 应用领域:UART广泛应用于各种设备和系统中,包括嵌入式系统、传感器、通信模块、无线模块、显示器、调制解调器、打印机和许多其他外部设备。
需要注意的是,UART是一种硬件接口,与特定的通信协议无关。在实际应用中,UART通常与其他协议(如RS232、RS485等)结合使用,以实现特定的数据传输要求。
2.2 STC89C52UART介绍
STC89C52中的串行I/O端口与80C52中的串口兼容。它提供同步和异步通信模式。它作为通用异步收发器(UART)在三种全双工模式(模式1、2和3)下运行。异步传输和接收可以同时发生,并且具有不同的波特率串行I/O端口包括以下增强功能:
•帧错误检测
•自动地址识别
(1) UART模块:STC89C52芯片内部集成了一个UART模块,该模块提供了与外部设备进行串口通信的功能。
(2) 引脚:串口通信需要使用芯片上的特定引脚与外部设备连接。在STC89C52中,串口通信使用P3.0(RXD)和P3.1(TXD)引脚。其中,RXD引脚用于接收数据,TXD引脚用于发送数据。
(3) 波特率设置:UART通信的一个重要参数是波特率,它定义了数据传输的速率。在STC89C52中,可以通过设置相关的寄存器来配置波特率。常见的波特率设置可以是9600、19200、38400、57600等。
(4) 数据格式:UART通信涉及的数据格式包括数据位、奇偶校验和停止位等。在STC89C52中,可以通过设置寄存器来配置数据位数、奇偶校验和停止位数。常见的数据格式是8位数据位、无奇偶校验和1个停止位。
(5) 缓冲区:STC89C52的UART模块具有发送和接收缓冲区,用于临时存储数据。发送数据时,将数据写入发送缓冲区,然后UART模块将数据从缓冲区发送到TXD引脚。接收数据时,UART模块从RXD引脚接收数据,并将数据存储在接收缓冲区中,供单片机读取。
(6) 中断:STC89C52的UART模块支持中断机制,可以通过配置相关的中断寄存器来启用接收和发送中断。使用中断可以提高串口通信的效率和可靠性。
(7) 编程:使用STC89C52的串口通信功能,需要在单片机程序中配置相关的寄存器和处理接收和发送的数据。具体的编程方式和操作方法可以参考STC89C52的数据手册和开发工具的相关文档。
2.3 STC89C52 UART寄存器介绍
UART寄存器用于配置和控制UART通信的参数和操作。以下是STC89C52中与UART相关的一些常用寄存器:
(1) SCON(Synchronous Control Register):用于配置UART的工作模式和控制UART的操作。其中的位字段包括:
SM0和SM1:UART工作模式选择位,用于设置UART的工作模式(如波特率、数据位数、校验位等)。
REN:接收使能位,用于启用或禁用UART的接收功能。
TI:发送中断标志位,用于指示上一次发送是否完成。
RI:接收中断标志位,用于指示接收到数据。
(2) TMOD(Timer/Counter Mode Register):用于配置定时器/计数器的工作模式。其中的位字段包括:
T1M1和T1M0:定时器1工作模式选择位。
T0M1和T0M0:定时器0工作模式选择位。
TH1和TL1(Timer1 High Byte 和 Timer1 Low Byte):用于配置定时器1的初值,从而设置UART的波特率。
(3) PCON(Power Control Register):用于配置和控制电源管理功能。其中的位字段包括:
SMOD:串口模式选择位,用于控制UART的波特率加倍。
其他位字段用于配置和控制其他电源管理功能。
(4) SBUF(Serial Buffer Register):用于存储UART接收和发送的数据。
这些寄存器提供了对UART通信的配置和控制,您可以根据具体的需求和应用,对这些寄存器进行适当的设置和操作。
2.4 STC89C52 UART操作
(1)简单的接收发送程序
/******************************************************************/ void sys_uart_init(void) { SCON = 0x50; /* SCON: 模式 1, 8-bit UART, 使能接收 */ TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */ TH1 = 0xFD; /* TH1: reload value for 9600 baud @ 11.0592MHz */ TR1 = 1; /* TR1: timer 1 run */ EA = 1; /*打开总中断*/ ES = 1; /*打开串口中断*/ } /******************************************************************/ /* 串口中断程序 */ /******************************************************************/ static unsigned char Temp = 0; //定义临时变量 void UART_isr(void) interrupt 4 //串行中断服务程序 { if(RI) //判断是接收中断产生 { RI=0; //标志位清零 Temp=SBUF; //读入缓冲区的值 P1=Temp; //把值输出到P1口,用于观察 SBUF=Temp; //把接收到的值再发回电脑端 } if(TI) //如果是发送标志位,清零 { TI=0; } }
(2)带有一定校验判断功能的用法
/******************************************************************/ /* 串口中断程序 */ /******************************************************************/ static unsigned char uart_temp = 0; //定义临时变量 static unsigned char uart_cnt = 0; //定义临时变量 void UART_isr(void) interrupt 4 //串行中断服务程序 { if(RI) //判断是接收中断产生 { RI=0; //标志位清零 uart_temp=SBUF; //读入缓冲区的值 if(uart_cnt==0) { if(0x02 == uart_temp) { uart_cnt = 1; } else { uart_cnt = 0; } } else if(uart_cnt==1) { if(0x05 == uart_temp) { uart_cnt = 2; } else { uart_cnt = 0; } } else if(uart_cnt==2) { uart_cnt = 0; P1=uart_temp; //把值输出到P1口,用于观察 SBUF=uart_temp; //把接收到的值再发回电脑端 } else { uart_cnt = 0; } } if(TI) //如果是发送标志位,清零 { TI=0; } }
(3)重定向使用printf
首先屏蔽uart中断,其次引用文件stdio.h文件,然后重定向putchar函数,至此,使用printf可以实现打印功能。
//c51_uart.c文件 #include "includes.h" /*----------------------------------------------- 名称:串口通信 内容:连接好串口或者usb转串口至电脑,下载该程序,打开电源 打开串口调试程序,将波特率设置为9600,无奇偶校验 晶振11.0592MHz,发送和接收使用的格式相同,如都使用 字符型格式,在发送框输入 hello,I Love MCU ,在接 收框中同样可以看到相同字符,说明设置和通信正确 ------------------------------------------------*/ /******************************************************************/ void sys_uart_init(void) { SCON = 0x50; /* SCON: 模式 1, 8-bit UART, 使能接收 */ TMOD |= 0x20; /* TMOD: timer 1, mode 2, 8-bit reload */ TH1 = 0xFD; /* TH1: reload value for 9600 baud @ 11.0592MHz */ TR1 = 1; /* TR1: timer 1 run */ EA = 1; /*打开总中断*/ //ES = 1; /*打开串口中断*/ } void Uart_SendChar(unsigned char dat) { SBUF = dat; while(!TI); TI = 0; } char putchar(char c)//重定向 { Uart_SendChar(c); return c; } /******************************************************************/ /* 串口中断程序 */ /******************************************************************/ static unsigned char uart_temp = 0; //定义临时变量 static unsigned char uart_cnt = 0; //定义临时变量 void UART_isr(void) interrupt 4 //串行中断服务程序 { if(RI) //判断是接收中断产生 { RI=0; //标志位清零 uart_temp=SBUF; //读入缓冲区的值 if(uart_cnt==0) { if(0x02 == uart_temp) { uart_cnt = 1; } else { uart_cnt = 0; } } else if(uart_cnt==1) { if(0x05 == uart_temp) { uart_cnt = 2; } else { uart_cnt = 0; } } else if(uart_cnt==2) { uart_cnt = 0; P1=uart_temp; //把值输出到P1口,用于观察 SBUF=uart_temp; //把接收到的值再发回电脑端 } else { uart_cnt = 0; } } if(TI) //如果是发送标志位,清零 { TI=0; } }
(5)整体工程代码如下
//main.c文件
#include "includes.h" /******************************************************************/ /* 微秒延时函数 //10us */ /******************************************************************/ void delay_us(unsigned int us)//delay us { while(us--) { } } /******************************************************************/ /* 微秒延时函数 */ /******************************************************************/ void delay_ms(unsigned int Ms)//delay us { while(Ms--) { delay_us(100); } } /*------------------------------------------------ 延时子程序 ------------------------------------------------*/ void delay(unsigned int cnt) { while(--cnt); } /*------------------------------------------------ 主函数 ------------------------------------------------*/ void main (void) { sys_timer_init(); sys_uart_init(); delay(10); delay_ms(10); sys_ledtube_on2(); sys_ledtube_on1(); //首先定义处于什么状态, tx1838_type = 1; printf("hello"); while (1) { // sys_keynum_ledon(9); sys_tx1838_test(); } }
//includes.h文件
#ifndef __INCLUDES_H__ #define __INCLUDES_H__ //#include<reg52.h> #include<intrins.h> //汇编指令_nop_ #include<stdio.h> //标准输入输出 //_nop_(); 产生一条NOP指令 //作用:对于延时很短的,要求在us级的,采用“_nop_”函数,这个函数相当汇编NOP指令,延时几微秒。 //NOP指令为单周期指令,可由晶振频率算出延时时间。 //8051 为每个机器周期 12 时钟 //对于12M晶振,延时1uS。 //11.0592M晶振,延时1.0851uS。 //对于延时比较长的,要求在大于10us,采用C51中的循环语句来实现。 //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义 #include "STC89C5xRC_RDP.h" //应用层头文件 //#include "c51_gpio.h" #include "c51_ledtube.h" //#include "c51_key.h" #include "c51_timer.h" //#include "c51_exit.h" //#include "c51_lcd1602.h" //#include "c51_iic.h" #include "c51_tx1838.h" #include "c51_uart.h" extern void delay(unsigned int cnt); extern void delay_us(unsigned int us);//delay us; extern void delay_ms(unsigned int Ms);//delay Ms; #endif
//c51_tx1838.h文件
#ifndef __C51_TX1838_H__ #define __C51_TX1838_H__ #define NEC P32 //红外线接收头 extern unsigned char tx1838_cnt; extern unsigned char tx1838_type; extern unsigned char tx1838_data[4]; extern void sys_tx1838_test(void); #endif
//c51_timer.c文件
#include "includes.h" void sys_timer_init(void) { sys_timer0_init(); sys_timer1_init(); sys_timer2_init(); sys_wdog_init(); clr_wdg(); } /*------------------------------------------------ 定时器初始化子程序 ------------------------------------------------*/ void sys_timer0_init(void) { TMOD |= 0x01; //使用模式1,16位定时器,使用"|"符号可以在使用多个定时器时不受影响 TH0=0x00; //给定初值,这里使用定时器最大值从0开始计数一直到65535溢出 TL0=0x00; //EA=1; //总中断打开 等最后一个中断打开 //ET0=1; //定时器中断打开 TR0=1; //定时器开关打开 } /*------------------------------------------------ 定时器初始化子程序 ------------------------------------------------*/ void sys_timer1_init(void) { TMOD |= 0x20; //使用模式2, TH1=0x05; //给定初值,这里使用定时器最大值从5开始计数一直到255溢出 TL1=0x00; //EA=1; //总中断打开 //ET1=1; //定时器中断打开 //TR1=1; //定时器开关打开 } /*------------------------------------------------ 定时器初始化子程序 ------------------------------------------------*/ void sys_timer2_init(void) { RCAP2H = 0/256;// RCAP2L = 0/256; //ET2=1; //打开定时器中断 //EA=1; //打开总中断 //TR2=1; //打开定时器开关 } void sys_wdog_init(void) { //WDT_CONTR = 0x35; } void clr_wdg(void) { //WDT_CONTR = 0x35; } /*------------------------------------------------ 定时器中断子程序 ------------------------------------------------*/ void Timer0_isr(void) interrupt 1 { TH0=0x00; //重新赋值 TL0=0x00; //sys_led_test1(); //流水灯操作 } /*------------------------------------------------ 定时器中断子程序 ------------------------------------------------*/ void Timer1_isr(void) interrupt 3 { //sys_led_test1(); //流水灯操作 } /*------------------------------------------------ 定时器中断子程序 ------------------------------------------------*/ void Timer2_isr(void) interrupt 5//定时器2中断 { TF2=0; //sys_led_test1(); //流水灯操作 }
//c51_timer.h文件
#ifndef __C51_TIMER_H__ #define __C51_TIMER_H__ extern void sys_timer_init(void); extern void sys_timer0_init(void); extern void Timer0_isr(void); extern void sys_timer1_init(void); extern void Timer1_isr(void); extern void sys_timer2_init(void); extern void Timer2_isr(void); extern void sys_wdog_init(void); extern void clr_wdg(void); #endif
//c51_ledtube.c文件
#include "includes.h" // 显示段码值01234567,可对应原理图查看显示不同图形对应的引脚高点电平配置状态 unsigned char const EL[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,\ 0x77,0x7c,0x39,0x5e,0x79,0x71};//0-F /******************************************************** 函数名称:sys_ledtube_on1 函数功能:点亮一个数码管全为亮起来 入口参数: 出口参数: 修 改: 内 容: ********************************************************/ void sys_ledtube_on1(void) { //根据原理图,将P0口全部输出高电平,P2选择0号数码管 P0=0xFF;//取显示数据,段码 P2=0; //取位码 } /******************************************************** 函数名称:sys_ledtube_on2 函数功能:显示一组数据 入口参数: 出口参数: 修 改: 内 容: ********************************************************/ static unsigned char ledtube_cnt = 0; void sys_ledtube_on2(void) { ledtube_cnt++; if(ledtube_cnt>7) { ledtube_cnt = 0; } P0 = 0x00; //防止切换数码管瞬间有虚影出现 P2 = 0x00; P0 = EL[ledtube_cnt]; //取显示数据,段码 P2 = ledtube_cnt; //取位码 //根据人眼适应虚影缓冲时间为50ms左右 //我们调整delay在500以下可以看到明显的看起来是一串数据一起显示 delay(100); } /******************************************************** 函数名称:sys_keynum_ledon 函数功能:显示按键数值 入口参数:按键数值 出口参数: 修 改: 内 容: ********************************************************/ void sys_keynum_ledon(unsigned char num) { //根据原理图,将P0口全部输出高电平,P2选择0号数码管 P0=EL[num];//取显示数据,段码 P2=0; //取位码 }
//c51_ledtube.h文件
#ifndef __C51_LEDTUBE_H__ #define __C51_LEDTUBE_H__ extern unsigned char const EL[]; extern void sys_ledtube_on1(void); extern void sys_ledtube_on2(void); extern void sys_keynum_ledon(unsigned char num); #endif
//c51_tx1838.c文件
#include "includes.h" unsigned char tx1838_cnt = 0; unsigned char tx1838_type = 0; unsigned char tx1838_data[4] = {0}; void sys_tx1838_test(void) { unsigned int time = 0; if(!NEC) { while(!NEC); //等待低电平结束 TH0 = 0; TL0 = 0; delay_us(1); if(tx1838_type==4) { tx1838_type =1; } else { while(NEC) //等待数据位计时 { if(TH0>30) { tx1838_type =1; break; } } } time =(TH0<<8)+TL0; //取得脉冲宽度 switch(tx1838_type) { case 1: { if(time>3000 && time<7000) //接收到数据 { tx1838_type = 2; tx1838_cnt = 0; //接收位数量清0 tx1838_data[0] = 0; tx1838_data[1] = 0; tx1838_data[2] = 0; tx1838_data[3] = 0; } else if(time>2000 && time<3000)//接收到重复码 { tx1838_type = 3; } else { tx1838_type = 1; } break; } case 2: { tx1838_cnt ++ ; if(time>168 && time<800) //接收到数据位为0的时间长度 { } else { if(time>1100 && time<1800) //接收到数据位为1的时间长度 { if(tx1838_cnt<=8) { tx1838_data[0] |= (1<<(tx1838_cnt-1)); } else if(tx1838_cnt<=16) { tx1838_data[1] |= (1<<(tx1838_cnt-9)); } else if(tx1838_cnt<=24) { tx1838_data[2] |= (1<<(tx1838_cnt-17)); } else if(tx1838_cnt<=32) { tx1838_data[3] |= (1<<(tx1838_cnt-25)); } else { tx1838_type = 1; tx1838_cnt = 0; //接收位数量清0 tx1838_data[0] = 0; tx1838_data[1] = 0; tx1838_data[2] = 0; tx1838_data[3] = 0; } } else //重新解码 //接收到引导码或者结束码,或者接收到的是重复码,本章节不进行演示 { tx1838_type = 1; tx1838_cnt = 0; //接收位数量清0 tx1838_data[0] = 0; tx1838_data[1] = 0; tx1838_data[2] = 0; tx1838_data[3] = 0; } } if(tx1838_cnt>=32) { tx1838_type = 4; switch(tx1838_data[3])//判断数码值 { case 255:sys_keynum_ledon(0);break;//0 显示相应的按键值 case 254:sys_keynum_ledon(1);break;//1 case 253:sys_keynum_ledon(2);break;//2 case 252:sys_keynum_ledon(3);break;//3 case 251:sys_keynum_ledon(4);break;//4 case 250:sys_keynum_ledon(5);break;//5 case 249:sys_keynum_ledon(6);break;//6 case 248:sys_keynum_ledon(7);break;//7 case 247:sys_keynum_ledon(8);break;//8 case 246:sys_keynum_ledon(9);break;//9 显示相应的按键值 } } break; } case 3: //重复码 { tx1838_type = 1; sys_keynum_ledon(11); break; } case 4: //结束码 { tx1838_type = 1; tx1838_cnt = 0; //接收位数量清0 break; } case 5: { break; } default: tx1838_type = 1; break; } } }
//c51_uart.h文件
#ifndef __C51_UART_H__ #define __C51_UART_H__ extern void Uart_SendChar(unsigned char dat); extern char putchar(char c);//重定向 extern void sys_uart_init(void); extern void UART_isr(void); #endif
3 C51 UART总结
这些总结要点提供了关于C51中UART功能的基本概述。请注意,具体的UART配置和操作方法可能因不同的C51系列和具体的芯片型号而有所差异。为了准确使用C51的UART功能,请参考相应的芯片手册、官方文档或相关资料,以获取详细的UART配置和操作说明。