51单片机学习–定时器–中断–串口通信

定时器–中断–串口通信

中断分类

定时器中断 外部中断 串口中断

  1. 基本概念
    对于单片机来讲, 中断是指 CPU 在处理某一事件 A 时, 发生了另一事件 B,
    请求 CPU 迅速去处理(中断发生); CPU 暂时停止当前的工作(中断响应), 转去
    处理事件 B(中断服务); 待 CPU 将事件 I 处理完毕后, 再回到原来事件 A 被
    中断的地方继续处理事件 A(中断返回), 这一过程称为中断。
  2. 学习目的
    中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,
    中断功能的存在, 很大程度上提高了单片机处理外部或内部事件的能力。 它也是
    单片机最重要的功能之一, 是我们学习单片机必须要掌握的。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  3. 流程图
  4. 中断系统-中断源
    引起 CPU 中断的根源称为中断源。 中断源向 CPU 提出中断请求, CPU 暂时
    中断原来的事务 A, 转去处理事件 B, 对事件 B 处理完毕后, 再回到原来被中断
    的地方(即断点), 称为中断返回。 实现上述中断功能的部件称为中断系统(中断
    机构)。
  5. 优点
    ①分时操作。 CPU 可以分时为多个 I/O 设备服务, 提高了计算机的利用率;
    ②实时响应。 CPU 能够及时处理应用系统的随机事件, 系统的实时性大大增
    强;
    ③可靠性高。 CPU 具有处理设备故障及掉电等突发性事件能力, 从而使系统
    可靠性提高。
  6. STC89C5X系列单片机一般配置
    51 系列单片机一定有基本的 5 个中断, 但不全有 8 个中断, 需要查看芯片手册,基本的 5 个中断: INT0、 INT1、 定时器 0/1, 串口中断。
    基本类型单片机的中断有2个中断优先级。
    用户可以用关总中断允许位(EA/IE.7)或相应中断的允许位来屏蔽所有的中断请求, 也可以用打开相应的中断允许位来使 CPU 响应相应的中断申请。 其中有些中断源可以用软件独立地控制为开中断或关中断状态。 每一个中断的优先级别均可用软件设置。
    高优先级的中断请求可以打断低优先级的中断, 反之, 低优先级的中断请求不可以打断高优先级及同优先级的中断。 当两个相同优先级的中断同时产生时, 将由查询次序来决定系统先响应哪个中断。 STC89C5X 系列单片机的各个中断查询次序表如下图所示:
  7. 传统51单片机的5个基本中断源的理解

    ①INT0 对应的是 P3.2 口的附加功能, 可由 IT0(TCON.0)选择其为低电平有
    效还是下降沿有效。 当 CPU 检测到 P3.2 引脚上出现有效的中断信号时, 中断标
    志 IE0(TCON.1)置 1, 向 CPU 申请中断。
    ②INT1 对应的是 P3.3 口的附加功能, 可由 IT1(TCON.2)选择其为低电平有
    效还是下降沿有效。 当 CPU 检测到 P3.3 引脚上出现有效的中断信号时, 中断标
    志 IE1(TCON.3)置 1,向 CPU 申请中断。
    ③T0 对应的是 P3.4 口的附加功能, TF0(TCON.5) ,片内定时/计数器 T0 溢
    出中断请求标志。 当定时/计数器 T0 发生溢出时, 置位 TF0, 并向 CPU 申请中断。
    ④T1 对应的是 P3.5 口的附加功能, TF1( TCON.7) , 片内定时/计数器 T1
    溢出中断请求标志。 当定时/计数器 T1 发生溢出时, 置位 TF1, 并向 CPU 申请中
    断。
    ⑤RXD 和 TXD 对应对应的是 P3.0 和 P3.1 口的附加功能, RI(SCON.0) 或 TI
    (SCON.1) , 串行口中断请求标志。 当串行口接收完一帧串行数据时置位 RI 或
    当串行口发送完一帧串行数据时置位 TI, 向 CPU 申请中断。
  8. 中断允许控制
    CPU 对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制。

    EX0(IE.0), 外部中断 0 允许位;
    ET0(IE.1), 定时/计数器 T0 中断允许位;
    EX1(IE.2), 外部中断 0 允许位;
    ET1(IE.3), 定时/计数器 T1 中断允许位;
    ES(IE.4), 串行口中断允许位;
    EA (IE.7), CPU 中断允许(总允许)位。
  9. 中断请求标志 TCON

    IT0(TCON.0) , 外部中断 0 触发方式控制位。
    当 IT0=0 时, 为电平触发方式。
    当 IT0=1 时, 为边沿触发方式(下降沿有效) 。
    IE0(TCON.1) , 外部中断 0 中断请求标志位。
    IT1(TCON.2) , 外部中断 1 触发方式控制位。
    IE1(TCON.3) , 外部中断 1 中断请求标志位。
    TF0(TCON.5) , 定时/计数器 T0 溢出中断请求标志位。
    TF1(TCON.7) , 定时/计数器 T1 溢出中断请求标志位。
  10. 中断优先级
  11. 中断号
  12. 中断响应条件
    ①中断源有中断请求;
    ②此中断源的中断允许位为1;
    ③CPU 开中断(即 EA=1)。
  13. 中断源入口地址
    中断源 入口地址
    外部中断0 0003H
    定时器T0中断 000BH
    外部中断1 0013H
    定时器T1中断 001BH
    串行口中断 0023H

软件代码分析

一般具有的框架

//主程序中需要有以下代码:
EA=1//打开总中断开关
EX0=1//开外部中断 0
IT0=0/1//设置外部中断的触发方式普中 51 单片机开发攻略
//中断服务函数
void int0() interrupt 0 using 1
{
    // 编写用户所需的功能代码
}

注意:在中断函数中 int0 是函数名,可自定义,但必须符合 C 语言标识符定义规则, interrupt 是一个关键字, 表示 51 单片机中断。 后面的“0” 是中断号, 外部中断 0 中断号为 0, 这个可参考前面的内容。后面的 using 1 可省略不写。

外部中断

void Int0Init()

{
    //设置 INT0
    IT0=1;//跳变沿出发方式(下降沿)
    EX0=1;//打开 INT0 的中断允许。
    EA=1;//打开总中断
} 

//触发中断后即会进入中断服务函数, 外部中断 0 中断服务函数如下:
void Int0() interrupt 0 //外部中断 0 的中断函数
{
    //执行所需的功能
}

定时器

  1. 基本概念
    ①振荡周期: 为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡
    周期) 。
    ②状态周期: 2 个振荡周期为 1 个状态周期, 用 S 表示。 振荡周期又称 S 周
    期或时钟周期。
    ③机器周期: 1 个机器周期含 6 个状态周期, 12 个振荡周期。
    ④指令周期: 完成 1 条指令所占用的全部时间, 它以机器周期为单位。
    例如: 外接晶振为 12MHz 时, 51 单片机相关周期的具体值为:
    振荡周期=1/12us;
    状态周期=1/6us;
    机器周期=1us;
    指令周期=1~4us;
  2. 注意
    ①51 单片机有两组定时器/计数器, 因为既可以定时, 又可以计数, 故称之
    为定时器/计数器。
    ②定时器/计数器和单片机的 CPU 是相互独立的。 定时器/计数器工作的过程
    是自动完成的, 不需要 CPU 的参与。
    ③51 单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信
    号对寄存器中的数据加 1。
  3. 原理
    STC89C5X 单片机内有两个可编程的定时/计数器T0、T1和一个特殊功能定时器 T2。定时/计数器的实质是加 1 计数器(16 位),由高 8 位和低 8 位两个寄存器 THx 和 TLx 组成。 它随着计数器的输入脉冲进行自加 1, 也就是每来一个脉冲, 计数器就自动加 1, 当加到计数器为全 1 时, 再输入一个脉冲就使计数器回零, 且计数器的溢出使相应的中断标志位置 1, 向 CPU 发出中断请求(定时/计数器中断允许时) 。 如果定时/计数器工作于定时模式, 则表示定时时间已到;如果工作于计数模式, 则表示计数值已满。 可见, 由溢出时计数器的值减去计数初值才是加 1 计数器的计数值。
  4. 定时器/计数器内部结构

    T0 和 T1 引脚对应的是单片机 P3.4 和 P3.5 管脚.
    51单片机定时器/计数器:两个特殊功能寄存器控制。
    TMOD 是定时/计数器的工作方式寄存器, 确定工作方式和功能;
    TCON 是控制寄存器, 控制 T0、 T1 的启动和停止及设置溢出标志;
  5. TMOD寄存器 工作方式寄存器
    工作方式寄存器 TMOD 用于设置定时/计数器的工作方式, 低四位用于 T0, 高
    四位用于 T1。
    GATE=0;(不需要外部中断INT0/1引脚为高电平)
    用于控制定时器的启动是否受外部中断源信号的影响。只要用软件使 TCON 中的 TR0或TR1为1,就可以启动定时/计数器工作;
    GATE=1;
    要用软件使TR0或TR1为1.同时外部中断引脚 INT0/1 也为高电平时,才能启动定时/计数器工作.即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件;

C/T :定时/计数模式选择位。
C/T =0 为定时模式;
C/T =1 为计数模式;


M1M0:工作方式设置位:定时器/计数器共有4种方式。
M1M0 工作方式计数器模式 TMOD(设置定时器模式)
0 0 方式0 13位计数器 TMOD=0x00
0 1 方式1 16位计数器 TMOD=0x01
1 0 方式2 自动重装8位计数器 TMOD=0x02
1 1 方式3 T0分为2个8位独立计数器,T1为无中断重装8位计数器 TMOD=0x03

  1. 控制寄存器 TCON
    TCON 的低 4 位用于控制外部中断,已在前面介绍。 TCON 的高 4 位用于控制定
    时/计数器的启动和中断申请。

    TCON寄存器高四位
    TF1(TCON.7):T1溢出中断请求标志位。T1计数溢出时由硬件自动置TF1
    为 1。 CPU 响应中断后TF1由硬件自动清0。T1工作时,CPU可随时查询 TF1 的状态。 所以, TF1 可用作查询测试的标志。 TF1 也可以用软件置 1 或清 0, 同硬件置 1 或清 0 的效果一样。
    TR1(TCON.6):T1 运行控制位。 TR1 置 1 时, T1 开始工作; TR1 置 0 时,T1 停止工作。 TR1 由软件置 1 或清 0。 所以, 用软件可控制定时/计数器的启动与停止。
    TF0(TCON.5) : T0 溢出中断请求标志位, 其功能与 TF1 类同。
    TR0(TCON.4) : T0 运行控制位, 其功能与 TR1 类同。

TR1,TR0=0;关闭定时器1,0
TR1,TR0=1;打开定时器1,0

  1. 定时器/计数器工作方式
方式0

方式 0 为 13 位计数, 由 TL0 的低 5 位(高 3 位未用) 和 TH0 的 8 位组成。TL0 的低 5 位溢出时向 TH0 进位, TH0 溢出时, 置位 TCON 中的 TF0 标志, 向 CPU发出中断请求。

方式1

方式 1 的计数位数是 16 位, 由 TL0 作为低 8 位, TH0 作为高 8 位, 组成了16 位加 1 计数器。

方式2

方式 2 为自动重装初值的 8 位计数方式。 工作方式 2 特别适合于用作较精确的脉冲信号发生器。

方式3

方式 3 只适用于定时/计数器 T0, 定时器 T1 处于方式 3 时相当于 TR1=0,
停止计数。 工作方式 3 将 T0 分成为两个独立的 8 位计数器 TL0 和 TH0。
在这里插入图片描述

  1. 单片机编写定时器程序时一些步骤
    对TMOD赋值,以确定T0和T1的工作方式;
    计算初值,并将初值写入TH0,TL0或者TH1,TL1;
    中断方式时,则对IE赋值,开放中断
    使TR0或TR1置位,启动定时器/计数器定时或者计数。
  2. 应用
    应用较多的是方式 1 和方式 2。 定时器中通常使用定时器方式 1, 串口通信中通常使用方式 2。

定时器配置

步骤

①对 TMOD 赋值,以确定 T0 和 T1 的工作方式,如果使用定时器0即对T0配置, 如果使用定时器 1 即对 T1 配置。
②根据所要定时的时间计算初值,并将其写入 TH0、 TL0 或 TH1、 TL1。
③如果使用中断, 则对 EA 赋值, 开放定时器中断。
④使 TR0 或 TR1 置位, 启动定时/计数器定时或计数。

初值的计算:

TH0=(65536-50000)/256; //装定时器初值高8位
TL0=(65536-50000)%256; //装定时器初值低8位 
TH0=(65536-T0)/256 ;//装定时器初值高8位
TL0=(65536-T0)%256 ;//装定时器初值低8位
//其中T0是需要计算的时间
//一般编程过程并直接计算T0的值,直接写让单片机自己去计算
//定时器0中断函数 
void T0/T1_time() interrupt 1//使用定时器中断1
{
    
    TMOD=0X01;//选择了定时器0 方式1
    //可以设置TMOD=0x00;0x01;0x10;0x11;共四组方式
    
    TH0=(65536-T0)/256;   //装定时器初值高8位
	TL0=(65536-T0)%256;    //装定时器初值低8位
    //T0是需要计算的时间
    uchar count=0;
    if(count==X)
    
    {
        count=0; //清0重新计数
        执行程序代码
    }
    //其中X是需要计算次数,比如让定时器定时个1s;
    //则需要定时器一个50ms的时间;
    //如果要达成1s则需要执行20次;
    //让定时器定时个50ms时间,需要这样去写
    //TH0=(65536-50000)/256;
    //TL0=(65536-50000)%256;
    //让定时器定时个T0时间,需要这样去写
    //
     //TH0=(65536-T0乘10的3次方)/256;
    //TL0=(65536-T0乘10的3次方)%256;
// 使用定时器0
//配置定时器0工作方式1,设置1ms的初值,打开定时器计数器功能
void Timer0Init()
{
    TMOD|=0X01;//选择为定时器 0 模式, 工作方式 1, 仅用 TR0 打开启动。
    TH0=0XFC; //给定时器赋初值, 定时 1ms
    TL0=0X18;
    ET0=1;//打开定时器 0 中断允许
    EA=1;//打开总中断普中 51 单片机开发攻略
    TR0=1;//打开定时器
}

简单案例

/**************************************************************** ********************** 实验现象: 下载程序后, D1 小灯循环点亮 1 秒, 熄灭 1 秒。 使用单片机内部 定时器可以实现准确延时 接线说明: (具体接线图可见开发攻略对应实验的“实验现象” 章节) 1, 单片机-->LED&交通灯模块 P20-->D1 注意事项: ***************************************************************** **********************/
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit led=P2^0; //定义 P20 口是 led
/**************************************************************** *************** * 函 数 名 : Timer0Init * 函数功能 : 定时器 0 初始化 * 输 入 : 无 * 输 出 : 无 ***************************************************************** **************/
void Timer0Init()
{
    TMOD|=0X01;//选择为定时器 0 模式, 工作方式 1, 仅用 TR0 打开启动。
    TH0=0XFC; //给定时器赋初值, 定时 1ms
    TL0=0X18;
    ET0=1;//打开定时器 0 中断允许
    EA=1;//打开总中断
    TR0=1;//打开定时器
}
/**************************************************************** *************** * 函 数 名 : main * 函数功能 : 主函数 * 输 入 : 无 * 输 出 : 无 ***************************************************************** **************/
void main()
{
    Timer0Init(); //定时器 0 初始化
    while(1);
}
/**************************************************************** *************** * 函 数 名 : void Timer0() interrupt 1 * 函数功能 : 定时器 0 中断函数 * 输 入 : 无 * 输 出 : 无 ***************************************************************** **************/
void Timer0() interrupt 1
{
    static u16 i;
    TH0=0XFC; //给定时器赋初值, 定时 1ms
    TL0=0X18;
    i++;
    if(i==1000)
    {
        i=0;
        led=~led;
    }
}

串口中断(目前还没有写)

串口相关寄存器

  1. 串口控制寄存器SCON

  2. 电源控制寄存器PCON

串口工作方式

1.方式0
2.方式1
3.方式2
4.方式4

串口初始化

步骤
①确定 T1 的工作方式(TMOD 寄存器) ;
②确定串口工作方式(SCON 寄存器) ;普中 51 单片机开发攻略
③计算 T1 的初值(设定波特率) , 装载 TH1、 TL1;
④启动 T1(TCON 中的 TR1 位) ;
⑤如果使用中断, 需开启串口中断控制位(IE 寄存器)。

串口初始化案例

void UsartInit()
{
    SCON=0X50; //设置为工作方式 1
    TMOD=0X20; //设置计数器工作方式 2
    PCON=0X80; //波特率加倍
    TH1=0XF3; //计数器初始值设置, 注意波特率是 4800 的
    TL1=0XF3;
    ES=1; //打开接收中断
    EA=1; //打开总中断
    TR1=1; //打开计数器
}

串口中断方式案例

#include <reg52.h>
#define FOSC 12000000UL 
#define BAUD 2400UL 
void  UsartConfiguration(void)				//串口初始化
{
		 TMOD&=0x0F;      //定时器1模式控制在高4位
		 TMOD|=0x20;      //定时器1工作在模式2,自动重装模式
		 SCON=0x50;       //串口工作在模式1
		 TH1=256-FOSC/(BAUD*12*16);  //计算定时器重装值
		 TL1=256-FOSC/(BAUD*12*16);
		 PCON|=0x80;    //串口波特率加倍
		 ES=1;         //串行中断允许
		 TR1=1;        //启动定时器1
		 REN=1;        //允许接收 
		 EA=1;         //允许中断
}
void InterruptUART() interrupt 4 //中断源是4
{
	//unsigned char receive_data;
	if(RI)
	{
	 	RI = 0;
		SBUF = SBUF;
		//receive_data = SBUF;//接收到的数据
		//if(receive_data == '1') 
		//{
    // LED1 = 0;
    // DELAY_nMS(500);
    // LED1=1;
    //}
// if(receive_data == ' ') //接受一个字符 
// {
// //执行代码,你想要的控制的东西,例如led,蜂鸣器,等传感器 
// } 
	 }
	//EA=1;
	if(TI)
	{
		TI=0;	
	}		
}

void main()
{
	UsartConfiguration();
	while(1)
	{
		;
	}
	
}

软件代码

简单实现定时器led

#include"reg51.h"

sbit led=P1^1;//定义一个LED灯为1.1引脚
#define uchar unsigned char
//#define uint unsigned int
uchar num;//定义全局变量

void timer0Iint()//定时器0的初始化函数
{   
    TMOD=0x01;//设置定时器0为工作方式1(M1 M0为01)
    TH0=(65536-50000)/256;//装初值11.0582晶振定时50ms数为45872
    TL0=(65536-50000)%256;
    EA=1;//开总中断
    ET0=1;//开定时器0中断
}

void T0_time() interrupt 1
{
       num++;//num每加一次判断一次是否到20次
      if(num==20)//如果到了20次,说明1秒时间到
        {
            num=0;//num清0重新计数
            led=~led;
        }
}

void main()
{
     timer0Iint();
     TR0=1;//启动定时器0//该步骤也可在初始化写
    while(1)
    {
    }//程序停止在这里等待中断发生
 } 

一些案例

  1. 普中单片机串口接收数据控制灯亮灭
/*----------------------------------------------------------------------------- * 实 验 名 : 串口试验 * 实验说明 : 普中单片机串口接收数据控制灯亮灭 * 连接方式 : * 注 意 : 该试验使用的晶振是12MHZ *******************************************************************************/
 
#include<reg52.h>
#include<intrins.h>
 
#define uchar unsigned char
#define uint unsigned int
	

#define jingzhen 12000000UL /*使用12.0M晶体*/	
#define botelv 4800UL /*波特率定义为4800*/
 
uchar PuZh[] = "www.lemonhubs.github.io";
 
//--定义使用的IO--//
 
sbit LED1=P2^0;
sbit LED2=P2^1;
sbit LED3=P2^2;
 
//--声明全局函数--// 
void UsartConfiguration();
void Delay_1ms(uint i);


void DELAY_nMS(unsigned int time);
 
/******************************************************************** * 名称 : Com_Int() * 功能 : 串口中断子函数 * 输入 : 无 * 输出 : 无 ***********************************************************************/
void Com_Int(void) interrupt 4
{
	uchar i;
  uchar receive_data;
	
  EA = 0;
	
  if(RI == 1) //当硬件接收到一个数据时,RI会置位
	{ 		
			RI = 0;
			receive_data = SBUF;//接收到的数据
			
		if(receive_data == '1')	 
		{
				LED1= 0;
			  DELAY_nMS(500);
				LED2= 0;
			 DELAY_nMS(500);
				LED3= 1;
			 DELAY_nMS(500);
		}
		else
		{
				LED1 = 1; 
			 DELAY_nMS(500);
			  LED2 = 1; 
			 DELAY_nMS(500);
			  LED3 = 0; 
			 DELAY_nMS(500);
		}
		
	}
		
		for(i=0; i<36; i++)
		{
			SBUF = PuZh[i];   //将要发送的数据放入到发送寄存器
			while(!TI);		    //等待发送数据完成
			TI=0;			        //清除发送完成标志位
			Delay_1ms(1);
		}
		EA = 1;
}
 
/******************************************************************************* * 函 数 名 : main * 函数功能 : 主函数 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/
 
void main()
{
 
	UsartConfiguration();//初始化串口
	
	while(1);	
}
	
/******************************************************************************* * 函 数 名 : UsartConfiguration() * 函数功能 : 设置串口 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/


void  UsartConfiguration(void)				//串口初始化
{
		 EA=0;            //暂时关闭中断
		 TMOD&=0x0F;      //定时器1模式控制在高4位
		 TMOD|=0x20;      //定时器1工作在模式2,自动重装模式
		 SCON=0x50;       //串口工作在模式1
		 TH1=256-jingzhen/(botelv*12*16);  //计算定时器重装值
		 TL1=256-jingzhen/(botelv*12*16);
		 PCON|=0x80;    //串口波特率加倍
		 ES=1;         //串行中断允许
		 TR1=1;        //启动定时器1
		 REN=1;        //允许接收 
		 EA=1;         //允许中断
}
 
 
/******************************************************************** * 名称 : Delay_1ms() * 功能 : 延时子程序,延时时间为 1ms * x * 输入 : x (延时一毫秒的个数) * 输出 : 无 ***********************************************************************/
void Delay_1ms(uint i)//1ms延时
{
  uchar x,j;
  
  for(j=0;j<i;j++)
    for(x=0;x<=148;x++);
}

void DELAY_nMS(unsigned int time)
{
  unsigned int  i,j;             
  for(i=0;i<time;i++)    
     for(j=0;j<939;j++); 
}
  1. 普中单片机—串口通信(1)–通过按键控制发送
#include <reg52.h>

#define jingzhen 12000000UL /*使用12.0M晶体*/	
#define botelv 4800UL /*波特率定义为4800*/
 
unsigned char zifu='a';			//待显示字符

volatile unsigned char sending;

sbit S4=P3^2;//setting a key

void delay(unsigned char i)//延迟函数
{
	unsigned char j,k;
	for(j=i;j>0;j--)
		for(k=90;k>0;k--);
}
/************************************************************************* 函数名称:UART_init 函数功能:串口初始化 **************************************************************************/

void UART_init(void)				//串口初始化
{
		 EA=0;            //暂时关闭中断
		 TMOD&=0x0F;      //定时器1模式控制在高4位
		 TMOD|=0x20;      //定时器1工作在模式2,自动重装模式
		 SCON=0x50;       //串口工作在模式1
		 TH1=256-jingzhen/(botelv*12*16);  //计算定时器重装值
		 TL1=256-jingzhen/(botelv*12*16);
		 PCON|=0x80;    //串口波特率加倍
		 ES=1;         //串行中断允许
		 TR1=1;        //启动定时器1
		 REN=1;        //允许接收 
		 EA=1;         //允许中断
}
/************************************************************************* 函数名称:UART_Send_char 函数功能:发送一个字符 **************************************************************************/
void UART_Sendchar(unsigned char d)		  //发送一个字节的数据,形参d即为待发送数据。
{
	 SBUF=d; //将数据写入到串口缓冲
	 sending=1;	 //设置发送标志
	 while(sending); //等待发送完毕
}
/************************************************************************* 函数名称:UART_SendString 函数功能:发送一个字符串 **************************************************************************/
void UART_SendString(unsigned char *String)
{
    while(*String)
    {
        UART_Sendchar(*String);
        String++;
    }
}

/************************************************************************* 函数名称:UART_SendLine 函数功能:串口发送换行 **************************************************************************/
void UART_SendLine(void)
{
    UART_Sendchar(0x0D);
    UART_Sendchar(0x0A);
}

//*********************************************************************************** 
函数名称:UART_SendIntNumber
函数功能:串口发送整型数字
//*********************************************************************************** 
void UART_SendIntNumber(int Number)
{
    unsigned char NumbArray[6]={0};    // 定义局部数组,用于数据存储
    if(Number<0)
    {
        Number=0-Number;
        UART_Sendchar('-');
    }
    else
    {
       UART_Sendchar('+');
    }
    
    NumbArray[0]=(Number/10000)%10+0x30; 
    NumbArray[1]=(Number/1000) %10+0x30;
    NumbArray[2]=(Number/100)  %10+0x30;
    NumbArray[3]=(Number/10)   %10+0x30;
    NumbArray[4]=(Number/1)    %10+0x30; 
    NumbArray[5]= 0;     
    UART_SendString(NumbArray);
}

//*********************************************************************************** 


函数名称:UART_SendFloatNumber
函数功能:串口发送浮点型数字
//*********************************************************************************** 
void UART_SendFloatNumber(float Number)
{
    unsigned char NumberArray[11]={0};              // 定义局部数组,用于数据存储
    unsigned char i=1;                              // 定义局部变量,记录整数位数
    long j=1;
    unsigned int Real_Int=0;                        // 定义局部变量,记录整数部分
    unsigned int Real_Dec=0;                        // 定义局部变量,记录小数部分

    double Deci=0;                                  // 定义局部数组,暂存小数数值
    
    //----------------------------------------------------------------------------------------------
    // 判断 浮点数字正负
    //----------------------------------------------------------------------------------------------
    if(Number<0)
    {
        Number=0-Number;
        UART_Sendchar('-');
    }
    else
    {
        UART_Sendchar('+');
    }

    //----------------------------------------------------------------------------------------------
    // 分离 整数位与小数位
    //----------------------------------------------------------------------------------------------
    Real_Int=(int)Number;                           // 取整数部分
    Deci    =Number-Real_Int;                       // 取小数部分
    Real_Dec=(unsigned int)(Deci*1e4);              // 小数变整型数字,1e4科学计数法


    //----------------------------------------------------------------------------------------------
    // 串口输出
    //----------------------------------------------------------------------------------------------
    NumberArray[0] = (Real_Int/10000)%10+0x30;
    NumberArray[1] = (Real_Int/1000) %10+0x30;
    NumberArray[2] = (Real_Int/100)  %10+0x30;
    NumberArray[3] = (Real_Int/10)   %10+0x30;
    NumberArray[4] = (Real_Int/1)    %10+0x30;
    NumberArray[5] = '.';
    NumberArray[6] = (Real_Dec/1000)%10+0x30;
    NumberArray[7] = (Real_Dec/100) %10+0x30;
    NumberArray[8] = (Real_Dec/10)  %10+0x30;
    NumberArray[9] = (Real_Dec/1)   %10+0x30;
    NumberArray[10]= 0;

    UART_SendString(NumberArray);
}


/*************************************************************************
函数名称:MAIN
函数功能:主函数

**************************************************************************/
void main()
{
	UART_init();
	while(1)
	{
		if(S4==0)
		{
			delay(20);
			if(!S4)
			{
				while(!S4);		   
				//UART_Sendchar(zifu);
				//UART_SendString("dpj");
				UART_SendString("wlb");
				UART_SendLine();
				UART_SendIntNumber(100);
			}
		}
	}
}

void uart(void) interrupt 4		 //串口发送中断
{
	 if(RI)    //收到数据
	 {
		RI=0;   //清中断请求
	 }
	 else      //发送完一字节数据
	 {
			TI=0;
			sending=0;  //清正在发送标志
	 }
}


3. 普中单片机–串口通信(2)—通过串口助手发送数据点LED

#include <reg52.h>
#define jingzhen 12000000UL /*使用12.0M晶体*/	 //
#define botelv 4800UL /*波特率定义为4800*/
 
typedef unsigned char uchar;
typedef unsigned int uint;

uchar buf;

sbit beep=P1^5;

void main(void) 
{
		 EA=0; //暂时关闭中断
		 TMOD&=0x0F;  //定时器1模式控制在高4位
		 TMOD|=0x20;    //定时器1工作在模式2,自动重装模式
		 SCON=0x50;     //串口工作在模式1
		 TH1=256-jingzhen/(botelv*12*16);  //计算定时器重装值
		 TL1=256-jingzhen/(botelv*12*16);
		 PCON|=0x80;    //串口波特率加倍
		 ES=1;         //串行中断允许
		 TR1=1;        //启动定时器1
		 REN=1;        //允许接收 
		 EA=1;         //允许中断
    
	  while(1);        
}
/********************************************************* 串行中断服务函数 *********************************************************/
void  serial() interrupt 4 
{
   ES = 0;                //关闭串行中断
   RI = 0;                //清除串行接受标志位
   buf = SBUF;            //从串口缓冲区取得数据
  switch(buf)
   {
      case 0x31:  P2=0xfe;beep=1;break;  //接受到1,第一个LED亮 
      case 0x32:  P2=0xfd;beep=1;break;  //接受到2,第二个LED亮 
      case 0x33:  P2=0xfb;beep=1;break;  //接受到3,第三个LED亮 
      case 0x34:  P2=0xf7;beep=1;break;  //接受到4,第四个LED亮 
      case 0x35:  P2=0xef;beep=1;break;  //接受到5,第五个LED亮 
      case 0x36:  P2=0xdf;beep=1;break;  //接受到5,第六个LED亮 
      case 0x37:  P2=0xbf;beep=1;break;  //接受到5,第七个LED亮
	    case 0x38:  P2=0x7f;beep=1;break;  //接受到5,第八个LED亮
	    default:    beep=0;P2=0xff;break;  //接受到其它数据,蜂鸣器响 
   }
   ES = 1;    //允许串口中断
}
  1. HC05蓝牙模块LCD12864显示
/*----------------------------------------------------------------------------- * 实 验 名 : HC05蓝牙模块LCD12864显示 * 实验说明 : 普中单片机蓝牙串口接收数据控制灯亮灭和LCD12864显示 * 连接方式 : * 注 意 : 该试验使用的晶振是12MHZ *******************************************************************************/
 
#include<reg52.h>
#include<intrins.h>
#define uchar unsigned char
#define uint unsigned int
	

#define jingzhen 12000000UL /*使用12.0M晶体*/	
#define botelv 4800UL /*波特率定义为4800*/
 
uchar PuZh[] = "www.1234567890.com";
 
//--定义使用的IO--//
 /********************************************************************************************************** 端口定义 ************************************************************************************************************/

sbit RS=P2^6; //命令/数据选择 
sbit RW=P2^5;  //读写口 
sbit  E=P2^7;	  //锁存控制 
sbit RES = P2^0; 
sbit PSB = P2^2;

sbit LED1=P2^3;
sbit LED2=P2^4;

//--声明全局函数--// 
void UsartConfiguration();
void Delay_1ms(uint i);
void delay(unsigned int time);
void DELAY_nUS(unsigned int Time);
void DELAY_nMS(unsigned int time);
/******************************************************************** * 名称 : Delay_1ms() * 功能 : 延时子程序,延时时间为 1ms * x * 输入 : x (延时一毫秒的个数) * 输出 : 无 ***********************************************************************/
void Delay_1ms(uint i)//1ms延时
{
  uchar x,j;
  
  for(j=0;j<i;j++)
    for(x=0;x<=148;x++);
}

void DELAY_nMS(unsigned int time)
{
  unsigned int  i,j;             
  for(i=0;i<time;i++)    
     for(j=0;j<939;j++); 
}

void DELAY_nUS(unsigned int Time)
{
	while(--Time);
}


void delay(unsigned int time)              //int型数据为16位,所以最大值为65535 
 {										   //0.1ms
   unsigned int  i,j;                  //定义变量i,j,用于循环语句 
   for(i=0;i<time;i++)         //for循环,循环50*time次
     for(j=0;j<50;j++);       //for循环,循环50次
 }
 
/********************************************************** LCD12864部分 *************************************************************/
 void checkbusy(void)           
{
   RS=0;                   
   RW=1;                  
   E=1;                    
   while((P0&0x80)==0x80); 
   E=0;                   
}

void wcode(unsigned char cmdcode)
{
   checkbusy();           
   RS=0;                   
   RW=0;                   
   E=1;                  
   P0=cmdcode;            
   delay(10);               
   E=0;                    
}
void wdata(unsigned char dispdata)
{
   checkbusy();           
   RS=1;                  
   RW=0;                  
   E=1;                   
   P0=dispdata;           
   delay(10);              
   E=0;                    
}


void LCD12864_InitLCD(void)
   {
		 PSB=1;            
		 RES=0;
		 delay(10);         
		 RES=1;             
		 wcode(0x30);       
		 wcode(0x0c);       
		 wcode(0x01);       
		 wcode(0x06);       	
}  

void LCD12864_Display_Char(unsigned char x,unsigned char y,unsigned char  Char)   
{                                    
  switch(y)                              
     { 
			case 0: wcode(0x80+x);break;    //第1行
	    case 1: wcode(0x90+x);break;    //第2行
	    case 2: wcode(0x88+x);break;    //第3行
	    case 3: wcode(0x98+x);break;    //第4行
      default:break;
	   }
	 wdata(Char);


}

void LCD12864_Display_String(unsigned char x,unsigned char y,unsigned char  *s)   
{                                    
 switch(y)                                     
     { 
			case 0: wcode(0x80+x);break;   
			case 1: wcode(0x90+x);break;    
			case 2: wcode(0x88+x);break;   
			case 3: wcode(0x98+x);break;    
				default:break;
	 }
   while(*s>0)                        
     {  
				wdata(*s);                     
				delay(10);                     
				s++;                         
     }
}

void LCD12864_Display_UnsignedInt(unsigned char x,unsigned char y,unsigned int Number,unsigned char Count)
{
	unsigned char NumbArray[6]={0};    
   
    NumbArray[0]=(Number/10000)%10+0x30; 
    NumbArray[1]=(Number/1000) %10+0x30;
    NumbArray[2]=(Number/100)  %10+0x30;
    NumbArray[3]=(Number/10)   %10+0x30;
    NumbArray[4]=(Number/1)    %10+0x30; 
    NumbArray[5]= 0;
 
    LCD12864_Display_String(x,y,&NumbArray[5-Count]);
}


void Display_SignedInt(unsigned char x,
                       unsigned char y,
                       signed int Number,
                       unsigned char Count)
{
    unsigned char NumberArray[7]={0};    
    signed int Number_Temp;
    
    Number_Temp = Number;
    if(Number_Temp<0)
    {
        Number_Temp = 0 - Number_Temp;
    }

    NumberArray[0]='+'; 
    NumberArray[1]=(Number_Temp/10000)%10+0x30; 
    NumberArray[2]=(Number_Temp/1000) %10+0x30;
    NumberArray[3]=(Number_Temp/100)  %10+0x30;
    NumberArray[4]=(Number_Temp/10)   %10+0x30;
    NumberArray[5]=(Number_Temp/1)    %10+0x30;
    NumberArray[6]=0;
    
    if(Number>0)
    {
        NumberArray[5-Count] = '+';
    }
    else
    {
        NumberArray[5-Count] = '-';
    }
    
    LCD12864_Display_String(x, y, &NumberArray[5-Count]);
}


 
/******************************************************************** * 名称 : Com_Int() * 功能 : 串口中断子函数 * 输入 : 无 * 输出 : 无 ***********************************************************************/
void Com_Int(void) interrupt 4
{
	uchar i;
  uchar receive_data;
	
  EA = 0;
	
  if(RI == 1) //当硬件接收到一个数据时,RI会置位
	{ 		
			RI = 0;
			receive_data = SBUF;//接收到的数据
			
		if(receive_data == '1')	 
		{
				LED1=0;
			  DELAY_nMS(200);
			  LED1=1;
			  DELAY_nMS(200);
			  LCD12864_Display_String(0,1,"led1");
			  DELAY_nMS(500);
			  wcode(0x01);
				LED2=1;
			  DELAY_nMS(200);
			  LED2=0;
			  DELAY_nMS(200);
			  LCD12864_Display_String(0,2,"led2");
			  DELAY_nMS(500);
			  wcode(0x01);
		}
		else if(receive_data == '2')	 
		{
			  LED2=1;
			  DELAY_nMS(200);
			  LED2=0;
			  DELAY_nMS(200);
			  LCD12864_Display_String(8,2,"led2");
			  DELAY_nMS(500);
			  wcode(0x01);
				LED1=0;
			  DELAY_nMS(200);
			  LED1=1;
			  DELAY_nMS(200);
			  LCD12864_Display_String(8,1,"led1");
			  DELAY_nMS(500);
			  wcode(0x01);
			  
				
		}
		else 
		{
			 LCD12864_Display_String(0,3,"error");
			 DELAY_nMS(500);
			 wcode(0x01);
		}
		
	}
		
		for(i=0; i<36; i++)
		{
			SBUF = PuZh[i];   //将要发送的数据放入到发送寄存器
			while(!TI);		    //等待发送数据完成
			TI=0;			        //清除发送完成标志位
			Delay_1ms(1);
		}
		EA = 1;
}

/******************************************************************************* * 函 数 名 : UsartConfiguration() * 函数功能 : 设置串口 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/


void  UsartConfiguration(void)				//串口初始化
{
		 EA=0;            //暂时关闭中断
		 TMOD&=0x0F;      //定时器1模式控制在高4位
		 TMOD|=0x20;      //定时器1工作在模式2,自动重装模式
		 SCON=0x50;       //串口工作在模式1
		 TH1=256-jingzhen/(botelv*12*16);  //计算定时器重装值
		 TL1=256-jingzhen/(botelv*12*16);
		 PCON|=0x80;    //串口波特率加倍
		 ES=1;         //串行中断允许
		 TR1=1;        //启动定时器1
		 REN=1;        //允许接收 
		 EA=1;         //允许中断
}
 
/******************************************************************************* * 函 数 名 : main * 函数功能 : 主函数 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ 
void main()
{
 
	UsartConfiguration();//初始化串口
	LCD12864_InitLCD();
	//LCD12864_Display_String(0,1,"test");
	while(1)
	{
		//LCD12864_Display_String(0,0,"test");
	}
}

参考资料

普中科技–普中单片机开发资料
蒙蒙Plus–一周学习完51单片机
海创电子–51单片机学习
博客地址:https://lemonhubs.github.io/