首页 > 其他分享 >数字秒表+普中51单片机+江科大自化协

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

时间:2023-01-11 11:22:31浏览次数:53  
标签:P2 void 51 unsigned char 单片机 自化协 AT24C02 I2C

1 系统框图

 

2 实验现象

 

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 参考资料

标签:P2,void,51,unsigned,char,单片机,自化协,AT24C02,I2C
From: https://www.cnblogs.com/zclv/p/17043191.html

相关文章

  • C51单片机开发环境
    C51单片机开发环境0OS环境1IDE下载Clion2嵌入式插件安装pio插件3嵌入式安装PlatformIOCore我使用的是HomeBrew进行管理brewupdatebrewinstallpl......
  • CapStone/CS5518芯片,MIPI转双通道LVDS可pin√pin替代国腾GM877
    GM8775C型DSI转双通道LVDS发送器产品主要实现将MIPIDSI转单/双通道LVDS功能,MIPI支持1/2/3/4通道可选,最大支持4Gbps速率。LVDS时钟频率最高154MHz,最大支持视......
  • CodeForces - 510C Fox And Names
    CodeForces-510CFoxAndNames题解:建图+拓扑排序首先题目想让你按照给定的字符串修改字母表的字母序,我们很容易想到拓扑排序,但是这怎么建图?实际上对于两个输入的字......
  • 刷刷刷Day8| 151. 反转字符串中的单词
    151.反转字符串中的单词LeetCode题目要求给你一个字符串s,请你反转字符串中单词的顺序。单词是由非空格字符组成的字符串。s中使用至少一个空格将字符串中的单词......
  • OpenJ_Bailian - 1751
    OpenJ_Bailian-1751题解:最小生成树问题,Kruskal算法已经帮你建好的边就不用再建了,直接合并,当然我们这一题需要将给的坐标转化成边的,然后我们如何输出建哪几条路呢?我们......
  • leetcode-551-easy
    StudentAttendanceRecordIYouaregivenastringsrepresentinganattendancerecordforastudentwhereeachcharactersignifieswhetherthestudentwasab......
  • AtCoder Beginner Contest 251 题解
    AtCoderBeginnerContest251Solution目录AtCoderBeginnerContest251Solution更好的阅读体验戳此进入题面链接题面Luogu链接老样子abc太水了就跳了[ABC251D]AtM......
  • 2023/1/4 记录最近的单片机调试
    1同一单元的UART时钟最好用不同的时钟(指UART1与UART0,UART2由于是1单元因此目前看来不产生影响)。2GPS发送消息过多如果接收长度没有填好会导致填满内存最终死机。3......
  • ARM Cortex-M0单片机进Hardfault后串口如何打印输出错误信息
    如果在程序运行时进hardfault想要打印出现问题前的错误信息,可按如下操作实现:我们先找到系统启动文件中的HardFault_Handler汇编入口,将其整个替换为如下写法:HardFault_Ha......
  • P1251 餐巾计划问题
    P1251餐巾计划问题关键感觉是一个很好的题目,但是理解的还不是很深刻,很像一种减法1.毛巾不会多买,因为是直接连向了y点,所以数量是保证的2.把直接买的和向下传递进行分开......