单片机基础

小学期时单片机资料整理 个人向

Posted by Rigel on September 9, 2020

单片机基础

image-20200903172925408

这些是不同牌子的单片机,性能不一样。 arm最好,手机里都用这个

image-20200903173225744

image-20200903191240991

TTL电平信号规定,+5V等价于逻辑“1”,0V等价于逻辑“0”(采用二进制来表示数据时)。这样的数据通信及电平规定方式,被称做TTL(晶体管-晶体管逻辑电平)信号系统。这是计算机处理器控制的设备内部各部分之间通信的标准技术。

image-20200903191634362

image-20200903192259643

image-20200903192343960

image-20200903192429793

控制总线(ControlBus)简称CB。控制总线主要用来传送控制信号时序信号。控制信号中,有的是微处理器送往存储器输入输出设备接口电路的,比如:读/写信号、片选信号中断响应信号等;也有是其它部件反馈给CPU的,比如:中断申请信号、复位信号、总线请求信号、设备就绪信号等。因此,控制总线的传送方向由具体控制信号而定,一般是双向的,控制总线的位数要根据系统的实际控制需要而定。实际上控制总线的具体情况主要取决于CPU。 [1]

控制总线是连接在一起并完成和实现它们之间的通讯与数据传送的,因此总线的概念是理解PC和主板的组成结构、工作原理及部件之间相互关系统的基础。这些控制信息包括CPU对内存和输入输出接口的读写信号,输入输出接口对CPU提出的中断请求或DMA请求信号,CPU对这些输入输出接口回答与响应信号,输入输出接口的各种工作状态信号以及其他各种功能控制信号。控制总线来往于CPU、内存和输入输出设备之间。

image-20200903194804087

image-20200903195036407

ROM在STC-B单片机是可擦除的! 断电不会消失

​ ROM种类有很多,有可擦除的,也有不可擦除的

image-20200903195600144

这里PSW指一个寄存器

image-20200903195703209

​ 数据类型没有说明unsigned,那么默认是有符号!!

image-20200903195744299

有很多寄存器之类的,都已经在头文件中声明好了,我们直接用就行

image-20200903195905216

右移是否要进行符号补位,是根据数据类型判断的。 编译时,会根据数据类型生成 shr逻辑右移 或 sar算术右移

image-20200903200353890

image-20200903200441463

image-20200903201447045

晶振就像单片机的心脏。数字电路都是按节拍来进行处理的,而晶振就是提供这个节拍的,如果没有晶振,也就没有节拍,那也就不能处理任何数据,CPU主频是多少,其实这个主频就相当于单片机上面的晶振在单片机系统里晶振的作用非常大,结合单片机内部的电路,产生单片机所必须的时钟频率,单片机的一切指令的执行都是建立在这基础上的。晶振的提供时钟频率越高,单片机的运行速度也就越快。

晶振的作用是为系统提供基本的时序信号。通常一个系统共用一个晶振,便于各部分保持同步。有些通讯系统的基频和射频使用不同的晶振,而通过电子调整频率的方法保持同步。

在STC-B板中,晶体1用于给单片机提供时序信号;晶体2提供实时时钟信号!

单片机上的电池通常有两个作用,一是保存RAM中的数据不掉失,但现在很少使用了;二是为时钟芯片提供电源,保证关机状态下时间正常运行,即“某些特殊芯片的电源”

keil在新建工程时,必须要选一种单片机型号,否则不能建工程的。 会根据芯片类型切换不同的编译器/汇编器/连接器(51或MDK工具链); 会根据芯片类型切换不同的仿真资源配置。

开发环境会根据选择的芯片类型(51还是ARM)选择对应的编译器,并根据芯片类型配置整个项目的初始设置,其中有些设置(例如编译选项、芯片衍生库的包含关系、仿真所用的动态链接库等等)是关键性的,直接影响到编译、连接的成功性与正确性。

编译器的工作只是负责给你的代码生成烧录用的可执行文件,板型文件是告诉KEIL你建立工程项目的芯片情况,在软模拟的时候会用到。 写的是嵌入式编程,访问的是片内资源,KEIL怎么知道你用的是什么型号的芯片,这个芯片上都有哪些片上资源?就是通过这个版型文件来确定的呢。

汇编是跟随硬件的,不同体系的硬件用不同的汇编语言,如 AT&T汇编,Intel汇编,ARM汇编。 所以KEIL也需要根据单片机选择对应的编译器,编译成对应的机器码!

不同公司的单片机都有不同的汇编,并且不同型号的单片机也是不同的汇编。不过同公司的单片机的汇编一般都是大同小异。比如Microchip公司的PIC单片机的PIC18系列就只是在PIC16系列上多加几十条指令而已。

STC-B也是STC公司的,和C51一家公司,所以可以直接选C51

单片机的C语言(是类c,不完全是c!)中,有一个不伦不类的东西

在定义IO接口引脚的时候,常会用到

//第一个LED灯的引脚是P1口的0脚 sbit LED1=P1^0;

在C语言中:^符号, 可以表示乘方 可以表示按位异或

唯独在单片机这里,重新定义了这个符号的功能

P1的定义在Reg52.h里面能看到,但是^符号未定义,应该是keil的编译器自动识别,认为是引脚标识符号

在C51(注意,这个是一个编程语言,而不是指MSC51单片机)语言中,增加了bit,sbit两种变量,而对sbit变量的声明、定义、赋值,就会用到”^”符号,此时不是作为“异或”来解释了。

而异或时,也是^ 只有对sbit变量的声明、定义、赋值时,才会把^看作引脚说明。

image-20200903224920328

单片机不是linux,没有loader啥的,main不需要返回值来给exit()函数提供参数,而是直接运行main函数,所以main函数不需要返回值!

要注意,51单片机,各引脚初始值默认为1! 要变化的话,需要在程序中设置引脚值!

(1)发光二极管电路工作原理

如图2(只关注led灯部分电路),P0口的8位输出分别连接了8个发光二极管L0~L7的阳极, P2.3经过一个反相器连接到8个发光二极管L0~L7的阴极(共阴极)。根据二极管的单向导通性(当阳极为高(对应P0口位为1)、阴极为低时,二极管导通,否则不导通),若P2.3输出信号为低电平“0”,则二极管的阴极都为高电平,此时无论P0输出的是“1”还是“0”,二极管都不会导通,也就不会发光。因此想要发光二极管导通,必须先设置P2.3输出信号为“1”,再通过设置P0,点亮想要点亮的发光二极管。

(2) P0口设置与工作原理

STC系列芯片有5组8位输入口,分别为P0到P5,其中P5口仅P5.0~P5.5用于输入输出。STC芯片的所有I/O口都可以配置为四种工作模式之一:准双向口/弱上拉、推挽/强上拉、输入/高阻和开漏模式。STC15系列单片机上电复位后为准双向口/弱上拉工作模式。

每个I/O口的工作模式由2个控制寄存器中的相应位控制(PnM0和PnM1,n=0、1、2、3、4、5)。也就是说P0口的具体工作模式由P0M0和P0M1控制。具体赋值方法参照表1。

表1 I/O口工作模式设置

img其他I/O口工作模式设置类似。

四种工作模式的说明:

(1)准双向口。真正的双向口指的是具有输入和输出两种模式的端口,在不同模式之间需要进行转换;如果从输入改为输出,需要对某些控制寄存器进行设定,才能完成。而51系列单片机的I/O口线在输入和输出之间没有明确的模式区别。相应端口在同样模式下,既可以作为输入,又可以作为输出。P3口除外,因为它需要连接外设。51单片机的I/O口如果要读必须先写1才可以,因此称为“准”双向口。需要大电流高电平输出能力的场合和高速场合不能使用该模式。

(2)推挽电路输入输出(push-pull)模式。推挽电路的输出端好像有两个“臂”(两组放大元件),一个“臂”的电流增加时,另一个“臂”的电流则减小,二者的状态轮流转换。对负载而言,好象是一个“臂”在推,一个“臂”在拉,共同完成电流输出任务。该电路模式的主要作用是增强驱动能力,为外部设备提供大电流,可以直接输出高电平电压。

(3)输入/高阻模式。仅用于输入。

(4)开漏电路。I/O口的开漏就是没有连接上拉电阻

注意:需要将P0的8个引脚和P2.3都设置为推挽输出,led灯才可以点亮。其中的细节原理初学者可先不必深究。

如何设置delay时间

image-20200904144458669

时钟周期:又叫时钟振荡周期,指晶振振荡产生的脉冲,一个脉冲表示一个时钟周期。

状态周期就像之前学过的,一个周期有正脉冲,负脉冲,所以一个状态周期是2个振荡周期

机器周期就是CPU完成一个独立操作的时间。

指令周期就是执行完一条指令需要的时间,单位是机器周期。

最简单的方法:STC-ISP软件内有“软件延时计算器”,能自动生成目标时间的delay函数。

流水灯代码解析

#include<STC15F2K60S2.H>
#define uchar unsigned char
#define uint unsigned int
sbit led_sel=P2^3;//P2^3----E3   对引脚的声明,大小为一位!  用"^"来声明引脚是C51特定的,指明IO口的引脚位置
uchar led;      //一定要注意,因为P0是8个引脚,所以就8位,所以要定义成char!  不要定义成int,很容易因为移位等操作出错

void Init(){

	P0M1=0x00;
	P0M0=0xff;     //P0口的控制寄存器

	P2M1=0x00;
	P2M0=0x08;

	led_sel=1;	   //看电路图,P23有非门,置1才使二极管阴极为0
}

//STC-ISP“软件延时计算器”生成
void delay_ms(uint n)		
{
	while(n){
	uchar i, j;
	i = 11;
	j = 190;
	do
	{
		while (--j);
	} while (--i);
	n--;
}
}

void Delay200ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	//_nop_();
	//_nop_();
	i = 9;
	j = 104;
	k = 139;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void main()
{
	Init();
	led=0x01;
	while(1)                          //让程序一直跑,死循环不退出
	{
		P0=led;                     //对端口赋值,设置引脚电平
		//delay_ms(200);
		Delay200ms(); //延时200ms	
		if(led==0x80)                  //也可以用_crol_函数去循环左移,就不用判断0x80的情况了  汇编为rol指令   rotate left
			led=0x01;
		else
			led=led<<1;   //每次就亮一盏灯。所以用移位!
	}
	
}

对应的C51汇编代码:

$include(STC15F2K60S2.H)  

ORG   0000H  //伪指令
        LJMP   START  

;---------------------------------------主程序----------------------------
ORG   0100H          ;此指令用在原程序或数据块的开始,指明此语句后面目标程序或数据块存放的起始地址
//初始化,设置推挽输出
START:  MOV    P0M1, #00000000B      
        MOV    P0M0, #11111111B 
        MOV    P2M1, #00000000B
	    MOV    P2M0, #00001000B
		MOV    P2,   #00001000B
		
MAINLOOP:
        MOV    P0,   #00000001B
		CALL   DELAY  
        MOV    P0,   #00000010B
		CALL   DELAY
		MOV    P0,   #00000100B
		CALL   DELAY
		MOV    P0,   #00001000B
		CALL   DELAY
		MOV    P0,   #00010000B
		CALL   DELAY
		MOV    P0,   #00100000B
		CALL   DELAY
		MOV    P0,   #01000000B
		CALL   DELAY
		MOV    P0,   #10000000B
		CALL   DELAY
        JMP    MAINLOOP  

DELAY:  MOV    R0,   #0fFH          ;R0-R7是寄存器
L1:     MOV    R1,   #04FH
L2:		MOV    R2,   #20H
L3:     NOP
        DJNZ   R2,   L3      ;DJNZ RN,REL 是一条件转移指令,先将工作寄存器Rn中的数减“1”,判断结果是否为“0”,不为“0”程序就跳转到行标为REL的地方执行,否则,为“0”就不转移,继续执行下一条指令。
		DJNZ   R1,   L2
		DJNZ   R0,   L1
		RET        

        END          

IO口P0在头文件中被定义成sfr特殊功能寄存器,对应0x80地址。 因为导入了头文件,所以在汇编也能直接用P0

​ 单片机程序也有堆栈!!

sbit和sfr

1首先区分bit 和sbit

bit和int char之类的差不多,只不过char=8位, bit=1位而已。都是变量,编译器在编译过程中分配地址。除非你指定,否则这个地址是随机的。这个地址是整个可寻址空间,RAM+FLASH+扩展空间。bit只有0和1两种值,意义有点像Windows下VC中的BOOL。

sbit是对应可位寻址空间的一个位,可位寻址区:20H~2FH。一旦用了sbi xxx = REGE^6这样的定义,这个sbit量就确定地址了。sbit大部分是用在寄存器中的,方便对寄存器的某位进行操作的。

sbit的用法有三种:

  第一种方法:sbit 位变量名=地址值

  第二种方法:sbit 位变量名=SFR名称^变量位地址值

  第三种方法:sbit 位变量名=SFR地址值^变量位地址值

如定义PSW中的OV可以用以下三种方法:

sbit OV=0xd2 (1)说明:0xd2是OV的位地址值

sbit OV=PSW^2 (2)说明:其中PSW必须先用sfr定义好

sbit OV=0xD0^2 (3)说明:0xD0就是PSW的地址值

  因此这里用sfr P1_0=P1^0;就是定义用符号P1_0来表示P1.0引脚,如果你愿意也可以起P10一类的名字,只要下面程序中也随之更改就行了。

**注意:”^”就是特定的引脚声明用法,表示该引脚在IO口的位置,大小为1位。 因为sfr是一个字节,如果是异或的话,地址就变成了其他sfr寄存器的地址,并且大小是一字节!! 而引脚的大小是sbit,一个位! 所以”^”来声明引脚是C51特定的规则! **

Any symbolic NAME can be used in an sbit declaration. The expression to the right of the equal sign (‘=’) specifies an absolute bit address for the symbolic NAME.

sbit变量就是确定好地址的位变量,即对其进行更改,就是直接对对应地址进行更改!! 所以sbit led_sel=P2^3这样 led_sel 变量就直接代表P2端口的第三引脚这个位置! 所以对 led_sel 变量赋值就是直接对该引脚赋值!!

2.Sfr用法

sfr 似乎不是标准C 语言的关键字,而是Keil 为能直接访问80C51 中的SFR 而提供了一个新的关键词,其用法是:sfrt 变量名=地址值

例:sfr P1 = 0x90;

这样的一行即定义P1 与地址0x90 对应,P1 口的地址就是0x90.

SFR的定义在头文件reg51.h或reg52.h中。

​ The address specification after the equal sign (‘=’) must be a numeric constant. Expressions with operators are not allowed. Classic 8051 devicesSUPPORT the SFR address range 0x80 to 0xFF. The PHILIPS 80C51MX provides an additional extended SFR space with the address range 0x180 to 0x1FF.

​ sfr variables may not be declared inside a function. They must be declared outside of the function body.

img

因为C51单片机是8位的,所以一个寄存器占一字节!! 那么说明^就是特定的IO口引脚声明方法!! 不然如果是异或的话,地址就到了别的寄存器了!!

image-20200904211038155

image-20200904210932210

自检模块,异常处理等一直存在rom中,和电脑类似。

分清CPU寄存器和外设寄存器。CPU寄存器存储的是CPU的状态信息,而外设寄存器存储的是外设资源的状态信息。按理说,外设寄存器才被分配地址进行访问,而CPU寄存器直接访问,不能用地址访问。51出来得很早,寄存器设置得并不是很合理,它对于CPU寄存器和外设寄存器的分配并不是很清晰,所以导致有些其实是CPU寄存器的,但是被分配一个地址空间。 另外,纠结于这种事情着实没有必要。它们那么做,并不代表它们是科学的,而是跟芯片设计工程师的思维方法有关。一样新东西出来的时候,谁都对它不熟悉,包括设计工程师本身,所以一些设计看起来会很奇怪,令人费解。比如51的高地址空间即是特殊寄存器的,又是RAM的,访问的时候并不冲突,芯片内部会解决这种冲突,但是这会让有些使用者疑惑,比如楼主就是其中之一。后来MCU技术越来越成熟,于是现在的MCU在这些方面做得很好。这很像软件设计技术,软件技术刚出现的时候,很多人编写出来的代码用现代人看都是一团糟。 等楼主真的明白MCU简单来说其实就是由一系列的逻辑门组成的一个时序电路,在时钟的驱动下用于取出、翻译并执行指令(0和1)时,你就不再会纠结于这些东西了。

​ 所以io口有寄存器,属于外部寄存器,在iram中!! P0,P1等,里面的内容对应着管脚的输出。

ACC、PSW、PC、特殊功能寄存器是在CPU中还是RAM中??

51单片机的地址0~127是内部RAM,地址128~255是特殊功能寄存器的地址,可以直接寻址。

当单片机够大时,内存也被放置在了片上,这个时候有片内RAM;当单片机不够大或有空间有其他用途时,内存没有集成到片上,这个时候就有片外RAM的说法了。 所以系统的RAM等于片内RAM与片外RAM之和。

​ 0-127是片内RAM,128-255是特殊功能寄存器,属于外部寄存器,不在CPU内部,所以要分配地址访问!! 就是需要直接寻址访问的寄存器。 51单片机年代久远,架构快被淘汰了,寄存器和ram概念混淆。

51 单片机的 RAM 分为两个部分,一块是片内 RAM,一块是片外 RAM。标准 51 的片内 RAM 地址从 0x00H~0x7F 共 128 个字节,而现在我们用的 51 系列的单片机都是带扩展片内 RAM 的,即 RAM 是从 0x00~0xFF 共 256 个字节。片外 RAM 最大可以扩展到 0x0000~0xFFFF 共 64K 字节。这里有一点大家要明白,片内 RAM 和片外 RAM 的地址不是连起来的,片内是从 0x00 开始,片外也是从 0x0000 开始的。还有一点,片内和片外这两个名词来自于早期的 51 单片机,分别指在芯片内部和芯片外部,但现在几乎所有的 51 单片机芯片内部都是集成了片外 RAM 的,而真正的芯片外扩展则很少用到了,虽然它还叫片外 RAM,但实际上它现在也是在单片机芯片内部的

关于51单片机内存问题,一直是个疑惑大家的问题,因为51单片机是个很另类的单片机。 下面我给楼主讲解一下: 51单片机之所以另类,是因为,他寻址内存的空间,不是靠总线,是用指令的方式。 51单片机有以下几个内存模块组成: ROM或者Flash就不说了。 RAM有——内部RAM的低128位(00-7F) ——-内部RAM的高128位(80-FF)—【89C51单片机没有这一段空间】 ——-特殊功能寄存器(SFR)(80-FF) 分配地址,要直接寻址访问! ——外部RAM 64K(0000-FFFF) 楼主又疑惑了,好多地址是重复的,比如,我向80H地址写一个数值,单片机怎么知道读的是内部的高128位RAM?还是SFR?还是外部64K的RAM呢? 答案是用指令,如果是直接寻址,那么访问的就是SFR,如果是R0或者R1间接寻址,就是内部高128位RAM,如果是DPTR或者是R0,R1间接寻址,且配合的是MovX指令,那么就是访问外部64KRAM中的第80H个地址。

RAM是RAM,特殊功能寄存器 是 特殊功能寄存器,怎么混在一块来说? 特殊功能寄存器,是独立存在的,只能用直接寻址对它操作。 8051单片机片内的地址分配: 0~127:RAM; --可以直接寻址,也可以间接寻址 128~255:有21个特殊功能寄存器。 --只可以直接寻址 8052单片机片内的地址分配: 0~127:RAM; --可以直接寻址,也可以间接寻址 128~255:RAM; --只可以间接寻址 128~255:有21个特殊功能寄存器。 --只可以直接寻址

image-20200904221844627

image-20200904221917617

0x00-0x28合计32个寄存器,分为四组,每组8个寄存器,分别是R0—R7,0x20–0x30合计16个寄存器是可以为寻址的单元。128位,地址空间是0x00-0x7F。与低128B地址空间一样。但是两个物理地址是不同的。通过访问指令可以区别。

0x30–0xFF使用分配给用户使用RAM和程序堆栈。复位后SP指向0x07H.

io寄存器作用? –因为单片机内有cpu,而其他引脚对于cpu来说属于外部,所以执行指令时,内外速度不同,需要把IO口的值存放在寄存器内,然后给IO口读。 不然CPU需要等IO口完成电平转换,就太慢了。 另外,之后的操作需要知道各端口的值,所以也需要把端口值存起来!!

​ 我们改变寄存器,单片机通过寄存器内容的改变,来改变引脚。也就是说是间接操作。P口的每个引脚都连接一个相应的BUF(缓存器),其实我们对P口的读写都是对其相对应的BUF的读写。

8051的通用寄存器(r0-r7)通过PSW映射在IRAM的0x00开始到0x20结束,共4组,每组8字节。

至于累加器(ACC),辅助寄存器(B)和数据指针寄存器(DPTR)是编址在IRAM中的。8051其实类似堆栈机模型,而且CPU和IRAM的关联度非常高,不像现代的计算机结构一样容易分离。不过如果将IRAM看待为一个大型REGISTER FILE,就更好分析8051系统了。你可以认为IRAM就是个巨大的寄存器组,CPU直接访问IRAM的效率和寄存器的效率差别不大。

并且8051背负的历史包袱太严重。原本IRAM的0x00-0x7f作为RAM使用,0x80-0xff作为SFR使用。而后8052这款芯片将0x80-0xff这段IRAM的作用增多:指针访问做RAM,直接访问做SFR。这样直接导致了8051访存效率的降低以及初学者经常拎不清楚的问题出现。同样,外部的XRAM和ROM的访问必须依赖DPTR等指针访问,这迫使不少增强型8051单片机生产商推出了双DPTR的设计。

这样的比较混乱,略微复杂,靠着专利过期成功商业化的单片机结构即将迎来它的末日。建议题主改学ARMv7/LC2K/MIPS/RISC-V/AVR这类简洁而高效的架构。

现代计算机寄存器当然是CPU里的。然而8051的IRAM和CPU是同步运行的,并且IRAM被映射为寄存器。也就是说8051这个老旧的模型的CPU和IRAM绑的太死。认为IRAM是个巨大的register file更容易理解。

image-20200905105853775

image-20200905110919958

存储器和寄存器在有些时候是一样的,有的时候又有分别所指,是单片机发展过程导致的。 在电脑中,寄存器一般是指高速缓存,这部分是在CPU内部集成的,而存储器一般指RAM/ROM等储存设备,内存条就是一个典型的例子,在CPU以外。 而到单片机以后因为存储器,CPU都在一个片内,所以寄存器是片内RAM的一部分。 所以C51单片机寄存器还是属于ram 除了指令操作方式有所不同以外,实际是一样的,只不过操作工作寄存器的语句更简单。

中断系统

image-20200905204255808

image-20200905204823680

image-20200905204923311

image-20200905211410284

CPU可以在键盘等外部器件工作时继续运行其他进程,键盘工作完成再给CPU中断,调换到原来进程,这样就提高了效率,而不会阻塞。

image-20200905212102778

image-20200905212413510

image-20200905213403184

image-20200905213751069

中断处理过程

image-20200905214011851

image-20200905214829564

中断函数没有返回值!

interrupt 0表示中断序号,表示哪个中断服务程序。 序号表如下: 就像数组的序号,0-4

image-20200905215129104

定义好中断服务函数,那么在发生对应类型的中断时,就会调用中断服务函数,然后返回原程序。

定时器

image-20200905220309262

image-20200905220945025

image-20200905234927106

**C51有2个定时器,所以定时器的中断程序入口也有两个,一个定时器分配一个! **

一个定时器由两个一字节寄存器组成,TL0,TH0组成定时器0,TL1,TH1组成定时器1. TCON和TMOD用于分别设置2个定时器T0和T1相关功能。 六个寄存器都属于SFR!!

image-20200905221350091

所以定时器可以通过设初值来自定义定时的时间

image-20200905222414538

image-20200905231358784

image-20200905231615234

image-20200905231958237

image-20200905232323274

可用aim/256的方式来算高字节TH0的值,aim%256是低字节值

TMOD设置是定时模式还是计数模式!

  • 工作方式0 定时器/计数器T0工作在方式0时,16位计数器只用了13位,即TH0的高8位和TL0的低5位,组成一个13位定时器/计数器。
  • 2 工作方式1 定时器T0工作方式1与工作方式0类同,差别在于其中的计数器的位数。工作方式0以13位计数器参与计数,工作方式1则以16位计数器参与计数。 工作方式1是16位计数器。这是工作方式1与工作方式0在计数方式时唯一差别。
  • 3 工作方式2 定时器T0在工作方式2时,16位的计数器分成了两个独立的8位计数器TH0和TL0。 工作方式2与工作方式0、方式1的差别,在于工作方式2是一个8位的计数器。
  • 4 工作方式3 工作方式3仅对定时器T0有效。当定时器T0工作在方式3时,将16位的计数器分为两个独立的8位计数器TH0和TL0。

当定时器T0工作在方式3时,定时器T1只能工作在方式0~2,并且工作在不需要中断的场合。

定时器相关寄存器

①TMOD寄存器:

img

初始化时,设置定时器0,工作模式1即16位不可重装载模式,TMOD= 0x01 ;

②IE寄存器:

img

初始化时,设置EA=1,打开总的中断,设置ET0=1开启定时器0的中断;

③TH0、TL0寄存器:

同时还要设置TH0、TL0的初始值;

④TCON寄存器:

img

初始化时,设置TR0=1启动定时器0

image-20200905232617692

image-20200906004523479

这些sfr,sbit都在头文件定义好了,能直接用

​ 这里要计数50000次,是因为通过振荡周期计算,计一次50000大概是50ms,那么20次就是1s. 等定时器到时,就会调用中断服务函数,把计数变量tt++,等到tt=20,才更换数码管的数值。 这是利用定时器去让数码管的同一位定时变化的方法

image-20200906005500891

image-20200906005649298

在中断函数中,把定时器重新赋值,从此开始重新定时50000下。

​ 对于LED灯,数码管的变化,也可以写在中断函数内,这样时间比较准确。并且因为使用全局变量,函数内共享这些变化的值。

定时器可以实现相当于后台计时,计时过程中,原程序可继续执行。

用定时器实现时钟

要小心delay和定时器的冲突! 因为在dalay过程中,定时器可能发生中断,那么中断计数器就会多于20,从delay退出,返回到main函数时,判断if(cnt==20)就失效了。 可以判断cnt>=20,但是delay可能会影响数值变化

即使是 16 位定时器,最长的溢出时间也就几十毫秒,要定时一秒,就需要一个变量来保存溢出的次数,积累到了多少次之后,才执行一次操作。这样就可以累加到 1 秒或者更长的时间才做一次操作了。51 的T0,T1定时器会自动清除溢出标记,然后继续从0开始计数。 只要不改变定时器的控制寄存器,即不关掉定时器,定时器就一直循环计数

对于非常实时的应用要求,比如时钟,还是建议发送中断时要做的操作都在中断里做完,因为使用标记的方式时,主程序可能太忙而造成错过标记信号,就是这个标记还没有开始处理呢,下一个又来了。

中断函数要尽可能短!! 尽可能减少中断冲突。 如果同级中断同时发生,只会响应1个,其他的中断都被搁置。

image-20200908193704378

image-20200908175703442

可见,中断响应原理是,各个中断源器件发生中断时,会将中断标志位发送到CPU中,cpu在每一个机器周期都检查所有中断源的标志位,如果有标志位成立,说明中断发生了,cpu就去响应。中断源器件的标志位是否在中断后取消,还是需要软件取消,看中断源的设计。而CPU内的中断标志寄存器,会在处理完一个中断请求后将其置0,好让后续中断进来。 如果多个中断同时发生,虽然只会响应一个,但是其他中断并不会取消,标志位依然保留在那,相当于在那排队。 但是由于一个中断源只会有一个标志位,所以每个中断源在同一时间都只会有一个中断机会,所以不可能出现一个中断源同时有2次中断。 这样的话,如果某个中断很慢,就造成了中断阻塞,即产生中断的速度大于处理中断的速度,那么就会有大量中断失效。

​ 信号和中断不同。信号是内核产生的,在内核中各个进程都会维护一个数组,记录有无信号发送过来。

AD与DA工作原理及实现

D/A=digital/analog 数字量/模拟量

模拟量是指变量在一定范围连续变化的量;也就是在一定范围(定义域)内可以取任意值(在值域内)

数字量是分立量,而不是连续变化量,只能取几个分立值,如二进制数字变量只能取两个值。

image-20200907230824616

AD转换就是模数转换,就是把模拟信号转换成数字信号。D/A转换是把数字量转变成模拟的器件。

A/D转换器是用来通过一定的电路将模拟量转变为数字量。模拟量可以是电压、电流等电信号,也可以是压力、温度、湿度、位移、声音等非电信号。但在A/D转换前,输入到A/D转换器的输入信号必须经各种传感器把各种物理量转换成电压信号。 总而言之,就是 电压信号 和 数字 的转换

image-20200908001341425