高考没考出水平,大大影响了我的心情,打算复读,但随着时间的流逝,热情逐渐褪去。几乎用了半个月才调整好。漫长的暑假,总不能全用来调整心态吧,于是按计划,重新接触编程,编程带来的快乐很快就把阴霾驱散了。而且对大学生活的憧憬令我打消了复读的念头。
重新接触编程好几天了,进展神速,感觉有必要记录一下这段学习经历,这将会成为我宝贵的经验啊,待我成为高手之时,把这些记录拿出来,写教材,写书都容易啦。哈哈。我也希望通过这种方式,结交同道中人,共同学习。
在这篇文章中先把前几天学习的内容记录一下,以后每天更新,记录编程中的酸甜苦辣。
高一的时候在学校制作机器人的经历至今记忆犹深,组员诗恒编程技术的高超让我惊叹。高一的时候,他就有好些电路板,跑马灯之类的小制作,佩服之余又羡慕他能有那么多资金去制作这些东西,一起为参赛而努力的时光值得我一生去珍藏啊。我在高二在参加机器人竞赛之余又参加了信息学竞赛,这过程中学会看懂c语言的比较高级的程序,自己也能编写几个程序去解那些弱弱的题目。高三奋发图强,把编程搁在一边,努力备考了,编程荒废了一年,忘记得七七八八了。
在这里跳过知道高考成绩的调整期。
因为觉得学单片机比较好玩,而且比较实际,于是就打算先不学习以前那些抽象的算法,打算学单片机了。
大约是在7月7号吧,在淘宝看中了一个51单片机开发箱,觉得挺好,还包括电烙铁,万用表之类的东西,下了订单。同时在网上找了很多电子电路,单片机,三极管之类的教程。可恶的是那卖家速度比蜗牛还慢,在15号才送到,打击我学习的热情。收货那天顺便买了本书《51单片机自学笔记》。
买了书,当然是很热情地看啦,可惜这本书开头那10几页介绍51单片机组成,寄存器,引脚之类的东东,看不明白,后来我又在网上看了一些资料,终于半知半解地看完这部分。
之后就一直按着这本书去学习了。先是“一个led闪烁”。那个单片机与led灯连接的图还是不太懂,但因为有开发箱,实际电路部分不用我去焊接,所以我可以直接编程序了。
这个程序没有保存,太简单了,试验一次成功之后我就把它删了。不过这个程序虽说是简单,但还是比c语言中的“hello world”难得多,有些概念不懂还真的理解不了,从中还是学到了不少。
比如:org 0000h
sjmp start
org 0030h
start:
*********
就是这一段,我也是到后期写多几个程序,在keil中调试时看寄存器的状态时才真正弄懂:单片机开机时,从地址为0000h的地方开始执行命令,而sjmp start就存放在这个0000h中,所以一开始单片机就得到命令,跳到0030h,也就是start存储的位置开始执行,至于为什么要跳过,现在也只是知道不跳过可能会出错吧。呵呵
接下来就是为什么程序中向p0口输出数据就可以控制led的亮和灭了,虽然以前也知道计算机中的01 0101是代表高低电平,也代表一个数字。后来明白,p0口也是寄存器,我们把某个数字写进去,比如1111 1110写进p0,比如写16进制的数:#0ffh,和二进制的11111111b,10进制的***是一样的。那就可以控制与p0口相连的8条线路的高低电平状态,又根据发光二极管的单向导电性来控制led灯的亮和灭了。
还有一个重要的就是delay,延时子程序了。教材中
delay:mov r0,#250
d2:mov r1,#250
d1:djnz r1,d1
djnz r0,d2
ret
我想,如果没有竞赛时训练出来的读程序的基础,对初学者来说,这的确不容易理解啊,
特别是那djnz ,r1,d1 它的意思就是令r1寄存器实现c语言中的 r1--,连续减250次,然后执行下一句
,令r0--,r0减一次之后,就跳回d2:那句,给r1重新赋值250,再循环。总共执行250*250次。这个是搞懂了,但它为什么能延时啊,才知道以前自己学c语言从不理会程序执行的时间,单片机执行每个程序都要一定的时间, 250*250次,加起来的时间就有零点几秒了,人就可以打破视觉暂留,看到led的闪烁了。
接下来学了个控制8个led灯的程序,从位操作指令变成字节操作指令,有之前一个led的铺垫,不难。
难的是再接下来的“led万能闪烁”,它实现了只修改数字,就可以改变led的闪烁状态。
从中学会了查表,单片机的指针,还有累加器的使用。
例如:mov dptr #tab 就把标号为tab的表的内存地址给了dptr指针,实现查找功能
再者 movc a,@a+dptr 是把原来a的数值加上dptr指针中的数值作为内存地址,去这个地址中的数据,再把这个数据的值赋给累加器a,可恶的是这个批令不可以把a改成任何一个寄存器,所以每次使用都要重新给a赋值再使用,这是单片机的缺陷吧。还要说一下调试中出现的错误,我把movc写成mov了,错,还有很容易忽略中间的“,”逗号。
接下来是单个数码管的程序,很简单,作为过渡吧,了解到共阳极码,共阴极码这些东西,明白计算机真的是挺笨啊,只不过是控制了电路的通断而已,通过控制8个led管的亮与灭,来实现显示人类理解的数字(我在此不是否定人工智能,我是持人工智能总有一天超越人类的态度的。)
接下来动态显示生日的程序有一定难度。做了这个程序,终于明白所谓的扫描是什么意思,计算机高速扫描,人类因为视觉暂留,看到一整幅图像。这个程序的原理是控制8个数码管(不是8个led管哦, 8个数码管=64个led灯管)因为51单片机最多也只有32个引脚,也就是可以控制32个电路,每个电路控制一个led灯的亮灭,所以要8个引脚才能控制1个数码管的数字显示情况,而64个led管足足比32个引脚多了一倍啊。在此我不得不感叹前辈的才智。这个程序只用16个引脚(电路)就控制了64个led的亮与灭。输出数据的8个引脚分别与每个数码管对应的被称为a,b,c,d,e,f,g,dp,的led灯相连,结果是各个数码管相同称号的led管并联起来,所以输出数据的电路一旦发出11110101之类的信号,就能控制8个数码管输出相同的数字了,但是这样就不能实现显示生日了啊,毕竟生日不是同一个数字组成的2222,2222,我要显示的是1991,0410。所以就用到另外8个引脚了,每个引脚与1个数码管相连,用于控制整个数码管的通断,所以我就可以实现数码管一个一个地输出数字了,当前8个引脚(数据总线)给8个数码管输出1这个数字时,我再用后8个引脚(控制总线)来控制第一个数码管亮,其余数码管灭,同理,熄灭第一个数码管,数据总线输出9,控制总线使第二个数码管亮,其余灭。如是逐个输出数字,当19910410这几个数字逐个逐个出现,刷新速度极快的时候,我们就可以看到不变的,整体的生日了。
在这个程序中我参照了教材,做了一点创新,教材的程序控制通断是用位操作指令的,异常繁琐,我先用了一个查表的方式,后来用了跑马灯的 RL操作指令,简化得接近完美,哈哈
还有个循环自加计数器的程序,这是目前为止最难的一个了。它包括初始化,主程序,自加子程序,显示子程序。这里主要有两个问题:一,每数码管是分立的,显示36这样的两位数不能直接告诉数码管显示36,查表的话会很复杂,而是要告诉它,第一个数码管显示3,第二个数码管显示6,这样人就能理解是36了,数码管无头脑,它不用这样理解。二,程序自加速度太快,我们需要数码管显示一个数字,然后要停留一段时间,人才能分辨出来,否则速度极快的时候,数码管只是显示88这两数字(每次显示数字不同,且刷新速度太快,视觉暂留,看起来16个数码管都一直亮着,所以看起来像88)。解决方法是自加一次后,两个数码管不刷新同一个两位数,这样看起来就像停顿一样了,这个刷新过程和显示生日的大同小异,先显示3,再显示6,循环一定次数,然后数据加一,显示37。
这个程序中还学到了几样东西,count equ 30h 这个伪指令,意思是给地址为30h的内存空间取个名字叫count。还有div ab,意思是用a中的数除以b中的数,商存储在a中,余数存储在b中。另外,cjne这个指令
不能用内存空间中的数与另一个数比较,如只能先把count存放在寄存器r0中,再用cjne r0,#10这样比较,也可以用a来代替,但不能直接cjne count,#10。
终于写到我今天学的内容了,写完这个明天就可以分开一篇一篇来记录,不那么长啦。
今天学的是按键处理。
main:jnb p1.4,led0 ;如果p1.4低电平跳到led0处
jnb p1.5,led7
sjmp main
led0:cpl p0.0
sjmp main
led7:cpl p0.7
sjmp main
学了个jnb,jb,简单指令,不值一提,要提的是我自己写程序时把led0,和led7中的sjmp main改成了ret
发现p0.0控制这个灯一直在亮,可知程序出错了,错在我是用jnb跳进led0的,而不是平时的call,没有用call而用了ret,就不知道是不是跳到060h这个初始化的指针地址了。所以一直在亮。
另一个问题是按键会产生波形数据,按键有时会失灵,这就是进入到了模电的知识吧,因为波形不是直接的1010,所以会出现失灵的现象,需要去抖动处理。明天我再编写这个去抖动的程序吧。
写了这么长的一篇文章,内容不深不浅,不明不白,外行人看不懂,内行人不想看。或者这篇文章就只可当作我自己的记录吧,以后会逐渐改进我的写作啦!还会再加上我的程序代码文件和单片机实验现象。