专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

单片机开源项目之Stopwatch数码管显示-实用秒表

作者:寒竹子   来源:本站原创   点击数:  更新时间:2014年05月02日   【字体:

此程序是在51hei单片机开发板上面做的,如需要移植到自己的电路上,修改相应的端口即可,开发板完整的电路图下载:  点这里 (注意:只需要看相关部分即可,其他部分可以忽略)

/**
  ****************************************************************************************
  *file   :   main.c
  *author :   xr
  *date   :   2014年4月15日10:08:26 - 2014年4月15日12:40:51
  *version:   V1.2.3
  *brief  :   Stopwatch数码管显示-实用秒表   单片机STC89C52RC MCU 晶振 11.0592MHZ
  ****************************************************************************************
  */
#include <reg52.h>

//74HC138
sbit ADDR0 = P1^0;
sbit ADDR1 = P1^1;
sbit ADDR2 = P1^2;
sbit ADDR3 = P1^3;
sbit ENLED = P1^4;
//矩阵按键
sbit KEY_IN_1 = P2^4;//列
sbit KEY_IN_2 = P2^5;
sbit KEY_IN_3 = P2^6;
sbit KEY_IN_4 = P2^7;
//数码管编码
unsigned char code LedTable[] = {
    0xC0,  //"0"
                0xF9,  //"1"
                0xA4,  //"2"
                0xB0,  //"3"
                0x99,  //"4"
                0x92,  //"5"
                0x82,  //"6"
                0xF8,  //"7"
                0x80,  //"8"
                0x90,  //"9"
                0x88,  //"A"
                0x83,  //"B"
                0xC6,  //"C"
                0xA1,  //"D"
                0x86,  //"E"
                0x8E   //"F"
   };
//数码管显示缓冲区
unsigned char LedBuff[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
bit flag10ms = 0;//10ms标志位
bit stopwatchRunning = 0;//秒表启动和停止标志
unsigned char thr0, tlr0;//T0重载值的高低字节
unsigned char decimal = 0;//小数位
unsigned int  integer = 0;//整数位
static unsigned char volatile KeySta[4] = {1, 1, 1, 1}; //保存按键的当前值(static 保存在静态存储区,加上volatile关键字使KeySta[i]的值每次都从静态存储区读取,避免读取错误!)
/****************function definition**********************/
void Initial();
void ConfigTimer0(unsigned long xms);
void StopwatchDisplay();
void KeyAction();
/*主程序main()*/
void main(void)
{
 Initial();
 ConfigTimer0(1);//定时1ms
 while (1)
 {
  KeyAction();       //检测按键动作
  if (flag10ms)      //10ms检测秒表的运行状态,当stopwatchRunning标志=1时进行计时++,否则就停止计时
  {
   flag10ms = 0;
   if (stopwatchRunning)  //10ms刷新秒表计数
   {
    decimal++;
    if (decimal >= 100)
    {
     decimal = 0;
     integer++;
     if (integer >= 10000)
     {
      integer = 0;
     }
    }
    StopwatchDisplay();//分解decimal和integer,存入LedBuff数码管缓冲区 
   }
  }
 }
}
/*程序初始化*/
void Initial()
{
 ADDR3 = 1;
 ENLED = 0;//选择数码管
 P2 = 0xFE;  //选择第四行按键检测(原理图上是第四列)
 P0 = 0xFF;//默认关闭段选
 LedBuff[0] = LedTable[decimal % 10];  //初始显示0.00
 LedBuff[1] = LedTable[decimal / 10 % 10];
 LedBuff[2] = LedTable[integer % 10];
 LedBuff[2] &= 0x7F;//点亮小数点
}
/*配置定时器T0*/
void ConfigTimer0(unsigned long xms)
{
 unsigned long tmp;//32bit 注意:此处必须用unsigned long 数据,因为11059200已经超出了16位unsigned int
 tmp = 11059200/12;//单片机的机器周期频率
 tmp = (tmp * xms) / 1000;//定时xms所需的频率
 tmp = 65536-tmp;//定时xms的初始装载值
 tmp += 18;  //补偿值
 thr0 = (unsigned char)(tmp >> 8);
 tlr0 = (unsigned char)tmp;
 TMOD &= 0xF0;//清零T0控制位
 TMOD |= 0x01;//配置T0工作在方式1,16位可设定定时器
 TH0 = thr0;
 TL0 = tlr0;
 TR0 = 1;
 ET0 = 1;
 EA  = 1;  
}
/*数码管刷新*/
void LedCharRefresh()
{
 static unsigned char j = 0;
 P0 = 0xFF;//消隐
 P1 = (P1 & 0xF8) | j;//先清零P1口低三位,再|j
 P0 = LedBuff[j++];
 if (j > 5)
  j = 0;
}
/*分解decimal和integer,存入LedBuff数码管缓冲区*/
void StopwatchDisplay()
{
 signed char i = 0;
 unsigned char buf[6];
 buf[0] = decimal % 10;
 buf[1] = decimal / 10 % 10;
 buf[2] = integer % 10;
 buf[3] = integer / 10 % 10;
 buf[4] = integer / 100 % 10;
 buf[5] = integer / 1000 % 10;
 for (i = 5; i >= 3; i--)    //去掉无效位
 {
  if (buf[i] == 0)
   buf[i] = 0xFF;
  else
   break;
 }
 //处理有效位
 for (; i >=0; i--)
 {
  buf[i] = LedTable[buf[i]]; //转为实际的数字
 }
 //拷贝
 for (i = 0; i <= 5; i++)
 {
  LedBuff[i] = buf[i];//拷贝到显示缓冲区
 }
 LedBuff[2] &= 0x7F;//点亮小数点
}
/*秒表启动和停止*/
void StopwatchAction()
{
 if (stopwatchRunning)   //当前状态为运行
 {
  stopwatchRunning = 0;  //停止运行
 }
 else
 {
  stopwatchRunning = 1;  //启动
 }
}
/*秒表复位*/
void StopwatchReset()
{
 stopwatchRunning = 0;//停止秒表计时
 decimal = 0;
 integer = 0;//清零
 StopwatchDisplay();//显示
}
/*按键动作*/
void KeyAction()
{
    static unsigned char keybackup[4] = {1, 1, 1, 1};//按键的备份值(前一次按键值) 必须是静态的
 unsigned char i = 0;
 for (i = 0; i < 4; i++)
 {
  if (keybackup[i] != KeySta[i])  //按键的上一次的状态和当前状态不相同,即按键有动作
  {
   if (keybackup[i] != 0) //如果上一次按键的值是1,则此时按键是按下动作
   {
    switch (i)
    {
     case 1: StopwatchReset();  break;   //ESC键秒表复位
     case 2: StopwatchAction(); break; //Enter键控制秒表的启动和停止
     default: break;
    }
   }
   keybackup[i] = KeySta[i];//备份当前按键值
  }
 }
}
/*按键扫描函数*/
void KeyScan()
{
 static unsigned char KeyBuf[4] = {0xFF, 0xFF, 0xFF, 0xFF}; //按键扫描缓冲区
 unsigned char i = 0;
 
 //检测一行按键的状态并存入按键检测缓冲区中
 KeyBuf[0] = (KeyBuf[0] << 1) | KEY_IN_1;
 KeyBuf[1] = (KeyBuf[1] << 1) | KEY_IN_2;
 KeyBuf[2] = (KeyBuf[2] << 1) | KEY_IN_3;
 KeyBuf[3] = (KeyBuf[3] << 1) | KEY_IN_4;
 //更新消抖后按键的值(1ms检测一次,8ms检测)
 for (i = 0; i < 4; i++)
 {
  if ((KeyBuf[i] & 0xFF) == 0x00)
   KeySta[i] = 0;//8次检测按键状态都是0,即按键稳定按下
  else if ((KeyBuf[i] & 0xFF) == 0xFF)
   KeySta[i] = 1;//8次检测按键的状态都是1,即按键稳定弹起
 }
}
/*定时器Timer0中断服务*/
void Timer0_ISP() interrupt 1
{
 static unsigned char counter = 0;//计数器
 TH0 = thr0;
 TL0 = tlr0;//重新装载定时器初值
 counter++;
 LedCharRefresh();//数码管刷新
 KeyScan();   //按键扫描
 if (counter >= 10)
 {
  counter = 0;
  flag10ms = 1;
 }
}
关闭窗口

相关文章