F3 Föreläsning i Mikrodatorteknink 2006-08-29 Kärnan i microcontrollern består av ett antal register och en ALU. Till detta kommer också ett antal portar. Det finns 64 st portar. Några är anslutna mot yttervärlden andra är interna. Repetition General Purpose registers R0 R25 X ( R27 : R26 ) Y ( R29 : R28 ) Z ( R31 : R30 ) ALU I T H S V N Z C SREG Alla register och alla portar är på en byte Antag att vi behöver skriva in talet 25 till register R18 Maskinkoden ser ut så här: 1110 KKKK dddd KKKK Instruktionen är 16 lång dvs. 2 byte. d anger vilket register som man vill använda. De som kan användas just här är R16 R31 K får vara ett värde 0 <= K <= 255 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 1/9
Register R18 anges med 18-16 = 2, binärt 0010 Talet 25 är binärt 1 1001 Koden blir alltså 1110 0001 0010 1001 Eller hexadecimalt E129 Assembler Assembler är att program som gör en översättning till maskinkkod. Istället för att plocka fram alla ettor och nollor så skriver man : LDI R18,25 LDI är en Mnemonic, och just denna kan utläsas Load Immediate Generellt skrivs koden LDI Rd, K Rd är destinationsregister K en konstant. Rr är källregister. Assemblerprogrammet är specifikt för en processor. Alla instruktioner är beroende på vad det finns för register, och hur dessa är organiserade. Resultatet av assemblern är en hex-fil. Denna är inte heller ren maskinkod. För vår microcontroller är det en Intel-hex-fil. Varje rad i denna fil består av: Antal byte, adress, typ av information, MASKINKOD, checksumma. Denna hex-fil används sedan av den programmerare som läser in programmet till FLASH-minnet. Atmel använder Intel- hex eller Extended Intel-hex Motorola använder sin egen S-record. Assemblern gör en översättning av förkortningar till maskinkod. Assemblern hjälper också till med att räkna ut olika adresser. Information till själva assemblerprogrammet kallas direktiv. En del assemblerdirektiv..include.def.equ.cseg.org Exempelvis m16def.inc Tar med information som finns i filen Exempel.DEF temp = R16 sätter ett nytt namn på R16.EQU DAG = 29 Sätter namn på en konstant här börjar koden.org 100 Koden skrivs in på den hexadecimala adressen 0x64 Det finns ytterligare assemblerdirektiv. Direktiven finns inte med i den slutliga maskinkoden. 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 2/9
Skriv ett program som läser in värdet på PORTB och skickar samma värde till PORTD Vad är en PORT?? Det finns 4 st portar som man kan ansluta kontakter till. De kallas PORTA, PORTB, PORTC och PORTD. Det är kontakter med 8 stift. Begreppet port är större än så. Alla register med adresser 0x00 till 0x3F kallas portar. Det finns alltså 64 portar. Dessa är beskrivna i Register Summary. Ett enkelt program Flödesschema Start, initieringar Läs in på PORTB Läs ut tíll PORTD Se instruction set. Funktionen OUT Operation I/O (A) Rr Syntax OUT A,Rr 0 r 31 0 A 63 PC PC + 1 Om man I initieringen tar med filen m16def.inc så kan man använda det symboliska namnet PORTD Vi behöver också ett register.def temp = R16 Alltså OUT PORTD, temp 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 3/9
Funktionen IN Operation Rd I/O (A) Syntax IN Rd, A 0 d 31 0 A 63 PC PC + 1 När vi läser in från en port använder vi inte portb utan PINB. Alltså IN temp, PINB ( Det är ett vanligt fel att skriva PORTB istället för PINB ) Nu har vi bara initieringarna kvar: Startrutan: ; Skriv kommentar, vad programmet gör ; Vem som gjort programmet och datum ;.INCLUDE m16def.inc.def temp = R16.CSEG :ORG 0 rjmp START START: ldi temp,low(ramend) out SPL, temp ldi, temp,high(ramend) out SPH, temp ;; för att få rätt riktning på portarna clr temp out DDRB, temp ser temp out DDRD, temp Hela programmet ; Skriv kommentar, vad programmet gör ; Vem som gjort programmet och datum ;.INCLUDE m16def.inc.def temp = R16.CSEG :ORG 0 rjmp START START: ldi temp,low(ramend) out SPL, temp ldi, temp,high(ramend) out SPH, temp ;; för att få rätt riktning på portarna 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 4/9
clr out ser out in out temp DDRB, temp temp DDRD, temp temp, PINB PORTD, temp Alla instruktioner tar en klockcykel, utom rjmp som tar 2 klockcykler. Efter 12 klockcykler havererar troligen programmet. Varför?? För varje instruktion som hämtas så kommer programräknaren att peka på nästa instruktion. När sista instruktionen har hämtats så kommer programräknaren att peka på det som följer i programminnet. Processorn kommer att försöka att tolka den informationen som en instruktion. Detta kan bli vad som helst. Programmet måste avslutas. Ett sätt är stop: rjmp stop Ovanstående program fungerar inte speciellt bra. Inläsning från portb kommer bara att ske under en klockcykel, och det är en mycket kort tid. För att få ett fungerande program skapar man en slinga. Koden på föregående sida Samt out DDRD, temp hit: in temp, PINB out PORTD, temp rjmp hit rjmp är en relativ hoppinstruktion. Programräknaren ställs om till den adress som finns vid label hit: Assemblerprogrammet räknar fram den relativa adressen. De tre sista instruktionerna utförs evigt. Rinnande ljus En port består av 8 bitar. På labkortet finns en ramp med 8 lysdioder. En lysdiod skall tändas en kort tid, släckas och sedan skall dioden till vänster tändas osv. osv. 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 5/9
Börja med ett flödesschema Hit: Start Läs in en etta till ett register Skifta vänster Inledningen på föregående program ldi till ett register Hit: lsl registret Läs ut out portb, registret rjmp Hit Hit Använd exempelvis register R16, det som tidigare har fått namnet temp Programmet blir nu ; rinnande ljus.include m16def.inc.def temp = R16 ; det som kallas registret ovan.cseg :ORG 0 rjmp START START: ldi temp,low(ramend) out SPL, temp ldi, temp,high(ramend) out SPH, temp ser temp ;;skriver in 0xFF i temp out DDRB, temp ; portb blir utport Hit: ldi temp, 1 lsl temp ; ( extra kod ) out portb, temp rjmp hit 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 6/9
Hur fungerar programmet? Inte som man tänkt sig. Troligtvis hinner man inte se något. Instruktionen lsl fungerar på följande sätt: Instruktionen skiftar innehållet i ett register ett steg åt vänster. Instruktionen fungerar på samtliga 32 register. För varje gång skiftas en nolla in i den minst signifikanta positionen. Efter 8 varv i slingan är innehållet i registret noll, och det finns inget att läsa ut. Man kan lösa problemet med att läsa in talet på nytt när innehållet i registret blir noll. Det finns också ett annat sätt. Använd instruktionen rol C är en flagga ( en bit ) i statusregistret. När vår etta kommer till position b7 så kommer den att skiftas in i carry-biten nästa gång. Då instruktionen rol exekveras nästa gång skiftas ettan in i registret igen. I det här exemplet är det lämpligt att använda instruktionen rol temp. Om man testar programmet i AVR Studio ser man att programmet fungerar. Då man provkör programmet ser man bara att samtliga dioder lyser. Förklaringen är att slingan går för fort, vi kommer inte att uppfatta blinkningarna. 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 7/9
Fördröjning Vi behöver en fördröjning. Denna kan stoppas in i programmet ovan. Det finns en plats som är markerad ( extra kod ) Back: 0xFF till R17 Minska R17 med ett Back Nej Reg R17 =0? Ja 0xFF till R17 koden blir ldi R17, 0xFF minska med ett koden blir dec R17 Då R17 blir noll kommer Z-flaggan att sättas till ett. Vi behöver ett villkorligt hopp som reagerar på Z-flaggan i statusregistret. I Instruction Set Summary hittar man två möjliga instruktioner. BREQ Branch if Equal if Z = 1 så sker hopp BRNE Branch if Not Equal if Z = 0 så sker hopp Här är det lämpligt att använda BRNE. Så länge som R17 inte är noll skall vi hoppa tillbaka. Back är en label som anger vart vi skall hoppa. Extra kod blir nu: ldi R17,0xF Back: dec R17 brne Back Då hopp sker tar detta två klockcykler, annars en klockcykel. Totalt kommer den här koden att ta 766 klockcykler. 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 8/9
Fördröjningen är faktiskt inte tillräckligt lång. För längre fördröjning använder man två register. Yttre: 0xFF till R18 Back: 0xFF till R17 Minska R17 med ett Back Nej Reg R17 =0? Ja Minska R18 med ett Yttre Nej Reg R18 =0? Koden blir för den sista biten: ldi R18, 0xFF Yttre: ldi R17, 0xFF Back: dec R17 brne back dec R18 brne Yttre Ja Nu är vi framme vid en fördröjning på i princip 766 * 766 klockcykler AT mega 16 kan köras med klockfrekvens upp till 8 MHz. Vid leverans är den interna klockan ställd på 1 MHz. Den här slingan ger alltså en fördröjning på c:a 0,5 sekunder. 2006-08-29 H:\CT3760\Peroid1_2006\F3_2006.doc 9/9