Debugging C, the Mega, and the STK500

  1. Summary of ways C can mess you up.
  2. STK does not program
  3. You load a program and nothing happens
  4. MCU seems slow or erratic and/or PORTC is not operating properly
  5. UART does not work correctly
  6. General hardware considerations

C "features" (Also see Common C errors)
  1. Never ignore a warning!
    This version of C will accept misspelled ISR names and nonexistant functions with just a warning, among other nasty surprises.

  2. 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).

  3. Type char defaults to unsigned. Be sure to specify what you need.
    In general, distinguish between unsigned and signed variables.

  4. 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.

  5. 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.

  6. 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.

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

  8. 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.

  9. 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.

  10. 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.

  11. 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)))

  12. 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).

  13. 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).

  14. Some people have had a weird error, which doesn't show a red dot because it is a linker error. The message is:
    
    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.

  15. You need to add acouple of libraries to get floating point input working. In 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.
    In the input box on the right, type
    -Wl,-u,vfprintf -lprintf_flt -lm
    and click Add. Then type
    -Wl,-u,vfscanf -lscanf_flt -lm
    and click Add again.
    Kenneth Bongort found these instructions in 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.

  16. If you decide to print floating point numbers using fprintf you need to follow the directions in Using sprintf function for float numbers in AVR-GCC the AVRstudio directions are at the bottom of the page, but NOTE that one library is left out. The correct summary is: Open the Project>Project Configuration dialog box, then go to the custom options , then select linker options, and then add the
    -Wl,-u,vfprintf -lprintf_flt -lm and -Wl,-u,vfscanf -lscanf_flt -lm switches.

STK500 does not program:

  1. Does the board have power? Are the LEDs lit?
  2. Does the board have a crystal?
  3. Are all the jumpers in place as shown in lab0?
  4. Did somebody insert the MCU backwards?
    The end of the MCU with the small semicircle cutout should be toward the PORTA header. Check the pictures in lab 0.
  5. If the VTARGET LED on the STK500 (green LED near the RESET switch) goes out it means you have built a short circuit.
    If the regulator on the STK500 is too hot to touch, TURN IT OFF and look for the short.
  6. Do you have PORTB connected to anything? If so, disconnect.
    The LCD will always cause programming to fail if it is on PORTB.
  7. Follow these steps:
    Select the Tools>Program AVR>Autoconnect menu item which will open a dialog box. In the dialog box:
    1. In the Main tab, set the ISP frequency (Settings... dropdown) to 57 Khz, then press Write then Close.
      Make sure the device is Mega644.
  8. In the folder C:\programFiles\atmel\AVRtools\STK500 you will find the utility "upgrade.exe". This file reflashes the PROGRAMMER on the STK500. To do this: Turn off the STK500, hold down the "program" switch and turn it on. Run the utility and follow directions or ask for help.
  9. The last step is to swap out the cpu. Have the TA help you.

You load a program and nothing happens.

  1. Is the master interrupt bit set using sei()?
    If your code depends on a timer for scheduling tasks, it will appear dead if the I-bit is 0.
  2. Is every variable which is set in an interrupt and read in main (or vice-versa) declared as volatile?
    The optimizer may remove variables which change asynchronously in an ISR. (see optimizer comments in the C section above).
  3. Is your code reseting continuously?
    If you turn on any interrupts for which you did not write an ISR, GCC inserts a default routine which simply resets the MCU every time the invalid interrupt flag is set.
  4. If :
    1. your task clock is timer0 and
    2. timer1 is set to interrupt on match, and
    3. timer1 is set to clear on match, and
    4. OCR1A is zero, then
    timer0 will never take an interrupt. Timer 1 has priority and takes an interrupt on every cycle.
  5. A timer can be in normal or PWM mode, but not both.
    Never turn on the compare-match ISR and the overflow ISR at the same time.
  6. Did your code exit main?
    GCC lets you do this even though there is no code running after you leave main.
  7. Start inserting 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:

  1. Go to the Fuses tab in the programmer window and set the SUT_CLKSEL fuse to:
    Ext crystal osc 8- MHz; startup time: 16k clk+65 mSec.
  2. Uncheck the CLKDIV8 box.
    (If you leave the box checked, the external clock is divided by 8).
  3. Uncheck the JTAGEN box and answer YES in the confirming dialog.
    (If you leave the box checked, PORTC does not work correctly).
  4. Click Program

UART does not work correctly.

  1. Is the jumper from D.0 and D.1 connnected to the RS232 SPARE header?
  2. Did you use the same COM port for the programmer and for the terminal program (hyperterm).
    If so, change one of them.
  3. You are getting junk characters in the terminal.
    Check the baud rate and crystal frequency settings and the physical crystal frequency. Note that uart.c has a clock frequency define which will need to be changed if you change crystals.
  4. You try to print a float and get a question mark.
    To print floating point numbers: Open the 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.
  5. Very occasionally, WindowsXT will cause a COM driver to hang.
    If you suspect this, look at the list of running processes. There may be a zombie copy of avrstudio or hyperterm running. You will probably have to reboot.
  6. Note that if you execute uart_init(), then port pins D.0 and D.1 are disabled for general i/o.
  7. You need to add acouple of libraries to get floating point input working. In 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.
    In the input box on the right, type
    -Wl,-u,vfprintf -lprintf_flt -lm
    and click Add. Then type
    -Wl,-u,vfscanf -lscanf_flt -lm
    and click Add again.
    Kenneth Bongort found these instructions in 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.

  8. If you decide to print floating point numbers using fprintf you need to follow the directions in Using sprintf function for float numbers in AVR-GCC the AVRstudio directions are at the bottom of the page, but NOTE that one library is left out. The correct summary is: Open the 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. An easier way to print floats is to use the AVRLIBC stdlib function dtostrf as explained in AVRfreaks. An example.

Hardware.

  1. If the LCD produces weird characters, then make sure you set the cyrstal frequency in the project...project config dialog.
  2. If you execute uart_init(), then port pins D.0 and D.1 are disabled for general i/o.
  3. The STK500 voltage regulator is controllable from software.
    Check the voltage with a DVM. If it is wrong call a TA.
  4. If you circuit seems not to work, or voltages are weird (for instance, no ground), check the pins on the gray cable connecting the STK500 to the solderless whiteboard. In fact, always check the pins.
  5. If the VTARGET LED on the STK500 (green LED near the RESET switch) goes out it means you have built a short circuit. If the regulator on the STK500 is too hot to touch, TURN IT OFF and look for the short.

General considerations for hardware debugging:

  1. If you hook up any hardware to the STK500, connect the scope to it to see if the waveforms are what you expect.
    Don't use the autoset feature on the scope.
    Autoset only works when you are getting close to the waveform you expect.
  2. You can get the polarity of an LED by using the diode test setting on the digital voltmeter.
  3. You can test a keypad with an ohmmeter.
  4. If something is hot, turn off the power, then check the connections.
  5. Wires break in the middle more often than you would think.
    Check the voltage everywhere.
  6. Some capacitors are polarized and require that the positive lead be at higher average potential than the negative lead. Sometimes there is a plus near one lead. Sometimes a long line which indicates minus. Sometimes one lead is longer. When in doubt, find the datasheet for the part.
  7. There is no standard for pinputs on transistors or most other components. You have to look up each different type.
  8. A close reading of the 644 data sheet, table 26-7, gives the V-reference impedance as 30 kohm. The STK500 schematic
    http://people.ece.cornell.edu/land/courses/ece4760/AtmelStuff/STK500_Schematics.pdf indicates (pages 4 and 2) that 20 nanofarad is placed from Aref to ground. This implies a time-constant to CHANGE Vref of
    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