UL/FRI/UNI-RI/ARS2/Vaje/ARM
Iz E-študij, proste zakladnice študentskega znanja
winIDEA, asmprojekt, ki ga dobiš na laps.fri, kodo pišemo v start.s, razdelek /*main program*/, konstante navajamo pri /*constants*/, morebitni halt izvedemo kot "label: b label". ..
Oglej si GNU ARM referenco in uporabljaj ARMv4t referenco ter ARM dokumentacijo. Vse je tam. :)
Vsebina |
[uredi] 1. Naloga
Seštejte dve 64-bitni števili: spr1 = spr1 + spr2, kjer je spr1= 0x00000001F0000000 in spr2=0x0000000010000000. Pomagajte si z zastavico C. Opozorilo: ukazi spreminjajo stanje zastavic le, če to zahtevamo (s). Spremenljivki naj bosta v pomnilniku takoj za konstantami. Naslov spremenljivke dobimo v register s pomočjo psevdoukaza adr. Program naj okvirno zgleda tako:
adr r0, spr1
ldr r1, [r0]
ldr r2, [r0, #4]
...
...
_Lforever:
b _Lforever
...
.global _vars
_vars:
spr1: .word 0x00000001, 0xF0000000
spr2: ...
.end
Opozorilo: s pomočjo adr lahko v register naložimo naslov spremenljivke le, če je ta dovolj blizu trenutnemu pc. Če temu ni tako, uporabimo ldr r0, =spr1. ldr je v tem primeru psevdoukaz! Preverite, v kakšen ukaz se prevede!
/* main program */ adr r0,spr1 ldr r1,[r0] ldr r2,[r0,#4] ldr r3,[r0,#8] ldr r4,[r0,#12] adds r6,r2,r4 adc r5,r1,r3 l: b l /* constants */ spr1: .word 0x00000001,0xF0000000 spr2: .word 0x00000000,0x10000000
[uredi] 2. Naloga
Napišite program, ki ustreza naslednjemu programu v c-ju:
if(r0 != 5) { r1=r1+r0-r2; }
Uporabite cmp in pogojno izvajanje ukazov. Pomagajte si s prosojnicami.
/* main program */ adr r3,spr1 ldr r0,[r3] ldr r2,[r3,#4] cmp r0,#5 addne r1,r1,r0 subne r1,r1,r2 l: b l /* constants */ spr1: .word 4,5
[uredi] 3. Naloga
Napišite program, ki ustreza naslednjemu programu v c-ju:
if(r0 > r1){ r0 = r0 - r1; }else{ r1 = r1 - r0; }
Nato napišite program za iskanje največjega skupnega delitelja s pomočjo Evklidovega algoritma, ki je podan tako:
while(r0 != r1) { if(r0 > r1) { r0 = r0 - r1; }else{ r1 = r1 - r0; } }
Algoritem preizkusite z majhnimi števili (da je konstanto mogoče naložiti v register s pomočjo ukaza mov).
/* main program */ mov r0,#39 mov r1,#35 cmp r0,r1 subhi r0,r0,r1 /* SKOK SEM */ sublo r1,r1,r0 cmp r0,r1 subne pc,pc,#24 /* pc===r15; skočit mormo nazaj na cmp r0,r1 - torej mormo odštet 24 od pc-ja*/
EDIT: mogoče bi bilo boljše če dodamo pred cmp oznako WHILE, in PC povečamo tako: adr r15, WHILE
Alt. rešitev
_main: mov r0, #35 mov r1, #7 while: cmp r0,r1 beq _wait_for_ever subgt r0,r0,r1 sublt r1,r1,r0 b while _wait_for_ever: b _wait_for_ever
[uredi] 4. Naloga
Napišite program, ki kopira elemente iz ene tabele v drugo. V tabelah naj bo po 5 32-bitnih števil. Tabeli naj bosta takoj za konstantami (kot v nalogi 1).
- uporabite posredno naslavljanje brez odmika
- uporabite posredno naslavljanje z avtomatskim po-indeksiranjem
- uporabite posredno naslavljanje s pomaknjenim registrskim odmikom. Odmik določite s pomikanjem števca obhodov zanke!
- uporabite pet registrov in ldm / stm.
/* main program */
adr r1,tab1
adr r2,tab2
mov r5,#5
/* POSREDNO *
mov r0,r5
ldr r3,[r1] /*SKOK SEM*/
str r3,[r2]
add r1,r1,#4
add r2,r2,#4
subs r0,r0,#1
subne r15,r15,#28
/* */
/* POSREDNO POST-INCREMENT */
mov r0,r5
ldr r3,[r1],#4 /*SEM*/
str r3,[r2],#4
subs r0,r0,#1
subne r15,r15,#20
/* */
/* POSREDNO Z REGISTRSKIM ODMIKOM *
mov r0,#0
ldr r3,[r1,r0, lsl #2]
str r3,[r2,r0, lsl #2]
add r0,r0,#1
cmp r0,#5
subne r15,r15,#24
/* */
/* LOAD MULTIPLE - 5 REGISTROV */
ldmia r1,{r6-r10}
stmia r2,{r6-r10}
/* */
l: b l
/* constants */
tab1: .word 1,2,3,4,0x00000005
tab2: .word 0,0,0,0,0
[uredi] 5. Naloga
Napišite podprogram, ki vrne število negativnih števil v tabeli. Podprogram sprejme dva parametra: v r0 je naslov tabele (kazalec na tabelo), v r1 pa število elementov v tabeli. Po koncu podprograma naj bo rezultat v r0. Podprogram naj na začetku na sklad (r13, sklad je že inicializiran) shrani vse delovne registre in povratni naslov (r14). Pred vrnitvijo naj delovne registre obnovi. Vstopna in izstopna točka (vključno z vračanjem v glavni program) sta pri optimalnem programiranju dolgi le po en ukaz! Sklad naj bo podoben tistemu, ki smo ga uporabljali pri programiranju HIP-a. Na skladu so 32 bitne vrednosti, r13 naj kaže na prosto mesto, sklad naj se širi proti nižjim naslovom (Empty Descending).
/* main program */
adr r0,tab1
mov r1,#8
bl podprogram
b l
podprogram:
stmed sp,{r1-r3,r14} /* sp (stack pointer) === r13 */
mov r3,#0
ldr r2,[r0],#4
cmp r2,#0
addlt r3,r3,#1
subs r1,r1,#1
subne r15,r15,#24
add r0,r3,#0
ldmed r13,{r1-r3,r14}
bx r14
l: b l
/* constants */
tab1: .word 1,2,-3,4,0x00000005,0xF0000000,-17,2457
Alt. rešitev
_main:
ldr r0, =spr1
mov r1, #4
bl pp
_wait_for_ever:
b _wait_for_ever
pp:
stmed r13!,{r2-r12}
mov r2, #0
mov r3, #0
loop:
ldr r4,[r0,r3]
cmp r4,#0
addlt r2,r2, #1
add r3,r3, #4
subs r1,r1,#1
bne loop
mov r0,r2
ldmed r13!,{r2-r12}
mov pc,r14
/* end user code */
/* constants */
.global _vars
_vars:
spr1: .word 5, -3, -2, 3
[uredi] 6. Naloga
Psevdoukaza ldr in adr, ponovitev. Napišite program, ki ustreza programu v c-ju: a = b + c; a, b in c so 32-bitne spremenljivke, ki naj bodo v pomnilniku tik za programom. Najprej proučite naslednjo obliko psevdoukaza ldr: ldr r0, b. Napišite ustrezen program v zbirnem jeziku. V kakšen ukaz se prevede ldr r0, b? Kateri pogoj mora biti izpolnjen, da lahko uporabljamo tako obliko ldr? Namig: odmik. Nato program napišite še z uporabo psevdoukazov oblike ldr r0, =b. V kakšen ukaz se prevede ldr te oblike? Zakaj so lahko sedaj spremenljivke kjerkoli v naslovnem prostoru? Kdaj lahko ldr r0, =b nadomestimo za adr r0, b in zakaj je to smiselno?
/* main program */
ldr r0,b /* ldr r0,[pc,0020] */
ldr r1,=b /* ldr r0,[pc,0028] - nalozi se naslov*/
adr r2,b /* add r2,pc,#18 - naslov */
ldr r0,b
ldr r1,c
add r0,r0,r1
str r0,a
l: b l
/* constants */
a: .word 0
b: .word 1
c: .word 2
[uredi] 7. Naloga
Napišite program v zbirnem jeziku, ki ustreza naslednjemu programu za množenje dveh števil s pomočjo seštevanja v c-ju:
int multiplicand, multiplier; int product; if (multiplier > 255) product = -1; /* Error condition */ else if (multiplier < 0) product = -1; /* Error condition */ else { product = 0; while (multiplier > 0) { product = product + multiplicand; multiplier = multiplier - 1; } }
/* main program */ mov r1,#2 /* 2 */ mov r2,#3 /* 3 */ cmp r1,#0xFF movhi r0,#-1 bhi end cmp r1,#0 movlt r0,#-1 blt end mov r0,#0 addne r0,r0,r2 /* =6 */ subnes r1,r1,#1 subne r15,r15,#16 end: b end
[uredi] 8. Naloga
Napišite program v zbirnem jeziku, ki ustreza izboljšanemu programu za množenje dveh števil v c-ju:
void main(void){ int multiplicand, int multiplier; int product; if (multiplier > 255) product = -1; /* Error condition */ else if (multiplier < 0) product = -1; /* Error condition */ else { product = 0; if (multiplier >= 128) { product = product + (multiplicand << 7); multiplier = multiplier - 128; } if (multiplier >= 64) { product = product + (multiplicand << 6); multiplier = multiplier - 64; } if (multiplier >= 32) { product = product + (multiplicand << 5); multiplier = multiplier - 32; } if (multiplier >= 16) { product = product + (multiplicand << 4); multiplier = multiplier - 16; } if (multiplier >= 8) { product = product + (multiplicand << 3); multiplier = multiplier - 8; } if (multiplier >= 4) { product = product + (multiplicand << 2); multiplier = multiplier - 4; } if (multiplier >= 2) { product = product + (multiplicand << 1); multiplier = multiplier - 2; } if (multiplier >= 1) { product = product + multiplicand; multiplier = multiplier - 1; } } }
Predpostavite, da sta na začetku množenec in množitelj v registrih r0 in r1. Na koncu naj bo produkt v r0. Za množenje s potencami števila 2 uporabite hitri pomikalnik (drugi operand pri add). Z uporabo pogojnega izvajanja ukazov dobite elegantno rešitev.
glej 9.
[uredi] 9. Naloga
Program iz prejšnje naloge dodajte v projekt v c-ju (pomagajte si s projektom cpr1). Program preuredite v podprogram z imenom produkt8:
.global produkt8 .global _produkt8 produkt8: produkt8: ... mov pc, r14
Podprogram sme spremeniti vrednost registrom r0 – r3. Če uporablja še druge registre, jih shranite na sklad in na koncu obnovite. V datoteki main.c dodajte vrstico:
extern int produkt8(int a, int b);
sedaj lahko iz funkcije main() pokličete funkcijo produkt8.
datoteka: produkt8.s:
.global produkt8 .global _produkt8 produkt8: produkt8: str r4,[r13] cmp r0,#0xFF movhi r2,#-1 bhi end cmp r0,#0 movlt r2,#-1 blt end mov r2,#0 mov r3,#7 mov r4,#1 cmp r0,r4, lsl r3 addhs r2,r2,r1, lsl r3 subhs r0,r0,r4, lsl r3 sub r3,r3,#1 cmp r3,#-1 subne r15,r15,#28 mov r0,r2 ldr r4,[r13] end: bx r14 /* RETURN */
datoteka: main.c
#include "Main.h" extern int produkt8(int,int); /* lepo dela tudi brez tega */ int main(void){ int a = produkt8(111,3); while (1); }
[uredi] 10. Naloga
Vklopite oranžno LED diodo na FRI-SMS. Dioda je priključena na priključek PIO_PC1. Orientirajte ga izhodno. Nato izhod postavite v stanje 0. Pomagajte si s prosojnicami...
/* main program */ .equ PIOC_BASE, 0xFFFFF800 /* Zacetek registrov za PIOC */ .equ PIO_PER, 0x00 /* Odmiki... */ .equ PIO_OER, 0x10 .equ PIO_SODR, 0x30 /* .equ je neke vrste #define */ .equ PIO_CODR, 0x34 ldr r0, =PIOC_BASE mov r1, #1 << 1 str r1, [r0, #PIO_PER] /* Prikljucek C1 krmili PIO */ str r1, [r0, #PIO_OER] /* Omogoci izhod - dioda sveti */ /*str r1, [r0, #PIO_SODR] /* Na prikljucek C1 zapisi stanje 1 - dioda ne sveti */ str r1, [r0, #PIO_CODR] /* Na prikljucek C1 zapisi stanje 0 - dioda sveti */
[uredi] 11. Naloga
S pomocjo programske zanke realizirajte podprogram za zakasnitev cca. 0,5 sekunde. Izracunajte koliko ciklov traja en obhod zanke (upoštevajte kontrolne nevarnosti pri skoku). Frekvenca ure je 192 MHz. Nato v zanki klicite podprogram in vklapljajte/izklapljajte oranžno LED diodo (0,5 s vklopljena, 0,5 s izklopljena, itd…).
/* main program */
ldr r0, =PIOC_BASE
mov r1, #0x02
str r1, [r0, #PIO_PER] /* Priključek C15 krmili PIO */
str r1, [r0, #PIO_OER] /* Omogoci izhod */
mov r6,#500 /* naloži število ponavljanj prižiganja/ugašanja - seveda lahko naredite tudi neskončno zanko */
it:
str r1, [r0, #PIO_SODR] /* ugasni LED */
bl zanka /* skoči v podprogram, ki se izvaja 0.5 sekunde */
str r1, [r0, #PIO_CODR] /* prižgi LED */
bl zanka /* skoči v podprogram, ki se (spet) izvaja 0.5 sekunde */
subs r6,r6,#1 /* zmanjšaj število iteracij za 1 */
bne it /* če smo iterirali
b _Lforever /* skok na konec programa */
zanka: /* podprogram zanka */
ldr r5,=24000000 /* 192MHz=192*10^6 / 4 * 0.5 = 24*10^6 */
yanka: /* v zanki odštevamo do 0 */
subs r5,r5,#1
bne yanka
mov r15,r14 /* skočimo iz podprograma */
Ali je ta zanka in število ponovitev prav ?
Ziher ni. Frekvenca namreč ni 96 MHz ampak 196 MHz.
--> Če pomnite, se je workspace med potekom vaj spreminjal, zraven pa tudi sama frekvenca...
[uredi] 12. Naloga
Predelajte podprogram za zakasnitev iz prejšnje naloge. Uporabite časovnik TC0. Izberite ustrezno uro, da bo mogoče doseči podobno dolgo zakasnitev - časovnik naj šteje do vrednosti 0xffff. V zanki berite statusni register časovnika in preverjajte ustrezno zastavico. Zanka naj teče dokler se zastavica ne postavi na 1. Frekvenca periferne ure MCK je 48 MHz.
Pred kopiranjem si obvezno poberite najnovejši workspace iz arsove strani: [1] Potem ni več potrebno definirati /*define*/ polja, ker je že definirano vse potrebno na začetku datoteke.
/* define */
.equ TC0_BASE, 0xFFFA0000
.equ PIOC_BASE, 0xFFFFF800
.equ TC_CMR, 0x04
.equ TC_CCR, 0x00
.equ PIO_PER, 0x00 /* Odmiki... */
.equ PIO_OER, 0x10
.equ PIO_SODR, 0x30
.equ PIO_CODR, 0x34
.equ TC_SR, 0x20
/* main program */
/* user code here */
_main:
ldr r2, =PMC_PCER /* vklopi uro za ID17 -> 17. bit*/
mov r3, #0x20000 /* 2^17 -> hex -> 0x20000 */
str r3, [r2]
ldr r2,=TC0_BASE /* naslov za posredno naslavljanje*/
mov r3, #4 /* izberi uro */
str r3, [r2,#TC_CMR]
mov r3, #1 /* omogoči uro...*/
str r3, [r2,#TC_CCR]
mov r3, #4 /*... & omogoči števec */
str r3, [r2,#TC_CCR]
ldr r0, =PIOC_BASE /* vklopi LED diodo */
mov r1, #0x02
str r1, [r0, #PIO_PER] /* Priključek C15 krmili PIO */
str r1, [r0, #PIO_OER] /* Omogoci izhod */
streq r1, [r0, #PIO_CODR] /* ugasni diodo */
add r5,r5,#0 /* v r5 spremenljivka za preverjanje sodosti/lihosti */
zanka:
ldr r4,[r2,#TC_SR] /* preberi statusni register*/
tst r4,#1 /* poglej, če je zastavica COVFS enaka #1 */
beq zanka /* če zastavica NI 1 (poglej TST ukaz!), ponovi zanko */
tst r5,#1 /* preveri samo prvi bit -> 2^n + 1 == liho število */
streq r1, [r0, #PIO_SODR] /* ugasni diodo, če je sod */
strne r1, [r0, #PIO_CODR] /* prižgi diodo, če lih */
add r5,r5,#1 /* povečaj spremenljivko za 1 */
b zanka
a ta je kul??
jaz tule vidim več napak:
ldr r2, =PMC_PCER a ni PMC_PCSR? -> v dokumentaciji piše [Peripheral Clock Enable Register PMC _PCER]. Tako da pomoje je PCER ok.
Iz PMC_PCSR lahko le bereš, ker je to statusni register tako da ga ne moreš spreminjati!
potem še define
.equ PMC_PCER, 0x0018
popraviti je treba vrstici:
mov r3, #1 /* omogoči uro...*/ str r3, [r2,#TC_CCR] mov r3, #4 /*... & omogoči števec */ str r3, [r2,#TC_CCR]
ker v tem primeru ti drugi zapis v TC_CCR prvega pobriše. Zato narediš:
mov r3, #5 /* 5 (101) = 4(100) (omogoci uro) + 1(001) omogoci stevec */ str r3, [r2,#TC_CCR]
Že res, ampak tako boš ostale nastavitve povozil Bulic pravi, da je pravilno prebrat & ANDat in potem storat nazaj. Ti registri nimajo onih parov, da bi lahko bit po bit popravljal.
Zakaj pa stvar deluje tudi če zgornjih komentarjev ne upoštevam ?
[uredi] 13. Naloga
Rešitev prejšnje naloge predelajte tako, da bo frekvenca utripanja LED natančno 1 Hz. Uporabite števec TC0, ki naj šteje do vrednosti, določene z RC. Pol sekunde naj bo LED prižgana, pol sekunde pa ugasnjena. Glejte prosojnice in tovarniško listino! Navodilo: LED prižigajte in ugašajte v podprogramu. Ta naj uporablja spremenljivko, kateri vsakič poveča vrednost za 1. Če je vrednost spremenljivke soda, naj LED ugasne, če je liha, naj LED prižge. Podprogram kličite v zanki vsake pol sekunde…
/* definiramo naslove registrov, ki jih bomo potrbovali */ .equ TC0_BASE, 0xFFFA0000 .equ PIOC_BASE, 0xFFFFF800 .equ PMC_PCER, 0x10 /* Peripheral Clock Enable Register */ .equ TC_CMR, 0x04 .equ TC_CCR, 0x00 .equ PIO_PER, 0x00 .equ PIO_OER, 0x10 .equ PIO_SODR, 0x30 .equ PIO_CODR, 0x34 .equ TC_SR, 0x20 .equ TC_RC, 0x1C /* TC0 Register C */
ldr r1, =PMC_BASE /* ID_TC0 = 17; bit 17 potrebno postavit 1 */ mov r2, #0x20000 str r2, [r1,#PMC_PCER] ldr r1, =TC0_BASE /* nastavimo r1 na TC0_ BASE */ mov r3, #4 /* izberemo uro TC0 */ str r3, [r1, #TC_CMR] ldr r6, [r1, #TC_CMR] orr r3, r6, #6 << 13 /* 15bit 1, 14 bit 1 */ str r3, [r1, #TC_CMR] adr r9, zakasnitev ldr r3, [r9] str r3, [r1, #TC_RC] mov r3, #1 /* omogocimo uro */ str r3, [r1, #TC_CCR] mov r3, #4 /* sprozi stevec */ str r3, [r1, #TC_CCR] ldr r0, =PIOC_BASE mov r6, #2 str r6, [r0, #PIO_PER] /* Priključek C15 krmili PIO */ str r6, [r0, #PIO_OER] /* Omogoci izhod, vklopi LED */ mov r5, #0 /* stevec za vklapljanje in vgasanje ledice */
LOOP: ldr r4, [r1, #TC_SR] tst r4, #0x10 /* and bit0(r4) in 0..10000 */ beq LOOP /* ce bit0(r4) != 1 ponovimo */ tst r5, #1 /* preverjamo samo prvi bit, if bit==0 je sodo */ streq r6, [r0, #PIO_SODR] strne r6, [r0, #PIO_CODR] add r5, r5, #1 b LOOP _Lforever: b _Lforever
/* constants */ zakasnitev: .word 32768
[uredi] 14. Naloga
Namesto čakanja na postavitev CPCS v zanki, konfiguirajte TC0 tako, da bo CPCS predstavljala prekinitveno zahtevo. Nato ustrezno konfigurirajte prekinitveni krmilnik (AIC). V grobem je potrebno narediti naslednje: V AIC_SMR17 vpišite primerno prioriteto, npr. 4. (0 – najnižja, 7 – najvišja); PERIPHERAL_ID TC0 je 17. V AIC_SVR17 vpišite naslov vašega PSP. Z vpisom 1 v bit 17 AIC_ECR omogočite prekinitve za TC0. Ne pozabite pobrisati bita I v CPSR. Vse to morate narediti pred preklopom v uporabniški režim. Pred izhodom iz PSP morate pisati (karkoli) v AIC_EOICR. PSP naj se kliče vsake pol sekunde, iz PSP pokličite podprogram za prižiganje/ugašanje LED iz prejšnje naloge. Glejte prosojnice in tovarniško listino!
/* nekam na vrh */ .equ AIC_BASE, 0xFFFFF000 /* Zacetek AIC */ .equ AIC_SMR17, 0x44 /* odmiki */ .equ AIC_SVR17, 0xC4 .equ AIC_IVR, 0x100 .equ AIC_IECR, 0x120 .equ AIC_EOICR, 0x130 ldr r0, =AIC_BASE mov r1,#4 str r1,[r0, #AIC_SMR17] ldr r1, =PrekinitvenoServisniProgram str r1,[r0,#AIC_SVR17] mov r1, #1 << 17 str r1,[r0,#AIC_IECR]
/* user code here */ /* naloži vse naslove, odmike, nato: */ ldr r0, =PIOC_BASE mov r1, #2 str r1, [r0, #PIO_PER] /* Prikljucek C1 krmili PIO */ str r1, [r0, #PIO_OER] /* Omogoci izhod */ ldr r0, =PMC_PCER mov r1, #1 << 17 str r1, [r0] /* vklopili power management za uro - timer 0 */ ldr r2, =TC0_BASE ldr r1,=16500 str r1, [r2, #TC0_RC] ldr r1, =0xC004 /* wave, wavesel(triger on CR), timer 4 = SLCK */ str r1, [r2, #TC0_CMR] mov r1, #5 str r1, [r2, #TC0_CCR] mov r1, #16 str r1, [r2, #TC0_IER] zanka: bal zanka
PrekinitvenoServisniProgram:
sub lr,lr,#4
stmfd sp!,{r0,r1,lr}
mrs r0, cpsr
and r0,r0,#127 /* Omogoci prekinitve */
msr cpsr_c,r0
ldr r0,=PIOC_BASE
ldr r1,[r0,#PIO_ODSR]
tst r1,#2
mov r1,#2
strne r1, [r0, #PIO_CODR]
streq r1, [r0, #PIO_SODR]
ldr r0,=TC0_BASE
ldr r1,[r0, #TC0_SR]
mrs r0, cpsr
orr r0,r0,#128 /* Onemogoci prekinitve */
msr cpsr_c,r0
ldr r1,=0xFFFFF130 /* AIC_BASE + AIC_EOICR */
str r1,[r1]
ldmfd sp!,{r0,r1}
ldmfd sp!,{pc}^ /* ^ kopira spsr v cpsr */
[uredi] 15. Naloga
V tovarniški listini poiščite opis enote 'debug unit'. Uporabite serijski vmesnik te enote – ta je preko serijskega kabla povezan s serijskim vmesnikom na računalniku PC. Z vpisom primernih vrednosti v ustrezne registre naredite naslednje:
- Serijski vmesnik enote debug nastavite na hitrost 115200 baudov. Upoštevajte, da frekvenca ure MCK znaša 48 MHz (točno 115200 baudov ni mogoče nastaviti, odstopanje je dovolj majhno). Namig: izbrati je potrebno ustrezen izvor ure in primerno nastaviti delilnik..
- Omogočite oddajnik in sprejemnik serijskega vmesnika (TX, RX).
- Določite prenos brez paritete.
Za preizkus serijskega vmesnika napišite dva podprograma.
- V prvem počakajte, da sprejemnik sprejme znak, ki ga preko terminala pošljete z računalnika PC. Uporabite kar terminal, ki je vgrajen v okolje WinIDEA. Nastavite 115200 baudov, 8 bitov, 1 stop bit, brez paritete.
- V drugem podprogramu najprej počakajte, da je oddajnik pripravljen, nato pošljite znak v smeri ploščica -> računalnik PC.
Podprograma preizkusite tako, da računalniku PC vračate znake, ki jih od njega sprejmete (echo) – podprograma izmenično kličete.
/* main program */ _main: ldr r0,=DBGU_BASE mov r1,#80 /* TX enable && RX enable */ str r1,[r0,#DBGU_CR] mov r1,#2048 /* No Parity */ str r1,[r0,#DBGU_MR] mov r1,#26 /* 48MHz / 16 / 26 == 115384 */ str r1,[r0,#DBGU_BRGR] loop: bl BERI bl PISI b loop BERI: ldr r1,[r0,#DBGU_SR] ands r1,r1,#1 beq BERI ldrne r1,[r0,#DBGU_RHR] movne r2,r1 bxne lr PISI: ldr r1,[r0,#DBGU_SR] ands r1,r1,#2 beq PISI strne r2,[r0,#DBGU_THR] bxne lr bal:bal bal
[uredi] 16. Naloga
Razhroščevalno enoto ('debug unit') nastavite tako, da bo znake sprejemala s pomočjo prekinitveno servisnega programa in oddajala s pomočjo DMA prenosov. Ostale nastavitve ostanejo enake tistim iz naloge 15. Pri tem napišite naslednje podprograme:
- Prekinitveno servisni program (PSP) mora prebrani znak zapisati v polje NIZ in če je prebrani znak enak znaku za tipko Enter (ASCII koda 13), ali če je že sprejeto 80 znakov, naj nastavi ustrezno spremenljivko na vrednost 1. Za vsak prejeti znak mora tudi klicati podprogram PARNOST (glej naprej specifikacije podprograma PARNOST). Pri nastavitvi prekinitvenega krmilnika v glavnem programu upoštevajte, da ima razhroščevalna enota kot prekinitveni vir ID = 1.
- Podprogram PARNOST prejme v registru R0 8-bitno vrednost (ASCII kodo znaka). Podprogram mora izračunati liho parnost znaka – skupaj z bitom parnosti mora biti število enic liho – in z bitom parnosti krmiliti LED diodo (v glavnem programu je potrebno ustrezno nastaviti PIO enoto – naloga 10.).
- Podprogram ZAMENJAJ, ki zamenja velike in male črke v nizu znakov. Naslov niza, ki je zaključen z 0 dobi podprogram v registru R0.
- Podprogram SPROZI_DMA, ki ga kličemo iz glavnega programa takrat, ko smo preko razhroščevalne enote sprejeli znak Enter oziroma 80 drugih znakov (PSP nastavi ustrezno spremenljivko). Podprogram mora preveriti, ali se je prejšnji DMA prenos že zaključil in če se je, mora podprogram DMA krmilnik razhroščevalne enote nastaviti tako, da se bosta prenesla dva niza: najprej običajen prejeti niz znakov (zapiše ga PSP) in nato še prejeti niz, ki ima zamenjane velike in male črke (podprogram ZAMENJAJ).
Ploščico priključite na PC še preko RS232 kabla in v WinIDEA-i odprite okno Terminal (lahko zaženete tudi Windows program Hyperterminal). Ob pravilnem delovanju glavnega programa in vseh podprogramov boste v terminalskem oknu za vsak vtipkan niz znakov dobili izpisana dva niza: izvorni in niz z obrnjenimi velikimi in malimi črkami.
.global _start _start: ldr r0, =AIC_BASE mov r1,#4 /* Priority */ str r1,[r0, #AIC_SMR1] ldr r1, =RX_PSP /* RX Ready Interrupt Handler */ str r1,[r0,#AIC_SVR1] mov r1, #2 /* Enable SYS Interrupts */ str r1,[r0,#AIC_IECR]
/* main program */ _main: ldr r0, =PIOC_BASE /* Enable LED */ mov r1, #2 str r1, [r0, #PIO_PER] str r1, [r0, #PIO_OER] ldr r0,=DBGU_BASE mov r1,#2048 /* No Parity */ str r1,[r0,#DBGU_MR] mov r1,#26 /* 48MHz / 16 / 26 == 115384 */ str r1,[r0,#DBGU_BRGR] mov r1,#0x50 /* TX enable && RX enable */ str r1,[r0,#DBGU_CR] mov r1,#1 /* Enable RX ready interrupt */ str r1,[r0,#DBGU_IER] ldr r3,=CHECK80 ldr r4,=STR_PTR ldr r5,=STRING ldr r6,=STR_REP zanka: ldr r0,[r3] cmp r0,#0 beq zanka mov r0,r5 bl REPLACE ldr r0,[r3] bl START_DMA mov r0,#0 str r0,[r4] str r0,[r3] b zanka
RX_PSP:
sub lr,lr,#4
stmfd sp!,{r0-r2,lr}
ldr r0,=DBGU_BASE /* Received Char */
ldr r0,[r0,#DBGU_RHR]
bl PARITY
ldr r2,=STR_PTR
ldr r1,[r2],#4 /* Position in STRING */
cmp r1,#81
bhs end_psp
cmp r0,#10 /* is character <Enter> key? (LF) */
streqb r0,[r2,r1]
addeq r1,r1,#1
subeq r2,r2,#4
streq r1,[r2,#-4] /* Signal full array with CHECK80 != 0 */
beq end_psp
strb r0,[r2, r1] /* Store character in correct position */
add r1,r1,#1
cmp r1,#80 /* is STRING full (length == 80) */
moveq r0,#10 /* LF */
streqb r0,[r2,r1]
addeq r1,r1,#1
str r1,[r2,#-4]! /* Store new STR_PTR */
streq r1,[r2,#-4] /* Signal full array with CHECK80 != 0 */
end_psp:
ldr r1,=0xFFFFF130 /* End Interrupt */
str r1,[r1]
ldmfd sp!,{r0-r2,pc}^
REPLACE:
stmfd sp!,{r0-r2,lr}
ldr r1,=STR_REP
loop:
ldrb r2,[r0],#1
cmp r2,#65
blo endloop
cmp r2,#90
addls r2,r2,#32
bls endloop
cmp r2,#97
blo endloop
cmp r2,#122
subls r2,r2,#32
endloop:
strb r2,[r1],#1
cmp r2,#10 /* Compare with LF instead of 0x0 */
bne loop
ldmfd sp!,{r0-r2,pc}
START_DMA: /* gets length in r0 */
stmfd sp!,{r1,r2,lr}
ldr r1,=DBGU_BASE /* DBGU_Status_Register */
l:ldr r2,[r1,#DBGU_SR]
tst r2,#0x10
beq l
mov r2,#0x200 /* Disable DMA Transmition */
str r2,[r1,#DBGU_PTCR]
str r0,[r1,#DBGU_TCR]
str r0,[r1,#DBGU_TNCR]
ldr r2,=STR_REP
add r2,r2,#0x200000
str r2,[r1,#DBGU_TNPR]
ldr r2,=STRING
add r2,r2,#0x200000
str r2,[r1,#DBGU_TPR]
mov r2,#0x100 /* Enable DMA Transmition */
str r2,[r1,#DBGU_PTCR]
ldmfd sp!,{r1,r2,pc}
PARITY:
stmfd sp!,{r1,r2,lr}
mov r1,#0
tst r0,#1
eorne r1,r1,#1 /* XORED BITS 0 TO ... */
tst r0,#2
eorne r1,r1,#1
tst r0,#4
eorne r1,r1,#1
tst r0,#8
eorne r1,r1,#1
tst r0,#16 /* . . . */
eorne r1,r1,#1
tst r0,#32
eorne r1,r1,#1
tst r0,#64
eorne r1,r1,#1
tst r0,#128
eorne r1,r1,#1 /* ... 7 */
eors r1,r1,#1 /* Final Negation */
ldr r2,=PIOC_BASE
mov r1,#2
streq r1, [r2, #PIO_SODR]
strne r1, [r2, #PIO_CODR]
ldmfd sp!,{r1,r2,pc}
/* constants */ CHECK80: .word 0 STR_PTR: .word 0 STRING: .space 82 STR_REP: .space 82
Prirejeno na lepše.