一、串口
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 name | address | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
SCON | 98H | SM0/FE | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
SM0与SM1组合之后的工作方式
REN:允许/禁止串行接口接收控制位,由软件控制,即当REN=1时,则为允许接收,可启动RxD,开始接收消息,反之则禁止接收
TI:发送请求中断标志位。当发送的8位数据结束之后,硬件内部自动置位,TI=1,向主机发送中断请求,相应中断之后必须用软件进行复位,即TI=0。
RI:接收请求中断标志位。当接收的8位数据结束之后,硬件内部自动置位,RI=1,向主机发送中断请求,相应中断之后必须用软件进行复位,即RI=0。
串行口控制器PCON:不可寻址位
SFR name | address | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
PCON | 87H | SMOD | SMOD0 | - | POF | GF1 | GF0 | PD | IDL |
SMOD:波特率选择。当软件进行置位后,即SMOD = 1,则串行通讯方式1,2,3比特率加倍;当SMOD=0,则各个工作方式的波特率加倍。
2.2.3:串行口中断控制器
中断允许寄存器IE(可寻址位)
SFR name | address | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
IE | A8H | EA | - | ET2 | ES | ET1 | EX1 | ET0 | EX0 |
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”
解出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);
}