你的位置: 首页 > 家电弱电 > 单片机

8051单片机模块化编程技巧

2016-10-23 10:46:41 | 人围观 | 评论:

  目前在8051 单片机应用开发中主要有两种编程语言:汇编语言和C51 语言。C51 语言是一种结构化的编程语言,采用C51 编写的应用程序结构清晰、模块化程度高、可读性强、并容易移植。但C51 语言也有缺点,就是编译后生成的目标代码空间要比汇编的大。

  而且目前单片机的教材还是侧重于汇编语言。因此学习用单片机汇编语言程序进行结构化设计还是很有必要的。我们知道C51 语言是函数式语言,其程序由函数构成,每一个源程序有且只有一个主函数main() 和若干个函数组成。其中每一个函数都用于完成某一特定任务。也就是说,一个项目若具有几个功能,实现这些功能就会需要由若干个任务来完成,那么它的源程序中就会有若干个或以上的函数。而在汇编语言中,源程序中只有程序和子程序。那么我们能否以子程序为基本单位,用一个子程序实现一种功能来做到模块化编程呢?实践证明是可行的。但在编制程序中不要忘记汇编语言的特点,注意子程序之间对单片机资源的使用,避免不同子程序交叉共用同一资源引起程序的错误执行。子程序嵌套调用的级数等。本文以“60秒倒计时电路”为例谈一谈51 单片机汇编语言模块化编程的一点技巧。

  一、60秒倒计时电路及编程

  1. 功能要求

  所谓倒计时,就是首先给定一个初始值,然后对初始值进行减“1”操作,直到该值为“0”为止。60 秒倒计时就是对给定的初始值“60”每隔1 秒钟对其进行减“1”,一直减到该值为“0”为止。

  该倒计时电路要求有两个按钮。一个是“复位”按钮,按下按钮设置倒计时初始值,并把指示灯熄灭;另一个是“开始”按钮,按下按钮开始倒计时。并用两位LED 数码管显示当前倒计时值。计时时间到,指示灯点亮。

  2. 电路组成

  实现上述功能要求的单片机接口电路如图1 所示。

单片机接口电路

图1 单片机接口电路 

  图中用按钮SB1 作为“置初值”按钮,按钮SB2 作为“开始”按钮。按下SB1 按钮,将显示值设置为“60”。

  按下按钮SB2,每隔一秒显示值减“1”,直到值为“0”

  停止计数。按钮和指示灯接在P0 口上,P0.0 为初始按钮,P0.1 为开始按钮,P0.7 为指示灯。十位LED 数码管接P2 口,个位LED 数码管接P1 口。图2 为单片机基本系统电路。

单片机基本系统电路

图2 单片机基本系统电路

  3. 功能分析

  根据60 秒倒计时的功能要求,需要单片机完成以下任务:

  ⑴ 按键扫描。用来判断有没有键被按下,是哪个键被按下?根据不同的键,给出相应的键值。

  ⑵ 计时显示。这里时间值使用的是两位数,故需要将被显示的时间值取出个位数和十位数,然后才能进行显示。

  ⑶ 被显示数转换成7 段码。由于单片机中的数据都是以二进制形式存放或运算的。而这里输出显示使用了两位LED 数码管来显示计时数值的,一个被显示的数要点亮数码管的某几段才能显示出这个数,不同的数需要点亮数码管的不同段。因此需要将被显示的这个数转换成相应的显示段码,才能被正确显示出来。

  ⑷ 延时。包括1秒钟延时和按键消抖的10毫秒延时。

  ⒋ 程序编制

  程序按实现功能采用模块化结构,有一个主程序和若干个子程序组成。每个子程序分别是完成某个任务的独立模块,有时会用到调用参数。本实例共有5 个子程序,分别是按键扫描子程序、10ms 延时子程序、1s 延时子程序、显示子程序、取段码子程序。

  ⑴ 按键扫描子程序

  按键扫描子程序完成对按键进行扫描,确定有没有键被按下,当有键被按下并抬起后将相关键值返回给主程序的任务。其流程如图3 所示。该子程序没有入口参数,但有一个出口参数,即按键的键值,存放在寄存器R3 中。寄存器R3 中的值为“60H”表示SB1 键被按下;寄存器R3 中的值为“00H”表示SB2 键被按下。

按键扫描子程序流程图

图3 按键扫描子程序流程图

  按照图3 的流程图和51 单片机的指令系统编制的子程序如下:

  ;----------- 按键扫描描--------------

  ; 出口参数键值存放在寄存器R3 中,用于识别哪个键。

  ;R3=60H, 说明SB1 被按下;R3=00H, 说明SB2 被按下

  key_scan: jnb kb_init, k1check ; SB1 按下转移

  jnb kb_begin, k2check ; SB2 按下转移

  sjmp ksr ;

  k1check: acall del10 ; 调用毫秒延时,去抖

  jb kb_init, ksr ; 干扰,返回

  jnb kb_init,$ ; 等待按键释放

  mov r3, #60h; 是SB1,键值“60H”送寄存器R3

  sjmp ksr ; 是,不进行任何操作返回

  k2check: acall del10 ; 调用毫秒延时,去抖

  jb kb_begin, ksr ; 干扰,返回

  jnb kb_begin,$ ; 等待按键释放

  mov r3, #00h; 是SB2,键值“00H”送寄存器R3

  ksr: ret ; 返回

  ;---------------------------------

  ⑵ 显示子程序

  显示子程序完成从被显示值中取出十位数将其转换成显示断码,并送单片机的P2 口;从被显示值中取出个位数将其转换成显示断码,并送单片机的P1 口任务。其流程如图4 所示。该子程序有一个入口参数,即被显示的值,存放在寄存器R2 中。

显示子程序流程图

图4 显示子程序流程图

  按照图4 的流程图和51 单片机的指令系统编制的子程序如下:

  ;------------ 显示子程序------------

  ; 入口参数存放在寄存器R2 中

  display:mov a, r2 ; 取被显示值

  mov b, #10; 取被显示值的十位数

  div ab;

  acall seg7; 调用转换子程序,取显示断码

  mov p2, a ; 十位数段码送P2 口

  mov a, b; 取个位数

  acall seg7 ; 调用转换子程序,取显示断码

  mov p1, a ; 个位数段码送P1 口

  ret ; 返回

  ;---------------------------------

  ⑶ 取段码子程序

  取段码子程序完成将被显示的数转换成7 段共阳LED 数码管对应数的段码的任务。其流程如图5 所示。

取段码子程序流程图

图5 取段码子程序流程图

  该子程序有一个入口参数和一个出口参数。入口参数就是被显示的数,出口参数就是该数的段码(相应位=0表示亮),都存放在累加器A 中。

  按照图5 的流程图和51 单片机的指令系统编制的子程序如下:

  ;-------------- 取段码--------------

  ; 对累计器A 中的值由查表得到显示断码

  ; 入口和出口参数存放在累计器A 中

  seg7: inc a ; 取被显示数,累加器A 加1

  movc a, @a+pc ; 查表

  ret ; 返回

  db 0c0h,0f9h,0a4h,0b0h;0123

  db 99h,92h,82h,0f8h;4567

  db 80h,90h,88h,83h;89AB

  db 0c6h,0a1h,86h,8eh;cdEF

  ;---------------------------------

  ⑷ 延时子程序

  延时子程序完成一定的延时时间任务。这里有两个延时时间不同的子程序(也可以调用100 次10mS 做1S 延迟),其流程如图6 所示。延时子程序没有入口和出口参数。

延时子程序流程图

图6 延时子程序流程图

  按照图6 的流程图和51 单片机的指令系统编制的子程序如下;

  ;----------- 延时10ms 程序----------

  ; 用到寄存器组1 中的R6 和R7 寄存器

  del10: setb psw.3 ; 切换至第1 组寄存器

  mov r7, #0bh ;

  dl1: mov r6, #0ffh ;

  dl2: djnz r6, dl2 ;

  djnz r7, dl1;

  clr psw.3 ; 切换至第0 组寄存器

  ret ;

  ;---------------------------------

  ;------------- 延时1s 程序-----------

  ; 用到寄存器组1 中的R1、R2 和R3 寄存器

  del1s: setb psw.3 ; 选用寄存器区1

  mov r1 , #46; 立即数46 送寄存器R1

  del0: mov r2 , #100; 立即数100 送寄存器R2

  del1: mov r3 , #100 ; 立即数100 送寄存器R3

  djnz r3 , $ ; 寄存器R3 中的内容减1,不为零转移到当

  前指令

  djnz r2 , del1; 寄存器R2 中的内容减1,不为零转移到

  del1

  djnz r1 , del0; 寄存器R1 中的内容减1,不为零转移到

  del0

  clr psw.3 ; 选用寄存器区0

  ret ; 子程序返回

  ;---------------------------------

  ⑸ 主程序编制

  主程序需要实现的功能是:完成单片机端口定义;初始化任务;调用键扫描子程序,根据按键状态实现置初值或进行倒计时,并调用显示子程序等。其流程如图7 所示。

主程序编制流程图

图7 主程序编制流程图

  按照图7 的流程图和51 单片机的指令系统、以上编制的各子程序,主程序如下:

  ;**********************************************************

  ; 文件名:counter.asm 功能:60 秒倒计时

  ; 说明:p2 和p1 口分别接一个LED 数码管, 显示两位

  十进制数。

  ; p0.0 和p0.1 口接置初值按钮和开始倒计时按钮,p0.7

  接提示LED。

  ; 晶振频率11.0592MHz.

  ;**********************************************************

  ;------------ 端口定义--------------

  kb_init bit p0.0 ; 置初值按钮定义

  kb_begin bit p0.1 ; 开始按钮定义

  warn bit p0.7 ; 提示

  ;---------------------------------

  org 0000h

  ajmp begin

  ;============ 主程序===============

  org 00b0h

  begin:

  mov sp, #50h ; 初始化

  mov p0, #0ffh

  mov p1, #0ffh

  mov p2, #0ffh

  mov r2, #60

  mov r3, #0ffh

  main:

  lcall key_scan ; 按扫描键

  mov a, r3 ; 取返回值

  cjne a, #60h, lp1 ; 非SB1 按键转移

  mov r2, #60 ; 初值送寄存器R2

  setb p0.7 ; 清指示灯

  acall display ; 调显示子程序

  ajmp main ; 转移

  lp1: mov a, r3 ; 取返回值

  cjne a, #00h, main ; 非SB2 按键转移

  setb p0.7

  mov r2, #60

  lp2: acall display

  acall del1s ; 调用1 秒延时子程序

  dec r2

  cjne r2, #00h,lp2

  acall display

  clr p0.7

  mov r3, #0ffh

  ajmp main

  ;=================================

  二、Keil C 中编译

  1. 新建项目

  打开“Keil C”软件,新建一个项目。项目名也不妨为“counter”。

  点击桌面上的图标 ,进入Keil C51 μVision2集成开发环境。在主界面上点下拉菜单“Project”,选“New Project?”命令。在弹出的对话框中将项目命名为“counter”。点“保存”按钮,选“Atmel”下的“AT89S52”后返回。

  2. 添加源程序

  打开已建立的文件“counter.asm”;并将该文件添加到“Source Group 1”中。

  在μVision2 主界面上点击打开文件按钮 ,在弹出的对话框内找到刚才新建并保存的文件“counter.asm”。点“打开”按钮打开。

  在中间左边的“项目空间(Project Workspace)”内,点击“+”展开。再用右键点击“Source Group 1”文件夹,在弹出的菜单命令中选“Add Files to group‘Source Group 1’”。

  3. 参数设置

  在“Options for Target‘ Target 1’”中的“Output”标签页上进行设置。

  点下拉菜单“Project”, 选“Options for Target‘Target 1’”。在弹出对话框上的“Target”标签页内,把单片机的运行频率调整为11.0592MHz。在“Output”标签页上,点“Create HEX File”前的复选框,使其内出现“√”,这样编译后就能生成目标文件了。点“确定”按钮返回。

  4. 程序编译

  点编译和建立目标文件,得到“counter.hex”文件。

  在μVision2 主界面上点重新编译按钮,对源程序文件进行编译,结果如图8 所示。

图8

图8

  三、Preteus仿真

  ISIS 仿真图如图9 所示。设置CPU:89C51 的特性,加载counter.HEX 代码加载,运行仿真。将光标移至按钮SB1,使光标变成一只“手”时,点击鼠标左键,使按钮按下。按钮释放后,数码管显示值加“60”,如图9 所示。启动倒计时。将光标移至按钮SB2,使光标变成一只“手”时,点击鼠标左键,使按钮按下。按钮释放后倒计时开始。原文来自www.dqjsw.com.cn图9ISIS 仿真图

图9ISIS 仿真图

  四、基本系统上运行

  用单片机基本系统板来验证程序,首先准备好实验用器材基本系统板、下载器、电源和万能板及所需元器件。然后按下面步骤进行操作。

  ⒈在应用实验板上按图1 焊接好电阻、电容、数码管和接插件、按钮等。

  ⒉拔去最小系统板上的跳线J101、J102、J103,插上AT89S52 芯片。将下载线的接口板插入电脑的并口上,连接电缆把最小系统与接口板连好,再在最小系统上接上电源。如图10 所示。

图10

图10

  ⒊打开下载软件,并设置好有关参数;加载待写文件“counter.hex”;点“编程”按钮下载程序。必要时须先对芯片进行“擦写”( 若该芯片中曾烧录过程序)。

  ⒋完成上面的操作后,关闭电源,拔下连接电缆,插上跳线J101,接上实验电路。

  ⒌上电验证程序,按下按键SB1 置初值,按下按键SB2 开始倒计时。若不符合要求则进行修改(可以先在μVision2 进行调试或Proteus 中仿真)。

  ⒍重复上述步骤直到实现要求的功能。

  五、结束语

  用汇编语言编制应用程序时虽然要考虑单片机的硬件资源的分配,且实现相同功能时的语句可能比C51 编程更多,汇编的模块按结构化编程,同样也能编制出结构清晰、功能明确、可读性强、的应用程序。





标签: