找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 5066|回复: 2
收起左侧

avr单片机汇编参考程序

[复制链接]
ID:104287 发表于 2016-1-31 20:37 | 显示全部楼层 |阅读模式
该AVR的汇编程序选自《M128》,程序中体现了AVR汇编的基本特点,仅供大家参考。

   该应用系统为一个带1/100秒的简易24小时制时钟,它在上电后能够自动从11时59分55秒00开始计时和显示时间。下图为简易时钟系统硬件电路图。



                           图  简易24小时时钟硬件原理图



   系统使用8个LED数码管显示时、分、秒、1/100秒4个时段的数字,每个时段占用2个LED。显示方式采用动态扫描方式,ATmega128的PA口输出显示数字的7段码(注意:图中省缺了PA口连接到LED各段的8个限流电阻,阻值800欧左右),PC口用于控制8个LED的位选。ATmega128使用外部16MHz晶振(图中未画出)。

  系统还使用ATmega128片内的计数/定时器T1,设计T1工作在定时溢出中断方式,定时间隔为2ms,即T1每2ms产生一次中断。5次中断得到10ms的时间间隔,此时时钟的1/100秒加1,并相应进行时、分、秒的调整。

  LED动态扫描方式的设计如下:在每2ms的时间中,点亮8个LED中的一个,显示其相应的数字(PC口的输出只有一位为低电平,选通一个LED,保持2ms)。因此PC口的输出值为0b11111110,每隔2ms循环右移,到0b01111111时8个LED各点亮一次,时间为16ms。在1秒钟内,循环8个LED的次数为62.5(1000/16),是人眼的滞留时间(25次/秒)的2.5倍,保证了LED显示亮度均匀,无闪烁。在程序设计中,在各个LED转换和7段码输出时,关闭位选信号(PC输出0b11111111),消除了显示的拖尾现象(消影功能)。

  T1的设计:T1为16位定时器,系统时钟为16M,采用其64分频后的时钟作为T1的计数信号(寄存器TCCR1B = 0x03),一个计数周期为4us,2ms需要计500个(0x01F4)。由于T1溢出中断发生在0xFFFF后下一个T1计数脉冲的到来(参见第二章关于定时器原理部分),因此T1的计数初始值为0xFE0C = 0xFFFF – 0x01F3(65535-499),即寄存器TCNT1的初值为0xFE0C。



3.8.2  AVR汇编源代码

  该系统的汇编源代码如下,开发软件平台使用AVR Studio 4.08。



  1. ;********************************************************

  2. ;AVR汇编程序实例

  3. ;简易带1/100秒的24小时制时钟

  4. ;********************************************************

  5. .include "m128def.inc"                ;引用器件I/O配置文件



  6. ;定义程序中使用的变量名(在寄存器空间)

  7. .def count                =        r18        ;循环计数单元

  8. .def position                =        r19        ;LED显示位指针,取值为0-7

  9. .def p_temp                 =        r20        ;LED显示位选,其值取反由PC口输出

  10. .def count_10ms        =        r21                ;10ms计数单元

  11. .def flag_2ms                =        r22        ;2ms到标志

  12. .def temp                        =        r23        ;临时变量

  13. .def temp1                =        r24                ;临时变量

  14. .def temp_int                =        r25        ;临时变量(中断中使用)



  15. ;中断向量区定义,flash空间$0000-$0045

  16. .org $0000

  17.         jmp reset                ;复位处理

  18.         reti                        ;IRQ0 Handler

  19.         nop

  20.         reti                        ;IRQ1 Handler

  21.         nop

  22.         reti                        ;IRQ2 Handler

  23.         nop

  24.         reti                        ;IRQ3 Handler

  25.         nop

  26.         reti                        ;IRQ4 Handler

  27.         nop

  28.         reti                        ;IRQ5 Handler

  29.         nop

  30.         reti                        ;IRQ6 Handler

  31.         nop

  32.         reti                        ;IRQ7 Handler

  33.         nop

  34.         reti                        ;Timer2 Compare Handler

  35.         nop

  36.         reti                        ;Timer2 Overflow Handler

  37.         nop

  38.         reti                        ;Timer1 Capture Handler

  39.         nop

  40.         reti                        ;Timer1 CompareA Handler

  41.         nop

  42.         reti                        ;Timer1 CompareB Handler

  43.         nop

  44.         jmp time1_ovf ;Timer1 Overflow Handler

  45.         reti                        ;Timer0 Compare Handler

  46.         nop

  47.         reti                        ;Timer0 Overflow Handler

  48.         nop

  49.         reti                        ;SPI Transfer Complete Handler

  50.         nop

  51.         reti                        ;USART0 RX Complete Handler

  52.         nop

  53.         reti                        ;USART0 UDR Empty Handler

  54.         nop

  55.         reti                        ;USART0 TX Complete Handler

  56.         nop

  57.         reti                        ;ADC Conversion Complete Handler

  58.         nop

  59.         reti                        ;E2PROM Ready Handler

  60.         nop

  61.         reti                        ;Analog Comparator Handler

  62.         nop

  63.         reti                        ;Timer1 CompareC Handler

  64.         nop

  65.         reti                        ;Timer3 Capture Handler

  66.         nop

  67.         reti                        ;Timer3 CompareA Handler

  68.         nop

  69.         reti                        ;Timer3 CompareB Handler

  70.         nop

  71.         reti                        ;Timer3 CompareC Handler

  72.         nop

  73.         reti                        ;Timer Overflow Handler

  74.         nop

  75.         reti                        ;USART1 RX Complete Handler

  76.         nop

  77.         reti                          ;USART1 UDR Empty Handler

  78.         nop

  79.         reti                        ;USART1 TX Complete Handler

  80.         nop

  81.         reti                        ;Two-wire Serial Interface Handler

  82.         nop

  83.         reti                        ;SPM Ready Handler

  84.         nop               



  85. ;程序开始

  86. .org $0046

  87. reset:

  88.         ldi r16,high(RAMEND)                ;设置堆栈指针高位

  89.         out sph,r16

  90.         ldi r16,low(RAMEND)                ;设置堆栈指针低位

  91.         out spl,r16

  92.         

  93.         ser temp                        

  94.         out ddra,temp                                ;设置PORTA为输出,段码输出

  95.         out ddrc,temp                                ;设置PORTC为输出,位码控制

  96.         out portc,temp                        ;PORTC输出$FF, 无显示



  97.         ldi position,0x00                        ;段位初始化为1/100秒低位

  98.         ldi p_temp,0x01                        ;LED第1位亮



  99. ;初始化时钟时间为11:59:55:00

  100.         ldi xl,low(time_buff)                ;

  101.         ldi xh,high(time_buff)                ;X寄存器取得时钟单元首指针

  102.         ldi temp,0x00

  103.         st  x+,temp                                ;1/100秒 = 00

  104.         ldi temp,0x55

  105.         st  x+,temp                                ;秒 = 55

  106.         ldi temp,0x59

  107.         st  x+,temp                                ;分 = 59

  108.         ldi temp,0x11

  109.         st  x,temp                                ;时 = 11



  110.         ldi temp,0xfe                                ;T1初始化,每隔2ms中断一次

  111.         out tcnt1h,temp

  112.         ldi temp,0x0c

  113.         out tcnt1l,temp

  114.         clr temp

  115.         out tccr1a,temp

  116.         ldi temp,0x03                                ;16M,64分频 2ms

  117.         out tccr1b,temp

  118.         ldi temp,0x04

  119.         out timsk,temp                        ;允许T1溢出中断

  120.         sei                                                ;全局中断允许



  121. ;主程序

  122. main:

  123.         cpi flag_2ms,0x01                        ;判2ms到否

  124.         brne main                                        ;No,转main循环

  125.         clr flag_2ms                                ;到,请2ms标志

  126.         rcall display                                ;调用LED显示时间(动态扫描显示一位)

  127. d_10ms_ok:

  128.         cpi count_10ms,0x05                ;判10ms到否

  129.         brne main                                        ;No,转main循环

  130.         clr count_10ms                        ;10ms到,清零10ms计数器

  131.         rcall time_add                        ;调用时间加10ms调整

  132.         rcall put_t2d                                ;将新时间值放入显示缓冲单元

  133.         rjmp main                                        ;转main循环



  134. ;LED动态扫描显示子程序,2ms执行一次,一次点亮一位,8位循环

  135. display:

  136.         clr r0

  137.         ser temp                                        ;temp = 0x11111111

  138.         out portc,temp                        ;关显示,去消影和拖尾作用

  139.         ldi yl,low(display_buff)

  140.         ldi yh,high(display_buff)        ;Y寄存器取得显示缓冲单元首指针

  141.         add yl,position                        ;加上要显示的位值

  142.         adc yh,r0                                        ;加上低位进位

  143.         ld temp,y                                        ;temp中为要显示的数字

  144.         

  145.         clr r0

  146.         ldi zl,low(led_7 * 2)

  147.         ldi zh,high(led_7 * 2)                ;Z寄存器取得7段码组的首指针

  148.         add zl,temp                                ;加上要显示的数字

  149.         adc zh,r0                                        ;加上低位进位        

  150.         lpm                                        ;读对应七段码到R0中

  151.         out porta,r0                                ;LED段码输出



  152.         mov r0,p_temp

  153.         com r0,

  154.         out portc,r0                        ;输出位控制字,完成LED一位的显示

  155.         

  156.         inc position                        ;调整到下一次显示位

  157.         lsl p_temp

  158.         cpi position,0x08

  159.         brne display_ret

  160.         ldi position,0x00

  161.         ldi p_temp,0x01

  162. display_ret:

  163.         ret



  164. ;时钟时间调整,加0.01秒

  165. time_add:

  166.         ldi xl,low(time_buff)        ;

  167.                 ldi xh,high(time_buff)        ;X寄存器为时钟单元首指针

  168.                 rcall dhm3                        ;ms单元加1调整

  169.                 cpi temp,0x99                ;

  170.                 brne        time_add_ret        ;未到99ms返回

  171.                 rcall dhm                        ;秒单元加1调整

  172.                 cpi temp,0x60

  173.                 brne        time_add_ret        ;未到60秒返回

  174.                 rcall dhm                        ;分单元加1调整

  175.                 cpi temp,0x60

  176.                 brne time_add_ret                ;未到60分返回

  177.                 rcall dhm                        ;时单元加1调整

  178. cpi temp,0x24

  179.      brne time_add_ret                        ;未到24时返回

  180.         clr temp

  181.         st x,temp                                ;到24时,时单元清另

  182. time_add_ret:

  183.         ret



  184. ;低段时间清零,高段时间加1,BCD调整

  185. dhm:  clr temp                        ;当前时段清零

  186. dhm1: st  x+,temp                        ;当前时段清零,X寄存器指针加一

  187. dhm3: ld  temp,x                        ;取出新时段数据

  188.       inc temp                  ;加一

  189.       cpi temp,0x0A                ;若个位数码未到$0A(10)

  190.       brhs dhm2                        ;例如$58+1=$59,不须调整;

  191.       subi temp,0xFA                ;否则做减$FA调整:例如$49+1-$FA=$50

  192. dhm2: st x,temp                        ;并将调整结果送回

  193.       ret



  194. ;将时钟单元数据送LED显示缓冲单元中

  195. put_t2d:

  196.         ldi xl,low(time_buff)                ;

  197.         ldi xh,high(time_buff)                ;X寄存器时钟单元首指针

  198.         ldi yl,low(display_buff)

  199.         ldi yh,high(display_buff)        ;Y寄存器显示缓冲单元首指针

  200.         ldi count,4                                ;循环次数 = 4

  201. loop:

  202.         ld        temp,x+                                ;读一个时间单元

  203.         mov        temp1,temp

  204.         swap        temp1

  205.         andi        temp1,0x0f                        ;高位BCD码

  206.         andi        temp,0x0f                                ;低位BCD码

  207.         st y+,temp                                ;写入2个显示单元

  208.         st y+,temp1                        ;低位BCD码在前,高位在后

  209.         dec        count

  210.         brne        loop                        ;4个时间单元->8个显示单元

  211.         ret



  212. ;T1时钟溢出中断服务

  213. time1_ovf:

  214.         in temp_int,sreg

  215.         push temp_int                                ;保护状态寄存器

  216.         

  217.         ldi temp_int,0xfe                        ;T1初始值设定,2ms中断一次

  218.         out tcnt1h,temp_int

  219.         ldi temp_int,0x0c

  220.         out tcnt1l,temp_int

  221.         

  222.         inc count_10ms                        ;10ms计数器加一

  223.         ldi flag_2ms,0x01                        ;置2ms标志到

  224.         

  225. pop temp_int

  226.         out sreg,        temp_int                        ;恢复状态寄存器

  227.         reti                                                ;中断返回               

  228.         

  229. .CSEG                                        ;LED七段码表,定义在Flash程序空间

  230. led_7:                                        ;7段码表

  231. .db 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07

  232. .db 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71        



  233. ;字 PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 共阴极 共阳极

  234. ;        h        g        f        E        d        c        b        a               

  235. ;0        0        0        1        1        1        1        1        1        3FH                C0H

  236. ;1        0        0        0        0        0        1        1        0        06H                F9H

  237. ;2        0        1        0        1        1        0        1        1        5BH                A4H

  238. ;3        0        1        0        0        1        1        1        1        4FH                B0H

  239. ;4        0        1        1        0        0        1        1        0        66H                99H

  240. ;5        0        1        1        0        1        1        0        1        6DH                92H

  241. ;6        0        1        1        1        1        1        0        1        7DH                82H

  242. ;7        0        0        0        0        0        1        1        1        07H                F8H

  243. ;8        0        1        1        1        1        1        1        1        7FH                80H

  244. ;9        0        1        1        0        1        1        1        1        6FH                90H

  245. ;A        0        1        1        1        0        1        1        1        77H                88H

  246. ;b        0        1        1        1        1        1        0        0        7CH                83H

  247. ;C        0        0        1        1        1        0        0        1        39H                C6H

  248. ;d        0        1        0        1        1        1        1        0        5EH                A1H

  249. ;E        0        1        1        1        1        0        0        1        79H                86H

  250. ;F        0        1        1        1        0        0        0        1        71H                8EH



  251. .DSEG                                ;定义程序中使用的变量位置(在RAM空间)

  252. .ORG     $0100

  253. display_buff:                        ;LED显示缓冲区,8个字节

  254. .BYTE        0x00                        ;LED 1 位显示内容

  255. .BYTE        0x00                        ;LED 2 位显示内容

  256. .BYTE        0x00                        ;LED 3 位显示内容

  257. .BYTE        0x00                        ;LED 4 位显示内容

  258. .BYTE        0x00                        ;LED 5 位显示内容

  259. .BYTE        0x00                        ;LED 6 位显示内容

  260. .BYTE        0x00                        ;LED 7 位显示内容

  261. .BYTE        0x00                        ;LED 8 位显示内容



  262. .org        $0108

  263. time_buff:                        ;时钟数据缓冲区,4个字节

  264. .BYTE        0x00                        ;1/100s单元

  265. .BYTE        0x00                        ;秒单元

  266. .BYTE        0x00                        ;分单元

  267. .BYTE        0x00                        ;时单元
复制代码



  该程序实例采用规范标准的设计理念和风格,程序中已给出比较详细的注解。关于程序如何具体完成和实现系统的功能请读者仔细阅读程序,用心体会。下面仅对编写ATmega128汇编程序时,在结构和语句使用上一些需要注意的方面加以介绍。

1.将程序中操作最频繁以及需要特殊位处理的变量定义在AVR的32个工作寄存器空间,因为MCU对R0-R31的操作仅需要一个时钟周期,而且功能强大。由于R0-R31的功能有不同,而且也仅有32个,所以程序员应认真考虑和规划这32个工作寄存器的使用。如尽量不要将变量放置在R26-R31中,因为这6个寄存器构成3个16位的X、Y、Z地址指针寄存器,应保留用于各种寻址使用。

2.ATmega128有35个中断源,Flash程序存储器的低段空间为这35个中断向量地址。由于ATmega128的程序存储器空间为64K字,所以与其它AVR不同的是,ATmega128的一个向量地址空间为2个字长度,在中断向量处应使用长转移指令jmp转移到中断服务程序,而一般的AVR的一个向量地址空间为1个字长度,使用rjmp转移指令。出于提高系统可靠性的设计,对于系统不使用的中断向量,应填充2个中断返回指令reti(每个reti占一个字)。在本程序中,为了程序的理解和阅读方便,使用了reti和nop指令填充一个2个字长度的向量地址空间。

3.程序中使用X、Y、Z三个16位的地址指针寄存器,基于他们的一些指令有自动加(减)一的功能,以及先加(减)、后使用,和先使用、后加(减)的区别,在使用中应注意正确和灵活的使用。

4.由于LED的七段码对照表是固定不变的,程序中将LED的七段码表放置在Flash存储器中。对于Flash存储器的间址取数只能使用Z寄存器。由于程序存储器的地址是以字(双字节)为单位的,因此,16位地址指针寄存器Z的高15位为程序存储器的字地址,最低位LSB为“0”时,指字的低字节;为“1”时,指字的高字节。程序中使用伪指令db定义的七段码为一个字节,他保存在一个字的低字节处。如果定义字,应使用伪指令dw。

本例使用指令lpm读取Flash中的一个字节,因此在取七段码表的首地址时乘2(ldi zl,low(led_7 * 2)),将地址左移一位,Z寄存器的LSB为“0”,表示取该字的低位字节。

指令lpm能寻址的程序存储器空间为低64K字节的页(32k字),因此如果常量表的位置处在高64字节的页中,请使用指令elpm。详细的指令功能见3.4.3的内容。

5.中断服务程序中,必须对MCU的标志寄存器SREG进行保护。在T1的溢出中断服务程序中,还需要对TCNT1的初值进行设置,以保证下一次中断仍为2ms。中断服务程序应尽量短小,因此在中断服务中,只将2ms标志置位和10ms加一计数,其它处理应尽量放在主程序中。

6.程序中定义了8个字节的显示缓冲区和4个字节的时钟数据缓冲区,分别存放8个LED所对应的显示数字和4个时间段的时间值(BCD码),这12个单元定义放置在ATmega128的RAM中。ATmega128的RAM单元应从0x0100开始,前面的地址分别对应的是32个工作寄存器、I/O寄存器、扩展I/O寄存器,因此不要把一般的数据单元定义在小于0x0100的空间(参见2.2.2,RAM空间分配)。

7.与使用db或dw伪指令在Flash空间定义常量不同的是,在RAM空间予留变量空间的定义应使用byte伪指令。byte伪指令的功能是定义变量的位置(予留空间),不能定义(填充)变量的值,变量具体的值是需要由程序在运行中写入的。而伪指令db、dw具有数据位置和值定义(填充)的功能。
回复

使用道具 举报

ID:342822 发表于 2020-8-14 21:31 | 显示全部楼层
325行com r0,错误改为:com r0 去掉逗点编译通过。
回复

使用道具 举报

ID:342822 发表于 2020-8-16 14:21 | 显示全部楼层
Proteus 8仿真avr单片机汇编参考程序 屏幕截图(37).png

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表