Debugging: Using Macros to Monitor Program Flow

Abstract

Outputting a debug value in a quasi-serial protocol, over a single output pin, can be an effective debugging method.

So now you've got that LED to blink, signifying that something is running in your MCU. But you'd like a little more information from your prototype system: perhaps some indication of which code paths are executing and which ones are not. One-way communication would suffice...

You can use C preprocessor macros to create a debug tag output. You can insert this output statement wherever you want to report an event, and then enable or disable it in your executable as necessary. When you need it, it only consumes a few bytes and cycles, and gives you a better window on your program than a blinking LED. When you don't need it, it disappears.

What's better, you don't need any special equipment to use it. At a minimum, an oscilloscope can give you a simple readout of the tag value, as a bit pattern. A trace analyser will give you lots more information, of course, but it isn't strictly necessary.

To add this debugging capability to your program:

  1. Simply #define the following symbols...

     #define DEBUG_TRACE	  	 /* Enable debugging output */ 
    #define D_IDLE    1     /* Idle, will change to BEGIN */ #define D_DIRPORT DDRx  /* The data direction of the port and ... */ #define D_DIRPIN  x     /* ... pin [0..7] on which to signal */ #define D_SETOUT  1     /* The output data direction */ #define D_PORT    PORTx /* The port and ... */ #define D_PIN     x     /* ... pin [0..7] on which to signal */ 
  2. ...and then #include the following file.

     #ifndef __DEBUG_H #define __DEBUG_H /*****************************************************************************  *                                                                           *  * Byte Craft Limited Code Development Systems                               *  *                                                                           *  *****************************************************************************  *                                                                           *  * Header file information:                                                  *  *                                                                           *  * $ DeviceName:       ALL $                                                 *  * $ Manufacturer:     ALL $                                                 *  * $ Filename:         DEBUG.H $                                             *  * $ HeaderVer:        1.00  $                                               *  * $ Copyright:        2002 $                                                *  * $ Compiler:         ALL $                                                 *  * $ CompilerVer:      ANY $                                                 *  *                                                                           *  * This code may be adapted for any purpose when used                        *  * with any Byte Craft Limited Code Development System.                      *  * No warranty is implied or given as to its usability                       *  * for any purpose.                                                          *  *                                                                           *  * (c) Copyright 2002 Byte Craft Limited                                     *  * 421 King St.N., Waterloo, ON, Canada, N2J 4E4                             *  * VOICE: 1 (519) 888 6911                                                   *  * FAX  : 1 (519) 746 6751                                                   *  * email: support@bytecraft.com                                              *  *                                                                           *  *****************************************************************************/ 
    #pragma option -l;
    /*****************************************************************************  *                                                                           *  * Revision History:                                                         *  * ~~~~~~~~~~~~~~~~~                                                         *  * $ V:1.00  BH  11/12/02 Initial Version                                  $ *  *                                                                           *  *****************************************************************************  *                                                                           *  * Notes:                                                                    *  * ~~~~~~                                                                    *  * These macros provide an easy way to troubleshoot programs with interrupts *  * by displaying different patterns on a chosen port pin.                    *  *                                                                           *  * Instructions:                                                             *  *                                                                           *  * 1. The following symbols must be defined prior to inclusion of this file: *  *       D_IDLE    (Line idle state)                                         *  *       D_DIRPORT (Data direction port for debug pin)                       *  *       D_DIRPIN  (Port pin direction bit in direction register)            *  *       D_SETOUT  (Value to set direction pin for output)                   *  *       D_PORT    (Data port to use for debugging)                          *  *       D_PIN     (Data port bit to use as debug output)                    *  *	                                                                     *  * 2. In the initialization code, use the D_INIT() macro.                    *  *                                                                           *  * 3. D_TAG(n) may be used anywhere debugging output is required             *  *                                                                           *  *****************************************************************************/ #pragma option +l;
     /* Debugging output is enabled */ #ifdef DEBUG_TRACE     /* Check to ensure that the user has defined all necessary symbols */ #if !defined(D_IDLE)    #error D_IDLE (debug pin idle state) must be defined! #elif !defined(D_DIRPORT)    #error D_DIRPORT (debug direction port) must be defined! #elif !defined(D_DIRPIN)    #error D_DIRPIN (debug direction pin) must be defined! #elif !defined(D_SETOUT)    #error D_SETOUT (debug direction pin output state) must be defined! #elif !defined(D_PORT)    #error D_PORT (debug port) must be defined! #elif !defined(D_PIN)    #error D_PIN (debug pin) must be defined! #endif 
    /* Initialization macro that sets direction and initial state of debug pin */ #define D_INIT()  D_DIRPORT.D_DIRPIN=D_SETOUT; D_PORT.D_PIN=D_IDLE 
    /* Preamble and postamble for bit pattern */ #define D_START() D_PORT.D_PIN=D_IDLE^1; D_PORT.D_PIN=D_IDLE #define D_END()   D_PORT.D_PIN=D_IDLE 
    /* Shorten the defined names for a more compact macro */ #define D_PT D_PORT #define D_P  D_PIN 
    #define D_TAG(n) D_START();\                  D_PT.D_P=(n>>7)&1;\                  D_PT.D_P=(n>>6)&1;\                  D_PT.D_P=(n>>5)&1;\                  D_PT.D_P=(n>>4)&1;\                  D_PT.D_P=(n>>3)&1;\                  D_PT.D_P=(n>>2)&1;\                  D_PT.D_P=(n>>1)&1;\                  D_PT.D_P=(n>>0)&1;\                  D_END() 
    /* Debugging output is disabled */				  #else 
    /* Define empty macros for D_INIT and D_TAG(n) so that no code is generated */ #define D_INIT() #define D_TAG(n) 
    #endif /* DEBUG_TRACE */                 				  
    #endif /* __DEBUG_H */ 
  3. Finally, add a call to D_TAG() wherever you want to cause a debugging output to occur on your oscilloscope or similar device. Call D_TAG() with a literal 8-bit integer value.

  4. Compile the software and program your embedded MCU.

    Here is an example of (C6808) generated code with the D_TAG() statement enabled.

     8001 3F FE        CLR             < 3 >  for(i = 0; i < 256; i++)                                               { 8003 3F FD        CLR             < 3 >      for(j = 0; j < 256; j++)                                               { 8005 11 00        BCLR   0,$00       < 4 >      D_TAG(6);
    8007 10 00        BSET   0,$00       < 4 >
    8009 11 00        BCLR   0,$00       < 4 >
    800B 11 00        BCLR   0,$00       < 4 >
    800D 11 00        BCLR   0,$00       < 4 >
    800F 11 00        BCLR   0,$00       < 4 >
    8011 11 00        BCLR   0,$00       < 4 >
    8013 10 00        BSET   0,$00       < 4 >
    8015 10 00        BSET   0,$00       < 4 >
    8017 11 00        BCLR   0,$00       < 4 >
    8019 10 00        BSET   0,$00       < 4 >
    801B 3F FC        CLR             < 3 >      for(k = 0; k < 256; k++)                                                   { 801D AD E1        BSR    $8000       < 4 >          do_something();
                                                      } 801F 3C FC        INC             < 4 >
    8021 20 FA        BRA    $801D       < 3 >
                                                  } 8023 3C FD        INC             < 4 >
    8025 20 DE        BRA    $8005       < 3 >
                                                  } 8027 3C FE        INC             < 4 >
    8029 20 D8        BRA    $8003       < 3 >
  5. Run the device, and 'scope the port pin you specified above. You should see a bit pattern with start, data and stop bits.