Never ignore a warning!
This version of C will accept misspelled ISR names and nonexistant functions with just a warning, among other nasty surprises.
Use static
, const
and volatile
variables correctly.
Incorrect use may affect program execution and efficiency. Every variable which is set in an interrupt and read in main
(or vice-versa) must be declared as volatile
. The optimizer may remove variables which change asynchronously in an ISR unless you use the volatile
keyword. (see optimizer comments below).
Type char
defaults to unsigned. Be sure to specify what you need.
In general, distinguish between unsigned and signed variables.
This version of C allows you to modify 16-bit registers by just assigning a value.
For instance TCNT1 = 0x4444
;.
If you decide that you need to modify the high and low parts of the 16-bit register separately, then the high byte should be written before the low byte for a writing operation, and the low byte must be read before the high byte for a reading operation.
Distinguish between = and ==.
When xyzzy is a variable, the following syntax using = instead of ==:
if (xyzzy=0){stuff};
is completely legal and will never enter the if-block because the variable is first assigned the value zero, then checked to see if it is not-zero.
The operators ! (logical not) and ~ (bitwise not) are not equivalent.
Let's say we have the char variable xyzzy=1. The syntax:
if (~xyzzy){stuff};
will always enter the if-block because ~1 for a char variable is 0xFE, which is non-zero.
The operators & (bitwise and) and && (logical and) are not equivalent.
Let's say that xyzzy=1
and abx=2
.
Then (xyzzy & abx)=0
but (xyzzy && abx) = 1
(logical TRUE)
Similarly for | and || (bitwise and logical or).
Variables seem to change without reason.
If variables seem to be changing on their own, or inserting debug variables seems to 'fix' the problem, you probably have a memory issue. Check the compiler-generated .map file. Look for zero-based versus one-based array errors.
Look for buffer overruns! You can overrun array boundaries at run time (C has no bounds checks). This includes strings which are longer than the declared array size.
Watch out for the radix of constants.
When mixing binary and hex (0b, 0x) in assignments it is very easy to miss mistakes like
REGISTER=0x00001000;
when you meant to use binary.
Buffer overrun errors are easy when printing or copying strings.
Use snprintf
instead of sprintf
to print a given number of characters.
There is also strncopy
instead of strcopy
as well as other number-limited string commands.
The default way of setting/clearing bits in an i/o register can be confusing.
You could substitute the following bit operation macros:
#define READ(U, N) ((U) >> (N) & 1u)
#define SET(U, N) ((void)((U) |= 1u << (N)))
#define CLR(U, N) ((void)((U) &= ~(1u << (N))))
#define FLIP(U, N) ((void)((U) ^= 1u << (N)))
Putting a semicolon at the end of a for
statement makes the statement a null loop!
for (i=0; i<10; i++);
<--wrong (unless for some reason you want a null loop).
Sometimes you can debug errors by modifying the level of optimization.
This can sometimes catch code which is optimized away because it is unreachable or because a variable is never set in main.
Quoting from AvrLibC:
Increasing Optimization level n is meant to optimize more. An optimization level of 0 means no optimization at all, which is the
default if no -O option is present. The special option -Os is meant to turn on all -O2 optimizations that are not expected to increase code size. Note that at -O3, gcc attempts to inline all "simple" functions. For the AVR target, this will normally constitute a large pessimization (opposite of optimization) due to the code size increase. The only other optimization turned on with -O3 is -frename-registers, which could rather be enabled manually instead. A simple -O option is equivalent to -O1. Note also that turning off all optimizations will prevent some warnings from being issued since the generation of those warnings depends on code analysis steps that are only performed when optimizing (unreachable code, unused variables).
c:/winavr-20080610/bin/../lib/gcc/avr/4.3.0/../../../../avr/lib/avr5\libc.a(fp_powsodd.o): In function `__fp_powsodd': (.text.fplib+0x10): relocation truncated to fit: R_AVR_13_PCREL against symbol `__mulsf3' defined in .text section in c:/winavr-20080610/bin/../lib/gcc/avr/4.3.0/avr5\libgcc.a(_mul_sf.o) c:/winavr-20080610/bin/../lib/gcc/avr/4.3.0/../../../../avr/lib/avr5\libc.a(fp_powsodd.o): In function `__fp_powsodd': (.text.fplib+0x20): relocation truncated to fit: R_AVR_13_PCREL against symbol `__mulsf3' defined in .text section in c:/winavr-20080610/bin/../lib/gcc/avr/4.3.0/avr5\libgcc.a(_mul_sf.o)
The fix is described here:
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=printview&t=56149&start=0
But
all you really need to do is go to project options -> libraries
->
then click on libm.a
and add to the list.
Project -> Configuration Options
, click the Libraries
tab, and then add libprintf_flt.a
and libscanf_flt.a
. Next, in the Custom Options
tab of the same window, select [Linker Options]
in the window on the left. -Wl,-u,vfprintf -lprintf_flt -lm
-Wl,-u,vfscanf -lscanf_flt -lm
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html
. The extended printf library might not be necessary, but it helps with debugging. -Wl,-u,vfprintf -lprintf_flt -lm and -Wl,-u,vfscanf -lscanf_flt -lm
switches. Tools>Program AVR>Autoconnect
menu item which will open a dialog box. In the dialog box:
Main tab
, set the ISP frequency (Settings
... dropdown) to 57 Khz, then press Write
then Close
. Mega644
. sei()
?main
(or vice-versa) declared as volatile
? main
? asserts
(or blink some LEDs) to see where the code stalls.
MCU seems slow or erratic and/or PORTC is not operating properly.
The MCU is probably new and has not had it's fuses set properly:
Fuses
tab in the programmer window and set the SUT_CLKSEL
fuse to:Ext crystal osc 8- MHz; startup time: 16k clk+65 mSec.
CLKDIV8
box.JTAGEN
box and answer YES in the confirming dialog. Program
RS232 SPARE
header? uart.c
has a clock frequency define which will need to be changed if you change crystals. Project>Project
Configuration
dialog box, then go to the custom options
,
then select linker options
, and then add the -Wl,-u,vfprintf
-lprintf_flt
switches. uart_init()
, then port pins D.0 and D.1 are disabled for general i/o. Project -> Configuration Options
, click the Libraries
tab, and then add libprintf_flt.a
and libscanf_flt.a
. Next, in the Custom Options
tab of the same window, select [Linker Options]
in the window on the left. -Wl,-u,vfprintf -lprintf_flt -lm
-Wl,-u,vfscanf -lscanf_flt -lm
http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html
. The extended printf library might not be necessary, but it helps with debugging. -Wl,-u,vfprintf -lprintf_flt
switches. An easier way to print floats is to use the AVRLIBC stdlib function dtostrf
as explained in AVRfreaks. An example.project...project config
dialog.uart_init()
, then port pins D.0 and D.1 are disabled for general i/o. General considerations for hardware debugging:
3e4 x 20e-9 = 60e-5
or 600 microseconds! This means that to get 8-bit accuracy after you change Vref, you must wait 1/256 = exp(-t/600)
or t~3 mSec before doing another conversion!Copyright Cornell University Feb 2009