【嵌入式】基于STC89C52RC的51单片机学习(六)——串口

一、串口的基本认识

        串口是串型通信接口的简称,也叫串行通信接口或者串行通讯接口,是设备间接线通信的一种方式。

        特点:

       (1)串行接口是指数据一位位地顺序传送,其特点是线路简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传输速度较慢。

       (2)双向通信,全双工。

二、关于电器标准和协议

         串行接口按电气标准及协议来分包括RS-232-C、RS-422RS485等。RS-232-CRS-422RS-485 标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

(1) RS-232

        也称标准 串口 ,最常用的一种 [ 串行通讯接口 , 比如我们的电脑主机的 9 针串口 ,最高速率为 20kb/s RS-232是为 点对点 (即只用一对收、发设备)通讯而设计的,其传送距离最大为约 15 米。所以 RS-232 适合本地设备之间的通信。

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第1张图片

(2) RS-422

        由于接收器采用高输入阻抗和发送 驱动器 RS232 更强的 驱动能力 ,故允许在相同传输线上连接多个接 收 节点 ,最多可接 10 个节点。即一个主设备( Master ),其余为从设备( Slave ),从设备之间不能通信,所以RS-422 支持一点对多点的双向通信。
        RS-422的最大传输距离为1219 米,最大传输速率为 10Mb/s 。平衡双绞线的长度与传输速率成反比

(3) RS-485

 是从RS-422基础上发展而来的,无论四线还是二线连接方式总线上可多接到32个设备。

三、串口的电平 

     我们经常听说的UART 是指异步串行(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。 UART包含TTL电平的串口RS232电平的串口

(1)RS232电平

        逻辑1 -3~-15V 的电压 , 逻辑 0 3~15V 的电压
        笔记本通过RS232电平和单片机通信

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第2张图片

(2)TTL

        TTL是 Transistor-Transistor Logic ,即晶体管 - 晶体管逻辑的简称,它是计算机处理器控制的设备内部各部分之间通信的标准技术。TTL 电平信号应用广泛,是因为其数据表示采用二进制规定, +5V等价于逻辑 ”1” 0V 等价于逻辑 ”0”
        数字电路中,由TTL 电子元器件组成电路的电平是个电压范围,规定:
        输出高电平>=2.4V ,输出低电平 <=0.4V
        输入高电平>=2.0V ,输入低电平 <=0.8V
        笔记本电脑通过TTL 电平与单片机通信
        TX发送线(端口) 3.1
        RX接收线 ( 端口) 3.0
        USB转 TTL ,使用 ch340 通信

(3) 串口接线方式

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第3张图片

 【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第4张图片

四、串口发送收发数据过程 

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第5张图片

 串口收发数据的时序电路图

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第6张图片

五、相关的寄存器 

        在串口中,输入/ 输出数据缓冲器都叫做 SBUF , 都用 99H 地址码,但是是两个独立的 8 位寄存器
        代码体现为: 想要接收数据 char data = SBUF 想要发送数据 SBUF = data
        回忆UART 是异步串行接口,通信双方使用时钟不同,因为双方硬件配置不同,但是需要约定通信速度,叫做波特率。
        直接写代码先玩一下再学相关的寄存器,我们先用软件生成串口初始化的代

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第7张图片

(1) 通过串口发送一个字符给电脑

#include "reg52.h"
#include "intrins.h"


sfr AUXR  = 0x8e;

void UartInit(void)		//[email protected]
{
	  PCON &= 0x7F;
	  SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN允许串口收数据
	  SCON |= 0x50;
	  TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器
	  TMOD |= 0x20;
	  TL1   = 0xFD;//设置定时器初值
	  TH1   = 0xFD;
	  TR1 = 1;		//启动定时器1
}

void Delay10ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 18;
	j = 235;
	do
	{
		while (--j);
	} while (--i);
}


void sentByte(char data_msg)
{
	   SBUF = data_msg;
	   //Delay10ms();
	   while(!TI);
	   TI = 0;
}

void main()
{
	 char data_msg = 'a';
   
	//配置C51的串口通信方式
     
	  UartInit();
       while(1){
	// 往发送缓冲区写入数据,就完成发送数据
			Delay1000ms();
            sentByte(data_msg);
		}
}

下面我们来了解一下相关寄存器(这些寄存器在芯片手册中都可以找到,下面只是截取了一部分)

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第8张图片

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第9张图片

  串口不同工作模式下波特率还可能与定时器有关,所以还需要进行定时器初值的计算,计算方法如上图所示。【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第10张图片

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第11张图片

        通过上面软件给我们初始化的串口来看,配置了寄存器SCON寄存器,我们一般需要设置SM1、SM2、REN、以及TI、RI

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第12张图片

        PCON,我们主要关心SMOD和SOMD0,一般情况下SMOD=0;SOMD0=0;

【嵌入式】基于STC89C52RC的51单片机学习(六)——串口_第13张图片

        当然还有与中断相关的寄存器,这在我们以后的代码中会用到。
     

(2) 发送一个字符串给电脑

#include "reg52.h"
#include "intrins.h"
#include 

sfr AUXR  = 0x8e;
 
void UartInit(void)		//[email protected]
{
	  PCON &= 0x7F;
	  SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN不允许串口收数据
	  SCON |= 0x40;
	  TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器
	  TMOD |= 0x20;
	  TL1   = 0xFD;//设置定时器初值
	  TH1   = 0xFD;
	  TR1 = 1;		//启动定时器1
	
	  ES = 1;//串口中断打开
	  EA = 1;//总中断打开
}


void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void sentByte(char data_msg)
{
	   SBUF = data_msg;
	   //Delay10ms();
	   while(!TI);
	   TI = 0;
}



void sentString(char *str)
{
	 
	 while(*str){
		 sentByte(*str);
		 str++;
	 }
}


void main()
{
	//配置C51的串口通信方式
	  UartInit();
       while(1){
	// 往发送缓冲区写入数据,就完成发送数据
			Delay1000ms();
			sentString("Hello world!\r\n");
		}
}

        这里我们在发送字符串时,若是sentByte(char data_msg)函数中没有延迟或者while条件判断则会出现错误,因为不断向SBUF中发送数据,但是电脑还没有来的及从接收缓冲区中取回,就发现后面又送来数据了,会把之前缓冲区中的数据给淹没,所以我们要设置一个延时。

        更好的解决方法是开启串口中断,判断TI(串行发送第8位数据结束时,硬件自动置一,软件清零)是否为一,是则发送下一个并清零。

(3) 每隔一秒,单片机向PC发送一个字符串 ,PC上位机串口调试助手发送字母o点亮LED,发送字母c关闭LED

#include "reg52.h"
#include "intrins.h"


sfr AUXR  = 0x8e;
sbit led5 = P3^7;


void UartInit(void)		//[email protected]
{
	  PCON &= 0x7F;
		SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN允许串口收数据
	  SCON |= 0x50;
	  TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器
	  TMOD |= 0x20;
	  TL1   = 0xFD;//设置定时器初值
	  TH1   = 0xFD;
	  TR1 = 1;		//启动定时器1
}

void Delay10ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 18;
	j = 235;
	do
	{
		while (--j);
	} while (--i);
}


void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sentByte(char data_msg)
{
	   SBUF = data_msg;
	   //Delay10ms();
	   while(!TI);
	   TI = 0;
}


void sentString(char *str)
{
	 
	 while(*str){
		 sentByte(*str);
		 str++;
	 }
}

void main()
{
	 char cmd;
   
	//配置C51的串口通信方式
	  UartInit();
    while(1){
	// 往发送缓冲区写入数据,就完成发送数据
			Delay1000ms();
			sentString("Hello world!\r\n");
	//怎么知道收到数据,查询RI的值,如果RI是1(收到数据后由硬件置1)
		    if( RI == 1){			 
  					RI = 0;
					  cmd = SBUF;
					  if( cmd == 'o'){
							led5 = 0;
						}
						else{
							led5 = 1;
						}
        }
}

这里我们必须开启接收使能,在SCON寄存器中配置即可。

同过判断RI是否等于1来判断第八个bit接收是否结束,结束后清零,然后跟据SBUF中的数据来决定是否点灯。

我们试试发送一个字符串来控制灯的关闭,因此我们需要设置一个数组了,当输入open,打开,close关闭灯 

#include "reg52.h"
#include "intrins.h"
#include 

#define Size 12

sfr AUXR  = 0x8e;
sbit led5 = P3^7;
sbit led6 = P3^6;

char cmd[Size];
   
void UartInit(void)		//[email protected]
{
	  PCON &= 0x7F;
		SCON &= 0x0F;//设置串行工作方式为8位波特率可变,REN允许串口收数据
	  SCON |= 0x50;
	  TMOD &= 0x0F;//设置定时器1的工作方式为8位自动重装载定时器
	  TMOD |= 0x20;
	  TL1   = 0xFD;//设置定时器初值
	  TH1   = 0xFD;
	  TR1 = 1;		//启动定时器1
	
	  ES = 1;//串口中断打开
	  EA = 1;//总中断打开
}

void Delay10ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 18;
	j = 235;
	do
	{
		while (--j);
	} while (--i);
}


void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void sentByte(char data_msg)
{
	   SBUF = data_msg;
	   //Delay10ms();
	   while(!TI);
	   TI = 0;
}

void sentString(char *str)
{
	 
	 while(*str){
		 sentByte(*str);
		 str++;
	 }
}

void main()
{
  led6 = 1;
	//配置C51的串口通信方式
	  UartInit();
    while(1){
	// 往发送缓冲区写入数据,就完成发送数据
			Delay1000ms();
			sentString("Hello world!\r\n");
		}
}

void Uart_Handler() interrupt 4
{
	   static int i=0;
		//怎么知道收到数据,查询RI的值,如果RI是1(收到数据后由硬件置1)
		 if( RI == 1){			 
         RI = 0;
				cmd[i++] = SBUF;
			  if( i>=Size ){
					  i = 0;
					  led6 = !led6;
				}
				if( strstr(cmd ,"en")){
						led5 = 0;
					  i = 0;
					  memset(cmd,'\0',Size);
				}
				if( strstr(cmd ,"se")){
						led5 = 1;
					  i = 0;
					  memset(cmd,'\0',Size);
				}
    }
}

 这里我们使用了串口的中断函数,当发送完第8位或接收完第8位都会发生中断,我们只判断是否接收完第八位,并将接收到的字符存入数组中,数组最大为12。由于我们收到的字符存放在数组中的位置有很多可能,所以我们只简单判断PC端输入的字符中是否含有en(open)或se(close)字串,用到了strstr()函数,烧入代码后会发现还存在一些问题,比如在PC端通过串口调试助手发送的字符过多的话灯不受控制,这里我们当i超过12时就清零并将led6点亮作为提示。

串口就先到这里,以后学习一些串口的应用使我们能更好理解和掌握。 

你可能感兴趣的:(51单片机,学习,嵌入式硬件)