13.1 单片机空闲与掉电模式的应用
1. 空闲模式
当单片机进入空闲模式时,除CPU处于休眠状态外,其余硬件全部处于活动状态,芯片中程序未涉及的数据存储器和特殊功能寄存器中的数据在空闲模式期间都将保持原值。假若定时器正在运行,那么计数器寄存器中的值还将会增加。单片机在空闲模式下可由任一个中断或硬件复位唤醒。需要注意,使用中断唤醒单片机时,程序从原来停止处继续运行;当使用硬件复位唤醒单片机时,程序将从头开始执行。
让单片机进入空闲模式的目的通常是为了降低系统的功耗,举个很简单的例子,数字万用表在正常使用时表内部的单片机处于正常工作模式,当不用却又忘记关掉万用表的电源时,大多数数字万用表在等待数分钟后,若没有人为操作,便会自动将液晶显示关闭,以降低系统功耗,类似这种功能的实现通常就是使用了单片机的空闲模式或掉电模式以STC89系列单片机为例,当单片机正常工作时的功耗通常为4mA~7mA,进入空闲模式时其功耗降至2mA,进入掉电模式时功耗可降至0.1A以下。
2. 掉电模式
当单片机进入掉电模式(也称休眠模式)时,外部晶振停振,CPU、定时器、串行口全部停止工作,只有外部中断继续工作。使单片机进入掉电模式的指令将成为休眠前单片机执行的最后一条指令。进入掉电模式后,芯片中程序未涉及的数据存储器和特殊功能寄存器中的数据都将保持原值。可用外部中断低电平触发,或下降沿触发中断,或硬件复位模式唤醒单片机。需要注意,使用中断唤醒单片机时,程序从原来停止处继续运行;当使用硬件复位唤醒单片机时,程序将从头开始执行。
正常工作电流>空闲模式电流>休眠模式电流
在 TX-1C 实验板上完成如下描述:开启两个外部中断,设置低电平触发中断,用定时器计数并且显示在数码管的前两位,当计数到5时,使单片机进入空闲(眠)模式,同时关闭定时器,当单片机响应外部中断后,从空闲(休眠)式返回,同时开启定时器。程序代码如下:
//开启两个外部中断,设置低电平触发中断,用定时器计数并且显示在数码管的前两位
//当计数到5时,使单片机进入空闲(眠)模式,同时关闭定时器
//当单片机响应外部中断后,从空闲(休眠)式返回,同时开启定时器。
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned intsbit dula=P2^6; //声明U1锁存器的锁存端
sbit wela=P2^7; //声明U2锁存器的锁存端
sbit led1=P1^0; //第一位发光二极管(0亮1灭)
sbit beep=P2^3; //声明蜂鸣器引脚(0响1灭)uchar code dula_table[]={0x3f,0x06,0x5b,0x4f, //0,1,2,30x66,0x6d,0x7d,0x07, //4,5,6,70x7f,0x6f,0x77,0x7c, //8,9,10,110x39,0x5e,0x79,0x71 //12,13,14,15
};uchar code wela_table[]=
{0xdf,0xef,0xf7,0xfb,0xfd,0xfe //从右向左数第一到六位
};uchar num,a,b;
uint wei;void main()
{uchar num1; void init(); //初始化操作void delayxms(uint xms);void display();init();while(1){if(num>=20){num=0;num1++;if(num1==6){ET0=0; //在下一句话进入休眠模式前要关闭定时器以等待外部中断的产生PCON=0x02;}a=num1/10;b=num1%10;}display();}
}void init() //初始化操作
{dula=0; //初始化,段选置0wela=0; //初始化,片选置0led1=1; //初始化,二极管灯灭beep=1; //初始化,蜂鸣器不响TMOD=0x01; //设置定时器0的工作方式为1(0000 0001)TH0=(65536-50000)/256; //装初值TL0=(65536-50000)%256; //装初值EA=1; //打开总中断ET0=1; //打开定时器1中断EX0=1; //开启外部中断0EX1=1; //开启外部中断1TR0=1;//启动定时器1}void delayxms(uint xms)
{uint x,y;for(x=xms;x>0;x--)for(y=124;y>0;y--);
} void display()
{void wedu(uchar dula_num,uchar wela_num);for(wei=0;wei<2;wei++){switch(wei){case 0: wedu(b,wei);break;case 1: wedu(a,wei);break;} }
}void wedu(uchar dula_num,uchar wela_num)
{wela=1; //打开U2锁存端P0=wela_table[wela_num]; //送入U2锁存端wela=0; //关闭U2锁存端P0=0xc0; //消影,防止P0残留电位信号干扰段选dula=1; //打开U1锁存端P0=dula_table[dula_num]; //送入段选信号dula=0; //关闭U1锁存端P0=0xff; //消影,防止P0残留电位信号干扰片选delayxms(1);
}void T0_time() interrupt 1 //中断程序
{TH0=(65536-50000)/256; //装初值TL0=(65536-50000)%256; //装初值num++;led1=~led1;beep=~beep;
} void ex_int0() interrupt 0 //外部中断0服务程序
{PCON=0; //进入外部中断服务程序后首先将PCON中原先设定的休眠控制位清除ET0=1; //开启定时器0
}void ex_int1() interrupt 2
{PCON=0;ET0=1;
}
13.2 “看门狗”概念及其应用
在由单片机构成的系统中,由于单片机的工作有可能受到来自外界电磁场的干扰,造成程序的跑飞,从而陷入死循环,程序的正常运行被打断,由单片机控制的系统便无法继续工作,这样会造成整个系统陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于监测单片机程序运行状态的芯片,俗称“看门狗”(Watch Dog)。
加入看门狗电路的目的是使单片机可以在无人状态下实现连续工作,其工作过程如下:看门狗芯片和单片机的一个I/O 引脚相连,该I/O 引脚通过单片机的程序控制,使它定时地往看门狗芯片的这个引脚上送入高电平(或低电平),这一程序语句是分散地放在单片机其他控制语句中间的,一旦单片机由于干扰造成程序跑飞而陷入某一程序段进入死循环状态时,给看门狗引脚送电平的程序便不能被执行到,这时,看门狗电路就会由于得不到单片机送来的信号,便对它与单片机复位引脚相连的引脚送出一个复位信号,使单片机复位,从而使单片机从程序存储器的起始位置重新开始执行程序,这样便实现了单片机的自动复位。通常,看门狗电路是通过将一个专门的看门狗芯片连接到单片机来实现的,不过这样会给电路设计带来复杂性,STC系列单片机内部自带了看门狗,通过对相应的特殊功能寄存器的设置就可实现看门狗的应用。STC89系列单片机内部有一个专门的看门狗定时器寄存器Watch Dog Timer 寄存器,其功能见下。
看门狗定时器寄存器(WDR_CONTR)
STC系列单片机看门狗定时器寄存器在特殊功能寄存器中的字节地址为E1H,不能位寻址,该寄存器用来管理STC单片机的看门狗控制部分,包括启/停看门狗、设置看门狗溢出时间等。单片机复位时该寄存器不一定全部被清 0,在 STC 下载程序软件界面上可设置复位关看门狗或只有停电关看门狗的选择,可根据需要做出适合自己设计系统的选择。其各位的定义如下表所示。
EN WDT—看门狗允许位,当设置为1时,启动看门狗。
CLR WDT—看门狗清0位,当设为1时,看门狗定时器将重新计数。硬件自动将此位清 0。
IDLE WDT—看门狗IDLE模式位,当设置为1时,看门狗定时器在单片机的“空闲模式”计数;当该位清0时,看门狗定时器在单片机的“空闲模式”时不计数。
PS2,PS1,PS0—看门狗定时器预分频值,12MHz晶振条件下不同值对应预分频数如下表所示。
看门狗溢出时间与预分频数有直接的关系,公式如下:
看门狗溢出时间=(N*预分频数*32768)/晶振频率
式中,N表示 STC单片机的时钟模式。STC单片机有两种时钟模式,一种是单倍速,也就是12 时钟模式,该时钟模式下,STC 单片机与其他公司 51 单片机具有相同的机器周期,即 12个振荡周期为一个机器周期;另一种是双倍速,又称6时钟模式,在该时钟模式下,STC单片机比其他公司的 51单片机运行速度快一倍。在下载程序软件界面上有单倍速与双倍速的设置选择,大家可自行下载测试程序运行速度。预分频数的值由PS2,PS1和PS0的组合确定如上表所示。晶振频率即为当前系统的时钟频率。
在应用看门狗时,需要在整个大程序的不同位置喂狗,每两次喂狗的时间间隔一定不能小于看门狗定时器的溢出时间,否则程序会不停地复位。
在 TX-1C 实验板上实现如下描述:程序启动后设定看门狗溢出时间为 2s,然后点亮第一个发光二极管,稍延时一会,然后熄灭发光二极管,使程序进入等待死循环状态,并且在死循环中大约每隔1s喂狗一次,看程序运行是否正常。程序代码如下:
//程序启动后设定看门狗溢出时间为 2s,然后点亮第一个发光二极管,稍延时一会
//然后熄灭发光二极管,使程序进入等待死循环状态,并且在死循环中大约每隔1s喂狗一次
//看程序运行是否正常。程序代码如下:
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned intsfr WDT_CONTR=0Xe1; //定义STC单片机中新加入的看门狗寄存器sbit led1=P1^0; //第一位发光二极管(0亮1灭)
sbit beep=P2^3; //声明蜂鸣器引脚(0响1灭)void main()
{void delayxms(uint xms);WDT_CONTR=0X35; led1=0;beep=0;delayxms(500);led1=1;beep=1;while(1){delayxms(1000);//WDT_CONTR=0X35; //若将此举删掉,则看门狗定时器溢出后单片机复位从头执行,小灯闪烁}
}void delayxms(uint xms)
{uint x,y;for(x=xms;x>0;x--)for(y=124;y>0;y--);
}
13.3 用软件实现系统复位
用户应用程序在运行过程中,有时会有特殊需求,需要实现单片机系统复位(热启动之一),传统的 8051单片机由于硬件上不支持此功能,用户必须用软件模拟实现,实现起来比较麻烦。STC单片机增加了相应的硬件功能,内部的ISPAP控制寄存器ISP_CONTR便可以实现此功能。用户只需简单控制ISP_CONTR 特殊功能寄存器中的 SWBS 和 SWRST 两位就可以实现系统复位。
ISP/IAP控制寄存器(ISPCONTR)
STC 单片机 ISP/IAP 控制寄存器在特殊功能寄存器中的字节地址为 E7H,不能位寻址,该寄存器用来管理和 ISP/IAP 相关的功能设定及是否软件复位等。单片机复位时该寄存器全部被清 0。其各位的定义如下表所示。
ISPEN—ISP/IAP 功能允许位。0禁止ISP/IAP编程改变 Flash。1允许编程改变 Flash。SWBS-软件选择从用户应用程序区启动(0),还是从ISP程序区启动(1)。要与SWRST直接配合才可以实现。
SWRST—0不操作;1产生软件系统复位,硬件自动清0。
WT2,WT1, WTO—ISP/AP 编程时设定 CPU等待的最长时间。ISP/IAP 编程时可对 Flash进行读操作、写操作、擦除操作。当进行这些操作时,时钟将被CPU锁定只进行这些操作而不同的操作将会耗费 CPU 不同的时间,这里我们通过人为设定可以给 CPU 一个最长的等待时间,若在此时间段内相应的操作未完成,数据将丢失或错误。下表给出了芯片厂商推荐的等待时间参考表。
SWBS与SWRST组合情况如下:
从用户应用程序区(AP 区)软件复位并切换到用户应用程序区(AP区)开始执行程:
ISP_CONTR=00100000B,SWBS=0(选择AP区),SWRST=1(软复位)
从系统 ISP 监控程序区软件复位并切换到用户应用程序区(AP区)开始执行程序:
ISP CONTR=00100000B,SWBS=0(选择AP区),SWRST=1(软复位)
从用户应用程序区(AP 区)软件复位并切换到系统ISP监控程序区开始执行程序:
ISP CONTR=01100000B,SWBS=1(选择ISP区),SWRST=1(软复位)
从系统 ISP 监控程序区软件复位并切换到系统ISP监控程序区开始执行程序:
ISP CONTR=01100000B,SWBS=1(选择ISP区),SWRST=1(软复位)。
本复位是整个系统复位,所有的特殊功能寄存器都会复位到初始值,I/O 口也会被初始化,用户应用程序区(AP区)指仅仅是用户自己编写的程序区。ISP 监控程序区 ISP 区是指芯片出厂时就已经固化在单片机内部的一段程序,STC单片机可以进行ISP 串行下载程序,这就是因为芯片在出厂时已经在单片机内部固化了ISP引导码程序首次上电时先会从ISP 区开始执行代码,体现在实际实验中时,就是我们在下载程序时先要单击下载软件界面上的下载,然后再开启单片机电源,当单片机检测到上位机有下载程序的需要时,便启用ISP下载功能给单片机下载程序。若经过短暂的时间没有检测到上位机有下载程序的需求,单片机便会从用户应用程序区(AP区)开始执行代码。
下面通过一个例子实现程序运行中的突然复位。
在 TX-1C实验板上实现如下描述:在数码管前两位显示以秒递增的数,增加到10 时,利用STC单片机的软件复位功能让单片机复位。程序代码如下:
//在数码管前两位显示以秒递增的数
//增加到10 时,利用STC单片机的软件复位功能让单片机复位。
#include<reg52.h>
#define uchar unsigned char
#define uint unsigned intsfr ISP_CONTR=0xe7; //定义ISP/IAP控制寄存器sbit dula=P2^6; //声明U1锁存器的锁存端
sbit wela=P2^7; //声明U2锁存器的锁存端
sbit led1=P1^0; //第一位发光二极管(0亮1灭)
sbit beep=P2^3; //声明蜂鸣器引脚(0响1灭)uchar code dula_table[]={0x3f,0x06,0x5b,0x4f, //0,1,2,30x66,0x6d,0x7d,0x07, //4,5,6,70x7f,0x6f,0x77,0x7c, //8,9,10,110x39,0x5e,0x79,0x71 //12,13,14,15
};uchar code wela_table[]=
{0xdf,0xef,0xf7,0xfb,0xfd,0xfe //从右向左数第一到六位
};uchar num,a,b;
uint wei;void main()
{uchar num1; void init(); //初始化操作void delayxms(uint xms);void display();init();while(1){if(num>=20){num=0;num1++;led1=~led1;beep=~beep;if(num1==10){ISP_CONTR=0x20; //用软件复位到用户应用程序区(AP区),重新开始执行程序}a=num1/10;b=num1%10;}display();}
}void init() //初始化操作
{dula=0; //初始化,段选置0wela=0; //初始化,片选置0led1=1; //初始化,二极管灯灭beep=1; //初始化,蜂鸣器不响TMOD=0x01; //设置定时器0的工作方式为1(0000 0001)TH0=(65536-50000)/256; //装初值TL0=(65536-50000)%256; //装初值EA=1; //打开总中断ET0=1; //打开定时器1中断TR0=1;//启动定时器1
}void delayxms(uint xms)
{uint x,y;for(x=xms;x>0;x--)for(y=124;y>0;y--);
} void display()
{void wedu(uchar dula_num,uchar wela_num);for(wei=0;wei<2;wei++){switch(wei){case 0: wedu(b,wei);break;case 1: wedu(a,wei);break;} }
}void wedu(uchar dula_num,uchar wela_num)
{wela=1; //打开U2锁存端P0=wela_table[wela_num]; //送入U2锁存端wela=0; //关闭U2锁存端P0=0xc0; //消影,防止P0残留电位信号干扰段选dula=1; //打开U1锁存端P0=dula_table[dula_num]; //送入段选信号dula=0; //关闭U1锁存端P0=0xff; //消影,防止P0残留电位信号干扰片选delayxms(1);
}void T0_time() interrupt 1 //中断程序
{TH0=(65536-50000)/256; //装初值TL0=(65536-50000)%256; //装初值num++;
}
13.4 内部扩展RAM的应用
RAM 是在程序运行中存放随机变量的数据空间,51单片机默认的内部RAM只有128B;52单片机增加至 256B;STC89C52增加到512B;STC89C54,55,58,516等增加到1280B.对于编程者来说,一个芯片的RAM 空间越大,写起程序来就越容易得心应手,不会总考虑RAM 不够用怎么办,连过多的变量都不敢定义。
如果定义一个变量后,不对这个变量进行初始化,这个变量默认的初值就是 0,其实这个结论是需要一定的条件的,在用Kei1编写程序时,总程序中所有变量占用的字节之和要小于128B,并且存储器模式为small模式的前提下,对定义的变量不进行初始化时,编译器会默认将变量值设定为0。一旦程序中的总变量超过128B,必须对所有变量进行初始化,否则没有被初始化的变量默认值将是不确定的。当变量总和超过128B时,还必须要在编译器中重新设定存储器的存储模式。
存储器模式一共有三种,分别为small,compact和large 模式,在Keil编译器中有可选项,选项表如下图所示。它决定了没有明确指定存储类型的变量、函数参数等数据的默认存储区域。如果在某些函数中需要使用非默认的存储模式,也可以使用关键字直接说明。下面分别介绍这三种模式。
1. small 模式
small 模式中,所有默认变量均装入单片机内部128B的RAM中,当定义类似如“uchar a;”“float b;”等变量时,这些变量都装入内部128B RAM 中。使用该模式的优点是访问速度快;缺点是空间有限,且对堆栈的空间分配比较少,难以把握,碰到递归调用的时候需要小心。这种模式只适用于小程序。
2. compact 模式
compact模式中,所有默认变量均位于单片机内部256B的RAM中,和在small 模式中使用关键字 pdata 来定义变量的效果相同,如“uchar pdata a[100];”在该模式下,程序总变量空间不得超过 256B。对于只有128B 的单片机,使用此模式定义变量超过128B 时,程序将出错。优点是空间较 small 模式宽裕,速度较 smal 慢,但较large 模式要快,是一种中间状态。
3. large 模式
在1arge模式中,所有默认变量可放在多达64KB的RAM区,包括内部RAM和外部RAM这和使用关键字 xdata 来定义变量的效果相同。该模式的优点是空间大,可存变量多;缺点是访问速度慢,尤其对于两个以上的多字节变量访问速度来说更是如此。
STC单片机要访问扩展RAM时,可直接用以下方法。
首先,在编译器中设置存储器模式为large 模式,如上图所示。
其次,为安全起见,当程序中定义多于128B的其他变量时,最好申明为xdata型,如“ucharxdata table[100];"意思为定义一个 100个字节容量的数组,将其在扩展 RAM 中分配存储空间。另外,一定要注意,在1arge模式下编写程序,定义的变量总数千万不要超过对应单片机的内部最大 RAM 字节数,因为即使超过了,编译器也不会提示错误,但是程序必定会出错。
13.5 扩展P4口的应用
对于 POFP-44 和 PLCC-44 封装的 STC89 系列单片机增加了4个I/O 口,即 P4口。P4口在特殊功能寄存器的地址为E8H,可以进行位寻址,用户在使用P4口之前需要先定义地址然后就像操作其他I/O 口一样操作 P4 口。
另外,P4 口的 P4.2和P4.3 引脚新增加了两个外部中断作为它们的第二功能,使用这两个外部中断时,和使用 P3.2和P3.3引脚的外部中断方法相同。P4.2(INT2)的中断向量入口地址为33H,中断序号为6;P4.3(INT3)的中断向量地址为3BH,中断序号为7;P4口的外部中断由另一个新增加的扩展中断控制寄存器来设置。
扩展中断控制寄存器(XICON)
STC89系列单片机扩展中断控制寄存器在特殊功能寄存器中的字节地址为C0H,能位寻址,该寄存器用来管理扩展中断开启、关闭及中断优先级设定。单片机复位时该寄存器全部被清 0。其各位的定义如下表所示。
PX3—置位表明外部中断3的优先级为高,但优先级最终由中断优先级寄存器IP,扩展中断优先级寄存器 IPH 和扩展中断控制寄存器XICON中的PX3与 PX2共同决定,如[PX3H,PX3]=[0,0],[0,1],[1,0],[1,1]代表不同的优先级。
EX3—置1,允许外部中断3中断;清0,禁止外部中断3中断。
IE3—外部中断3中断请求标志位,中断条件成立后,IE3=1,该位由硬件自动清0。
IT3—置1时,外部中断3为下降沿触发中断;清0时,为低电平触发中断。
PX2—置位表明外部中断2的优先级为高,但优先级最终由中断优先级寄存器IP,扩展中断优先级寄存器 IPH 和扩展中断控制寄存器XICON中的PX3,PX2共同决定,如[PX2H,PX2]=[0,0],[0,1],[1,0],[1,1]代表不同的优先级。
EX2—置1,允许外部中断2中断;清0,禁止外部中断2中断。
IE2—外部中断2中断请求标志位,中断条件成立后,IE2=1,该位由硬件自动清0。
IT2—置1时,外部中断2为下降沿触发中断;清0时,为低电平触发中断,
扩展中断优先级寄存器(IPH)
STC89系列单片机扩展中断优先级寄存器在特殊功能寄存器中的字节地址为B7H,不能位寻址。该寄存器与中断优先级寄存器、扩展中断控制寄存器 XICON 中的 PX3 和 PX2 位共同决定单片机所有中断的最终优先级。单片机复位时该寄存器全部被清 0。其各位的定义如下表所示。
关于扩展中断优先级寄存器的用法是这样的:假定[PX3H,PX3]=[1,1],[PX2H,PX2]=[1,1],其他中断的组合均不是[1,1],则这时外部中断 3、外部中断2的优先级比其他中断优先级都高,因为不可能有两个最高。所以再来看系统默认的优先级,在软件设置优先级相同时,外部中断2的优先级更高。用此方法可设置任一个中断的优先级。使用 P4 口编写C语言代码时,需要在程序开始处加上如下语句,以后便可像操作其他I/O 口一样操作 P4 口。
sfr P4=0xe8;
注:STC89LE516AD,STC89LE516X2,STC89LE58AD,STC89LE54ADr的P4地址为COH。
13.6 内部E2PROM的应用
STC89C51,STC89C52内部都自带有2KB的EPROM;54,55和58都自带有16KB的E2PROM,STC单片机是利用IAP技术实现的E2PROM,内部Flash擦写次数可达100000次以上,下面先来介绍 ISP 与IAP 的区别和特点。
ISP与IAP介绍
ISP(In System Programable)是指在系统编程。通俗地讲,就是片子已经焊在板子上不用取下,就可以简单而方便地对其进行编程。比如,我们通过计算机给STC单片机下载程序,或给AT89S51单片机下载程序,这就是利用了ISP技术。
IAP(In Application Programable )是指在应用编程。就是片子提供一系列的机制(硬件软件上的),当片子在运行程序时可以提供一种改变 Fash 数据的方法。通俗点讲,也就是说程序自己可以往程序存储器里写数据或修改程序。这种方式的典型应用就是用一小段代码来实现程序的下载,实际上单片机的 ISP 功能就是通过 IAP 技术来实现的,即片子在出厂前就已经有一段小的 boot程序在里面,片子上电后,开始运行这段程序,当检测到上位机有下载要求时,便和上位机通信,然后下载数据到存储区。大家要注意,千万不要尝试去擦除这段ISP引导程序,否则恐怕以后再也下载不了程序了
STC单片机内部有几个专门的特殊功能寄存器负责管理ISP/IAP 功能的,见下表。
ISP_DATA-ISP/AP 操作时的数据寄存器。ISPAP从Flash 读出的数据放在此处,向Flash写入的数据也放在此处。
ISP ADDRH-ISP/IAP操作时的地址寄存器高八位。
ISP ADDRL-ISP/IAP操作时的地址寄存器低八位,
ISP_CMD-ISPIAP 操作时的命令模式寄存器,须命令触发寄存器触发方可生效。其模式设置如下表所示。
程序在系统 ISP 程序区时可以对用户应用程序区/数据 Flash 区(E2PROM)进行字节读/字节编程/扇区擦除;程序在用户应用程序区时,仅可以对数据Flash区(E2PROM)进行字节读/字节编程/扇区擦除。STC89C51RC/RD+系列单片机出厂时已经固化有ISP 引导码,并设置为上电复位进入ISP程序区,并且出厂时就已完全加密。
ISP TRIG-ISP/IAP操作时的命令触发寄存器
在ISPEN(ISPCONTR.7)=1时,对ISPTRIG先写入46H,再写入B9H,ISPAP 命令才会生效。
STC89C52RC,STC89LE52RC单片机内部可用的DataFlash(E2PROM)的地址如下表所示,其他型号单片机请查阅相关资料。
每个扇区为 512B,建议大家在写程序时,将同一次修改的数据放在同一个扇区,方便修改,因为在执行擦除命令时,一次最少擦除一个扇区的数据,每次更新数据前都必须擦除原数据方可重新写入新数据,不能直接在原来数据基础上更新内容。
在 TX-1C实验板上实现如下描述:操作 STC单片机自带的 E’PROM,存储一组按秒递增的两位数据,并且将数据实时显示在数码管上,数据每变化一次就往EPROM中写入一次,当关闭实验板电源并再次开启电源时,从EPROM中读取先前存储的数据,接着递增显示。程序代码如下:
#include<reg52.h>
#include<intrins.h>#define uchar unsigned char
#define uint unsigned int
#define RdCommand 0x01 //定义ISP操作命令
#define PrgCommand 0x02
#define EraseCommand 0x03
#define Error 1
#define Ok 0
#define WaitTime 0x01 //定义CPU等待时间sfr ISP_DATA=0xe2; //寄存器声明
sfr ISP_ADDRH=0xe3;
sfr ISP_ADDRL=0xe4;
sfr ISP_CMD=0xe5;
sfr ISP_TRIG=0xe6;
sfr ISP_CONTR=0xe7;sbit dula=P2^6; //声明U1锁存器的锁存端
sbit wela=P2^7; //声明U2锁存器的锁存端uchar code dula_table[]={0x3f,0x06,0x5b,0x4f, //0,1,2,30x66,0x6d,0x7d,0x07, //4,5,6,70x7f,0x6f,0x77,0x7c, //8,9,10,110x39,0x5e,0x79,0x71 //12,13,14,15
};uchar code wela_table[]=
{0xdf,0xef,0xf7,0xfb,0xfd,0xfe //从右向左数第一到六位
};uchar num,a,b,num1;
uint wei;void delayxms(uint xms)
{uint x,y;for(x=xms;x>0;x--)for(y=124;y>0;y--);
} void display()
{void wedu(uchar dula_num,uchar wela_num);for(wei=0;wei<2;wei++){switch(wei){case 0: wedu(b,wei);break;case 1: wedu(a,wei);break;} }
}void wedu(uchar dula_num,uchar wela_num)
{wela=1; //打开U2锁存端P0=wela_table[wela_num]; //送入U2锁存端wela=0; //关闭U2锁存端P0=0xc0; //消影,防止P0残留电位信号干扰段选dula=1; //打开U1锁存端P0=dula_table[dula_num]; //送入段选信号dula=0; //关闭U1锁存端P0=0xff; //消影,防止P0残留电位信号干扰片选delayxms(1);
}//打开ISP,IAP功能
void ISP_IAP_enable(void)
{EA=0; //关中断ISP_CONTR=ISP_CONTR&18; //0001,1000ISP_CONTR=ISP_CONTR|WaitTime; //写入硬件延时ISP_CONTR=ISP_CONTR|0x80; //ISPEN=1
}//关闭ISP,IAP功能
void ISP_IAP_disable(void)
{ISP_CONTR=ISP_CONTR&0x7f; //ISPEN=0ISP_TRIG=0x00;EA=1; //开中断
}//公用的触发代码
void ISPgoon(void)
{ISP_IAP_enable(); //打开ISP,IAP功能ISP_TRIG=0x46; //触发ISP_IAP命令字节1ISP_TRIG=0xb9; //触发ISP_IAP命令字节2_nop_();
}//字节读
uchar byte_read(uint byte_addr)
{ISP_ADDRH=(uchar)(byte_addr>>8); //地址赋值ISP_ADDRL=(uchar)(byte_addr&0x00ff);ISP_CMD=ISP_CMD&0xf8; //清除低3位ISP_CMD=ISP_CMD|RdCommand; //写入读命令ISPgoon(); //触发执行ISP_IAP_disable(); //关闭ISP,IAP功能return(ISP_DATA); //返回读到的数据
}//扇区擦除
void SectorErase(uint sector_addr)
{uint iSectorAddr;iSectorAddr=(sector_addr&0xfe00); //取扇区地址ISP_ADDRH=(uchar)(iSectorAddr>8); //地址赋值ISP_ADDRL=0x00;ISP_CMD=ISP_CMD&0xf8; //清除低3位ISP_CMD=ISP_CMD|EraseCommand; //擦除命令3ISPgoon(); //触发执行ISP_IAP_disable(); //关闭ISP,IAP功能
}//字节写
void byte_write(uint byte_addr,uchar original_data)
{ISP_ADDRH=(uchar)(byte_addr>>8); //地址赋值ISP_ADDRL=(uchar)(byte_addr&0x00ff);ISP_CMD=ISP_CMD&0xf8; //清除低3位ISP_CMD=ISP_CMD|PrgCommand; //写命令2ISP_DATA=original_data; //写入数据准备ISPgoon(); //触发执行ISP_IAP_disable(); //关闭ISP,IAP功能
}void init() //初始化操作
{dula=0; //初始化,段选置0wela=0; //初始化,片选置0TMOD=0x01; //设置定时器0的工作方式为1(0000 0001)TH0=(65536-50000)/256; //装初值TL0=(65536-50000)%256; //装初值EA=1; //打开总中断ET0=1; //打开定时器1中断TR0=1;//启动定时器1
}void main()
{ init();num1=byte_read(0x2000); //程序开始时读取EEPROM中的数据if(num1>=60) num1=0;while(1){if(num>=20){num=0;num1++;SectorErase(0x2000); //擦除扇区byte_write(0x2000,num1); //重新写入数据if(num1==60){num1=0;}a=num1/10;b=num1%10;}display();}
}void T0_time() interrupt 1 //中断程序
{TH0=(65536-50000)/256; //装初值TL0=(65536-50000)%256; //装初值num++;
}
13.7 STC89系列单片机内部A/D应用
STC89LE52AD,54AD,58AD,516AD这几款89系列的STC单片机内部自带有8路8位的 A/D 转换器,分布在P1口的8位上,当时钟在 40MHz 以下时,每 17个机器周期可完成一次 A/D 转换。
与 AD 相关的几个寄存器如下表所示。
P1 ADC EN—P1.x口的 AD 使能寄存器相应位设置为1时,对应的P1.x口作为AD转换使用,内部上拉电阻自动断开。ADC_CONTR-A/D 转换控制寄存器。
ADC START—AD转换启动控制位,设置为1时,AD开始转换。ADC FLAG-AD 转换结束标志位,当AD转换完成后,ADCFLAG=1。CHS2,CHS1,CHS0-模拟输入通道选择,其设置如下表所示ADC DATA-AD 转换结果寄存器。AD转换结果的计算公式如下:
结果=256xUin/Vcc
式中,Uin为模拟输入通道输入电压,Vcc为单片机实际工作电压,用单片机工作电压作为模拟参考电压。
13.8 STC12系列单片机内部A/D应用
STC12C2052AD系列单片机自带8路8位AD转换器,STC12C5410AD系列单片机带8路 10 位 A/D 转换器,它们的 A/D 转换口都在 P1 口(P1.0~P1.7),转换速度均可达到 100kHz(10万次/秒)。该8路 AD 转换器为电压输入型,可做温度检测、电池电压检测、按键扫描频谱检测等。上电复位后P1口为弱上拉型IO口,用户可以通过软件设置将8路中的任一路设置为 A/D 转换,不需作为 AD 使用的口可继续作为 IO 口使用。
需作为 A/D 使用的口需先将其设置为开漏模式或高阻输入,在P1M0,P1M1 寄存器中对相应的位进行设置,其端口配置模式如下表所示。
下表为STC12系列单片机与A/D相关的寄存器。
ADC_CONTR—寄存器的 CHS2,CHS1,CHS0位为 A/D 通道选择位,与 STC89 系列用法相同。
ADC START—转换启动控制位,设置为1时,开始转换,转换结束后清0。
ADC FLAG—模转换结束标志位,当AD 转换完成后,ADC FLAG=1,必须由软件清 0。不管是 AD转换完成后由该位申请产生中断,还是由软件查询该标志位判断 AD 转换是否结束,当A/D 转换完成后,ADCFLAG都将置1,该位必须由软件清0。
SPEED1,SPEED0-转换速度控制位,见下表。
ADC_POWER—电源控制位。置0关闭 A/D 转换器电源;置1打开 AD 转换器电源。如果软件设置进入空闲模式,该电源会自动关闭。启动 AD 转换前一定要确认 AD 电源已打开,AD 转换结束后关闭 AD 电源可降低功耗,也可不关闭。初次打开内部 AD 转换模拟申源,需适当延时,等内部模拟电源稳定后,再启动 AD 转换,建议启动 AD 转换后,在 AD转换结束之前,不改变任何 IO 口的状态,这样有利于提高 AD 转换精度。
ADC DATA,ADC LOW2—A/D 转换结果寄存器,对于STC12C5412AD 系列单片机来说,因为其 A/D为10位精度,ADC_DATA寄存器存储转换结果的高8位,ADCLOW2的低2位存储转换结果的最低2位:STC12C2052AD系列单片机AD转换精度只有8位,固无ADC_LOW2寄存器。
A/D 转换结果计算公式如下:
转换结果(ADC DATA[7:0],ADC LOW2[1:0])=1024*Uin/Vcc
式中,Uin为模拟输入通道输入电压,Vcc为单片机实际工作电压,用单片机工作电压作为模拟参考电压。
取 ADC_DATA 的8位为 A/D 转换的高8位,取 ADC_LOW2的低2位为 A/D 转换的低2位,则为10位精度;如果舍弃ADCLOW2的低2位,只用ADCDATA寄存器的8位,则 AD 转换结果为8位精度。
结果 ADC_DATA[7:0]=256*Uin/Vcc
关于 AD 转换模块参考电压源的一点说明:
STC12C5410AD和STC12C2052AD系列单片机的参考电压源是输入工作电压Vcc,所以.般不用外接参考电压源。如 7805 的输出电压是5V,但实际电压可能是4.88V或4.96V,用户需要精度比较高的话,可在出厂时将实际测出的工作电压值记录在单片机内部的E-PROM里面,以供计算。如果有些用户的Vc不固定,如电池供电,电池电压在5.3~4.2V之间漂移则 Vcc不固定,就需要在8路 AD 转换的一个通道外接一个稳定的参考电压源,来计算出此时的工作电压 Vcc,再计算出其他几路 AD转换通道的电压。如可在 AC 转换通道的第7通道外接一个1.25V(或1V…)的基准参考电压源,由此求出此时的工作电压Vcc,再计算出其他几路 A/D 转换通道的电压。
13.9 STC12系列单片机的PCA/PWM介绍(略)
PCA(Programable Counter Array)是可编程计数器阵列,PWM(Pulse Width Modulation)是脉宽调制,这两项技术在单片机内部都是由定时器/计数器和比较/捕捉模块组成,如下图所示。
13.10 STC12系列单片机的SPI接口介绍(略)
STC12C5410AD及STC12C2052AD系列单片机还提供另一种高速串行通信接口--SPI接口。SPI是一种全双工、高速、同步的通信总线,它有两种操作模式:主模式和从模式。在主模式中,它支持高达3Mbps的速率( 系统时钟频率为12MHz时,如果CPU主频采用20MHz~36MHz,则其速率可更高,而从模式时速度无法太快,8以内较好),同时还具有传输完成标志和写冲突标志保护。
13.11 STC12系列单片机的“576MHz”超速运行(略)
通常在使用 AT及其他公司的51单片机时,外接时钟频率为12MHz或11.0592MHz不过大多数单片机所支持的最高时钟频率为24MHz,它们的机器周期还要将时钟周期12分频。而STC89系列单片机时钟频率最高可支持80MHz,STC12系列单片机时钟频率最高可支持48MHz,并且该系列单片机有1T指令模式,即执行指令时,一个时钟周期就是一个机器周期,比通用8051内核单片机执行速度快12倍,12X48MHz=576MHz,由此可知STC单片机的运行速度的确快得惊人。
参考资料:
[1] 郭天祥. 新概念51单片机C语言教程:入门、提高、开发、拓展全攻略[M]. 北京: 电子工业出版社, 2009.