串行通信基础知识
计算机与外界的信息交换称为通信。通信的基本方式可分为并行通信和串行通信两种。
并行通信是指数据的各位同时在多根数据线上发送或接收。
并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。
串行通信是指 使用一条数据线,将数据一位一位地依次传输,每 一位数据占据一个固定的时间长度。其只需要少数几条线就可以 在系统间交换信息,特别适用于计算机与计算机、计算机与外设 之间的远距离通信。
串行通信的特点:传输线少,长距离传送时成本低,但数据的传送控制比并行通信复杂。
异步通信与同步通信
异步通信 异步通信是指通信的发送与接收设备使用各自的时钟控制数据 的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。
异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的时间间隔是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。
同步通信 同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。
串行通信的制式
在串行通信中,数据是在两个站之间传送的。按照数据传送方向,串行通信可分为三种制式。
传输速率
比特率是每秒钟传输二进制代码的位数, 单位是:位/秒(bps)。如每秒钟传送 240个字符,而每个字符格式包含10位(1 个起始位、1个停止位、8个数据位),这 时的比特率为: 10位×240个/秒 = 2400 bps
串行接口的结构
在逻辑上,SBUF只有一个,它既表示发送寄存器,又表示接收寄存器,具有同一个单元地址99H。
但在物理结构上,则有两个完全独立的SBUF,一个是发送缓冲寄存器SBUF,另一个是接收缓冲寄存器SBUF。
如果CPU写SBUF,数据就会被送入发送寄存器准备发送;如果CPU读SBUF,则读入的数据一定来自接收缓冲器。即CPU对SBUF的读写,实际上是分别访问上述两个不同的寄存器。 a = SBUF; SBUF = a;
串行控制寄存器SCON 地址98H
用于设置串行口的工作方式、监视串行口的工作状态、控制发送与接收的状态等。它是一个既可以字节寻址又可以位寻址的8位特殊功能寄存器。其格式如下图。
REN:串行接受允许控制位。该位由软件置位或复位。当REN=1,允许接收;当REN=0,禁止接收。
RI:接收中断标志位。RI=1,表示一帧数据接收结束。可由软件查询RI位标志,也可以向CPU申请中断。 注意:RI在任何工作方式下也都必须由软件清0。
TI:发送中断标志位。TI=1,表示已结束一帧数据发送,可由软件查询TI位标志,也可以向CPU申请中断。 注意:TI在任何工作方式下都必须由软件清0。
串行发送中断TI和接收中断RI的中断入口地址是同是0023H,因此在中断程序中必须由软件查询TI和RI的状态才能确定究竟是接收还是发送中断,进而作出相应的处理。单片机复位时,SCON所有位均清0。
SM0和SM1为工作方式选择位,可选择四种工作方式:
电源控制寄存器PCON
SMOD:在串行口工作方式 1、2、3 中, 是波特率加倍位 (产生高波特率时启用平时不用,比如用11.0592晶振产生57600波特率时就要设置成SMOD=1)
=1 时,波特率加倍(PCON=0x80;)
=0 时,波特率不加倍。(PCON=0x00;) (在PCON中只有这一个位与串口有关)
中断允许控制寄存器IE(A8H)
ES,串行口中断允许位;=0 时禁止中断; =1 时允许中断。
波特率的计算
在串行通信中,收发双方对发送或接收数据的速率要有约定。通过软件可对单片机串行口编程为四种工作方式,其中方式0和方式2的波特率是固定的,而方式1和方式3的波特率是可变的,由定时器T1的溢出率来决定。
方式0的波特率 = fosc/12
方式2的波特率 =(2^SMOD/64)· fosc
方式1的波特率 =(2^SMOD/32)·(T1溢出率)
方式3的波特率 =(2^SMOD/32)·(T1溢出率)
当T1作为波特率发生器时,最典型的用法是使T1工作在自动再装入的8位定时器方式(即方式2,且TCON的TR1=1,以启动定时器)。这时溢出率取决于TH1中的计数值。
T1 溢出率 = fosc /{12×[256 -(TH1)]}
常用串口波特率: 300、600、1200、2400、4800、9600、19200 ……115200;
串行口工作之前需对相关寄存器进行配置,设定其工作模式。
(1)设置T1的工作方式(编程TMOD寄存器);
(2)计算T1的初值,装载TH1、TL1;
(3)启动T1(编程TCON中的TR1位);
(4)确定串行口控制(编程SCON寄存器);
如需串行口在中断方式工作时,要进行中断设置编程IE寄存器。
串行通信接口标准
一、RS-232C接口 RS-232C是EIA(美国电子工业协会)1969年修订RS-232C标准。RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准。
1、机械特性 RS-232C接口规定使用25针连接器,连接器的尺寸及每个插针的排列位置都有明确的定义。(阳头)
2、功能特性
典型的RS-232C通信电路 解决电平不兼容的问题
将计算机的232电平(逻辑‘1’ 是+3V to +15V,逻辑‘0’是 -3V to -15V)转换为单片机TTL电平(0V to 0.8V,逻辑‘1’ 是+3V to +5V,逻辑‘0’是 0V to 0.8V )
串口的发送(给计算机发送数据):
#include <reg52.h> #define uchar unsigned char #define uint unsigned int uchar num; void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); } void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装
//波特率:9600 TH1=256- 11.0592*10^6/(9600*32*12)=253 = 0xFD TH1 = 0xfd; TL1 = 0xfd; //比特率9600 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 REN = 1; //串口允许接收 } void main() { UART_init(); //串口初始化 while(1)
{
SBUF = num; //发送数据给计算机
while(!TI);//发送成功
TI= 0; //软件清中断
num++;
delay(500);
} }
单片机不停地给计算机发送数据:
串口的接收(计算机发送数据给单片机 并点亮第一个灯):
#include <reg52.h> #define uchar unsigned char #define uint unsigned int uchar num; void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); } void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装 TH1 = 0xfd; TL1 = 0xfd; //比特率9600 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 REN = 1; //串口允许接收 } void main() { UART_init(); //串口初始化 while(1) { while(!RI);//接收成功 P1 = SBUF; //接收计算机发的数据,并给P1 IO RI= 0; //软件清中断 } }
利用串口中断进行计算机与串口通信:
#include <reg52.h> #define uchar unsigned char #define uint unsigned int uchar num; void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装 TH1 = 0xfd; TL1 = 0xfd; //比特率9600 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 REN = 1; //串口允许接收 EA = 1; //开总中断 ES =1; //开串口中断 } void main() { UART_init(); //串口初始化 while(1); } void UART() interrupt 4 { if(RI)//如果计算机发送给电片机数据成功 { num = SBUF;//将数据取出来 P1 = num; num ++; RI =0; SBUF = num;//将数据在单片机端送到Buff,给计算机 while(!TI); //发送完成 TI = 0;//清中断 } }
串口接收一字节数码管以十进制显示
/* 以4800bps从计算机发任意一字节数据,通过数码管以十进制显示 的形式显示出来。 */ #include <reg52.h> #define uchar unsigned char #define uint unsigned int sbit we = P2^7; //数码管位选 sbit du = P2^6; //数码管段选 /*数码管段码*/ uchar code leddata[]={ 0x3F, //"0" 0x06, //"1" 0x5B, //"2" 0x4F, //"3" 0x66, //"4" 0x6D, //"5" 0x7D, //"6" 0x07, //"7" 0x7F, //"8" 0x6F, //"9" 0x77, //"A" 0x7C, //"B" 0x39, //"C" 0x5E, //"D" 0x79, //"E" 0x71, //"F" 0x76, //"H" 0x38, //"L" 0x37, //"n" 0x3E, //"u" 0x73, //"P" 0x5C, //"o" 0x40, //"-" 0x00, //熄灭 0x00 //自定义 }; /*1毫秒延时函数*/ void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); } /* 串口初始化函数 工作模式1 10位异步收发 发送速率4800bps */ void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装 TH1 = 0xfa; TL1 = 0xfa; //比特率4800 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 REN = 1; //串口允许接收 } /*3位数码管显示函数*/ void display(uchar num) { uchar bai,shi,ge; bai = num / 100; //求模 shi = num % 100 / 10; //求余100后求出有多少个10 ge = num % 10; //求余 P0 = 0xff; //清除断码 we = 1; P0 = 0xfe; //点亮第一位数码管 we = 0; du = 1; P0 = leddata[bai]; //显示百位 du = 0; delay(1); P0 = 0xff; //清除断码 we = 1; P0 = 0xfd;//点亮第二位数码管 we = 0; du = 1; P0 = leddata[shi]; //显示十位 du = 0; delay(1); P0 = 0xff; //清除断码 we = 1; P0 = 0xfb;//点亮第三位数码管 we = 0; du = 1; P0 = leddata[ge]; //显示各位 du = 0; delay(1); } void main() { UART_init();//串口配置初始化 while(1) { if (RI) //检测是否接收完成 { RI = 0; //清除接收标志位,以便于下次接收 } display(SBUF); //取出接收SBUF的值赋给数码管显示 } }
发送矩阵键盘的按键值到计算机
/* 把矩阵键盘的键值以2400bps上传到计算机串口助手 */ #include <reg52.h> #define uchar unsigned char #define uint unsigned int /*1毫秒延时函数*/ void delay(uint z) { uint x,y; for(x = z; x > 0; x--) for(y = 114; y > 0 ; y--); } /* 串口初始化函数 工作模式1 10位异步收发 发送速率2400bps */ void UART_init() { TMOD = 0x20; //T1工作模式2 8位自动重装 TH1 = 0xf4; TL1 = 0xf4; //比特率2400,计算公式256-11059200/2400/32/12 TR1 = 1; //启动T1定时器 SM0 = 0; SM1 = 1; //串口工作方式1 10位异步 // REN = 1; //串口允许接收 } /* 4*4矩阵键盘扫描函数 带返回值,返回键值码 */ uchar KeyScan() { uchar cord_l,cord_h;//声明列线和行线的值的储存变量 P3 = 0xf0;//1111 0000 if( (P3 & 0xf0) != 0xf0)//判断是否有按键按下 { delay(5);//软件消抖 if( (P3 & 0xf0) != 0xf0)//判断是否有按键按下 { cord_l = P3 & 0xf0;// 储存列线值 P3 = cord_l | 0x0f; cord_h = P3 & 0x0f;// 储存行线值 while( (P3 & 0x0f) != 0x0f );//松手检测 return (cord_l + cord_h);//返回键值码 } } } /* 4*4矩阵键盘键值码处理函数 返回转换后的键值码 */ uchar KeyPro() { uchar key_value; //存放转换后的按键值 switch( KeyScan() ) { //第一行键值码 case 0xee: key_value = 0x01; break; case 0xde: key_value = 0x02; break; case 0xbe: key_value = 0x03; break; case 0x7e: key_value = 0x04; break; //第二行键值码 case 0xed: key_value = 0x05; break; case 0xdd: key_value = 0x06; break; case 0xbd: key_value = 0x07; break; case 0x7d: key_value = 0x08; break; //第三行键值码 case 0xeb: key_value = 0x09; break; case 0xdb: key_value = 0x0a; break; case 0xbb: key_value = 0x0b; break; case 0x7b: key_value = 0x0c; break; //第四行键值码 case 0xe7: key_value = 0x0d; break; case 0xd7: key_value = 0x0e; break; case 0xb7: key_value = 0x0f; break; case 0x77: key_value = 0x10; break; } return (key_value);//返回转换后的键值码 } void main() { UART_init();//串口初始化 while(1) { SBUF = KeyPro();//调用带返回值的键值码转换函数,把转换后的键值码送入发送SBUF while(!TI); //检测是否发送完毕 TI = 0; //清楚发送完毕标志位,已便于下次发送 } }
原文:https://www.cnblogs.com/darren-pty/p/13285994.html