单片机的串口通讯编程

一、串口

1.1串口基本认知

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了本,特别适用于远距离通信,但传送速度较慢
小结:
1).是设备间接线通讯的一种方式
2).数据是一位一位的传输
3).双向全双工通讯
4).传输速度相对较慢

1.2串口标准与协议

串行接口常用接口有RS-232、RS-422、RS-485接口

**RS-232**
由于RS-232其发送电平与接收电平的差仅为2V至3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。
**RS-422**
典型的RS-422是四线接口。实际上还有一根信号地线,共5根线。其DB9连接器引脚定义。由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Salve),从设备之间不能通信,所以RS-422支持点对多的双向通信。
RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。
RS-422需要一终接电阻,要求其阻值约等于传输电缆的特性阻抗。在短距离传输时可不需终接电阻,即一般在300米以下不需终接电阻。终接电阻接在传输电缆的最远端。

**RS-485**

由于RS-485是从RS-422基础上发展而来的,所以RS-485许多电气规定与RS-422相仿。如都采用平衡传输方式、都需要在传输线上接终接电阻等。RS-485可以采用二线与四线方式,二线制可实现真正的多点双向通信,而采用四线连接时,与RS-422一样只能实现点对多的通信,即只能有一个主(Master)设备,其余为从设备,但它比RS-422有改进, 无论四线还是二线连接方式总线上可多接到32个设备。
RS-485与RS-422的不同还在于其共模输出电压是不同的,RS-485是-7V至+12V之间,而RS-422在-7V至+7V之间,RS-485接收器最小输入阻抗为12k剑 鳵S-422是4k健; 旧峡梢运礴S-485满足所有RS-422的规范,所以RS-485的驱动器可以用在RS-422网络中应用。
RS-485与RS-422一样,其最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能获得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s
RS-485需要2个终接电阻,其阻值要求等于传输电缆的特性阻抗。在短距离传输时可不需终接电阻,即一般在300米以下不需终接电阻。终接电阻接在传输总线的两端。

 1.3接口电平

RS-232电平
逻辑1的电平为-3~-15V,逻辑0的电平为+3~+15V,注意电平的定义反相了一次
TTL电平
+5V等价于逻辑“1”,0V等价于逻辑“0”(采用二进制来表示数据时)。
输出高电平>=2.4V,输出低电平<=0.4V;
输入高电平>=2.0V,输入低电平<=0.8V

1.4串口通讯接线

**两个芯片之间的通讯**

 芯片与计算机串口连接

 

二、串口配置与基础印象

2.1基础印象

对于输入输出的数据需要先传送到SBUF寄存器中

想要接收数据

char datas= = SBUF;

想要发送数据
 

SBUF = datas

2.2串口配置与工作模式

2.2.1:串行口相关寄存器

 2.2.2:串行口控制器SCON与PCON

串行口控制器SCON:可寻址位

SFR nameaddressb7b6b5b4b3b2b1b0
SCON98HSM0/FESM1SM2RENTB8RB8TIRI

SM0与SM1组合之后的工作方式

 REN:允许/禁止串行接口接收控制位,由软件控制,即当REN=1时,则为允许接收,可启动RxD,开始接收消息,反之则禁止接收

TI:发送请求中断标志位。当发送的8位数据结束之后,硬件内部自动置位,TI=1,向主机发送中断请求,相应中断之后必须用软件进行复位,即TI=0。

RI:接收请求中断标志位。当接收的8位数据结束之后,硬件内部自动置位,RI=1,向主机发送中断请求,相应中断之后必须用软件进行复位,即RI=0。

 串行口控制器PCON:不可寻址位

SFR nameaddressb7b6b5b4b3b2b1b0
PCON87HSMODSMOD0-POFGF1GF0PDIDL

SMOD:波特率选择。当软件进行置位后,即SMOD = 1,则串行通讯方式1,2,3比特率加倍;当SMOD=0,则各个工作方式的波特率加倍。

 2.2.3:串行口中断控制器

中断允许寄存器IE(可寻址位)

SFR nameaddressb7b6b5b4b3b2b1b0
IEA8HEA-ET2ESET1EX1ET0EX0

EA:CPU总中断控制,EA=1,CPU开放中断,EA=0,CPU停止中断;在各个中断中,首先有EA控制,然后在由自己中断进行控制

ES:串口中断允许标志,ES =1,允许中断;ES=0,禁止中断

三.串口配置方式

配置串口要求:晶振频率11.0592Mhz,波特率为9600,选择串口UART1,工作方式配置为1,允许串行接收控制,允许接收与发送中断标志位

SCON位 :0   1    0    1    0    0    1    1

PCON位: 0   1    0    1    0    0    0    0

由波特率计算出定时器初始值:设定时器的初始值为“X”

9600=(2^0)/32)*(11059200/12/(256-X))

解出X = 3,定时器溢出率=10#253=16#FD

代码实现

void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	AUXR &= 0xBF;		//定时器1时钟为Fosc/12,即12T
	AUXR &= 0xFE;		//串口1选择定时器1为波特率发生器
	TMOD &= 0x0F;		//清除定时器1模式位
	TMOD |= 0x20;		//设定定时器1为8位自动重装方式
	TL1 = 0xFD;		//设定定时初值
	TH1 = 0xFD;		//设定定时器重装值
	ET1 = 0;		//禁止定时器1中断
	TR1 = 1;		//启动定时器1
}

四.程序练习

1.通过串口向电脑每次间隔一秒发送一个字符“A”

#include <reg52.h>
#include "intrins.h"
sfr AUXR = 0x8E;
/*配置C51串口的通信方式*/
void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x40; //配置串口工作方式1,REN不使能接收
	TMOD &= 0xF0;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 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 main()
{
	char data_msg = 'a';  //data_msg为内部数据参数,并给定数据字符“a“
	UartInit();
	while(1)
		{
		Delay1000ms();
		//往发送缓冲区写入数据,就完成数据的发送
		SBUF = data_msg;
	  }
}

2.通过串口向电脑每次间隔一秒发送字符

#include <REGX52.H>
#include "intrins.h"
/*配置C51串口的通信方式*/
void UartInit()		//9600bps@11.0592MHz
{
	AUXR = 0x01;
	SCON = 0x40; //配置串口工作方式1,REN不使能接收
	TMOD &= 0xF0;
	TMOD |= 0x20;//定时器1工作方式位8位自动重装
	
	TH1 = 0xFD;
	TL1 = 0xFD;//9600波特率的初值
	TR1 = 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);
}
/*由于SBUF是8位为一组,因此需要建立一个字节发送函数,以发送相应的字节
形式参数:datas,用于承接发送的实际内容*/
void sendbyte(char datas)
{
	SBUF  = datas;
	while(TI == 0);  //当发送数据的时候,TI=0,这时while循环满足,不执行下面的程序,当TI=1时,while循环不满足,执行下面的程序:复位TI
	TI = 0;
}
/*发送字符串函数
形式参数:char型字符*/
void sendstring(char* str)
{
	sendbyte(*str);
	while(*str !='\0')
	{
		sendbyte(*str);
		str++;
	}		//当字符串没有结尾的时候,则要调用sendbyte函数,进行函数的发送
}
void main()
{
	UartInit();
	while(1)
	{
		Delay1000ms();
		sendstring("youare handsome\r\n")  //向sendstring形式参数中传输实际参数
	}
}

3.通过串口接收到的数据控制指示灯亮灭

#include <REGX52.H>
#include <intrins.h>
sbit LED = P2^0;
void UartInit()		
{
	PCON &= 0x7F;	
	SCON = 0x50;	
	TMOD &= 0x0F;	
	TMOD |= 0x20;	
	TL1 = 0xFD;		
	TH1 = 0xFD;		
	ET1 = 0;		
	TR1 = 1;		
}
void Delay1000ms()		
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
/*void sendbyte(char datas)
{
	SBUF = datas;
	while(TI == 0);
	TI=0;
}
void sendstring(char *str)
{
	while(*str !='\0')
	{
		sendbyte(*str)
		str++;
	}
}
*/
void main()
{
	char cmd;
	LED = 0;   //LED指示灯初始化为熄灭状态
	UartInit();
	while(1)
	{
		Delay1000ms();
		RI = 0;		//当接收到数据完成之后,RI=0
		cmd = SBUF;	//当RI=0之后,将SBUF中的数据读取到cmd中
		/*当读取到的字符为“O”时,点亮LED;当读取到字符为“C”时,熄灭LED*/
		if(cmd = 'O')
		{
			LED = 0;
		}
		if(cmd = 'C')
		{
			LED = 1;
		}

	}

}

经过测试,这段代码没有功能上的问题,但是会存在一定的延迟

4.通过串口中断的方法控制LED指示灯

中断优先级

#include "reg52.h"
#include "intrins.h"
sbit D5 = P3^7;
char cmd;

void UartInit(void)		
{
	SCON = 0x50; 
	TMOD &= 0xF0;
	TMOD |= 0x20;
	TH1 = 0xFD;
	TL1 = 0xFD;
	TR1 = 1;
	EA = 1;
	ES = 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 sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);
	TI = 0;
}

void sendString(char* str)
{
	while( *str != '\0'){
		sendByte(*str);
		str++;
	}
}
*/
void main()
{

	D5 = 1;
	//配置C51串口的通信方式
	UartInit();
	
	while(1){
		Delay1000ms();
		//往发送缓冲区写入数据,就完成数据的发送
		/*sendString("chenlichen shuai\r\n");*/
	}
}

void Uart_Handler() interrupt 4
{
	if(RI)//中断处理函数中,对于接收中断的响应
	{
			RI = 0;//清除接收中断标志位
			cmd = SBUF;
			if(cmd == 1){
				D5 = 0;//点亮D5
			}
			if(cmd == 0){
				D5 = 1;//熄灭D5
			}
	}
		
	if(TI);
}

5.通过发送字符方式控制指示灯

#include "reg52.h"
#include "intrins.h"
#include <string.h>

#define SIZE 12
sbit D5 = P3^7;
char cmd[SIZE];

void UartInit(void)		
{
	SCON = 0x50; 
	TMOD &= 0xF0;
	TMOD |= 0x20;
	TH1 = 0xFD;
	TL1 = 0xFD;
	TR1 = 1;
	EA = 1;
	ES = 1;
}

void Delay1000ms()		
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}
/*
void sendByte(char data_msg)
{
	SBUF = data_msg;
	while(!TI);
	TI = 0;
}

void sendString(char* str)
{
	while( *str != '\0'){
		sendByte(*str);
		str++;
	}
}
*/
void main()
{
	D5 = 1;
	//配置C51串口的通信方式
	UartInit();
	while(1)
		{
		Delay1000ms();
		//往发送缓冲区写入数据,就完成数据的发送
		sendString("chenlichen shuai\r\n");
	  }
}

void Uart_Handler() interrupt 4
{
	static int i = 0;//定义字符串的指针,静态变量,被初始化一次
	if(RI)//中断处理函数中,对于接收中断的响应
	{
			RI = 0;//清除接收中断标志位
			cmd[i] = SBUF; //将SBUF中的数据读取到cmd中
			i++;  //将指针进行右移
			if(i == SIZE)
			{
				i = 0;
			}  //当移动到最后一位时,将指针移动到首位
			if(strstr(cmd,"en"))
			{
				D5 = 0;//点亮D5
				i = 0;
				memset(cmd,'\0',SIZE);
			}  //当接收到字符中有‘en’(open),启动LED,同时将cmd中数据清空
			if(strstr(cmd,"cl"))
			{
				D5 = 1;//熄灭D5
				i = 0;
				memset(cmd,'\0',SIZE);
			}  //当接收到字符中有‘cl’(close),关闭LED,同时将cmd中数据清空
	}
		
	if(TI);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值