AVR Assembler Makros: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
K (Syntaxhighlight repariert (avrasm→asm), Kategorisierung ans Ende verschoben, Kleinigkeiten)
 
(6 dazwischenliegende Versionen von 4 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
[[Category:AVR]]
Hier entsteht eine Sammlung von verschiedenen nützlichen [[Makro]]s für den [[AVR]]-[[Assembler]].


Hier entsteht eine Sammlung von verschiedenen nützlichen [[Makro|Makros]] für den [[AVR]] [[Assembler]].
=== 16-Bit-Konstante in Z-Pointer laden ===


=== 16 Bit Konstante in Z-Pointer laden ===
<syntaxhighlight lang="asm">
 
<avrasm>
.MACRO SetZPtr ;(Adresse)
.MACRO SetZPtr ;(Adresse)
         ldi    ZL, LOW(@0)
         ldi    ZL, LOW(@0)
         ldi    ZH, HIGH(@0)
         ldi    ZH, HIGH(@0)
.ENDMACRO
.ENDMACRO
</avrasm>
</syntaxhighlight>


Natürlich auch möglich mit X- und Y-Pointer.
Natürlich auch möglich mit X- und Y-Pointer.


==Speicher==
==Speicher==
=== 2 Register ohne Zwischenspeicher vertauschen ===
=== 2 Register ohne Zwischenspeicher vertauschen ===
<avrasm>
<syntaxhighlight lang="asm">
.MACRO SWAP ;(a, b)
.MACRO SWAP ;(a, b)
         eor    @0, @1
         eor    @0, @1
Zeile 23: Zeile 20:
         eor    @0, @1
         eor    @0, @1
.ENDMACRO
.ENDMACRO
</avrasm>
</syntaxhighlight>


==Arithmetik==
==Arithmetik==
Zeile 29: Zeile 26:
=== Konstante addieren ===
=== Konstante addieren ===


<avrasm>
<syntaxhighlight lang="asm">
.MACRO ADDI ;(a, k)
.MACRO ADDI ;(a, k)
         subi    @0, -(@1)
         subi    @0, -(@1)
.ENDMACRO
.ENDMACRO
</avrasm>
</syntaxhighlight>


=== Konstante addieren (16 Bit) ===
=== Konstante addieren (16 Bit) ===


<avrasm>
<syntaxhighlight lang="asm">
.MACRO ADDIW ;(RdL:RdH, k)
.MACRO ADDIW ;(RdL:RdH, k)
         subi    @0L, LOW(-@1)
         subi    @0L, LOW(-@1)
         sbci    @0H, HIGH(-@1)
         sbci    @0H, HIGH(-@1)
.ENDMACRO
.ENDMACRO
</avrasm>
</syntaxhighlight>
oder (sinnlos)
oder (sinnlos)
<avrasm>
<syntaxhighlight lang="asm">
.MACRO ADDIW ;(Rd, k)
.MACRO ADDIW ;(Rd, k)
         sbiw    @0, (-@1)
         sbiw    @0, (-@1)
.ENDMACRO
.ENDMACRO
</avrasm>
</syntaxhighlight>
DAS geht auch ohne Makro
DAS geht auch ohne Makro
<avrasm>
<syntaxhighlight lang="asm">
         adiw a, b
         adiw a, b
</avrasm>
</syntaxhighlight>
SBIW und ADIW sind aber beide auf die Register(paare) R24, R26, R28, R30 beschränkt UND nehmen nur Zahlen <64 an.
SBIW und ADIW sind aber beide auf die Register(paare) R24, R26, R28, R30 beschränkt UND nehmen nur Zahlen &lt;64 an.


==I/O==
==I/O==


Bei grösseren und neueren AVRs sind etliche I/O-Register nicht mit IN/OUT-Befehlen ansprechbar. LDS/STS erreicht zwar alle, ist aber bei kleineren oder älteren ineffizient.
Bei größeren und neueren AVRs sind etliche I/O-Register nicht mit IN/OUT-Befehlen ansprechbar. LDS/STS erreicht zwar alle, ist aber bei kleineren oder älteren ineffizient.


=== Port lesen ===
=== Port lesen ===


<avrasm>
<syntaxhighlight lang="asm">
.macro input
.macro input
   .if @1 < 0x40
   .if @1 < 0x40
Zeile 69: Zeile 66:
   .endif
   .endif
.endm
.endm
</avrasm>
</syntaxhighlight>


=== Port schreiben ===
=== Port schreiben ===


<avrasm>
<syntaxhighlight lang="asm">
.macro output
.macro output
   .if @0 < 0x40
   .if @0 < 0x40
Zeile 81: Zeile 78:
   .endif
   .endif
.endm
.endm
</avrasm>
</syntaxhighlight>


=== Portbit abfragen ===
=== Portbit abfragen ===
Zeile 89: Zeile 86:


Branch if Bit in I/O-Register is Set
Branch if Bit in I/O-Register is Set
<avrasm>
<syntaxhighlight lang="asm">
.macro bbis ;port,bit,target
.macro bbis ;port,bit,target
   .if @0 < 0x20
   .if @0 < 0x20
Zeile 104: Zeile 101:
   .endif
   .endif
.endm
.endm
</avrasm>
</syntaxhighlight>


Branch if Bit in I/O-Register is Cleared
Branch if Bit in I/O-Register is Cleared
<avrasm>
<syntaxhighlight lang="asm">
.macro bbic ;port,bit,target
.macro bbic ;port,bit,target
   .if @0 < 0x20
   .if @0 < 0x20
Zeile 122: Zeile 119:
   .endif
   .endif
.endm
.endm
</avrasm>
</syntaxhighlight>
 
== Location Pointer ==
== Location Pointer ==
=== Align ===
=== Align ===
Manchmal ist es notwendig, Tabellen oder Puffer auf eine bestimmte Byte- oder Wortgrenze zu bringen, z.B. um beim Zugriff einen Überlauf des Index in das obere Adressbyte zu verhindern.
Manchmal ist es notwendig, Tabellen oder Puffer auf eine bestimmte Byte- oder Wortgrenze zu bringen, z.B. um beim Zugriff einen Überlauf des Index in das obere Adressbyte zu verhindern.


<avrasm>
<syntaxhighlight lang="asm">
;align to 1<<@0
.macro      align  ;align to 1<<@0
.macro      align
alignfromhere:
alignfromhere:
       .if (alignfromhere & ((1<<@0)-1))  ;if not already aligned
       .if (alignfromhere & ((1<<@0)-1))  ;if not already aligned
Zeile 135: Zeile 132:
       .endif
       .endif
.endmacro
.endmacro
</avrasm>
</syntaxhighlight>


@0 bezeichnet die Anzahl rechtsbündiger binärer Nullen.
@0 bezeichnet die Anzahl rechtsbündiger binärer Nullen.
Zeile 160: Zeile 157:
Bereits verbrauchte Takte werden aus den Instruktionen errechnet, die zwischen
Bereits verbrauchte Takte werden aus den Instruktionen errechnet, die zwischen
den zu verzögernden Ereignissen liegen. Beispiel:
den zu verzögernden Ereignissen liegen. Beispiel:
<avrasm>
<syntaxhighlight lang="asm">
             sbi    porta,0
             sbi    porta,0
             wait_ns 1000,2,R16
             wait_ns 1000,2,R16
             cbi    porta,0
             cbi    porta,0
</avrasm>
</syntaxhighlight>
In diesem Fall besteht die Anzahl der verbrauchten Takte lediglich aus den Instruktionen,
In diesem Fall besteht die Anzahl der verbrauchten Takte lediglich aus den Instruktionen,
die zum Ereignis führen. Am Ende von SBI wird die steigende Flanke, am Ende von CBI
die zum Ereignis führen. Am Ende von SBI wird die steigende Flanke, am Ende von CBI
Zeile 176: Zeile 173:
erreicht, allerdings nimmt dann auch die Genauigkeit der Verzögerung ab (exakt bis +1 Takt).
erreicht, allerdings nimmt dann auch die Genauigkeit der Verzögerung ab (exakt bis +1 Takt).


<avrasm>
<syntaxhighlight lang="asm">
;
;
; wait_ns  waittime in ns , cyles already used , waitcount register  
; wait_ns  waittime in ns , cyles already used , waitcount register
;
;
;    cycles already used will be subtracted from the delay
;    cycles already used will be subtracted from the delay
Zeile 193: Zeile 190:
       .else
       .else
         .if (cycles > 6)
         .if (cycles > 6)
           .set  loop_cycles = (cycles / 3)    
           .set  loop_cycles = (cycles / 3)
           ldi  @2,loop_cycles
           ldi  @2,loop_cycles
           dec  @2
           dec  @2
Zeile 213: Zeile 210:
       .endif
       .endif
.endmacro
.endmacro
</avrasm>
</syntaxhighlight>


== Strukturierte Programmierung ==
== Strukturierte Programmierung ==
Zeile 223: Zeile 220:


Einige Beispiele:
Einige Beispiele:
 
Verschachteltes If-Then-Else<avrasm>
Verschachteltes If-Then-Else<syntaxhighlight lang="asm">
             cpi  r16,'a'
             cpi  r16,'a'
             ifeq  a_chr
             ifeq  a_chr
Zeile 241: Zeile 238:
               end  c_but_no_a
               end  c_but_no_a
             end  a_chr
             end  a_chr
</avrasm>
</syntaxhighlight>


 
Mehrere und/oder verknüpfte Bedingungen<avrasm>
Mehrere und/oder verknüpfte Bedingungen<syntaxhighlight lang="asm">
             cpi  zh,high(end_buffer)
             cpi  zh,high(end_buffer)
             ifeq_and    end_buffer_reached
             ifeq_and    end_buffer_reached
Zeile 253: Zeile 250:
             end        end_buffer_reached
             end        end_buffer_reached
             ld    r0,z+                  ;read buffer
             ld    r0,z+                  ;read buffer
</avrasm>
</syntaxhighlight>


Das Gleiche als Do-Loop<avrasm>
 
Das Gleiche als Do-Loop<syntaxhighlight lang="asm">
; reading a buffer until end
; reading a buffer until end
; similar to: for z = buffer to end_buffer
; similar to: for z = buffer to end_buffer
Zeile 267: Zeile 264:
               cpi  zl,low(end_buffer)
               cpi  zl,low(end_buffer)
             loopne read_buf
             loopne read_buf
</avrasm>
</syntaxhighlight>
 
Mehr Beispiele und das SAM-include als [[Media:sam.zip|Download]].


Mehr Beispiele und das SAM-include als [[Media:sam.zip|Download.]]
[[Kategorie:AVR]]

Aktuelle Version vom 25. April 2023, 19:28 Uhr

Hier entsteht eine Sammlung von verschiedenen nützlichen Makros für den AVR-Assembler.

16-Bit-Konstante in Z-Pointer laden

.MACRO SetZPtr ;(Adresse)
        ldi     ZL, LOW(@0)
        ldi     ZH, HIGH(@0)
.ENDMACRO

Natürlich auch möglich mit X- und Y-Pointer.

Speicher

2 Register ohne Zwischenspeicher vertauschen

.MACRO SWAP ;(a, b)
        eor     @0, @1
        eor     @1, @0
        eor     @0, @1
.ENDMACRO

Arithmetik

Konstante addieren

.MACRO ADDI ;(a, k)
        subi    @0, -(@1)
.ENDMACRO

Konstante addieren (16 Bit)

.MACRO ADDIW ;(RdL:RdH, k)
        subi    @0L, LOW(-@1)
        sbci    @0H, HIGH(-@1)
.ENDMACRO

oder (sinnlos)

.MACRO ADDIW ;(Rd, k)
        sbiw    @0, (-@1)
.ENDMACRO

DAS geht auch ohne Makro

        adiw a, b

SBIW und ADIW sind aber beide auf die Register(paare) R24, R26, R28, R30 beschränkt UND nehmen nur Zahlen <64 an.

I/O

Bei größeren und neueren AVRs sind etliche I/O-Register nicht mit IN/OUT-Befehlen ansprechbar. LDS/STS erreicht zwar alle, ist aber bei kleineren oder älteren ineffizient.

Port lesen

.macro input
  .if @1 < 0x40
	in	@0, @1
  .else
  	lds	@0, @1
  .endif
.endm

Port schreiben

.macro output
  .if @0 < 0x40
	out	@0, @1
  .else
  	sts	@0, @1
  .endif
.endm

Portbit abfragen

Abfrage eines Bits eines I/O-Ports und Sprung wenn 1/0. Überschreibt u.U. ZL.

Branch if Bit in I/O-Register is Set

.macro bbis ;port,bit,target
  .if @0 < 0x20
 	sbic	@0, @1
	rjmp	@2
  .elif @0 < 0x40
	in	zl, @0
	sbrc	zl, @1
	rjmp	@2
  .else
	lds	zl, @0
	sbrc	zl, @1
	rjmp	@2
  .endif
.endm

Branch if Bit in I/O-Register is Cleared

.macro bbic ;port,bit,target
  .if @0 < 0x20
 	sbis	@0, @1
	rjmp	@2
  .elif @0 < 0x40
	in	zl, @0
	sbrs	zl, @1
	rjmp	@2
  .else
	lds	zl, @0
	sbrs	zl, @1
	rjmp	@2
  .endif
.endm

Location Pointer

Align

Manchmal ist es notwendig, Tabellen oder Puffer auf eine bestimmte Byte- oder Wortgrenze zu bringen, z.B. um beim Zugriff einen Überlauf des Index in das obere Adressbyte zu verhindern.

.macro      align   ;align to 1<<@0
alignfromhere:
      .if (alignfromhere & ((1<<@0)-1))   ;if not already aligned
         .org  (alignfromhere & (0xffff<<@0)) + (1<<@0)
      .endif
.endmacro

@0 bezeichnet die Anzahl rechtsbündiger binärer Nullen. Beispiel: align 8 setzt die nächste Adresse auf $xx00. Funktioniert in allen Segmenten.

Delay

Verzögerung um X Nanosekunden

von Klaus2m5

Taktgenaue Verzögerung der Instruktionsausführung durch Angabe der Verzögerungszeit in Nanosekunden. Dabei werden maximal 4 Instruktionen erzeugt. Taktgenau bedeutet, dass auf die nächste volle Anzahl von Takten aufgerundet wird. Beispiel: 75ns bei 20MHZ (50ns Taktzeit) bedeutet eine tatsächliche Verzögerung von 2 Zyklen und entspricht 100ns.

Die Variable Osc_Hz muss der verwendeten Taktquelle angepasst werden und definiert die CPU-Taktfrequenz in Hertz.

wait_ns wird mit folgenden Parametern aufgerufen:

 1. Verzögerungszeit in Nanosekunden
 2. bereits verbrauchte Takte
 3. ein Immediate-Register (R16-R31) als Zähler

Bereits verbrauchte Takte werden aus den Instruktionen errechnet, die zwischen den zu verzögernden Ereignissen liegen. Beispiel:

            sbi     porta,0
            wait_ns 1000,2,R16
            cbi     porta,0

In diesem Fall besteht die Anzahl der verbrauchten Takte lediglich aus den Instruktionen, die zum Ereignis führen. Am Ende von SBI wird die steigende Flanke, am Ende von CBI die fallende Flanke des Signals erzeugt. Wenn wir also möglichst exakt eine Pulsbreite von einer Mikrosekunde erzeugen wollen, müssen wir die Ausführungszeit von CBI von unserer Wartezeit abziehen. Die Ausführung von CBI liegt vor dem Ereignis!

Wenn die Verzögerungszeit kleiner als die Anzahl bereits verbrauchter Taktzyklen ist, wird keine weitere Verzögerung erzeugt. Die maximale Verzögerung ist 767 Takte entsprechend 38350ns bei 20MHZ. Bei niedrigeren Frequenzen wird eine längere Verzögerung erreicht, allerdings nimmt dann auch die Genauigkeit der Verzögerung ab (exakt bis +1 Takt).

;
; wait_ns  waittime in ns , cyles already used , waitcount register
;
;     cycles already used will be subtracted from the delay
;     the waittime resolution is 1 cycle (delay from exact to +1 cycle)
;     the maximum delay at 20MHz (50ns/clock) is 38350ns
;     waitcount register must specify an immediate register
;
.set     Osc_Hz         = 7372800                    ;7,3728 MHz (Baudrate xtal)
.set     cycle_time_ns  = (1000000000 / Osc_Hz)      ;clock duration
.macro   wait_ns
      .set cycles = ((@0 + cycle_time_ns - 1) / cycle_time_ns - @1)
      .if (cycles > (255 * 3 + 2))
        .error "MACRO wait_ns - too many cycles to burn"
      .else
        .if (cycles > 6)
          .set  loop_cycles = (cycles / 3)
          ldi   @2,loop_cycles
          dec   @2
          brne  pc-1
          .set  cycles = (cycles - (loop_cycles * 3))
        .endif
        .if (cycles > 0)
          .if   (cycles & 4)
            rjmp  pc+1
            rjmp  pc+1
          .endif
          .if   (cycles & 2)
            rjmp  pc+1
          .endif
          .if   (cycles & 1)
            nop
          .endif
        .endif
      .endif
.endmacro

Strukturierte Programmierung

SAM (Structured Assembly Macros)

von Klaus2m5

SAM unterstützt strukturiertes Programmieren durch If-Then-Else und Do-Loop Makros. Beliebige Verschachtelung und Mehrfachbedingungen sind möglich. Läuft unter aktuellen Versionen von AVRASM2.

Einige Beispiele:

Verschachteltes If-Then-Else

            cpi   r16,'a'
            ifeq  a_chr
              cpi   r17,'b'
              ifeq   a_and_b
                ;...a&b
              else   a_and_b
                ;...a&-b
              end    a_and_b
            else  a_chr
              cpi r17,'c'
              ifeq  c_but_no_a
                ;...-a&+c
              else  c_but_no_a
                ;...-a&c
              end   c_but_no_a
            end   a_chr


Mehrere und/oder verknüpfte Bedingungen

            cpi   zh,high(end_buffer)
            ifeq_and    end_buffer_reached
            cpi   zl,low(end_buffer)
            ifeq        end_buffer_reached
              ldi   zh,high(buffer)       ;wrap buffer
              ldi   zl,low(buffer)
            end         end_buffer_reached
            ld    r0,z+                   ;read buffer


Das Gleiche als Do-Loop

; reading a buffer until end
; similar to: for z = buffer to end_buffer
            ldi   zh,high(buffer)   ;init buffer pointer
            ldi   zl,low(buffer)
            do    read_buf
              ld    r0,z+
              cpi   zh,high(end_buffer)
            loopne read_buf
              cpi   zl,low(end_buffer)
            loopne read_buf

Mehr Beispiele und das SAM-include als Download.