|
該AVR的匯編程序選自《M128》,程序中體現(xiàn)了AVR匯編的基本特點(diǎn),僅供大家參考。
該應(yīng)用系統(tǒng)為一個(gè)帶1/100秒的簡(jiǎn)易24小時(shí)制時(shí)鐘,它在上電后能夠自動(dòng)從11時(shí)59分55秒00開(kāi)始計(jì)時(shí)和顯示時(shí)間。下圖為簡(jiǎn)易時(shí)鐘系統(tǒng)硬件電路圖。

圖 簡(jiǎn)易24小時(shí)時(shí)鐘硬件原理圖
系統(tǒng)使用8個(gè)LED數(shù)碼管顯示時(shí)、分、秒、1/100秒4個(gè)時(shí)段的數(shù)字,每個(gè)時(shí)段占用2個(gè)LED。顯示方式采用動(dòng)態(tài)掃描方式,ATmega128的PA口輸出顯示數(shù)字的7段碼(注意:圖中省缺了PA口連接到LED各段的8個(gè)限流電阻,阻值800歐左右),PC口用于控制8個(gè)LED的位選。ATmega128使用外部16MHz晶振(圖中未畫(huà)出)。
系統(tǒng)還使用ATmega128片內(nèi)的計(jì)數(shù)/定時(shí)器T1,設(shè)計(jì)T1工作在定時(shí)溢出中斷方式,定時(shí)間隔為2ms,即T1每2ms產(chǎn)生一次中斷。5次中斷得到10ms的時(shí)間間隔,此時(shí)時(shí)鐘的1/100秒加1,并相應(yīng)進(jìn)行時(shí)、分、秒的調(diào)整。
LED動(dòng)態(tài)掃描方式的設(shè)計(jì)如下:在每2ms的時(shí)間中,點(diǎn)亮8個(gè)LED中的一個(gè),顯示其相應(yīng)的數(shù)字(PC口的輸出只有一位為低電平,選通一個(gè)LED,保持2ms)。因此PC口的輸出值為0b11111110,每隔2ms循環(huán)右移,到0b01111111時(shí)8個(gè)LED各點(diǎn)亮一次,時(shí)間為16ms。在1秒鐘內(nèi),循環(huán)8個(gè)LED的次數(shù)為62.5(1000/16),是人眼的滯留時(shí)間(25次/秒)的2.5倍,保證了LED顯示亮度均勻,無(wú)閃爍。在程序設(shè)計(jì)中,在各個(gè)LED轉(zhuǎn)換和7段碼輸出時(shí),關(guān)閉位選信號(hào)(PC輸出0b11111111),消除了顯示的拖尾現(xiàn)象(消影功能)。
T1的設(shè)計(jì):T1為16位定時(shí)器,系統(tǒng)時(shí)鐘為16M,采用其64分頻后的時(shí)鐘作為T(mén)1的計(jì)數(shù)信號(hào)(寄存器TCCR1B = 0x03),一個(gè)計(jì)數(shù)周期為4us,2ms需要計(jì)500個(gè)(0x01F4)。由于T1溢出中斷發(fā)生在0xFFFF后下一個(gè)T1計(jì)數(shù)脈沖的到來(lái)(參見(jiàn)第二章關(guān)于定時(shí)器原理部分),因此T1的計(jì)數(shù)初始值為0xFE0C = 0xFFFF – 0x01F3(65535-499),即寄存器TCNT1的初值為0xFE0C。
3.8.2 AVR匯編源代碼
該系統(tǒng)的匯編源代碼如下,開(kāi)發(fā)軟件平臺(tái)使用AVR Studio 4.08。
- ;********************************************************
- ;AVR匯編程序?qū)嵗?br />
- ;簡(jiǎn)易帶1/100秒的24小時(shí)制時(shí)鐘
- ;********************************************************
- .include "m128def.inc" ;引用器件I/O配置文件
- ;定義程序中使用的變量名(在寄存器空間)
- .def count = r18 ;循環(huán)計(jì)數(shù)單元
- .def position = r19 ;LED顯示位指針,取值為0-7
- .def p_temp = r20 ;LED顯示位選,其值取反由PC口輸出
- .def count_10ms = r21 ;10ms計(jì)數(shù)單元
- .def flag_2ms = r22 ;2ms到標(biāo)志
- .def temp = r23 ;臨時(shí)變量
- .def temp1 = r24 ;臨時(shí)變量
- .def temp_int = r25 ;臨時(shí)變量(中斷中使用)
- ;中斷向量區(qū)定義,flash空間$0000-$0045
- .org $0000
- jmp reset ;復(fù)位處理
- reti ;IRQ0 Handler
- nop
- reti ;IRQ1 Handler
- nop
- reti ;IRQ2 Handler
- nop
- reti ;IRQ3 Handler
- nop
- reti ;IRQ4 Handler
- nop
- reti ;IRQ5 Handler
- nop
- reti ;IRQ6 Handler
- nop
- reti ;IRQ7 Handler
- nop
- reti ;Timer2 Compare Handler
- nop
- reti ;Timer2 Overflow Handler
- nop
- reti ;Timer1 Capture Handler
- nop
- reti ;Timer1 CompareA Handler
- nop
- reti ;Timer1 CompareB Handler
- nop
- jmp time1_ovf ;Timer1 Overflow Handler
- reti ;Timer0 Compare Handler
- nop
- reti ;Timer0 Overflow Handler
- nop
- reti ;SPI Transfer Complete Handler
- nop
- reti ;USART0 RX Complete Handler
- nop
- reti ;USART0 UDR Empty Handler
- nop
- reti ;USART0 TX Complete Handler
- nop
- reti ;ADC Conversion Complete Handler
- nop
- reti ;E2PROM Ready Handler
- nop
- reti ;Analog Comparator Handler
- nop
- reti ;Timer1 CompareC Handler
- nop
- reti ;Timer3 Capture Handler
- nop
- reti ;Timer3 CompareA Handler
- nop
- reti ;Timer3 CompareB Handler
- nop
- reti ;Timer3 CompareC Handler
- nop
- reti ;Timer Overflow Handler
- nop
- reti ;USART1 RX Complete Handler
- nop
- reti ;USART1 UDR Empty Handler
- nop
- reti ;USART1 TX Complete Handler
- nop
- reti ;Two-wire Serial Interface Handler
- nop
- reti ;SPM Ready Handler
- nop
- ;程序開(kāi)始
- .org $0046
- reset:
- ldi r16,high(RAMEND) ;設(shè)置堆棧指針高位
- out sph,r16
- ldi r16,low(RAMEND) ;設(shè)置堆棧指針低位
- out spl,r16
-
- ser temp
- out ddra,temp ;設(shè)置PORTA為輸出,段碼輸出
- out ddrc,temp ;設(shè)置PORTC為輸出,位碼控制
- out portc,temp ;PORTC輸出$FF, 無(wú)顯示
- ldi position,0x00 ;段位初始化為1/100秒低位
- ldi p_temp,0x01 ;LED第1位亮
- ;初始化時(shí)鐘時(shí)間為11:59:55:00
- ldi xl,low(time_buff) ;
- ldi xh,high(time_buff) ;X寄存器取得時(shí)鐘單元首指針
- ldi temp,0x00
- st x+,temp ;1/100秒 = 00
- ldi temp,0x55
- st x+,temp ;秒 = 55
- ldi temp,0x59
- st x+,temp ;分 = 59
- ldi temp,0x11
- st x,temp ;時(shí) = 11
- ldi temp,0xfe ;T1初始化,每隔2ms中斷一次
- out tcnt1h,temp
- ldi temp,0x0c
- out tcnt1l,temp
- clr temp
- out tccr1a,temp
- ldi temp,0x03 ;16M,64分頻 2ms
- out tccr1b,temp
- ldi temp,0x04
- out timsk,temp ;允許T1溢出中斷
- sei ;全局中斷允許
- ;主程序
- main:
- cpi flag_2ms,0x01 ;判2ms到否
- brne main ;No,轉(zhuǎn)main循環(huán)
- clr flag_2ms ;到,請(qǐng)2ms標(biāo)志
- rcall display ;調(diào)用LED顯示時(shí)間(動(dòng)態(tài)掃描顯示一位)
- d_10ms_ok:
- cpi count_10ms,0x05 ;判10ms到否
- brne main ;No,轉(zhuǎn)main循環(huán)
- clr count_10ms ;10ms到,清零10ms計(jì)數(shù)器
- rcall time_add ;調(diào)用時(shí)間加10ms調(diào)整
- rcall put_t2d ;將新時(shí)間值放入顯示緩沖單元
- rjmp main ;轉(zhuǎn)main循環(huán)
- ;LED動(dòng)態(tài)掃描顯示子程序,2ms執(zhí)行一次,一次點(diǎn)亮一位,8位循環(huán)
- display:
- clr r0
- ser temp ;temp = 0x11111111
- out portc,temp ;關(guān)顯示,去消影和拖尾作用
- ldi yl,low(display_buff)
- ldi yh,high(display_buff) ;Y寄存器取得顯示緩沖單元首指針
- add yl,position ;加上要顯示的位值
- adc yh,r0 ;加上低位進(jìn)位
- ld temp,y ;temp中為要顯示的數(shù)字
-
- clr r0
- ldi zl,low(led_7 * 2)
- ldi zh,high(led_7 * 2) ;Z寄存器取得7段碼組的首指針
- add zl,temp ;加上要顯示的數(shù)字
- adc zh,r0 ;加上低位進(jìn)位
- lpm ;讀對(duì)應(yīng)七段碼到R0中
- out porta,r0 ;LED段碼輸出
- mov r0,p_temp
- com r0,
- out portc,r0 ;輸出位控制字,完成LED一位的顯示
-
- inc position ;調(diào)整到下一次顯示位
- lsl p_temp
- cpi position,0x08
- brne display_ret
- ldi position,0x00
- ldi p_temp,0x01
- display_ret:
- ret
- ;時(shí)鐘時(shí)間調(diào)整,加0.01秒
- time_add:
- ldi xl,low(time_buff) ;
- ldi xh,high(time_buff) ;X寄存器為時(shí)鐘單元首指針
- rcall dhm3 ;ms單元加1調(diào)整
- cpi temp,0x99 ;
- brne time_add_ret ;未到99ms返回
- rcall dhm ;秒單元加1調(diào)整
- cpi temp,0x60
- brne time_add_ret ;未到60秒返回
- rcall dhm ;分單元加1調(diào)整
- cpi temp,0x60
- brne time_add_ret ;未到60分返回
- rcall dhm ;時(shí)單元加1調(diào)整
- cpi temp,0x24
- brne time_add_ret ;未到24時(shí)返回
- clr temp
- st x,temp ;到24時(shí),時(shí)單元清另
- time_add_ret:
- ret
- ;低段時(shí)間清零,高段時(shí)間加1,BCD調(diào)整
- dhm: clr temp ;當(dāng)前時(shí)段清零
- dhm1: st x+,temp ;當(dāng)前時(shí)段清零,X寄存器指針加一
- dhm3: ld temp,x ;取出新時(shí)段數(shù)據(jù)
- inc temp ;加一
- cpi temp,0x0A ;若個(gè)位數(shù)碼未到$0A(10)
- brhs dhm2 ;例如$58+1=$59,不須調(diào)整;
- subi temp,0xFA ;否則做減$FA調(diào)整:例如$49+1-$FA=$50
- dhm2: st x,temp ;并將調(diào)整結(jié)果送回
- ret
- ;將時(shí)鐘單元數(shù)據(jù)送LED顯示緩沖單元中
- put_t2d:
- ldi xl,low(time_buff) ;
- ldi xh,high(time_buff) ;X寄存器時(shí)鐘單元首指針
- ldi yl,low(display_buff)
- ldi yh,high(display_buff) ;Y寄存器顯示緩沖單元首指針
- ldi count,4 ;循環(huán)次數(shù) = 4
- loop:
- ld temp,x+ ;讀一個(gè)時(shí)間單元
- mov temp1,temp
- swap temp1
- andi temp1,0x0f ;高位BCD碼
- andi temp,0x0f ;低位BCD碼
- st y+,temp ;寫(xiě)入2個(gè)顯示單元
- st y+,temp1 ;低位BCD碼在前,高位在后
- dec count
- brne loop ;4個(gè)時(shí)間單元->8個(gè)顯示單元
- ret
- ;T1時(shí)鐘溢出中斷服務(wù)
- time1_ovf:
- in temp_int,sreg
- push temp_int ;保護(hù)狀態(tài)寄存器
-
- ldi temp_int,0xfe ;T1初始值設(shè)定,2ms中斷一次
- out tcnt1h,temp_int
- ldi temp_int,0x0c
- out tcnt1l,temp_int
-
- inc count_10ms ;10ms計(jì)數(shù)器加一
- ldi flag_2ms,0x01 ;置2ms標(biāo)志到
-
- pop temp_int
- out sreg, temp_int ;恢復(fù)狀態(tài)寄存器
- reti ;中斷返回
-
- .CSEG ;LED七段碼表,定義在Flash程序空間
- led_7: ;7段碼表
- .db 0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07
- .db 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71
- ;字 PA7 PA6 PA5 PA4 PA3 PA2 PA1 PA0 共陰極 共陽(yáng)極
- ; h g f E d c b a
- ;0 0 0 1 1 1 1 1 1 3FH C0H
- ;1 0 0 0 0 0 1 1 0 06H F9H
- ;2 0 1 0 1 1 0 1 1 5BH A4H
- ;3 0 1 0 0 1 1 1 1 4FH B0H
- ;4 0 1 1 0 0 1 1 0 66H 99H
- ;5 0 1 1 0 1 1 0 1 6DH 92H
- ;6 0 1 1 1 1 1 0 1 7DH 82H
- ;7 0 0 0 0 0 1 1 1 07H F8H
- ;8 0 1 1 1 1 1 1 1 7FH 80H
- ;9 0 1 1 0 1 1 1 1 6FH 90H
- ;A 0 1 1 1 0 1 1 1 77H 88H
- ;b 0 1 1 1 1 1 0 0 7CH 83H
- ;C 0 0 1 1 1 0 0 1 39H C6H
- ;d 0 1 0 1 1 1 1 0 5EH A1H
- ;E 0 1 1 1 1 0 0 1 79H 86H
- ;F 0 1 1 1 0 0 0 1 71H 8EH
- .DSEG ;定義程序中使用的變量位置(在RAM空間)
- .ORG $0100
- display_buff: ;LED顯示緩沖區(qū),8個(gè)字節(jié)
- .BYTE 0x00 ;LED 1 位顯示內(nèi)容
- .BYTE 0x00 ;LED 2 位顯示內(nèi)容
- .BYTE 0x00 ;LED 3 位顯示內(nèi)容
- .BYTE 0x00 ;LED 4 位顯示內(nèi)容
- .BYTE 0x00 ;LED 5 位顯示內(nèi)容
- .BYTE 0x00 ;LED 6 位顯示內(nèi)容
- .BYTE 0x00 ;LED 7 位顯示內(nèi)容
- .BYTE 0x00 ;LED 8 位顯示內(nèi)容
- .org $0108
- time_buff: ;時(shí)鐘數(shù)據(jù)緩沖區(qū),4個(gè)字節(jié)
- .BYTE 0x00 ;1/100s單元
- .BYTE 0x00 ;秒單元
- .BYTE 0x00 ;分單元
- .BYTE 0x00 ;時(shí)單元
復(fù)制代碼
該程序?qū)嵗捎靡?guī)范標(biāo)準(zhǔn)的設(shè)計(jì)理念和風(fēng)格,程序中已給出比較詳細(xì)的注解。關(guān)于程序如何具體完成和實(shí)現(xiàn)系統(tǒng)的功能請(qǐng)讀者仔細(xì)閱讀程序,用心體會(huì)。下面僅對(duì)編寫(xiě)ATmega128匯編程序時(shí),在結(jié)構(gòu)和語(yǔ)句使用上一些需要注意的方面加以介紹。
1.將程序中操作最頻繁以及需要特殊位處理的變量定義在AVR的32個(gè)工作寄存器空間,因?yàn)镸CU對(duì)R0-R31的操作僅需要一個(gè)時(shí)鐘周期,而且功能強(qiáng)大。由于R0-R31的功能有不同,而且也僅有32個(gè),所以程序員應(yīng)認(rèn)真考慮和規(guī)劃這32個(gè)工作寄存器的使用。如盡量不要將變量放置在R26-R31中,因?yàn)檫@6個(gè)寄存器構(gòu)成3個(gè)16位的X、Y、Z地址指針寄存器,應(yīng)保留用于各種尋址使用。
2.ATmega128有35個(gè)中斷源,F(xiàn)lash程序存儲(chǔ)器的低段空間為這35個(gè)中斷向量地址。由于ATmega128的程序存儲(chǔ)器空間為64K字,所以與其它AVR不同的是,ATmega128的一個(gè)向量地址空間為2個(gè)字長(zhǎng)度,在中斷向量處應(yīng)使用長(zhǎng)轉(zhuǎn)移指令jmp轉(zhuǎn)移到中斷服務(wù)程序,而一般的AVR的一個(gè)向量地址空間為1個(gè)字長(zhǎng)度,使用rjmp轉(zhuǎn)移指令。出于提高系統(tǒng)可靠性的設(shè)計(jì),對(duì)于系統(tǒng)不使用的中斷向量,應(yīng)填充2個(gè)中斷返回指令reti(每個(gè)reti占一個(gè)字)。在本程序中,為了程序的理解和閱讀方便,使用了reti和nop指令填充一個(gè)2個(gè)字長(zhǎng)度的向量地址空間。
3.程序中使用X、Y、Z三個(gè)16位的地址指針寄存器,基于他們的一些指令有自動(dòng)加(減)一的功能,以及先加(減)、后使用,和先使用、后加(減)的區(qū)別,在使用中應(yīng)注意正確和靈活的使用。
4.由于LED的七段碼對(duì)照表是固定不變的,程序中將LED的七段碼表放置在Flash存儲(chǔ)器中。對(duì)于Flash存儲(chǔ)器的間址取數(shù)只能使用Z寄存器。由于程序存儲(chǔ)器的地址是以字(雙字節(jié))為單位的,因此,16位地址指針寄存器Z的高15位為程序存儲(chǔ)器的字地址,最低位LSB為“0”時(shí),指字的低字節(jié);為“1”時(shí),指字的高字節(jié)。程序中使用偽指令db定義的七段碼為一個(gè)字節(jié),他保存在一個(gè)字的低字節(jié)處。如果定義字,應(yīng)使用偽指令dw。
本例使用指令lpm讀取Flash中的一個(gè)字節(jié),因此在取七段碼表的首地址時(shí)乘2(ldi zl,low(led_7 * 2)),將地址左移一位,Z寄存器的LSB為“0”,表示取該字的低位字節(jié)。
指令lpm能尋址的程序存儲(chǔ)器空間為低64K字節(jié)的頁(yè)(32k字),因此如果常量表的位置處在高64字節(jié)的頁(yè)中,請(qǐng)使用指令elpm。詳細(xì)的指令功能見(jiàn)3.4.3的內(nèi)容。
5.中斷服務(wù)程序中,必須對(duì)MCU的標(biāo)志寄存器SREG進(jìn)行保護(hù)。在T1的溢出中斷服務(wù)程序中,還需要對(duì)TCNT1的初值進(jìn)行設(shè)置,以保證下一次中斷仍為2ms。中斷服務(wù)程序應(yīng)盡量短小,因此在中斷服務(wù)中,只將2ms標(biāo)志置位和10ms加一計(jì)數(shù),其它處理應(yīng)盡量放在主程序中。
6.程序中定義了8個(gè)字節(jié)的顯示緩沖區(qū)和4個(gè)字節(jié)的時(shí)鐘數(shù)據(jù)緩沖區(qū),分別存放8個(gè)LED所對(duì)應(yīng)的顯示數(shù)字和4個(gè)時(shí)間段的時(shí)間值(BCD碼),這12個(gè)單元定義放置在ATmega128的RAM中。ATmega128的RAM單元應(yīng)從0x0100開(kāi)始,前面的地址分別對(duì)應(yīng)的是32個(gè)工作寄存器、I/O寄存器、擴(kuò)展I/O寄存器,因此不要把一般的數(shù)據(jù)單元定義在小于0x0100的空間(參見(jiàn)2.2.2,RAM空間分配)。
7.與使用db或dw偽指令在Flash空間定義常量不同的是,在RAM空間予留變量空間的定義應(yīng)使用byte偽指令。byte偽指令的功能是定義變量的位置(予留空間),不能定義(填充)變量的值,變量具體的值是需要由程序在運(yùn)行中寫(xiě)入的。而偽指令db、dw具有數(shù)據(jù)位置和值定義(填充)的功能。
|
|