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

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 <REGX52.H>
#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 <reg52.h>
#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 <REGX52.H>
#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 <REGX52.H>

/**
  * @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 <REGX52.H>

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为低电平,主机为发送器,写数据
        I2C_SCL=1;                //SCL为高电平,从机为接收器,读数据
        I2C_SCL=0;                //时序要求,51单片机速度比较慢
    }
}

/**
  * @brief  I2C主机接收从机一个字节,SCL为同步信号,高电平读数据
  * @param  无
  * @retval 接收到的一个字节数据
  */
unsigned char I2C_ReceiveByte(void)
{
    unsigned char i,Byte=0x00;
    I2C_SDA=1;                //主机释放数据线SDA
    for(i=0;i<8;i++)
    {
        I2C_SCL=1;            //主机作为接收器
        if(I2C_SDA) Byte|=(0x80>>i);    //读数据
        I2C_SCL=0;            //从机作为发送器,写数据
    }    
    return Byte;
}

/**
  * @brief  I2C主机发送应答
  * @param  AckBit 应答位,0为应答,1为非应答
  * @retval 无
  */
void I2C_SendAck(unsigned char AckBit)
{
    I2C_SDA=AckBit;
    I2C_SCL=1;
    I2C_SCL=0;
}

/**
  * @brief  I2C主机接收应答位
  * @param  无
  * @retval 接收到的应答位,0为应答,1为非应答
  */
unsigned char I2C_ReceiveAck(void)
{
    unsigned char AckBit;
    I2C_SDA=1;
    I2C_SCL=1;
    AckBit=I2C_SDA;
    I2C_SCL=0;
    return AckBit;
}
#ifndef _i2c_h_
#define _i2c_h_

void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);
    
#endif

3.6 AT24C02控制函数

#include <REGX52.H>
#include "i2c.h"

#define AT24C02_ADDRESS 0xA0

/**
  * @brief  AT24C02写入一个字节
  * @param  WordAddress 要写入字节的地址
  * @param  Data 要写入的数据
  * @retval 无
  */
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{
    I2C_Start();
    I2C_SendByte(AT24C02_ADDRESS);
    I2C_ReceiveAck();
    I2C_SendByte(WordAddress);
    I2C_ReceiveAck();
    I2C_SendByte(Data);
    I2C_ReceiveAck();
    I2C_Stop();
}

/**
  * @brief  AT24C02读取一个字节
  * @param  WordAddress 要读出字节的地址
  * @retval 读出的数据
  */
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
    unsigned char Data;
    I2C_Start();
    I2C_SendByte(AT24C02_ADDRESS);
    I2C_ReceiveAck();
    I2C_SendByte(WordAddress);
    I2C_ReceiveAck();
    
    I2C_Start();
    I2C_SendByte(AT24C02_ADDRESS|0x01);
    I2C_ReceiveAck();
    Data=I2C_ReceiveByte();
    I2C_SendAck(1);
    I2C_Stop();
    return Data;
}
#ifndef _at24c02_h_
#define _at24c02_h_

void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);
    
#endif

3.7 延时函数

#include <intrins.h>

void delayms(unsigned int xms)        //@11.0592MHz
{
    unsigned char i, j;

    while(xms--)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);    
    }
}
#ifndef _delayms_h_
#define _delayms_h_

delayms(unsigned int xms);
    
#endif

4 参考资料

posted @ 2023-01-11 11:13  豌豆茶  阅读(444)  评论(0编辑  收藏  举报