用户登陆 用户注册
您的位置:首页> 技术文章>内容正文
PIC系列单片机程序设计基础
[正文]:1、程序的基本格式   先介绍二条伪指令:  equ ——标号赋值伪指令  org ——地址定义伪指令  pic16c5x在reset后指令计算器pc被置为全“1”,所以pic16c5x几种型号芯片的复位地址为:   pic16c54/55:1ffh   pic16c56:3ffh   pic16c57/58:7ffh  一般来说,pic的源程序并没有要求统一的格式,大家可以根据自己的风格来编写。
但这里我们推荐一种清晰明了的格式供参考。
  title this is …… ;程序标题  ;--------------------------------------  ;名称定义和变量定义  ;--------------------------------------  f0    equ  0  rtcc   equ  1  pc    equ  2  status  equ  3  fsr   equ  4  ra    equ  5  rb    equ  6  rc    equ  7         ┋  pic16c54 equ 1ffh ;芯片复位地址  pic16c56 equ 3ffh  pic16c57 equ 7ffh  ;-----------------------------------------  org pic16c54 goto main   ;在复位地址处转入主程序  org   0          ;在0000h开始存放程序  ;-----------------------------------------  ;子程序区  ;-----------------------------------------  delay movlw 255      ┋      retlw 0  ;------------------------------------------  ;主程序区  ;------------------------------------------  main      movlw b‘00000000’      tris rb       ;rb已由伪指令定义为6,即b口       ┋  loop      bsf rb,7 call delay              bcf rb,7 call delay        ┋      goto loop  ;-------------------------------------------       end       ;程序结束   注:main标号一定要处在0页面内。
  2、程序设计基础  1) 设置 i/o 口的输入/输出方向  pic16c5x的i/o 口皆为双向可编程,即每一根i/o 端线都可分别单独地由程序设置为输入或输出。
这个过程由写i/o控制寄存器tris f来实现,写入值为“1”,则为输入;写入值为“0”,则为输出。
      movlw 0fh  ;0000 1111(0fh)            输入 输出      tris 6    ;将w中的0fh写入b口控制器,             ;b口高4位为输出,低4位为输入。
      movlw 0c0h ; 11 000000(0c0h)              rb4,rb5输出0 rb6,rb7输出1  2) 检查寄存器是否为零  如果要判断一个寄存器内容是否为零,很简单,现以寄存器f10为例:      movf 10,1      ;f10→f10,结果影响零标记状态位z      btfss status,z    ;f10为零则跳      goto nz        ;z=0即f10不为零转入标号nz处程序       ┋          ;z=1即f10=0处理程序  3) 比较二个寄存器的大小  要比较二个寄存器的大小,可以将它们做减法运算,然后根据状态位c来判断。
注意,相减的结果放入w,则不会影响二寄存器原有的值。
  例如f8和f9二个寄存器要比较大小:       movf 8,0       ;f8→w       subwf 9,0      ;f9—w(f8)→w       btfsc status,z    ;判断f8=f9否       goto f8=f9       btfsc status,c    ;c=0则跳       goto f9>f8       ;c=1相减结果为正,f9>f8       goto f9<f8       ;c=0相减结果为负,f9<f8         ┋   4) 循环n次的程序  如果要使某段程序循环执行n次,可以用一个寄存器作计数器。
下例以f10做计数器,使程序循环8次。
       count equ 10     ;定义f10名称为count(计数器)          ┋       movlw 8       movwf count loop   ;循环体   loop           ┋       decfsz count,1    ;count减1,结果为零则跳       goto loop       ;结果不为零,继续循环           ┋       ;结果为零,跳出循环   5)“if……then……”格式的程序  下面以“if x=y then goto next”格式为例。
       movf x,0      ;x→w       subwf y,0     ;y—w(x)→w       btfsc status,z   ;x=y 否       goto next      ;x=y,跳到next去执行。
          ┋       ;x≠y   6)“for……next”格式的程序  “for……next”程序使循环在某个范围内进行。
下例是“for x=0 to 5”格式的程序。
f10放x的初值,f11放x的终值。
      start  equ  10      daend  equ  11           ┋      movlw 0      movwf start     ; 0→start(f10)      movlw 5      movwf daend     ;5→daend(f11)   loop           ┋      incf start,1     ;start值加1      movf start,0      subwf daend,0     ;start=daend ?(x=5否)      btfss status,z      goto loop        ;x<5,继续循环           ┋       ;x=5,结束循环   7)“do while……end”格式的程序  “do while……end”程序是在符合条件下执行循环。
下例是“do while x=1”格式的程序。
f10放x的值。
      x  equ  10        ┋      movlw  1      movwf  x     ;1→x(f10),作为初值   loop        ┋      movlw 1      subwf x,0      btfss status,z   ;x=1否?      goto loop      ;x=1继续循环        ┋        ;x≠1跳出循环   8) 查表程序  查表是程序中经常用到的一种操作。
下例是将十进制0~9转换成7段led数字显示值。
若以b口的rb0~rb6来驱动led的a~g线段,则有如下关系:     
点击查看大图片
  设led为共阳,则0~9数字对应的线段值如下表: 十进数 线段值 十进数 线段值 0 c0h 5 92h 1 c9h 6 82h 2 a4h 7 f8h 3 b0h 8 80h 4 99h 9 90h   pic的查表程序可以利用子程序带值返回的特点来实现。
具体是在主程序中先取表数据地址放入w,接着调用子程序,子程序的第一条指令将w置入pc,则程序跳到数据地址的地方,再由“retlw”指令将数据放入w返回到主程序。
下面程序以f10放表头地址。
      movlw  table     ;表头地址→f10        movwf  10          ┋      movlw  1        ;1→w,准备取“1”的线段值      addwf  10,1      ;f10+w =“1”的数据地址      call  convert      movwf  6        ;线段值置到b口,点亮led          ┋  convert movwf  2        ;w→pc table      retlw  0c0h      ;“0”线段值      retlw  0f9h      ;“1”线段值          ┋      retlw  90h       ;“9”线段值   9)“read……data,restore”格式程序  “read……data”程序是每次读取数据表的一个数据,然后将数据指针加1,准备取下一个数据。
下例程序中以f10为数据表起始地址,f11做数据指针。
      pointer  equ  11   ;定义f11名称为pointer          ┋      movlw   data      movwf   10     ;数据表头地址→f10      clrf   pointer   ;数据指针清零          ┋      movf   pointer,0        addwf 10,0      ;w =f10+pointer          ┋      incf    pointer,1  ;指针加1      call convert      ;调子程序,取表格数据          ┋  convert movwf   2    ;数据地址→pc  data  retlw   20h    ;数据          ┋      retlw 15h      ;数据  如果要执行“restore”,只要执行一条“clrf pointer”即可。
  10) 延时程序  如果延时时间较短,可以让程序简单地连续执行几条空操作指令“nop”。
如果延时时间长,可以用循环来实现。
下例以f10计算,使循环重复执行100次。
      movlw d‘100’      movwf 10  loop  decfsz 10,1   ;f10—1→f10,结果为零则跳      goto loop       ┋  延时程序中计算指令执行的时间和即为延时时间。
如果使用4mhz振荡,则每个指令周期为1μs。
所以单周期指令时间为1μs,双周期指令时间为2μs。
在上例的loop循环延时时间即为:(1+2)*100+2=302(μs)。
在循环中插入空操作指令即可延长延时时间:      movlw  d‘100’      movwf  10  loop   nop       nop       nop      decfsz 10,1      goto loop        ┋  延时时间=(1+1+1+1+2)*100+2=602(μs)。
  用几个循环嵌套的方式可以大大延长延时时间。
下例用2个循环来做延时:      movlw   d‘100’      movwf   10  loop  movlw   d‘16’      movwf   11  loop1  decfsz   11,1      goto    loop1      decfsz   10,1      goto loop       ┋  延时时间=1+1+[1+1+(1+2)*16-1+1+2]*100-1=5201(μs)  11) rtcc计数器的使用  rtcc是一个脉冲计数器,它的计数脉冲有二个来源,一个是从rtcc引脚输入的外部信号,一个是内部的指令时钟信号。
可以用程序来选择其中一个信号源作为输入。
rtcc可被程序用作计时之用;程序读取rtcc寄存器值以计算时间。
当rtcc作为内部计时器使用时需将rtcc管脚接vdd或vss,以减少干扰和耗电流。
下例程序以rtcc做延时:      rtcc  equ  1       ┋      clrf  rtcc    ;rtcc清0      movlw  07h      option    ;选择预设倍数1:256→rtcc   loop  movlw  255   ;rtcc计数终值      subwf  rtcc,0      btfss status,z   ;rtcc=255?      goto loop       ┋  这个延时程序中,每过256个指令周期rtcc寄存器增1(分频比=1:256),设芯片使用4mhz振荡,则:  延时时间=256*256=65536(μs)  rtcc是自振式的,在它计数时,程序可以去做别的事情,只要隔一段时间去读取它,检测它的计数值即可。
  12) 寄存器体(bank)的寻址  对于pic16c54/55/56,寄存器有32个,只有一个体(bank),故不存在体寻址问题,对于pic16c57/58来说,寄存器则有80个,分为4个体(bank0-bank3)。
在对f4(fsr)的说明中可知,f4的bit6和bit5是寄存器体寻址位,其对应关系如下:bit6  bit5 bank 物理地址  0    0bank0 10h~1fh  0    1bank1 30h~3fh  1    0bank2 50h~5fh  1    1bank3 70h~7fh   当芯片上电reset后,f4的bit6,bit5是随机的,非上电的reset则保持原先状态不变。
  下面的例子对bank1和bank2的30h及50h寄存器写入数据。
  例1.(设目前体选为bank0)      bsf   4,5    ;置位bit5=1,选择bank1      movlw  data      movwf  10h    ; data→30h      bcf   4,5      bsf   4,6   ;bit6=1,bit5=0选择bank2      movwf  10h    ;data→50h  从上例中我们看到,对某一体(bank)中的寄存器进行读写,首先要先对f4中的体寻址位进行操作。
实际应用中一般上电复位后先清f4的bit6和bit5为0,使之指向bank0,以后再根据需要使其指向相应的体。
  注意,在例子中对30h寄存器(bank1)和50h寄存器(bank2)写数时,用的指令“movwf 10h”中寄存器地址写的都是“10h”,而不是读者预期的“movwf 30h”和“movwf 50h”,为什么?  让我们回顾一下指令表。
在pic16c5x的所有有关寄存器的指令码中,寄存寻址位都只占5个位:fffff,只能寻址32个(00h—1fh)寄存器。
所以要选址80个寄存器,还要再用二位体选址位pa1和pa0。
当我们设置好体寻址位pa1和pa0,使之指向一个bank,那么指令“movwf 10h”就是将w内容置入这个bank中的相应寄存器内(10h,30h,50h,或70h)。
  有些设计者第一次接触体选址的概念,难免理解上有出入,下面是一个例子:  例2:(设目前体选为bank0)      movlw  55h       movwf  30h   ;欲把55h→30h寄存器      movlw  66h      movwf  50h   ;欲把66h→50h寄存器  以为“movwf 30h”一定能把w置入30h,“movwf 50h”一定能把w置入50h,这是错误的。
因为这两条指令的实际效果是“movwf 10h”,原因上面已经说明过了。
所以例2这段程序最后结果是f10h=66h,而真正的f30h和f50h并没有被操作到。
  建议:为使体选址的程序清晰明了,建议多用名称定义符来写程序,则不易混淆。
  例3:假设在程序中用到bank0,bank1,bank2的几个寄存器如下:bank0 地址 bank1 地址 bank2 地址 bank3 地址 a 10h b 30h c 50h · 70h · · · · · · · · · · · · · · · ·        a   equ  10h   ;bank0       b   equ  10h   ;bank1       c   equ  10h   ;bank2          ┋       fsr  equ  4       bit6  equ  6       bit5  equ  5       data  equ  55h          ┋       movlw  data       movwf  a         bsf   fsr,bit5       movwf  b     ;data→f30h       bcf   fsr,bit5       bsf   fsr,bit6       movwf  c     ;data→f50h          ┋  程序这样书写,相信体选址就不容易错了。
  13) 程序跨页面跳转和调用  下面介绍pic16c5x的程序存储区的页面概念和f3寄存器中的页面选址位pa1和pa0两位应用的实例。
  (1)“goto”跨页面   例:设目前程序在0页面(page0),欲用“goto”跳转到1页面的某个地方key(page1)。
       status  equ  3       pa1   equ  6       pa0   equ  5           ┋       bsf  status,pa0  ;pa0=1,选择page页面       goto  key      ;跨页跳转到1页面的key           ┋       key   nop     ;1页面的程序           ┋  (2)“call”跨页面  例:设目前程序在0页面(page0),现在要调用——放在1页面(page1)的子程序delay。
           ┋       bsf  status,pa0   ;pa0=1,选择page1页面       call  delay      ;跨页调用       bcf  status,pa0   ;恢复0页面地址           ┋       delay nop       ;1页面的子程序           ┋  注意:程序为跨页call而设了页面地址,从子程序返回后一定要恢复原来的页面地址。
  (3)程序跨页跳转和调用的编写  读者看到这里,一定要问:我写源程序(.asm)时,并不去注意每条指令的存放地址,我怎么知道这个goto是要跨页面的,那个call是需跨页面的? 的确,开始写源程序时并知道何时会发生跨页面跳转或调用,不过当你将源程序汇编时,就会自动给出。
当汇编结果显示出:       x x x(地址)“goto out of range"       x x x(地址)“call out of range"  这表明你的程序发生了跨页面的跳转和调用,而你的程序中在这些跨页goto和call之前还未设置好相应的页面地址。
这时应该查看汇编生成的.lst文件,找到这些goto和call,并查看它们要跳转去的地址处在什么页面,然后再回到源程序(.asm)做必要的修改。
一直到你的源程序汇编通过(0 errors and warnnings)。
   (4)程序页面的连接  程序4个页面连接处应该做一些处理。
一般建议采用下面的格式: 即在进入另一个页面后,马上设置相应的页面地址位(pa1,pa0)。
页面处理是pic16c5x编程中最麻烦的部分,不过并不难。
只要做了一次实际的编程练习后,就能掌握了。



网站首页 培训课程 维修指南
技术文章 家电专栏 供应信息
求购信息 培训资讯 展会信息
电脑专栏 教程下载 资料下载
常用软件 PLC教程 PLC资料
变频伺服 低压电器 维修资料
人机界面 自控仪表 工控机类
文章标题: 搜文章
中国工控资源网手机版 2012
电话:010-67577139 13811659603
培训咨询QQ:657167934 471895637 销售咨询QQ:623769457
联系邮箱:zggkzyw@163.com
 京ICP备11002135号
报时(2026-04-06 13:08:46) 流量统计