前言

前面两篇主要是介绍了蓝桥省赛的一些参赛技巧,此篇主要是分享程序框架和一些客观题的链接。

程序框架

蓝桥的评分是综合了效果和代码步骤的,在比赛的最后提交时是需要源代码一起提交的,一个可读性高的代码或多或少的会给评分人好感,评分人心情一好说不定就会多几分,这样没有实现的功能就能一定程度被优秀的代码框架填补。所以在比赛和训练过程中一定要保持一个良好的编程习惯;这里笔者分享几点,仅供参考。

main+中断 两段式代码结构

在比赛过程中首先仔细分析出各个模块的任务,然后根据任务优先级别(比如说又要求刷新率的任务)进行大致规划,将有刷新率要求的代码放在定时器中断中会比较保险,至于为什么要放在定时器中断中,且看下面的分析。

单片机运行流程

单片机裸机开发的代码运行流程如下所示,程序永远是一行一行往下执行,每一个任务都要等上一个任务运行完毕后才能被执行,总是一条路走到黑。
没有中断产生时:整个程序会一路向前直至进入主循环,然后不停的在主循环里面转圈圈,从while(1)循环的任务1的第一行代码运行到最后一个任务的最后一行代码,碰了南墙后会回头继续来到while(1)的第一个任务的第一行执行。
有中断时:当初始化时的中断条件到达后单片机会停下正在执行的任务,自动进入中断服务函数去执行中断里面的任务,执行完毕后会自动返回到刚刚正在执行的任务中继续往下执行。
例:假设程序在任务1的位置时定时器1ms中断溢出标志到达,这时程序会进入定时器中断服务函数,执行中断服务函数,依次执行任务3,任务4…当中断服务函数的任务被执行完毕后,会返回到任务1刚刚运行截止的位置继续执行。

在这个运行流程中有这么几个点需要注意或者说规避的,
1.初始化时中断使能放到初始化代码的最后;中断的产生对于主线程序的运行是随机的,可能在任务1的某一行代码,也有可能在任务2的某一行代码,还可能在任务切换之间,换而言之,只要中断使能后,任何位置的代码运行都可能会被中断打断,对于主循环的函数来说还好,对于初始化的代码来说就会有影响,尤其是涉及到需要通讯时序来初始化的器件,如果通讯过程中被中断打断很可能初始化失败。因此在代码初始化的时候,尽量要将中断使能放在初始化最后,这样中断的时候程序已经进入了主循环,不会对其他的初始化造成影响。

**2.定时器中断内部不能放耗时特别长的函数;**拿1ms定时器中断来举个例子,如果任务4要花费790us的时间来运行,而任务3需要花费200us运行,此时如果后面还有一个任务5需要花费50us的时间运行,显然这些程序的运行时间加起来已经超过了1ms的中断周期,这样的话整个程序就会死在中断了,因此中断服务函数里面不能放时间复杂度特别高的函数。

**3.while(1)主循环内不要使用大于1ms的Delay阻塞式延时;**这一点也很好理解,举个反例,如下图所示代码,Keil仿真前面四个任务运行一遍的时间是差不多10ms;

当放置了Delay100ms后,仿真时间来到了103ms(这个100ms的延时是用的11.0592M晶振频率,而仿真是12M晶振所以时间有点不对);也就是说,整个主循环原本一秒钟可以循环1000次,现在只能循环10次了;这对于代码中的按键动作函数来说是致命的,原本按下就可以检测到,但是加了阻塞式延时后,这个按键我们最短要按下110ms才能被识别,这会严重影响整个代码的运行。
当然在学习过程中大家可能已经习惯了这种阻塞式延时,这个习惯是很不好的。笔者实习的时候,一个工程师就举了这样一个例子,实际产品中经常会设计急停按钮以防发生生产过程中危险,代码中如果使用这种阻塞的延时,当产线上发生了危险,一个人被绞进了设备,旁边的工人已经第一时间按下了急停,但是由于阻塞式延时的存在,没能第一时间检测到急停信号,设备没能及时停下,可想后果会是怎样。

有关非阻塞式延时和阻塞式延时的介绍可以参考吴坚鸿的《从单片机基础到程序框架》一书。这里还是推荐大家借用定时器中断来实现延时,可以参考此文——51单片机 利用定时中断做“非阻塞式”点灯。当然如果你有足够的能力,试试时间片轮询也是个不错的方式。

代码风格

除了在代码运行流程上需要注意的以外,笔者还有一些代码风格的建议;
1.建立工程时使用多.c结构;按照功能模块建立多个c文件和h文件,而不是一股脑的全部放在main.c中,这样既利于自己后期检查,也有利于评阅老师看清代码思路。

2.在函数命名上尽量统一格式;采用首字母大写的结构命名,多个单词拼接使用 “_” 来连接,且每个单词的首字母大写。

3.同一类型变量建议使用结构体来统一管理;这样有利于自己检查。

typedef struct
{
  float                kp;         //P
  float                ki;         //I
  float                kd;         //D
  float                imax;       //积分限幅
   
  float                out_p;  //KP输出
  float                out_i;  //KI输出
  float                out_d;  //KD输出
  float                out;    //pid输出
 
  float                integrator; //< 积分值
  float                last_error; //< 上次误差
  float                last_derivative;//< 上次误差与上上次误差之差
  unsigned long        last_t;     //< 上次时间
}pid_param_t;

pid_param_t Speed_PID;

4.对于界面切换或者多个模式时建议使用枚举法和switch配合;以免自己陷入if的逻辑黑洞。

客观题

有关客观题以及一些大佬的经验分享,笔者在此给大家把链接贴出来
蓝桥杯单片机准备目录
【蓝桥杯】历届单片机客观题及答案解析

【蓝桥杯单片机组】客观题(赛前必看)
你该不会还不知道吧——免费资源蓝桥杯单片机组完全版信息总结
这里强烈推荐最后一篇的内容,是一个很好的蓝桥杯经验分享系列,快上车跟紧大佬车队。

总结

有关蓝桥杯单片机的记录就到此为止,有不妥之处欢迎指出。哦,对了,大家赛前记得一定要去找一个Keil4的版本去建一下工程,尤其是在其他学校比赛的同学,有可能赛场提供的keil版本是老款,新建工程稍微有一点不一样,大家提前预防一手。最后,再次预祝各位取得让自己满意的成绩。

目录

蓝桥杯单片机组——榨干选手资源包(STC)
蓝桥杯单片机组——榨干选手资源包(芯片数据手册)