电子时钟 需要的变量有:year,month,day,hour,min,sec,week
储存它们的方式有很多,我这里选择了结构体,好处是整个代码的可读性比较高,缺点是结构体内的内存分配并不连续,占用的空间相较于数组可能会大一点。
确定好存放变量的形式后,就可以开始考虑实现具体的功能了
分析基础任务,关键词有两个,串口助手和每一秒打印一次
我这里选择先实现变量的每一秒自增,为了精准度和灵敏度,我选择使用计时器0来进行计时。在计时器0中,实现每一秒sec的值加1,然后依次进位。到了月份更替的时候,我利用了数组存放了每个月的天数,当天数大于该月月数时,天数减去该月月数,月数加一,实现了月份的增加。
之后就是考虑用串口输出这些变量。这是基础任务中最难的部分,我前前后后修改了很多次。关键就在于,用结构体存放的变量都是数据类型,并且是十进制的数字,而串口助手能接收的并发送的,只有文本形式或者HEX十六进制形式。一开始,我是使用了ASCII码的规律,即十进制数加48即为文本数字对应的ASCII码。而其中的如'-'和'(space)',我选择了直接传递给串口助手 e.g. 1+48=49,对应的文本‘1’,则串口助手也可以识别到。
接着第二个难题出现了:存放的变量都是两位数,甚至年份year是四位数。故而需要运用公式,将其十位数和个位数换算出来,再传递给串口助手。 e.g. month = 10;month / 10 = 1;month % 10 = 0;然后再各自+48传递给串口助手,这个问题也就解决了。
第三个难题是星期几的输出。ASCII码中没有对应的数据可以直接传递给串口助手,所以只能直接以文本形式传递给串口助手。即直接将WED在接收窗口直接打印出来。我这里选用了switch函数,0对应"SUN",1对应"MON",将文本逐字传递给串口助手即可。在这里也可以用数组形式传递文本,但是51芯片的内存有限用此方法占用的空间很大,所以我舍弃了数组的方法。
进阶任务我选择了先从与基础任务中的代码有关联的开始做
串口助手快速调整时间
这个相较于基础任务难度更上了一层楼。 难点是对接收字符串的转化。从SBUF寄存器中接收到的文本,利用函数,将其存入到字符串数组中,接着也是根据ASCII码的对应关系,将数组中对应的文本 -48 转化为数据,再通过运算赋值给结构体变量
数码管显示任务
这个任务倒也不难,将作业中的代码拿过来用就可以了,变量也是一样的,如显示月份与日子,就把两位数的月份用数码管1、2显示,日子用数码管3、4显示,时间同理。这题的难点在于用按键切换两个数据的显示。由于已经用了计时器0,和串口中断,如果此时再用外部中断0或1的话,串口之间会互相干扰而且计时器的精度会下降。故我这里直接在主函数中判断KEY1是否有被按下,并用choice这个变量来存放是显示月日还是时间的标志,然后不断用switch来判断是在数码管上显示月日还是时间。
流水灯任务
流水灯任务是十分简单的,将其放在计时器0中,每一秒变化一次LED灯的状态即可
设置闹钟任务
此任务的核心是记录所要计时的秒数。我使用了两种设置闹钟的方法。我定义了一个全局变量tick来记录。第一种是通过按键KEY2来增加tick变量,适用于快速增加倒计时的秒数,方便快速,其缺点是无法精确设定倒计时。故第二种方法是通过串口助手来调整倒计时的时间,通过第一个字符位C再加上四位数,如“C0123”,便可以设定一个倒计时为123秒的闹钟。蜂鸣器的使用则再tick == 0时触发即可,再次按下KEY3,停止蜂鸣器。
断电保存任务
此任务用到EEPROM,整体思路是使用第一扇区,即0x2000~0x21FF的空间来存放我定义好的结构体变量。然后每一秒sec自加时,擦除第一扇区,然后逐个写入当前变量的值即可。而读取放在主函数中,每次通电时读取一次扇区内的数据,并给到相应的变量即可。
自定义串口助手
其实这个任务看起来难,完成起来却并不困难。通过学习视频的内容,写好对应的代码,将串口连接好即可使用了。当然,使用前也是需要先用ISP下载好程序,在用自制的串口助手运行即可。
理解上的不透彻
在使用51单片机中断服务函数的时候,对于中断优先级的认识和考虑不够深入,导致在串口助手和定时器0的处理上折腾了小半天,各种方法都尝试了,最后请教了毛助教和同学,发现我的中断优先级没有设定好,导致定时器0中断了串口助手,让其无法正常输出。一开始我也有考虑到中断优先级的问题,但检查代码时发现我是重复定义了TMOD,导致第二次定义的覆盖了第一次定义,相当于中断优先级还是原来那样。这也提醒了我在写代码时,不能贪图方便,直接将已有的代码复制粘贴,而是要逐句检查,防止一些bug的出现,这样反而会更浪费时间!
函数的实现不够简洁
这个问题体现在最后,我想实现更完善的功能时,发现报错了。报错的原因是内存不够了。该来的总该会来,于是,我重新编写了一下函数,在数据类型转化为字符串类型和输出字符串的函数上进行大量的删减,最后刚刚好实现了所有的任务。之后我也有询问周围的同学,也有遇到和我一样的情况,都在尽力减少代码的长度。最让我值得敬佩的是我们的组长,实现的功能即完善,所余留下来的空间也十分充足,事后一定要看看她的代码才行。
写代码的良好习惯
这次大作业让我意识到了良好的习惯有多么重要。
简洁明了的注释既方便自己Debug,也让助教看得没那么痛苦,可以更快帮助自己找出问题所在
函数模块化的思想,方便多次运用,也方便自己修改和升级完善。而在布局的时候,也可以直接忽略实现函数的具体代码,专注于整体的架构
备份很重要!备份很重要!备份很重要!
Debug、自学能力的提升
自学能力是很明显的,一周下来,每天都在电脑前学习,需要极强的定力和自学能力,我在其中得到了充分的磨练。 而这次大作业真的会遇到各种各样的小问题,这时候就很考验Debug能力。这里又要夸夸我们的组长,Debug能力一流!我也在帮同学Debug的时候学习到了很多,有则改之,无则加勉,共同进步。
一些珍贵回忆
虽然学习、接受新知识是一件艰难的事,但好在我并不是一个人,助教们,同学们都给予了我很大的帮助,我十分感谢他们。同时,在下课放松后我们也会闲聊,分享各自遇到的问题,讨论一些好玩儿的事情,了解互相的学习进度,分享自己成功的喜悦。这些都是我枯燥学习中最快乐的时光。
本文章使用limfx的vscode插件快速发布