When using the optimizer, in a loop like the following one:
uint8_t flag;
...
while (flag == 0) {
...
}
the compiler will typically optimize the access to flag
completely away, since its code path analysis shows that nothing inside the loop
could change the value of flag
anyway. To tell the compiler that
this variable could be changed outside the scope of its code path analysis (e.
g. from within an interrupt routine), the variable needs to be declared like:
volatile uint8_t flag;
Back to FAQ Index.
I get "undefined reference to..." for functions like
"sin()"
In order to access the mathematical functions that are declared in
<math.h>
, the linker needs to be told to also link the
mathematical library, libm.a
.
Typically, system libraries like libm.a
are given to the final C
compiler command line that performs the linking step by adding a flag
-lm
at the end. (That is, the initial lib and the filename
suffix from the library are written immediately after a -l flag. So for
a libfoo.a
library, -lfoo
needs to be provided.) This
will make the linker search the library in a path known to the system.
An alternative would be to specify the full path to the libm.a
file at the same place on the command line, i. e. after all the object
files (*.o
). However, since this requires knowledge of where the
build system will exactly find those library files, this is deprecated for
system libraries.
Back to FAQ Index.
How to permanently bind a variable to a register?
This can be done with
register unsigned char counter asm("r3");
See C Names Used in Assembler Code for more details.
Back to FAQ Index.
How to modify MCUCR or WDTCR early?
The method of early initialization (MCUCR
, WDTCR
or
anything else) is different (and more flexible) in the current version.
Basically, write a small assembler file which looks like this:
;; begin xram.S #include <avr/io.h> .section .init1,"ax",@progbits ldi r16,_BV(SRE) | _BV(SRW) out _SFR_IO_ADDR(MCUCR),r16 ;; end xram.S
Assemble it, link the resulting xram.o
with other files in your
program, and this piece of code will be inserted in initialization code, which
is run right after reset. See the linker script for comments about the new
.init
N sections (which one to use, etc.).
The advantage of this method is that you can insert any initialization code
you want (just remember that this is very early startup -- no stack and no
__zero_reg__
yet), and no program memory space is wasted if this
feature is not used.
There should be no need to modify linker scripts anymore, except for some
very special cases. It is best to leave __stack
at its default
value (end of internal SRAM -- faster, and required on some devices like
ATmega161 because of errata), and add -Wl,-Tdata,0x801100
to start
the data section above the stack.
For more information on using sections, including how to use them from C code, see Memory Sections.
Back to FAQ Index.
What is all this _BV() stuff about?
When performing low-level output work, which is a very central point in
microcontroller programming, it is quite common that a particular bit needs to
be set or cleared in some IO register. While the device documentation provides
mnemonic names for the various bits in the IO registers, and the AVR
device-specific IO definitions reflect these names in definitions for
numerical constants, a way is needed to convert a bit number (usually within a
byte register) into a byte value that can be assigned directly to the register.
However, sometimes the direct bit numbers are needed as well (e. g. in an
sbi()
call), so the definitions cannot usefully be made as byte values in the first
place.
So in order to access a particular bit number as a byte value, use the
_BV()
macro. Of course, the implementation of this macro is just the usual bit shift
(which is done by the compiler anyway, thus doesn't impose any run-time
penalty), so the following applies:
_BV(3) => 1 << 3 => 0x08
However, using the macro often makes the program better readable.
"BV" stands for "bit value", in case someone might ask you. :-)
Example: clock timer 2 with full IO clock (CS2
x
= 0b001), toggle OC2 output on compare match (COM2
x =
0b01), and clear timer on compare match (CTC2
= 1). Make OC2
(PD7
) an output.
Back to FAQ Index.
Basically yes, C++ is supported (assuming your compiler has been configured
and compiled to support it, of course). Source files ending in .cc
,
.cpp
or .C
will automatically cause the compiler
frontend to invoke the C++ compiler. Alternatively, the C++ compiler could be
explicitly called by the name avr-c++
.
However, there's currently no support for libstdc++
, the
standard support library needed for a complete C++ implementation. This imposes
a number of restrictions on the C++ programs that can be compiled. Among them
are:
new
and delete
are not
implemented, attempting to use them will cause the linker to complain about
undefined external references. (This could perhaps be fixed.)extern "C" { . . . }
-fno-exceptions
in the compiler options. Failing this, the linker
will complain about an undefined external reference to
__gxx_personality_sj0
.When programming C++ in space- and runtime-sensitive environments like
microcontrollers, extra care should be taken to avoid unwanted side effects of
the C++ calling conventions like implied copy constructors that could be called
upon function invocation etc. These things could easily add up into a
considerable amount of time and program memory wasted. Thus, casual inspection
of the generated assembler code (using the -S
compiler option)
seems to be warranted.
Back to FAQ Index.
Shouldn't I initialize all my variables?
Global and static variables are guaranteed to be initialized to 0 by the C
standard. avr-gcc
does this by placing the appropriate code into
section .init4
(see The
.initN Sections). With respect to the standard, this sentence is somewhat
simplified (because the standard allows for machines where the actual bit
pattern used differs from all bits being 0), but for the AVR target, in general,
all integer-type variables are set to 0, all pointers to a NULL pointer, and all
floating-point variables to 0.0.
As long as these variables are not initialized (i. e. they don't have an equal sign and an initialization expression to the right within the definition of the variable), they go into the .bss section of the file. This section simply records the size of the variable, but otherwise doesn't consume space, neither within the object file nor within flash memory. (Of course, being a variable, it will consume space in the target's SRAM.)
In contrast, global and static variables that have an initializer go into the .data section of the file. This will cause them to consume space in the object file (in order to record the initializing value), and in the flash ROM of the target device. The latter is needed since the flash ROM is the only way that the compiler can tell the target device the value this variable is going to be initialized to.
Now if some programmer "wants to make doubly sure" their variables really get a 0 at program startup, and adds an initializer just containing 0 on the right-hand side, they waste space. While this waste of space applies to virtually any platform C is implemented on, it's usually not noticeable on larger machines like PCs, while the waste of flash ROM storage can be very painful on a small microcontroller like the AVR.
So in general, variables should only be explicitly initialized if the initial value is non-zero.
Back to FAQ Index.
Why do some 16-bit timer registers sometimes get
trashed?
Some of the timer-related 16-bit IO registers use a temporary register
(called TEMP in the Atmel datasheet) to guarantee an atomic access to the
register despite the fact that two separate 8-bit IO transfers are required to
actually move the data. Typically, this includes access to the current
timer/counter value register (TCNT
n), the input capture
register (ICR
n), and write access to the output compare
registers (OCR
nM). Refer to the actual datasheet for each
device's set of registers that involves the TEMP register.
When accessing one of the registers that use TEMP from the main application, and possibly any other one from within an interrupt routine, care must be taken that no access from within an interrupt context could clobber the TEMP register data of an in-progress transaction that has just started elsewhere.
To protect interrupt routines against other interrupt routines, it's usually best to use the SIGNAL() macro when declaring the interrupt function, and to ensure that interrupts are still disabled when accessing those 16-bit timer registers.
Within the main program, access to those registers could be encapsulated in calls to the cli() and sei() macros. If the status of the global interrupt flag before accessing one of those registers is uncertain, something like the following example code can be used.
uint16_t read_timer1(void) { uint8_t sreg; uint16_t val; sreg = SREG; cli(); val = TCNT1; SREG = sreg; return val; }
Back to FAQ Index.
How do I use a #define'd constant in an asm
statement?
So you tried this:
asm volatile("sbi 0x18,0x07;");
Which works. When you do the same thing but replace the address of the port by its macro name, like this:
asm volatile("sbi PORTB,0x07;");
you get a compilation error: "Error: constant value required"
.
PORTB
is a precompiler definition included in the processor
specific file included in avr/io.h
. As you may know, the
precompiler will not touch strings and PORTB
, instead of
0x18
, gets passed to the assembler. One way to avoid this problem
is:
asm volatile("sbi %0, 0x07" : "I" (PORTB):);
avr/io.h
already provides a sbi()
macro definition, which can be used in C programs.
Why does the PC randomly jump around when
single-stepping through my program in avr-gdb?
When compiling a program with both optimization (-O)
and debug
information (-g)
which is fortunately possible in
avr-gcc
, the code watched in the debugger is optimized code. While
it is not guaranteed, very often this code runs with the exact same
optimizations as it would run without the -g
switch.
This can have unwanted side effects. Since the compiler is free to reorder
code execution as long as the semantics do not change, code is often rearranged
in order to make it possible to use a single branch instruction for conditional
operations. Branch instructions can only cover a short range for the target PC
(-63 through +64 words from the current PC). If a branch instruction cannot be
used directly, the compiler needs to work around it by combining a skip
instruction together with a relative jump (rjmp)
instruction, which
will need one additional word of ROM.
Another side effect of optimzation is that variable usage is restricted to
the area of code where it is actually used. So if a variable was placed in a
register at the beginning of some function, this same register can be re-used
later on if the compiler notices that the first variable is no longer used
inside that function, even though the variable is still in lexical scope. When
trying to examine the variable in avr-gdb
, the displayed result
will then look garbled.
So in order to avoid these side effects, optimization can be turned off while debugging. However, some of these optimizations might also have the side effect of uncovering bugs that would otherwise not be obvious, so it must be noted that turning off optimization can easily change the bug pattern. In most cases, you are better off leaving optimizations enabled while debugging.
Back to FAQ Index.
How do I trace an assembler file in avr-gdb?
When using the -g
compiler option, avr-gcc
only
generates line number and other debug information for C (and C++) files that
pass the compiler. Functions that don't have line number information will be
completely skipped by a single step
command in gdb
.
This includes functions linked from a standard library, but by default also
functions defined in an assembler source file, since the -g
compiler switch does not apply to the assembler.
So in order to debug an assembler input file (possibly one that has to be
passed through the C preprocessor), it's the assembler that needs to be told to
include line-number information into the output file. (Other debug information
like data types and variable allocation cannot be generated, since unlike a
compiler, the assembler basically doesn't know about this.) This is done using
the (GNU) assembler option --gstabs
.
Example:
$ avr-as -mmcu=atmega128 --gstabs -o foo.o foo.s
When the assembler is not called directly but through the C compiler frontend
(either implicitly by passing a source file ending in .S
, or
explicitly using -x assembler-with-cpp
), the compiler frontend
needs to be told to pass the --gstabs
option down to the assembler.
This is done using -Wa,--gstabs
. Please take care to only
pass this option when compiling an assembler input file. Otherwise, the
assembler code that results from the C compilation stage will also get line
number information, which confuses the debugger.
-Wa,-gstabs
since the compiler will add the
extra '-
' for you.
$ EXTRA_OPTS="-Wall -mmcu=atmega128 -x assembler-with-cpp" $ avr-gcc -Wa,--gstabs ${EXTRA_OPTS} -c -o foo.o foo.S
Also note that the debugger might get confused when entering a piece of code that has a non-local label before, since it then takes this label as the name of a new function that appears to have been entered. Thus, the best practice to avoid this confusion is to only use non-local labels when declaring a new function, and restrict anything else to local labels. Local labels consist just of a number only. References to these labels consist of the number, followed by the letter b for a backward reference, or f for a forward reference. These local labels may be re-used within the source file, references will pick the closest label with the same number and given direction.
Example:
myfunc: push r16
push r17
push r18
push YL
push YH
...
eor r16, r16 ; start loop
ldi YL, lo8(sometable)
ldi YH, hi8(sometable)
rjmp 2f ; jump to loop test at end
1: ld r17, Y+ ; loop continues here
...
breq 1f ; return from myfunc prematurely
...
inc r16
2: cmp r16, r18
brlo 1b ; jump back to top of loop
1: pop YH
pop YL
pop r18
pop r17
pop r16
ret
Back to FAQ Index.
#include <inttypes.h> #include <avr/io.h> void set_bits_func_wrong (volatile uint8_t port, uint8_t mask) { port |= mask; } void set_bits_func_correct (volatile uint8_t *port, uint8_t mask) { *port |= mask; } #define set_bits_macro(port,mask) ((port) |= (mask)) int main (void) { set_bits_func_wrong (PORTB, 0xaa); set_bits_func_correct (&PORTB, 0x55); set_bits_macro (PORTB, 0xf0); return (0); }
The first function will generate object code which is not even close to what
is intended. The major problem arises when the function is called. When the
compiler sees this call, it will actually pass the value in the the
PORTB
register (using an IN
instruction), instead of
passing the address of PORTB
(e.g. memory mapped io addr of
0x38
, io port 0x18
for the mega128). This is seen
clearly when looking at the disassembly of the call:
set_bits_func_wrong (PORTB, 0xaa); 10a: 6a ea ldi r22, 0xAA ; 170 10c: 88 b3 in r24, 0x18 ; 24 10e: 0e 94 65 00 call 0xca
So, the function, once called, only sees the value of the port register and knows nothing about which port it came from. At this point, whatever object code is generated for the function by the compiler is irrelevant. The interested reader can examine the full disassembly to see the the function's body is completely fubar.
The second function shows how to pass (by reference) the memory mapped address of the io port to the function so that you can read and write to it in the function. Here's the object code generated for the function call:
set_bits_func_correct (&PORTB, 0x55); 112: 65 e5 ldi r22, 0x55 ; 85 114: 88 e3 ldi r24, 0x38 ; 56 116: 90 e0 ldi r25, 0x00 ; 0 118: 0e 94 7c 00 call 0xf8
You can clearly see that 0x0038
is correctly passed for the
address of the io port. Looking at the disassembled object code for the body of
the function, we can see that the function is indeed performing the operation we
intended:
void set_bits_func_correct (volatile uint8_t *port, uint8_t mask) { f8: fc 01 movw r30, r24 *port |= mask; fa: 80 81 ld r24, Z fc: 86 2b or r24, r22 fe: 80 83 st Z, r24 } 100: 08 95 ret
Notice that we are accessing the io port via the LD
and
ST
instructions.
The port
parameter must be volatile to avoid a compiler warning.
IN
and OUT
assembly
instructions, they can not be used inside the function when passing the port
in this way. Readers interested in the details should consult the
Instruction Set data sheet.
set_bits_macro (PORTB, 0xf0); 11c: 88 b3 in r24, 0x18 ; 24 11e: 80 6f ori r24, 0xF0 ; 240 120: 88 bb out 0x18, r24 ; 24
Of course, in a real application, you might be doing a lot more in your function which uses a passed by reference io port address and thus the use of a function over a macro could save you some code space, but still at a cost of execution speed.
Back to FAQ Index.
char
is 8 bits, int
is 16
bits, long
is 32 bits, long
long is 64 bits,
float
and double
are 32 bits (this is the only
supported floating point format), pointers are 16 bits (function pointers are
word addresses, to allow addressing the whole 128K program memory space on the
ATmega devices with > 64 KB of flash ROM). There is a -mint8
option (see Options
for the C compiler avr-gcc) to make int
8 bits, but that is
not supported by avr-libc and violates C standards (int
must be at least 16 bits). It may be removed in a future
release.r0 - temporary register, can be clobbered by any C code (except interrupt handlers which save it), may be used to remember something for a while within one piece of assembler code
r1 - assumed to be always zero in any C code, may be used to
remember something for a while within one piece of assembler code, but
must then be cleared after use (clr r1
). This includes
any use of the [f]mul[s[u]]
instructions, which return their
result in r1:r0. Interrupt handlers save and clear r1 on entry, and restore r1
on exit (in case it was non-zero).
char
, have one free register
above them). This allows making better use of the movw
instruction on the enhanced core.
If too many, those that don't fit are passed on the stack.
Return values: 8-bit in r24 (not r25!), 16-bit in r25:r24, up to 32 bits in
r22-r25, up to 64 bits in r18-r25. 8-bit return values are zero/sign-extended
to 16 bits by the caller (unsigned char
is more efficient than
signed char
- just clr r25
). Arguments to functions
with variable argument lists (printf etc.) are all passed on stack, and
char
is extended to int
.