单片机零基础入门-3-1

单片机零基础入门-3

本周练习题

决赛试题 - Led 彩灯智能控制系统

自己写的代码:

/* 头文件声明区域 */
#include <REGX52.H>
#include "Key.h"
#include "Seg.h"
#include <intrins.h>

/* 变量声明区域 */
unsigned char Key_Slow_Down;//按键减速专用函数 10ms
unsigned char Seg_Slow_Down;//数码管减速专用函数 500ms
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;//按键处理专用变量
unsigned char Seg_Pos;//数码管扫描位选数据变量
unsigned char Seg_Buf[]={1,2,3,4,5,6};//数码管扫描段选数据缓存数组
//添加的变量
//LED处理变量
//四种模式对应四种状态,还需对各状态进行控制,所以需要定义一个模式的索引
unsigned char Led_Flow_Mode=0;
//通过数组进行LED的流转
//模式1、2,由于模式1、2是正序和逆序的差别,所以这两个模式可以共用一个数组
//(注意到模式1、2是单个LED灯进行流转的所以可以用变量赋值)
unsigned char ucled = 0xfe;//1111 1110 对应第一个灯亮
//模式3、4,由于模式3、4是正序和逆序的差别,所以这两个模式可以共用一个数组
unsigned char Led_Flow_Buf[]={0x7e,0xbd,0xdb,0xe7};//分别对应题目模式3、4要求的4种状态
//题目要求每个流转模式上电默认全部都是400ms,但需要对某个模式的流转间隔单独进行增减,所以通过数组进行控制
unsigned int Led_Flow_Time[]={400,400,400,400};
/*如何去实现LED固定按照模式 1、 模式 2、 模式 3、 模式 4 的次序循环往复运行呢?
 *回答这个问题前需要先回答如何使用定时器进行LED的流转:
 *方案1:使用标志位,当Led_Flow_Time计到就置1,标志位置1后就流转一次
 *可以实现
 */
//方案1:标志位置1
//流转标志位
unsigned char Led_Flow_Flag = 0;
//计时变量
unsigned int Led_Ms=0;
//模式3、4数组流转索引
unsigned char Led_Flow_index=0;
//Seg处理变量
unsigned char Seg_Disp_Mode=0;//数码管显示三种页面 0-运行状态界面 1-流转时间设置界面 2-数据显示界面
//每个页面的内容都需要单独用一个数组进行缓存,避免代码的其他逻辑对该页面的影响,减少代码的耦合性
unsigned char Seg_Run_Disp[6];
unsigned char Seg_Setting_Disp[6];
unsigned char Seg_Data_Disp[6];
//按键6按下第一次进入流转时间设置界面且此时‘运行模式编号’闪烁,按下第二次‘流转时间’闪烁,按下第三次保存参数并退出
unsigned int Seg_Flash_Ms=0;
//闪烁标志位 每800ms翻转一次
bit Seg_Flash_Flag=0;
//设置页面索引
unsigned char Seg_Setting_Index=0;
//数据显示界面小数点数组
unsigned char Seg_Point[]={0,0,0,0,0,0};
unsigned char Seg_Point_Sp[]={1,1,0,0,0,0};
//数据显示界面LED灯数据缓存变量
unsigned char Led_Data_Single=0;//单灯流转模式数据缓存变量
unsigned char Led_Data_Double=0;//双灯流转模式数据缓存变量
//设置页面流转间隔缓存变量
unsigned int Led_Time_Buf;
unsigned char Led_Mode_Buf;

/* 按键处理函数 */
void Key_Proc(void)
{
	unsigned char i=0;
	if(Key_Slow_Down) return;
	Key_Slow_Down = 1;
	
	Key_Val = Key_Read();
	Key_Down = Key_Val & (Key_Val^Key_Old);
	Key_Up = ~Key_Val & (Key_Val^Key_Old);
	Key_Old = Key_Val;
	
	if (Led_Flow_Flag == 0)
	{
		if (Key_Old == 4 && Seg_Disp_Mode == 0)
			Seg_Disp_Mode = 2;   // 按着 S4 → 数据界面
		else if(Key_Up == 4)
			Seg_Disp_Mode = 0;   // 松开 S4 → 回到运行界面
	}
	//通过按键对数码管的数据进行控制更改
	switch(Key_Down)
	{
		case 7:
				Led_Flow_Flag ^= 1;
		break;
		case 6:
			if(Led_Flow_Flag == 0)
			{
				Led_Mode_Buf = Led_Flow_Mode;
				Led_Time_Buf = Led_Flow_Time[Led_Flow_Mode];
				Seg_Disp_Mode = 1;
				Seg_Setting_Index++;
				if(Seg_Setting_Index == 3)
				{
					Led_Flow_Mode = Led_Mode_Buf;
					Led_Flow_Time[Led_Flow_Mode] = Led_Time_Buf;
					Seg_Setting_Index = 0;
					Seg_Disp_Mode = 0;
				}	
			}
		break;
		case 5:
			if(Seg_Disp_Mode == 1)
			{
				if(Seg_Setting_Index == 1)
				{
					if(++Led_Mode_Buf == 4)
					{
						Led_Mode_Buf = 0;
					}
				}
				if(Seg_Setting_Index == 2)
				{
					if(Led_Time_Buf == 1200)
					{
						Led_Time_Buf = 1200;
					}
					else
					{
						Led_Time_Buf = Led_Time_Buf + 100;
					}
				}
			}
		break;
		case 4:
			if(Seg_Disp_Mode == 1)
			{
				if(Seg_Setting_Index == 1)
				{
					if(--Led_Mode_Buf == 255)
					{
						Led_Mode_Buf = 3;
					}
				}
				if(Seg_Setting_Index == 2)
				{
					if(Led_Time_Buf == 400)
					{
						Led_Time_Buf= 400;
					}
					else
					{
						Led_Time_Buf = Led_Time_Buf - 100;
					}
				}
			}
		break;
	}
}


/* 数码管处理函数 */
void Seg_Proc(void)
{
	unsigned char i=2;
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	
	//数码管显示处理函数一般逻辑为将按键设置好的值直接赋给数码管显示缓存数组,
	//闪烁逻辑在完全显示赋值完后进行状态的反转
	//题目逻辑是要求是将LED对应模式的流转间隔、运行模式、运行状态进行显示,跟一般的按键逻辑不一样,所以我们需要先将值获取到Seg对应缓存数组再赋给Seg显示缓存数组
	switch(Seg_Disp_Mode)
	{
		//0-运行状态界面
		case 0:
			//将运行状态界面的值取进去
			Seg_Run_Disp[0]=Led_Flow_Flag + 11;
			Seg_Run_Disp[1]=Led_Flow_Mode + 1;
			Seg_Run_Disp[2]=Led_Flow_Time[Led_Flow_Mode] / 1000 %10;
			Seg_Run_Disp[3]=Led_Flow_Time[Led_Flow_Mode] / 100 %10;
			Seg_Run_Disp[4]=Led_Flow_Time[Led_Flow_Mode] / 10 %10;
			Seg_Run_Disp[5]=Led_Flow_Time[Led_Flow_Mode] % 10;
		
			//高位熄灭逻辑
			while(Seg_Run_Disp[i] == 0)
			{
				Seg_Run_Disp[i] = 10;
				i++;
				if(i==6) break;
			}
		
			//将运行状态界面赋给Seg_Buf数组
			for(i=0;i<6;i++)
			{
				Seg_Buf[i]=Seg_Run_Disp[i];
			}
		break;
		//1-流转时间设置界面
		case 1:
			Seg_Setting_Disp[0]= (Led_Mode_Buf / 10 == 0)?13:(Led_Mode_Buf / 10);
			Seg_Setting_Disp[1]=(Led_Mode_Buf % 10) + 1;
			Seg_Setting_Disp[2]=Led_Time_Buf / 1000 %10;
			Seg_Setting_Disp[3]=Led_Time_Buf / 100 %10;
			Seg_Setting_Disp[4]=Led_Time_Buf / 10 %10;
			Seg_Setting_Disp[5]=Led_Time_Buf % 10;
			
			//高位熄灭逻辑
			while(Seg_Setting_Disp[i] == 0)
			{
				Seg_Setting_Disp[i] = 10;
				i++;
				if(i==6) break;
			}
		
			//将流转时间设置界面赋给Seg_Buf数组
			for(i=0;i<6;i++)
			{
				Seg_Buf[i]=Seg_Setting_Disp[i];
			}
		
			//闪烁逻辑
			if(Seg_Setting_Index == 1)
			{
				Seg_Buf[0]=Seg_Flash_Flag?Seg_Setting_Disp[0]:10;
				Seg_Buf[1]=Seg_Flash_Flag?Seg_Setting_Disp[1]:10;
			}
			
			if(Seg_Setting_Index == 2)
			{
				Seg_Buf[2]=Seg_Flash_Flag?Seg_Setting_Disp[2]:10;
				Seg_Buf[3]=Seg_Flash_Flag?Seg_Setting_Disp[3]:10;
				Seg_Buf[4]=Seg_Flash_Flag?Seg_Setting_Disp[4]:10;
				Seg_Buf[5]=Seg_Flash_Flag?Seg_Setting_Disp[5]:10;
			}
		break;
		//2-数据显示界面
		case 2:
			Seg_Data_Disp[0]=14;
			Seg_Data_Disp[1]=15;
			Seg_Data_Disp[2]= (Led_Flow_Mode / 10 == 0)?13:(Led_Flow_Mode / 10);
			Seg_Data_Disp[3]=(Led_Flow_Mode % 10) + 1;
			Seg_Data_Disp[4]=(Led_Flow_Mode == 0 || Led_Flow_Mode == 1)?(Led_Data_Single / 10 % 10):(Led_Data_Double / 10 % 10);
			Seg_Data_Disp[5]=(Led_Flow_Mode == 0 || Led_Flow_Mode == 1)?(Led_Data_Single % 10):(Led_Data_Double % 10);
		
			for(i=0;i<6;i++)
			{
				Seg_Buf[i]=Seg_Data_Disp[i];
			}
		break;
	}
}

/* 其他处理函数 */
void Led_Proc(void)
{
//	//LED流动标志位每LED对应模式流转间隔置一次1 置1后进入流转位切换逻辑 
//	//注:这里是进行切换与之前for循环逻辑(这个逻辑不能实现使用定时器进行延时)进行区分
//	//但如果标志位是这样置位的话是有问题的,因为这样写的话,无论你启动与否,当你等了Led_Flow_Time[Led_Flow_Mode]这么多时间后,标志位就会置1
	bit Led_Data_Flag=0;
	unsigned char i=0;
	
	//数据显示界面逻辑
	if(Led_Flow_Mode == 0 || Led_Flow_Mode == 1)
	{
		Led_Data_Single = 0;
		for(i=0;i<8;i++)
		{
			//ucled & (0000 0001 << i)
			if((ucled & (1 << i)) == 0)
			{
				
				Led_Data_Single = i+1;
				break;
			}
		}
	}
	else
	{
		Led_Data_Double = 0;
		for(i=0;i<8;i++)
		{
			//ucled & (0000 0001 << i)
			if((ucled & (1 << i)) == 0)
			{
				if(Led_Data_Flag == 0)
				{
					Led_Data_Flag = 1;
					Led_Data_Double = (i+1)*10;
				}
				else
				{
					Led_Data_Flag = 0;
					Led_Data_Double = Led_Data_Double + (i + 1);
					break;
				}
			}
		}
	}
	
	if(Led_Flow_Flag == 1)
	{
		//为什么不用‘==’,因为考虑到可能有被中断打断的情况发生会出现不相等的情况,所以用‘>=’
		if(Led_Ms>=Led_Flow_Time[Led_Flow_Mode])
		{
			Led_Ms = 0;
			switch(Led_Flow_Mode)
			{
				case 0:
					if(ucled == 0x7f){Led_Flow_Mode = 1;}
					else
					{ucled = _crol_(ucled,1);}
				break;
				case 1:
					if(ucled == 0xfe)
					{
						Led_Flow_Mode = 2;
						ucled = 0x7e;
					}
					else ucled = _cror_(ucled,1);
				break;
				case 2:
						Led_Flow_index++;
						if(Led_Flow_index == 4)
						{
							Led_Flow_Mode = 3;
						}
						else
						{
							ucled = Led_Flow_Buf[Led_Flow_index];
						}
				break;
				case 3:
						Led_Flow_index--;
						if(Led_Flow_index == 255)
						{
							ucled = 0xfe;
							Led_Flow_Mode = 0;
						}
						else
						{
							ucled = Led_Flow_Buf[Led_Flow_index];
						}
				break;
			}
		}
	}
	P1 = ucled;
}

/* 定时器0初始化函数 */
void Timer0_Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;
	EA = 1;
}



/* 定时器0中断服务函数 */
void TimerServer(void) interrupt 1
{
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	
	//减速逻辑
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
	
	//数码管扫描逻辑
	if(++Seg_Pos == 6) Seg_Pos = 0;
	if(Seg_Disp_Mode == 2)
	{
		Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point_Sp[Seg_Pos]);
	}
	else
	{
		Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
	}
	
//	//Led标志位逻辑 计时变量每ms加一次,当计时变量加到流转模式对应的时间的时候,标志位置1,计时变量清零(以进行下一次计时)
	if(Led_Flow_Flag == 1) Led_Ms++;
	if(++Seg_Flash_Ms == 800)
	{
		Seg_Flash_Ms = 0;
		Seg_Flash_Flag ^= 1;
	}
}

/* Main */
void main(void)
{
	Timer0_Init();
	while(1)
	{
		Key_Proc();
		Seg_Proc();
		Led_Proc();
	}
}

范例代码:

/* 头文件声明区 */
#include <REGX52.H>//单片机寄存器专用头文件
#include <Key.h>//按键底层驱动专用头文件
#include <Seg.h>//数码管底层驱动专用头文件
#include <intrins.h>

/* 变量声明区 */
unsigned char Key_Val,Key_Down,Key_Old;//按键专用变量
unsigned char Key_Slow_Down;//按键减速专用变量
unsigned char Seg_Buf[6] = {10,10,10,10,10,10};//数码管显示数据存放数组
unsigned char Seg_Point[6] = {0,0,0,0,0,0};//数码管小数点数据存放数组
unsigned char Seg_Pos;//数码管扫描专用变量
unsigned int Seg_Slow_Down;//数码管减速专用变量
unsigned char Seg_Disp_Mode;//数码管显示模式变量 0-运行状态界面 1-流转时间设置界面 2-数据显示界面
bit System_Flag;//系统使能标志位 0-暂停 1-启动 上电默认处于暂停状态
unsigned char Led_Mode;//Led模式标志位
unsigned int Led_Time_Data[4] = {400,400,400,400};//Led流转时间数据存放数组
unsigned int Sys_Tick;//系统计时器专用变量
unsigned char ucLed = 0xfe;//Led数据存放数组
unsigned char ucLed_Data[4] = {0x7e,0xbd,0xdb,0xe7};//模式3、4数据存放数组
unsigned char ucLed_Data_Index;//模式3、4数组指针
bit Set_Flag;//设置标志位 0-模式编号 1-流转间隔
unsigned int Led_Time_Set[4];//Led流转数据设置数组
unsigned char Led_Time_Set_Index;//Led流转数据设置数组指针
bit Seg_Star_Flag;//数码管闪烁标志位
unsigned int Timer_400Ms;//400毫秒变量
bit Data_Disp_Flag;//数据显示标志位 0-不显示 1-显示
unsigned char Led_Data;//Led数据储存变量

/* 键盘处理函数 */
void Key_Proc()
{
	unsigned char i;//For循环专用变量
	if(Key_Slow_Down) return;
	Key_Slow_Down = 1;//键盘减速程序

	Key_Val = Key_Read();//实时读取键码值
	Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
	Key_Old = Key_Val;//辅助扫描变量
	
	if(System_Flag == 0)//系统处于暂停状态
	{
		if(Key_Old == 4 && Seg_Disp_Mode == 0)//在显示界面下长按S4
			Data_Disp_Flag = 1;//显示数据
		else
			Data_Disp_Flag = 0;//显示状态
	}
	else
		Data_Disp_Flag = 0;
	
	switch(Key_Down)
	{
		case 7://启动暂停按键
			System_Flag ^= 1;
		break;
		case 6://设置切换按键
			if(Seg_Disp_Mode == 0)//若在显示界面下使能此按键
			{
				for(i=0;i<4;i++)//读取当前设置数据
					Led_Time_Set[i] = Led_Time_Data[i];
				Seg_Disp_Mode = 1;//跳转到参数设置界面
			}
			else if(Seg_Disp_Mode == 1)//else-为了避免第一次按下后直接执行下列程序 所以需要另外加else使得在执行完if语句后跳出判断主体
			{
				Set_Flag ^= 1;//用于判断设置处于哪个状态的变量                        
				if(Set_Flag == 0)//第二次跳转到模式设置状态
				{
					for(i=0;i<4;i++)
						Led_Time_Data[i] = Led_Time_Set[i];//保存当前设置数据
					Seg_Disp_Mode = 0;
				}
			}
		break;
		case 5://参数+
			if(Seg_Disp_Mode == 1)//处于参数设置界面
			{
				if(Set_Flag == 0)//编号设置
				{
					if(++Led_Time_Set_Index == 4)
						Led_Time_Set_Index = 0;
				}
				else
				{
					Led_Time_Set[Led_Time_Set_Index] = Led_Time_Set[Led_Time_Set_Index]+100;
					if(Led_Time_Set[Led_Time_Set_Index] >= 1300)
						Led_Time_Set[Led_Time_Set_Index] = 400;
				}
			}
		break;
		case 4://参数-
			if(Seg_Disp_Mode == 1)//处于参数设置界面
			{
				if(Set_Flag == 0)//编号设置
				{
					if(--Led_Time_Set_Index == 255)
						Led_Time_Set_Index = 3;
				}
				else
				{
					Led_Time_Set[Led_Time_Set_Index] = Led_Time_Set[Led_Time_Set_Index]-100;
					if(Led_Time_Set[Led_Time_Set_Index] <= 300)
						Led_Time_Set[Led_Time_Set_Index] = 1200;
				}
			}
		break;		
	}
}

/* 信息处理函数 */
void Seg_Proc()
{
	unsigned char i = 2;//用于熄灭高位未启用数码管
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;//数码管减速程序

	switch(Seg_Disp_Mode)
	{
		case 0://运行状态显示界面
			if(Data_Disp_Flag == 0)
			{
				Seg_Buf[0] = System_Flag?11:12;//11-S 12-R
				Seg_Buf[1] = Led_Mode+1;
				Seg_Buf[2] = Led_Time_Data[Led_Mode] / 1000 % 10;
				Seg_Buf[3] = Led_Time_Data[Led_Mode] / 100 % 10;
				Seg_Buf[4] = Led_Time_Data[Led_Mode] / 10 % 10;
				Seg_Buf[5] = Led_Time_Data[Led_Mode] % 10;
				while(Seg_Buf[i] == 0)//使用循环体结构令高位为0的数码管熄灭 直到不为0时退出
				{
					Seg_Buf[i] = 10;
					i++;
				}
			}
			else
			{
				Seg_Buf[0] = 0;
				Seg_Buf[1] = 12;
				Seg_Buf[2] = 13;
				Seg_Buf[3] = Led_Mode+1;
				Seg_Buf[4] = Led_Data / 10;
				Seg_Buf[5] = Led_Data % 10;
			}
		break;
		case 1://流转时间设置界面
			Seg_Buf[0] = 13;
			Seg_Buf[1] = Led_Time_Set_Index+1;
			Seg_Buf[2] = Led_Time_Set[Led_Time_Set_Index] / 1000 % 10;
			Seg_Buf[3] = Led_Time_Set[Led_Time_Set_Index] / 100 % 10;
			Seg_Buf[4] = Led_Time_Set[Led_Time_Set_Index] / 10 % 10;
			Seg_Buf[5] = Led_Time_Set[Led_Time_Set_Index] % 10;
			if(Set_Flag == 0)
			{
				Seg_Buf[0] = Seg_Star_Flag?13:10;
				Seg_Buf[1] = Seg_Star_Flag?Led_Time_Set_Index+1:10;
			}
			else
			{
				Seg_Buf[2] = Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index] / 1000 % 10:10;
				Seg_Buf[3] = Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index] / 100 % 10:10;
				Seg_Buf[4] = Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index] / 10 % 10:10;
				Seg_Buf[5] = Seg_Star_Flag?Led_Time_Set[Led_Time_Set_Index] % 10:10;				
			}
			
			while(Seg_Buf[i] == 0)//使用循环体结构令高位为0的数码管熄灭 直到不为0时退出
			{
				Seg_Buf[i] = 10;
				i++;
			}		
		break;
	}
	Seg_Point[0] = Seg_Point[1] = Data_Disp_Flag&(!Data_Disp_Flag)?0:1;
}

/* 其他显示函数 */
void Led_Proc()
{
	unsigned char i;//用于循环判断
	P1 = ucLed;
	if(Sys_Tick == Led_Time_Data[Led_Mode])//当计时器走到当前模式设定的流转时间间隔时
	{
		Sys_Tick = 0;//计时器复位 便于下次循环
		switch(Led_Mode)//根据Led_Mode的不同 选择不同的彩灯运行模式
		{
			case 0:
				if(ucLed == 0x7e)//判断是否从模式4切换至模式1 若是,则复位ucLed数据
					ucLed = 0xfe;
				else
				{
					ucLed = _crol_(ucLed,1);//模式1 L1-L8
					if(ucLed == 0x7f) Led_Mode = 1;//当L8点亮时 跳转到模式2
				}
			break;
			case 1:
				ucLed = _cror_(ucLed,1);//模式2 L8-L1
				if(ucLed == 0xfe) Led_Mode = 2;//当L1点亮时 跳转到模式3
			break;
			case 2:
				ucLed = ucLed_Data[ucLed_Data_Index];//模式3 从外往内
				if(++ucLed_Data_Index == 4)//当指针溢出 即完成一次循环时 跳转到模式4
				{
					Led_Mode = 3;
					ucLed_Data_Index = 3;//指针复位至模式四初始数据
				}
			break;
			case 3:
				ucLed = ucLed_Data[ucLed_Data_Index];//模式4 从内往外
				if(--ucLed_Data_Index == 255)//当指针溢出 即完成一次循环时 跳转到模式1
				{	
					Led_Mode = 0;
					ucLed_Data_Index = 0;	//指针复位至模式三初始数据
				}
			break;
		}
	}
	/* 数据读取设计思路 */
	i = 0;//每次循环判断前将i置0,便于读取Led状态
	while((~ucLed & (0x01 << i)) != (0x01 << i))
		i++;
	if(Led_Mode < 2)//若处于前两种模式,则数据直接等于i+1
		Led_Data = i+1;
	else
		Led_Data = (i+1)*10+(8-(i+1));//若处于后两种模式,则高位数据等于i+1,根据对称变换,低位加高位的数值之和等于8
}

/* 定时器0中断初始化函数 */
void Timer0Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初始值
	TH0 = 0xFC;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;    //定时器0中断打开
	EA = 1;     //总中断打开
}

/* 定时器0中断服务函数 */
void Timer0Server() interrupt 1
{
 	TL0 = 0x18;		//设置定时初始值
	TH0 = 0xFC;		//设置定时初始值   
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//键盘减速专用
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//数码管减速专用
	if(++Seg_Pos == 6) Seg_Pos = 0;//数码管显示专用
	Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
	if(System_Flag == 1)//当系统启动时 计时器一毫秒自加一次	
		Sys_Tick++;
	if(++Timer_400Ms == 400)//400毫秒触发一次
	{
		Timer_400Ms = 0;
		Seg_Star_Flag ^= 1;
	}
}

/* Main */
void main()
{
	Timer0Init();
	while (1)
	{
		Key_Proc();
		Seg_Proc();
		Led_Proc();
	}
}

蓝桥杯过渡模拟训练 · 一 模拟电压采集记录器

自己写的代码:

/* 头文件声明区域 */
#include <REGX52.H>
#include "Key.h"
#include "Seg.h"

/* 变量声明区域 */
unsigned char Key_Slow_Down;//按键减速专用函数 10ms
unsigned char Seg_Slow_Down;//数码管减速专用函数 500ms
unsigned char Key_Val,Key_Down,Key_Up,Key_Old;//按键处理专用变量
unsigned char Seg_Pos;//数码管扫描位选数据变量
unsigned char Seg_Buf[]={1,2,3,4,5,6};//数码管扫描段选数据缓存数组
//添加的变量
//宏定义
#define Extinguish 10
#define dash 11
#define U 12
#define P 13
#define N 14
//数码管小数点显示位数据缓存数组
unsigned Seg_Point_Buf_Disp[]={0,0,0,1,0,0};
unsigned Seg_Point_Buf_NDisp[]={0,0,0,0,0,0};
unsigned char Seg_Disp_Mode=0;//数码管显示模式  0-电压采集界面 1-数据显示界面 2-参数设置界面 3-计数统计界面
//每个页面的数据单独使用数组进行缓存
unsigned char Voltage_Collect_Interface[6]={Extinguish,Extinguish,dash,dash,dash,dash};//0-电压采集界面
unsigned char Voltage_Collect_Data[4]={dash,dash,dash,dash};//电压采集数据数组
unsigned int Voltage_Data_New=0;//电压采集数据变量 新
unsigned int Voltage_Data_Old=0;//电压采集数据变量 旧
unsigned char Voltage_Collect_Index = 3;//电压采集位索引
bit Voltage_Flash_Flag = 0;//选中位闪烁标志位
unsigned int Voltage_Flash_500Ms = 0;//闪烁计数变量 500ms

unsigned char Data_Disp_Interface[6]={Extinguish,Extinguish,Extinguish,Extinguish,Extinguish,Extinguish};//1-数据显示界面
unsigned int Data_Disp_Data[3]={Extinguish,Extinguish,Extinguish};//实际电压显示数据

unsigned char Parameter_Setting_Interface[6]={Extinguish,Extinguish,Extinguish,Extinguish,Extinguish,Extinguish};//2-参数设置界面
unsigned int Parameter_Setting_Data=300;//电压参数数据

unsigned char Count_Statistics_Interface[6]={Extinguish,Extinguish,Extinguish,Extinguish,Extinguish,Extinguish};//3-计数统计界面
unsigned char Counter[5]={0,0,0,0,0};//计数统计数组

//LED显示数据缓存变量
unsigned char Ucled_L1=0xff;
unsigned char Ucled_L2=0xff;
unsigned char Ucled_L3=0xff;
unsigned int L1_Time_5000Ms=0;//指示灯1点亮计数变量
unsigned char Fake_Control=0;//无效操作计数变量

/* 按键处理函数 */
void Key_Proc(void)
{
	unsigned char i=0;
	if(Key_Slow_Down) return;
	Key_Slow_Down = 1;
	
	Key_Val = Key_Read();
	Key_Down = Key_Val & (Key_Val^Key_Old);
	Key_Up = ~Key_Val & (Key_Val^Key_Old);
	Key_Old = Key_Val;
	
	if(Seg_Disp_Mode == 0 && Key_Down >= 1 && Key_Down <= 10)
	{
		Fake_Control = 0;
		Voltage_Collect_Data[Voltage_Collect_Index] = Key_Down - 1;
		if(Voltage_Collect_Index == 0)
		{
			Voltage_Data_Old = Voltage_Data_New;
			Voltage_Data_New = Voltage_Collect_Data[0]+Voltage_Collect_Data[1]*10+Voltage_Collect_Data[2]*100+Voltage_Collect_Data[3]*1000;
			Voltage_Collect_Index = 3;
		}
		else
		{
			Voltage_Collect_Index--;
		}
	}
	else if(Key_Down >= 1 && Key_Down <= 10)
	{
		Fake_Control++;
	}

	switch(Key_Down)
	{
		case 11:
			if(Seg_Disp_Mode == 0)
			{
				Fake_Control = 0;
				if(Voltage_Collect_Index == 3)
				{
					Data_Disp_Data[2] = Voltage_Data_New / 1000 % 10;
					Data_Disp_Data[1] = Voltage_Data_New / 100 % 10;
					Data_Disp_Data[0] = Voltage_Data_New / 10 % 10;
					
					if(Voltage_Data_Old > (Parameter_Setting_Data*10) && Voltage_Data_New < (Parameter_Setting_Data*10))
					{
						if(++Counter[4] == 10)
						{
							Counter[4] = 0;
							if(++Counter[3] == 10)
							{
								Counter[3] = 0;
								if(++Counter[2] == 10)
								{
									Counter[2] = 0;
									if(++Counter[1] == 10)
									{
										Counter[1] = 0;
										if(++Counter[0] == 10)
										{
											Counter[0] = 0;
										}
									}
								}
							}
						}
					}
					
					Seg_Disp_Mode = 1;
				}
				else
				{
					Seg_Disp_Mode = 0;
					Voltage_Collect_Index = 3;
					for(i=0;i<4;i++)
					{
						Voltage_Collect_Data[i] = dash;
					}
				}
			}
			else if(Seg_Disp_Mode != 0)
			{
					Fake_Control = 0;
					Seg_Disp_Mode = 0;
					Voltage_Collect_Index = 3;
					for(i=0;i<4;i++)
					{
						Voltage_Collect_Data[i] = dash;
					}
			}
			else
			{
				Fake_Control++;
			}
		break;
		case 12:
			if(Seg_Disp_Mode != 0)
			{
				Fake_Control = 0;
				if(++Seg_Disp_Mode == 4)
				{
					Seg_Disp_Mode = 1;
				}
			}
			else
			{
				Fake_Control++;
			}
		break;
		case 13:
				Fake_Control++;
		break;
		case 14:
			if(Seg_Disp_Mode == 0)
			{
				Fake_Control = 0;
				Voltage_Collect_Index = 3;
				Voltage_Data_New = 0;
				for(i=0;i<4;i++)
				{
					Voltage_Collect_Data[i] = dash;
				}
			}
			else if(Seg_Disp_Mode == 3)
			{
				Fake_Control = 0;
				for(i=0;i<5;i++)
				{
					Counter[i]=0;
				}
			}
			else
			{
				Fake_Control++;
			}
		break;
		case 15:
			if(Seg_Disp_Mode == 2)
			{
				Fake_Control = 0;
				Parameter_Setting_Data = Parameter_Setting_Data + 50;
				if(Parameter_Setting_Data > 600)
				{
					Parameter_Setting_Data = 100;
				}	
			}
			else
			{
				Fake_Control++;
			}
		break;
		case 16:
			if(Seg_Disp_Mode == 2)
			{
				Fake_Control = 0;
				Parameter_Setting_Data = Parameter_Setting_Data - 50;
				if(Parameter_Setting_Data < 100)
				{
					Parameter_Setting_Data = 600;
				}	
			}
			else
			{
				Fake_Control++;
			}
		break;
	}
}


/* 数码管处理函数 */
void Seg_Proc(void)
{
	unsigned char i=0;
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;
	
	switch(Seg_Disp_Mode)
	{
		case 0:
			Seg_Buf[0]=Extinguish;
			Seg_Buf[1]=Extinguish;
			Seg_Buf[2]=Voltage_Collect_Data[3];
			Seg_Buf[3]=Voltage_Collect_Data[2];
			Seg_Buf[4]=Voltage_Collect_Data[1];
			Seg_Buf[5]=Voltage_Collect_Data[0];
		
			Seg_Buf[5-Voltage_Collect_Index] = Voltage_Flash_Flag?Voltage_Collect_Data[Voltage_Collect_Index]:Extinguish;
		break;
		case 1:
			Seg_Buf[0]=U;
			Seg_Buf[1]=Extinguish;
			Seg_Buf[2]=Extinguish;
			Seg_Buf[3]=Data_Disp_Data[2];
			Seg_Buf[4]=Data_Disp_Data[1];
			Seg_Buf[5]=Data_Disp_Data[0];
		break;
		case 2:
			Seg_Buf[0]=P;
			Seg_Buf[1]=Extinguish;
			Seg_Buf[2]=Extinguish;
			Seg_Buf[3]=Parameter_Setting_Data / 100 % 10;
			Seg_Buf[4]=Parameter_Setting_Data / 10 % 10;
			Seg_Buf[5]=Parameter_Setting_Data % 10;
		break;
		case 3:
			while(Counter[i] == 0)
			{
				Seg_Buf[i+1] = Extinguish;
				if(++i == 4)
				{
					if(Counter[4] == 0)	break;
				}
			}
			
			Seg_Buf[0]=N;
			for(;i<5;i++)
			{
				Seg_Buf[i+1] = Counter[i];
			}
		break;
	}
}

/* 其他处理函数 */
void Led_Proc(void)
{
	
	if(L1_Time_5000Ms >= 5000)
	{
		Ucled_L1 = 0xfe;
	}
	else
	{
		Ucled_L1 = 0xff;
	}
	
	if((Counter[0]*10000 + Counter[1]*1000 + Counter[2]*100 + Counter[3]*10 + Counter[4] + 1) % 2 == 0)
	{
		Ucled_L2 = 0xfd;
	}
	else
	{
		Ucled_L2 = 0xff;
	}
	
	if(Fake_Control >= 3)
	{
		Ucled_L3 = 0xfb;
	}
	else
	{
		Ucled_L3 = 0xff;
	}
	
	P1 = Ucled_L1 & Ucled_L2 & Ucled_L3;
}

/* 定时器0初始化函数 */
void Timer0_Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;				//清除TF0标志
	TR0 = 1;				//定时器0开始计时
	ET0 = 1;
	EA = 1;
}

/* 定时器0中断服务函数 */
void TimerServer(void) interrupt 1
{
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	
	//减速逻辑
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;
	
	//数码管扫描逻辑
	if(++Seg_Pos == 6) Seg_Pos = 0;
	if(Seg_Disp_Mode == 0 || Seg_Disp_Mode == 3)
	{
		Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point_Buf_NDisp[Seg_Pos]);	
	}
	else
	{
		Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point_Buf_Disp[Seg_Pos]);	
	}
	
	//闪烁计数逻辑
	if(++Voltage_Flash_500Ms == 500)
	{
		Voltage_Flash_500Ms = 0;
		Voltage_Flash_Flag ^= 1;
	}
	
	if(Voltage_Data_New < (Parameter_Setting_Data*10))
	{
		if(L1_Time_5000Ms < 5000)   // 饱和在 5000
			L1_Time_5000Ms++;
	}
	else L1_Time_5000Ms = 0;
}

/* Main */
void main(void)
{
	Timer0_Init();
	while(1)
	{
		Key_Proc();
		Seg_Proc();
		Led_Proc();
	}
}

范例代码:

/* 头文件声明区 */
#include <REGX52.H>//单片机寄存器专用头文件
#include <Key.h>//按键底层驱动专用头文件
#include <Seg.h>//数码管底层驱动专用头文件
#include <Led.h>//Led底层驱动专用头文件

/* 变量声明区 */
unsigned char Key_Val,Key_Down,Key_Old;//按键专用变量
unsigned char Key_Slow_Down;//按键减速专用变量
unsigned char Seg_Buf[6] = {10,10,10,10,10,10};//数码管显示数据存放数组
unsigned char Seg_Pos;//数码管扫描专用变量
unsigned int Seg_Slow_Down;//数码管减速专用变量
unsigned char Seg_Disp_Mode;//数码管显示模式标志位 0-电压采集 1-数据显示 2-参数设置 3-计数统计
unsigned char Seg_Input[4] = {11,11,11,11};//数码管输入数据储存数组
unsigned char Seg_Input_Index;//数码管输入数据储存数组指针
unsigned int Timer_500Ms;//五百毫秒计时变量
unsigned char Seg_Point[6] = {0,0,0,0,0,0};//数码管小数点显示数组
unsigned char ucLed[8] = {0,0,0,0,0,0,0,0};//Led显示数据存放数组
unsigned char Led_Pos;
unsigned int Count;// 计数值专用变量
unsigned int Sys_Tick;//系统计时器
unsigned char Key_Error_Count;//无效按键统计变量
float Voltage;//实际电压变量
float Voltage_Parameter = 3.0;//电压设置参数
float Voltage_Parameter_Ctrol = 3.0;//实际电压设置参数  默认值3.0V
bit Seg_Flag;//数码管闪烁标志位
bit Voltage_Flag ;//电压参考标志位

/* 键盘处理函数 */
void Key_Proc()
{
	unsigned char i;//用于For循环
	if(Key_Slow_Down) return;
	Key_Slow_Down = 1;//键盘减速程序

	Key_Val = Key_Read();//实时读取键码值
	Key_Down = Key_Val & (Key_Old ^ Key_Val);//捕捉按键下降沿
	Key_Old = Key_Val;//辅助扫描变量
	
	if(Key_Down >= 1 && Key_Down <= 10)//键盘使能条件
	{
		if(Seg_Disp_Mode == 0 && Seg_Input_Index < 4)
		{
			Seg_Input[Seg_Input_Index] = Key_Down - 1;
			Seg_Input_Index++;
			Key_Error_Count = 0;
		}
		else
			Key_Error_Count++;
	}
	
	switch(Key_Down)
	{
		case 11:
			Key_Error_Count = 0;
			if(Seg_Disp_Mode == 0)//处于电压采集界面
			{
				if(Seg_Input_Index >= 4)//判断数据有效性
				{
					Voltage = (Seg_Input[0] * 1000 + Seg_Input[1] * 100 + Seg_Input[2] * 10 + Seg_Input[3] + 5) / 1000.0;
					if(Voltage >= 0.01)
						Seg_Disp_Mode = 1;//跳转到数据显示界面
					else
					{
						Seg_Input_Index = 0;//输入指针复位
						for(i = 0;i < 4;i++)//输入数组复位
							Seg_Input[i] = 11;
					}
				}
				else
				{
					Seg_Input_Index = 0;//输入指针复位
					for(i = 0;i < 4;i++)//输入数组复位
						Seg_Input[i] = 11;
				}
			}
			else//处于非电压采集界面
			{
				Seg_Disp_Mode = 0;//跳转到电压采集界面
				Seg_Input_Index = 0;//输入指针复位
				for(i = 0;i < 4;i++)//输入数组复位
					Seg_Input[i] = 11;
			}
		break;
		case 12:
			if(Seg_Disp_Mode != 0)//处于非电压采集界面
			{
				Key_Error_Count = 0;
				if(Seg_Disp_Mode == 2)
					Voltage_Parameter = Voltage_Parameter_Ctrol;//保存当前设置参数
				if(++Seg_Disp_Mode == 4)
					Seg_Disp_Mode = 1;
			}
			else
				Key_Error_Count++;
		break;
		case 15:
			if(Seg_Disp_Mode == 2)//处于参数设置界面
			{
				Key_Error_Count = 0;
				Voltage_Parameter += 0.5;
				if(Voltage_Parameter > 6)
					Voltage_Parameter = 1;
			}
			else
				Key_Error_Count++;
		break;
		case 16:
			if(Seg_Disp_Mode == 2)//处于参数设置界面
			{
				Key_Error_Count = 0;
				Voltage_Parameter -=0.5;
				if(Voltage_Parameter < 1)
					Voltage_Parameter = 6;
			}
			else
				Key_Error_Count++;
		break;
		case 14:
			if(Seg_Disp_Mode == 0)
			{
				Key_Error_Count = 0;
				Seg_Input_Index = 0;//输入指针复位
				for(i = 0;i < 4;i++)//输入数组复位
					Seg_Input[i] = 11;
			}
			else if(Seg_Disp_Mode == 3)
			{
				Key_Error_Count = 0;
				Count = 0;
			}
			else
				Key_Error_Count++;
		break;
	}
}

/* 信息处理函数 */
void Seg_Proc()
{
	unsigned char i;//用于For循环
	unsigned char j = 1;//用于While循环
	if(Seg_Slow_Down) return;
	Seg_Slow_Down = 1;//数码管减速程序
	
	if(Voltage > Voltage_Parameter_Ctrol)//当实际电压大于参考电压时
		Voltage_Flag = 1;//拉高标志位
	else if(Voltage_Flag == 1)
	{
		Voltage_Flag = 0;//标志位复位
		Count++;//计数值+1
	}
	switch(Seg_Disp_Mode)
	{
		case 0://电压采集界面
			Seg_Point[3+(int)Voltage/10] = 0;
			Seg_Buf[0] = Seg_Buf[1] = 10;
			for(i = 0;i< 4;i++)
				Seg_Buf[2+i] = Seg_Input[i];
			if(Seg_Buf[5] == 11)//只有当最后一位为-时 才实现数码管闪烁功能
				Seg_Buf[2+Seg_Input_Index] = Seg_Flag?Seg_Input[Seg_Input_Index]:10;
		break;
		case 1://数据显示界面
			Seg_Point[3+(int)Voltage/10] = 1;
			Seg_Buf[0] = 12;
			Seg_Buf[1] = Seg_Buf[2] = 10;
			Seg_Buf[3] = (int)Voltage/10?1:(unsigned char)Voltage % 10;
			Seg_Buf[4] = (unsigned int)(Voltage * 100) / 10 % 10;
			Seg_Buf[5] = (unsigned int)(Voltage * 100) % 10;
		break;
		case 2://参数设置界面
			Seg_Point[3+(int)Voltage/10] = 1;
			Seg_Buf[0] = 13;
			Seg_Buf[1] = Seg_Buf[2] = 10;
			Seg_Buf[3] = (unsigned char)Voltage_Parameter % 10;
			Seg_Buf[4] = (unsigned int)(Voltage_Parameter * 100) / 10 % 10;
			Seg_Buf[5] = (unsigned int)(Voltage_Parameter * 100) % 10;			
		break;
		case 3://计数统计界面
			Seg_Point[3+(int)Voltage/10] = 0;
			Seg_Buf[0] = 14;
			Seg_Buf[1] = Count / 10000 % 10;
		  Seg_Buf[2] = Count / 1000 % 10;
			Seg_Buf[3] = Count / 100 % 10;
			Seg_Buf[4] = Count / 10 % 10;
			Seg_Buf[5] = Count % 10;
			while(Seg_Buf[j] == 0)
			{
				Seg_Buf[j] = 10;
				if(++j == 5) break;
			}
		break;
	}
}

/* 其他显示函数 */
void Led_Proc()
{
	if(Voltage < Voltage_Parameter_Ctrol)
	{
		if(Sys_Tick >= 5000)
			ucLed[0] = 1;
	}
	else
	{
		Sys_Tick = ucLed[0] = 0;
	}
	ucLed[1] = Count % 2;
	ucLed[2] = Key_Error_Count / 3;
}

/* 定时器0中断初始化函数 */
void Timer0Init(void)		//1毫秒@12.000MHz
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初始值
	TH0 = 0xFC;		//设置定时初始值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0 = 1;    //定时器0中断打开
	EA = 1;     //总中断打开
}

/* 定时器0中断服务函数 */
void Timer0Server() interrupt 1
{
 	TL0 = 0x18;		//设置定时初始值
	TH0 = 0xFC;		//设置定时初始值   
	if(++Key_Slow_Down == 10) Key_Slow_Down = 0;//键盘减速专用
	if(++Seg_Slow_Down == 500) Seg_Slow_Down = 0;//数码管减速专用
	if(++Seg_Pos == 6) Seg_Pos = 0;//数码管显示专用
	if(++Led_Pos == 8) Led_Pos = 0;
	Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos],Seg_Point[Seg_Pos]);
	Led_Disp(Led_Pos,ucLed[Led_Pos]);
	if(Voltage < Voltage_Parameter_Ctrol)
		Sys_Tick++;
	if(++Timer_500Ms == 500)
	{
		Timer_500Ms = 0;
		Seg_Flag ^= 1;
	}
}

/* Main */
void main()
{
	Timer0Init();
	while (1)
	{
		Key_Proc();
		Seg_Proc();
		Led_Proc();
	}
}