目录
串口初始化
SCON
PCON
TMOD
波特率设置
代码实现
收发消息以及串口中断
主函数
完整代码
最近在学习51单片机,需要做一个通过串口与MCU通信来控制LED亮灭的小项目。不多逼逼,直接开搞。
串口初始化
使用串口通信,需要对SCON、PCON和TMOD三个特殊寄存器进行配置。
SCON
串口工作方式寄存器SCON(98H)的结构如下表所示:
SCON
位 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
意义 |
SM0 |
SM1 |
SM2 |
REN |
TB8 |
RB8 |
TI |
RI
|
SM2:多机通信控制位。
REN:串行接收允许位。置1表示允许,置0表示禁止。
TB8:使用奇偶校验时,发送数据校验位的存放位。
RB8:使用奇偶校验时,接收数据校验位的存放位。
TI:发送中断标志位。数据帧的停止位开始发送时,由硬件自动置1,需软件置0,方可继续发送下一帧。
RI:接收中断标志位。数据帧的停止位被接收到时,由硬件自动置1,需软件置0,方可继续接收下一帧。
SM0、SM1:串口工作方式。
51单片机通用异步收发器的工作方式表
SM0 |
SM1 |
方式 |
说明 |
波特率 |
备注 |
0 |
0 |
0 |
移位寄存器 |
51单片机UART方式1时序图
PCON
PCON(97H)中只有最高位SMOD与串行通信有关。
PCON
位 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
意义 |
SMOD |
- |
- |
- |
- |
- |
- |
- |
SMOD:波特率倍增标志位。
TMOD
TMOD(89H)用于设定计数/定时器的工作方式。
TMOD
位 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
意义 |
GATE |
|
M1 |
M0 |
GATE |
|
M1 |
M0 |
上表中,高四位控制T1,第四位控制T0。
GATE
0:仅由运行控制位TRx(x = 0,1,位于TCON中)来控制定时器/计数器运行。
1:用外中断引脚( INT0或 INT1)上的电平与运行控制位TRx共同来控制定时器/计数器运行。
0:为定时器工作模式,对单片机的晶振频率12分频后的脉冲进行计数。
1:为计数器工作模式,计数器对外部输入引脚T0(P3.4)或T1(P3.5)的外部脉冲(负跳变)计数。
M1、M0
M1 |
M0 |
方式 |
说明 |
0 |
0 |
0 |
13位定时器/计数器 |
0 |
1 |
1 |
16位定时器/计数器 |
1 |
0 |
2 |
低8位作8位定时器/计数器,高8位存放自动装入低8位的初值 |
1 |
1 |
3 |
T1停止计数,T0分为两个8位定时器/计数器 |
波特率设置
TCON采用方式1(SM0=0,SM1=1)。波特率由定时器T1设定得到。波特率的计算公式如下:
X是定时器T1的初始值。
反过来,例如晶振频率为11.0592MHz时,要使波特率为9600,可使SMOD=0,定时器T1工作在方式2(TMOD=0x20),初始值应为:
代码实现
void init_UART(void)
{
SCON = 0x50; // 设定串行工作方式1
TMOD = 0x20; // 设定定时器T1工作方式2 M1=1,M0=0
TH1 = 0xfd; // 设定定时器T1初始值设置为FDH
TL1 = TH1;
TR1 = 1; // 使能定时器T1
EA = 1; // 使能总中断
ES = 1; // 使能串口中断
PCON |= 0x80; // SMOD置1,波特率倍增为19200
}
收发消息以及串口中断
// 串口中断函数
/*
每当串口收发中断发生,该函数被执行
当RI == 1,即接收完一个数据帧,接收中断发生时,
符合约定好的消息协议的消息会被写入全局字符串变量recv_buff中
该消息协议指接收到的字符串必须以字符'$'开始,以字符'#'结尾,中间部分为消息的内容,
写入recv_buff中,并将RI置0
接收到完整的符合协议的消息后,全局变量recved_whole置1
当TI == 1,即发送完一个数据帧,发送中断发生时,
将TI置0,并把TX总线忙状态清除,即全局变量TX_busy = 0
*/
void UART_interrupt() interrupt 4
{
uchar byte_buff = 0; // unsigned char 大小为1个字节
static ucahr i = 0;
static bit has_recved = 0; // 接收到消息开始字符'$'时置1
if(RI)
{
RI = 0;
byte_buff = SBUF;
if(byte_buff=='$' && !has_recved)
has_recved = 1;
else if(byte_buff=='#' && has_recved)
{
has_recved = 0;
recv_buff[i] = 0;
recved_whole = 1;
i = 0;
}
else
recv_buff[i++] = byte_buff;
}
else
{
TI = 0;
TX_busy = 0;
}
}
// 通过串口发送字符串
void sendString(uchar *str)
{
while(*str!=0)
{
if(!TX_busy) // 等待TX总线空闲,以进行下一个字符的发送
{
SBUF = *str;
str++;
TX_busy = 1; // 置TX总线状态为忙,中断函数会把此值重新置0
}
}
}
主函数
要求根据上位机(PC)发送的指令来控制两个共阴极发光二极管LED_0、LED_1的亮灭,单片机做出响应回应:
上位机指令 |
说明 |
全局字符串变量recv_buff的值 |
单片机回应 |
$00# |
将LED_0连接的IO口置0,点亮LED_0 |
00 |
LED0 ON |
$01# |
将LED_0连接的IO口置1,熄灭LED_0 |
01 |
LED0 OFF |
$10# |
将LED_1连接的IO口置0,点亮LED_1 |
10 |
LED1 ON |
$11# |
将LED_1连接的IO口置1,熄灭LED_1 |
11 |
LED1 OFF |
其他 |
除上述指令以外的所有字符串 |
|
ERROR |
void main()
{
init_UART();
while(1)
{
if(recved_whole) // 是否已经接收到了一个完整的消息
{
if(recv_buff[0]=='0' && recv_buff[1]=='1' && recv_buff[2]==0)
{
LED_0 = 1;
sendString("LED0 OFF");
}
else if(recv_buff[0]=='0' && recv_buff[1]=='0' && recv_buff[2]==0)
{
LED_0 = 0;
sendString("LED0 ON");
}
else if(recv_buff[0]=='1' && recv_buff[1]=='1' && recv_buff[2]==0)
{
LED_1 = 1;
sendString("LED1 OFF");
}
else if(recv_buff[0]=='1' && recv_buff[1]=='0' && recv_buff[2]==0)
{
LED_1 = 0;
sendString("LED1 ON");
}
else
sendString("ERROR");
recved_whole = 0;
}
}
}
完整代码
#include
#define uchar unsigned char
#define uint unsigned int
sbit LED_0 = P1^0; // 共阴极发光二极管 低电平点亮
sbit LED_1 = P1^1;
uchar recv_buff[10] = {0}; // 用于存储从PC接收到的字符串
bit recved_whole = 0; // 接收到完整指令的标志位
bit TX_busy = 0; // TX总线忙标志位
void init_UART();
void sendString(uchar*);
void main()
{
init_UART();
while(1)
{
if(recved_whole)
{
if(recv_buff[0]=='0' && recv_buff[1]=='1' && recv_buff[2]==0)
{
LED_0 = 1;
sendString("LED0 OFF");
}
else if(recv_buff[0]=='0' && recv_buff[1]=='0' && recv_buff[2]==0)
{
LED_0 = 0;
sendString("LED0 ON");
}
else if(recv_buff[0]=='1' && recv_buff[1]=='1' && recv_buff[2]==0)
{
LED_1 = 1;
sendString("LED1 OFF");
}
else if(recv_buff[0]=='1' && recv_buff[1]=='0' && recv_buff[2]==0)
{
LED_1 = 0;
sendString("LED1 ON");
}
else
sendString("ERROR");
recved_whole = 0;
}
}
}
void init_UART(void)
{
SCON = 0x50; // 串口工作方式1
TMOD = 0x20; // 定时器工作方式2
TH1 = 0xfd; // 定时器初始值设置为FDH
TL1 = TH1;
TR1 = 1; // 使能定时器1
EA = 1; // 使能总中断
ES = 1; // 使能串口中断
PCON |= 0x80; // SMOD置1,波特率倍增为19200;否则波特率为9600
}
// 串口中断函数
void UART_interrupt() interrupt 4
{
uchar byte_buff = 0;
static i = 0;
static has_recved = 0;
if(RI)
{
RI = 0;
byte_buff = SBUF;
if(byte_buff=='$' && !has_recved)
has_recved = 1;
else if(byte_buff=='#' && has_recved)
{
has_recved = 0;
recv_buff[i] = 0;
recved_whole = 1;
i = 0;
}
else
recv_buff[i++] = byte_buff;
}
else
{
TI = 0;
TX_busy = 0;
}
}
// 通过串口发送字符串
void sendString(uchar *str)
{
while(*str!=0)
{
if(!TX_busy)
{
SBUF = *str;
str++;
TX_busy = 1;
}
}
}
以上。
作者:元爔
51单片机
串口
串口通信
通信
单片机
|