51单片机

本51项目基于STC89C52MCU,温度传感器为DS18B20,显示模块用的是LCD1602,无线模块用的是Nodemcu。

项目用到的编程语言:C,C++,Lua。

实现思路是这样,DS18B20测温,然后数据串行传送给51单片机,然后51通过串口将数据传送给Nodemcu,Nodemcu通过其WIFI模块将数据发送给上位机,上位机上的程序是用Qt编写的GUI。(这里无线传输采用的是无连接的UDP协议)

1. DS18B20温度测量模块

DS18B20是单总线器件,所以时序要求非常严格,程序编写时注意时序,否则读不出温度。DS18B20有寄生供电和单独供电,这里采用单独供电方式。它的驱动程序如下:

#include"Temp.h"
void delay1ms(unsigned int y)
{
unsigned int x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}

void Init18b20(){
int i;
DQ=0;
i=60;
while(i--); //10us左右,一共延时60*10=600us左右。
DQ=1;

while(DQ)
{
i=1;
while(i--);
}
}

void Writeonebyte(unsigned char ins){
int i=0;
int j=0;
for(i=0;i DQ=0;
j++; //延时1us
DQ=0x01&ins; //写0或者1,写完以后保持,15us内DS18B20会来采集数据。
j++;
j=6;
while(j--); //一个写周期至少60us。
DQ=1; //释放数据线
ins=ins>>1; //写下一位数据
}
}

unsigned char Readonebyte(){
int i=0;
int j=0;
unsigned char bi, byte;
for(i=0;i DQ=0;
j++;
DQ=1; //拉低1us后释放,给一个准备读的信号
j++;
j++;
bi=DQ;
byte=(byte>>1)|(bi j=5;
while(j--);
}
return byte;
}

void Tempconvert(){
Init18b20();
delay1ms(1);
Writeonebyte(0xcc);
Writeonebyte(0x44);
}

void Readrom(){
Init18b20();
delay1ms(1);
Writeonebyte(0xcc);
Writeonebyte(0xbe);
}

int Readtemp(){
int temp;
unsigned char tml,tmh;
Tempconvert();
Readrom();
tml=Readonebyte();
tmh=Readonebyte();
temp=tmh;
temp=temp temp=temp|tml;
return temp;
}

头文件
#include"reg52.h"
sbit DQ=P1^0;
void Init18b20();
void Writeonebyte(char ins);
unsigned char Readonebyte();
void Tempconvert();
void Readrom();
int Readtemp();
void delay1ms();

2. LCD1602显示模块

这个模块比较简单,注意时序,然后初始化函数里面不要写清屏指令,以免显示的数据一直闪烁。

#include"lcd1602.h"
char init[]="Temp ";
void delay1ms() //误差 -0.651041666667us
{
unsigned char a,b;
for(b=102;b>0;b--)
for(a=3;a>0;a--);
}

void delay5ms() //误差 -0.000000000001us
{
unsigned char a,b;
for(b=15;b>0;b--)
for(a=152;a>0;a--);
}
void writecom(int com){
E=0;
RS=0;
RW=0;
P0=com; //并行送数据
delay1ms(); //送完后保持一下 使其稳定
E=1; //拉高,让数据发送
delay5ms();
E=0;
}
void writedat(char dat){
E=0;
RS=1;
RW=0;
P0=dat;
delay1ms();
E=1;
delay5ms();
E=0;
}
void lcdinit(){
int i=0;
delay1ms();
writecom(0x06);
writecom(0x0c);
delay1ms();
writecom(0x38);
writecom(0xc3);
for(i=0;i writedat(init[i]);
}

头文件

#include<reg52.h>
sbit RS=P2^0;
sbit RW=P2^1;
sbit E=P2^2;
void delay1ms();
void delay5ms();
void lcdinit();
void writecom(int com);
void writedat(char dat);

3. Nodemcu模块

手里只有这个MCU,没有ESP8266。其实差不多,Nodemcu就是一个ESP8266的小系统板,然后将一些资源进行二次封装,然后用Lua语言开发。如果是单独的ESP8266模块,就用串口 AT指令调试。 相同的是一开始都要烧固件进去,固件可以自己选,模块都在github上。这个Nodemcu三种工作方式 STA ,AP,STA+AP。 STA模式就是可以像手机一样接到你的路由器上,然后你可以用你的手机电脑通过网络来操纵。AP就是这个模块成为一个热点,你手机电脑可以连接上这个热点,这种方式适用于局域网。STA+AP是都可以的。本次项目使用STA模式。

代码如下

print('Setting up WIFI...')
wifi.setmode(wifi.STATION)
wifi.sta.config('AP', 'SSID')
wifi.sta.connect()

tmr.alarm(1, 1000, tmr.ALARM_AUTO, function()
if wifi.sta.getip() == nil then
print('Waiting for IP ...')
else
print('IP is ' .. wifi.sta.getip())
tmr.stop(1)
end
end)

cu=net.createConnection(net.UDP, 0)
cu:connect(9999,"ip")
cu:on("receive",function(cuk,c) print(c) end )

uart.setup(0,9600,8,0,1,0)
uart.on("data",
function(data)
print("receive from uart:", data)
cu:send(data)
end, 0)

这里要注意的是你烧进去的第一个程序,名字必须是init.lua 它上电是从这个文件开始执行的。

最后一点

各个模块的组合,即C语言主函数如下

#include"Temp.h"
#include"lcd1602.h"
#include<reg52.h>
void dlay1ms(int y)
{
int x;
for( ; y>0; y--)
{
for(x=110; x>0; x--);
}
}
void Serialinit(){
TMOD=0x20;
TH1=0XFD;
TL1=0XFD;
TR1=1;
SCON=0x50; //配置好串口,这里波特率为9600. 只用定时器和串口,不用中断。
}

char list[11]={'0','1','2','3','4','5','6','7','8','9','.'};
char value[4];
int i=0;

void Datapros(int temp){
float fp;
fp=temp;
temp=fp*0.0625*100; //注意 0.0625*temp是温度,但是乘出来的可能是带小数部分的,你要先把temp变成带小数的,再乘0.0625,否则乘出来的小数部分就被类型转换截掉了。
value[0]=list[temp/1000];
value[1]=list[temp%1000/100];
value[2]=list[10];
value[3]=list[temp%100/10]; //显示一位小数,这种显示算法对数码管也通用。
}

void Lcddisplay(){
lcdinit();
writedat(value[0]);
dlay1ms(20);
writedat(value[1]);
dlay1ms(20);
writedat(value[2]);
dlay1ms(20);
writedat(value[3]);
dlay1ms(20);
}

int main(){
Serialinit();
while(1){
Datapros(Readtemp());
Lcddisplay();
for(i=0;i {
SBUF=value[i]; //启动串口传输,51单片机的TXD RXD接到Nodemcu上的TX RX上。
while(!TI);
TI=0;
}
}
}

上位机界面用Qt编写。

代码就不贴出来了。

整个项目做完以后的总结与不足。

总结:DS18B20时序一定要特别注意。Lcd如果添加了清屏指令,显示温度会闪烁。Nodemcu第一个程序名一定是init.lua。烧写工作有好几种,自己选择。Lua语言需要了解一下,Nodemcu的API需要了解一下。然后就是Qt写的GUI,里面用到的UDP协议,端口和IP要对应好,不然接收不到数据。

不足: DS18B20可以用寄生方式供电,这个我不是很了解,下次可以尝试寄生供电方式工作,这个适合远距离测温。无线数据传说部分可以用更可靠的TCP协议来传输,这个必须要会,下次会尝试使用这个协议来编写上位机程序。采集到的温度没有进行处理,只用来显示了,希望在学习数据库以后加入数据的存储筛选功能。

来源:博客园 - Crenx

围观 8
471

一、总线概述

计算机系统是以微处理器为核心的,各器件要与微处理器相连,且必须协调工作,所以在微处理机中引入了总线的概念,各器件共同享用总线,任何时候只能有一个器件发送数据(可以有多个器件同时接收数据) 。

计算机的总线分为控制总线、地址总线和数据总线等三种。而数据总线用于传送数据,控制总线用于传送控制信号, 地址总线则用于选择存储单元或外设。

二、单片机的三总线结构

51系列单片机具有完善的总线接口时序,可以扩展控制对象,其直接寻址能力达到64k( 2的16次方) 。在总线模式下,不同的对象共享总线,独立编址、分时复用总线,CPU 通过地址选择访问的对象,完成与各对象之间的信息传递。

单片机三总线扩展示意如图1 所示。

51单片机总线时序介绍

1、数据总线

51 单片机的数据总线为P0 口,P0 口为双向数据通道,CPU 从P0 口送出和读回数据。

2、地址总线

51 系列单片机的地址总线为16 位。

为了节约芯片引脚,采用P0 口复用方式,除了作为数据总线外,在ALE 信号时序匹配下,通过外置的数据锁存器,在总线访问前半周期从P0口送出低8位地址,后半周期从P0 口送出8 位数据。

高8位地址则通过P2 口送出。

3、控制总线

51 系列单片机的控制总线包括读控制信号P3.7 和写控制信号P3.6 等,二者分别作为总线模式下数据读和数据写的使能信号。

三、单片机总线时序分析

51 单片机总线时序如图2 所示。

51单片机总线时序介绍

从图2 中可以看出,完成一次总线( 读写) 操作周期为T,P0 口分时复用,在T0 期间,P0 口送出低8 位地址,在ALE 的下降沿完成数据锁存,送出低8位地址信号。在T1 期间,P0 口作为数据总线使用,送出或读入数据,数据的读写操作在读、写控制信号的低电平期间完成。

需要注意的是,在控制信号( 读、写信号) 有效期间,P2 口送出高8位地址,配合数据锁存器输出的低8 位地址,实现16 位地址总线,即64kB 范围的内的寻址。

由于CPU不可能同时执行读和写操作,所以读、写信号不可能同时有效。

四、常见单片机编址电路

1、简单地址扩展

51 单片机的P2 口可以直接作为高8位地址总线使用,在一些简单系统电路中,常使用P2口直接编址驱动。

下面以使用数据缓冲器74LS273 驱动数码显示为例,分析P2 口编址驱动的静态数码显示电路的设计。

一位LED 数码显示单元电路如图3 所示。

51单片机总线时序介绍

WR 与A8( P2.0) 相或提供74LS273的时钟信号,当执行“MOVX @DPTR,A”指令时,地址信息由DPTR 寄存器确定,会出现有效的写信号WR,只有当地址A8 为满足“0”时,写信号才可以作为74LS273 的时钟信号输入,完成数据锁存。

P2 口为A8~A15 的8 位地址线,很容易扩展到8 只LED 数码管,WR 信号分别与A8~A15 按或关系连接,每位地址线均为低电平有效,即可实现8 个有效地址。

该方案电路简单,但有效地址数太少,不适用于复杂系统设计。

2、低8 位地址锁存

通常的设计电路是使用8D 锁存器74LS373 实现地址锁存,74HC573 与之逻辑功能相同,只是引脚布局不一样,使用74HC573 布线更容易。

74LS373 真值表如图4所示。

51单片机总线时序介绍

在输出允许OE 为L、控制使能LE 为H 时,输出为跟随状态;

OE 为L、LE 为L 时,输出为保持状态。

地址锁存电路如图5 所示。OE 接地,LE 接单片机的ALE脚将产生满足时序的低8 位地址信号。

执行以下三条指令会得到如图6所示的时序图。

MOV DPTR,# 0FF55H; 低8 位地址为55H

MOV A,# 0AAH; 待发送数据0AAH→A( 55H 取反)

MOVX,@DPTR,A; A 中的0AAH送地址为0FF55H 的对象中会。

51单片机总线时序介绍

从图6 中可以看出,P0 口先送55H,在ALE 下降沿实现地址锁存,随后送出数据0AAH,在WR 有效( 低电平) 期间锁存器输出低8 位地址55H,P0 口送出数据0AAH。

3、带译码器的复杂地址接口电路

理论上高8 位地址线可以产生256 个有效地址,如何实现地址“扩展”呢? 地址扩展准确描述是地址译码,例如3 根地址线可以译码成8 个地址,4根译码成16 个有效地址。这里选择3-8 译码器实现地址译码,电路图以及对应的编址如表1 所示。

51单片机总线时序介绍

五、单片机总线编址电路实例

带总线扩展接口的单片机系统,包括外部32k RAM 扩展、LCD1602 接口、输入输出口。

带编址扩展的单片机最小系统电路如图7 所示。

51单片机总线时序介绍

使用74HC573 锁存低8 位地址;74138 实现8 个地址扩展,74138 的A、B、C 接A8 ~A10,E1 接A15, E2、E3 接地常有效,得到0F8FFH 到0FFFFH8 个地址( 无关位用1 表示) 或者8000H 到8700H( 无关位用0 表示) 。

32k RAM 接口如图8 所示。

51单片机总线时序介绍

D0~D7 接数据总线P0 口,地址线A0~A14接单片机地址总线低15 位,单片机地址线A15 接RAM 片选信号,低电平有效,这样RAM 地址分配从0000H 到7FFFH,与74138 译码地址不冲突。

LCD1602 接口电路如图9 所示。

51单片机总线时序介绍

RS、RW 分别接A12、A13,使能信号编址为Y7,这样LCD 的四个驱动地址( 数据读写和命令读写) 为0CFFFH 到0FFFFH ( 无关位为1) 或者8700H 到0B700H( 无关位为0)。

有些时候单片机引脚不够用,还要进行扩展,输入口扩展电路如图10 所示。

51单片机总线时序介绍

利用74HC573( 74LS373) 的高阻态功能,将其输出Q0~Q7 接P0 口,在满足总线地址读操作中,可以把输入InPORT的数据读入单片机的累加器,地址为0F8FFH 或8000H。

输出口扩展电路如图11 所示。

51单片机总线时序介绍

利用74LS273 数据锁存功能,在满足总线地址写操作中,可以把单片机累加器里的数据写入273 锁存输出,地址为0F8FFH 或8000H。由于所用控制总线不同,可以和输入共用地址。

六、结束语

总线扩展是设计单片机控制电路必须掌握的技术,大量的特殊功能IC都支持总线接口, 如ADC0809,TLC7528,DDS 器件AD9851 等。

总线接口的要点就是在严格的控制时序下,总线被分时复用,以实现复杂系统设计。

转自 51单片机总线时序 - 电子技术基础知识 - 21IC中国电子网 http://www.21ic.com/jichuzhishi/mcu/shixu/2013-02-27/159327.html

围观 11
855

data:固定指前面0x00-0x7f的128个RAM,可以用acc直接读写的,速度最快,生成的代码也最小。

bit:是指0x20-0x2f的可位寻址区

idata:固定指前面0x00-0xff的256个RAM,其中前128和dATa的128完全相同,只是因为访问的方式不同。

idata是用类似C中的指针方式访问的。

汇编中的语句为:mox ACC,@Rx.(不重要的补充:c中idATa做指针式的访问效果很好)

xdATa:外部扩展RAM,一般指外部0x0000-0xffff空间,用DPTR访问。

pdATa:外部扩展RAM的低256个字节,地址出现在A0-A7的上时读写,用movx ACC,@Rx读写。这个比较特殊,而且C51好象有对此BUG,建议少用。但也有他的优点,具体用法属于中级问题,这里不提。

startup.a51的作用和汇编一样,在C中定义的那些变量和数组的初始化就在startup.a51中进行,如果你在定义全局变量时带有数值,如unsigned char dATa xxx="100";,那startup.a51中就会有相关的赋值。如果没有=100,startup.a51就会把他清0。
(startup.a51==变量的初始化)。

这些初始化完毕后,还会设置SP指针。对非变量区域,如堆栈区,将不会有赋值或清零动作。

有人喜欢改startup.a51,为了满足自己一些想当然的爱好,这是不必要的,有可能错误的。比如掉电保护的时候想保存一些变量,但改startup.a51来实现是很笨的方法,实际只要利用非变量区域的特性,定义一个指针变量指向堆栈低部:0xff处就可实现。为什么还要去改? 可以这么说:任何时候都可以不需要改startup.a51,如果你明白它的特性。

bit 是在内部数据存储空间中 20H .. 2FH 区域中一个位的地址,这在DATA的20H以后以字节形式出现,可互相参照。另外加上8051 可寻址 的SFR,但刚刚试过,只是00H--7FH起作用,也就是说当数据有变化时颜色变红,以后的从80H到--FFH就不是位寻址区了,是位寻址的特殊寄存器,如涉及到了可位寻址的那11个当然会有反应。

复位后,程序计数器PC的内容为0000H,内部RAM各单元的值不确定。

各功能寄存器的复位值如下:

堆栈指针SP的复位值为07H,累加器ACC、寄存器B的复位值为00H,数据指针DPTR的复位值为0000H,而p0、p1、p2、p3四个口的复位值为0FFH。其他SFR如PSW、TCON、TMOD、TL0、TH0、TL1、TH1的复位值也为00H。

wave中是低128字节和高128字节(0-7FH),低128字节是片内RAM区,高128字节(80-FFH)是SFR(特殊功能寄存器)bit则是位于低128字节的20H .. 2FH 区域,即data的20H .. 2FH 区域
code 是在 0000H .. 0FFFFH 之间的一个代码地址。

我用
ORG 5000H
TAB: DB 22H,3BH,43H,66H,5H,6DH,88H后,
CODE从5000H开始以后变成DB各位

data 是在 0 到 127 之间的一个数据存储器地址,或者加 128 .. 255 范围内的一个特殊功能寄存器(SFR)地址。两者访问的方式不同。实际上由于PSW的复位设置PSW.3=RS0和PSW.4=RS1皆为0,所以通用工作寄存器区就是第0区,所以data的00--07H部分是与REG栏中的R0--R7对应的。以后的则仅代表低128字节的内部RAM。

idata 是 0 to 255 范围内的一个 idata 存储器地址

idata与data重合低128字节,有的地方只有DATA表示256字节的片内RAM

xdata 是 0- 65535 范围内的一个 xdata 存储器地址。

指针类型和存储区的关系详解

一、存储类型与存储区关系

data --->   可寻址片内ram
bdata --->   可位寻址的片内ram
idata --->  可寻址片内ram,允许访问全部内部ram
pdata ---> 分页寻址片外ram (MOVX @R0) (256 BYTE/页)
xdata ---> 可寻址片外ram (64k 地址范围FFFFH)
code ---> 程序存储区 (64k 地址范围),对应MOVC @DPTR

二、指针类型和存储区的关系

对变量进行声明时可以指定变量的存储类型如:

uchar data x和data uchar x相等价都是在内ram区分配一个字节的变量。

同样对于指针变量的声明,因涉及到指针变量本身的存储位置和指针所指向的存储区位置不同而进行相应的存储区类型关键字的使用如:

uchar xdata * data pstr
是指在内ram区分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向xdata区("*"前xdata关键字的作用),

可能初学C51时有点不好懂也不好记。没关系,我们马上就可以看到对应“*”前后不同的关键字的使用在编译时出现什么情况。
......
uchar xdata tmp[10]; //在外ram区开辟10个字节的内存空间,地址是外ram的0x0000-0x0009
......

第1种情况:

uchar data * data pstr;
pstr="tmp";
首先要提醒大家这样的代码是有bug的, 他不能通过这种方式正确的访问到tmp空间。 为什么?我们把编译后看到下面的汇编

代码:
MOV 0x08,#tmp(0x00) ;

0x08是指针pstr的存储地址看到了吗!本来访问外ram需要2 byte来寻址64k空间,但因为使用data关键字(在"*"号前的那个),所以按KeilC编译环境来说就把他编译成指向内ram的指针变量了,这也是初学C51的朋友们不理解各个存储类型的关键字定义而造成的bug。

特别是当工程中的默认的存储区类为large时,又把tmp[10] 声明为uchar tmp[10] 时,这样的bug是很隐秘的不容易被发现。

第2种情况:

uchar xdata * data pstr;
pstr = tmp;
这种情况是没问题的,这样的使用方法是指在内ram分配一个指针变量("*"号后的data关键字的作用),而且这个指针本身指向 xdata区("*"前xdata关键字的作用)。编译后的汇编代码如下。

MOV 0x08,#tmp(0x00) ;0x08和0x09是在内ram区分配的pstr指针变量地址空间
MOV 0x09,#tmp(0x00)

这种情况应该是在这里所有介绍各种情况中效率最高的访问外ram的方法了,请大家记住他。

第3种情况:

uchar xdata * xdata pstr;
pstr="tmp";
这中情况也是对的,但效率不如第2种情况。编译后的汇编代码如下。
MOV DPTR, #0x000A ;0x000A,0x000B是在外ram区分配的pstr指针变量地址空间
MOV A, #tmp(0x00)
MOV @DPTR, A
INC DPTR
MOV A, #tmp(0x00)
MOVX @DPTR, A
这种方式一般用在内ram资源相对紧张而且对效率要求不高的项目中。

第4种情况:

uchar data * xdata pstr;
pstr="tmp";
如果详细看了第1种情况的读者发现这种写法和第1种很相似,是的,同第1 种情况一样这样也是有bug的,但是这次是把pstr分配到了外ram区了。编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A是在外ram区分配的pstr指针变量的地址空间
MOV A, #tmp(0x00)
MOVX @DPTR, A

第5种情况:

uchar * data pstr;
pstr="tmp";
大家注意到"*"前的关键字声明没有了,是的这样会发生什么事呢?下面这么写呢!对了用齐豫的一首老歌名来说就是 “请跟我 来”,请跟我来看看编译后的汇编代码,有人问这不是在讲C51吗? 为什么还要给我们看汇编代码。C51要想用好就要尽可能提升C51 编译后的效率,看看编译后的汇编会帮助大家尽快成为生产高效C51代码的高手的。还是看代码吧!

MOV 0x08, #0X01 ;0x08-0x0A是在内ram区分配的pstr指针变量的地址空间
MOV 0x09, #tmp(0x00)
MOV 0x0A, #tmp(0x00)

注意:这是新介绍给大家的,大家会疑问为什么在前面的几种情况的pstr指针变量都用2 byte空间而到这里就用3 byte空间了呢?这是KeilC的一个系统内部处理,在KeilC中一个指针变量最多占用 3 byte空间,对于没有声明指针指向存储空间类型的指针, 系统编译代码时都强制加载一个字节的指针类型分辩值。具体的对应关系可以参考KeilC的help中C51 User’s Guide。

第6种情况:

uchar * pstr;
pstr="tmp";

这是最直接最简单的指针变量声明,但他的效率也最低。还是那句话,大家一起说好吗!编译后的汇编代码如下。

MOV DPTR, #0x000A ;0x000A-0x000C是在外ram区分配的pstr指针变量地址空间
MOV A, #0x01
MOV @DPTR, A
INC DPTR
MOV DPTR, #0x000A
MOV A, #tmp(0x00)
MOV @DPTR, A
INC DPTR
MOV A, #tmp(0x00)
MOVX @DPTR, A
这种情况很类似第5种和第3种情况的组合,既把pstr分配在外ram空间了又增加了指针类型的分辨值。

转自:张凌001

围观 10
814

一、作用

定时器/计数器以定时器的作用最为突出。一般来说涉及到定时器,都可以算作定时器算法。简单的如秒表等。复杂的如果系统内核的定时算法等。在学习单片机的路上,必学会定时器。

二、工作原理

定时器/计数器的原理其实没那么难。

定时器也就是一个计数器。而每计一个数的时间是一定的,于是就等价了定时器。51的定时器是从你设置的初值开始计数,直到他所能达到的最高数值为止。即:

定时时间 =(最大值 – 初值)X 每计一个数的时间

最大值因不同模式而定,初值需要你设计。

当达到定时器最大值的时候,就会触发定时器标志置位。当开启全局中断和定时器中断的时候,定时器标记置位会触发定时器中断函数,执行玩中断后,标记会自动复位。备注:如果没有触发中断函数,标记不会自动复位。

三、定时器术语理解

1、溢出:当定时器达到定时或者计数标准的时候,定时器就处在溢出停止状态。

2、溢出标记:当定时器溢出的时候,定时器标志就会置位。

3、初值:设计者自行设计的定时器计数的起步值。定时器是从这个值开始定时或者计数到最大值。

4、初值的计算:定时器有装载定时计数的寄存器,只要对这个寄存器进行操作即可。

四、工作模式寄存器TMOD,地址89H(不可按位寻址)

51单片机的定时计数器

GATE:定时操作开关控制位。当GATE=1时,INT0或INT1引脚为高电平,同时TCON中的TR0或TR1控制位为1时,计时/计数器0或1才开始工作。若GATE=0,则只要将TR0或TR1控制位设为1,计时/计数器0或1就开始工作。

C/T:定时器/计数器功能的选择位。C/T=1为计数器,通过外部引脚T0或T1输入计数脉冲。C/T=0时为定时器,由内部系统时钟提供计时工作脉冲。

M1和M0为定时器工作模式选择位。

定时器的工作模式如图2所示:

51单片机的定时计数器

五、控制寄存器TCON,地址位88H(可按位寻址)

51单片机的定时计数器

TF1:定时器T1溢出标志,可由程序查询和清零,TF1也是中断请求源,当CPU响应T1中断时由硬件清零。

TF0:定时器T0溢出标志,可由程序查询和清零,TF0也是中断请求源,当CPU响应T0中断时由硬件清零。

TR1:T1充许计数控制位,为1时充许T1计数。

TR0:T0充许计数控制位,为1时充许T0计数。

备注:其他位供其他功能使用

六、控制寄存器T2CON

51单片机的定时计数器

TF2:T2溢出中断标志。TF2必须由用户程序清“0”。当T2作为串口波特率发生器时,TF2不会被置“1”。

EXF2:定时器T2外部中断标志。EXEN2为1时,当T2EX(P1.1)发生负跳变时置1中断标志DXF2,EXF2必须由用户程序清“0”。

TCLK:串行接口的发送时钟选择标志。TCLK=1时,T2工作于波特率发生器方式。

RCLK:串行接口的接收时钟选择标志位。RCLK=1时,T2工作于波特率发生器方式。

EXEN2:T2的外部中断充许标志。

C/T2:外部计数器/定时器选择位。C/T2=1时,T2为外部事件计数器,计数脉冲来自T2(P1.0);C/T2=0时,T2为定时器,振荡脉冲的十二分频信号作为计数信号。

TR2:T2计数/定时控制位。TR1为1时充许计数,为0时禁止计数。

CP/RL2:捕捉和常数自动再装入方式选择位。为1时工作于捕捉方式,为0时T2工作于常数自动再装入方式。当TCLK或RCLK为1时,CP/RL2被忽略,T2总是工作于常数自动再装入方式。

定时器2工作模式如图5所示:

51单片机的定时计数器

七、定时器应用技巧

1、定时器定时长度扩展。可以人为的加入变量,每当定时器溢出,变量进行自加。这样就可以无限的扩展定时时间长度。

2、定时器误差问题。因为在执行定时器中断的时候,执行里面指令肯定要耗时。所以减少误差的就两种办法:简化中断服务程序;将定时器中断的程序外迁。

转自:武力戡乱的博客

围观 8
762

51系列单片机内部主要有四大功能模块,分别是I/O口模块、中断模块、定时器模块和串口通信模块(串行I/O口)。51开发的重点其实就是对这四个部分进行具体的开发,而其对这四个模块的开发实质则又是能否对每个模块所对应寄存器的正确操纵。

单片机的内部结构可以大概归纳如下图:四大功能模块相关的寄存器又可分为四大部分:

I/O口相关:P1 P2 P3 P4
中断相关:IP IE
定时器相关:TMOD TCON TL0、TH0、TL1、TH1
串口通信相关:PCON SBUF

51的特殊功能寄存器sfr笔记

51单片机内部共有21个特殊功能寄存器SFR,从下图中可以看出,每个SFR占1个字节,多数字节单元中的每一位又有专用的“位名称”。这21个SFR又按是否可以位寻址分为两大部分,ACC、IE、P1等11个可以位寻址,SP、TMOD等不可以位寻址。

51的特殊功能寄存器sfr笔记

51的特殊功能寄存器sfr笔记

能位寻址是指能够对它的每一位都可以进行位操作,比如我们常用的用十六进制的数据0x01为p1口赋值使得p1^0输出高电平,这种叫做能位寻址;不可寻址,则是指不能单独进行每一位的操作,如TMOD定时器工作模式及工作方式寄存器,在进行操作时,只能写TMOD=0xXX。再以IE寄存器为例进行位操作的解释。IE寄存器为中断允许寄存器,如各位的作用如图.其中第7位EA是51单片机5个中断的总开关,如要进入任何一个中断时,需先把EA打开,因为可以进行位操作,此时程序有两种写法:1)IE=0x80(假如其它位为0,即1000 0000),也可以直接写EA=1,后者EA=1即属于位操作。

51的特殊功能寄存器sfr笔记

关于能否进行位操作,可以通过查相关资料知道,当然还有个技巧就是其字节地址换成10进制后能否被“8”整除,能被“8”整除的就能进行位操作,不能被“8”整除就不能,如P1地址为90H,10进制为144 144/8=18,能被整除,所以可以位操作。再如TMOD地址为89H, 10进制为137 137/8=17.125,不能被整除,所以不可以位操作。

转自:劳斯机要开车了

围观 20
616

数据传送指令共有29条,数据传送指令一般的操作是把源操作数传送到目的操作数,指令执行完成后,源操作数不变,目的操作数等于源操作数。

如果要求在进行数据传送时,目的操作数不丢失,则不能用直接传送指令,而采用交换型的数据传送指令,数据传送指令不影响标志C,AC和OV,但可能会对奇偶标志P有影响。

以累加器A为目的操作数类指令(4条)

这4条指令的作用是把源操作数指向的内容送到累加器A。有直接、立即数、寄存器和寄存器间接寻址方式:
MOV A,data;   (data)→(A) 直接单元地址中的内容送到累加器A
MOV A,#data;   #data →(A) 立即数送到累加器A中
MOV A,Rn;    (Rn)→(A) Rn中的内容送到累加器A中
MOV A,@Ri;   ((Ri))→(A) Ri内容指向的地址单元中的内容送到累加器A

以寄存器Rn为目的操作数的指令(3条)

这3条指令的功能是把源操作数指定的内容送到所选定的工作寄存器Rn中。有直接、立即和寄存器寻址方式:
MOV Rn,data;  (data)→(Rn) 直接寻址单元中的内容送到寄存器Rn中
MOV Rn,#data;  #data →(Rn) 立即数直接送到寄存器Rn中
MOV Rn,A;    (A)→(Rn)    累加器A中的内容送到寄存器Rn中

以直接地址为目的操作数的指令(5条)

这组指令的功能是把源操作数指定的内容送到由直接地址data所选定的片内RAM中。有直接、立即、寄存器和寄存器间接4种寻址方式:
MOV data,data; (data)→(data) 直接地址单元中的内容送到直接地址单元
MOV data,#data; #data→(data)   立即数送到直接地址单元
MOV data,A; (A)→(data)    累加器A中的内容送到直接地址单元
MOV data,Rn; (Rn)→(data)    寄存器Rn中的内容送到直接地址单元
MOV data,@Ri; ((Ri))→(data) 寄存器Ri中的内容指定的地址单元中数据送到直接地址单元

以间接地址为目的操作数的指令(3条)

这组指令的功能是把源操作数指定的内容送到以Ri中的内容为地址的片内RAM中。有直接、立即和寄存器3种寻址方式:
MOV @Ri,data; (data)→((Ri)) 直接地址单元中的内容送到以Ri中的内容为地址的RAM单元
MOV @Ri,#data; #data→((Ri)) 立即数送到以Ri中的内容为地址的RAM单元
MOV @Ri,A; (A)→((Ri)) 累加器A中的内容送到以Ri中的内容为地址的RAM单元

查表指令(2条)

这组指令的功能是对存放于程序存储器中的数据表格进行查找传送,使用变址寻址方式:
MOVC A,@A+DPTR;((A))+(DPTR)→(A) 表格地址单元中的内容送到累加器A中
MOVC A,@A+PC; ((PC))+1→(A),((A))+(PC)→(A) 表格地址单元中的内容送到累加器A中

累加器A与片外数据存储器RAM传送指令(4条)

这4条指令的作用是累加器A与片外RAM间的数据传送。使用寄存器寻址方式:
MOVX @DPTR,A; (A)→((DPTR)) 累加器中的内容送到数据指针指向片外RAM地址中
MOVX A, @DPTR; ((DPTR))→(A) 数据指针指向片外RAM地址中的内容送到累加器A中
MOVX A, @Ri; ((Ri))→(A) 寄存器Ri指向片外RAM地址中的内容送到累加器A中
MOVX @Ri,A; (A)→((Ri)) 累加器中的内容送到寄存器Ri指向片外RAM地址中

堆栈操作类指令(2条)

这4类指令的作用是把直接寻址单元的内容传送到堆栈指针SP所指的单元中,以及把SP所指单元的内容送到直接寻址单元中。

这类指令只有两条,下述的第一条常 称为入栈操作指令,第二条称为出栈操作指令。

需要指出的是,单片机开机复位后,(SP)默认为07H,但一般都需要重新赋值,设置新的SP首址。入栈的第 一个数据必须存放于SP+1所指存储单元,故实际的堆栈底为SP+1所指的存储单元。
PUSH data; (SP)+1 →(SP),(data)→(SP) 堆栈指针首先加1,直接寻址单元中的数据送到堆栈指针SP所指的单元中
POP data; (SP)→(data),(SP)-1→(SP) 堆栈指针SP所指的单元数据送到直接寻址单元中,堆栈指针SP再进行减1操作

交换指令(5条)

这5条指令的功能是把累加器A中的内容与源操作数所指的数据相互交换。
XCH A,Rn;  (A)←→(Rn)         累加器与工作寄存器Rn中的内容互换
XCH A,@Ri;  (A)←→((Ri))       累加器与工作寄存器Ri所指的存储单元中的内容互换
XCH A,data; (A)←→(data)       累加器与直接地址单元中的内容互换
XCHD A,@Ri; (A3-0)←→((Ri)3-0) 累加器与工作寄存器Ri所指的存储单元中的内容低半字节互换
SWAP A;    (A3-0)←→(A7-4)     累加器中的内容高低半字节互换

16位数据传送指令(1条)

这条指令的功能是把16位常数送入数据指针寄存器。
MOV DPTR,#data16;#dataH→(DPH)   #dataL→(DPL)  16位常数的高8位送到DPH,低8位送到DPL

来源:Bruce Chen

围观 7
599

在进行应用系统的总体设计时,软件设计和硬件设计应统一考虑,相结合进行。当系统的电路设计定型后,软件的任务也就明确了 ,系统中的软件是根据系统功能要求设计的。

一般地讲,软件的功能可分为两大类。

- 一类是执行软件,它能完成各种实质性的功能,如测量,计算,显示,打印,输出控制等;

- 另一类是监控软件,它是专门用来协调各执行模块和操作者的关系,在系统软件中充当组织调度角色。

由于应用系统种类繁多,程序编制者风格不一,因此应用软件因系统而异。尽管如此,作为优秀的系统软件还是有其共同特点和其规律的。

设计人员在进行程序设计时应从以下几个方面加以考虑:

根据软件功能要求,将系统软件分成若干个相对独立的部分,根据它们之间的联系和时间上的关系,设计出合理的软件总体结构,使其清晰,简洁,流程合理。培养结构化程序设计风格,各功能程序实行模块化,子程序化。既便于调试,连接,又便于移植,修改。

建立正确的数学模型。即根据功能要求,描述出各个输入和输出变量之间的数学关系,它是关系到系统性功能好坏的重要因素。

为提高软件设计的总体效率,以简明,直观的方法对任务进行描述,在编写应用软件之前,应绘制出程序流程图。这不仅是程序设计的一个重要组成部分,而且是决定成败的关键部分。从某种意义上讲,多花一份时间来设计程序流程图,就可节约几倍源程序编译调试时间。

要合理分配系统资源,包括ROM,RAM,定时器/计数器,中断源等。其中最关键的是片内RAM分配。对8031来讲,片内RAM指00H-7FH单元,这128个字节的功能不完全相同,分配时应充分发挥其特长,做到物尽其用。

例如:在工作寄存器的8个单元中,R0和R1具有指针功能,是编程的重要角色,避免作为它用,20H—2FH这16个具有寻址功能,用来存放各种标志位。逻辑变量,状态变量等。

设置堆栈区时应事先估算出子程序和中断嵌套的级数及程序中堆栈操作指令使用情况,其大小应留有余量。若系统扩展了RAM存储器,应把使用频率最高的数据缓冲器安排在片内RAM中,以提高处理速度,当RAM资源规划好后,应列出一张RAM资源详细分配表,以备编程查用方便。注意在程序的有关位置处写上功能注 释,提高程序的可读性。

加强软件抗干扰设计,它是提高计算机应用系统可靠性的有力措施。

通过编辑软件编辑出的原程序,必须用编译程序汇编后生成目标代码。如果原程序有语法错误则返回编译过程,修改原文件再继续编译,直到无语法错误为止。这之后就是利用目标码进行程序调试了,在运行中发现设计上的错误再重新修改源程序,如此反复直到成功。

一个单片机应用系统经过总体设计,硬件设计,软件设计,制板,元器件安装后,在系统的程序存储器中放入编制好的应用程序,系统即可运行。但一次性成功几乎是不可能的,多少会出现一些硬件,软件上的错误。这就需要通过调试来发现错误并加以改正。由于单片机在执行程序时是无法控制的,为了能调试程序,检查硬件,软件运行状态,就必须借助某种开发工具模拟用户实际的单片机,并且能随时观察运行的中间过程而不改变运行中原有的数据性能和结果,从而进行模仿现场的真实调试。完成这一模仿工作的开发工具就是单片机仿真器。

来源:广电电器

围观 36
411

用C语言进行MCS51系列单片机程序设计是单片机开发和应用的必然趋势。Keil公司的C51编译器支持经典8051和8051派生产品的版本,通称为Cx51。应该说,Cx51是C语言在MCS51单片机上的扩展,既有C语言的共性,又有它自己的特点。本文介绍的是Cx51程序设计时堆栈的计算方法。   

1.堆栈的溢出问题。MCS51系列单片机将堆栈设置在片内RAM中,由于片内RAM资源有限,堆栈区的范围也是有限的。堆栈区留得太大,会减少其他数据的存放空间,留得太少则很容易溢出。所谓堆栈溢出,是指在堆栈区已经满了的时候还要进行新的压栈操作,这时只好将压栈的内容存放到非堆栈区的特殊功能寄存器(SFR)中或者堆栈外的数据区中。特殊功能寄存器的内容影响系统的状态,数据区的内容又很容易被程序修改,这样一来,之后进行出栈操作(如子程序返回)时内容已变样,程序也就乱套了。因此,堆栈区必须留够,宁可大一些。要在Cx51程序设计中防止堆栈的溢出,要解决两个问题:第一,精确计算系统分配给用户的堆栈大小,假设是M;第二,精确计算用户需要堆栈的大小,假设是N。要求M≥N,下面分别分析这两个问题。

2.计算系统分配给用户的堆栈大小Cx51程序设计中,因为动态局部变量是长驻内存中的,实际上相当于局部静态变量,即使在函数调用结束时也不释放空间(这一点不同于标准C语言)。Cx51编译器按照用户的设置,将所有的变量存放在片内和片外的RAM中。片内变量分配好空间后,将剩下的空间全部作为堆栈空间,这个空间是最大可能的堆栈空间。当然,因为Cx51是一种可以访问寄存器的C语言(特殊功能寄存器),因此可在程序中访问SP,将堆栈空间设置得小一点。不过,一般没有人这么做。

本文只是讨论放在片内RAM的变量。我们把变量分为两种情况:

① 用作函数的参数和函数返回值的局部变量。这种变量尽量在寄存器组中存放。为了讨论方便,假设统一用寄存器组0,具体的地址为0x00~0x07。最多可以传递3个参数,如果参数的个数比较多,就将多余的参数放到内存(0x08以后的地址)中存放。这里,假设每个函数的参数都不大于3个。

② 我们在程序中定义的全局变量,以及不是用作函数的参数和函数返回值的局部变量。以上两种变量在内存中0x08地址以后存放,存放完毕后将堆栈指针SP指向分配了变量的片内RAM的最后一个字节。因为MCS51单片机的堆栈是一种满递增堆栈且堆栈的宽度为8位,所以在需要压栈操作时将堆栈指针先加1,后入栈有效内容。有了以上规则,就可以精确地计算出系统分配给用户的堆栈空间。以求两个数的最大公约数和最小公倍数的函数为例,代码如下:

#include <REG52.H>  

unsigned char max(unsigned char a, unsigned char b);   

unsigned char min(unsigned char a, unsigned char b);   

unsigned char M;  

void main (void)

{   

unsigned char  n;   

M = max(12, 9);  

     n = min(12, 9);   

}  

unsigned char max(unsigned char a, unsigned char b)

{   

while(a != b)

{   

if(a > b)   

a = a - b;   

else

  b = b - a;  

}

  return a;  

 }   

unsigned char min(unsigned char a, unsigned char b)

{  

 unsigned char k;   

k = ab/M;  

 return k;   

}  

这段程序中资源的分配情况如下:一个全变量M(无符号字符型)存放最大公约数;主函数中定义一个局部变量n(无符号字符型)存放最小公倍数;求最大公约数的函数unsigned char max(unsigned char a, unsigned char b),有两个参数a和b;求最小公倍数的函数unsigned char min(unsigned char a, unsigned char b),有两个参数a和b,并且定义了一个变量k存放函数的返回值。可以由此计算出系统分配给变量的空间。函数的参数和返回值在工作寄存器组中存放,所以不占用0x08地址以后的空间。系统只给变量M和变量n分配存储空间,这两个变量占两个字节(地址为0x08和0x09),则堆栈指针SP应该指向0x09。Cx51系统编译后生成代码的系统资源占用情况如下:全局变量M的地址为0x08,n的地址为0x09,SP的值为0x09。这与我们的计算结果相符。   

3.计算用户需要堆栈的大小。堆栈区到底留多大才算足够呢? Cx51程序设计中,用户需要堆栈的大小可以从普通子函数和中断子程序的嵌套层数来计算。普通子函数的调用比较简单,每次调用时就是将函数的返回地址保存在堆栈中,这个地址占两个字节。函数嵌套调用时,从最内层的子函数算起,总的堆栈需求字节数为嵌套的层数乘以2。中断子程序的堆栈需求分为两种情况:

① 中断子程序使用中断发生前的寄存器组。在中断发生时,保存中断子程序的返回地址需要2个字节。中断发生后,在中断子程序中系统会自动进行如下操作:将ACC、B、DPH、DPL、PSW、R0~R7共13个寄存器压栈。加上中断返回地址,中断的堆栈需求为15个字节。

② 中断子程序使用自己专用的寄存器组。这种情况下不需要保存R0~R7的内容,可以减少堆栈需求,其他的内容仍需要压栈保护。中断发生时,保存中断子程序的返回地址需要2个字节。中断发生后,在中断子程序中系统会自动进行如下操作:将ACC、B、DPH、DPL、PSW共5个寄存器压栈。加上、中断返回地址,这种堆栈的需求为7个字节。但是这种情况应该注意:如果中断子程序中调用子函数,且函数需要参数和返回值,则被调用的子函数和中断子程序要使用相同的寄存器组,否则会出现不可预料的后果。

以一个温度测试系统为例。系统采用8051作为处理器,温度信号在A/D转换结束后通过外部中断0提醒单片机接收处理。定时中断0作为监控程序,中断周期为20 ms。温度信号可以自动测量(每秒一次)或者手动测量(按测量键后测量),这两种测量方法可以通过控制键切换。中断子程序和普通子函数的嵌套情况为:在定时中断程序中调用显示子程序,外部中断0内部没有函数调用。

部分程序如下:

void int0(void) interrupt 0 using 1

{

   读取转换数据;

   数据处理;  

}

void time0 (void) interrupt 1

{

   计数值重装;   

读键;

按键处理;

leddisp(adat);//显示

}

void main (void)

{

 相关数据初始化和数码显示自检;   

外部中断和定时器初始化设置;

单片机休眠;

}

void leddisp(unsigned char pt)

{

  用串口工作方式0发送显示数据,并经过74LS164转换后静态显示;   

}

接下来分析这段程序的最大堆栈需求。假设定时器0中断时,调用了显示函数void leddisp(unsigned char pt),在调用显示函数时A/D转换结束发生了外部中断0的中断。这时应该是程序对堆栈的最大需求,堆栈的大小是:定时器0(15字节)+显示函数(2字节)+外部中断0(7字节)=24字节。  

结语:通过精确的计算编译系统分配给用户的堆栈空间和用户自己最大的堆栈需求,不仅能从根本上解决堆栈溢出的问题,还可以很好地安排单片机比较紧张的资源。此外,通过在片内存储器存放适量局部变量,还可以有效地提高软件的执行速度。

转自:Bingoo&Echo - 博客园

围观 13
414

如何确定晶振正常起振?

1、判断方法很多,用示波器看波形是最直接的,用数字万用表的电压档测电压也行,因晶振波形的占空比为50%,所以测得的平均电压为1/2Vcc左右,对于51单片机,在使用外置程序存储器的时候还可以测PSEN引脚或P0口引脚的电压或波形,只有晶振电路正常工作,那些引脚才会有信号输出,但现在很少采用片外扩展存储器,所以测晶体两端的电压或波形即可,只是晶振电路设计不良时,测试设备的引入有可能导致停振。

2、晶体两端的电压差不是平均电压差,虽然事实上因外电路的影响,晶体两端的电压可能会有差别,但这不是判断晶振是否起振的依据,也不是晶振电路正常工作的条件。至于一高一低没有工作是指一端为Vcc或接近Vcc,另一端为0或接近0,这时晶振电路当然没有起振,否则50%的占空比势将平均电压拉到1/2Vcc左右,但这么表达是不确切的,搞技术应该尽量定量精确描述。

3、听声音判断晶振是否起振不可靠,晶体的振荡频率远超人耳能够听见的频率上限,有时能够听到反而是有问题的,说明晶体质量不佳,更多的时候,正常工作的晶体是不会发出任何人耳能听到的声音的,有时声音来自外电路元件

4、单片机的两个信号输入脚一个是19脚(XTAL1)一个是18脚(XTAL2)对应单片机内部的电路是高增益放大器,当外面接晶振的时候,19脚对应高增益放大器的输入端,18脚对应高增益放大器的输出端,所以你测量的时候应该是高增益输出端有信号也就是18脚

51单片机振荡电路?

在MCS-51单片机片内有一个高增益的反相放大器,反相放大器的输入端为XTAL1,输出端为XTAL2,由该放大器构成的振荡电路和时钟电路一起构成了单片机的时钟方式。根据硬件电路的不同,单片机的时钟连接方式可分为内部时钟方式和外部时钟方式,如图所示

51单片机不能正常起振的原因
(a)内部方式时钟电路 (b)外接时钟电路

51单片机不能正常起振的原因
内部时钟原理图 (就是一个自激振荡电路)

在内部方式时钟电路中,必须在XTAL1和XTAL2引脚两端跨接石英晶体振荡器和两个微调电容构成振荡电路,通常C1和C2一般取30pF,晶振的频率取值在1.2MHz~12MHz之间。对于外接时钟电路,要求XTAL1接地,XTAL2脚接外部时钟,对于外部时钟信号并无特殊要求,只要保证一定的脉冲宽度,时钟频率低于12MHz即可。

晶体振荡器的振荡信号从XTAL2端送入内部时钟电路,它将该振荡信号二分频,产生一个两相时钟信号P1和P2供单片机使用。时钟信号的周期称为状态时间S,它是振荡周期的2倍,P1信号在每个状态的前半周期有效,在每个状态的后半周期P2信号有效。CPU就是以两相时钟P1和P2为基本节拍协调单片机各部分有效工作的。

转自:单片机精讲吴鉴鹰

围观 39
830

51单片机的外部中断有两种触发方式可选:电平触发和边沿触发。选择电平触发时,单片机在每个机器周期检查中断源口线,检测到低电平,即置位中断请求标志,向CPU请求中断。选择边沿触发方式时,单片机在上一个机器周期检测到中断源口线为高电平,下一个机器周期检测到低电平,即置位中断标志,请求中断。

这个原理很好理解。但应用时需要特别注意的几点:

1)电平触发方式时,中断标志寄存器不锁存中断请求信号。也就是说,单片机把每个机器周期的S5P2采样到的外部中断源口线的电平逻辑直接赋值到中断标志寄存器。标志寄存器对于请求信号来说是透明的。这样当中断请求被阻塞而没有得到及时响应时,将被丢失。换句话说,要使电平触发的中断被CPU响应并执行,必须保证外部中断源口线的低电平维持到中断被执行为止。因此当CPU正在执行同级中断或更高级中断期间,产生的外部中断源(产生低电平)如果在该中断执行完毕之前撤销(变为高电平)了,那么将得不到响应,就如同没发生一样。同样,当CPU在执行不可被中断的指令(如RETI)时,产生的电平触发中断如果时间太短,也得不到执行。

2)边沿触发方式时,中断标志寄存器锁存了中断请求。中断口线上一个从高到低的跳变将记录在标志寄存器中,直到CPU响应并转向该中断服务程序时,由硬件自动清除。因此当CPU正在执行同级中断(甚至是外部中断本身)或高级中断时,产生的外部中断(负跳变)同样将被记录在中断标志寄存器中。在该中断退出后,将被响应执行。如果你不希望这样,必须在中断退出之前,手工清除外部中断标志。

3)中断标志可以手工清除。一个中断如果在没有得到响应之前就已经被手工清除,则该中断将被CPU忽略。就如同没有发生一样。

4)选择电平触发还是边沿触发方式应从系统使用外部中断的目的上去考虑,而不是如许多资料上说的根据中断源信号的特性来取舍。

来源:网络

围观 6
795

页面

订阅 RSS - 51单片机