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为低电平,主机为发送器,写数据 I2C_SCL=1; //SCL为高电平,从机为接收器,读数据
设计资源 培训 开发板 精华推荐
- Protues仿真实例(8051)-ULN2803.rar
- 使用 Analog Devices 的 LTC1439EG 的参考设计
- 使用 NXP Semiconductors 的 TDA8927 的参考设计
- SPC582B-DIS,SPC582B 线路电源架构 MCU 探索套件
- 直流低压恒温铝基板加热台ESP32主控
- 使用 TC7117 模数转换器实现 +5V 单电源的典型应用
- 使用 Analog Devices 的 LT1117CM-2.85 的参考设计
- 【毕设】基于阿里云IOT平台的智慧工厂设计-智能插排子项目
- 使用 Aimtec 的 AM3G-4815SH30Z 的参考设计
- [已验证]LTC4412 理想二极管-电源自动切换