前言:
今天有两个项目,分别为:
串口接收:

串口发送:

如上图将文件放在Keli5 中即可,然后烧录在单片机中就行了

烧录软件用的是STC-ISP,不知道怎么安装的可以去看江科大的视频:
【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】https://www.bilibili.com/video/BV1Mb411e7re?p=2\&vd_source=ada7b122ae16cc583b4add52ad89fd5e
串口接收源代码:
++头文件要记得宏定义和重定义,避免重复调用:++
cpp
#ifndef _Timer0_h_//名字根据文件名定义即可
#define _Timer0_h_
//声明函数......
#endif
main.c
cpp
//接收程序
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"
unsigned char Sec;
void main(){
UATE_Init();//初始化
while(1){
UATE_SendByte(Sec);//发送字节
Sec++;//每秒递增
Delay(1000);
}
}
UART.c
cpp
#include <STC89C5xRC.H>
//初始化串口
//void UART_Init(){
// SCON=0x40;//0100 0000
// PCON=0;
// //&只有在两个位都为1时结果位才是1,而|只要有一个位为1结果位就是1
// TMOD &= 0x0F; //设置定时器模式,高四位清0,低四位不变
// TMOD |= 0x20; //设置定时器模式,高四位设计成0010,低四位不变,定时器1,模式2
// TL0 = 0x66; //设置定时初值
// TH0 = 0xFC; //设置定时初值
// TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
// ET0=1;//允许中断
// EA=1;//允许总中断
// PT0=0;//低优先级
//}
void UATE_Init() //[email protected]
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
// AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
// AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFA; //设定定时初值
TH1 = 0xFA; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
}
//发送字节(只发送,不接受)
void UATE_SendByte(unsigned char Byte){
SBUF=Byte;//传入字节数据
while(TI==0);//发送循环,发送完TI=1;
TI=0;//软件复位
}
UART.h
cpp
//UART.h
#ifndef __UART_H__
#define __UART_H__
void UATE_Init();
void UATE_SendByte(unsigned char Byte);
#endif
Delay.c
cpp
//Delay.c
#include <STC89C5xRC.H>
#include <INTRINS.H>
//延时函数
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms){
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
xms--;
}
}
Delay.h
cpp
//Delay.h
#ifndef __Delay_H__
#define __Delay_H__
//延时函数头文件
void Delay(unsigned int xms);
#endif
串口发送源代码:
++头文件要记得宏定义和重定义,避免重复调用:++
cpp
#ifndef _Timer0_h_//名字根据文件名定义即可
#define _Timer0_h_
//声明函数......
#endif
main.c
cpp
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"
void main(){
UATE_Init();//初始化
while(1){
}
}
//中断函数
void UART_Routine() interrupt 4{
if(RI==1){//RI等于1表示可以中断
P2=~SBUF;//发送数据
UATE_SendByte(SBUF);//接收数据
RI=0;//软件复位
}
}
UART.c
cpp
#include <STC89C5xRC.H>
//初始化串口
//void UART_Init(){
// SCON=0x40;//0100 0000
// PCON=0;
// //&只有在两个位都为1时结果位才是1,而|只要有一个位为1结果位就是1
// TMOD &= 0x0F; //设置定时器模式,高四位清0,低四位不变
// TMOD |= 0x20; //设置定时器模式,高四位设计成0010,低四位不变,定时器1,模式2
// TL0 = 0x66; //设置定时初值
// TH0 = 0xFC; //设置定时初值
// TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
// ET0=1;//允许中断
// EA=1;//允许总中断
// PT0=0;//低优先级
//}
void UATE_Init() //[email protected]
{
PCON &= 0x7F; //波特率不倍速
SCON = 0x50; //8位数据,可变波特率
// AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T
// AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //清除定时器1模式位
TMOD |= 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFA; //设定定时初值
TH1 = 0xFA; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1;//启动总中断
ES=1;//启动串口中断
}
//发送字节(只发送,不接受)
void UATE_SendByte(unsigned char Byte){
SBUF=Byte;//传入字节数据
while(TI==0);//发送循环,发送完TI=1;
TI=0;//软件复位
}
串口中断模版
中断函数
//void UART_Routine() interrupt 4{
// if(RI==1){//RI等于1表示可以中断
// P2=~SBUF;//发送数据
// UATE_SendByte(SBUF);//接收数据
// RI=0;//软件复位
// }
//}
UART.h
cpp
//UART.h
#ifndef __UART_H__
#define __UART_H__
void UATE_Init();
void UATE_SendByte(unsigned char Byte);
#endif
Delay.c
cpp
//Delay.c
#include <STC89C5xRC.H>
#include <INTRINS.H>
//延时函数
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms){
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
xms--;
}
}
Delay.h
cpp
//Delay.h
#ifndef __Delay_H__
#define __Delay_H__
//延时函数头文件
void Delay(unsigned int xms);
#endif
++注意两个项目的代码有部分不同!!!!++
第一个项目是串口接收:
主程序代码:
cpp#include <STC89C5xRC.H> #include "Delay.h" #include "UART.h" unsigned char Sec; void main(){ UATE_Init();//初始化 while(1){ UATE_SendByte(Sec);//发送字节 Sec++;//每秒递增 Delay(1000); } }
注意:接收是串口向电脑发送数据,发送一个16进制,每秒增加,电脑接收。而且发送是没有中断的!!!
UART.c代码:
cppvoid UATE_Init() //[email protected] { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //8位数据,可变波特率 // AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T // AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //清除定时器1模式位 TMOD |= 0x20; //设定定时器1为8位自动重装方式 TL1 = 0xFA; //设定定时初值 TH1 = 0xFA; //设定定时器重装值 ET1 = 0; //禁止定时器1中断 TR1 = 1; //启动定时器1 } //发送字节(只发送,不接受) void UATE_SendByte(unsigned char Byte){ SBUF=Byte;//传入字节数据 while(TI==0);//发送循环,发送完TI=1; TI=0;//软件复位 }
没有中断,因此ET1=0,禁止中断。
运行:
单片机没有任何变化,电脑端STC-ISP的串口助手接收缓冲区会有逐渐增加的数据。
串口接收数据
第二个项目是串口发送:
主程序代码:
cpp#include <STC89C5xRC.H> #include "Delay.h" #include "UART.h" void main(){ UATE_Init();//初始化 while(1){ } } //中断函数 void UART_Routine() interrupt 4{ if(RI==1){//RI等于1表示可以中断 P2=~SBUF;//发送数据 UATE_SendByte(SBUF);//接收数据 RI=0;//软件复位 } }
注意:发送是电脑向串口发送数据,利用中断来控制LED灯(P2),RI=1打开中断。
UART.c代码:
cppvoid UATE_Init() //[email protected] { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //8位数据,可变波特率 // AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T // AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //清除定时器1模式位 TMOD |= 0x20; //设定定时器1为8位自动重装方式 TL1 = 0xFA; //设定定时初值 TH1 = 0xFA; //设定定时器重装值 ET1 = 0; //禁止定时器1中断 TR1 = 1; //启动定时器1 EA=1;//启动总中断 ES=1;//启动串口中断 } //发送字节(只发送,不接受) void UATE_SendByte(unsigned char Byte){ SBUF=Byte;//传入字节数据 while(TI==0);//发送循环,发送完TI=1; TI=0;//软件复位 }
有中断,因此EA,ES启动中断。
运行:
发送对应的16进制,用LED灯来检测,如发送0f,就是0000 1111,也就是后四个灯亮,单片机对应的灯会亮。
串口发送数据
代码解析与教程:
Dealy模块
- 包含源代码与头文件,不需要知道怎么实现的会用即可,后续使用,直接将头文件和源代码拿过来用即可;
xms是定义的毫秒,1000毫秒就是1秒;模版生成的是1毫秒的,因此xms等于1000UART模块
包含源代码与头文件,需要知道怎么实现,会用
51单片机串口通讯十分重要,要理解怎么用,要知道原理是什么,要结合原理图来分析怎么做,先看代码
cpp#include <STC89C5xRC.H> //初始化串口 //void UART_Init(){ // SCON=0x40;//0100 0000 // PCON=0; // //&只有在两个位都为1时结果位才是1,而|只要有一个位为1结果位就是1 // TMOD &= 0x0F; //设置定时器模式,高四位清0,低四位不变 // TMOD |= 0x20; //设置定时器模式,高四位设计成0010,低四位不变,定时器1,模式2 // TL0 = 0x66; //设置定时初值 // TH0 = 0xFC; //设置定时初值 // TF0 = 0; //清除TF0标志 // TR0 = 1; //定时器0开始计时 // ET0=1;//允许中断 // EA=1;//允许总中断 // PT0=0;//低优先级 //} void UATE_Init() //[email protected] { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //8位数据,可变波特率 // AUXR &= 0xBF; //定时器1时钟为Fosc/12,即12T // AUXR &= 0xFE; //串口1选择定时器1为波特率发生器 TMOD &= 0x0F; //清除定时器1模式位 TMOD |= 0x20; //设定定时器1为8位自动重装方式 TL1 = 0xFA; //设定定时初值 TH1 = 0xFA; //设定定时器重装值 ET1 = 0; //禁止定时器1中断 TR1 = 1; //启动定时器1 } //发送字节(只发送,不接受) void UATE_SendByte(unsigned char Byte){ SBUF=Byte;//传入字节数据 while(TI==0);//发送循环,发送完TI=1; TI=0;//软件复位 }
最上面的代码:主要是理解SCON和PCON,其他的就是定时器/计数器,中断的内容
Timer0模块
- 包含源代码与头文件,需要知道怎么实现,会用
- 51单片机的定时器和计数器十分重要,要理解怎么用,要知道原理是什么,要结合原理图来分析怎么做,先看代码
cpp#include <STC89C5xRC.H> //void Timer0_Init() //{ // TF0=0;TR0=1;//TCON,寄存器 // //TMOD=0x01;//0000 0001,寄存器 // TMOD=TMOD&0xF0;//把TMOD的低四位清零,高四位不变,方便使用两个定时器 // TMOD=TMOD&0x01;//把TMOD的最低位置1,高四位不变,方便使用两个定时器 // TH0=64535/256;//高电位,寄存器,1毫秒 // TL0=64535%256;//低电位,寄存器,1毫秒 // ET0=1;EA=1;PT0=0;//打开中断开关 // //} //定时器0初始化函数 void Timer0_Init() //1毫秒@11.0592MHz { // AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0x66; //设置定时初值 TH0 = 0xFC; //设置定时初值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0=1;//允许中断 EA=1;//允许总中断 PT0=0;//低优先级 } 中断程序函数 //中断函数模版 //void Timer0_Routine() interrupt 1 //{ // static unsigned int T0Count; // TL0 = 0x66; //设置定时初值 // TH0 = 0xFC; //设置定时初值 // T0Count++; // if(T0Count>=1000){ // T0Count=0; // //下面是代码区 // } //}
- 最上面注释掉的代码是要求理解的;中间的代码是STC-ISP软件生成的;最下面的代码是中断函数模版,拿到main.c中可直接使用,但是也要了解原理:
定时器/计数器、中断教程(重点!!!!)
- 首先,要理解原理,会认原理图:
(本篇均是我自己理解的,只是帮助大家理解,若像深学深究,请去自行找资源,如有不对,希望大家指出)
先看官方解释:
- 红色部分是定时器,作用是自己设定一个最大值,让计数器达到时,完成什么什么,比如执行中断
- 黄色部分是计数器,作用是自己设定,让其变化,比如+1,毫秒,微妙,完成时继续怎么怎
- 蓝色部分是中断器,中断当前执行,执行自己设定的东西,中断这部分可以嵌套,像if函数一样,低优先级让高优先级
++他们三者的关系非常微妙,仅仅相连,相辅相成:++
- 定时器就是设定一个时间,计数器开始计时,到点了中断器开始执行;举个例子:你订一个10.00的闹钟(定时器)提醒你起床,时间一点一点的过去(计数器),10.00的时候闹钟提醒你(定时器),随后你开始起床(中断器);那么他们是怎么实现的呢
定时器/计数器(模式一)
- 先看原理图:
- 相关寄存器:
- 官方解释
- 模式一官方解释
- ++下面开始来解释我的理解(再看原理图):++
- 序号1是非门:反向输出,例如:GATE是1,输出0,是0,输入1。
- 序号2是或门:符号为梯形(或弧形缺口),逻辑上满足 "有 1 出 1,全 0 出 0"。
- 序号3是与门 :符号为矩形缺口,逻辑上满足 "全 1 出 1,有 0 出 0"。
++1,2,3序号均是TMOD里的,请看原理图:++![]()
代码TMOD=0x01,是16进制,转化为二进制为0000 0001,前(高)四位对应着定时器1;后(低)四位对应着定时器0;本代码使用的是定时器0, 0001分别对应GATE,C/T,M1,M0,由上面的官方解释可得,M1=0,M0=1时,就是使用并启动本寄存器。
但是这样定义有弊端,也就是定时器1和定时器0不能一起使用,因次,使用下面的代码:TMOD &=0xF0,也就是TMOD = TMOD & 0xF0(1111 0000)。看不懂没关系,举个例子:1010 0101 & 1111 0000 = 1010 0000,也就是有0出0;
1010 0101 | 1111 0000 = 1111 0101,也就是有1出1;因此使用代码:
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
就可以定义定时器0;
因此,当GATE=0时,通过序号1,输出1;然后通过序号2,输出1;因此,只需要将TR0设定成1,输出就是1,就可以启动寄存器了。
- 序号4是高电位和低电位寄存器 :用来设置定时初值,如图:
![]()
- 先解释上面的代码,可帮助理解,下面的代码可以不理解(后续可生成):TH0和TL0,最大位就是65535,为了分开储存,用TH0和TL0分别储存,例如:现在有一个数123,但是一个盒子只能装进2位数,因此分成123/100=1和123%100=23储存,同理身为16进制,就要用256来做除数 ;代码中设定为64535的目的是因为1000毫秒就是1秒,65535-64535=1000,用来表示1秒,计时器每次加1秒。
- 序号7.8(图中忘记标了(TR0))是TCON(定时器控制)寄存器:TF0=0时,可以理解成初始化;TR0=1时,表示允许计时;反之两个就是反义理解
- 序号5.6是TMOD(定时器模式)寄存器 :用来控制定时器和计数器的模式,本篇只讲用的多的模式一
中断器
先看原理图:
这里我们定义好定时器0后,TF0=1,ET0=1,打开中断,随后PT0=0,接入低级优先:
理解上面的东西后,再看中断函数:
运用静态局部变量T0Count,来表示定时区间,达到1000毫秒(1秒)后重新执行该函数
P20行代码是代码区,也就是放你想每1秒就重复执行的代码。
串口通讯教程(方式1)(接收和发送)(重点!!!!!!)
主要是理解SCON和PCON,其他的就是定时器/计数器,中断的内容
SCON(方式1)
SCON先看官方解释:
SCON解释:
如图:SCON正好是个16进制,8个变量;我们只用方式1 ;根据官方解释来看,SM0=0,SM1=1,就是方式1;SM2,TB8,RB8不是方式1的制定为0;REN=1是启动串口接收;TI,RI默认为0,表示串口通讯(中断)允许开启(后续可变);
PCON
PCON官方解释:
PCON解释:
SBUF就是接收数据的,如图:
根据我标的图来看,你需要将数据写进SBUF里,然后通过设置定时器初值波特率倍率,然后从发送端到接收端。
cpp//发送字节(只发送,不接受) void UATE_SendByte(unsigned char Byte){ SBUF=Byte;//传入字节数据 while(TI==0);//发送循环,发送完TI=1; TI=0;//软件复位 }
其中TI就是SCON中的其中一个变量,发送的时候TI=0,发送完TI=1,因此要自定义软件复位。EA,ES分别置于1,作用如下:
cppEA=1;//启动总中断 ES=1;//启动串口中断
下面来看:
cppET1 = 0; //禁止定时器1中断 TR1 = 1; //启动定时器1
TR1是定时器的教程(放在下面了),作用是打开定时器1
ET1是中断的内容作用是禁止定时器1中断,如图:
现在来看中断函数:
RI=0是接收数据用的,当接收时RI=1,接收完毕后进行软件复位。
LED教程:
- 先看原理图:
**
LED是高电位,设定为0就是通电- P2就是16进制的LED灯总控制,如:0xFF=1111 1111,就是全关,例如代码中的:
P2=0xFE,就是1111 1110,就是L1亮,左移一位就是1111 1101,就是L2亮,其他同理,配合中断函数 ,就可以每秒移动一次
举一反三(新项目):
++在第二个项目的基础上,改一下main.c文件++
cpp
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"
void main(){
UATE_Init();//初始化
UATE_SendByte(SBUF);//发送字节(新增)
while(1){
}
}
//中断函数
void UART_Routine() interrupt 4{
if(RI==1){//RI等于1表示可以中断
P2=~SBUF;//发送数据
UATE_SendByte(SBUF);//接收数据
RI=0;//软件复位
}
}
就可以做到发送缓冲区发送后, 接收缓冲区也显示:

注意选择同样的模式(文本模式或者HEX模式) ,支持的文本如下:

效果视频:
串口的接收和发送
注:该代码是本人自己所写,可能不够好,不够简便,欢迎大家指出我的不足之处。如果遇见看不懂的地方,可以在评论区打出来,进行讨论,或者联系我。上述内容全是我自己理解的,如果你有别的想法,或者认为我的理解不对,欢迎指出!!!如果可以,可以点一个免费的赞支持一下吗?谢谢各位彦祖亦菲!!!!!