串口操作流程

  1. 配置串口工作方式
  2. 配置用于串口的定时器T1为工作方式2
  3. 设置波特率
  4. 使能总中断、串口中断
  5. 设置接收中断函数
  6. 设置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

波特率=\frac{fosc}{12}

工作方式1

波特率=\frac {2^{SMOD}\times {f_{osc}}}{32 \times 12 \times(定时器最大值-定时器初值)}

工作方式2

波特率=\frac{2^{SMOD} \times f_{osc}}{64}

工作方式3

波特率=\frac {2^{SMOD}\times {f_{osc}}}{32 \times 12 \times(定时器最大值-定时器初值)}

使用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;		  //重启串口中断
}