抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

串口通信

原理简介

通信有并行通信和串行通信两种方式。在多微机系统以及现代测控系统中信息的交换多采用串行通信方式

并行通信

并行通信通常是将数据字节的各位用多条数据线同时进行传送

特点:并行通信控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难。

image-20210822131931271

串行通信

串行通信是使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息。

image-20210822132211661

同步、异步通信

串行通信又分 同步、异步两种

同步:接收方与发送方时钟一致

异步:双方使用各自的时钟,但应该尽可能保持一致

数据传输方向

单工:一台设备只能收/发

半双工:同一时间只能收或发

全双工:可同时进行收发

image-20210822134038629

奇偶校验

在发送数据时,数据位尾随的1位为奇偶校验位(1或0)。

  • 奇校验时,数据中“1”的个数与校验位“1”的个数之和应为奇数;

  • 偶校验时,数据中“1”的个数与校验位“1”的个数之和应为偶数。接收字符时,对“1”的个数进行校验,若发现不一致,则说明传输数据过程中出现了差错。

代码和校验

代码和校验是发送方将所发数据块求和(或各字节异或),产生一个字节的校验字符(校验和)附加到数据块末尾。接收方接收数据同时对数据块(除校验字节外)求和(或各字节异或),将所得的结果与发送方的“校验和”进行比较,相符则无差错,否则即认为传送过程中出现了差错。

循环冗余校验

这种校验是通过某种数学运算实现有效信息与校验位之间的循环校验,常用于对磁盘信息的传输、存储区的完整性校验等。这种校验方法纠错能力强,广泛应用于同步通信中。

使用串口进行数据发送和接收

初始化配置

  1. 打开总中断和串口中断

    image-20210822141906412

  2. **选择串口工作方式(配置串口控制寄存器SCON)**不懂就先选方式1

    image-20210822142218968

  3. 允许串口接收(配置串口控制寄存器SCON)

    image-20210822142735027

    image-20210822150203890

  4. 配置波特率

    假如选择波特率为9600,根据下面的公式计算出定时计数器的初值,SMOD没有配置默认为0,fosc为频率11.0592MHz,定时计数器选择工作方式2(8位自动重装),计算得到初值为253,即0xFD

    image-20210822143113859

    代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void UARTInit()
    {
    EA = 1; //打开总中断
    ES = 1; //打开串口中断
    SM0 = 0; SM1 = 1; //串口工作方式1,8位UART波特率可变
    REN = 1;//串口允许接收

    TR1 = 1;//启动定时器1
    TMOD |= 0x20;//定时器1,工作模式2 8位自动重装
    TH1 = 0xfd;
    TL1 = 0xfd;//设置比特率9600
    }

    说明:TMOD |= 1, 使用了 |= 运算符,因为这里使用T1定时器,在前面实际还有关于定时器T0的配置,要让T0, T1同时工作, 就要同时设置T1和T0的工作方式

    image-20210822100638172

    这是T0的初始化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void T0_init()
    {
    EA = 1; //打开总中断
    ET0 = 1;//打开定时器0中断
    TR0 = 1; //启动定时器0
    REN = 1;//允许串口接收
    TMOD |= 0X01; //定时器工作模式1,16位定时模式
    TH0 = 0xED;
    TL0 = 0xFF; //定时5ms
    }

    void UARTInit(){
    //......
    }

发送、接收数据

image-20210822145358670

接收:通过读取接收缓冲器SBUF中的数据

发送:通过往发送缓冲器SBUF写入数据

发送、接收缓冲器物理上是相互独立的,但它们占用同一地址0x99

RI、TI看这2幅图,如下

image-20210822142735027

image-20210822150203890

1
2
3
4
5
6
7
8
9
10
11
12
13
void UART() interrupt 4
{
uchar temp;
if(RI) //判断接收是否完成
{
num = SBUF; //读SBUF,读出串口接收到的数据
RI = 0; //软件清零接收标志位
temp = num;
SBUF = ++temp; //写SBUF,把要发送的数据送给发送缓存器
}
if(TI) //判断是否发送完成
TI = 0; //清零发送完成标志位
}

完整代码:使用串口接收数据,用数码管显示,然后把数据+1后发出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <reg52.h>
#include <intrins.h>

#define uint unsigned int
#define uchar unsigned char

sbit DU = P2^6;//数码管段选
sbit WE = P2^7;//数码管段选
uchar num;//数码管显示的值

//共阴数码管段选表0-9
uchar code SMGduan[]= {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F,};
//数码管位选码
uchar code SMGwei[] = {0xfe, 0xfd, 0xfb};


void display(uchar i)
{
static uchar wei;
P0 = 0XFF;//清除断码
WE = 1;//打开位选锁存器
P0 = SMGwei[wei];
WE = 0;//锁存位选数据
switch(wei)
{
case 0: DU = 1; P0 = SMGduan[i / 100]; DU = 0; break;
case 1: DU = 1; P0 = SMGduan[i % 100 / 10]; DU = 0; break;
case 2: DU = 1; P0 = SMGduan[i % 10]; DU = 0; break;
}
wei++;
if(wei == 3)
wei = 0;
}
//定时器0初始化
void T0_init()
{
EA = 1; //打开总中断
ET0 = 1;//打开定时器0中断
TR0 = 1; //启动定时器0
REN = 1;//允许串口接收
TMOD |= 0X01; //定时器工作模式1,16位定时模式
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
}

//串口初始化
void UART_init()
{
EA = 1; //打开总中断
ES = 1; //打开串口中断
SM0 = 0; SM1 = 1;//串口工作方式1,8位UART波特率可变
REN = 1;//串口允许接收
TR1 = 1;//启动定时器1
TMOD |= 0x20;//定时器1,工作模式2 8位自动重装
TH1 = 0xfd;
TL1 = 0xfd;//设置比特率9600
}
void main()//main函数自身会循环
{
T0_init();//定时器0初始化
UART_init();//串口初始化
while(1);
}

//定时器0中断函数
void t0() interrupt 1
{
TH0 = 0xED;
TL0 = 0xFF; //定时5ms
display(num); //数码管显示函数
}

//串口中断函数
void UART() interrupt 4
{
uchar temp;
if(RI)//判断接收是否完成
{
num = SBUF;//读SBUF,读出串口接收到的数据
RI = 0;//软件清零接收标志位
temp = num;//
SBUF = ++temp;//写SBUF,把要发送的数据送给发送缓存器
}
if(TI)//判断是否发送完成
TI = 0;//清零发送完成标志位
}

评论