学习设计秒表总结以及感悟

2018-06-29

今天偷懒了,早上5:40起床,批评下自己;

6点10分到达实验室,学习利用51单片机实现秒表设计;

学习设计秒表总结以及感悟_第1张图片


秒表需要实现的功能:

①通过数码管低两位显示毫秒;

②高四位显示秒,从左数第三个数码管会有小数点显示;

③设计两个按键,启动和暂停是一个按键,按下进行秒表计时或者暂停;

④还有一个是复位按键,按下秒表清零;




8点全部完成整体程序学习,读代码全部读懂,每个子函数知道内部功能。

如果放在以前,我可能会把程序写到开发板,看着程序运行,麻痹自己觉得自己真的会了,但是我闭上眼睛一想,每个模块功能是什么。他们是怎么调用联系,有什么主线,如果我是这个单片机,在我的脑袋里面去模拟运行,我自己是模拟不出来的,所以就有了今天的下文;


因为看的书是电子版,如果在电脑上看程序,很多全局变量,局部变量,静态局部变量,函数调用几次就会把你搞晕。

所以我采用了自己的独家秘诀,抄程序,遇到大程序,我都会一步一步抄下来,仔细琢磨。

抄程序有什么好处,应该怎么抄?

好处:在抄程序的时候可以更仔细的去体会作者所写子函数的用意;

方法:把每一个小函数当做一个小零件印记在脑袋里,主要记忆它的功能,其次是实现函数;后面再构造整体函数的时候,就是用之前的小零件去组装。

很像小时候玩搭积木,你想要用积木做一个城堡,你要先构建每一个房屋,路,树。 然后考虑怎么去完成拼接和摆放。越来越感觉编写程序就是在玩一个拼图,或者建房子的游戏。

我抄完的程序 是这样的;


这样来回看程序的调用,很舒服,你试试就知道了。

抄写程序后,我发现我能构建起每一个子函数的功能,但是还是没有一个完整的体系在脑袋里面能模拟出这个秒表是怎么运行的。我把程序精简化。

只留下 全局变量 子函数 main 和中断函数 是这个样子的


在纸上把个个函数之前的关系,谁调用了谁,完成了什么功能,对全局变量有什么影响全部都整理出来。现在发现我对这个程序已经简单了解了,在脑子里面我能大概的完成很多模块的相互间调用和对全局变量的更新;

目前为止,程序编程也就这些,但是我感觉还是不到火候,虽然了解了函数之前的调用,来回对全局变动的改动但我并没有找出,设计者的思路,下一次我自己设计的时候,我还是没有站在足够的高度,我还是不会达到程序设计者的能力,这么好的程序例程,如果就这么被学到这个程度,我感谢愧对自己和前辈。

接下来怎么做?  接下来说的就是程序设计问题,我不是专业学习计算机出身,所以我没有学过如果去完成一个程序总体设计,只能凭着蛮力去自己总结试试,下面的方法如果不好,请大你提出来,以便我自己改进自己的思路,在交流中得到提升,我会很感谢你。


程序设计的主线:  关于主线的思考, 程序一直执行,是用while函数实现的,它是老大,它赋予了所以子函数能被调用的机会; 还有一个重要函数是 定时器中断函数,中断函数,也是具备调用子函数的能力;

基于以上考虑,我把主线分为两条 分别为 主函数和中断函数,

那么下面看看主函数和中断都做了什么;  (这里说的主函数不是main函数。而是while一直运行的函数)


在这之前还需要提前说两个标志位,很多函数是否被调用或者执行,都是看标志位的状态,这个也是程序设计的基本思想(我今天才懂那么一点,嘿嘿)

秒表运行标志位WatchRunning:当秒表正常运行时 标志位为1,在秒表暂停时为0;

秒表上的数值是否需要更新WatchRefesh:当秒表running,每过0.01ms对应显示器数值加1,这是就需要把WatchRefesh置1,表示要更新显示器上数值。如果秒表处于暂停状态或者复位状态,WatchRefesh为零;

这里有人可能会想,这两个标志位应该合为一个,秒表跑的时候不就是应该刷新吗?秒表不跑不就是不应该刷新?你定义了两个不是不重复的,其实不是的。

原因是这样的,单片机的世界里每纳秒(1秒等于1000000000纳秒)相当于咱们现实时间的一秒,你一秒过去的很快也没有做什么事情,但是单片机不一样,他在一秒内已经完成很多操作;

在秒表running状态下,每间隔0.01s(也就是10000000us)才会让秒表上更新显示数据(之前开篇提到的设计要求)。单片机的世界要每隔10000us才会去更新一次现实数据,所以说这两个状态标志位是不同的。

有了以上基础,来完成最终的程序构建

学习设计秒表总结以及感悟_第2张图片

中间两条是秒表的两个状态标志

上下是while 函数和 中断函数 的内容,中间带箭头的线是每次执行的时候对状态的判断和影响;

写到这不知道还有没有人还会看,仔细看懂这面的图,印到脑子里面,我感觉定时器算是掌握的差不多了。

之前大学学过程序流程图,但我一点都不喜欢那东西,说真的一点不直观,我不知道我画的应该叫什么图,但是我感觉能有助于别人理解的就是好图。

忽然想起来之前看一个师兄答辩的事情,他因为程序流程图画的不规范而被一个教授喷,那么垃圾的图,早就应该放弃了,怎么还去研究那么细节。值得么? 具体值不值得我不懂,因为我没到那个水平,没法评断,反正我感觉无聊的一B。


说到这,我还想说,我发现  手把手教你学51单片机-C语言版 的这个例程也不是很完善,贴出来大家看看 

/* 配置并启动 T0, ms-T0 定时时间 */
void ConfigTimer0(unsigned int ms)
{
unsigned long tmp; //临时变量
tmp = 11059200 / 12; //定时器计数频率
tmp = (tmp * ms) / 1000; //计算所需的计数值
tmp = 65536 - tmp; //计算定时器重载值
tmp = tmp + 18; //补偿中断响应延时造成的误差
T0RH = (unsigned char)(tmp>>8); //定时器重载值拆分为高低字节
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零 T0 的控制位
TMOD |= 0x01; //配置 T0 为模式 1
TH0 = T0RH; //加载 T0 重载值
TL0 = T0RL;
ET0 = 1; //使能 T0 中断
TR0 = 1; //启动 T0
}

这个函数能实现配置 定时器起始值 的功能,但是当 ms >75时 tep =65536-tmp;此处溢出,导致程序奔溃,

应该在程序入口加入限定。


/* 秒表计数显示函数 */
void StopwatchDisplay()
{
signed char i;     //注意 必须为signed 类型,因为后面有一个if语句判断i>=0如果是unsigned会永远大于0,进入死循环,
                    别问我是为什么知道的!!!血的教训
unsigned char buf[4]; //数据转换的缓冲区
//小数部分转换到低 2 位
LedBuff[0] = LedChar[DecimalPart%10];
LedBuff[1] = LedChar[DecimalPart/10];
//整数部分转换到高 4 位
buf[0] = IntegerPart%10;
buf[1] = (IntegerPart/10)%10;
buf[2] = (IntegerPart/100)%10;
buf[3] = (IntegerPart/1000)%10;
for (i=3; i>=1; i--) //整数部分高位的 0 转换为空字符
{
if (buf[i] == 0)
LedBuff[i+2] = 0xFF;
else
break;
}
for ( ; i>=0; i--) //有效数字位转换为显示字符
{
LedBuff[i+2] = LedChar[buf[i]];
}
LedBuff[2] &= 0x7F; //点亮小数点
}


源程序就不贴了,在 手把手教你学51单片机 C语言版   161-166页  宋雪松 写的



至此 告一段落 13:55  利用中午睡觉时间码出来这个,整体对这个编程思路有得到一次提升和夯实吧;

其实有很多专业术语我不是不会,而是真的不喜欢用,我希望我写的东西,初中水平就能看懂。这样更有利他们翻看。

更希望我的父母能看懂,因为。。。你估计不会懂。

为什么这么想是因为我在本周一25号花了12小时不到读完了一本神经网络的书叫《Python神经网络编程》,他是我第二喜欢的书,一晚上让我从一点不懂神经网络,但对神经网络怎么运行,权值运算,各种实现全部了解,虽然不会特别高深,但是让我对神经网络应该算是入门了。

当时我想什么是大牛,大牛就是当你什么都懂的时候,你写出来的说出来的,依然能让非专业的人也能懂,让他对你的东西感兴趣,到最后一步步深入浅出让人入门。可是真正写书的时候有几个人能够放下姿态来这么做?看看现在的人写出来的书,看一页就像撕了。

不说了,学习去。

good luck。





你可能感兴趣的:(C-51的血泪史)