(51单片机)串口通讯(串口通讯教程)(串口接收发送教程)

前言:

今天有两个项目,分别为:

串口接收:

串口发送:

如上图将文件放在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()		//4800bps@11.0592MHz
{
	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()		//4800bps@11.0592MHz
{
	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代码:

    cpp 复制代码
    void UATE_Init()		//4800bps@11.0592MHz
    {
    	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代码:

    cpp 复制代码
    void UATE_Init()		//4800bps@11.0592MHz
    {
    	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等于1000

UART模块
  • 包含源代码与头文件,需要知道怎么实现,会用

  • 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()		//4800bps@11.0592MHz
    {
    	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. 首先,要理解原理,会认原理图:

(本篇均是我自己理解的,只是帮助大家理解,若像深学深究,请去自行找资源,如有不对,希望大家指出)

先看官方解释:

  • 红色部分是定时器,作用是自己设定一个最大值,让计数器达到时,完成什么什么,比如执行中断
  • 黄色部分是计数器,作用是自己设定,让其变化,比如+1,毫秒,微妙,完成时继续怎么怎
  • 蓝色部分是中断器,中断当前执行,执行自己设定的东西,中断这部分可以嵌套,像if函数一样,低优先级让高优先级

++他们三者的关系非常微妙,仅仅相连,相辅相成:++

  • 定时器就是设定一个时间,计数器开始计时,到点了中断器开始执行;举个例子:你订一个10.00的闹钟(定时器)提醒你起床,时间一点一点的过去(计数器),10.00的时候闹钟提醒你(定时器),随后你开始起床(中断器);那么他们是怎么实现的呢
定时器/计数器(模式一)
  • 先看原理图:
  • 相关寄存器:
  • 官方解释
  • 模式一官方解释
  • ++下面开始来解释我的理解(再看原理图):++
  1. 序号1是非门:反向输出,例如:GATE是1,输出0,是0,输入1。
  2. 序号2是或门:符号为梯形(或弧形缺口),逻辑上满足 "有 1 出 1,全 0 出 0"。
  3. 序号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;因此使用代码:

      复制代码
      1. TMOD &= 0xF0; //设置定时器模式

      2. TMOD |= 0x01; //设置定时器模式

      就可以定义定时器0;

    • 因此,当GATE=0时,通过序号1,输出1;然后通过序号2,输出1;因此,只需要将TR0设定成1,输出就是1,就可以启动寄存器了。

  4. 序号4是高电位和低电位寄存器 :用来设置定时初值,如图:
    • 先解释上面的代码,可帮助理解,下面的代码可以不理解(后续可生成):TH0和TL0,最大位就是65535,为了分开储存,用TH0和TL0分别储存,例如:现在有一个数123,但是一个盒子只能装进2位数,因此分成123/100=1和123%100=23储存,同理身为16进制,就要用256来做除数 ;代码中设定为64535的目的是因为1000毫秒就是1秒,65535-64535=1000,用来表示1秒,计时器每次加1秒。
  5. 序号7.8(图中忘记标了(TR0))是TCON(定时器控制)寄存器:TF0=0时,可以理解成初始化;TR0=1时,表示允许计时;反之两个就是反义理解
  6. 序号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,作用如下:

    cpp 复制代码
        EA=1;//启动总中断
    	ES=1;//启动串口中断

    下面来看:

    cpp 复制代码
        ET1 = 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模式) ,支持的文本如下:

效果视频:

串口的接收和发送

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

相关推荐
猷咪8 分钟前
C++基础
开发语言·c++
17(无规则自律)9 分钟前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考
IT·小灰灰10 分钟前
30行PHP,利用硅基流动API,网页客服瞬间上线
开发语言·人工智能·aigc·php
快点好好学习吧11 分钟前
phpize 依赖 php-config 获取 PHP 信息的庖丁解牛
android·开发语言·php
秦老师Q12 分钟前
php入门教程(超详细,一篇就够了!!!)
开发语言·mysql·php·db
烟锁池塘柳012 分钟前
解决Google Scholar “We‘re sorry... but your computer or network may be sending automated queries.”的问题
开发语言
是誰萆微了承諾12 分钟前
php 对接deepseek
android·开发语言·php
CSDN_RTKLIB15 分钟前
WideCharToMultiByte与T2A
c++
2601_9498683616 分钟前
Flutter for OpenHarmony 电子合同签署App实战 - 已签合同实现
java·开发语言·flutter
飞机和胖和黄28 分钟前
考研之王道C语言第三周
c语言·数据结构·考研