|AVR Libc Home Page||AVR Libc Development Pages|
|Main Page||User Manual||Library Reference||FAQ||Alphabetical Index||Example Projects|
Usually, all but the first could probably be done easily using the inline assembler facility of the compiler.
Although avr-libc is primarily targeted to support programming AVR microcontrollers using the C (and C++) language, there's limited support for direct assembler usage as well. The benefits of it are:
avr-gcc) that in turn will call the assembler and linker as required.
This approach has the following advantages:
avr-gcc, regardless of the actual source language used.
.o) and linker script.
Note that the invokation of the C preprocessor will be automatic when the filename provided for the assembler file ends in
.S (the capital letter "s"). This would even apply to operating systems that use case-insensitive filesystems since the actual decision is made based on the case of the filename suffix given on the command-line, not based on the actual filename from the file system.
Alternatively, the language can explicitly be specified using the
-x assembler-with-cpp option.
#include <avr/io.h> ; Note  work = 16 ; Note  tmp = 17 inttmp = 19 intsav = 0 SQUARE = PD6 ; Note  ; Note : tmconst= 10700000 / 200000 ; 100 kHz => 200000 edges/s fuzz= 8 ; # clocks in ISR until TCNT0 is set .section .text .global main ; Note  main: rcall ioinit 1: rjmp 1b ; Note  .global TIMER0_OVF_vect ; Note  TIMER0_OVF_vect: ldi inttmp, 256 - tmconst + fuzz out _SFR_IO_ADDR(TCNT0), inttmp ; Note  in intsav, _SFR_IO_ADDR(SREG) ; Note  sbic _SFR_IO_ADDR(PORTD), SQUARE rjmp 1f sbi _SFR_IO_ADDR(PORTD), SQUARE rjmp 2f 1: cbi _SFR_IO_ADDR(PORTD), SQUARE 2: out _SFR_IO_ADDR(SREG), intsav reti ioinit: sbi _SFR_IO_ADDR(DDRD), SQUARE ldi work, _BV(TOIE0) out _SFR_IO_ADDR(TIMSK), work ldi work, _BV(CS00) ; tmr0: CK/1 out _SFR_IO_ADDR(TCCR0), work ldi work, 256 - tmconst out _SFR_IO_ADDR(TCNT0), work sei ret .global __vector_default ; Note  __vector_default: reti .end
#define work 16
intby default in order to calculate constant integer expressions.
TCCNT0register, we therefore have to account for the number of clock cycles required for interrupt acknowledge and for the instructions to reload
TCCNT0(4 clock cycles for interrupt acknowledge, 2 cycles for the jump from the interrupt vector, 2 cycles for the 2 instructions that reload
TCCNT0). This is what the constant
mainis the application entry point that will be jumped to from the ininitalization routine in
sleepinstruction (using idle mode) could be used as well, but probably would not conserve much energy anyway since the interrupt service is executed quite frequently.
.global in order to be acceptable for this purpose. This will only work if
<avr/io.h>has been included. Note that the assembler or linker have no chance to check the correct spelling of an interrupt function, so it should be double-checked. (When analyzing the resulting object file using
avr-nm, a name like
__vector_Nshould appear, with N being a small integer number.)
_SFR_IO_ADDR. (The AT90S1200 does not have RAM thus the memory-mapped approach to access the IO registers is not available. It would be slower than using
TCCNT0is time-critical, it is even performed before saving
SREG. Obviously, this requires that the instructions involved would not change any of the flag bits in
SREG. (Note that this serves as an example here only since actually, all the following instructions would not modify
SREGeither, but that's not commonly the case.)
__vector_default. This must be
.global, and obviously, should end in a
retiinstruction. (By default, a jump to location 0 would be implied instead.)http://sources.redhat.com/binutils/.
As gas comes from a Unix origin, its pseudo-op and overall assembler syntax is slightly different than the one being used by other assemblers. Numeric constants follow the C notation (prefix
0x for hexadecimal constants), expressions use a C-like syntax.
Some common pseudo-ops include:
.byte allocates single byte constants
.ascii allocates a non-terminated string of characters
.asciz allocates a \0-terminated string of characters (C string)
.data switches to the .data section (initialized RAM variables)
.text switches to the .text section (code and ROM constants)
.set declares a symbol as a constant expression (identical to
.globl) declares a public symbol that is visible to the linker (e. g. function entry point, global variable)
.extern declares a symbol to be externally defined; this is effectively a comment only, as gas treats all undefined symbols it encounters as globally undefined anyway
.org is available in gas as well, but is a fairly pointless pseudo-op in an assembler environment that uses relocatable object files, as it is the linker that determines the final position of some object in ROM or RAM.
Along with the architecture-independent standard operators, there are some AVR-specific operators available which are unfortunately not yet described in the official documentation. The most notable operators are:
lo8Takes the least significant 8 bits of a 16-bit integer
hi8Takes the most significant 8 bits of a 16-bit integer
pmTakes a program-memory (ROM) address, and converts it into a RAM address. This implies a division by 2 as the AVR handles ROM addresses as 16-bit words (e.g. in an
ICALLinstruction), and can also handle relocatable symbols on the right-hand side.
ldi r24, lo8(pm(somefunc)) ldi r25, hi8(pm(somefunc)) call something
This passes the address of function
somefunc as the first parameter to function