串口操作流程
初始化串口
- 配置串口工作方式
- 配置用于串口的定时器T1为工作方式2
- 设置波特率
- 使能总中断、串口中断
- 设置接收中断函数
- 设置stdio库
串口寄存器
与串口有关的寄存器有以下几个:
- SUBF:数据缓冲器.
- SCON:串行口控制寄存器
- PCON寄存器
SUBF:数据缓冲器
有两个,用于接收/发送数据。
需要注意的是,该两个缓冲器地址一致,可以当作普通寄存器看待。
SCON寄存器
串行口控制寄存器
用于设置串行口的工作方式、监视串行口的工作状态、控制发送与接收的状态等。
位序 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位名 | SM0 | SM1 | SM2 | REN | TB8 | RB8 | TI | RI |
SM0和SM1位
用于选择串行工作方式
SM0 SM1 | 工作方式 | 功能 | 波特率 |
---|---|---|---|
00 | 0 | 8位同步移位寄存器(输出) | 波特率固定为fosc/12 |
01 | 1 | 10位异步收发方式(8位数据) | 波特率可变 |
10 | 2 | 11位异步收发(9位数据) | 波特率固定为fosc/64或fosc/32 |
11 | 3 | 11位异步收发方式(9位数据) | 波特率可变 |
SM2位
多机通信控制位,主要用于方式2和方式3。
- 在工作方式0中,SM2必须设成0。
- 在工作方式1中,当处于接收状态时,若SM2=1,则只有接收到的停止位“1”时,RI才能被激活成“1”(产生中断请求)。
- 在工作方式2和3中,当SM2=0时,串口以单机发送或接收方式工作,TI和RI以正常方式被激活并产生中断请求;
- 在工作方式2和3中,当SM2=1时,若RB8=1,则RI被激活并产生中断请求;若RB8=0,不激活RI,也不引起接收中断请求。
REN位
使能串行接收位。
- 为1时,启动串行口接收数据。
- 为0时,禁止接收。
TB8位
- 在方式2或方式3中,是发送数据的第九位。可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位。
- 在方式0和方式1中,该位未使用。
RB8位
- 在方式2或方式3中,是接收到数据的第九位。作为奇偶校验位或地址帧/数据帧的标志位。
- 在方式1时,若SM2=0,则RB8是接收到的停止位。
TI位
发送中断标志位
- 在方式0中,当串行发送第8位数据结束时,硬件自动将改位置1,并向CPU发送中断请求。
- 在其它方式中,串行发送停止位的开始时,硬件自动将改位置1,并向CPU发送中断请求。
- 必须用软件方式清除该位。
RI位
接收中断标志位
- 在方式0中,当串行接收第8位数据结束时,硬件自动将改位置1,并向CPU发送中断请求。
- 在其它方式中,串行接收停止位的中间时,硬件自动将改位置1,并向CPU发送中断请求。
- 必须用软件方式清除该位。
注意
TI和RI位在任何工作方式下都必须由软件清零。这点和外部中断及定时中断的标志位的清除都不同。
PCON寄存器
位序 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
---|---|---|---|---|---|---|---|---|
位名 | SMOD | - | - | - | GF1 | GF0 | PD | IDL |
SMOD位
波特率加倍位
- 只能工作于方式1~3
- 为1的时候,串口波特率增加1倍。
- 为0的时候,串口波特率不加倍。
波特率计算
工作方式0
波特率 = f o s c 12 波特率=\frac{fosc}{12} 波特率=12fosc
工作方式1
波特率 = 2 S M O D × f o s c 32 × 12 × ( 定时器最大值 − 定时器初值 ) 波特率=\frac {2^{SMOD}\times {f_{osc}}}{32 \times 12 \times(定时器最大值-定时器初值)} 波特率=32×12×(定时器最大值−定时器初值)2SMOD×fosc
工作方式2
波特率 = 2 S M O D × f o s c 64 波特率=\frac{2^{SMOD} \times f_{osc}}{64} 波特率=642SMOD×fosc
工作方式3
波特率 = 2 S M O D × f o s c 32 × 12 × ( 定时器最大值 − 定时器初值 ) 波特率=\frac {2^{SMOD}\times {f_{osc}}}{32 \times 12 \times(定时器最大值-定时器初值)} 波特率=32×12×(定时器最大值−定时器初值)2SMOD×fosc
使用stdio库
C51单片机可以调用C语言的stdio库,只需要重新实现putchar函数和_getkey函数:
void putchar(char buf)
{
SBUF = buf;
while(TI==0);
TI = 0;
}
char _getkey (void)
{
while(RI == 0);
RI = 0;
return SBUF;
}
将上述代码复制到主代码中,然后导入stdio.h
,右键打开该头文件,将以下两行代码注释即可:
extern char putchar (char);
extern char _getkey (void);
使用SailLib库
SailLib库提供了串口相关的函数:
函数名 | 描述 |
---|---|
Serial_init | 串口初始化函数 |
Serial_set_callback | 设置读串口回调函数 |
Serial_init
串口初始化函数
参数:
- 是否启用读串口中断。设置为1时,当串口接收到数据的时候会产生一个中断,这个中断会调用一个回调函数,该回调函数需要通过[[# Serial_set_callback]]函数设置。
Serial_set_callback
设置读串口回调函数
参数:
- 回调函数。需要传入一个无返回值、参数为char的函数,用于串口接收到数据产生中断时调用。
库代码
声明:
static void (*_cb)(char buf);
void Serial_init(uint8_t ES_en);
void Serial_set_callback(void (*cb)(char buf));
定义:
void Serial_init(uint8_t ES_en)
{
//设置串口为工作方式1
SCON = 0x50;
//设置波特率
// T1工作于方式2(8位自动重载)
TMOD |= 0x20;
//波特率不倍增
PCON &= 0x7f; //关闭最高位
//设置波特率9600
TL1 = 0xfd;
TH1 = 0xfd;
//使能定时器1
TR1 = 1;
EA = 1; //打开全局中断
ES = ES_en; //打开串口中断(如果不使用中断,将使用阻塞函数)
}
void Serial_set_callback(void (*cb)(char buf))
{
_cb = cb;
}
void Serial_receive() interrupt 4
{
if (RI == 0) //如果没有接收到串口中断标志
return;
ES = 0; //关闭串口中断
RI = 0; //复位串口中断标志
(*_cb)(SBUF); //调用回调函数,返回串口的数据
ES = 1; //重启串口中断
}