[TOC]

串口

简介

什么是串口

串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方

式的扩展接口。串行接口(Serial Interface)是指数据一位一位地顺序传送。其特点是通信线路简

单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成

本,特别适用于远距离通信,但传送速度较慢

  • 是设备间接线通信的一种方式
  • 数据一位一位地顺序传送
  • 双向通信,全双工
  • 传送速度相对较慢

关于电器标准和协议

串行接口按电气标准及协议来分包括RS-232-C、RS-422、RS485等。RS-232-C、RS-422与RS-485

标准只对接口的电气特性做出规定,不涉及接插件、电缆或协议。

​ 也称标准串口,最常用的一种[串行通讯接口,比如我们的电脑主机的9针串口 ,最高速率为20kb/sRS-232是为点对点(即只用一对收、发设备)通讯而设计的,其传送距离最大为约15米。所以RS-232适合本地设备之间的通信

image-20220630161405514

RS-422

由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Slave),从设备之间不能通信,所以RS-422支持点对多的双向通信。

RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比

RS-485

是从RS-422基础上发展而来的,无论四线还是二线连接方式总线上可多接到32个设备。

image-20220630161450717

关于串口的电平

经常听说的UART

异步串行是指UART(Universal Asynchronous Receiver/Transmitter),通用异步接收/发送。

UART包含TTL电平的串口和RS232电平的串口

RS232电平

逻辑1为-3~-15V的电压, 逻辑0为3~15V的电压

  • 笔记本通过RS232电平和单片机通信
image-20220630161724673
  • TTL电平

​ TTL是Transistor-Transistor Logic,即晶体管-晶体管逻辑的简称,它是计算机处理器控制的设备内部各部分之间通信的标准技术。TTL电平信号应用广泛,是因为其数据表示采用二进制规定,

  • +5V等价于逻辑”1”,0V等价于逻辑”0”。

    数字电路中,由TTL电子元器件组成电路的电平是个电压范围,规定:

  • 输出高电平>=2.4V,输出低电平<=0.4V;

  • 输入高电平>=2.0V,输入低电平<=0.8V

    • 笔记本电脑通过TTL电平与单片机通信

      TX发送线(端口)3.1

      RX接收线 (端口)3.0

USB转TTL,使用ch340通信

image-20220630161942810
  • 上官一号

    image-20220630162811236

串口通信

串口接线方式

  • RXD:数据输入引脚,数据接受;STC89系列对应P3.0口,上官一号有单独引出

  • TXD:数据发送引脚,数据发送;STC89系列对应P3.1口,上官一号有单独引出

  • 接线方式

image-20220630162903062
image-20220630162907262

印象塑造

  • 输入/输出数据缓冲器都叫做SBUF, 都用99H地址码,但是是两个独立的8位寄存器

  • 代码体现为: 想要接收数据 char data = SBUF 想要发送数据SBUF = data

image-20220630195523220
  • 回忆UART是异步串行接口,通信双方使用时钟不同,因为双方硬件配置不同,但是需要约定通信速度,叫做波特率

对于电脑来说,别人做好了软件,鼠标点点点就能配置好,而苦逼单片机的波特率配置需要我们写代码

点点点配置什么,我们代码也要配置对应参数

image-20220630195630843
  • 字符'a'是如何从单片机上传到PC的

    a的ASSII码是97,16进制就是0x61, 二进制是01010001,这个8位就是数据位

    串口工作模式1,一帧数据有10位,起始位(0),数据位,停止位(1)

    那么a的一帧数据就是 0 1000 1010 1 起始位,a的低位到高位,停止位

  • 除了速度要求,还要有数据格式,双方暗号对上了再发数据,所以有起始位,和停止位 的概念

串口代码

发送一个字符

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
#include <REG52.H>
sfr AUXR = 0x8E;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //9600bps@11.0592MHz
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
AUXR &= 0xBF; //定时器时钟12T模式
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFD; //设置定时初始值
TH1 = 0xFD; //设置定时重载值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
}

void main()
{
char data_msg = 'A';
UartInit();
while (1)
{
Delay1000ms();
SBUF = data_msg;
}
}
image-20220630214450155

发送一个字符串

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
#include <REG52.H>
sfr AUXR = 0x8E;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //9600bps@11.0592MHz
{
AUXR = 0x01;
SCON = 0x50; //配置串口工作方式1,REN使能接收
TMOD &= 0xF0;
TMOD |= 0x20;//定时器1工作方式位8位自动重装
TH1 = 0xFD;
TL1 = 0xFD;//9600波特率的初值
TR1 = 1;//启动定时器
EA = 1;//开启总中断
ES = 1;//开启串口中断
}
void sendByte(char data_msg){
SBUF = data_msg;
while (!TI);
TI = 0 ;

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

}
void main()
{
UartInit();
while (1)
{
Delay1000ms();
SendString("xiaoliutongxue NB\r\n");
}
}
image-20220701180259863

代码中的

1
2
while(!TI);
TI = 0;

TI 是单片机串口发送中断的请求位:

  • 当 串行数据发送 结束时 TI=1
  • 在发送过程中为 TI = 0 ;

所以 当在发送数据的时候 TI0 通过 while等待 直到数据全部写入完成

想要实现换行 使用会使串口数据换行对齐


PC发送数据 控制开发板的小灯

当RI = 1 的时候 单片机就知道 串口 有数据来了

这里需要和发送请求为TI做区分

接收完数据后需要软件将RI接受位 置0

非中断方式 判断串口数据接受位
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
#include <REG52.H>
#include<STDIO.H>
sfr AUXR = 0x8E;
sbit LED = P3^6;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void UartInit(void) //9600bps@11.0592MHz
{
AUXR = 0x01;
SCON = 0x50; //配置串口工作方式1,REN使能接收
TMOD &= 0xF0;
TMOD |= 0x20;//定时器1工作方式位8位自动重装
TH1 = 0xFD;
TL1 = 0xFD;//9600波特率的初值
TR1 = 1;//启动定时器
EA = 1;//开启总中断
ES = 1;//开启串口中断
}
void sendByte(char data_msg){
SBUF = data_msg;
while (!TI);
TI = 0 ;
}
void SendString(char* str){
while (*str != '\0')
{
sendByte(*str);
str++;
}

}
void main()
{
char cmd;
UartInit();
while (1)
{
Delay1000ms();
SendString("xiaoliutongxue NB\r\n");
if(RI == 1){
cmd = SBUF;
if (cmd == 'o')
{
LED = 0 ;
}else if (cmd == 'c')
{
LED = 1;
}
RI =0;
}
}
}
中断方式

中断号为 4 需要开启总中断 使EA = 1 开启串口中断 ES = 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
#include <REG52.H>
#include <STDIO.H>
sfr AUXR = 0x8E;
sbit LED = P3 ^ 6;
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void UartInit(void) // 9600bps@11.0592MHz
{
AUXR = 0x01;
SCON = 0x50; //配置串口工作方式1,REN使能接收
TMOD &= 0xF0;
TMOD |= 0x20; //定时器1工作方式位8位自动重装
TH1 = 0xFD;
TL1 = 0xFD; // 9600波特率的初值
TR1 = 1; //启动定时器
EA = 1; //开启总中断
ES = 1; //开启串口中断
}
void sendByte(char data_msg)
{
SBUF = data_msg;
while (!TI)
;
TI = 0;
}
void SendString(char *str)
{
while (*str != '\0')
{
sendByte(*str);
str++;
}
}
char cmd[128];
void main()
{

UartInit();
while (1)
{
Delay1000ms();
SendString("xiaoliutongxue NB\r\n");
}
}
void UART_Hander() interrupt 4
{
static int i = 0 ;
if (RI)
{
RI = 0;
cmd[i] = SBUF;
i++;
if (i == 12 )
{
i = 0
}

if (cmd == 'o')
{
LED = 0;
}
else if (cmd == 'c')
{
LED = 1;
}
}
}

处理 单词命令

头文件string.h 处理字符串 详见[C 标准库 – | 菜鸟教程 (runoob.com)]:

用到的api

strstr(参数1,参数2)

  • 参数1 需要在那个字符串中查找
  • 参数2 要查找的字符串

memset(参数1,参数2,参数3)

  • 参数1 要初始化的字符串数组或指针
  • 参数2 要初始化为什么字符
  • 参数3 该字符串的大小 根据数组大小或者指针大小而定
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
87
#include <REG52.H>
#include "STRING.H"
#include <STDIO.H>
sfr AUXR = 0x8E;
sbit LED = P3 ^ 6;
#define SIZE 16
void Delay1000ms() //@11.0592MHz
{
unsigned char i, j, k;
i = 8;
j = 1;
k = 243;
do
{
do
{
while (--k)
;
} while (--j);
} while (--i);
}
void UartInit(void) // 9600bps@11.0592MHz
{
AUXR = 0x01;
SCON = 0x50; //配置串口工作方式1,REN使能接收
TMOD &= 0xF0;
TMOD |= 0x20; //定时器1工作方式位8位自动重装
TH1 = 0xFD;
TL1 = 0xFD; // 9600波特率的初值
TR1 = 1; //启动定时器
EA = 1; //开启总中断
ES = 1; //开启串口中断
}
void sendByte(char data_msg)
{
SBUF = data_msg;
while (!TI)
;
TI = 0;
}
void SendString(char *str)
{
while (*str != '\0')
{
sendByte(*str);
str++;
}
}
char cmd[16];
void main()
{
LED = 0 ;
UartInit();
while (1)
{
Delay1000ms();
SendString("xiaoliutongxue NB\r\n");
}
}
void UART_Hander() interrupt 4
{
static int i = 0 ;
if (RI)
{
RI = 0;
cmd[i] = SBUF; //将SBUF的第一位放到cmd[0]
i++; // 向后累加
if (i == SIZE)
{
i = 0 ;//指向头部
}

if (strstr(cmd,"en")) // open
{
LED = 0;
i= 0 ;
memset(cmd,'\0',SIZE);
}
else if (strstr(cmd,"se")) //close
{
LED = 1;
i = 0 ;
memset(cmd,'\0',SIZE);
}
}
}