嵌入式开发学习(STC51-11-中断系统)

news2024/12/24 21:44:22

内容

外部中断-使用独立按键K3控制LED亮灭;

定时器(中断)-通过定时器0中断控制D1指示灯间隔1秒闪烁;

串口通信(中断)-通过串口(UART)实现与PC机对话,51单片机的串口收到PC机发来的数据后原封不动的返回给PC机显示;

中断系统介绍

中断简介

中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力;它也是单片机最重要的功能之一,是我们学习单片机必须要掌握的;

举一个生活事例:你打开火,烧上一壶水,然后去洗衣服;在洗衣服的过程中,突然听到水壶发出水开的报警声,这时,你停止洗衣服动作,立即去关掉火,然后将开水灌入暖水瓶中;灌完开水后,你又回去继续洗衣服;这个过程中实际上就发生了一次中断;

对于单片机来讲,中断是指CPU在处理某一事件A时,发生了另一事件B,请求CPU迅速去处理(中断发生);CPU暂时停止当前的工作(中断响应),转去处理事件B(中断服务);待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断;

引起CPU中断的根源称为中断源;中断源向CPU提出中断请求,CPU暂时中断原来的事务A,转去处理事件B,对事件B处理完毕后,再回到原来被中断的地方(即断点),称为中断返回;实现上述中断功能的部件称为中断系统(中断机构);

中断的开启与关闭、设置启用哪一个中断等都是由单片机内部的一些特殊功能寄存器来决定的,操作单片机的IO口即操作IO口寄存器,只不过编译器已经帮我们把IO口寄存器封装好,直接操作IO即可;

中断技术不仅可以解决快速主机与慢速I/O设备的数据传送问题,而且还具有如下优点:

  • 分时操作,CPU可以分时为多个I/O设备服务,提高了计算机的利用率;
  • 实时响应,CPU能够及时处理应用系统的随机事件,系统的实时性大大增强;
  • 可靠性高,CPU具有处理设备故障及掉电等突发性事件能力,从而使系统可靠性提高;

中断结构

STC89C5X系列单片机提供了8个中断请求源,它们分别是:外部中断O(INTO)、外部中断1(INT1)、外部中断2(INT2)、外部中断3(INT3)、定时器0中断、定时器1中断、定时器2中断、串口(UART)中断;

(注意:51系列单片机一定有基本的5个中断,但不全有8个中断,需要查看芯片手册,通常我们使用的都是基本的5个中断:INT0、INT1、定时器0/1,串口中断)

以51单片机均有的5个中断来介绍,其内部结构框图如下所示:
在这里插入图片描述
外部中断0/1对应p32/p33口,定时中断0/1对应p34/p35口,串行中断接收/发送即RX/TX对应p30/p31口

  • 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申请中断;
  • RX和TX对应的是P3.0和P3.1口的附加功能,RI(SCON.0)或TI(SCON.1),串行口中断请求标志;当串行口接收完一帧串行数据时置位RI或当串行口发送完一帧串行数据时置位TI,向CPU申请中断;

所有的中断都具有四个中断优先级(基本型只有两个)

用户可以用关总中断允许位(EA/IE.7)或相应中断的允许位来屏蔽所有的中断请求,也可以用打开相应的中断允许位来使CPU响应相应的中断申请;

其中有些中断源可以用软件独立地控制为开中断或关中断状态;

每一个中断的优先级别均可用软件设置,高优先级的中断请求可以打断低优先级的中断,反之,低优先级的中断请求不可以打断高优先级及同优先级的中断;当两个相同优先级的中断同时产生时,将由查询次序来决定系统先响应哪个中断;

STC89C5X系列单片机的各个中断查询次序表如下图所示:
在这里插入图片描述
通过设置新增加的特殊功能寄存器IPH中的相应位,可将中断优先级设为四级,如果只设置IP或XICON,那么中断优先级就只有两级,与传统8051单片机两级中断优先级完全兼容;

上图中的中断查询次序即为中断号,这个中断号在编程时非常重要,当中断来临时,只有中断号正确才能进入中断;

中断寄存器

中断允许控制位IE

CPU对中断系统所有中断以及某个中断源的开放和屏蔽是由中断允许寄存器IE控制的;

76543210
字节地址:A8HEAESET1EX1ET0EX0IE

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

中断请求标志TCON

76543210
字节地址:88HTF1TR1TF0TR0IE1IT1IE0IT0TCON

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溢出中断请求标志位;

中断优先级

同一优先级中的中断申请不止一个时,则有中断优先权排队问题;

同一优先级的中断优先权排队,由中断系统硬件确定的自然优先级形成,其排列如所示:

中断源中断标志中断服务程序入口优先级顺序
外部中断0(INTO)IE00003H1
定时/计数器0(TO)TF0000BH2
外部中断1(INTI)IE10013H3
定时/计数器1(T1)TF1001BH4
串行口RI或TI0023H5

中断号

在这里插入图片描述

中断响应条件

以下三条同时满足时,CPU 才有可能响应中断:

  • 中断源有中断请求;
  • 此中断源的中断允许位为1;
  • CPU开中断(即EA=1);

中断的使用

1 确定好要使用的中断类型,选择相应的中断号;
2 选择好要触发的类型;
3 写好中断后要做的操作;

以外部中断0为例

首先要有中断配置程序:(1、2)

EA = 1;		 // 打开总中断开关
EX0 = 1;	 // 开外部中断0
IT0 = 0 / 1; // 设置外部中断的触发方式

中断服务函数:(3)

void int0() interrupt 0 using 1 // using 1可忽略
{
	// 编写用户所需的功能代码
}

外部中断

在上面的中断系统中已经将外部中断描述的比较清楚,这里不再重复描述;

内容

使用独立按键K3控制LED亮灭

原理图

在这里插入图片描述

在这里插入图片描述
k3按键连接p32口,而led1连接p20口

思路

当k3按钮按下时,对应的p32口电位由高变低,会产生下降沿,因此我们可以选择,使用外部中断0,选择下降沿触发方式;

在中断函数里消抖,即确保检测k3按键是否按下(为低电位),然后变化led1的电位;

(注意:外部中断0/1对应p32/p33口,定时中断0/1对应p34/p35口,串行中断接收/发送即RI/TI-对应p30/p31口)

编码

main.c

/*
 * @Description: 按下K3键控制D1指示灯亮灭
 */
#include "reg52.h"

typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;

// 定义LED1管脚
sbit LED1 = P2 ^ 0;

// 定义独立按键K3控制脚
sbit KEY3 = P3 ^ 2;

/**
 * @description: 延时函数(循环一次大约10us)
 * @param {u16} ten_us
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**
 * @description: 延时函数(循环一次大约1ms)
 * @param {u16} ms
 * @return {*}
 */
void delay_ms(u16 ms)
{
	u16 i, j;
	for (i = ms; i > 0; i--)
		for (j = 110; j > 0; j--)
			;
}

/**
 * @description: 外部中断0配置函数
 * @return {*}
 */
void exti0_init(void)
{
	IT0 = 1; // 跳变沿触发方式(下降沿)
	EX0 = 1; // 打开INT0的中断允许
	EA = 1;	 // 打开总中断
}

/**
 * @description: 外部中断0中断函数
 * @return {*}
 */
void exti0() interrupt 0 // 中断号必须对应上
{
	delay_10us(1000); // 消抖
	if (KEY3 == 0)	  // 再次判断K3键是否按下
		LED1 = !LED1; // LED1状态翻转
}

void main()
{
	exti0_init(); // 外部中断0配置

	while (1)
	{
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:按下k3键后能控制指示灯d1亮灭

定时器(中断)

内容

通过定时器0中断控制D1指示灯间隔1秒闪烁

定时器介绍

CPU时序的有关知识

  • 振荡周期:为单片机提供定时信号的振荡源的周期(晶振周期或外加振荡周期);
  • 状态周期:2个振荡周期为1个状态周期,用S表示,振荡周期又称S周期或时钟周期;
  • 机器周期:1个机器周期含6个状态周期,12个振荡周期;
  • 指令周期:完成1条指令所占用的全部时间,它以机器周期为单位;
    例如:外接晶振为 12MHz 时,51 单片机相关周期的具体值为:
    振荡周期=1/12us;
    状态周期=1/6us;
    机器周期=1us;
    指令周期=1~4us;

51单片机的定时器

  • 51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器;
  • 定时器/计数器和单片机的CPU是相互独立的,定时器/计数器工作的过程是自动完成的,不需要CPU的参与;
  • 51单片机中的定时器/计数器是根据机器内部的时钟或者是外部的脉冲信号对寄存器中的数据加1;
  • 有了定时器/计数器之后,可以增加单片机的效率,一些简单的重复加1的工作可以交给定时器/计数器处理,CPU转而处理一些复杂的事情,同时可以实现精确定时作用;

定时器原理

STC89C5X单片机内有两个可编程的定时/计数器T0、T1和一个特殊功能定时器T2;

定时/计数器的实质是加1计数器(16位),由高8位和低8位两个寄存器THx和TLx组成;

它随着计数器的输入脉冲进行自加1,也就是每来一个脉冲,计数器就自动加1,当加到计数器为全1时,再输入一个脉冲就使计数器回零,且计数器的溢出使相应的中断标志位置1,向CPU 发出中断请求(定时/计数器中断允许时);如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满;

溢出时计数器的值减去计数初值就是加1计数器的计数值;

定时器结构

定时器0/1(T0、T1)对应的是单片机P3.4和P3.5管脚;

51单片机定时/计数器的工作由两个特殊功能寄存器控制(工作方式寄存器、控制寄存器);

TMOD是定时/计数器的工作方式寄存器,确定工作方式和功能;TCON是控制寄存器,控制定时器T0、T1的启动和停止及设置溢出标志;

工作方式寄存器TMOD

工作方式寄存器TMOD用于设置定时/计数器的工作方式,低四位用于T0,高四位用于T1

其格式如下:

76543210
字节地址:89HGATEC/TM1M0GATEC/TM1M0TMOD
  • GATE,门控位,用于控制定时器的启动是否受外部中断源信号的影响;GATE=0时,只要用软件使TCON中的TR0或TR1为1,就可以启动定时/计数器工作;GATA=1时,要用软件使TR0或TR1为1,同时外部中断引脚INT0/1也为高电平时,才能启动定时/计数器工作;即此时定时器的启动条件,加上了INT0/1引脚为高电平这一条件;
  • C/T,定时/计数模式选择位;C/T=0为定时模式;C/T=1为计数模式;
  • M1、M0,工作方式设置位;定时/计数器有四种工作方式;
M1M0工作方式说明
00方式013位定时/计数器
01方式116位定时/计数器
10方式28位自动重装定时/计数器
11方式3T0分成两个独立的8位定时/计数器;T1此方式下停止计数;

控制寄存器TCON

TCON的低4位用于控制外部中断,TCON的高4位用于控制定时/计数器的启动和中断申请;

其格式如下:

76543210
字节地址:88HTF1TR1TF0TR0TCON
  • 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类同;

定时器的工作方式

方式0

方式0为13位计数,由TL0的低5位(高3位未用)和TH0的8位组成;

TL0的低5位溢出时向TH0进位,TH0溢出时,置位TCON中的TF0标志,向CPU发出中断请求;

其结构图如下所示:
在这里插入图片描述
门控位GATE具有特殊的作用;当GATE=0时,经反相后使或门输出为1,此时仅由TR0控制与门的开启;当GATE=1时,由外中断引脚信号控制或门的输出,此时控制与门的开启由外中断引脚信号和TR0共同控制;

计数模式时,计数脉冲是T0引脚上的外部脉冲;

计数初值与计数个数的关系为:X=2^13-N

方式1

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

其结构图如下所示:
在这里插入图片描述
计数初值与计数个数的关系为:X=2^16-N

方式2

方式2为自动重装初值的8位计数方式;

工作方式2特别适合于用作比较精确的脉冲信号发生器;

其结构图如下所示:
在这里插入图片描述
计数初值与计数个数的关系为:X=2^8-N

方式3

方式3只适用于定时/计数器T0,定时器T1处于方式3时相当于TR1=0,停止计数;

工作方式3将T0分成为两个独立的8位计数器TL0和TH0;

其结构如下所示:
在这里插入图片描述
这几种工作方式中应用较多的是方式1和方式2;

定时器中通常使用定时器方式1,串口通信中通常使用方式2;

计算定时器初值

首先我们知道机器周期的概念,它是CPU完成一个基本操作所需要的时间,计算公式是:机器周期=1/单片机的时钟频率;

51单片机内部时钟频率是外部时钟的12分频,也就是说当外部晶振的频率输入到单片机里面的时候要进行12分频;比如说你用的是12MHZ晶振,那么单片机内部的时钟频率就是12/12MHZ,当你使用12MHZ的外部晶振的时候,机器周期=1/1M=1us;

比如我们想定时1ms的初值,1ms/1us=1000,也就是要计数1000个,初值=65535-1000+1=64536=FC18H(因为实际上计数器计数到65536=2 * 16才溢出,所以后面要加1),所以初值即为 THx=0XFC,TLx=0X18;

由于定时计数器位数有限,我们不能直接通过初值定时很长时间,所以如果要实现很长时间的定时,比如定时1秒钟,可以通过初值设置定时1ms,每当定时1ms结束后又重新赋初值,并且设定一个全局变量累计定时1ms的次数,当累计到1000次,就表示已经定时1秒了;

定时器的配置

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

例如使用定时器0,选择定时器工作方式1、设定1ms初值,开启定时器计数功能以及总中断:

void time0_init(void)
{
	TMOD |= 0X01; // 选择为定时器0模式,工作方式1
	TH0 = 0XFC;	  // 给定时器赋初值,定时1ms
	TL0 = 0X18;
	ET0 = 1; // 打开定时器0中断允许
	EA = 1;	 // 打开总中断
	TR0 = 1; // 打开定时器
}

(TMOD高四位用于T0,低四位用于T1)

思路

选择定时器0,配置为1ms,循环1000次(通过变量累计值达到1000)来达到计时1s的效果;

变量累计值达到1000时改变led1电位,同时变量清0,重新开始计时;

编码

main.c

/*
 * @Description: 通过定时器0中断控制D1指示灯间隔1秒闪烁
 */
#include "reg52.h"

typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;

// 定义LED1管脚
sbit LED1 = P2 ^ 0;

/**
 * @description: 延时函数(循环一次大约10us)
 * @param {u16} ten_us
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**
 * @description: 延时函数(循环一次大约1ms)
 * @param {u16} ms
 * @return {*}
 */
void delay_ms(u16 ms)
{
	u16 i, j;
	for (i = ms; i > 0; i--)
		for (j = 110; j > 0; j--)
			;
}

/**
 * @description: 定时器0中断配置函数,通过设置TH和TL即可确定定时时间
 * @return {*}
 */
void time0_init(void)
{
	TMOD |= 0X01; // 选择为定时器0模式,工作方式1
	TH0 = 0XFC;	  // 给定时器赋初值,定时1ms
	TL0 = 0X18;
	ET0 = 1; // 打开定时器0中断允许
	EA = 1;	 // 打开总中断
	TR0 = 1; // 打开定时器
}

/**
 * @description: 定时器0中断函数
 * @return {*}
 */
void time0() interrupt 1 // 中断号必须对应上
{
	static u16 i; // 定义静态变量i
	TH0 = 0XFC;	  // 给定时器赋初值,定时1ms
	TL0 = 0X18;
	i++;
	if (i == 1000)
	{
		i = 0;
		LED1 = !LED1;
	}
}

void main()
{
	time0_init(); // 定时器0中断配置

	while (1)
	{
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

结果:指示灯D1间隔1秒闪烁

串口通信(中断)

内容

通过串口(UART)实现与PC机对话,51单片机的串口收到PC机发来的数据后原封不动的返回给PC机显示;

串口通信介绍

通信的基本概念

通信的方式可以分为多种,按照数据传送方式可分为串行通信和并行通信;

按照通信的数据同步方式,可分为异同通信和同步通信;

按照数据的传输方向又可分为单工、半双工和全双工通信;

串行通信与并行通信

1 串行通信

串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度;

其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信;

串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂;

2 并行通信
并行通信通常是将数据字节的各位用多条数据线同时进行传送,通常是8位、16位、32位等数据一起传输;

并行通信的特点:控制简单、传输速度快;由于传输线较多,长距离传送时成本高,且接收方的各位同时接收存在困难,抗干扰能力差;

异步通信与同步通信

1 异步通信

异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程,为使双方的收发协调,要求发送和接收设备的时钟尽可能一致;

异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“ 位间隔”的整数倍;

异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3位用于起止位,各帧之间还有间隔,因此传输效率不高;

2 同步通信

同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步;

同步通信时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系;

单工、半双工与全双工通信

1 单工通信
单工是指数据传输仅能沿一个方向,不能实现反向传输;

2 半双工通信
半双工是指数据传输可以沿两个方向,但需要分时进行;

3 全双工通信
全双工是指数据可以同时进行双向传输;

通信速率

衡量通信性能的一个非常重要的参数就是通信速率,通常以比特率(Bitrate)来表示;

比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps);

如每秒钟传送240个字符,而每个字符格式包含10位(1个起始位、1个停止位、8个数据位),这时的比特率为:10位×240 个/秒 = 2400bps

还有一个“波特率”的概念,它表示每秒钟传输了多少个码元;

码元是通信信号调制的概念,通信中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元;

如常见的通信传输中,用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;

如果在通信传输中,有0V、2V、4V以及6V分别表示二进制数00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半;

由于很多常见的通信中一个码元都是表示两种状态,所以我们常常直接以波特率来表示比特率;

串口简介

串口通信(Serial Communication),是指外设和计算机间通过数据信号线、地线等按位进行传输数据的一种通信方式,属于串行通信方式;

串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议;

在串口通信中,通常我们只使用2、3、5三个管脚,即TXD、RXD、SGND,其他管脚功能暂时看不明白也没关系;

串口的输入输出对应单片机IO口,TXD(输出)对应的是P3.1管脚,RXD(输入)对应的是P3.0管脚;

串口相关寄存器

串口控制寄存器SCON

76543210
字节地址:98HSM0SM1SM2RENTB8RB8TIRISCON
  • SM0和SM1为工作方式选择位
SM1SM0工作方式说明波特率
00方式0移位寄存器fsoc/12
01方式110步异位收发器(8位数据)可变
10方式211步异位收发器(8位数据)fsoc/64或fsoc/32
11方式311步异位收发器(8位数据)可变
  • SM2:多机通信控制位,主要用于方式2和方式3;当SM2=1时可以利用收到的RB8来控制是否激活RI(RB8=0时不激活RI,收到的信息丢弃;RB8=1时收到的数据进入SBUF,并激活RI,进而在中断服务中将数据从SBUF读走);当SM2=0时,不论收到的RB8为0和1,均可以使收到的数据进入SBUF,并激活RI(即此时RB8不具有控制RI激活的功能);通过控制SM2,可以实现多机通信;
  • REN:允许串行接收位;由软件置REN=1,则启动串行口接收数据;若软件置REN=0,则禁止接收;
  • TB8:在方式2或方式3中,是发送数据的第9位,可以用软件规定其作用;可以用作数据的奇偶校验位,或在多机通信中,作为地址帧/数据帧的标志位;在方式0和方式1中,该位未用到;
  • RB8:在方式2或方式3中,是接收到数据的第9位,作为奇偶校验位或地址帧/数据帧的标志位;在方式1时,若SM2=0,则RB8是接收到的停止位;
  • TI:发送中断标志位;在方式0时,当串行发送第8位数据结束时,或在其它方式,串行发送停止位的开始时,由内部硬件使TI置1,向CPU发中断申请;在中断服务程序中,必须用软件将其清0,取消此中断申请;
  • RI:接收中断标志位;在方式0时,当串行接收第8位数据结束时,或在其它方式,串行接收停止位的中间时,由内部硬件使RI置1,向CPU发中断申请;也必须在中断服务程序中,用软件将其清0,取消此中断申请;

电源控制寄存器PCON

76543210
字节地址:97HSMODPCON

SMOD:波特率倍增位;在串口方式1、方式2、方式3时,波特率与SMOD有关,当SMOD=1时,波特率提高一倍,复位时,SMOD=0;

串口工作方式

方式0

方式0时,串行口为同步移位寄存器的输入输出方式,主要用于扩展并行输入或输出口;

数据由RXD(P3.0)引脚输入或输出,同步移位脉冲由TXD(P3.1)引脚输出;

发送和接收均为8位数据,低位在先,高位在后;

波特率固定为fosc/12;

对应的输入输出时序图如下所示:
方式0输入
在这里插入图片描述
方式0输出
在这里插入图片描述

方式1

方式1是10位数据的异步通信口;

TXD为数据发送引脚,RXD为数据接收引脚,传送一帧数据的格式如下所示:(其中1位起始位,8位数据位,1位停止位)
在这里插入图片描述

对应的输入输出时序图如下所示:
方式1输入
在这里插入图片描述
方式1输出
在这里插入图片描述
置REN为1时,接收器以所选择波特率的16倍速率采样RXD引脚电平,检测到RXD引脚输入电平发生负跳变时,则说明起始位有效,将其移入输入移位寄存器,并开始接收这一帧信息的其余位;

接收过程中,数据从输入移位寄存器右边移入,起始位移至输入移位寄存器最左边时,控制电路进行最后一次移位;

当RI=0,且SM2=0(或接收到的停止位为1)时,将接收到的9位数据的前8位数据装入接收SBUF,第9位(停止位)进入RB8,并置RI=1,向CPU请求中断;

方式2和方式3

方式2或方式3时为11位数据的异步通信口;

TXD为数据发送引脚,RXD为数据接收引脚;

其数据格式如下所示:
在这里插入图片描述

对应的输入输出时序图如下所示:
方式2、方式3输入
在这里插入图片描述
接收时,数据从右边移入输入移位寄存器,在起始位0移到最左边时,控制电路进行最后一次移位;

当RI=0,且SM2=0(或接收到的第9位数据为1)时,接收到的数据装入接收缓冲器SBUF和RB8(接收数据的第9位),置RI=1,向CPU请求中断;如果条件不满足,则数据丢失,且不置位RI,继续搜索RXD引脚的负跳变;

方式2、方式3输出
在这里插入图片描述
发送开始时,先把起始位0输出到TXD引脚,然后发送移位寄存器的输出位(D0)到TXD引脚;每一个移位脉冲都使输出移位寄存器的各位右移一位,并由TXD引脚输出;第一次移位时,停止位“1”移入输出移位寄存器的第9位上,以后每次移位,左边都移入0;当停止位移至输出位时,左边其余位全为0,检测电路检测到这一条件时,使控制电路进行最后一次移位,并置TI=1,向CPU请求中断;

计算波特率

不同方式下波特率的计算公式:
方式0的波特率 = fosc/12
方式1的波特率 =(2SMOD/32)·(T1溢出率)
方式2的波特率 =(2SMOD/64)·fosc
方式3的波特率 =(2SMOD/32)·(T1溢出率)

其中,T1溢出率 = fosc/{12*[256 -(TH1)]}
(fosc:时钟频率)
建议使用波特率计算软件计算

串口配置

  • 确定T1的工作方式(TMOD寄存器);
  • 确定串口工作方式(SCON寄存器);
  • 计算T1的初值(设定波特率),装载TH1、TL1;
  • 启动T1(TCON中的TR1位);
  • 如果使用中断,需开启串口中断控制位(IE寄存器)。

例如,设置串口为工作方式1、波特率为9600、波特率加倍、使用中断:

void uart_init(u8 baud)
{
	TMOD |= 0X20; // 设置计数器工作方式2
	SCON = 0X50;  // 设置为工作方式1
	PCON = 0X80;  // 波特率加倍
	TH1 = baud;	  // 计数器初始值设置
	TL1 = baud;
	ES = 1;	 // 打开接收中断
	EA = 1;	 // 打开总中断
	TR1 = 1; // 打开计数器
}

在主函数中调用该函数并传入OXFA值即可,如下:

uart_init(0XFA); // 波特率为 9600

原理图

结构图
在这里插入图片描述
线路图
在这里插入图片描述
由结构图可知,串口通过RXD发送数据,数据进入SBUF,然后由TXD接收SBUF的数据;

由线路图可知,开发板通过CH340G芯片把51单片机的串口(RTX-U、TDX-U)与PC机的USB口(D+、D-)进行连接;

同时CH340G芯片并不直接连接单片机的串口,而是通过连接在p5端子上,这样方便通过后续使用串口或CH340G芯片做其他实验;

思路

配置串口中断,根据对应方式的数据格式和时序编写中断函数,要求能够存储(可以使用变量保存)接收到的数据(来自pc机),并发送数据;

编码

main.c

/*
 * @Description: 通过串口助手发送数据给单片机,单片机原封不动转发给串口助手显示
 */
#include "reg52.h"

typedef unsigned int u16; // 对系统默认数据类型进行重定义
typedef unsigned char u8;

/**
 * @description: 延时函数(循环一次大约10us)
 * @param {u16} ten_us
 * @return {*}
 */
void delay_10us(u16 ten_us)
{
	while (ten_us--)
		;
}

/**
 * @description: 延时函数(循环一次大约1ms)
 * @param {u16} ms
 * @return {*}
 */
void delay_ms(u16 ms)
{
	u16 i, j;
	for (i = ms; i > 0; i--)
		for (j = 110; j > 0; j--)
			;
}

/**
 * @description: 串口通信中断配置函数,通过设置TH和TL即可确定定时时间
 * @param {u8} baud // 波特率对应的TH、TL值
 * @return {*}
 */
void uart_init(u8 baud)
{
	TMOD |= 0X20; // 设置计数器工作方式2
	SCON = 0X50;  // 设置为工作方式1
	PCON = 0X80;  // 波特率加倍
	TH1 = baud;	  // 计数器初始值设置
	TL1 = baud;
	ES = 1;	 // 打开接收中断
	EA = 1;	 // 打开总中断
	TR1 = 1; // 打开计数器
}

/**
 * @description: 串口通信中断函数
 * @return {*}
 */
void uart() interrupt 4 // 中断号必须对应上
{
	u8 rec_data; // 定义变量存储接收到的数据

	RI = 0; // 清除接收中断标志位
	rec_data = SBUF;
	SBUF = rec_data; // 将接收到的数据放入到发送寄存器
	while (!TI)
		;	// 等待发送数据完成
	TI = 0; // 清除发送完成标志位
}

void main()
{
	uart_init(0XFA); // 波特率为9600

	while (1)
	{
	}
}

编译和结果

按F7编译,无错误,生成.hex文件,使用pz-isp将hex文件下载到单片机

(注意要使用黄色跳线帽将P5端子的1、2短接,3、4短接)
结果:使用串口调试助手,发送数据给单片机,单片机能把数据发送回来;
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/860835.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode算法递归类—合并两个有序链表

目录 21. 合并两个有序链表 题解: 代码: 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2&a…

logstash 采集 docker 日志

1、nginx容器部署 参考:nginx容器部署 将容器内的nginx日志文件映射到宿主机/home/logs/nginx目录下 注意:并且需要需要将日志的输出格式修改为json 2、编辑vim /opt/logstash-7.4.2/config/nginx-log-es.conf 文件,收集docker nginx容器日…

【时间格式引发的事故】

时间格式引发的事故 背景实战演示结论 背景 前不久写了一个删除数据接口,条件是根据时间删除时间后面的数据。入参是 时间字符串。后台的时间格式 是 yyyyMMdd。然后当时前端传参数的时候,随意的传了2023-07-31的时间,然后将该表的数据全部删…

CCLINK IE转MODBUS-TCP网关modbus tcp协议详解

你是否曾经遇到过需要同时处理CCLINK IE FIELD BASIC和MODBUS两种数据协议的情况?捷米的JM-CCLKIE-TCP网关可以帮助你解决这个问题。 捷米JM-CCLKIE-TCP网关可以分别从CCLINK IE FIELD BASIC一侧和MODBUS一侧读写数据,然后将数据存入各自的缓冲区。接着…

【学习】若依源码(前后端分离版)之 “ 用户管理根据不同角色、部门显示数据范围”

大型纪录片:学习若依源码(前后端分离版)之 “ 用户管理根据不同角色、部门显示数据范围” 前端部分后端部分“ /list " 方法" /treeselect " 方法 结语 起因是我想做一个根据不同角色以及其所在的部门展示其相应的信息&#x…

StarRocks 3.1重磅发布,云原生湖仓新范式再升级!

StarRocks 自4月底发布3.0版本,拥抱云原生,开启极速统一的湖仓新范式;8月7日,StarRocks 正式发布全新3.1版本,全面提升云原生存算分离构架、极速数据湖分析、物化视图等重量级特性,让用户更简单的实现极速统…

前端性能优化之性能优化的指标和工具(chrome devtools、lighthouse、webpagetest)

文章目录 引言一、为什么要进行web性能优化二、RAIL测量模型1. 什么是RAIL2. 性能测量工具 三、性能测量工具的使用和性能指标以及优化目标1. Chrome DevTools1. 打开调试工具方式和配置2. network下的几个性能指标1. requests 请求总数2. transferred实际从服务器下载的数据量…

uni-app:实现点击按钮,进行数据累加展示(解决数据过多,导致出错)

效果 代码 核心代码 一、标签显示 <!-- 加载更多 --> <view class"load_more" v-if"info.length > pageNum * pageSize" tap"loadMore">加载更多 </view> v-if"info.length > pageNum * pageSize"&#xf…

主数据管理案例-某研究所

1、 背景介绍及难点分析 某军工研究所是机电类科研生产一体化研究所&#xff0c;具有多品种、小批量、离散性、央企、军工保密等特点&#xff0c;在数据管理系统和研制管理体系的控制下&#xff0c;设计、工艺、 制造、试验、售后服务等环节都产生了大量的数据。在管理信息化、…

影像维修工程师专项技能培训

最近遇到很多咨询的人员都在对医疗行业产生疑惑&#xff0c;新闻报道说很多医院、公司的领导都被查&#xff0c;这样会不会影响设备维修方面&#xff0c;对后期找工作等有没有影响&#xff1f;总不能学好了技术却没有发挥的余地&#xff1f; 最近确实是国家整体在对医疗方面做…

Win11 VS2022 配置CGAL-5.6

由于项目要用到几何库CGAL&#xff0c;因此做了配置。采用的是官方文档中的“Installing from the Source Archive”方式。 1. 下载安装CGAL &#xff08;1&#xff09;CGAL-5.6.zip下载地址&#xff1a;Releases CGAL/cgal GitHub 下载下图所示的两个文件。 &#xff08…

LeetCode面向运气之Javascript—第27题-移除元素-98.93%

LeetCode第27题-移除元素 题目要求 一个数组nums和一个值val&#xff0c;你需要原地移除所有数值等于val的元素&#xff0c;并返回移除后数组的新长度 举例 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2] 输入&#xff1a;nums [0,1,2,2,3,0,4,2…

【C++】——模板

目录 泛型编程函数模板函数模板的概念函数模板格式&#xff1a;函数模板的原理函数模板的实例化模板参数的匹配原则 类模板类模板定义格式类模板的实例化 泛型编程 泛型编程&#xff1a;编写与类型无关的通用代码&#xff0c;是代码复用的一种手段。模板是泛型编程的基础 引例…

并发——线程池实践

文章目录 1. 使用 ThreadPoolExecutor 的构造函数声明线程池2.监测线程池运行状态3.建议不同类别的业务用不同的线程池4.别忘记给线程池命名5.正确配置线程池参数常规操作美团的骚操作 简单总结一下我了解的使用线程池的时候应该注意的东西&#xff0c;网上似乎还没有专门写这…

带你了解科研院所

一、什么是科研院所 研究院是独立于教育部和高校系统之外的&#xff0c;以科研工作为业务核心的各级、各类研究机构。独立研究院有很多种&#xff0c;其中实力最强、名气最大、分布最广、数量最集中的是直属国务院的中科院、社科院两大科研系统中的各类研究所和研究中心。 两大…

大数据课程I1——Kafka的概述

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Kafka的概念&#xff1b; ⚪ 掌握Kafka的配置与启动&#xff1b; 一、简介 1. 基本概念 Apache kafka 是一个分布式数据流平台。可以从如下几个层面来理解&#x…

第十六章、【Linux】程序管理与SELinux初探

16.1 什么是程序 &#xff08;process&#xff09; 在Linux 系统当中&#xff1a;“触发任何一个事件时&#xff0c;系统都会将他定义成为一个程序&#xff0c;并且给予这个程序一个 ID &#xff0c;称为 PID&#xff0c;同时依据启发这个程序的使用者与相关属性关系&#xff…

Ubuntu18.04使用carla0.9.5联合仿真搭环境报错

Ubuntu18.04使用工程与carla0.9.5联合仿真报错 1 File "/home/cg/Auto_driving/src/ros-bridge/carla_ros_bridge/src/carla_ros_bridge/client.py", line 18, in <module>from carla_ros_bridge.bridge_with_rosbag import CarlaRosBridgeWithBagFile "…

04.利用Redis国逻辑过期实现缓存功能---解决缓存击穿

学习目标&#xff1a; 提示&#xff1a;学习如何利用Redis逻辑过期实现添加缓存功能解决缓存击穿 学习产出&#xff1a; 缓存击穿讲解图&#xff1a; 解决方案&#xff1a; 采用互斥锁采用逻辑过期 1. 准备pom环境 <dependency><groupId>org.springframework…

webpack 创建VUE项目

1、安装 node.js 下载地址&#xff1a;https://nodejs.org/en/ 下载完成以后点击安装&#xff0c;全部下一步即可 安装完成&#xff0c;输入命令验证 node -vnpm -v2.搭建VUE环境 输入命令&#xff0c;全局安装 npm install vue-cli -g安装完成后输入命令 查看 vue --ver…