AVR程序释疑.docx
《AVR程序释疑.docx》由会员分享,可在线阅读,更多相关《AVR程序释疑.docx(26页珍藏版)》请在冰豆网上搜索。
AVR程序释疑
把寄存器R0~R29置$FF,把SRAM$0060~$006F单元清0,把$0070~$007F单元置$FF,把$0080~$008F单元分别设置为0~F
.include"m8def.inc";包含m8def.inc文件
.org$0000;代码起始端定位
rjmpMAIN;跳转到主程序
.equdata1=$00ff;设置符号项等于一个表达式
.org$0013;代码段定位,跳过中断向量区
MAIN:
;初始化堆栈指针,对所有带SRAM的AVR单片机,堆栈指针必须设置在SRAM最后的地址
ldir16,high(RAMEND);取RAMEND的高位字节到r16
outSPH,r16;将RAMEND的高位送堆栈寄存器SP高位字节中
ldir16,low(RAMEND);取RAMEND的低位字节到r16
outSPL,r16;将RAMEND的低位送堆栈寄存器SP低位字节中
;把寄存器R0~R29置0xFF
Ldir16,low(data1);取data1的低位字节到r16
part1:
stz+,r16;加载间接寻址数据,然后地址加一,即(Z)←Rr,Z←Z+1。
Z的初始指向是R0
cpr29,r16;比较,r29-r16
brnepart1;如果不相等,转到part1
;把SRAM0x60~0x6F单元清0。
为什么这里的X寄存器需要分高位和低位?
.setdatad=$0060;设置一个与表达式值相等的符号,能在后面的程序中重新改变设置
.setdatau=$006f
ldir30,high(data1);取data1的高位字节到r30,r30是Z寄存器低字节
ldixl,low(datad);取datad低位字节到X寄存器低字节,就是r26
ldixh,high(datad);取datad高位字节到X寄存器高字节,就是r27
ldir16,low(datau);取datau的低位字节到r16
part2:
cpxl,r16;比较xl和r16
stx+,r30;以间接寻址方式存储数据,然后地址加一,(X)←Rr,X←X+1
brnepart2;如果不相等转到part2
;把0x70~0x7F单元置0xFF
.setdatad=$0070
.setdatau=$007f
ldir31,low(data1);R31是Z寄存器高字节,纯粹用作数据寄存,因为R0~R29已被设置了
ldiyl,low(datad)
ldiyh,high(datad)
ldir17,low(datau)
part3:
cpyl,r17
sty+,r31
brnepart3
;nop
;0x80~0x8F单元分别设置为0~F
.setdatad=$0080
.setdatau=$0090
Ldir16,$00
ldizl,low(datad)
ldizh,high(datad)
ldir17,low(datau)
part4:
stz+,r16;加载间接寻址数据,然后地址加一,即(Z)←Rr,Z←Z+1。
Z的初始地址是R0
incr16;r16←r16+1
cpzl,r17
brnepart4
;nop
here:
rjmphere
把SRAM地址为$0060开始的256个单元内容依次复制到地址为$0100开始的单元(注意两数据区有部分重叠)
include"m8def.inc"
.org$0000
rjmpMAIN
.setsrm1=$0160;注意这是十六进制
.setsrm2=$0200
.org$0013
MAIN:
;初始化堆栈指针
ldir16,low(ramend)
outspl,r16
ldir16,high(ramend)
outsph,r16
;设置指针地址
ldizl,low(srm1)
ldizh,high(srm1)
ldixl,low(srm2)
ldixh,high(srm2)
ldir16,-1;因为立即数最大只能设255,要复制256个数据,只能初始设为-1
rcallsubroutine
here:
rjmphere
;调用子程序完成复制
subroutine:
ldr0,-z;地址减一后加载间接寻址数据Z←Z-1,R0←(Z).
;为什么减一呢,因为复制的数据有重叠,为了完整复制,就从末尾往前,避免遗失数据。
st-x,r0;地址减一后以间接寻址方式存储数据X←X-1,(X)←R0
incr16
cpir16,255
brnesubroutine
ret
把Flash程序存储器中地址为0x003开始的10个字单元内容传送到SRAM(先低字节,后高字节)。
.include"m8def.inc"
.org$0000
rjmpMAIN
.setflh=$0003
.setsrm=$0070
.org$0013
MAIN:
Ldir16,low(ramend)
outspl,r16
ldir16,high(ramend)
outsph,r16
;设置Z指针对应于flash地址的值
ldizl,low(flh*2);一个单位的flash存储位置,其实占用两个位置的地址栏
ldizh,high(flh*2)
ldixl,low(srm)
ldixh,high(srm)
ldir16,0
subroutine:
lpmr0,z+;加载程序空间的数据,然后地址加一R0←(Z),Z←Z+1
lpmr1,z+;正因为是16位,每次的赋值只能赋8位,所以要赋2次才能完成8+8=16的赋值过程
stx+,r0;因为atmega系列的单片机不支持内存to内存的赋值方式,所以要用寄存器做中继,可以看到,不管是从flash到reg,还是reg到sram,每一个值都用了两步
stx+,r1
incr16
cpir16,10
brnesubroutine
here:
rjmphere
用I/O引脚驱动若干个LED,编写跑马灯程序
.include"m8def.inc"
.org$0000
.setleft=1;left=1就是left=0b00000001,也就是八个led灯仅有最右一个是高电平,我估计你们那块板子的灯是高电平点亮的,这样的话如果让left生效则点亮最右一盏灯,这样往后向左一步步循环嘛.
.setright=0b10000000;同上,这就是点亮最左一盏灯,这样向右才能做
rjmpMAIN
.org$0013
MAIN:
ldir16,low(ramend);设置堆栈地址
outspl,r16
ldir16,high(ramend)
outsph,r16
;设置D端口为输出
ldir19,$ff
outddrd,r19
ldir21,right
ldir20,left
;实现LED灯从左到右依次闪烁
part1:
outportd,r20
rcalldelay;延时,避免移太快,眼睛有视觉延迟,太快了看上去就是全亮的
lslr20;这条命令是r20循环左移一位,换一个灯
brccpart1
;实现LED灯从右到左依次闪烁
part2:
outportd,r21
rcalldelay
lsrr21;这就是循环右移一位,其他的和part1一样,你看两条命令就很明显l代表left,r代表right
brccpart2
;初始化寄存器状态并循环
ldir21,right;赋值,把right变量的赋给r21,left赋给20
ldir20,left
;初始化寄存器状态并循环
ldir21,right
ldir20,left
here:
rjmppart1
;延时程序
delay:
ldir16,0x00;赋值
ldir17,0x00
ldir18,0x00
delay1:
incr16
;delay1做到的就是进来的时候,r16是00.先做一步自增一,变成01,然后每次和r18相比(00),如果不同则继续循环delay1,这就意味着r16要从00自增到ff然后继续自增1才会跳转到00,这不就执行了ff次的delay1么
cpr16,r18
brnedelay1
incr17
;做到了ff次的delay1的前三句之后,让r17自增1,然后同上,需要r17自增到ff再自增一次跳转到00才能结束延时程序,就是说要做ff*ff次的delay1前三句才能完成延时子程序噻~这就是延时的真谛
cpr17,r18
brnedelay1
clc;清零C标志位
ret
把Flash中地址为$013开始的20个字单元内的数据代码作为20个16位二进制符号数,将它们分别取绝对值后存储到SRAM中起始地址为$0100的区域,要求低字节在前,高字节在后。
.include"m8def.inc"
.org$0000
.equflh=$013
.equsrm=$100
rjmpmain
.org$0013
main:
ldir16,low(ramend);设置堆栈地址
outspl,r16
ldir16,high(ramend)
outsph,r16
ldizl,low(flh*2)
ldizh,high(flh*2)
ldixl,low(srm)
ldixh,high(srm)
ldir18,0
part1:
lpmr22,z+;取数后,Z加一
lpmr23,z+
rcallpart2;调用取绝对值子程序
store:
stx+,r22;存数后,X加一
stx+,r23
incr18
cpir18,20
brnepart1
here:
rjmphere
part2:
TSTR23;测寄存器为零或者负
BRPLstore;为正转移
SUBIR22,1;r22里面存的是负数的补码,补码是原码取反加一,所以要减去这个加上去的一,然后获得负数的原码
SBCIR23,0;这个是此负数的高八位,因为负数比较大所以需要两个寄存器存储
COMR22;对高位取反
COMR23;对低位取反,取反后得到负数的正数形式的原码,正数的补码即为其原码,所以得到绝对值
Ret
已知SRAMADDR($0062)为起始地址的数据块的数据是无符号数,块长在LEN($0061)单元。
求最大值并存入MAX($0060)单元
.include"m16def.inc"
.SETLEN=$0061
.SETADDR=$0062
.SETMAX=$0060
.ORG$000
RJMPSTART
.ORG0X002A
START:
LDSR20,LEN;r20存入数据块长
CLRR16;r16清零
LDIXL,LOW(ADDR);X指针指向数据起始
LDIXH,HIGH(ADDR)
LOOP:
LDR18,X+;X件址取数后加一
CPR18,R16;比较R18和R16,如果R18小于R16,则跳转到next,否则R18数据送到R16
BRCSNEXT;BRLO
MOVR16,R18
NEXT:
DECR20;r20减一,数据块长减一
BRNELOOP;跳回loop,没有遍历所有数据则跳回loop继续
STSMAX,R16;R16的数据送max
here:
rjmphere
.exit
无符号数求最小值
.include"m16def.inc"
.SETLEN=$0061
.SETADDR=$0062
.SETMIN=$0060
.ORG$000
RJMPSTART
.ORG0X002A
START:
LDSR20,LEN
SERR16;设置最大值,R16置为255,即11111111
LDIXL,LOW(ADDR)
LDIXH,HIGH(ADDR)
LOOP:
LDR18,X+
CPR16,R18
BRCSNEXT;比较R18和R16,如果R16大于R18,则R18送R16,否则跳转至next
;够减则C位为1,BRCS是进位标志位C为1转移
MOVR16,R18
NEXT:
DECR20
BRNELOOP
STSMIN,R16
here:
rjmphere
.exit
已知SRAMADDR($0062)为起始地址的数据块的数据是符号数,块长在LEN($0061)单元。
求最大值并存入MAX($0060)单元
.include"m16def.inc"
.SETLEN=$0061
.SETADDR=$0062
.SETMAX=$0060
.ORG$000
RJMPSTART
.ORG0X002A
START:
LDSR20,LEN
LDIR16,$80;八位有符号数的范围是-128~+128,怎么判断我这里存入的比较数值是-128?
LDIXL,LOW(ADDR)
LDIXH,HIGH(ADDR)
LOOP:
LDR18,X+
CPR18,R16
BRLTNEXT;如果R18小于R16,则跳转next,否则R18送R16
MOVR16,R18
NEXT:
DECR20
BRNELOOP
STSMAX,R16
here:
rjmphere
.exit
有符号数求最小值
.include"m8def.inc"
.SETLEN=$0061
.SETADDR=$0062
.SETMIN=$0060
RJMPSTART
.ORG0X002A
START:
LDSR20,LEN
LDIR16,$7F;设置最大值,$7F=127
LDIXL,LOW(ADDR)
LDIXH,HIGH(ADDR)
LOOP:
LDR18,X+
CPR16,R18;如果R18小于R16,则跳转next,否则R18送R16
BRLTNEXT;BRLT小于转移
MOVR16,R18
NEXT:
DECR20
BRNELOOP
STSMIN,R16
here:
rjmphere
.exit
设SRAM中有个数据块(起始地址为$0061,块长放在$0060单元),数据块内每个字节单元含有两个十六进制数(高4位和低4位),试分别求出这些十六进制数的ASCII码,并放在SRAM中以$0163为起始地址的区域,要求低4位数的ASCII码在前,高4位数的ASCII码在后。
.include"m8def.inc"
.SETLEN=$0060
.SETBLOCK1=$0061
.SETBLOCK2=$0163
.ORG$000
RJMPMAIN
ASCTAB:
.DB$30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$41,$42,$43,$44,$45,$46
.ORG0X002A
;十六进制和ASCII码的转换,30H-39H为0-9的Ascii码,41H-46H为A-F的Ascii码,将十六进制对应地加上去就是了
MAIN:
ldir16,low(ramend)
outspl,r16
ldir16,high(ramend)
outsph,r16
LDSR25,LEN
LDIXL,LOW(BLOCK1)
LDIXH,HIGH(BLOCK1)
LDIYL,LOW(BLOCK2)
LDIYH,HIGH(BLOCK2)
LOOP:
LDIZL,LOW(ASCTAB*2)
LDIZH,HIGH(ASCTAB*2)
CLRR18
LDR17,X
ANDIR17,$0F;取十六进制数低位
;andi是"与立即数,这个语句的目的就是把R17里面的低位留下,高位清零,比如R17=$23,andi之后,R17=$03
ADDZL,R17;低位,无进位加法
ADCZH,R18;高位,带进位加法,其实R18就是0,这里是保险的做法
LPMR17,Z;转换为ASCII码
STY+,R17
LDR17,X+
ANDIR17,$F0;取十六进制数高位
SWAPR17;半字节交换,因为后面的加法运算不能直接跳过低位在高位运算,所以必须交换,而两个十六进制数(高4位和低4位),只需要转换后按照高低顺序存入新的区域便可,转换后的是ascii码要用8位来存,所以不存在交换的问题。
LDIZL,LOW(ASCTAB*2)
LDIZH,HIGH(ASCTAB*2)
ADDZL,R17
ADCZH,R18
LPMR17,Z
STY+,R17
DECR25
BRNELOOP
HERE:
RJMPHERE
设SRAM中$0060和$0061单元存储的是一个16位二进制无符号数(高字节在前),试调用代码转换子程序和延时子程序,把这个数转换成十进制数(即BCD码),并把其万、千、百、十、个位用一个数码管轮流显示出来,每位显示时间约1秒钟
.include"m8def.inc"
.equdata=$0060
.org$0000
rjmpmain
tab1:
.db$3f,$06,$5b,$4f,$66,$6d,$7d,$07,$7f,$6f
.org$0013
main:
ldir16,low(ramend)
outspl,r16
ldir16,high(ramend)
outsph,r16
ldir16,$ff
outddrd,r16
ldir16,$64;预设数值为13412
stsdata,r16
ldir16,$34
stsdata+1,r16
ldsr16,data
ldsr17,data+1
rcallb16td5
;显示数字
start:
movr18,r24
rcallcqb
rcalldelay
movr18,r23
rcallcqb
rcalldelay
movr18,r22
rcallcqb
rcalldelay
movr18,r21
rcallcqb
rcalldelay
movr18,r20
rcallcqb
rcalldelay
here:
rjmphere
b16td5:
serr24;r24先送-1
loop1:
incr24;r24增1
subir16,low(10000)
sbcir17,high(10000);(r17:
r16)-10000
brccloop1;够减则返回loop1
subir16,low(-10000)
sbcir17,high(-10000);不够减,加10000恢复余数
serr23
loop2:
incr23
subir16,low(1000)
sbcir17,high(1000)
brccloop2
subir16,low(-1000)
sbcir17,high(-1000)
serr22
loop3:
incr22
subir16,low(100)
sbcir17,high(100)
brccloop3
subir16,low(-100)
sbcir17,high(-100)
serr21
loop4:
incr21
subir16,low(10)
brccloop4
subir16,low(-10)
serr20
loop5:
incr20
subir16,low
(1)
brccloop5
subir16,low(-1);其实个位可以直接送
ret
cqb:
ldizl,low(tab1*2)
ldizh,high(tab1*2)
addzl,r18
lpm
outportd,r0
ret
;延迟0.5S,书上虽然这么说,但是个人认为延迟1.5s了
delay1:
ldir16,100
ldir17,200
ldir25,200
decr25
brnedelay1+3
decr17
brnedelay1+2
decr16
brnedelay1+1
ret
;延迟1S
delay:
rcalldelay1
rcalldelay1
采用外部中断的方式对按键次数实现计数。
计数值由一个共阴数码管来显示,初值为0。
每按键一次,计数值增1……计到F后,再按键一次则回0。
画出电路图。
.include"m8def.inc"
.deftemp=r23;临时变量
.defcounter=r24;计数变量
;中断向量区配置,FLASH空间$000~$028
.org$000
rjmpRESET;复位处理
rjmpEXT_INT0;中断向量
.org$02A
RESET:
;上电初始化程序
ldir16,high(RAMEND)
outSPH,r16
ldir16,low(RAMEND)
outSPL,r16;设置堆栈指针为RAM的顶部
sertemp
outddrb,temp;设置portb为输出,段码输出
outportb,temp;设置portb输出全1
lditemp,0x0a
outmcucr,temp;INT0、INT1下降沿触发
lditemp,0xc0
outgicr,temp;允许INT0、INT1中断
outgifr,temp;清除INT0、INT1中断标志位
cbiddrd,2
clrcounter
sei;使能中断
MAIN:
clrr0
ldizl,low(led_7*2)
ldizh,high(led_7*2);Z寄存器取得7段码组的首指针
addzl,counter;加上要显示的数字
adczh,r0;加上低位进位
lpm;读对应七段码到R0中
outportb,r0;LED段码输出
rjmpMAIN;循环显示
EXT_INT0:
intemp,sreg
pushtemp;中断现场保护
inccounter;计数单元加1
cpicounter,0x10;与16比较
brneEXT_INT0_RET;小于16转中断返回
clrcounter;计数单元清0
EXT_