单片机
返回首页

STC51从入门到精通(汇编)~~~ 第八讲:串行通信技术

2022-04-24 来源:eefocus

8.1 80C51单片机串行通信技术的特点

80C51单片机具有一个全双工串行通信接口,即能同时进行串行发送和接收。

可以作UART(通用异步接收和发送器)用

可以作同步位移寄存器用。

可以实现点对点的单机通信、多机通信和80C51与系统机的单机或多机通信。


8.2 串行通信基本知识

8.2.1 数据通信

通信方式有两种,即并行通信和串行通信:


并行通信是指数据的各位同时进行传送(发送或接收)的通信方式。其优点是传送速度快;缺点是数据有多少位,就需要多少根传送线。


串行通信指数据是一位一位按顺序传送的通信方式。它的突出优点是只需一对传输线(利用电话线就可作为传送线),这样就大大降低了传送成本,特别适用于远距离通信;其缺点是传送速度较低。


8.2.2 串行通信的传输方式

单工(或单向)配置,只允许数据向一个方向传送;

半双工(或半双向)配置,允许数据向两个方向中的任一方向传送,但每次只能有一个发送,一个接收;

全双工(全双向)配置,允许同时双向传送数据,因此,全双工配置是一对单向配置,它要求两端的通信设备都具有完整和独立的发送和接收能力。


8.2.3 异步通信和同步通信

1、异步通信

帧格式:一个字符由四部分组成:起始位、数据位、奇偶校验位和停止位。

起始位(0):占用一位,用来通知接收设备一个待接收的字符开始到达。

数据位:5~8位数据(规定低位在前,高位在后)

奇偶校验位(可省略):也可用来确定一帧中的字符所代表信息的性质(地址/数据等)。

停止位(1):停止位用来表征字符的结束。停止位可以是1位、1.5位或2位。接收端收到停止位后,知道上一字符已传送完毕 

2、同步通信


3、波特率


1、波特率的定义是每秒钟传送二进制数码的位数(亦称比特数),单位是b/s。


2、假设数据传送速率是120字符/s,而每个字符格式包含10个代码(1个起始位、1个终止位、8个数据位)。这时,传送的波特率为


(10b/字符)×120字符/s = 1200 b/s


8.3 串行接口的组成和特性

8.3.1 串行口的结构

组成:两个物理上独立的串行数据缓冲寄存SBUF、发送控制器、接收控制器、输入移位寄存器和输出控制门。

发送缓冲寄存器SBUF只能写,不能读;

接收缓冲寄存器SBUF只能读,不能写。

两个缓冲寄存器共用一个地址99H,可以用读/写指令区分。

串行发送时,通过“MOV SBUF,A”写指令,写入发送SBUF(99H),再由TxD一位一位地向外发送;

串行接收时,RxD一位一位地接收数据,直到收到一个完整的字符数据后通知CPU,再通过“MOV  A,SBUF”读指令,CPU从接收SBUF(99H)读出数据,送到累加器A中。


8.3.2 串行口控制器及控制寄存器

1、串行口控制寄存器SCON(98H)


SM0、SM1:串行口工作方式控制位,两位对应四种工作方式,如下表所示(fosc是晶振频率)。

8.4  串行通信接口的工作方式

8.4.1 工作方式0

当SM0 SM1=00时,串行接口选择工作方式0,为同步移位寄存器输入/输出方式,常用于扩展I/O口。串行数据通过RXD输入或输出,而TXD用于输出移位时钟,作为外接部件的同步信号。发送或接收的是8位数据(低位在前,高位在后)。

8.4.2 工作方式1

工作方式1,SM0 SMl= 01,为可变波特率的8位异步通信方式。

发送数据由TXD端输出,接收数据由RXD端输入。

方式1以10位为一帧传输,设有1个起始位(0),8个数据位和1个停止位(1)。其帧格式起始位(0),8个数据位和1个停止位(1)。

8.4.3 工作方式2和工作方式3

SM1 SM0= 10,串行接口选择工作方式2,


SM1 SM0= 11,串行接口选择工作方式3。


方式2或方式3是一个9位的异步串行通信接口,TXD为数据发送端,RXD为数据接收端。


方式2的波特率固定为fosc/64或fosc/32,


方式3的波特率由定时器T1或T2 (80C52)的溢出率所确定。


方式2和方式3以11位为1帧传输,设有1个起始位(0),8个数据位,1个附加第9位和1个停止位(1)。


8.5  波特率设计

8.5.1 波特率的计算方法

1. 方式0波特率


方式0波特率 = fosc /12


若振荡器频率fosc = 12MHz,则波=fosc/12=12MHz/12=1MHz/s,即1μs移位一次。


2. 方式2波特率


方式2波特率 = (2^SMOD/64) × fosc


SMOD为0时,波特率等于振荡器频率的1/64;SMOD为1时,波特率等于振荡器频率的1/32。


3. 方式1和方式3的波特率


串行口方式1和方式3的波特率由定时器T1或T2(89C52等单片机)的溢出率和SMOD所确定。


8.5.2 波特率的产生  

1. 用定时器T1产生波特率


方式1和方式3波特率 =(2^SMOD/32) ×(T1溢出率)


溢出周期 =12/振荡器频率×(256-X)


溢出率为溢出周期的倒数,所以有


波特率 = 2^SMOD ×振荡器频率/[32×12×(256-X)]


定时器T1在工作方式2时的初值为


X = 256 - fosc×(SMOD+1)/(384×波特率)


8.6 C语言程序示例

/*******************************************************************************

================================================================================

【平    台】STC89C51_sumjess平台

【编    写】sumjess

【E-mail  】1371129880@qq.com

【软件版本】V2.0

【最后更新】2019年06月10日

【相关信息参考下列地址】

【网    站】

           https://blog.csdn.net/qq_38351824

           http://www.51hei.com/bbs/mcu-2-1.html

---------------------------------------------------------------------------------

【dev.env.】MDK4.02及以上版本

【Target  】STC89C51

第一次修订:2019/05/09

第二次修订:2019/05/21

第三次修订:2019/06/10

【problem 】

    (1)库内补充的不全面;

    (2)库内解释部分不全面;

    (3)库内还存在一定的bug;

【direction】

      下一步的目标就是把库继续集成!

【explain 】

      为了方便使用,我也自己写了很多的库,和优化了算法和表示方式!

【warning】

      目前程序中暂无错误 !   

---------------------------------------------------------------------------------

没有完美的代码,只有不断的奉献,大家一起努力;

赠人玫瑰手留余香,欢迎大家反馈bug!

================================================================================

********************************************************************************/

#include //编译器自带的库用 < > 编译器包含C52的定义

#include 'UART_Sum.h'

//

//STC51只有一个串口 RXD---P3.0   TXD---P3.1

//

/*以下为四种波特率的计算公式:

//

//方式0的波特率 = f(osc)/12

//方式1的波特率 = 2^(SMOD)/32 x (T1溢出率)

//方式2的波特率 = 2^(SMOD)/64 x f(osc)

//方式3的波特率 = 2^(SMOD)/32 x (T1溢出率)

//

//式中f(osc)为系统晶振频率,通常为12MHz或11.0592MHz;SMOD是PCON寄存器的最高位- SMOD=0;串口方式1,2,3时,波特率正常。SMOD=1;串口方式1,2,3时,波特率加倍。

//   T1溢出率即定时器T1溢出的频率-只要算出T1定时器

//每溢出一次所需的时间T,那么T的倒数1/T就是他的溢出率。

//只有定时器2可以减少由于时间上带来的误差,因为方式二可以自动装载 */

 

 

//   在在具体操作串行口之前,需要对单片机的一些与串口有关的特殊寄存器做初始化设置,主要是设置产生波特率的定时器1、串行口控制和中断控制,具体步骤如下:

//   一、确定T1的工作方式(编程TMOD寄存器);

//   二、计算T1的初值,装载TH1,TL1;

//   三、启动T1(编程TCON中的TR1位);

//   四、确定串行口工作方式(编程SCON寄存器);

//   五、串行口工作在中断方式时,要进行中断设置(编程IE,IP寄存器)。

 

//                               常用波特率初值表

//

//波特率 晶振      初值 误差(%) 晶振       初值      误差(12MHz晶振)(%)

//(bps) (MHz) (SMOD=0) (SMOD=1) (MHz)(SMOD=0) (SMOD=1) (SMOD=0) (SMOD=1)

//300     11.0592 0xA0   0X40      0       12 0X98   0X30       0.16       0.16

//600   11.0592 0XD0   0XA0      0       12 0XCC   0X98       0.16       0.16

//1200   11.0592 0XE8   0XD0      0       12 0XE6   0XCC       0.16       0.16

//1800   11.0592 0XF0   0XE0      0       12 0XEF   0XDD       2.12       -0.79

//2400   11.0592 0XF4   0XE8      0       12 0XF3   0XE6       0.16       0.16

//3600   11.0592 0XF8    0XF0      0       12 0XF7   0XEF       -3.55       2.12

//4800   11.0592 0XFA   0XF4      0       12 0XF9   0XF3       -6.99       0.16

//7200   11.0592 0XFC   0XF8      0       12 0XFC   0XF7       8.51       -3.55

//9600   11.0592 0XFD   0XFA      0       12 0XFD   0XF9       8.51       -6.99

//14400   11.0592 0XFE   0XFC      0       12 0XFE   0XFC       8.51       8.51

//19200   11.0592 ——       0XFD      0       12   ——   0XFD       ——       8.51

//28800   11.0592 0XFF   0XFE      0       12 0XFF   0XFE       8.51       8.51

 

/*

  方式0时,串行口为同步移位寄存器的输入/输出方式,主要用于扩展并行输入或输出口。

  !!!注意!!!串行口工作模式0并不是一个同步串口通信方式,他主要用途是与外面的同步移位寄存器相连。 具体见本篇程序下文件夹的word

  数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出。发送和接收均为8位数据,低位在先,高位在后。

  方式0的波特率 = f(osc)/12    

*/

void Init_UART_0(void)          //使用方式1  ---  1次中断为5ms,200次为1s

{

SCON=0; //设置T1定时器工作方式2

EA=1; //开总中断

ES=1; //开串口中断

TI=0;

}

#if 0

/配合主函数定时使用示例

输出波形///

void main()

{   

    Init_UART_0();

while(1)

{

SBUF=0xaa;    //发送接收到的数据

delay(1);

}

}

void serial() interrupt 4

{

  TI=0;

}

#endif

/*

  方式1的波特率 = 2^(SMOD)/32 x (T1溢出率)

  方式1是10位数据的异步通信口,其中1位起始位,8位数据位,1位停止位。

  TXD(P3.1)为数据发送引脚,RXD(P3.0)为数据接收引脚。

  其传输波特率是可变的,对于51单片机,波特率由定时器1的溢出率决定。

  通常我们在做单片机与单片机串口通信、单片机与计算机串口通信、计算机与计算机串口通信时,基本都选择方式1,因此这种方式大家务必要完全掌握。    

*/

void Init_UART_1(void)          //使用方式1  ---  1次中断为5ms,200次为1s

{

TMOD=0x20; //设置T1定时器工作方式2

TH1=0xfd;         //T1定时器装初值

TL1=0xfd; //T1定时器装初值

TR1=1; //启动T1定时器

REN=1; //允许串口接收

SM0=0; //设定串口工作方式1

SM1=1; //设定串口工作方式1

EA=1; //开总中断

ES=1; //开串口中断

}

#if 0

//如果不使用可不写中断服务函数/

/配合主函数定时使用示例

//收到什么发什么///

uchar b=0,flag=0;

void main()

{   

    Init_UART_1();

while(1)

{

if(flag==1)

{

ES=0;    //发送数据时,关串口中断

flag=0;    //清除标志位

SBUF=b;    //发送接收到的数据

while(!TI);//直到发送完毕,发送结束TI为1

TI=0;    //置0,准备下一次发送

ES=1;    //开启串口中断

}

}

}

void serial() interrupt 4

{

b=SBUF; //赋值,读取接收到的数据

flag=1; //写入标志位

RI=0; //置0等待下次中断

}

 

#endif

 

/*

方式2,3都为11位数据的异步通信口。

TXD(P3.1)为数据发送引脚,RXD(P3.0)为数据接收引脚。

这两种方式下,起始位1位,数据9位(含1位附加的第9位,发送时为SCON中的TBS,接收时为RBS),停止位1位,一帧数据为11位。

方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时器Tl的溢出率决定。

方式2和方式3的差别仅在于波特率的选取方式不同,在两种方式下,接收到的停止位与SBUF,RBS及RI都无关。

方式2的波特率 = 2^(SMOD)/64 x f(osc)

*/

void Init_UART_2(void)          //使用方式2  --- 具体情况时,需修改

{

       SCON = 0x50;   //REN=1允许串行接受状态,串口工作模式2  (1010 0000)   SM0 SM1 SM2 REN   

       TMOD|= 0x20;   //定时器工作方式2                      

       PCON|= 0x80;   //波特率提高一倍                                                   

       TH1 = 0xF4;    //波特率2400、数据位8、停止位1。效验位无 (12M)

       TL1 = 0xF4;   //因为 PCON|= 0x80; 波特率提高一倍为4800

       TR1 = 1;       //开启定时器1                                                     

       ES  = 0;       //开串口中断                 

       EA  = 0;       // 开总中断

}

#if 0

void main()

{   

    Init_UART_2();

while(1)

{

SBUF=0x01;    //发送接收到的数据

delay(1);

}

}

void serial() interrupt 4

{

  TI=0;

}

#endif

/*

方式2,3都为11位数据的异步通信口。

TXD(P3.1)为数据发送引脚,RXD(P3.0)为数据接收引脚。

这两种方式下,起始位1位,数据9位(含1位附加的第9位,发送时为SCON中的TBS,接收时为RBS),停止位1位,一帧数据为11位。

方式2的波特率固定为晶振频率的1/64或1/32,方式3的波特率由定时器Tl的溢出率决定。

方式2和方式3的差别仅在于波特率的选取方式不同,在两种方式下,接收到的停止位与SBUF,RBS及RI都无关。

方式2的波特率 = 2^(SMOD)/64 x f(osc)

*/

void Init_UART_3(void)          //使用方式3  ---  具体情况时,需修改

{

    TMOD = 0x20;      // 定时器1  方式 2

SM0=1;   // 设定串口工作方式3

SM1=1;   // 设定串口工作方式3

    TH1   = 0XFD;     // 定时器1初值

    TL1   = 0XFD;     // 定时器1初值

EA=1;   // 开总中断

    ES    = 1;        // 串口中断

    TR1   = 1;        // 开启定时器1

TI=0;   //置0,准备下一次发送

}

#if 0

void main()

{   

    Init_UART_3();

while(1)

{

SBUF=0xaa;    //发送接收到的数据

delay(1);

}

}

void serial() interrupt 4

{

  TI=0;

}

#endif

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 红外线探测报警器

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 用NE555制作定时器

  • RS-485基础知识:处理空闲总线条件的两种常见方法

  • 基于ICL296的大电流开关稳压器电源电路

    相关电子头条文章