蓝桥杯单片机第七届国赛笔记
Posted 时光654
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝桥杯单片机第七届国赛笔记相关的知识,希望对你有一定的参考价值。
蓝桥杯单片机第七届国赛笔记:
做完这届题目之后有很多感触和一些好的思想,想分享一下顺便巩固巩固
1.摆脱标志位的依赖:
好的编程我认为是用最简洁,清晰的程序 ,“最近的路径” 去实现某个功能。如果遇到问题就做标志位这种方法我认为有点low了,对思维和编程能力的提高效果微乎其微,以后的编程中我也一定要避免这个问题。
2.再写完程序后,发现现象不对,一定要进行总结,为什么不对,效果没出来的原因是什么?
3.制造寄存器,(这是我起的名字)这个套路非常,非常,非常实用本次题目。
下面就开始正文了:
别被题目吓到,这次题目方法用对,它就是 “Paper Tiger”
这届虽然简单,但我感触颇深,所以写篇总结记录下。
一、个人错误篇:
首先我仅仅我在实验中错误地地方列出来
1. ‘&’和‘&&’:
(对应上面,一个是‘位与’和‘逻辑与’ )当时也不知道咋想的,感觉逻辑与好看,就吧矩阵按键部分应该与的操作都换成了逻辑与 看程序吧;
#include "bsp_key.h"
unsigned char key_trigger()
unsigned char number = 7;
unsigned char temp;
P44 = 0; P42 = 1;
temp = P3;
P42 = 0; P44 = 1;
temp = (temp << 4) | (P3 & 0X0F); //这里的(P3 & 0X0F) 我写成了(P3 && 0X0F)
switch(~temp)
case 0x80: number = 4; break;
case 0x40: number = 5; break;
case 0x20: number = 6; break;
case 0x10: number = 7; break;
case 0x08: number = 8; break;
case 0x04: number = 9; break;
case 0x02: number = 10; break;
case 0x01: number = 11; break;
case 0x00: number = 0; break;
default: number = 0; break;
return number;
上面错误点我已经备注了,错误原因很简单:(但就是这个我程序按键部分全部无效)
原因:(针对第二列按键)
无按键按下时:
P3第四位默认I/O都是高电平,逻辑与以后也就为 ‘真’。也就会导致temp低四位部分一直为 ‘0001’,高四位不会影响。然而进入switch进行匹配的也就xxxx0001 取反以后就成了 xxxx1110也就是0X‘X’E 显然肯定不会有匹配的按键,所以无论按下呢个按键都只对应进入default指令。
有按键按下时:
按下小于4个按键时,都会造P3低四位至少有一个I/O口为高,同样保证了逻辑‘真’
第二列全部按下时,也就造成P3低四个I/O口都为‘0’也就造成了逻辑‘假’,但取反以后为1111同样没有对应的case。
2. 强制转化:
这部分有歧义。
sprintf(seg_buf,"-2-%05u",(unsigned int)(100000 / read_555_number));
这里是用sprintf将字符串打印到seg_buf中去。
有的说占位符是整型,如果表示浮点型的数,呢这个浮点型的数将会被强制转换 。
但我通过实验现象证明,需要加上强制转换,毕竟加上强制转换也更加牢靠,就算自身不带,也不会出现问题。
(如果不加现象就是显示数据变成乱码)
3. 马虎大意
(1)
Write_Ds1302_Byte(0x8e,0x00);
temp = (((timer_init[0] / 10) << 4) | (timer_init[0] % 10));
Write_Ds1302_Byte(0x84,temp);
temp = (((timer_init[1] / 10) << 4) | (timer_init[1] % 10));
Write_Ds1302_Byte(0x82,temp);
temp = (((timer_init[2] / 10) << 4) | (timer_init[2] % 10));
Write_Ds1302_Byte(0x80,temp);
在DS1302中我竟然把 % 写成了 * 导致写入初始时间错误。
(2)
定时器0读取频率,我竟然用定时器1来读取,我也是醉了。
二、优点篇:
1 .制造寄存器
(这一优点在本次例程中体现的淋漓尽致,不仅方便审查错误,而且思路也很清晰。)
unsigned char running_mode = 0x00;
0x10 | 0x11 |
---|---|
频率 | 频率周期 |
0x20 | 0x21 | 0x22 |
---|---|---|
电压参数 | 电压上限阈值 | 电压下限阈值 |
0x40 | 0x41 | 0x42 | 0x43 |
---|---|---|---|
时间 | 小时设置 | 分钟设置 | 秒设置 |
0x80 | 0x81 |
---|---|
最近一次电压波动的类型 | 最近一次电压波动时间 |
(像上面这样定义一个寄存器,不同情况存储对应的值,当我们需要对不同情况进行处理的时候只需要判断这个一个标志位就可以了。省了很多标志位,思路也很清晰。)
下面用例子来说明其好处:
void key_pricedure()
case 7: //时间
if(running_mode & 0x40)
running_mode = 0x40;
write_timer_fun(user_timer);
else running_mode = 0x40; //进入else语句使
break;
//.
//.
//.
case 4: //功能
switch(running_mode & 0xf0)
case 0x40: //时间
if(++running_mode == 0x44) running_mode = 0x41;
break;
void seg_pricedure()
switch(running_mode)
case 0x40:
read_timer_fun(user_timer);
sprintf(seg_buf,"%2d-%2d-%2d",(unsigned int)user_timer[0],(unsigned int)user_timer[1],(unsigned int)user_timer[2]);
break;
case 0x41:
case 0x42:
case 0x43:
sprintf(seg_buf,"%2d-%2d-%2d",(unsigned int)user_timer[0],(unsigned int)user_timer[1],(unsigned int)user_timer[2]);
break;
//.
//.
//.
解释:
(第一次按下S7进入时间显示界面)
第一次按下S7时 由于寄存器初始化时0x00,所以if括号内为0,(0 & 任何数 = 0) 也就不进入if语句,进入else后使寄存器制位成 ox40对应我们提前的配置,可以看出程序进入了时间显示界面,在显示处理函数中判断我们的寄存器,发现寄存器变成0x40 对应开始读取DS1302时间到user_timer[ ] 数组内,并开始显示数组的内容,也就是当前时间。
(按下S4进入时间设置界面)
我们按下S4 以后switch语句判断我们寄存器高四位,(细心的同学就发现了,我们寄存器高四位存放的是模式,低四位存放的是该模式下的状态。)由于running_mode & 0xf0 就代表我们只判断高四位,低四位不考虑其值,根据我们的模式,进行状态改变,根据我们对寄存器的配置可以看出,如果不断按下S4 呢我们就处于在时间设置的 时,分,秒的状态不段切换,在通过加减按键对其更改。
说到这里,此处有个问题不知道大家考虑进去没?
如果时间还在一直走,呢设置起来不是很难受,所以我们在设置的时候不能让时间走了。
实现的方法就是只有在时间显示界面下(也就是0x40)让其读取时间到数组内,其他设置状态(0x41,0x42…)不进行读取时间,也就时间了时间暂停的效果。
(题目要求再次按下S7时,表示时间设置成功。)
当我们再次按下S7 时,判断 running_mode & 0x40 是否为真,如果我们没有按下其余模式按键我们寄存器的高四位依然是0x40 ,所以if括号内为1 (任何数 & 本身 = 任何数) ,也就说明我们是第二次按下S7了,根据题目要求我们需要对DS1302重新赋值成我们改边后的参数,同时进入了时间显示界面。
这里也有一点应考虑的!
如果在设置时间按下其他模式按键,效果是怎样的?
我这里只说结论,原理大家想一下便知其所以然。
答案是:
进入其他状态,但下次按下时间显示界面的时候依然是按照设置之前的时间一直在进行加加。
呢这是一个,两个,三个怎么办?
如果你理解了这个技巧,你也就不用担心这个问题了。
2 .减少标志位
我以后也一定减少使用标志位,这样对个人思维的提高真的很有帮助。
(相对于标志位而言,前两天还听到有人说“遇到问题怎么办,一个标志位不够就来十个”真的是说到痛点上了,所以得改)
这里看完我写的,1s闪烁以后,我就对我以前写的说 拜拜!
void timer1() interrupt 3
dida_number++;
//*
//*
//*
if((dida_number - seg_delay) > 1000)
switch(running_mode)
case 0x41:
seg_buf[0] = ' '; seg_buf[1] = ' ';
break;
case 0x42:
seg_buf[3] = ' '; seg_buf[4] = ' ';
break;
case 0x43:
seg_buf[6] = ' '; seg_buf[7] = ' ';
break;
case 0x21:
seg_buf[0] = ' '; seg_buf[1] = ' '; seg_buf[2] = ' '; seg_buf[3] = ' ';
break;
case 0x22:
seg_buf[4] = ' '; seg_buf[5] = ' '; seg_buf[6] = ' '; seg_buf[7] = ' ';
break;
if((dida_number - seg_delay) > 2000)
seg_delay = dida_number;
这个原理太简单,我也就懒得介绍了。
大家琢磨琢磨吧,如果能用上你就知道这个的强大了!
大佬在我程序上好的建议请留言! 感谢!!!
if(提出程序改进合理建议的)
需要的话可以送我写的历年程序,虽然不咋滴,但希望对你有帮助!
还有一些学习资料。
else
需要我写的历年国赛、省赛全部程序的私聊即可,但可能有偿哦!但是加售后。
(国赛目前写了6届,省赛写了11届+1小蜜蜂老师的光温检测)
但有问题可以来私聊,虽然我也是个小白,但我会的尽力解决!
🐱🚀🐱🚀🐱🚀
以上是关于蓝桥杯单片机第七届国赛笔记的主要内容,如果未能解决你的问题,请参考以下文章