51单片机串口通信

Jane ·
更新时间:2024-09-20
· 940 次阅读

目录

串口初始化

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 C/\overline{T} M1 M0 GATE C/\overline{T} M1 M0

上表中,高四位控制T1,第四位控制T0。

GATE

0:仅由运行控制位TRx(x = 0,1,位于TCON中)来控制定时器/计数器运行。
1:用外中断引脚( INT0或 INT1)上的电平与运行控制位TRx共同来控制定时器/计数器运行。

C/\overline{T}

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设定得到。波特率的计算公式如下:

BaudRate=\frac{2^{SMOD}}{32}\times\frac{f_{osc}/12}{256-X}

X是定时器T1的初始值。

反过来,例如晶振频率为11.0592MHz时,要使波特率为9600,可使SMOD=0,定时器T1工作在方式2(TMOD=0x20),初始值应为:

\small X =256-\frac{2^{SMOD}}{32}\times\frac{f_{osc}/12}{BaudRate}=256-\frac{2^0}{32}\times\frac{11.0592\times10^6/12}{9600}=253=FDH

代码实现 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单片机 串口 串口通信 通信 单片机

需要 登录 后方可回复, 如果你还没有账号请 注册新账号