超易单片机入门 (学单片机一定要看)
( 向作者致敬!)
一.
话说人间事物的学习,不外两类:一类入门易精通难,比如矿石机,再比如练书法谁都会写两下,可写好却不是一朝一夕的事儿。另一类则入门难精通易,比如超外差收音机,学会需要各种放大、振荡、反馈、混频、中频、统调等等,但是一旦你学会,做出一个高质量的收音机,确也不是难事。单片机属于后者,所以无论如何,你得用些时间,学习一些新名词并理解,甚至还得花一些小钱。但是,一旦你掌握,其乐趣也像收音机一样,很有吸引力的。 单片机不同于收音机,收音机的任务最终就是听好声音一个,单片机可以做很多事情,要你来安排它的工作。安排它就先的认识它,熟悉它。大家自己查看一下单片机的外形,不妨将它看成是一个8条腿的小螃蟹,这个8条腿的小螃蟹就是我们的第一顿饭,只要把它吃下去,以后的大餐就好办了。 第1、8条腿接电源 +5V和地线。头两条腿是螃蟹钳子,好吃的很。现在已经剩下6条腿了。 第2、3条腿使用时外接一个叫作晶振的东西,我们接一个4MHz的晶振(这还不知道?还是看一下:单片机之路(www.)里面对晶振的介绍吧)。 第4条腿是复位脚,是一个信号输入脚。单片机正常运行时接高电平。当有一个低电平脉冲输入到这个脚时单片机就复位。所谓复位就是单片机内部所有的工作部件统统回到规定的状态,程序也复位到头一句上开始逐条运行。例如,你设计的一个报警锁定的LED红灯亮后,当需要解除报警时,用一个按钮给这个脚瞬时接地一下,相当于给它一个夫脉冲,系统就复位了,led灯就熄灭了,程序从头开始。 以上5个脚,几乎所有单片机都有,包括世界上最复杂的,和世界比较简单的单片机-PIC12CE519。 轮到底几条腿啦?奥,是第5条腿,这条叫单片机的I/O脚。就是输入输出脚。你可通过程序动态地控制它作为输入或输出,作为输出时可以程序控制它的输出电平为高1或低0。所以,他的工作状态有四种: 剩下的两条腿和第5脚功能一个样。 现在要控制使用这两只腿了,这不得不首先要讲一下软件,要想讲明白软件又不得不涉及到单片机的内部结构。可能有朋友说啦,可别提这软件和结构了,以前俺就是让它们打败的,现在听到这个心里就打鼓。嘿嘿,不要紧,果真如你所说,那你就不妨跟着我再失败一次,反正多一次失败又不纳税吗,嘿嘿。不过你也要有思想准备,彻底弄明白是个渐进的过程。 要说起这程序和单片机内部结构,还真是老大难,不过蟹黄蟹肉可都在里面。我现在要是给你说PIC单片机是哈佛结构的,51系列是冯-诺伊曼结构的话,恐怕你要立马走人了。所以我得用点心思不让你溜号。 好在PIC系列的制造商(microchip 微芯公司)理解我等苦衷,全部只有35条指令,而且有一些指令我们一般很少使用,常用的也就十几句,用的时候查手册,无需记忆。就算我们两天学习一句,也就两三个月时间,总比到老了还怕它们强啊。废话少说先看下面的两个例语: my_name006: movlw 02h '常数2进入w 每行只能写一句话; 标号: 操作指令 操作数 '程序注释 下面我结合例子把这四部分解释一下。 第一部分 my_name006: 叫做标号,它是由字母或数字组成,由冒号结束。标号可有可无,比如第二句就没有标号。 第二个例句中,省略了标号,当然注释也可以省略。他的指令是movwf,操作数是GPIO。操作数不一定是数字,也可能是一个由字母组成的字符串 这两句话的作用是把 2 这个常数写入到 GPIO 这个寄存器里。 每个寄存器可以储存一个八位的二进制数。这八个位的每一位都有名称,从左向右的名称是: 而每一个位对应一个管脚的电平,例如当GPIO寄存器的D0位等于1时表示管脚GP0的电平是高电平。D0位等于0时表示管脚GP0的电平是低电平。常数2的八位二进制表示是“00000010” 所以,GPIO寄存器存放的8位2进制数的每个位的值以及管脚电平是: 截止到现在,你已经学会如何控制管脚的电平高低了。尽管还有一些疑问,比如怎样定义管脚为输出脚(以后会说),我得说,如果事先GP1,GP0这两个管脚处于输入状态,这两个例句无效,因为在这种状态下它们是控制不了电平的。 无论如何,这一会儿,你就学会了两个指令,35条我看也没啥难的。 下面再加深一下对寄存器的认识: 要把一个常数存储到寄存器,或者说写到一个寄存器中,仅用一条指令是办不到的,必须通过一个特殊的寄存器W,把数据倒过去. 这就应该使用到下面两个语句: movlw 02H 指令的意思是把一个常数存入特殊寄存器W, 这个常数是3,后面的H是表示十六进制 这里涉及到两个概念,常数和寄存器: 常数好说,比如说十进制数35,26。但要注意,在单片机系统里我们一般不用十进制,而使用十六进制。有关数制转换方面的知识,是计算机的基础,必须会熟练地在二进制、十六进制、十进制之间转换,此处不再罗索。 寄存器也叫单片机的内存,一个寄存器可以存储的数值范围是0--255,用十六进制表示就是0--FFH.用二进制表示就是00000000----11111111。“单片机之路”提醒您:以后要养成习惯用十六进制表示数。 那么,一个单片机里有多少个这样的寄存器哩,pic12ce512里面有1024个这样的寄存器可以供你使用,为了使用方便生产商已经给它们编上了号码,第一号码是000H,往下依照次序为 001H,002H........3FFH.(怎么样,开始用十六进制说事了吧,如果你不熟悉熟制转换赶紧补课还来得及。)有了编号就像我们居住的房间有了房间号码,使用就方便的多了.房间号码在邮政行业叫地址,因此我们称这些号码叫做寄存器地址,或称地址数。例如:名称为GPIO的寄存器,它的地址或地址数是06H。所以我们的两个例句完全等同于: my_name006: movlw 02h '常数2进入w 有两个寄存器比较特殊,它们没有地址,一个名字叫做W,另一个叫做TRIS.所以他们两个在存储数据的时候比较快,一个指令就可以解决问题,例如:movlw 03H 一条指令就把常数3写入到W寄存器了。关于TRIS寄存器,我们以后用到它再说。除了他们两个以外的其他所有寄存器,在写入数据时一般都要用两条指令进行。 本文虽然只有两个指令,但主要目的是让大家接触一下指令,建立寄存器的概念以及他们同硬件部件的联系。增强学习的信心。能有这些体会,这一节就算过关了。 随着以后的深入,你会发现小小单片机里面是一个大世界,兴趣也由此而生。 在PIC单片机系列中,改变I/O口的输入输出依靠写入寄存器TRIS的值,相应位写0,表示对应管脚被定义成了输出,写1,就是输入。 现在假如预把GP1、GP2管脚定义成输出,其他脚全是输入。那就应该向TRIS寄存器写入二进制数 11111001,换算成十六进制就是F9H. 依照以前我们学到的知识,在PIC系列单片机里,本来应该用下列的语句来完成我们的设定: movlw 0F9H '常数进W,以字母开头的常数前面必须加0 实际上PIC系列的单片机也都是这么写的,后面加的A,表示第一个8位的口(有的单片机不仅一个口,还有好几个8位的I/O口如TRISB、TRISC、TRISD等等). 但是,记住了,PIC12系列的单片机必须改写成为: 写法不同,意思是一样的.这样你就又学了一个指令TRIS,不过这个指令的实质还是你曾经学过的movwf 只是写法不同罢了.在PIC12系列里TRIS作为指令, 在其他系列(PIC16\17\18)里把TRIS作为普通寄存器看待.因为我们现在讲的就是PIC12CE519,所以我们暂时用tris GPIO 这个格式,等以后进入PIC16C877 我们再写成 movwf TRISA,至于理解按照后者进行. 如果我们要控制GP1、GP2管脚的输出电平, 其他管脚作为输入.并且让GP1输出低电平,GP2输出高电平.完整的程序如下: movlw 0F9H '常数进W 更多的情况是:某个口内的某一个管脚需要改变电平,其他脚电平不变.例如我们仅需要GPIO口上的GP1这个管脚的电平拉高,其他管脚电平不发生变化.这时候位操作指令为我们提供了方便,假如我们事先已经把GP1管脚定义过输出了(方法见前面讲过的): bcf GPIO,GP1 '注释:GPIO口上GP1管脚电平拉低,我们行话叫清除。 怎么样,这样控制某一个管脚的电平就方便多了,你的编程效率将会大大提高. 记住:PIC所有单片机所有寄存器都是可以位操作的,这在51的单片机上是不能完全实现的.不仅如此,PIC所有单片机所有管脚的单腿驱动输出电流可以高达25mA,所以如果你驱动一个5到10mA电流的LED发光二极管,根本不用加三极管,串个电阻直接挂在单片机上就得了,这在51的单片机上也是不能实现的,要加驱动三极管或驱动芯片. 怎么样?学PIC有好处吧.别急,好处还有。。。。。。 一不小心,已经学会 5 个指令了,还有30个,加油啊。 五. 寄存器的英文是RAM. PIC12CE519里面共有48个寄存器供我们操作使用,它们每一个都有固定的地址编码。地址编码并不是连续的号码,而是分成了两段: 第一段: 从00H 开始, 依次是01H, 02H, 03H ....0AH, 0BH......到1FH 结束. 计32个寄存器 这种地址不是连续编号,而是要跳过一段的做法, 对于我们新手来说很是不习惯.为了让我们容易入门, 我们暂时先不管第二段RAM, 只当它不存在,所有程序我们只涉及到第一段连续的RAM地址.等我们熟练的掌握好了RAM的使用,再涉及第二段地址的RAM, 那时,你就会理解单片机设计者把它们分成两段的苦心了. 为了规范,我们今后一律把RAM的分段, 叫做分页.第一地址段叫00页面,第二地址段叫01页面.例如:我们学过的I/O口电平控制寄存器GPIO,它的地址编码是06H,属于00页面. 所有这32+16=48个寄存器除了在地址上分成了两个页面以外,又把它们分成两类: 一类专用寄存器,一类通用寄存器. 所谓专用,就是这个寄存器的功能已经由系统分配好了.例如:地址为06H的名称就做GPIO寄存器的功能,它的每个位都对应到一个I/O脚的电平.另一类是通用寄存器,你可以理解成它的功能系统没有事先预定,而是由你在编程序的时候随机使用. pic12ce519的专用寄存器有7个,位置在我们第00页面的最前面.这7个专用寄存器的地址编码是:00H,01H, 02H, ----06H,剩下的所有寄存器包括所有第01页面,全部都是通用寄存器. 例子:在两个通用寄存器09H、0AH内写入常数FCH movlw 0FCH '常数进W 从00H到06H都已经起好了名称,它们是专用寄存器,用处各有不同。以后我们会逐个介绍它们.剩下的都是通用寄存器或者叫普通寄存器(General Purpose Registers),意思是一般用途的寄存器。 内存图谱,不要求记下来,但是应该有个大体印象,用的时候会察看就可以了。等编程时间一长,就那么几个字节,自然就记住了。 所谓字节是衡量二进制数据长度的一个单位。一个寄存器刚好能记住一个字节的数据。如果你要存储的数据比较大超过了255,那就要占2个存储器甚至更多。描述的时候通常我们不说这个数值占了多少个寄存器,而是说这个数据是几个字节的。字节的英文是byte,一个二进制数的一位,叫比特,英文bit。1byte包含8bit。 六. nop '什么事情也不做,但执行这个指令也要消耗掉一点时间。它没有操作数。 如果你需要一个很短时间的延时,可以采用一连串的空操作。注意每个nop也是占一行,例如: movlw 0F9H '常数进W 运行的效果是接在管脚GP1上的LED灯先亮一段时间,再熄灭一段时间的闪烁。 七. movlw 02H '常数2进入W movlw 0FFH '常数255进入W 如果你事先定义好了地址为09H的这个寄存器里存储的数字大小代表电视机节目频道的话,你会很喜欢这两个指令的。并且当节目频道到达最大值255或最小值0的时候无需担心,寄存器在0时减1会得255,255状态下增1会得0。 至于为什么会这样,学过环形计数器的人不会感到奇怪的。你要是没有学过计数器电路也不要紧,记住一个寄存器的最大存储数值是255=0FFH就可以了,加减法都会导致它“进位”。 当然控制音量时这个程序不能使用,因为它在0和255之间变化,音量忽大忽小怎们行。为解决这个问题,我们必须再学习两条指令incfsz和decfsz。 它们与上两个功能基本相同,不同的是:寄存器增1或减1操作以后,该指令会自动判定寄存器内的结果是否为零,如果不为零,继续正常执行该指令后面的语句.但如果结果为零的话,则程序会"跳一步".绕过紧挨着它下面的一条指令,继续执行更下面的语句,举例子说明。 假定我们操作的寄存器还是09H: OK: nop '继续运行 yanshi: movlw 5EH ' 常数5E进W 标号是延时 jixu: decfsz 0AH '0A寄存器内的数减1,如果结果为0跳步 先打个比方,如果你做一顿饭,要做汤,炒菜,炖鱼,汆丸子,奥,忘了还有炒小螃蟹,期间有一个动作在我看来不断的重复,这个动作就是放盐,放盐的过程描述是这样的: 放盐:用一把小勺子深入盐罐 如果我们把做饭定义为主任务,那么放盐这个动作就叫做子任务。 这样定义的一个好处就是描述主任务的时候比较方便,当你用语言文字描述主任务的时候,无论哪一道菜,到了该加盐的时候不必细说用一把小勺子深入盐罐......,因为很多菜都有同样的这个过程,所以,你用 “放盐” 两个字就可以了。但是在你使用 放盐 这个词之前或者之后,你应该解释一下放盐这个词的具体过程是什么。 我们单片机的程序也是一样的,如果你设计一个电视机的自动搜索频道的程序,程序要求电视机每搜索成功一个频道,它面板上的发光二极管就眨一次眼睛,也就说,先熄灭一段时间然后再点亮。这样就会遇到很多这样的眨眼动作,为了简化主程序我们可以把眨眼这样一个过程定义为一段子程序,以后每次遇到需要眨眼的时候就调用一次子程序就可以了。 子程序的定义是这样的: 这样,子程序就定义完了,如果想在程序的某个位置需要led灯熄灭以下(眨眼一次),只需在那个程序位置调用一下子程序就可以了。调用的方法是用 call 命令。 主程序:
疑问2 嗯,那继续往下调用下去,有限制么? 疑问3 在同一层程序空间里,例如在我的某个子程序之中,调用另一个子程序的次数有限制么? 疑问4 我听说单片机在调用子程序以前,好像需要程序“堆栈”访问什么的,要进行一些程序计数器的保存保护,以保证子程序返回来得时候,程序能够正确回到原来位置和环境。是这样的么? 九. 单片机的中断,概念并不难以理解。只是要真正理解运用编程处理一些实际中断的例子,却也不是很容易,甚至是单片机学习、入门的拦路虎。要想学会实际的中断处理编程,也还需要清楚一些程序存储器,程序结构,程序计数器,硬件堆栈,现场保护等这些个另杂碎概念。因此,我们在学习中断以前、以后和学习中断过程中,都有必要介绍回顾复习一些有关上述关键词的概念和知识,否则,尽管你学了中断,用起来可还是不能得心应手,以至于茫然。 还是用比喻的方法介绍一下中断的概念: 你的主程序任务是做一桌可口的饭菜,期间可能要多次调用子程序“撒盐”。尽管子程序下边还有更小的子任务,比如“计算食盐的量”等过程,尽管这些子过程很复杂,但他们的出场时间和顺序是可以预料的,是可以预先安排的。也就说你肯定知道在什么时候放盐。 有一类子程序,他的出场时间是不确定的,突然的,处理他们的时间刻不容缓,必须赶紧的。我们称这一类子程序为中断子程序。也就是我们所说的“中断”。 你正在做菜的过程中,隔壁邻居小孩突然敲门说他的二大爷在他房间里摔倒了,请你帮忙把二大爷扶起来。这是急迫的,必须处理的事务。 你肯定关掉炉子一溜烟跑出去帮忙,等回来以后再点着炉子继续做菜。 这个事件的特点就是发生的时间你无法预先知道,而这个任务必须得停下当前工作去处理,并且是刻不容缓。从开始关炉子到回来点着炉子的这段时间里以及你的救人行为,就叫做“中断子程序”。 在单片机里,中断的例子也是很多的。我举一个你手里的手机的例子,你的GSM手机正工作在赋闲,屏幕上也就显示个时间日期中国电信什么的,表面看没有什么。其实它内部的cpu高速运行忙碌地工作在诸如联络无线网络,查询是否有短消息发来,计算当前信号强度,时间等任务。你突然按下数字键“8”,此时内部cpu必须停下它正在干的工作来应付你,也就是清屏,显示你按下的数字8,然后再回到它原来的任务接着运行。(当然,这个例子不一恰当,现在有操作系统Windows-ce windows-mobile的手机的工作机制远没有如此的简单). |
|