首页 > 编程语言 >基于ATMega16的流水灯实例(汇编)

基于ATMega16的流水灯实例(汇编)

时间:2023-12-04 21:36:36浏览次数:35  
标签:TMP 汇编 16 SP Rd PC 实例 寄存器 ATMega16

本例在ATMega16上,利用汇编程序实现一个流水灯,主要讨论寄存器移位及软件延时的使用方法。

本例中的八个LED电路通过限流电阻及跳线帽接在PA端口,电路如下图所示。

完整的汇编代码如下。 

.INCLUDE "M16DEF.INC"
.DEF    TMP = R16                 ;定义一个R16寄存器的别名(R不能小于16)
.DEF    SHIFT = R17               ;定义一个R17寄存器的别名(R不能小于16)
.ORG    $0000
    JMP    RESET
.ORG    $002A
RESET:
    LDI     TMP, HIGH(RAMEND)    ;获取RAM空间的最大地址高字节(ATMega16为$04)
    OUT     SPH, TMP             ;高字节送SP高位
    LDI     TMP, LOW(RAMEND)     ;获取RAM空间的最大地址低字节(ATMega16为$5F)
    OUT     SPL, TMP             ;低字节送SP低位
    SER     TMP                  ;把R16全部置1
    OUT     DDRA, TMP            ;把端口A设置为输出方向
    OUT     PORTA, TMP           ;把端口A输出高电平
    LDI     TMP, 114             ;设置延时的值,约100ms
    LDI     SHIFT, $FE           ;加载值$FE到R17中
    SEC                          ;置进位位C为1
LOOP:
    OUT     PORTA, SHIFT         ;输出R17的值到端口A
    ROL     SHIFT                ;对R17的值进行带进位位的左移
    RCALL   DELAY                ;调用延时子程序
    RJMP    LOOP                 ;跳转到LOOP

DELAY:                           ;延时子程序入口
    PUSH    TMP                  ;R16的值压入栈中
D1:
    PUSH    TMP
D2:
    PUSH    TMP                   ;连压三个R16
D3:
    DEC     TMP                   ;R16的值减一
    BRNE    D3                    ;若R16的值不为0则跳转到del3处
    POP     TMP                   ;R16的值出栈
    DEC     TMP
    BRNE    D2
    POP     TMP
    DEC     TMP
    BRNE    D1
    POP     TMP
    RET                           ;子程序返回

本例使用到了AVR中I/O空间的4个寄存器,即SPH、SPL、DDRA和PORTA。

先看SPH和SPL两个寄存器,它们同属于栈指针寄存器,分别管理高低两个8位,具体如下表所示。

栈指针寄存器一共有16位,可寻址64KB的SRAM空间。初始值为全0,即默认SP指向SRAM的最低地址。由于AVR的栈指针是向下生长型,所以一般在初始化时,要将SP指向SRAM空间的最高地址。在ATMega16中,$0000~$001F是32个通用寄存器空间,$0020~$005F是64个I/O空间寄存器,$0060~$045F才是SRAM空间,共有1KB,因此在初始化时,SP指向$045F地址。

再来看DDRA寄存器,它称为端口方向寄存器,用于设置端口是输出方向还是输入方向,具体如下表所示。

当某位被设置为1时,该位对应的引脚被设置为输出方向,设置为0时,为输入方向。复位后的初始值为0,即默认为输入方向。在ATMega16中,这样的方向寄存器一共有4个,即DDRA、DDRB、DDRC和DDRD,配置方法完全一样。

接着看PORTA寄存器,它称为端口数据寄存器,用于设置引脚电平,具体如下表所示。

当某位被设置为1时,该位对应的引脚输出高电平,设置为0时,输出低电平。复位后的初始值为0,即默认引脚输出低电平。在ATMega16中,这样的方向寄存器一共有4个,即PORTA、PORTB、PORTC和PORTD,配置方法完全一样。

本例中一共使用到了13条指令,具体解释如下。

1)直接跳转
  JMP    k    0 ≤ k < 4M
说明:直接跳转到指定的地址处(可达4M字的程序存储器),该指令不一定支持所有的AVR芯片。
操作:PC ← k   32位机器码:1001 010k kkkk 110k kkkk kkkk kkkk kkkk

2)立即数送寄存器
  LDI   Rd, K    16 ≤ d ≤ 31,0 ≤ K ≤ 255
说明:装入一个8位的立即数到寄存器R16~R31中。
操作:Rd ← K   PC ← PC + 1   16位机器码:1110 KKKK dddd KKKK 

3)寄存器数据送I/O空间
  OUT   A, Rr    0 ≤ r ≤ 31,    0 ≤ A ≤ 63
说明:将寄存器Rr中的数据传送到I/O空间。
操作:I/O(A) ← Rr    PC ← PC + 1   16位机器码:1011 1AAr rrrr AAAA

4)置寄存器为全1
  SER    Rd    16 ≤ d ≤ 31
说明:将$FF直接装入目的寄存器Rd。
操作:Rd ← $FF   PC ← PC + 1   16位机器码:1110 1111 dddd 1111

5)进位位置1
  SEC
说明:将进位标志位(C)置1。
操作:C ← 1   PC ← PC + 1   16位机器码:1001 0100 0000 1000

6)带进位位的寄存器循环左移
  ROL    Rd    0 ≤ d ≤ 31
说明:寄存器Rd中所有位左移1位,C标志被移到Rd的第0位,Rd的第7位移到C标志。
操作:C ← b7--------b0 ← C    PC ← PC + 1   16位机器码:0001 11dd dddd dddd

7)相对跳转
  RJMP    k    -2k ≤ k < 2k
说明:相对跳转到PC - 2K +1 ~ PC + 2K (字) 范围内的地址,对于程序存储器空间不超过4K字(8K 字节)的芯片,该指令可以寻址整个程序存储器空间的每一个地址。
操作:PC ← PC + k + 1   16位机器码:1100 kkkk kkkk kkkk

8)相对调用
  RCALL    k    -2k ≤ k < 2k
说明:相对调用PC-2K+1到PC+2K (字)范围内的子程序,返回地址(RCALL后面那条指令的地址)保存到堆栈当中。对于程序存储器不超过4K字(8K字节)的AVR芯片,这条指令可以寻址整个程序存储器空间。在调用RCALL指令时,堆栈指针是带后减量的(调用完成后减少)。
操作:STACK ← PC + 1   SP ← SP - 2 (2 字节, 16 位)    PC ← PC + 1 + k    16位机器码:1101 kkkk kkkk kkkk

9)压栈
  PUSH    Rr    0 ≤ r ≤ 31
说明:存储寄存器Rr中的数据到堆栈,堆栈的指针在PUSH后加1。该指令不是对所有的AVR芯片都有效。
操作:STACK ← Rr    SP ← SP - 1   PC ← PC + 1   16位机器码:1001001ddddd1111

10)出栈
  POP    Rd    0 ≤ d ≤ 31
说明:将堆栈中的字节装入寄存器Rd中,堆栈指针在POP之前首先减1。该指令不是对所有的AVR芯片都有效。
操作:Rd ← STACK    SP ← SP + 1   PC ← PC + 1   16位机器码:1001 000d dddd 1111

11)减1
  DEC   Rd    0 ≤ d ≤ 31
说明:寄存器Rd内容减1,并将结果置于目标寄存器Rd中。
操作:Rd ← Rd - 1    PC ← PC + 1   16位机器码:1001 010d dddd 1010

12)不相等跳转
  BRNE    k    -64 ≤ k ≤ +63
说明:条件相对跳转,测试零标志位Z,如果Z位被清零,则相对PC值跳转k个字。如果在执行CP、CPI、SUB或 SUBI指令后,立即执行该指令,且当寄存器Rd中数与寄存器Rr中数不相等时,将发生跳转。可跳转k个字,k 为7位带符号数,最多可向前跳63个字,向后跳64个字。
操作:If Rd ≠ Rr (Z = 0) then PC ← PC + k + 1, else PC ← PC + 1   16位机器码:1111 01kk kkkk k001

13)子程序返回
  RET
说明:从子程序返回,返回地址从堆栈中获得。在执行RET时堆栈指针带有预增量(调用前堆栈指针增加)。
操作:SP←SP + 2   PC(15:0) ← STACK    16位机器码:1001 0101 0000 1000

此外,程序中还用到了一些伪指令,具体解释如下。

1)包含指定的文件
语法:.INCLUDE“文件名”
说明:通知汇编器开始从一个指定的文件中读入程序语句,并对读入的语句进行编译,直到该包含文件结束或遇到该文件中的EXIT伪指令,然后再从本文件当前INCLUDE伪指令的下一行语句处继续编译。在一个包含文件中,也可以使用INCLUDE伪指令来包含另外一个指定的文件。

2)定义寄存器符号名
语法:.DEF 符号名 = 寄存器
说明:给寄存器定义一个替代的符号名。在后续程序中可以使用定义的符号名来表示被定义的寄存器。可以给一个寄存器定义多个符号名。符号名在后续程序中可以重新指定。编译时,凡遇到符号名,都以相应被定义的寄存器替代。

3)定义代码起始位置
语法:.ORG 表达式
说明:设置定位计数器为一个绝对数值,该数值为表达式的值,作为代码的起始位置。如果该伪指令出现在数据段中,则设定SRAM定位计数器;如果该伪指令出现在代码段中,则设定程序存储器计数器;如果该伪指令出现在EEPROM段中,则设定EEPROM定位计数器。如果该伪指令前带标号(在相同的语句行),则标号的定位由ORG的参数值定义。代码段和EEPROM段定位计数器的默认值是0;而当汇编器启动时,SRAM定位计数器的默认值是32(因为寄存器占有地址为0~31)。文件中可以出现多处ORG伪指令,但后面ORG所带的地址不能小于前一个ORG所带的地址,也不能落在由前一个ORG定位的代码段空间(指同一个存储器空间)。注意:EEPROM和SRAM定位计数器按字节计数,而程序存储器定位计数器按字计数。

下面对程序中的相关部分进行一下说明。

1)在AVR Studio的汇编语言环境中,十六进制数以$符号开头。

2)主程序的入口地址为$002A,因为在ATMega16中,从$0000~$002A之间有21个中断源的向量地址,主程序应该尽量避开这些地址,以免与中断程序冲突。

3)在AVR中,要访问I/O空间的寄存器,必须依靠某个通用寄存器来进行,不允许直接对I/O寄存器进行赋值。比如例程中,对SP寄存器(以及后续的DDRA、PORTA寄存器)的赋值只能通过通用寄存器TMP(R16)来中转。 

4)由于程序中使用了LDI和SER指令,所以通用寄存器TMP只能取R16~R31之间的值。

5)由于8个LED为共阳结构,所以流水灯采用0的循环左移即可。本例采用带进位位的左移,这样可以保证循环中始终有一个0,免去了左移到头的判断。

6)延时程序采取了空耗机器周期的方式,其中仅使用了一个寄存器(TMP),采用二次嵌套循环,并多次利用堆栈交换数据。它能在8MHz晶振时产生出长达1秒的延时,总机器周期数T与寄存器值x之间有如下关系。

总延时时间(秒)= T * 机器周期。如果晶振为8MHz,则机器机器周期为0.125us。本例中,x=114,T=800404,延时时间为100ms左右。

标签:TMP,汇编,16,SP,Rd,PC,实例,寄存器,ATMega16
From: https://www.cnblogs.com/fxzq/p/17863105.html

相关文章

  • 基于ATMega16的最小系统及其开发环境简介
    AVR实验例程用的最小系统如下图所示,芯片采用ATMega16A,主晶振频率为8MHz,异步晶振频率为32768Hz,系统采用JTAG接口调试及下载程序。以上仅是最小系统的电路图,后续例程中使用到的额外电路会在例程中给出相应的模块电路。AVRStudio集成开发环境(IDE)是专门用于开发AVR单片机的开发软......
  • 在net中通过Autofac实现AOP的方法及实例详解
     在本示例中,我们将使用Autofac和AspectC(Autofac.Extras.DynamicProxy2)来演示如何实现AOP(面向切面编程)。我们将创建一个简单的C#控制台应用程序,并应用AOP以解决日志记录的问题。首先,让我们讨论AOP的用途和目标。AOP(面向切面编程)的用途AOP是一种编程范式,旨在解决横切关注点(cro......
  • 汇编-.while循环语句
     语句结构: .while/.endw循环首先判断条件测试表达式,如果结果是“真”,则执行循环体内的指令,结束后再回到.while处判断表达式,如此往复,一直到表达式结果为“假”为止。.while/.endw指令有可能一遍也不会执行到循环体内的指令,因为如果第一次判断表达式时就遇到结果为“假......
  • 汇编-.repeat循环语句
     语法结构 .repeat/.until循环首先执行一遍循环体内的指令,然后再判断条件测试表达式,如果结果为“真”的话,就退出循环,如果为“假”,则返回.repeat处继续循环,可以看出,.repeat/.until不管表达式的值如何,至少会执行一遍循环体内的指令。      ......
  • 汇编-.if分支语句
     .if语句语法 注意:关键字if/elseif/else/endif的前面有个小数点,如果不加小数点,就变成宏汇编中的条件汇编伪操作了,结果可是天差地别。         ......
  • 【算法】远方来信,从数学表达式算法到汇编语法解释器
    在繁华的都市中,小悦作为一名软件工程师,每天都在这座钢筋水泥的森林里忙碌。她的生活似乎被工作和各种琐碎的事情填满了,但在这个繁忙的生活中,她总能在工作之余找到一些小小的乐趣。这天下班后,小悦收到了一封来自国外同学苏菲的email。邮件的内容让她的思绪一下子飘回了那个学习汇......
  • CTfpwn攻防世界int_overflow对于strlen的利用以及汇编是神
    分析这题题目已经在暗示用int数据的overflow了,不过不急,先分析一下。保护基本没啥保护,也挺好,适合不用搞太多花里胡哨的泄露,只需理解这题想告诉你的知识。后门函数看到有一个whatisthis函数,正是我们要的catflag函数。main函数login函数main函数里需要的操作很简单,只需输入一个1就......
  • 汇编-逻辑运算符
         MASM的条件测试语句有几个限制,首先是表达式的左边只能是变量或寄存器,不能为常数;其次表达式的两边不能同时为变量,但可以同时是寄存器。       ......
  • 阅读笔记4(实例化需求)
    《实例化需求》是一本由GojkoAdzic撰写的书籍,主要介绍了一种软件开发方法,即通过实例来进行需求规格说明,以确保团队交付正确的软件。以下是对这本书的阅读笔记:第一章:引言作者介绍了实例化需求的基本概念,即通过实例来明确软件需求。提到了该方法如何帮助团队在开发过程中更好地理解......
  • 实例化需求阅读笔记
    《SpecificationbyExample:HowSuccessfulTeamsDelivertheRightSoftware》是一本由GojkoAdzic撰写的书籍,深入探讨了利用实例来定义、验证和衡量软件功能的方法。这本书提供了关于使用实例来驱动软件开发的实践技巧和指导,帮助团队交付高质量、符合需求的软件。在阅读这本......