LBQ691477940 发表于 2012-5-6 11:59:36

用STC系列单片机制作四位可预置计时器


/***************************************************************************************
      用STC系列单片机制作四位可预置计时器_用UM5140X计时器电路改装
#:当使用的EEPROM起始地址不是C2~C7的IC时请到STC_EEPROM.h文件中注释#define EEPROM_C2_C7这行宏定义
功能: 具有二个按键,四位红色共阳极数码显示
P3.6长按 = 模式键、短按 = 移位、保存
P3.7长按 = 开始键、短按 = 加数、停止计时
具有正、倒;计分或计秒预设选择
具有预约、倒计时;开机功能分别输出一个1S低电平信号和一个长通低电平信号

使用说明:
A: 上电前若P1.0对地则为倒计秒//P1.1对地则为倒计分//其它状态组合未定义,建议不使用。
1: 长按P3.6口开关则先重新调出存在eeprom在的计时停止计时显示P***且第一个*以125ms闪烁
此时若依次短按P3.6口开关则移向下一位闪烁,在该位闪烁若短按P3.7口的开关则此位加1。
当第三个*闪烁时如果再短按一次P3.6口开关则保存此次设定值并退出设置状态,显示***。若
在闪烁10s内没有新键按下则自动退出设置状态并不保存此次设定值,显示***。
2: 长按P3.7口开关则开始计时显示***且最后一个dp点闪烁,在计时器运行中若短按P3.7口开关则
停止计时,10s内没有新键按下则自动重载预设值,显示***。
3: 当时间从预设值倒计到 000 时停止倒计时且P1^2口输出1S低电平信号(后自动变高)、P1.3口长通
低电平信号(短按P3.7口开关可关闭),10s后自动重载预设值。
程序设计:LBQ6914779402012-03-10
升级记录:
增加上电自动选择设置正计时与倒计时的初步程序编写。2012-3-11 23:08
P1.0 & P1.1 对地时为倒计秒
P1.0      对地时为倒计分
P1.1对地时为正计秒
P1.0 & P1.1 不接时为正计分       2012-3-11 09:33
/*********************************************************************/
#include<C:\Keil\C51\INC\STC\STC12C5A.h>
#include <intrins.h>
#include <STC_EEPROM.h>
#define nop _nop_()
#define uint unsigned int
#define uchar unsigned char
#define ulong unsigned long
#define SBI(Y,X) Y |= (1 << X) //让Y的X位置1
#define CBI(Y,X) Y &= ~(1 << X) //让Y的X位清0
#define set_keyP1//定义键盘口
#define KEYP3   //定义键盘口
#define set_djm0x00 //P1.0 & P1.1 对地时为倒计秒
#define set_zjm0x01 //P1.0      对地时为倒计分
#define set_djf0x02 //P1.1    对地时为正计秒
//#define set_zjf0x03 //P1.0 & P1.1 不接时为正计分
sbit    P1_4 = P1^4; //位驱动
sbit    P1_5 = P1^5; //位驱动
sbit P1_6 = P1^6; //位驱动
sbit P1_7 = P1^7; //位驱动
sbit OUT1S = P1^2; //到时间后输出约1S低电平
sbit OUTL = P1^3; //到时间后输出低电平,直到短按P3^7口开关键后才变高
sbit DP   = P2^2;
sbit sw_mode = P3^6;//长按为模式键、短按移位
sbit sw_strat = P3^7; //长按为开始键、短按加数
bitDPP = 0, g_shi = 0,up_dn; //闪烁标志位、千位是否显示标志位、正计时与倒计时标志位为1时倒计。
static uchar yiwei = 4;   //用于设置时间值时移位
static uint count = 0;    //定时器0内的计数器
uintset_count,set_time,time1 = 0; //倒计数次数、设置的时间值(倒计时计数器)、正计时计数器
ucharshi,ge,sf,bf,xflash,flash1;//千位、百位、十位、个位、10s后自动退出设置、到时间后输出1s
ucharTtotal;//前面三个字节为设置好的时间,最后一个字节用来判断eeprom是否写过
/*********************函数声明*****************************************/
uchar key_function(void);   //独立按键检测函数声明
void delay_ms(uint n);//12MHZ晶振时毫秒级CPU延时函数
void write_time(void);//转换时间值并写入
void abc_cba(unsigned int time);   //转换时间值供显示
void Read_time(void);//读出存在eeprom内的时间值
/*********************** 共阳极数字编码********************************/
uchar code table[]={
//P2.0=E / P2.1=D / P2.2=DP / P2.3=C / P2.4=G / P2.5=B / P2.6=F / P2.7=A
//   0   12 34   5    6 78   9
0x14,0xd7,0x4c,0x45,0x87,0x25,0x24,0x17,0x04,0x05,0x0e,0xff};//0~9 P共阳极数字编码
/***************************************************************************/

/*********************************************************************
功能:12MHZ晶振时毫秒级CPU延时函数
参数: 1~65535(不可以为0)
结果:占用CPU方式延时与参数值相同的毫秒时间
备注:应用于1T单片机时i<600 ;12T单片机时i<125
XX: LBQ691477940   2011-02-19
/**************************************************************************/
void delay_ms(uint n)
{
uint i;
while(--n !=0)
{
#ifdef EEPROM_C2_C7
for(i = 0;i < 600;i++);
#else
for(i = 0;i < 125;i++);
#endif
}
}
/****************************************************************************
/*********************************************************************
函数名:独立按键检测函数(带参数返回)
全局变量:无
参数说明:无
返回说明:返回按键值
说明:延时消抖,返回值为0xff表示无按键按下或按键不正常
/**********************************************************************************/
uchar key_function(void)
{
uchar data keytemp = 0;//按键值局部变量
if ((set_key & 0x03) != 0x03) //如果有按键按下 0000 0011 03h
         //      0000 0001 01h
{      //      0000 0010 02h
keytemp = set_key & 0x03; //取键值
delay_ms(50);    //延时消抖
if ((set_key & 0x03) == keytemp)
{
   return (keytemp); //返回按键值
}
else
{
   return (0xff);//无正常按键
}
}
return (0xff);      //无按键按下
}
/************************************************************************/
/*********************转换时间值并写入 ***************************/
void write_time(void)
{
Ttotal = time1 / 100;
Ttotal = time1 % 100 / 10;
Ttotal = time1 % 10;
Ttotal = 0x00;
Sector_Erase(EEPROM_ADD);   //擦除扇区
String_write(EEPROM_ADD,4,Ttotal);//重新写入数据
String_Read(EEPROM_ADD,4); //重新设置后读出来备用,目的是更新Read_buf[]数组方便定时器0
}       //中断后time = Read_buf * 100 + Read_buf * 10 + Read_buf;的取值
/*************************************************************************************/
/***************************** 转换时间值供显示 *************************************/
void abc_cba(unsigned int time)
{
ge = time / 100;
sf = time % 100 / 10;
bf = time % 10;
}
/**********************************************************************************/
/************************** 读出存在eeprom内的时间值 *********************/
void Read_time(void)
{   
String_Read(EEPROM_ADD,4);//读出来备用
         //EEPROM_ADD 要写读内部EEPROM的始起地扯
         //4    共要读出4个数据
if(Read_buf != 0)//不为0表示eeprom内容未写过
{
time1 = 138;//给一个为138的初值
write_time();//写入eeprom
}
set_time = Read_buf * 100 + Read_buf * 10 + Read_buf;
if(up_dn)time1 = set_time;//如果为倒计送设置的值
else time1 = 0;   //如果为正计送0
}
/******************************************************************************/

//*******************************主程序******************************************
void main()
{
uint keynum;//长按计数缓存区
bit key = 0 ;//sw_mode键长按标志位/正计、倒计
SP = 0x60; //堆栈指针

/******** 注意STC89系列没有此时钟频率分频功能故只能有 12M晶振******************/

//CLK_DIV = 0x00; //系统时钟
CLK_DIV = 0x01;//系统时钟/2
//CLK_DIV = 0x02; //系统时钟/4
//CLK_DIV = 0x03; //系统时钟/8
//CLK_DIV = 0x04; //系统时钟/16
//CLK_DIV = 0x06; //系统时钟/32
//CLK_DIV = 0x06; //系统时钟/64
//CLK_DIV = 0x07; //系统时钟/128

/*********************************************************************************/
set_key = 0xff;
switch (key_function())
{
case set_djm:
{
   up_dn = 1;//正计时与倒计时标志位、为1时倒计。
   set_count = 20;//P1.6对地倒计秒
   break;
}
case set_djf:
{
   up_dn = 1;//正计时与倒计时标志位、为1时倒计。
   set_count = 1200; //P1.7对地倒计分
   break;
}
case set_zjm:
{
   up_dn = 0;//正计时与倒计时标志位、为1时倒计。
   set_count = 20;//P1.6对地倒计秒
   break;
}
/* case set_zjf:
{   
   break;
} */
default:
{
   up_dn = 0;//正计时与倒计时标志位、为1时倒计。
   set_count = 1200; //P1.7对地倒计分
   break;
}
}
/***************************************************************************/
TMOD = 0x11;   //设置定时器0/1为工作方式1(0001 0001)
//write_time();//写入eeprom
TH0 = (65536 - 50000) / 256;
TL0 = (65536 - 50000) % 256;
TH1 = (65536 - 2500) / 256;
TL1 = (65536 - 2500) % 256;
ET1 = 1;
TR1 = 1;
ET0 = 1;
EA = 1;   //开定时器0中断、启动定时器0、开总中断
OUT1S = 1; //到时间后输出约1S低电平,上电后置1
OUTL = 1; //到时间后输出约低电平,上电后置1
Read_time(); //读出存在eeprom内的时间值
if(up_dn) time1 = set_time;
abc_cba(time1);//转换时间值供显示

/*********************长按为模式键、短按移位**************************************/
while(1)
{
if ((KEY & 0xc0) == 0x80) //如果有按键按下 1100 0000 C0h
         //      1011 1111 80h
{
   delay_ms(20); //延时消抖
   while ((KEY & 0xc0) == 0x80) //长按为模式键、短按移位
   {
    keynum++;
    delay_ms(10);//防止干扰
    if (keynum > 200) //如果是长按sw_mode模式键则
    {
   TR0 = 0;//停止计时器0
   DPP = 0;//关闭小数点闪烁标志位
   key = 1;//置sw_mode键有长按标志位
   g_shi = 1;//千位是否显示标志位置位
   yiwei = 0;
   xflash = 0;   //每按一次清0一次让其永远达不到10s的退出值
   time1 = Read_buf * 100 + Read_buf * 10 + Read_buf;//重调回之前值
   if(up_dn) abc_cba(set_time);//如果为倒计时转换set_time时间值供显示
   else abc_cba(time1); //如果为正计时转换time1时间值供显示
    }
   }
   if (keynum > 200)
   {
    keynum = 0;
   }   
   else if(keynum >= 10) //防止干扰
   {
    keynum = 0;   
    if(key)//如果sw_mode键有长按标志位
    {
   TR0 = 0;
   DPP = 0;   //关闭小数点闪烁标志位
   xflash = 0;   //每按一次清0一次让其永远达不到10s的退出值
   yiwei++;
   if(yiwei >= 3)
   {
      yiwei = 4;//让移位功能失效
      key = 0;//关闪sw_mode键有长按标志位
      g_shi = 0;//关闭千位是否显示标志位
      write_time(); //转换时间值并写入
      if(!up_dn)time1 = 0; //如果为正计时则保存完后恢复time1为 000
   }
    }
   }
}
/**************** 长按为开始键、短按加数、停止计时************************************/
if ((KEY & 0xc0) == 0x40) //如果有按键按下 1100 0000 C0h
{       //      0111 1111 40h
   delay_ms(20); //延时消抖
   while ((KEY & 0xc0) == 0x40) //长按为开始键、短按加数、停止计时
   {         
    keynum++;
    delay_ms(10);//防止干扰
    if (keynum > 200) //如果长按sw_strat开始键则
    {
   if((set_time !=0)||(up_dn == 0))//set_time=0未重置数时不能启动倒计时或为正计时模式时启动计时器
   {
      Read_time(); //启动前先更新set_time的值供定时器0中断程序参考
      count = 0;//定时器0内的计数器清0保持每次时间一样
      TR0 = 1;//开启计时器0
   }   
    }
   }
   if (keynum > 200)
   {
    keynum = 0;
   }   
   else if(keynum >= 10) //防止干扰
   {
    keynum = 0;
    TR0 = 0; //关闪计时器0
    DPP = 0; //关闭小数点闪烁标志位
    xflash = 0; //每按一次清0一次让其永远达不到10s的退出值
    OUTL = 1; //短按P3^7口开关键后才变高   
    switch(yiwei)
    {
   case 0:
   {
      ge++;
      if(ge >= 10) //如果大于10清0
      ge = 0;
      break;
   }
    case 1:
   {
      sf++;
      if(sf >= 10) //如果大于10清0
      sf = 0;
      break;
   }
    case 2:
   {
      bf++;
      if(bf >= 10) //如果大于10清0
      bf = 0;
      break;
   }
   default:
   {      
      break;
   }      
    }
    set_time = time1 = ge * 100 + sf * 10 + bf; //将调节后的值转换为time值
   }
}
if(up_dn) abc_cba(set_time);//如果为倒计时转换set_time时间值供显示
else abc_cba(time1); //如果为正计时转换time1时间值供显示   
}
}
/**************************************************************************/
/*****************************************************************************
函数功能:定时器中断
入口参数:
出口参数:
/****************************************************************************/
void timer0(void) interrupt 1
{   
CY = 0;
TL0 +=(65536 - 50007) % 256;//中断响应时间同步修正/重装初值(低8位修正值)
TH0 = (65536 - 50000) / 256 +(unsigned char)CY;//高8位初值修正/重装初值(高8位修正值)
count++;
if (count == set_count) //50ms x 20 = 1秒钟
{      //50ms x 1200 = 1分钟
count = 0x00;
time1++;
set_time--;
if(!set_time)   //如果已倒计为0则重读eeprom内的值
{
   TR0 = 0;
   flash1 = 0;   //到时间后先将其清0保证1s输出的时间
   DPP = 1;   //这里置1经下面取反后则为0关闭小数点闪烁标志位
   OUT1S = OUTL = 0; //输出低电平直到短按P3^7口开关键后才变高
}      //到时间后输出约1S低电平
}
DPP = ~DPP;//烁标志位取反一次
xflash = 0;//让其永远达不到10s的退出值
}
/******************************************************************/
/******************************************************************/
void timer1(void) interrupt 3
{
static uchar led;
uchar flash;
bit flas;
TH1 = (65536 - 2500) / 256;
TL1 = (65536 - 2500) % 256;
flash++;
if(flash == 50)//2.5ms x 50 = 125ms
{
flash = 0;
flas = !flas; //跟据移位值来使某位闪焕
xflash++;
if(!OUT1S)//从OUT1S输出为低时才++
flash1++;
}
if(flash1 == 8)//125ms x 8 = 1s 后自动变为高电平
{
OUT1S = 1;//到时间后输出约1S低电平
}
if(xflash == 80) //125ms x 80 = 10s 后自动退出设置状态
{
xflash = 0;//清0
yiwei = 4;//让跟据移位值来使某位闪焕功能失效
if(up_dn)
set_time = Read_buf * 100 + Read_buf * 10 + Read_buf;//如果为倒计时重调回之前值
else
time1 = 0;//如果为倒计时则清0
DPP = 0;//关闭小数点闪烁标志位
g_shi = 0;//关闭千位是否显示标志位
}
switch(led)
{
case 0:
{
   P1_7 = 0;
   if(g_shi)
   P2 = table; //不显示千位
   else
   P2 = table; //显示千位
   P1_4 = 1;
   led = 1;
   break;
}
case 1:
{
   if((flas)&&(yiwei == 0))//跟据移位值来使某位闪焕
   P2 = table; //不显示
   else
   P2 = table; //显示个位
   P1_4 = 0;
   P1_5 = 1;
   led = 2;
   break;
}
case 2:
{
   if((flas)&&(yiwei == 1))//跟据移位值来使某位闪焕
   P2 = table; //不显示
   else
   P2 = table; //显示十分位
   P1_5 = 0;
   P1_6 = 1;
   led = 3;
   break;
}
case 3:
{
   if((flas)&&(yiwei == 2))//跟据移位值来使某位闪焕
   P2 = table; //不显示
   else
   P2 = table; //显示百分位
   if(DPP)
    DP = 0;//直接打开小数点
   else
    DP = 1;//直接关闭小数点
   P1_6 = 0;
   P1_7 = 1;
   led = 0;
   break;
}
default:break;
}
}
/******************************************** END********************************/

辉Se天空 发表于 2012-5-6 13:40:32

这个好。。。。。
页: [1]
查看完整版本: 用STC系列单片机制作四位可预置计时器