这一届感觉比前两届都难一点,状态机没写过长按的,调了很久,感觉代码不够精简,不过也在五个小时内做完了,大概花了三个半小时。主要是长按的状态机法卡了比较久。
总结一下遇到的问题:
- led灯和继电器的代码执行比较快,最好放在定时器中断里。如果放在主函数,会执行到一半就被中断掉,可能会往P0口输入我们不想要的值而导致led显示不稳定,继电器吸合时蜂鸣器也偶尔乱叫。
- 因为整点时led1会点亮,所以初始化秒时分的时候不能初始化为0,如果初始化为0,那么上电时led程序里判断秒和分都为0,会导致一上电led1先亮五秒才熄灭。
- 最麻烦还是按键部分的程序编写,不太熟练,逻辑容易乱。我这个部分的代码肯定是不够精简的,仅仅是完成了功能。
#include <STC15F2K60S2.H>
#include "ds1302.h"
#include "onewire.h"
typedef unsigned char u8;
typedef struct
{
u8 b1:1;
u8 b2:1;
u8 b3:1;
u8 b4:1;
u8 b5:1;
u8 b6:1;
u8 b7:1;
u8 b8:1;
}bits;
typedef union
{
u8 Hex;
bits B;
}HexToBits;
HexToBits led_ctrl;
unsigned char code smg_duanma[10]={0xc0,0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
u8 smg[8];
unsigned char time[3] = {0x40, 0x59, 0x23};
unsigned char temp_set=25;
void vDevice_Process(u8 p2dat,u8 p0dat)
{
P0 = p0dat;
P2 = (P2&0x1f)|p2dat;
P2 = P2&0x1f;
}
//定时器2
void Timer2Init(void) //1毫秒@12.000MHz
{
AUXR |= 0x04; //定时器时钟1T模式
T2L = 0x20; //设置定时初值
T2H = 0xD1; //设置定时初值
AUXR |= 0x10; //定时器2开始计时
EA=1;
IE2 |= 0x04;
}
//读取时间
unsigned char sec=0x40,min=0x59,hour=0x23; //初始化为0的话led1会亮五秒才灭
void vRead_time()
{
sec = Read_Ds1302_Byte(0x81);
min = Read_Ds1302_Byte(0x83);
hour = Read_Ds1302_Byte(0x85);
}
//读取温度
unsigned int temp;
unsigned int cnt_temp;
void vRead_temp()
{
if(cnt_temp>=500)
{
cnt_temp=0;
temp = (rd_temperature())*10;
}
}
//继电器控制
bit mode_RELAY=0;
unsigned int cnt_RELAY;
bit flag_RELAY=0;
bit flag_Relay_led=0;
void vRELAY_Process()
{
if(mode_RELAY==0)//温度控制
{
if(((temp/10)+1)>temp_set)
{
flag_Relay_led=1;
vDevice_Process(0xa0,0x10);
}
else
{
flag_Relay_led=0;
vDevice_Process(0xa0,0x00);
}
}
if(mode_RELAY==1)//时间控制
{
if(sec==0&&min==0)
{
flag_RELAY=1;
flag_Relay_led=1;
vDevice_Process(0xa0,0x10);
}
if(cnt_RELAY>=5000&&flag_RELAY==1)
{
flag_RELAY=0;
flag_Relay_led=0;
cnt_RELAY=0;
led_ctrl.B.b3=1;
vDevice_Process(0xa0,0x00);
}
}
}
//LED 指示灯
unsigned int cnt_led,cnt_flash;
bit flag_led=0;
void vLED_Process()
{
//整点
if((sec==0x00)&&(min==0x00))
{
flag_led=1;
led_ctrl.B.b1=0;
}
if(flag_led==1&&cnt_led>=5000)
{
flag_led=0;
cnt_led=0;
led_ctrl.B.b1=1;
}
//工作模式
if(mode_RELAY==0)
{
led_ctrl.B.b2=0;
}
else if(mode_RELAY==1)
{
led_ctrl.B.b2=1;
}
//继电器状态
if(flag_Relay_led==1)
{
if(cnt_flash<100)
{
led_ctrl.B.b3=0;
}
else if(cnt_flash<200)
{
led_ctrl.B.b3=1;
}
else
{
cnt_flash=0;
}
}
vDevice_Process(0x80,led_ctrl.Hex);
}
//数码管操作函数
unsigned char mode_smg=1;
unsigned char mode_time=1;
void vSMG_Process()
{
if(mode_smg==1)
{
smg[0]=0xc1;
smg[1]=smg_duanma[1];
smg[2]=0xff;
smg[3]=0xff;
smg[4]=0xff;
smg[5]=smg_duanma[temp/100];
smg[6]=smg_duanma[temp/10%10]&0x7f;
smg[7]=smg_duanma[temp%10];
}
if(mode_smg==2)
{
if(mode_time==1)
{
smg[0]=0xc1;
smg[1]=smg_duanma[2];
smg[2]=0xff;
smg[3]=smg_duanma[hour/16];
smg[4]=smg_duanma[hour%16];
smg[5]=0xbf;
smg[6]=smg_duanma[min/16];
smg[7]=smg_duanma[min%16];
}
if(mode_time==2)//长按时显示分秒
{
smg[0]=0xc1;
smg[1]=smg_duanma[2];
smg[2]=0xff;
smg[3]=smg_duanma[min/16];
smg[4]=smg_duanma[min%16];
smg[5]=0xbf;
smg[6]=smg_duanma[sec/16];
smg[7]=smg_duanma[sec%16];
}
}
if(mode_smg==3)
{
smg[0]=0xc1;
smg[1]=smg_duanma[3];
smg[2]=0xff;
smg[3]=0xff;
smg[4]=0xff;
smg[5]=0xff;
smg[6]=smg_duanma[temp_set/10];
smg[7]=smg_duanma[temp_set%10];
}
}
//数码管显示
void vSMG_Show()
{
static u8 i;
vDevice_Process(0xc0,0x00);
vDevice_Process(0xe0,smg[i]);
vDevice_Process(0xc0,0x01<<i);
i=(i+1)%8;
}
//键盘扫描
unsigned char vScan_key()
{
unsigned char key_io;
P3=0xf0;P4=0xff; //扫描列
if(P44==0) key_io=4;
if(P42==0) key_io=8;
if(P35==0) key_io=12;
if(P34==0) key_io=16;
P3=0x0f;P4=0x00;
if(P33==0) key_io=key_io;
if(P32==0) key_io=key_io+1;
if(P31==0) key_io=key_io+2;
if(P30==0) key_io=key_io+3;
return key_io;
}
//按键状态机
#define KEY_NO 0
#define KEY_DOWN 1
#define KEY_UP 2
#define KEY_LONG 3 //长按状态
unsigned char cnt_key;
unsigned int cnt_Down;// 记录按下的时长
bit flag_Down=0;//按下的标志
unsigned char vKey_state()
{
static u8 Key_State=0;
unsigned char key_value=0;
unsigned char key_io=0;
key_io = vScan_key();
switch(Key_State)
{
case KEY_NO:
if(key_io)
{
Key_State = KEY_DOWN; //如果按下 进入判断消抖检测
flag_Down=1;
}
break;
case KEY_DOWN: //消抖检测 因为调用时是10ms调用一次,如果次时io还是0就是真的按下了
if(key_io)//如果还是按下的状态
{
key_value = key_io; // 返回单击按键值
Key_State = KEY_LONG; // 去判断是否长按
}
else //如果没有按下了,则判为抖动,不返回键值
{
Key_State = KEY_NO;
}
break;
case KEY_LONG: //长按检测
if(key_io) //如果io还是0,就判断按下的时长为多少
{
if(cnt_Down>=1000)//如果按下超过一秒 就是长按
{
flag_Down=0; //按下标志关闭
cnt_Down=0; //按下计时清零
key_value = 20; // 假设返回20表示长按
Key_State = KEY_UP; // 跳到up等待松开
}
}
else //如果cnt_Down还没到1000就松开了,执行此语句 不返回长按状态
{
flag_Down=0; //按下标志关闭
cnt_Down=0; //按下计时清零
Key_State = KEY_UP;// 跳到up等待松开
}
break;
case KEY_UP:
if(key_io==0)
{
Key_State=KEY_NO;
key_value=21; //松开时返回一个21表示松开了
}
break;
}
return key_value;
}
//判断按键
unsigned char key_val;
unsigned char clock_key=0; //锁住长按状态(上一个为长按才判断是否松手)。去掉这个也可以
void vKey_Process()
{
if(cnt_key>=10)
{
cnt_key=0;
key_val=vKey_state();
if(clock_key==1) //如果锁打开 就保持在长按状态 判断是否松开
{
if(key_val==21) //直到下次按键返回21,就代表松开了
{
mode_time=1;
clock_key=0; //关闭锁
}
}
if(key_val==12)
{
mode_smg++;
if(mode_smg==4)mode_smg=1;
}
if(key_val==13)
{
mode_RELAY=~mode_RELAY;
flag_Relay_led=0;
led_ctrl.B.b3=1;
}
if(key_val==16)
{
if(mode_smg==3)
{
temp_set++;
}
}
if(key_val==17)
{
if(mode_smg==3)
{
temp_set--;
}
}
if(key_val==20)//长按
{
clock_key=1;
if(mode_smg==2)
{
mode_time=2;
}
}
}
}
//系统初始化 关闭led等无关设备
void System_init()
{
led_ctrl.Hex=0xff;
Write_time(time[0],time[1],time[2]);
vDevice_Process(0x80,0xff);
vDevice_Process(0xa0,0x00);
}
void main()
{
System_init();
Timer2Init();
while(1)
{
vSMG_Process();
vRead_temp();
vKey_Process();
}
}
//定时器中断
void Timer2_service() interrupt 12
{
cnt_temp++;cnt_key++;
if(flag_RELAY==1)
{
cnt_RELAY++;
}
if(flag_Relay_led==1)
{
cnt_flash++;
}
if(flag_led==1)
{
cnt_led++;
}
if(flag_Down==1)
{
cnt_Down++;
}
vLED_Process();
vRELAY_Process();
vRead_time();
vSMG_Show();
}