蓝桥杯单片机第13届第一场

这一届感觉比前两届都难一点,状态机没写过长按的,调了很久,感觉代码不够精简,不过也在五个小时内做完了,大概花了三个半小时。主要是长按的状态机法卡了比较久。

总结一下遇到的问题:

  1. led灯和继电器的代码执行比较快,最好放在定时器中断里。如果放在主函数,会执行到一半就被中断掉,可能会往P0口输入我们不想要的值而导致led显示不稳定,继电器吸合时蜂鸣器也偶尔乱叫。
  2. 因为整点时led1会点亮,所以初始化秒时分的时候不能初始化为0,如果初始化为0,那么上电时led程序里判断秒和分都为0,会导致一上电led1先亮五秒才熄灭。
  3. 最麻烦还是按键部分的程序编写,不太熟练,逻辑容易乱。我这个部分的代码肯定是不够精简的,仅仅是完成了功能。
#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();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值