AVR Assembler Makros: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
Zeile 150: Zeile 150:
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
die fallende Flanke des Signals erzeugt. Wenn wir also möglichst exakt eine
die fallende Flanke des Signals erzeugt. Wenn wir also möglichst exakt eine
Pulsbreite von einer Millisekunde erzeugen wollen, müssen wir die Ausführungszeit
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!
von CBI von unserer Wartezeit abziehen. Die Ausführung von CBI liegt vor dem Ereignis!


Zeile 167: Zeile 167:
;    waitcount register must specify an immediate register
;    waitcount register must specify an immediate register
;
;
.set Osc_Hz        = 7372800                    ;7,3728 MHz (Baudrate xtal)
.set     Osc_Hz        = 7372800                    ;7,3728 MHz (Baudrate xtal)
.set cycle_time_ns  = (1000000000 / Osc_Hz)      ;clock duration
.set     cycle_time_ns  = (1000000000 / Osc_Hz)      ;clock duration
.macro     wait_ns
.macro   wait_ns
       .set cycles = ((@0 + cycle_time_ns - 1) / cycle_time_ns - @1)
       .set cycles = ((@0 + cycle_time_ns - 1) / cycle_time_ns - @1)
       .if (cycles > (255 * 3 + 2))
       .if (cycles > (255 * 3 + 2))

Version vom 30. Januar 2010, 17:05 Uhr


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

16 Bit Konstante in Z-Pointer laden

<avrasm> .MACRO SetZPtr ;(Adresse)

       ldi     ZL, LOW(@0)
       ldi     ZH, HIGH(@0)

.ENDMACRO </avrasm>

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


Speicher

2 Register ohne Zwischenspeicher vertauschen

<avrasm> .MACRO SWAP ;(a, b)

       eor     @0, @1
       eor     @1, @0
       eor     @0, @1

.ENDMACRO </avrasm>

Arithmetik

Konstante addieren

<avrasm> .MACRO ADDI ;(a, k)

       subi    @0, -(@1)

.ENDMACRO </avrasm>

Konstante addieren (16 Bit)

<avrasm> .MACRO ADDIW ;(RdL:RdH, k)

       subi    @0L, LOW(-@1)  
       sbci    @0H, HIGH(-@1)

.ENDMACRO </avrasm> oder (sinnlos) <avrasm> .MACRO ADDIW ;(Rd, k)

       sbiw    @0, (-@1)

.ENDMACRO </avrasm> DAS geht auch ohne Makro <avrasm>

       adiw a, b

</avrasm> 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ö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.

Port lesen

<avrasm> .macro input

 .if @1 < 0x40

in @0, @1

 .else
 	lds	@0, @1
 .endif

.endm </avrasm>

Port schreiben

<avrasm> .macro output

 .if @0 < 0x40

out @0, @1

 .else
 	sts	@0, @1
 .endif

.endm </avrasm>

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 <avrasm> .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 </avrasm>

Branch if Bit in I/O-Register is Cleared <avrasm> .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 </avrasm>

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: <avrasm>

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

</avrasm> 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).

<avrasm>

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 </avrasm>