省赛题第六届到第十二届单独分析

4月2日 第十一届没写完,现在是北京时间二十二点十三分,距离揽江楼关门还有十七分钟,我要收拾书包从五楼下去,明天早起写完,写了差不多五个小时吧

4月3日上午 写完了朋友们

4月5日下午 回来把第十二届补齐了


目录

废话

正题

第六届

第七届

第八届

第九届

第十届

第十一届

第十二届


废话

先巴拉巴拉几句题外话,简单梳理一下最近的state.

时隔一个多月重新开始准备比赛,三月一直在忙别的事情,申报大创课题啃了一堆文献,申请常青藤奖学金,参加电赛培训,参加高中英语教资的考试,看综艺《黑怕女孩》,哈利波特新赛季上分等等等等,反正比赛基本上一整个没准备的状态,然后学校的课程也全落下来了哈哈哈哈哈哈,不过小问题啦,又不是第一次这样干了,我的工作安排方法和工作效率如下图所示:

省赛题第六届到第十二届单独分析_第1张图片省赛题第六届到第十二届单独分析_第2张图片

人永远只有在玩的不够尽兴的时候才最尽兴,在有工作和学习需要去做的时候玩会有一种莫名的负罪的快感,笑死我怎么这么一本正经为自己摸鱼开脱

Ok let us come back to the topic.

我是跟小蜜蜂老师学的,然后自己买了一块板子边学习边验证的,反正我觉得挺好的,也没有什么大问题,一堆人说什么按键延时消抖会不行啊要用状态机啊要用定时器啊什么的,我也确实被这种说法困扰了很长时间,学了一堆乱七八糟的玩意,但我最终还是回归这种方法,因为我在验证的过程中没发现有什么问题,我在CSDN上也看到一位拿了国奖的博主也是这样写的(自由学者亻伊宸),所以,我就还是这么写,反正拿个省奖就好了嘛。

打算好好分析一下几套省题,归纳一下,再把常考模块过一遍,看看客观题,就over了。

正题

第六届

第六届省赛是温度记录器,主要用到的是数码管、DS1802(测量温度的),1302(时钟),LED,独立按键。

参数设置界面    S4控制DS1802采集温度的时间间隔,那就说明肯定要用到计时器,上电默认是1s

S5按一下进入时钟显示界面 提示符要进行闪烁,那么就需要一个smg_c控制闪烁时间,smg_f控制是否要进行闪烁

采集完十个数据L1会闪,那么就少不了LED_c&LED_f,而且需要一个索引值来计这十个温度值

S6按下L1熄灭,那就是LED_f为0了,

S7就是回到了参数设置界面

界面的切换也需要设置一个变量,来控制数码管显示哪一个界面

对按键进行定义,还有L1

参数的定义,这里给一下,因为我觉得设参的过程就是思考的过程

unsigned char code duanma[12] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0xbf,0xff};
unsigned char interval = 1;						 //温度采样间隔时间
unsigned char index	= 0;						 //采样的温度的索引值
unsigned char temps[10] = {0};					 //存放采样到的所有温度
unsigned char Write_Time[3] = {0x80,0x82,0x84};	 //时钟写入的地址
unsigned char Read_Time[3] = {0x81,0x83,0x85};	 //时钟读取的地址
unsigned char Timer[3] = {0x50,0x59,0x23};		 //用来存储的初始化的时间
unsigned char smg_stat = 0;						
 //标志此时数码管当前的显示状态,0采集前的设置,1采集中时间显示,2采集后的数据显示
unsigned char count = 0;						 //定时器的计数变量
unsigned char smg_f = 1;						 //控制数码管提示符闪烁的标志
unsigned char temp_f = 1;					     //控制温度读取的开关变量
unsigned char temp_c = 0;						 //定时器中控制温度取样间隔的计数变量
unsigned char temp_num;							 //温度采样个数
unsigned char led = 0;							 //控制led显示的开关变量
unsigned char k6 = 0;				             //控制读取后将每一个温度依次显示

这里数码管和LED闪烁共用了一个count,所以只需要分别写一个开关就可以了

采集温度的函数

void tempCollection ()
{
	if (smg_stat == 1)       //smg_stat为1,说明是采集阶段
	{
		if (temp_num <= 9)    //温度采集的数目小于10才需采集
		{
			if (temp_f == 1)   //采集温度的开关打开
			{
				temps[index] = Read_Temp ();
				temp_f = 0;            //这句的意义是:当temp_num=9的时候运行完保证采集温度
的开关是关掉的
				index++;
				temp_num++;	
			}	
		}
		else if (temp_num >= 10)
		{
			led = 1;		
		}	
	}
}

中断函数也展示一下

void ServiceTimer0 () interrupt 1
{
	TH0 = (65535 - 10000) / 256;
	TL0 = (65535 - 10000) % 256;

	count++;
	if (count >= 100)
	{
		count = 0;
		smg_f = ~smg_f;
	}

	if (temp_f == 0)
	{
		temp_c++;
	}

	if (temp_c >= interval * 100) //乘以100是因为采集间隔是以秒为单位的,但是
我们设的是0.01秒,也就是10ms
	{
		temp_c = 0;
		temp_f = 1;
	}
}

按键函数只展示核心,套路不放在这里占篇幅了


		if (S7 == 0)
		{
			while (S7 == 0)
			{
				ShowSMG ();	
			}
			if (smg_stat == 2)
			{
				smg_stat = 0;
			}
			temp_num = 0;
			index = 0;
			temp_f = 1;
			k6 = 0;
			for (i = 0;i<=9;i++)   //这里几句都是因为s7是起到一个清零回到参数设置
界面的作用
			{
				temps[i] = 0;
			}								

		if (S6 == 0)
		{
			while (S6 == 0)
			{
				ShowSMG ();	
			}
			if (smg_stat == 1)  //切换显示的内容
			{
				smg_stat = 2;
			}
			if (led == 1)     
			{
				led = 0;
			}

			if (smg_stat == 2)
			{
				k6++;
				if (k6 > 1);
				{
					index++;		
				}
				if (index >= 10)
				{
					index = 0;
					k6 = 1;	    //这里让k6等于1是为了保证下一次自加之后能大于1,
写0是不可以的
				}
			}
         }
										

		if (S5 == 0)
		{
			while (S5 == 0)
			{
				ShowSMG ();	
			}
			if (smg_stat == 0)
			{
				smg_stat = 1;
			}								
		}	


		if (S4 == 0)
		{
			while (S4 == 0)
			{
				ShowSMG ();	
			}
			if (smg_stat == 0)
			{
				if (interval == 1)
				{
					interval = 5;
				}
				else if (interval == 5)
				{
					interval = 30;	
				}
				else if (interval == 30)
				{
					interval = 60;
				}
				else if (interval == 60)
				{
					interval = 1;
				}
			}

 LED函数

void LEDRunning ()
{
	SelectHC573(4);	
	if (led == 1)
	{
		if (smg_f == 1)  //为什么是smg_f呢,因为两者都是要求1s闪(LED其实没要求),
所以设置一个变量就ok了啊
		{
			L1 = 0;
		}
		else if (smg_f == 0)
		{
			L1 = 1;
		}	
	}
	SelectHC573(0);
}

 第七届

第七届省赛是模拟风扇控制系统,主要用到的是数码管、DS1802(测量温度的),pwm输出,LED,独立按键。

工作模式就是不同的pwm

工作模式设置界面    S4切换工作模式

S5定时按键,每按一次定时时间增加一分钟,并且清零当前的倒计时,倒计时为0时,pwm停止

S6清零键,pwm也停了

S7起到切换工作模式设置界面和室温显示界面的作用

参数的设置

unsigned char code duanma[18] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
unsigned char t_c = 0;				    //定时1s的计数变量
unsigned char pwm_c = 0;				//脉宽调制的计数变量,看有没有达到pwm_t
unsigned char pwm_t = 2;				//具体的脉宽调制的值,默认值为2,也就是20%,
//是我们设置的
unsigned char pwm_f = 1;				//pwm输出的标志变量 开关标志
unsigned char temp = 0;					//用来存放读取的温度 
unsigned char mode = 1;					//标志风扇工作模式,默认为睡眠风
unsigned char rtime = 60;				//剩余工作时间,默认工作时间1min
unsigned char k7 = 0;					//S7按键的标志位,默认为时间倒计时界面 控制数码管显示啥
unsigned char k6 = 1;					//S6按键的标志位,默认为1  控制数码管显示啥

 中断函数

void ServiceTime0 () interrupt 1
{
	TH0 = (65535 - 100) / 256;
	TL0 = (65535 - 100) % 256;
	
	if (rtime > 0)    //只要剩余时间不为0,倒计时就得继续
	{
		t_c++;     
		if (t_c >= 10000)  //到了1s了
		{
			rtime = rtime - 1;
			t_c = 0;
		}
	}
	
	pwm_c = pwm_c + 1;
	if (pwm_c >= pwm_t) 
	{
		pwm_f = 0;     //比我们预设的大了,所以pwm就关掉了,不输出了,灯不亮了
	}
	if (pwm_c >= 10)   //pwm被我们意念分为十份,超过十份就自动归零,要重新开始计数
	{
		pwm_c = 0;
		pwm_f = 1;	  //此时,pwm开始输出,也就是开关打开
	}	
}

 数码管显示里面个十百千位我要拿出来单独说一下

        ShowSMG_Bit(5,duanma[rtime / 1000]);
		Delay_SMG (100);
		ShowSMG_Bit(6,duanma[(rtime / 100) % 10]);
		Delay_SMG (100);
		ShowSMG_Bit(7,duanma[(rtime / 10) % 10]);
		Delay_SMG (100);
		ShowSMG_Bit(8,duanma[rtime % 10]);


        ShowSMG_Bit(6,duanma[(temp / 10) % 10]);
		Delay_SMG (100);
		ShowSMG_Bit(7,duanma[temp % 10]);

按键函数 关键部分 主要就是逻辑


		if (S7 == 0)
		{
			while (S7 == 0)
			{
				ShowSMG ();
			}
			if (k7 == 0)
			{
				k7 = 1;
			}
			else if (k7 == 1)
			{
				k7 = 0;
			}
		}

		if (S6 == 0)
		{
			while (S6 == 0)
			{
				ShowSMG ();
			}
			rtime = 0;
		}

		if (S5 == 0)
		{
			while (S5 == 0)
			{
				ShowSMG ();
			}
			if(k6 == 1)
			{
				k6 = 2;
				rtime = 120;
			}
			else if (k6 == 2)
			{
				k6 = 0;
				rtime = 0;
			}
			else if (k6 == 0)
			{
				k6 = 1;
				rtime = 60;
			}
		}

		if (S4 == 0)
		{
			while (S4 == 0)
			{
				ShowSMG ();
			}
			if (mode == 1)			//睡眠风
			{
				mode = 2;
				pwm_t = 5;
			}
			else if (mode == 2)			//自然风
			{
				mode = 3;
				pwm_t = 10;
			}
			else if (mode == 3)		    //常风
			{
				mode = 1;
				pwm_t = 2;
			}
		}

 LED函数没啥好说的,就是要记住得是在倒计时没结束的时候才亮

这里main函数要说一下

void main ()
{
	InitSystem ();
	InitTime0 ();
	while (1)
	{
		ScanKey ();
		if (k7 == 1)       //k7=1表示此时是室温显示,这个时候读取温度的模块才开始工作
		{
		 	ReadTemp ();
		}
		ShowSMG ();
		LEDRunning ();				
	}
}

但是我感觉这道题很有问题啊,pwm没体现出来啊,LED只是受模式控制啊,也没说是让LED几秒亮几秒灭的,感觉题目有问题orz

第八届

第八届很难 我麻了 

第八届省赛是电子钟,主要用到的是数码管、DS1802(测量温度的),1302,LED,独立按键。

S5加,单位加1

S4减,单位减1  时钟显示界面加按s4,显示温度模式,松手即恢复

定时设置S6,切换时分秒

时钟设置 S7起到切换时分秒的作用,控制对哪一位进行设置 而且当前选中的单元要闪烁,那就要用到定时器了

定时和闹钟重合,也就是闹钟“响”了,此时L1工作,闹钟“响”的时候,按随意一个键,灯关掉

参数的设置

unsigned char duanma[18] = 
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,,0xbf,0xff};
unsigned char Write_addr[3] = {0x80,0x82,0x84};	   //DS1302时钟每一位写入的地址
unsigned char Read_addr[3] = {0x81,0x83,0x85};	   //DS1302时钟每一位读取的地址
unsigned char Timer[3] = {87,89,35};		       //用来存储读取的时间,初始化为23时59分50秒
unsigned char Clock[3] = {0,0,0};		           //用来存储闹钟的时间,初始化为00时00分00秒
unsigned char temp = 0;							   //用来存储读取的温度
unsigned char count = 0;							   //定时器的计数变量
unsigned char smg_f = 1;						       //控制数码管闪烁的标志变量
unsigned char led_c = 0;							   //控制LED的计数变量变量
unsigned char led_f = 0;							   //控制LED闪烁的标志变量
unsigned char led_k = 0;							   //控制led的开关变量,0为关
unsigned char k7 = 0;			   //记录S7的当前状态,0为时钟显示,1表示设置时钟的时这个位
unsigned char k6 = 0;			  //记录S6的当前状态,0为S6未按下,1表示设置闹钟的 时的这个位

这里要尤其注意一下,就是1302他是BCD码,但是咱平时计时都是十进制的,所以牵扯到一系列BCD码的问题,然后菜鸡的我不会,参考的是我上面提高的那个博主的文章

中断函数

void ServiceT0 () interrupt 1
{
	TH0 = (65535 - 10000) / 256;     0.000001
	TL0 = (65535 - 10000) % 256;
	
	count++;
	if (count == 100)      //1s闪一次  count 0.01s
	{
		smg_f = ~smg_f;
		count = 0;		
	}
	
	if (led_k == 1)        //LED的开关被打开啦
	{
		led_c ++;
		if (led_c % 20 == 0)    //0.2s为间隔闪烁
		{
			led_f = ~led_f;
			if (led_c >= 500)   //5s后停止
			{
				led_k = 0;
				led_c = 0;
				led_f = 0;
			}	
		}	
	}	
}

数码管相关函数,这里要好好说一哈 但是只有部分 拎重点

void ShowSMG_Time ()
{
	if (k6 == 0)    //表示现在数码管显示的设置时钟的界面
	{
		if (((k7 == 1) & (smg_f == 1)) | (k7 == 0) | (k7 == 2) | (k7 == 3)) 
//要操作时这一位,k7 = 1,又要闪烁显示,就要通过与一个smg_f == 1来实现,还要注意,在k7 = 0,2,3的时候,这一位要常亮,所以要或上k7 = 0,1,3
		{
			ShowSMG_Bit(1,duanma[Timer[2] / 16]);
			Delay_SMG(500);
			ShowSMG_Bit(2,duanma[Timer[2] % 16]);
			Delay_SMG(500);
		}
		ShowSMG_Bit(3,duanma[16]);
		Delay_SMG(500);
    }
//分秒的就不写了
	else if (k6 != 0) //只要k6不等于0就说明此时是设置时钟的界面
	{
		if (((k6 == 1) & (smg_f == 1)) | (k6 == 0) | (k6 == 2) | (k6 == 3))
		{
			ShowSMG_Bit(1,duanma[Clock[2] / 10]);
			Delay_SMG(500);
			ShowSMG_Bit(2,duanma[Clock[2] % 10]);
			Delay_SMG(500);
		}
		//分秒省略
	}
}

按键部分


		if (S7 == 0)
		{
			while (S7 == 0)
			{
				ShowSMG_Time ();  //要保证数码管亮,LED运作,温度也在读取
				LEDRunning ();
				ReadDS1302 ();	
			}
			if (led_k == 1)       //LED开关打开
			{
				led_k = 0;
				led_c = 0;
				led_f = 0;
			}
			k7++;        //k7加1是为了切换操作的是时 还是分 还是秒 
			if (k7 >= 4)  //超过3了就应该stop归零
			{
				k7 = 0;
			}
		}	
	}
s6和s7雷同,就是操作的是k6罢了,此不赘述
		if (S5 == 0)
		{
			while (S5 == 0)
			{
				ShowSMG_Time ();
				LEDRunning ();
				ReadDS1302 ();	
			}
			if (led_k == 1)
			{
				led_k = 0;
				led_c = 0;
				led_f = 0;
			}
			if (k7 == 1)
			{
				Timer[2] = Timeradd(Timer[2]);	//把读取到的时 的数字放到数组里		
				if (Timer[2] >= 18)          //大于十六进制的18 就是十进制的24了 
				{
					Timer[2] = 0;
				}
			}
			else if (k7 == 2) 
			{	
				Timer[1] = Timeradd(Timer[1]);
				if (Timer[1] >= 96)
				{
					Timer[1] = 0;
				}	
			}
			else if (k7 == 3)
			{	
				Timer[0] = Timeradd(Timer[0]);
				if (Timer[0] >= 96)
				{
					Timer[0] = 0;
				}	
			}
			if (k6 == 1)		 //k6的话就是定时相关的了
			{	
				Clock[2] = Clock[2] + 1;
				if (Clock[2] >= 24)      
				{
					Clock[2] = 0;
				}	
			}
			if (k6 == 2)
			{	
				Clock[1] = Clock[1] + 1;
				if (Clock[1] >= 60)
				{
					Clock[1] = 0;
				}	
			}
			if (k6 == 3)
			{	
				Clock[0] = Clock[0] + 1;
				if (Clock[0] >= 60)
				{
					Clock[0] = 0;
				}	
			}
			SetDS1302 ();     //这时1302一直是运作的
		}	

		if (S4 == 0)
		{
			while (S4 == 0) //注意一下代码在while循环里,就是说按键没有抬起,非常重要!!!
			{
				if ((k6 == 0) & (k7 == 0))  //此时不对任何一位进行操作,就是正常的时钟显示那个倒计时的状态
				{
					Read_Temp ();    
					ShowSMG_Temp ();	//那么就显示温度界面
				}
				else 
				{
					ShowSMG_Time (); //不然就显示时间
					LEDRunning ();	
				}
				ReadDS1302 ();	
			}
			if (led_k == 1)     
			{
				led_k = 0;
				led_c = 0;
				led_f = 0;
			}
			if (k7 == 1)//时位
			{
				Timer[2] = Timerminus(Timer[2]);			
				if (Timer[2] == 89)    //十六进制的59
				{
					Timer[2] = 35;     //十进制的23
				}
			}
			else if (k7 == 2)
			{	
				Timer[1] = Timerminus(Timer[1]);	
			}
			else if (k7 == 3)
			{	
				Timer[0] = Timerminus(Timer[0]);	
			}

			if (k6 == 1)		 
			{	
				Clock[2] = Clock[2] - 1;
				if (Clock[2] == -1)
				{
					Clock[2] = 23;
				}	
			}
			else if (k6 == 2)
			{	
				Clock[1] = Clock[1] - 1;
				if (Clock[1] == -1)
				{
					Clock[1] = 59;
				}	
			}
			else if (k6 == 3)
			{	
				Clock[0] = Clock[0] - 1;
				if (Clock[0] == -1)
				{
					Clock[0] = 59;
				}	
			}
			SetDS1302 ();
		}	

LED相关函数

void LEDRunning ()
{
	if ((BCDtoTEN(Timer[0]) == Clock[0]) & (BCDtoTEN(Timer[1]) == Clock[1]) & (BCDtoTEN(Timer[2]) == Clock[2]))
	{
		led_k = 1;		
	}
	if (led_f == 1)
	{
		SelectHC573(4);
		L1 = 0;	
		SelectHC573(0);
	}
	else if (led_f == 0)
	{
		SelectHC573(4);
		L1 = 1;	
		SelectHC573(0);
	}	
}

第九届

第六届省赛是彩灯控制器,主要用到的是数码管、ADC,E2PROM,LED,独立按键。

通过Rb2的输出电压控制LED灯的亮度  自己设置

S4减   在不是设置界面的时候,按下,数码管显示亮度等级,松开熄灭

S5加 运行模式,模式数字加一,流转模式,流转间隔加100ms

S6设置键,按一下设置流转的模式,再按一下设置流转的间隔,再按一下数码管灭,默认流转间隔为0.8秒

S7控制LED灯的启动还是停止

参数的定义

unsigned char rb2 = 0;								//存放读取的Rb2的数据
unsigned char mode = 1;								//流水灯模式
unsigned int interval[4] = {400,400,400,400};		//4种模式流水灯的流转间隔
unsigned char bright_grade = 4;		 				//led灯的亮度等级
unsigned char duanma[18] = 
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};			   
unsigned char led_mode[24] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0x7f,0xbf,
0xdf,0xef,0xf7,0xfb,0xfd,0xfe,0x7e,0xbd,0xdb,0xe7,0xe7,0xdb,0xbd,0x7e};		                
//24种led流转模式
unsigned char led_stat = 0;			 				//led模式对应的索引值 取数组里的哪一个值
unsigned int count = 0;              				//定时器T0的计数变量
unsigned char k7 = 0;                				//k7的标志位
unsigned char k6 = 0;				 				//k6的标志位
unsigned int smg_c = 0;				 				//定时器数码管计数变量
unsigned char smg_f = 0;			 				//数码管闪烁标志变量
unsigned char pwm_t = 100;			 				//脉宽占空比  我们自行设置的
unsigned int  pwm_c = 0;				 			//脉宽计数变量
unsigned char pwm_f = 1;			 				//灯亮灭标志变量

中断函数

void ServiceTime0 () interrupt 1
{
	TH0 = (65535 - 100) / 256;    //0.000001  0.0001s
	TL0 = (65535 - 100) % 256;

	if (k7 == 1)  //k7等于1,启动运转
	{
		count++;	
	}
	if (count >= (interval[mode - 1] * 10))  //注意定义的时候mode是等于1的哦,数组是从0开始的,所以这里要减一
	{
		led_stat++;       //LED的亮灭要轮换,从而展现出流转的样子
		count = 0;
	}

	smg_c++;
	if (smg_c >= 8000)  //数码管0.8s闪一次
	{
		smg_c = 0;
		if (smg_f == 0)     //其实这里写成smg_f=~smg_f也是可以的
		{
			smg_f = 1;
		}
		else if (smg_f == 1)
		{
			smg_f = 0;
		}
	}

	pwm_c++;
	if (pwm_c >= pwm_t) //如果实际的pwm大于我们预设的了
	{
		pwm_f = 0;  //那么输出就被我们关闭了
		if (pwm_c >= 100) //大于100也是要被强制清零的
		{
			pwm_c = 0;
			pwm_f = 1;	
		}
	}
}

数码管相关函数

void ShowSMG_M ()  //设置界面
{
	ShowSMG_bit(1,duanma[16]); 
	Delay_SMG(500);
	if (((k6 == 1) & (smg_f == 1)) | (k6 == 0) | (k6 == 2))
//k6等于1,说明现在设置的流转模式
	{
		ShowSMG_bit(2,duanma[mode]);
		Delay_SMG(500);		
	}
	ShowSMG_bit(3,duanma[16]);
	Delay_SMG(500);
	ShowSMG_bit(4,0xff);
	Delay_SMG(500);
	if (((k6 == 2) & (smg_f == 1)) | (k6 == 0) | (k6 == 1))
	{
		if (interval[mode - 1] > 999)
		{
			ShowSMG_bit(5,duanma[interval[mode - 1] / 1000]);
			Delay_SMG(500);	
		}
		ShowSMG_bit(6,duanma[(interval[mode - 1] / 100) % 10]);
		Delay_SMG(500);
		ShowSMG_bit(7,duanma[(interval[mode - 1] / 10) % 10]);
		Delay_SMG(500);
		ShowSMG_bit(8,duanma[interval[mode - 1] % 10]);
		Delay_SMG(500);		
	}
	AllSMG(0xff);
}

按键部分


		if (S7 == 0)//开or关
		{
			while(S7 == 0)
			{
				ShowSMG_M (); 
			}
			if (k7 == 0)
			{
				k7 = 1;
			}
			else if (k7 == 1)
			{
				k7 = 0;	
			}

		if (S6 == 0)   //切换 是设置模式,还是设置间隔,还是关掉数码管
		{
			while(S6 == 0)
			{
				ShowSMG_M ();	
			}
			if (k6 == 0)
			{
				k6 = 1;
			}
			else if (k6 == 1)
			{
				k6 = 2;
			}
			else if (k6 == 2)
			{
				k6 = 0;
			}
		}	
	}

		if (S5 == 0)
		{
			while(S5 == 0)
			{
				ShowSMG_M ();
			}
			if (k6 == 1)
			{
				mode++;
				if (mode >= 4)
				{
					mode = 4;
				}
			}
			else if (k6 == 2)
			{
				interval[mode - 1] = interval[mode - 1] + 100;
				if (interval[mode - 1] >= 1200)
				{
					interval[mode - 1] = 1200; //题目给的上限
				}
				if (mode == 1)  //写进存储器的操作 ,除以10是为了能存进去,内存太小了
				{
					Write24C02(0x00,interval[mode - 1] / 10);	
				}
				else if (mode == 2)
				{
					Write24C02(0x01,interval[mode - 1] / 10);	
				}
				else if (mode == 3)
				{
					Write24C02(0x02,interval[mode - 1] / 10);	
				}
				else if (mode == 4)
				{
					Write24C02(0x03,interval[mode - 1] / 10);	
				}
			}


		if (S4 == 0)
		{
			if (k6 == 0)  //灭掉的状态
			{
				while(S4 == 0)
				{
					ExecuteRb2 ();
					LEDRunning ();
					ShowSMG_L ();	//显示温度
				}	
			}
			else if (k6 == 1)
			{
				while(S4 == 0)
				{
					ShowSMG_M ();	
				}
				mode = mode -1;
				if (mode <=1)
				{
					mode = 1;
				}
			}
			else if (k6 == 2)
			{
				while(S4 == 0)
				{
					ShowSMG_M ();	
				}
				剩下和加一样,不写了
			}
		}

LED函数

void LEDRunning ()
{
	if (mode == 1)
	{
		if (led_stat > 7)//规定只能在前八个变,也就是从左到右闪
		{
			led_stat = 0;
		}
		if (pwm_f == 1)  //pwm输出的开关打开
		{
			XBYTE[0x8000] = led_mode[led_stat];	
		}
		else if (pwm_f == 0)
		{
			XBYTE[0x8000] = 0xff;	
		}
	}
	mode的其他模式省略了
}

rb2函数

void ExecuteRb2 ()//设置灯的亮度,其实就是控制pwm,写不同的值不同的亮度
{
	Readrb2 ();    
	if (rb2 <= 60)    
	{
		bright_grade = 1;
		pwm_t = 25;	
	}
	else if ((rb2 > 60) & (rb2 <= 120))
	{
		bright_grade = 2;
		pwm_t = 50;	
	}
	75 100省略了
}

main函数写一下

void main (void)
{
	InitSystem();
	interval[0] = Read24C02(0x00) * 10;  //之前写进存储器的时候除了10,所以现在乘回去
	interval[1] = Read24C02(0x01) * 10;
	interval[2] = Read24C02(0x02) * 10;
	interval[3] = Read24C02(0x03) * 10;
	InitTime0 ();
	while (1)
	{
		ExecuteRb2 ();
		ScanKey ();
		if (k7 == 1)
		{
			LEDRunning ();	
		}
		else if (k7 == 0)
		{
			XBYTE[0x8000] = 0xff;
		}
		ShowSMG_M ();	
	}
}

第十届

第六届省赛是彩灯控制器,主要用到的是数码管、ADC,NEC555,LED,独立按键,PCF8591

S4切换界面,电压测量和频率测量

S5  DAC输出模式切换,随rb2的值变化,还是固定是2

S6  LED的开关

S7数码管的开关

测电压的时候1亮2灭,测频率1灭2亮

省赛题第六届到第十二届单独分析_第3张图片

 DAC固定为2时,5亮

参数的定义

unsigned int rb2 = 0;				 //读取的电位器RB2的值
unsigned char duanma[18] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0xbf,0xff};
unsigned char duanmadot [10] = 
0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};
unsigned int f_dat = 0;				 //用于显示的频率
unsigned int u_dat = 0;				 //用于显示的电压
unsigned char k4 = 1;                //S4的标志位,1为显示电压
unsigned char k5 = 0;				//S5的标志位,0为显示固定电压2V
unsigned char k6 = 0;				 //S6的标志位,0为显示电压LED
unsigned char k7 = 0;				 //S7的标志位,0为显示数码管
unsigned int f_c = 0;				 //定时器T0用来对脉冲计数
unsigned char count = 0;			 //定时器T1的计数变量

定时器相关函数

void InitTime ()
{	
	TMOD = 0x16;
	
	TH0 = 0xff;
	TL0 = 0xff;
	
	TH1 = (65535 - 50000) / 256;
	TL1 = (65535 - 50000) % 256;

	TR0 = 1;
	TR1 = 1;

	ET0 = 1;
	ET1 = 1;
	EA = 1;
}

void ServiceTime0 () interrupt 1
{
	f_c++;
}

void ServiceTime1 () interrupt 3
{
	TH1 = (65535 - 50000) / 256;
	TL1 = (65535 - 50000) % 256;
	
	count++;
	if (count == 20)
	{	
		count = 0;
		f_dat = f_c;
		f_c = 0;	
	}	
}

数码管相关函数

void ShowSMG ()
{
	if(k4 == 1)				   //电压测量状态
具体不写了,很好处理
	else if (k4 == 2)		  //频率测量状态
}

按键部分就是简单的k6=~k6

LED没什么好说的就是逻辑或与放在一起要注意一下

if ((u_dat < 150) | ((u_dat >= 250) & (u_dat < 350)))
else if (((u_dat >= 150) & (u_dat < 250)) | (u_dat >= 350))

main函数 只写了while(1)的


		if (k5 == 0)						  //固定输出2V并显示在数码管上
		{
			SetDat_DAC (102.4);				  //5V对应255,2V对应102.4
			u_dat = 200;	
		}
		else if (k5 == 1)					  //输出的电压随Rb2的值变换而变化并显示在数码管上
		{									  
			ReadRb2_ADC ();
			u_dat = rb2 * 1.961;			  // 500 / 255 == 1.961
			//u_dat = rb2;
			SetDat_DAC(rb2);	
		}
		if (k7 == 0)
		{
			ShowSMG ();	
		}
		if (k6 == 0)
		{
			LEDRunning ();	
		}
		else if (k6 == 1)
		{
			SelectHC573(4);
			P0 = 0xff;	
		}

第十一届

第十一届省赛是温度记录器,主要用到的是数码管、AT24C02,ADC,LED,矩阵键盘

PCF8951测量Rb2的输出电压

S12 切换界面 数据界面、参数界面、计数界面

S13清零 仅在计数界面有效

S16加 电压参数加0.5,到5.00再按就回到0.00

S17减 电压参数减0.5,到0.00再按就回到5.00

从参数界面退出后,将参考电压乘以10存进E2PROM,内存地址0只占一个字节

当 实际电压 <参考电压的状态持续时间超过 5 秒时,L1 点亮,否则熄灭

当前计数值为奇数时,L2 点亮,否则熄灭

连续 3 次以上(含 3 次)的无效按键操作触发 L3 点亮,直到出现有效的按键操作,L3 熄灭。

参数的定义

特别要注意的是使用矩阵键盘时,要对P4端口进行一个定义

sfr P4 = 0xC0;
unsigned int count_L1 = 0;   //L1计数
bit L1_flag = 0;			 //L1点亮标志位
unsigned char mode_s12 = 0;  //S12按键模式
unsigned int dat_rb2 = 0;    //rb2阻值
unsigned int dat_v = 0;      //电压数据
int dat_set = 200;  //电压设置数据
unsigned int count = 0;     //计数参数
unsigned char code SMG_duama[18]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,
0x88,0x80,0xc6,0xc0,0x86,0x8e,0xbf,0x7f};//0~9,A~F,- , .
unsigned char code SMG_DOT[10] =
{0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10};//数码管段码带小数点
bit dat_v_pflag = 0;         //0表示dat_v为没有大于dat_set
unsigned char invalid_key = 0;  //无效按键次数

中断函数 

void InitTimer0()
{
	TMOD = 0x01;
	TH0 = (65535 - 10000) / 256;
	TL0 = (65535 - 10000) % 256;
	
	EA = 1;
	ET0 = 1;
}

void ServiceTimer0() interrupt 1
{
	TH0 = (65535 - 10000) / 256;
	TL0 = (65535 - 10000) % 256;
	
	count_L1++;
	if(count_L1 >= 500)
	{
		count_L1 = 0;    //超过5s就开始亮 但要先把TR0这个小开关关掉,
因为此时不能保证参考电压是大于实际电压的,还要结合起来一起判断
		L1_flag = 1;
		TR0 = 0;
	}
}

数码管

if(mode_s12 == 0)
	{
		Read_rb2();
		dat_v = dat_rb2 * 1.961;  //*1.961是为了在数码管上显示为5.00
		Display_v();
	}

矩阵键盘


		if(C3 == 0)//s12
		{
			while(C3 == 0)
			{
				Display();
			}
			if(mode_s12 == 0)
			{
				mode_s12 = 1;
			}
			else if(mode_s12 == 1)
			{
				mode_s12 = 2;
				Write_24C02(0x00,dat_set / 10); 
				Delay(1000);
			}
			else if(mode_s12 == 2)
			{
				mode_s12 = 0;
			}

			invalid_key = 0;

	
		if(C4 == 0) //s16
		{
			while(C4 == 0)
			{
				Display();
			}
			if(mode_s12 == 1)
			{	
				dat_set = dat_set + 50;
				if(dat_set >= 550)
				{
					dat_set = 0;
				}
				invalid_key = 0;	
			}
			else
			{
				invalid_key ++;	
			}
        }

		if(C3 == 0)   //s13
		{
			while(C3 == 0)
			{
				Display();
			}
			if(mode_s12 == 2)
			{
				count = 0;
				invalid_key = 0;
			}
			else
			{
				invalid_key ++;	
			}	
		}
  s17省略,和s16一样

LED

void LedRunning ()
{
	SelectHC573(4);
	if(dat_v < dat_set)
	{
		TR0 = 1;
	}
	else
	{
		TR0 = 0;
		count_L1 = 0;
		L1_flag = 0;
	}

	if (L1_flag == 1)
	{
		L1 = 0;
	}
	else
	{
		L1 = 1;	
	}
	
	if ((count % 2 ) == 1)
	{
		L2 = 0;
	}
	else 
	{
		L2 = 1;
	}

	if (invalid_key >= 3)
	{
		L3 = 0;
	}
	else
	{
		L3 = 1;
	}
	SelectHC573(0);	
}

main函数

void main()
{
	InitSystem();
	InitTimer0();
	dat_set = Read_24C02(0x00) * 10; //我们是除以10存进去的
	while(1)
	{
		Read_rb2();
		dat_v = dat_rb2 * 1.961;
		if (dat_v > dat_set)
		{
			dat_v_pflag = 1; 
		}
		else if (dat_v <= dat_set)
		{
			if (dat_v_pflag == 1)    //只有下降沿才计数
			{
				count++;
				dat_v_pflag = 0;
			}
		}
		Display();
		ArrayKeyScan();
		LedRunning ();
	}
}

第十二届

第十二届省赛,主要用到的是数码管、DS1802,DAC,LED,矩阵键盘

S4 切换数码管显示界面按键 温度、参数、DAC

S8 减 设置温度参数

S9 加 设置温度参数

S5 切换DAC输出模式 模式一:实际温度小于温度参数输出为0否则为5

模式二0~20度1v,20~40,函数 40  以上5V

模式一 L1亮 、温度L2亮、参数L3亮、DACL4亮

参数设置

unsigned char duanma[18] = 
{0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,,0xbf,0xff};
unsigned char temp = 0;							   //用来存储读取的温度
unsigned char para_temp=25;                         //温度参数
unsigned char voltage=325;                          //电压值
unsigned char count = 0;							   //定时器的计数变量
unsigned char dac_k = 0;							   //DAC刷新标志
unsigned char temp_k = 0;							   //温度刷新标志
unsigned char k4 = 0;			   //记录S4的当前状态,0为温度界面
unsigned char k5 = 0;			  //记录S5的当前状态,0为模式一

键盘部分 拎重点


		if(L4 == 0)
		{
				while(L4 == 0)
				{
					SMG_Display_V();
				}
				if(k5 == 1)
				{
					if(temp < para_temp)
					{
						Read_Rd2_DAC(0);	//实时温度小于温度参数时,DAC输出0V,
						voltage = 0;
					}
					else
					{
					 	Read_Rd2_DAC(255); //DAC输出5V
						voltage = 500; //5v 扩大100 倍 方便计算
					}
					k5 = 2;
				}
				else if(k5 == 2)
				{
				 	k5 = 0;
					if(Temperature <= 20)
					{
						 //5/255 = 1/x x->255/5  
						 Read_Rd2_DAC(51);
						 voltage = 100; //1v
					}
					else if(Temperature > 40)
					{
					//5/255 = 4/x x-> 204
						Read_Rd2_DAC(204);
						voltage = 400; //4v
					}
					else if(Temperature > 20 && Temperature < 40)
					{
						 Read_Rd2_DAC((0.15*Temperature-2)*51.0);
						 voltage = (0.15*Temperature-2)*100; //扩大100倍方便计算
					}
					work_mode = 1; 
				}
		}	

main 函数

void display_smg()
{
  switch(S4)
   {
	case 0:
	  read_temp();
	  display_t();break;
	case 1:
	  display_p();break;
	case 2:
	  set_t=0;
	  display_dac();break;
   }
}

数码管显示界面切换的又一思路

你可能感兴趣的:(蓝桥杯单片机学习记录,单片机,蓝桥杯,嵌入式硬件)