数字秒表+普中51单片机+江科大自化协

发布时间:2024-08-08  

1 系统框图

 

2 实验现象

   

 一上电,数码管显示时间为00-00-00,即分钟-秒钟-Mini秒,范围为00-00-00——59-59-99,计时精度为0.01秒,能正确地进行计时,同时能记录一次时间,并在下一次计时后对上一次计时时间进行查询。当按键Key1按下时,秒表开始运行,再按下时,秒表停止;每按下一次,状态翻转一次;当按键Key2按下时,数码管显示时间清0;当按键Key3按下时,单片机将数码管显示的数值保存在AT24C02中,掉电不丢失;当按键Key4被按下时,单片机从AT24C02中读取数据,并显示在数码管上。


3 参考程序


3.1 主程序

#include

#include "timer0.h"

#include "key.h"

#include "Nixie.h"

#include "delayms.h"

#include "at24c02.h"


unsigned char KeyNum;

unsigned char Min,Sec,MiniSec;

unsigned char RunFlag;


void main()

{

    timer0_init();

    while(1)

    {

        KeyNum=key();

        if(KeyNum==1)            //K1按键按下

        {

            RunFlag=!RunFlag;    //启动标识位翻转

        }    

        if(KeyNum==2)            //K2按键按下

        {

            Min=0;                //时间清0

            Sec=0;

            MiniSec=0;

        }    

        if(KeyNum==3)            //K3按键按下

        {

            AT24C02_WriteByte(0,Min);    //将分写入AT24C02的地址0

            delayms(5);

            AT24C02_WriteByte(1,Sec);    //将秒写入AT24C02的地址1

            delayms(5);

            AT24C02_WriteByte(2,MiniSec);//将Mini秒写入AT24C02的地址2

            delayms(5);

        }

        if(KeyNum==4)            //K3按键按下

        {

            Min=AT24C02_ReadByte(0);    //读出AT24C02数据

            Sec=AT24C02_ReadByte(1);

            MiniSec=AT24C02_ReadByte(2);

        }

        Nixie_SetBuf(1,Min/10);        //设置显示缓存,显示数据

        Nixie_SetBuf(2,Min%10);

        Nixie_SetBuf(3,11);

        Nixie_SetBuf(4,Sec/10);

        Nixie_SetBuf(5,Sec%10);

        Nixie_SetBuf(6,11);

        Nixie_SetBuf(7,MiniSec/10);

        Nixie_SetBuf(8,MiniSec%10);

    }

}


/**

  * @brief  秒表驱动函数,时间运行,在中断中调用

  * @param  无,MiniSec:0-99, Sec:0-59, Min:0-59

  * @retval 无

  */

void Sec_Loop(void)

{

    if(RunFlag)

    {

        MiniSec++;

        if(MiniSec>=100)

        {

            MiniSec=0;

            Sec++;

            if(Sec>=60)

            {

                Sec=0;

                Min++;

                if(Min>=60)

                {

                    Min=0;

                }

            }

        }

    }


}


void timer0_routine() interrupt 1

{

    static unsigned int T0Count1,T0Count2,T0Count3;

    TL0=0x66;        //设置定时初始值,1ms,@11.0592MHz

    TH0=0xFC;        //设置定时初始值,1ms,@11.0592MHz

    T0Count1++;

    if(T0Count1>=20)

    {

        T0Count1=0;

        key_loop();        //20ms调用一次按键驱动函数

    }

    

    T0Count2++;

    if(T0Count2>=2)

    {

        T0Count2=0;

        Nixie_Loop();    //2ms调用一次数码管驱动函数

    }

    T0Count3++;

    if(T0Count3>=10)

    {

        T0Count3=0;

        Sec_Loop();        //10ms调用一次数秒表驱动函数

    }

}


3.2 按键扫描函数(定时器扫描按键,20ms一次,不断扫描)


#include

#include "delayms.h"


sbit key1 = P3^1;

sbit key2 = P3^0;

sbit key3 = P3^2;

sbit key4 = P3^3;


unsigned char Key_Num;


/**

  * @brief  获取按键键码

  * @param  无

  * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下

  */

unsigned char key(void)

{

    unsigned char Temp=0;

    Temp=Key_Num;

    Key_Num=0;

    return Temp;

}


/**

  * @brief  获取独立按键键码

  * @param  无

  * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0

  */

unsigned char key_getstate()

{

    unsigned char KeyNumber = 0;

    if(key1==0){KeyNumber=1;}

    if(key2==0){KeyNumber=2;}

    if(key3==0){KeyNumber=3;}

    if(key4==0){KeyNumber=4;}

    return KeyNumber;

}


/**

  * @brief  按键驱动函数,在中断中调用

  * @param  无

  * @retval 无

  */

void key_loop(void)

{

    static unsigned char NowState,LastState;

    LastState=NowState;            //按键状态更新

    NowState=key_getstate();    //获取按键当前状态

    //如果上个时间点按键按下,当前时间点未按下,则是按键释放瞬间,以此避免消抖和松手检测

    if(LastState==1 && NowState==0)

    {

        Key_Num=1;

    }

    if(LastState==2 && NowState==0)

    {

        Key_Num=2;

    }

    if(LastState==3 && NowState==0)

    {

        Key_Num=3;

    }

    if(LastState==4 && NowState==0)

    {

        Key_Num=4;

    }

}


#ifndef _key_h_

#define _key_h_


unsigned char key();

void key_loop(void);


#endif


3.3 数码管驱动函数(定时器扫描数码管,2ms不断扫描)


#include

#include "delayms.h"    


//数码管显示缓存区,其中10为不显示,对应Nixietable[10]=0x00

unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};    


//数码管段码表,0-9,不显示,-

unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};


/**

  * @brief  设置显示缓存区

  * @param  Location 要设置的位置,范围:1~8

  * @param  Number 要设置的数字,范围:段码表索引范围

  * @retval 无

  */

void Nixie_SetBuf(unsigned char Location,Number)

{

    Nixie_Buf[Location]=Number;

}


/**

  * @brief  数码管扫描显示

  * @param  Location 要显示的位置,范围:1~8

  * @param  Number 要显示的数字,范围:段码表索引范围

  * @retval 无

  */

void Nixie_Scan(unsigned char Location,Number)

{

    P0=0x00;                //段码清0,消影

    switch(Location)        //位码输出

    {

        case 1:P2_4=1;P2_3=1;P2_2=1;break;

        case 2:P2_4=1;P2_3=1;P2_2=0;break;

        case 3:P2_4=1;P2_3=0;P2_2=1;break;

        case 4:P2_4=1;P2_3=0;P2_2=0;break;

        case 5:P2_4=0;P2_3=1;P2_2=1;break;

        case 6:P2_4=0;P2_3=1;P2_2=0;break;

        case 7:P2_4=0;P2_3=0;P2_2=1;break;

        case 8:P2_4=0;P2_3=0;P2_2=0;break;

    }

    P0=NixieTable[Number];    //段码输出

}


/**

  * @brief  数码管驱动函数,在中断中调用

  * @param  无

  * @retval 无

  */

void Nixie_Loop(void)

{

    static unsigned char i=1;

    Nixie_Scan(i,Nixie_Buf[i]);

    i++;

    if(i>=9){i=1;}

}


#ifndef __NIXIE_H__

#define __NIXIE_H__


void Nixie_SetBuf(unsigned char Location,Number);

void Nixie_Scan(unsigned char Location,Number);

void Nixie_Loop(void);


#endif


3.4 定时器函数(T0)


#include


/**

  * @brief  定时器0初始化,1毫秒@11.0592MHz

  * @param  无

  * @retval 无

  */

void timer0_init(void)        //1毫秒@11.0592MHz

{

    TMOD &= 0xF0;    //设置定时器模式,1111_0000,&,高四位保留,低四位清零

    TMOD |= 0x01;    //设置定时器模式,0000_0001,|,高四位保留,设置模式为T0

    TL0 = 0x66;        //设置定时初始值,1ms,@11.0592MHz

    TH0 = 0xFC;        //设置定时初始值,1ms,@11.0592MHz

    TF0 = 0;        //清除TF0标志

    TR0 = 1;        //定时器0开始计时

    ET0=1;            //打开定时器T0中断开关

    EA=1;            //打开中断系统总开关

    PT0=0;            //设置T0中断优先级,低

}


#ifndef _timer0_h_

#define _timer0_h_


    void timer0_init(void);

    

#endif


3.5 I2C驱动函数


#include


sbit I2C_SCL=P2^1;

sbit I2C_SDA=P2^0;


/**

  * @brief  I2C通信开始

  * @param  无

  * @retval 无

  */

void I2C_Start(void)

{

    I2C_SCL=1;    //空闲状态

    I2C_SDA=1;    //空闲状态

    I2C_SDA=0;

    I2C_SCL=0;

}


/**

  * @brief  I2C通信结束

  * @param  无

  * @retval 无

  */

void I2C_Stop(void)

{

    I2C_SDA=0;    

    I2C_SCL=1;    //回到空闲状态

    I2C_SDA=1;    //回到空闲状态

}


/**

  * @brief  I2C主机向从机发送一个字节,SCL为同步信号,低电平写数据

  * @param  Byte 要发送的字节

  * @retval 无

  */

void I2C_SendByte(unsigned char Byte)

{

    unsigned char i;

    for(i=0;i<8;i++)            //一个字节,8bit

    {

        I2C_SDA=Byte&(0x80>>i);    //SCL为低电平,主机为发送器,写数据

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>