tips:这是一系列文章,总目录在这里~
1. 基本的计算机组成
冯·诺依曼系统架构下的计算机由计算器、控制器和存储器组成。现代计算机通常有外部存储器和各种外围I/O设备组成。由于空间有限,我们只能简单地谈论计算器、控制器和存储器,不敢深入原理,否则就是另一个话题。如果你感兴趣,我们以后可以做另一个特别的话题。
以前上学的时候听过一句很奇怪的话,说是冯·诺依曼系统是二进制计算机。事实上,冯·诺依曼与二进制无关。说二进制是香农的功劳。
(1)运算器和控制器
众所周知,这两件事现在结合在一起被称为CPU。因为它们在发明之初是分不开的。 算术操作可以通过一定的逻辑电路成算术操作,最简单的是加法操作。你可以理解半加器和全加器的概念。现在,我只能说,操作员是造二进制加法的机器。 控制器是另一组逻辑电路,可以控制计算机何时读取指令,何时操作。驱动控制器的装置称为振荡器。下面将介绍它。
(2)存储器
存储器就是存储bit位置。它们的实现方式因类型而异。
- 寄存器:严格地说,寄存器也是一种存储器,因为它也可以存储bit。寄存器由一组触发器串联组成。它的访问速度最快,但由于触发器的制造成本相对较高,它只能存在CPU一用于内部。
- 静态存储器SRAM:一般用作高速缓存。它也由触发器组成。当然,还有其他实现高速缓存的方案。
- 动态存储器DRAM:使用电容器存储数据,电容器充电表示1,不充电表示0。电容器可以很小,可以集成到芯片中。只有电容器会放电,所以需要不断充电来刷新。
- 只读存储器ROM:一开始的ROM使用一次性写入的材料,所以只读;后来有可擦写的ROM出现,即闪存。优点是不丢失掉电数据。
- 其他:磁带、光盘、软盘、硬盘等
2. 晶振和时钟
振荡器,虽然陋,但却是计算机的心脏。机械振荡器可以通过继电器设计,但机械振荡器的振动频率不稳定。电子振荡器用于我们的现代电子计算机。电子振荡器的实现有很多种。晶体振荡器用于计算机,简称晶体振荡器。 振荡器产生周期性的振荡信号(计算机中通常为方波)。控制器如果为上升沿触发,则每个周期都会被触发一次。从而驱动控制器工作。所以振荡器也就是我说的时间驱动力在物理层面最本质的体现。
计算机中有时钟的概念。时钟主频的概念是我们经常听到的CPU控制器可以稳定工作频率。但振荡器不在位置CPU中间,但位于主板。所以买不同CPU必须搭配不同型号的主板,组装电脑不能盲目组装。另一个时钟的概念被称为RTC,即实时时钟,用来记录现实世界的时间。它通常有多个电源,即使电脑断电,它也可以用备用电源工作。在主板上放纽扣电池的家伙是RTC。您还可以看到各种嵌入式设备RTC的影子。
3. 中断
在计算机运行过程中,当主机需要干预一些事故时,机器可以自动停止正在运行的程序,并将其转移到处理新情况的程序中,然后返回原暂停的程序继续运行。这种电路设计被称为中断。
因此,通过一定的逻辑电路定时,触发时间后的中断称为。每次定时器触发中断,都可以执行中断处理逻辑(中断函数)。因此,我认为定时器中断是时间驱动力在物理层面的第二层。
① 什么?如何计时定时器?那还是用晶体振动,知道振荡周期,还需要定时时间吗? ② 如何实现中断?中断是实现纯硬件电路的一种方式。逻辑电路的设计实际上与软件设计非常相似,也可以通过电路表示与、或、非的关系。换句话说,逻辑电路也可以实现软件可以实现的功能。这就是为什么我们经常听到视频解码时的软硬解。如何设计逻辑电路是另一个领域。如果你感兴趣,你可以一起探索。
让我们以简单的8051单片机为例,解释定时器中断的概念和使用。然后看看x计算机如何在86架构下中断定时器?
4. 实现8051定时器
51单片机是一种8位微处理器,实现原理相对简单,更适合我们理解一些硬件原理和操作。
(1)中断系统简介
典型的C中断系统有5个中断源(8052) 6个) ,二级中断嵌套可实现两个优先级
0 | 外部中断0 | IE0(P3.2) |
1 | 定时器0溢出中断 | TF0 |
2 | 外部中断1 | IE1(P3.3) |
3 | 定时器1溢出中断 | TF1 |
4 | 串行口中断 | RI |
可以看到,C有三种类型的51中断:外部中断、定时器中断和串行口中断。 由于C内部有定时器逻辑电路和相关寄存器,因此定时器可以在不使用外部芯片的情况下中断。但C内部定时器不是实时钟,也就是说断电后会归零,所以每次使用都需要初始化。我们也可以使用外部RTC芯片作为中断源,所以我们将RTC芯片的相应输出端口连接到单片机IE0或者IE1管脚即可。
(2)中断控制寄存器
在C还有一组寄存器用于中断控制:
- 中断允许控制寄存器IE
- 定时器控制寄存器TCON
- 串口控制寄存器SCON
- 对寄存器进行中断优先控制IP
- 定时器工作模式控制寄存器TMOD
- 定时器的初始值赋予寄存器(TH0/TH1,TL0/TL1)
我们一个一个来看看。
IE寄存器
- EX0:外部中断0允许位;
- ET0:定时/计数器T0中断允许位;
- EX1:外部中断1允许位;
- ET1:定时/计数器T1中断允许位;
- ES :串行口中断允许位;
- EA :CPU允许(总允许)位置中断。
可以看出,EA它是一个总开关,所以必须是1;我们需要使用它ET0定时器中断,那么ET0要为1。
TCON寄存器
- IT0:外部中断0触发控制位
- 当IT0=0时,电平触发(低电平有效)
- 当IT0=1.边缘触发法(下降边缘有效)
- IE0:外部中断0中断请求标志位
- IT1:外部中断1触发控制位
- IE1.外部中断1中断要求标志位
- TF0:定时/计数器T溢出中断请求标志位置
- TF1:定时/计数器T溢出中断要求标志位置
- TR0:定时/计数器T0工作控制标志位,1–开始计时,0–停止
- TR1:定时/计数器T1工作控制标志位,1–开始计时,0–停止
SCON用于控制串口中断的寄存器,不在这里展开。
IP控制器
- PX0:外部中断0优先定位
- PT0:定时/计数器T0优先级定位
- PX1.外部中断0优先设定位置
- PT1:定时/计数器T1优先级定位
- PS :串行口优先设定位置
TMO寄存器
TMOD是用来指定定时器工作方式的寄存器
(3)计数寄存器
TX0和TX1,当前T0和T1计数的值将存放在计数寄存器中 TX0又分为TH0和TL0,TX1又分为TH1和TL1
:
时钟周期:假设我们使用12MHz的晶振,也就是说每秒震荡12M次;那么一个时钟周期就是12M的倒数。时钟周期就是最小的时间单位。 机器周期:1个时钟周期没办法让单片机干完所有事情,而12个时钟周期才能够完成基本的操作。
即:每计一次数,就会消耗掉一个机器周期=1/12M*12=1/1M=0.000001s=1μs
如果我想要每隔50ms定时器中断一次,我应该设置定时器的初值为:65536-50000=15536 换算为二进制就是:0011 1100 1011 0000
(4)定时器中断
我们先来看看如何使用内部的定时器中断的操作。 首先根据上面的介绍,我们知道要使用定时器中断,首先要进行初始化设置:
- 给TMOD赋值,确定工作方式
- 给定时器赋初值,写入TH0,TL0或TH1,TL1
- 中断允许控制,EA和ET0 或ET1的置位为1
- TR0或TR1置位,启动定时器
// 定时器初始化,我们使用T0定时器
void timerInit()
{
// 0000 0001
// 高四位用于T1,所以全部为0;低四位用于T0,第四位GATE为0即可,第三位选用定时模式为0,第二位和第一位设置工作方式为16位定时器
TMOD = 0x01;
// 0011 1100 1011 0000 = 15536(十进制)
TH0 = 0x3C;
TL0 = 0xB0;
// 总开关打开
EA = 1;
// T0开关打开
ET0 = 1;
// T0开始工作
TR0 = 1;
}
完整程序如下:
/********************************************************************************* *FileName: DynamicDigitalTube *Author: wjc133 *Version: 1.0.0 **********************************************************************************/
#include <8051.h>
#define A P2_4
#define B P2_3
#define C P2_2
#define SEGMENT P0
__code unsigned char NUMBER[] = {
0xFC, // 0: 0000 0011 1111 1100
0x60, // 1: 1001 1111 0110 0000
0xDA, // 2: 0010 0101 1101 1010
0xF2, // 3: 0000 1101 1111 0010
0x66, // 4: 1001 1001 0110 0110
0xB6, // 5: 0100 1001 1011 0110
0xBE, // 6: 0100 0001 1011 1110
0xE0, // 7: 0001 1111 1110 0000
0xFE, // 8: 0000 0001 1111 1110
0xF6, // 9: 0000 1001 1111 0110
};
/* 定义全局变量 */
unsigned char time;
unsigned int currentNum = 0;
/* 函数声明 */
void timerInit();
void timerInterrupt() __interrupt 1;
unsigned int getNumber(unsigned int n);
void display(unsigned int a);
void main()
{
time = 0;
timerInit();
while (1)
{
display(currentNum);
}
}
void timerInit()
{
TMOD = 0x01;
TH0 = 0x3C;
TL0 = 0xB0;
EA = 1;
ET0 = 1;
TR0 = 1;
}
/* 响应中断号为1的中断函数 */
void timerInterrupt() __interrupt 1
{
TH0 = 0x3C;
TL0 = 0xB0;
if (++time == 20)
{
currentNum = getNumber(currentNum);
time = 0;
}
}
unsigned int getNumber(unsigned int n)
{
if (n + 1 >= 1000)
{
return 0;
}
return n + 1;
}
void display(unsigned int a)
{
// 拆解分析a的位数
unsigned char i = 0;
unsigned int d[8];
do
{
d[i++] = a % 10;
a = a / 10;
} while (a > 0);
// 显示每一位
unsigned char ni = 0;
unsigned int nj = 0;
for (ni = 7; ni > 7 - i; ni--)
{
switch (ni)
{
case 0:
A = 0;
B = 0;
C = 0;
break;
case 1:
A = 1;
B = 0;
C = 0;
break;
case 2:
A = 0;
B = 1;
C = 0;
break;
case 3:
A = 1;
B = 1;
C = 0;
break;
case 4:
A = 0;
B = 0;
C = 1;
break;
case 5:
A = 1;
B = 0;
C = 1;
break;
case 6:
A = 0;
B = 1;
C = 1;
break;
case 7:
A = 1;
B = 1;
C = 1;
break;
default:
break;
}
SEGMENT = NUMBER[d[7 - ni]];
nj = 10;
while (nj--)
;
SEGMENT = 0x00;
}
}
参考资料
- C51 定时器/计数器个人笔记,by YuQiao0303
- 51单片机中断详解(上),by Line
- C51单片机晶振频率