找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 20882|回复: 7
打印 上一主题 下一主题
收起左侧

第7章 单片机内部比较器

  [复制链接]
跳转到指定楼层
楼主
51单片机轻松入门—基于STC15W4K系列(C语言版)
李友全
编著:http://www.51hei.com/bbs/dpj-37954-1.html


7章 单片机内部比较器与DataFlash存储器

1 比较器

7.1.1 比较器结构图
7.1.2 寄存器说明
7.1.3 比较器测试实验
2 DataFlash存储器
单片机内部比较器的重要用途是对单片机供电电源进行监测,当监测到外部

断电时立即产生中断,通知CPU保存程序运行过程中的重要数据(由于电路板 上有电容存储能量,外部断电时单片机还可继续运行一个短暂时间),我们选择 数据保存位置为单片机内部DataFlash存储器,举个例子:某产品需要进行寿命 测试,产品运行一个循环单片机记录一次,但运行过程中可能会突然断电,要求 重新上电后寿命测试继续运行,并且要求实际运行次数准确,这就需要用到单片 机内部比较器与DataFlash存储器断电瞬间存储数据的功能。

1 STC15W系列单片机内部比较器
7 7.1.1 比较器结构图
STC15W系列的比较器结构如图7-1所示。

7.1.2 寄存器说明

表7-1 比较控制寄存器1:CMPCR1 (复位值是:0000 0000B)

D7
D6
D5
D4
D3
D2
D1
D0
位名称
CMPEN
CMPIF
PIE
NIE
PIS
NIS
CMPOE
CMPRES
n CMPEN:比较器模块使能位。1:使能比较器模块;0:禁用比较器模块,比较器的电源

关闭。

n CMPIF :比较器中断标志位。当比较器的比较结果由LOW变成HIGH时,若是PIE被设置成

1,那么内部的一个 叫做CMPIF_p的位会被设置成1,当比较器的比较结果由HIGH变成LOW 时,若是NIE被设置成1,那么内部的一个 叫做CMPIF_n的位会被设置成1,当CPU去读此中断 标志位CMPIF时,会读到(CMPIF_p || CMPIF n),当CPU对此中断标志位CMPIF写0后, CMPIF_p以及CMPIF_n都会被清除为O,而中断产生的条件是[(EA==1)&& (((PIE=l)&&(CMPIF_p==l)) || ((NIE==1)&&(CMPIF_n==l)))],CPU进入中断函数后,并不 会自动清除此CMPIF标志,用户必须用软件写”0”去清除它。


n PIE:比较器上升沿中断使能位。1:使能比较器由LOW变HIGH的事件引发CMPIF_p

= 1产生中断,0:禁用比较器由LOW变HIGH的事件引发CMPIF_p = 1产生中断。

n NIE:比较器下降沿中断使能位。1,使能比较器由HIGH变LOW的事件引发CMPIF_n

= 1产生中断;0:禁用比较器由HIGH变LOW的事件引发CMPIF_n = 1产生中断。

n PIS: 比较器正极选择位。1:选择ADC_CONTR[2:0]所选择到的ADC输入引脚作为比 较器的正极输入源; 0:选择外部P5.5为比较器的正极输入源。

n NIS: 比较器负极选择位。1:选择外部管脚P5.4为比较器的负极输入源,0:选择 内部约1.27V的BandGap参考电压为比较器的负极输入源。

n CMPOE:比较结果输出控制位。1,使能比较器的比较结果输出到P1.2, 0,禁止比 较器的比较结果输出。

n CMPRES:比较器比较结果标志位。1: CMP+的电平高于CMP-的电平(或内部1.27V 的BandGap参考电压); 0: CMP+的电平低于CMP-的电平(或内部1.27V的BandGap 参考电压)。此bit是一个“只读”的bit;软件对它做写入的动作没有任何意义, 软件所读到的结果是“经过延时控制后的结果”,而非模拟比较器的直接输出结 果。

表7-2 比较控制寄存器2:CMPCR2(地址为E7H,复位值是:0000 1001B)

nINVCMPO:比较器输出取反控制位。l:比较器取反后再输出到P1.2,0:比较器正常输

出,比较器的输出是经过延时控制后的结果,而非模拟比较器的直接输出结果。

n DISFLT:去除比较器输出的0.luS 延时控制。1:去除比较器输出的O.luS延时控制(可以 让比较器速度有少许提升);0:比较器的输出有O.luS延时控制。

n LCDTY[5:0]:比较器输出端 Level-Change control的延时时钟选择,假设设置值为 bbbbbb,当比较器由LOW变HIGH后必须侦测到HIGH持续至少bbbbbb个时钟,此芯片线路才认定 比较器的输出是由LOW转成HIGH,如果在bbbbbb个时钟内,模拟比较器的输出又回复到LOW, 此线路认为甚么都没发生,视同比较器的输出一直维持在LOW,当比较器由HIGH变LOW,必须 侦测到LOW持续至少bbbbbb个时钟,此线路才认定比较器的输出是由HIGH转成LOW,如果在 bbbbbb个时钟内,比较器的输出又回复到HIGH,此线路认为甚么都没发生,视同比较器的输 出一直维持在HIGH. 若是设定成000000,代表没有Level-Change Control。

7.1.3 比较器测试实验
下图是STC15W系列典型掉电检测电路,正常供电使用5V电压,我们假设VCC从5V下跌到4.3V时 进入掉电保存处理程序,P5.5作比较器的信号输入正端,比较器的信号输入负端在程序中设置为
1.27V基准电压。
例7.1 利用IAP15W4K58S4的比较器实现掉电检测功能 (中断方式),要求当VCC低于大约4.3V时点亮一个LED,
高于大约4.3V时LED熄灭,本实验要求VCC使用0—5V直流 可调电源(最大不要超过5.5V),也可使用第14章的精
密电压表模块直接下载这里的代码测试。


#include "STC15W4K.H"    // 注意宏定义后面没分号
sbit LED=P4^1;

void main()
{
CMPCR1=0xB0;   // 10110000,开比较器中断
CMPCR2=0x00;   // 0000 0000,比较器只延时0.1uS
EA=1;
while(1);
}



void cmp()interrupt 21
{
CMPCR1 &=0xBF; // 清除中断标志, 1011 1111
LED = CMPCR1&0x01; // 将比较器结果CMPRES输出到测试口显示
}
实验结果:当VCC < 4.20V时LED点亮,当VCC > 4.20V时LED熄灭。对程序稍作修改,不使用中 断也能达到相同的实验结果。

例7.2 利用IAP15W4K58S4的比较器实现掉电检测功能(查询方式)。
void main()
{
CMPCR1=0x80; // 10000000
// D5(PIE) =0; 关闭比较器输出由LOW变HIGH的事件引发CMPIF_p = 1产生中断
// D4(NIE) =0; 关闭比较器输出由HIGH变LOW的事件引发CMPIF_n = 1产生中断

CMPCR2=0x00; // 0000 0000,比较器只延时0.1uS

while(1)
{
LED = CMPCR1&0x01; // 将比较器结果CMPRES输出到测试口显示
}
}

2 DataFlash存储器与相关实验

STC15F2K60S2单片机内部集成了1K字节的数据Flash存储器(也称作DataFlash)可当作EEPROM使 用,地址范围是0000H~03FFH,与程序Flash存储器空间是分开的,这1K字节的数据Flash存储器分 为2个扇区,每个扇区包含512字节,对应的地址范围分别为:第一扇区:0000H~01FFH,第二扇区:

0200H~03FFH,数据Flash存储器的擦除操作是按扇区进行的,数据Flash存储器擦写次数在10万次 以上,可用于保存一些需要在应用过程中修改并且断电不丢失的一些参数数据。STC15W4K32S系列单 片机原理与STC15F2K60S2系列完全相同。
对于IAP15W4K58S4芯片,内部没有数据Flash,是将程序存储器Flash的剩余空间当作EEPROM使 用,IAP的意思是“在应用编程”, 即在程序运行时“程序存储器Flash”可由程序自身进行擦写, 正是因为有了IAP,从而可以将数据写入到程序存储器Flash中,使得数据如同烧入的程序一样,掉 电不丢失,IAP可将所有程序存储器Flash空间当作EEPROM修改,因此要注意不要把自己的有效程序 擦除掉了,型号不是IAP开头的芯片无法对“程序存储器Flash”进行任何操作。

Flash存储器的操作提示:

① 对Flash存储器进行字节编程时,只能将1改为0,或1保持为1、0保持为0。如果该字节是

11111111B,则可将其中的1编程为0,如果该字节中有的位为0,要将其改为1,则须先将整个扇区擦 除,因为只有“扇区擦除”才可以将0变为1。

② 如果在一个扇区中存放了大量的数据,某次只需要修改其中的一个字节或一部分字节时,则另外 的不需要修改的数据须先读出放在单片机的RAM中,然后擦除整个扇区,再将需要保留的数据和需修 改的数据一并写回该扇区中,这时,每个扇区使用的字节数越少越方便(不需读出一大堆需保留数 据)。

③ 同一次修改的数据放在同一扇区中,不是同一次修改的数据放在另外的扇区,扇区不一定用满, 这样,可不需读出保护。

7.2.2 DataFlash操作实验(断电瞬间存储数据) 例7.3 STC15F2K60S2单片机内部DataFlash读写测试

本程序上电时先擦除DataFlash的第1个扇区,然后将第1个扇区的前半扇区与后半扇区分别写入数 据0~255,然后读出数据并判断与写入的数据是否一致,并通过串口助手显示程序运行过程中的数 据与最终结果是否正常,R/C时钟频率22.1184MHz,串口通信波特率9600。

程序主要使用到2个模块文件FLASH.H与FLASH.C,在程序移植过程中,FLASH.H里需要定义R/C时钟 频率和FLASH存储单元地址,FLASH.C无需作任何更改。
#define SYSclk 22118400L // 定义CPU实际运行的系统时钟

#define EEP_address 0x0000 // 主程序从0000地址开始读写数据

/////////////////////////// 主程序:Flash_Test.C ///////////////////////////////

#include "FLASH.H"

#include "uart_debug.h" void main()

{

unsigned char a;

unsigned int i;

UART_init(); // 波特率:9600 /22.1184MHZ(占用定时器1) UART_Send_Str("开始擦除\n");

EEPROM_SectorErase(EEP_address); // 扇区擦除 UART_Send_Str("擦除完毕\n");

for (i=0; i<512; i++) // 检测是否擦除成功(全FF检测)

{
EEPROM_read_n(EEP_address+i,&a,1); // 地址、数据、长度 UART_Send_StrNum("擦除值:",a) ;
if (a!=0xff) goto Error; // 如果校验错误,则退出
}
UART_Send_Str("开始写入\n");
for (i=0; i<512; i++) // 编程512字节
{
a=i;
EEPROM_write_n(EEP_address+i,&a,1);
//
地址、数据、长度
}
UART_Send_Str("写入完毕\n");
for (i=0; i<512; i++)
//
校验512字节
{
EEPROM_read_n(EEP_address+i,&a,1);
//
地址、数据、长度
UART_Send_StrNum("数据:",a);
if (a!=i%256) goto Error;
//
如果校验错误,则退出
}
UART_Send_Str("读出结束,测试正常");
while (1); Error:
UART_Send_Str("数据错误"); // IAP操作失败
while (1);
}
本程序使用“串口调试助手sscom33”在电脑上显示接收到的数据,文本模式,9600波特率,测
试结果正常,由于接收的数据量较大,其它串口助手可能会出现乱码或开始接收到的数据被后来的
数据覆盖掉而不能完整显示的问题。

例7.5 使用IAP15W4K58S4单片机内部比较器与DataFlash实现断电瞬间存储数据。

说明:IAP15W4K58S4与STC15F2K60S2内部DataFlash唯一不同的只有地址值不一样,为了防 止误写有效程序区,程序编译完成后进入软件调试环境,打开存储器窗口,我们会看到有 效的程序代码都存放在存储单元地址的低端,从原则上来说,凡是地址高端的FLASH空白区 域都可以当做EEPROM使用,对于IAP15W4K58S4芯片,具有58K字节的FLASH存储空间,由于

1K=1024字节,58×1024=59392字节,存储单元地址从0开始算起,地址范围是0~59391, 用16进制表示为0000H~E7FFH,因为每个扇区包含512个字节,59392/512=116个扇区,最 后3个扇区地址范围如表7-6所示,一般我们使用最后3个扇区地址已经够用了,比如本例, 我们使用最后一个扇区,程序中使用代码:

#define EEP_address 0xE600 即可。

表7-6 IAP15W4K58S4单片机内部DataFlash地址末尾3个扇区地址

第114扇区


第115扇区


第116扇区


起始地址
结束地址
起始地址
结束地址
起始地址
结束地址
E200H
E3FFH
E400H
E5FFH
E600H
E7FFH

主要程序代码如下:
#include "PowerDown_save.H"
#include "FLASH.H"
#include "myfun.h"
struct POWER_UP Power_up;
void cmp_init() // 掉电检测比较器初始化
{
CMPCR1=0xB0; // 10110000 ,开比较器中断 CMPCR2=0x00; // 0000 0000,比较器只延时0.1uS EA=1;
}
void cmp()interrupt 21
//
较器中断函数
{



CMPCR1 &=0xBF;
//
清除中断标志, 1011 1111
LED0 = CMPCR1&0x01;
//
将比较器结果CMPRES输出到测试口显示
if (LED0==0)


// 掉电
{



EA=0;
//
防止更高级的中断打断
EEPROM_write_n(EEP_address,(u8 *)&Power_up,sizeof(struct
POWER_UP));
// 掉电保存值
delay1S(); // 延时1秒后确认真的断电还是瞬间电源干扰

while(1)
{
if (CMPCR1&0x01==1)
// ==0,真正掉电,死循环等待程序停止运行
// ==1,供电正常,单片机程序没任何问题,退出循环继续按原程序运行
{
CMPCR1 &=0xBF;
// 清除中断标志, 1011 1111,清除可能多次产生的中断标志,防止中断死机 EA=1;
break;
}
}
}
}
void main()
{
UART_init();//波特率:9600 /22.1184MHZ(占用定时器1)
cmp_init();//掉电检测比较器初始化
EA=0;//防止刚上电读ROM时就产生断电存储操作,可能造成函数重入问题。
EEPROM_read_n(EEP_address,(u8 *)&Power_up,sizeof(struct POWER_UP)); // 读出保存值

EA=1; // 进入正常工作时打开总中断 EEPROM_SectorErase(EEP_address); // 当掉电后电压降落比较快时,在这里先擦除 UART_Send_StrNum("计数值:",Power_up.times);

// 串口输出计数值,波特率:9600 /22.1184MHZ

for(;;)

{

LED0=1; LED1=1; // 熄灭调试指示灯

delay1S();

Power_up.times++; // 秒计时器加1

UART_Send_StrNum("计数值:",Power_up.times);

}

}

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏5 分享淘帖 顶2 踩
回复

使用道具 举报

沙发
ID:243791 发表于 2017-12-27 18:19 | 只看该作者
收藏,借鉴一下,谢谢
回复

使用道具 举报

板凳
ID:884155 发表于 2021-2-14 02:02 | 只看该作者
刚刚开始学习,很需要这方面的资料。谢谢
回复

使用道具 举报

地板
ID:963185 发表于 2021-8-31 15:07 | 只看该作者
新手还看不大懂,谢谢分享!
回复

使用道具 举报

5#
ID:149799 发表于 2021-9-2 07:12 | 只看该作者
通俗易懂,谢谢分享。
回复

使用道具 举报

6#
ID:985401 发表于 2022-1-3 22:34 | 只看该作者
新手还看不大懂,谢谢分享!
回复

使用道具 举报

7#
ID:976070 发表于 2022-1-29 23:54 | 只看该作者
怎么利用IAP存储数据?写数据需要多长时间?
回复

使用道具 举报

8#
ID:969770 发表于 2022-1-30 10:07 | 只看该作者
我有一个想法是VCC做基础 另外一个端口接3-5的电压,电压值变化控制点亮不同状态的LED的灯,类似电量指示器
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表