20. Documentation

Documentation is conveying to a reader in the future what the designer/programmer was doing and thinking when the software was written. In writing and documenting code, it is important to remember — to roughly quote one of the the great fathers of Computer Science, Donald Knuth — that when you are writing source code, you are not just teaching the computer what to do, but also teaching other programmers how it works, not only users of the API, but also future maintainers of your source code. Comments add information about what you were thinking when the code was written, and why you did things that way — subtleties about the design that cannot be conveyed by the source code alone.

20.1. Qualities of Good Documentation

20.1.1. Orientation Material Comes First

Like any situation where ideas are conveyed from one individual to another, each piece of information presented needs to be able to connect with something the reader already knows. Since your reader may know nothing about what you are presenting (and this may be you 2 years from now when you don’t remember what you built and how it was designed), the most fundamental aspects of your topic need to come first.

Orientation:

Orientation material answers the questions:

  • What is it?

  • What does it do?

  • Why does it exist? (What problem(s) does it solve?)

  • How does it do it? (overview level)

as quickly and succinctly as possible.

If there is any background information that you are assuming the reader already knows, the beginning of the documentation is where to make that assumption clear, so if needed, the reader can go off and study that topic so he knows the terms and tools you will be using and referring to before continuing.

20.1.2. A Thorough Reference Section Is Present

After basics are presented, then you can proceed to the next level of orientation.

Analysis:

Analysis is the breakdown of a complex whole into its logical parts in a way that shows how the sub-parts work together to make the whole. For source-code documentation, this is typically about a subsystem, i.e. a class or module or cluster, but can be scaled up or down as needed to address the topic. For software documentation, it can be broken down into its major features. For other types of documentation, it breaks down into whatever its logical parts are — the major understandings (plural) a master of that topic will need to acquire.

Details:

Finally, all detail about each sub-part should be in one place, in the section where it belongs, i.e. where the reader will look for it. This detail is then referred to (e.g. with hyperlinks) from introductory material or from wherever it is mentioned and the reader might not already know about it. For API users, this detail can be shallow, but for maintainers, they will need to know it from top to bottom.

Because a reader reading the material in sequence needs to be accommodated, the Reference Section normally occupies the latter part of the material. Thus the reader can read the orientation material up to the Reference Section, and then skip about in the Reference Section to answer specific questions, or to thoroughly learn each topic when he needs to, because prerequisite understandings have already been established.

When the reader has a question, he will often first look in the table of contents, skimming through chapter and section names to quickly find where to look. For this reason, the Reference Section needs to be logically organized, with intuitive chapter and section names. The first major section should present details about the “thing” as a complete whole, and if the “thing” is complex, subsequent sections or chapters need to break the “thing” down into its parts in a logical sequence.

20.1.3. Search Mechanism

Finally, when the reader has a question and has not been able to find where to look for the answer by skimming chapter and section names, he needs some other way of finding his answer, that will ultimately be successful, even thought it may require more time investment. The solution to this need is some kind of search mechanism, and to fulfill the “ultimately successful” quality, it needs to be thorough. For printed material, this is an index. For material in electronic form of small-to-medium size, this can be a word or phrase search mechanism. For larger, more complex works, the search mechanism can additionally be accommodated by an index.

Because creating an index is itself a very large undertaking (typically consuming 10-30X the time it takes to read all of the material end to end, depending on how thorough it is), the decision to create one should be made carefully, and it should be undertaken only after the the material is in a very stable state, i.e. the writers have stopped changing it. (See How to Create an Index for more details.)

20.1.4. Learning Principle

The mind is ready to learn what it is asking questions about.

What questions is a reader new to your system or subsystem going to be asking?

After he has learned to use it but is still a beginner, what questions will he be asking?

And once he is an advanced user (and at the various points along the way while getting there), what questions will he be asking?

Good documentation answers your reader’s questions — where feasible — in roughly the same sequence he will ask them, while simultaneously making it easy to find answers to questions that pop up due to the reader’s unique needs and experience.

20.2. Priorities

To support the Purpose of Coding Standards mentioned earlier, when adding or modifying comments in code, these are the priorities are (in order of importance):

clarity:

the ease with which other programmers can understand your intention

readability:

the ease with which other programmers can read your comments (column alignment, tables, lists, diagrams all add to readability)

brevity:

using few words when speaking or writing (the reader’s time is often limited)

20.4. File’s Header Documentation Block

In addition to the above, every source file has (at the top) overview documentation of its own content in a block comment immediately below the copyright block. These are different for .C and .H files and contain Doxygen commands meant to be easily readable directly in the source code file. Note the /** first line is for Doxygen to recognize the comment block as a part of its input. So a space or EOL after the 2nd asterisk is required for Doxygen to recognize it.

-------(sample .H file)-------
/** ***********************************************************************
 * \copyright  Copyright (c) 2011-2024 HM QuickShifter UK, Ltd.
 *      All rights reserved.  Duplication and distribution prohibited
 *      except with explicit written permission.
 *
 * \par  Coding Style:
 *      See Coding Style documentation in README_CODING_STYLE.txt in HM Library.
 *
 * \file
 *
 * \brief   Exported API for PID_Controller.c.
 *
 * \see     For more information, see heading comments in PID_Controller.c.
 *
 * \version  Current revision:  @(#) v1.0  09-Aug-2023 10:29
 * \version  1.0  09-Aug-2023 10:29  vw  - Created.
 *//************************************************************************/
-------(end sample)-------
-------(sample .C file)-------
/** ***********************************************************************
 * \copyright  Copyright (c) 2011-2023 HM QuickShifter UK, Ltd.
 *      All rights reserved.  Duplication and distribution prohibited
 *      except with explicit written permission.
 *
 * \par  Coding Style:
 *      See Coding Style documentation in README_CODING_STYLE.txt in HM Library.
 *
 * \file
 *
 * \brief   PID-Controller storage and operations.
 *
 *
 * ## Peripherals/Resources Used:
 *
 * - CPU time
 * - Memory allocation for PidController_t objects is up to client software.
 *
 *
 * ## PID_Controller Vocabulary:
 *
 *  <dl>
 *  <dt>PID Controller  \anchor  pid_controller_pid_controller_def</dt><dd>
 *      A proportional-integral-derivative controller (PID controller or three-term
 *      ...(definition truncated for brevity)...
 *      \image  html  640px-PID_en.svg.png  "PID Controller&mdash;By Arturo Urquizo - File:PID.svg, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=17633925"
 *      </dd>
 *
 *  <dt>proportional (P term)  \anchor  pid_controller_proportional_def</dt><dd>
 *      In the context of a PID controller, the proportional (P term) is a proportion
 *      ...(definition truncated for brevity)...
 *      </dd>
 *
 *  <dt>integral (I term)  \anchor  pid_controller_integral_def</dt><dd>
 *      In the context of a PID controller, the integral is the <b>SP-PV error</b>
 *      ...(definition truncated for brevity)...
 *      </dd>
 *
 *  <dt>derivative (D term) \anchor  pid_controller_derivative_def</dt><dd>
 *      In the context of a PID controller, the derivative is the current rate of
 *      ...(definition truncated for brevity)...
 *      </dd>
 *  </dl>
 *
 *
 * ## Design:
 *
 *  A.  There is a concept of a PID_Controller object.
 *
 *      1.  It has:
 *          +   ifKp (float) proportional constant.
 *          +   ifKi (float) integral constant.
 *          +   ifKd (float) derivative constant.
 *          +   ifProportionCorrection (float) proportional correction amount, mainly
 *              used for tuning the PID controller via the constants.
 *          +   ifIntegralCorrection (float) proportional correction amount, mainly
 *              used for tuning the PID controller via the constants.
 *          +   ifDerivativeCorrection (float) proportional correction amount, mainly
 *              used for tuning the PID controller via the constants.
 *          +   iiSetValue (int) target process value of the controller (<b>SP</b>).
 *          +   iiProcessValue (float) measured value of the system being controlled (<b>PV</b>).
 *          +   iiError (int) difference between <b>SP</b> and <b>PV</b>.
 *          +   iiPreviousError (int) value of iiError on previous correction iteration.
 *          +   iiIntegral (int) <b>SP-PV error</b> accumulated over time.
 *          +   iiDerivative (int) current rate of change of the measured value (<b>PV</b>).
 *          +   iiCorrectionAmount (int) computed corrective amount to apply to control output.
 *          +   iiMaxCorrectionMagnitude (int) system-configuration value which is used to
 *              clamp correction value to this maximum (+ or -).
 *          +   iiDirection (PidcDirection_t) -1 = down, 0 = neutral, 1 = up.
 *          +   ifxnpApplyCorrection (PidcControlFxnp_t) user-supplied function to apply
 *              correction value.
 *          +   ifxnpGetMeasuredValue (PidcFetchValFxnp_t) user-supplied function to read
 *              and return a new measured value, called once per corrective iteration.
 *          +   iboolForceMinimum1Correction (bool) configuration flag indicating whether
 *              in a system with little inertia, to apply at least a corrective magnitude
 *              of 1 (+1 or -1) when the measured value (<b>PV</b>) does not match set point (<b>SP</b>).
 *      2.  It can be asked:
 *          +   Any of the above.
 *      3.  It can be requested to change PID_Controller objects as follows:
 *          +   PidCtrl_Initialize(&pc, <configuration_values>);
 *              +   Prepares PID Controller for operation in a given system.
 *          +   PidCtrl_EstablishNewSetPoint(&pc, aiNewSetPoint);
 *              +   Stores new set-point (<b>SP</b>) in PID controller struct.
 *          +   PidCtrl_ComputeAndApplyCorrection(&pc);
 *              +   Computes various intermediate values according to configuration.
 *              +   Optionally applies any needed correction by calling user-supplied function.
 *
 * \note  This is a standard PID Controller implementation with 2 additional features:
 *        1.  At initialization, user software can specify requiring a minimum corrective
 *            amount of 1 (+1 or -1) in the event that a corrective amount would not
 *            otherwise be applied.  This is useful for systems with little or no
 *            inertial factors, and speeds up <b>PV</b> reaching <b>SP</b>.
 *        2.  This PID Control implementation supplies a number of internal values that
 *            can be used to tune the PID Controller via the Kp, Ki and Kd constant values,
 *            or otherwise monitor the controller.  These values include:
 *            - ifProportionCorrection
 *            - ifIntegralCorrection
 *            - ifDerivativeCorrection
 *            - iiSetValue
 *            - iiProcessValue
 *            - iiCorrectionAmount, and
 *            - iiDirection
 *
 *
 * ### How to Use:
 *
 * 1.  User software defines storage for 1 or more PidController_t objects like this:
 * \code{.c}
 *         PidController_t  gPidCtrlWaterLevel;
 *         PidController_t  gPidCtrlCruiseControl;
 *         PidController_t  gPidCtrlSteamPressure;
 *         // etc.
 * \endcode
 *     One for each system to be controlled.
 * 2.  User software calls PidCtrl_Initialize(&gPidCtrl..., ...) on each with
 *     configuration values specific to the system being controlled.
 * 3.  User software calls PidCtrl_EstablishNewSetPoint(&gPidCtrl..., aiNewSetPoint)
 *     each time the set point changes.
 * 4.  User software periodically calls PidCtrl_ComputeAndApplyCorrection(&gPidCtrl...)
 *     to apply correction to system to bring the system's measured value (<b>PV</b>) to
 *     match its set point (<b>SP</b>).  The calling interval is generally governed
 *     by the needs of the system, but should generally be equal to or longer than
 *     the time required to for the system to generate a new stable read of <b>PV</b>
 *     (ready through the user-supplied function).
 *
 *
 *
 * \version  Current revision:  @(#) v1.0  09-Aug-2023 09:15
 * \version  1.0  09-Aug-2023 09:15  vw  - Created.
 *//************************************************************************/
-------(end sample)-------

20.5. Domain Vocabulary

In the .C file, if the domain of the .C file has its own vocabulary, all pertinent definitions are included near the top of the heading comments before they are used in design documentation or code. The terms and definitions seem to be best served (considering Doxygen’s output) by using the HTML tags for terms and definitions, like this:

/**
 ...
 * ## S1D13517 Vocabulary:
 *
 * <dl>
 * <dt>Write Window \anchor  write_window_def</dt><dd>
 *     is the single destination rectangle that will receive pixel writes from the host
 *     processor.  There is only one.  It can be anywhere within the \ref frame_buffer_def
 *     "Frame Buffer", including within the \ref s1d13517_buffer_def "Buffer" currently being
 *     displayed.  It is established using the S1D13517_Modify_SetInputMode() function.
 *     </dd>
 *
 * <dt>Display Buffer \anchor  display_buffer_def</dt><dd>
 *     the Buffer currently being displayed, controlled by a single index via one of the
 *     \ref s1d13517_display_setmode_functions "S1D13517_Display_SetMode...() functions".
 *     </dd>
 *
 * <dt>Address \anchor  address_def</dt><dd>
 *     a byte address within SDRAM.  It is up to the user to compute this correctly
 *     so that it lands on an 8-pixel boundary.  Several functions are available to assist
 *     with this task:
 *
 *     S1D13517_qui32SBufferAddress(BYTE aui8ZeroBasedBufferNo),
 *     S1D13517_qui32IntraBufferAddressOffsetFromXY(UINT16 x, UINT16 y),
 *     S1D13517_qui32SBufferXYAddress(BYTE aui8ZeroBasedBufferNo, UINT16 x, UINT16 y).
 *     </dd>
 *
 * <dt>PIP \anchor  pip_def</dt><dd>
 *     Picture-in-Picture.  The S1D13517 has the capability to overlay
 *     the background display image with 0, 1 or 2 PIP windows, without affecting
 *     the contents of the \ref display_buffer_def "Display Buffer".  The functions
 *     that assist with this task all start with
 *     \ref s1d13517_display_setmode_functions "S1D13517_Display_SetMode...()"
 *     and "S1D13517_Display_SetPip...()".
 *     </dd>
 * </dl>
 ...
 */

Doxygen then gives the heading an <h2> font and the terms and definitions a nice, readable layout, each with an HTML anchor so that it can be linked to from other parts of the documentation like this:

\ref  s1d13517_buffer_def  "Buffer"

in line with the rest of the text. Several examples of the use of the \ref command are in the above definitions as well. The names used after the \anchor shall be in lower case, and must be unique within the documentation project. If necessary to guarantee this uniqueness, a short hierarchy of context may be prepended to this name, such as: s1d13517_buffer_def (where “S1D13517” is the chip for which “buffer” has a unique definition, as well as the name of the stem of the file name where the term is defined). Since buffers are ubiquitous in most software and firmware applications, it is easy to see why a name like “buffer_def” is likely to be ambiguous in a large project.

20.6. Source-File Sections

Source files are divided up into sections, and these sections are labelled with “major” headings (lines made with “***”). These sections are:

20.6.1. .H Files

  1. Top section (not labeled) containing #include guard symbols and #include’s.

  2. Macros

  3. Typedefs. Generally typedefs are organized so that any typedefs being used to define other typedefs are defined ABOVE those typedefs where they are used. This is almost always possible, and doing so eliminates the need for any “trickery” as regards custom data type definitions.

  4. Exported External Storage Declarations

  5. Exported Function Prototypes

20.6.2. .C Files

  1. Top section (not labeled) containing #include’s. Note that including the mating .H file FIRST is firm policy for reasons covered in [Lakos1996].

  2. Private Macros & Typedefs (optional). This section is removed from finished source code when empty, which is true about 80% of the time, since these are for macros and typedefs which should not be exported and are intended exclusively for internal use within that .C file.

  3. Storage Definitions

  4. Function Definitions. Generally function definitions are organized so that functions being called are ABOVE functions calling them. This eases the task of declaring prototypes for static (internal-only) functions since usually none are needed, and #include-ing the mating .H first provides prototypes for all the exported functions automatically. This way, in 99% of the cases, NO separate prototypes are needed atop a .C file (a prototype atop a .C file is usually a sign of something not done right, and is often the source of bugs). This means that the “public interface” functions in a .C file are at the bottom, and utility (internal-use-only) functions tend to be at the top.

Since these are not intended to become part of the Doxygen-generated documentation, they do not use the special comment markers that designate Doxygen input blocks. They look like this:

   +---- No space here since this is not a Doxygen comment.
   |
   v
/**************************************************************************
 * Exported Function Prototypes
 ***************************************************************************/

Sequences of things in source files are generally pre-determined and uniform across source code files. Usually the work of creating these sections is done once in template files (separate for .C and .H), which are then used to create all new source files. (Note that in the .H file, the “Configuration” section is nested inside the “Macros” section, and in the .C file, the “Sanity Checks” is nested inside the “Private Macros & Typedefs” section.)

--------------( Template for .H File )-------------
#ifndef POWERTRAIN_H
#define POWERTRAIN_H


#if !defined(HMGENERICTYPES_H)
    #include <hmGenericTypes.h>              /* For generic typedefs. */
    /*                                       ^----(always column 50) */
#endif



/**************************************************************************
 * Macros
 ***************************************************************************/

/* Specify an extension for GCC based compilers */
#if !defined(__EXTENSION)
    #if defined(__GNUC__)
        #define __EXTENSION __extension__
    #else
        #define __EXTENSION
    #endif
#endif


/*-------------------------------------------------------------------------
 * Configuration
 *-------------------------------------------------------------------------*/



/**************************************************************************
 * Typedefs
 ***************************************************************************/



/**************************************************************************
 * Exported External Storage Declarations
 ***************************************************************************/



/**************************************************************************
 * Exported Function Prototypes
 ***************************************************************************/




#endif  /* POWERTRAIN_H */

--------------( End Template )---------------------
--------------( Template for .C File )-------------
#include "PowerTrain.h"                          /* To validate prototypes in .H file, provide relevant data types, etc. */
#include "Tire.h"                                /* For tire_t type. */
/*                                               ^----(always column 50) */
#if !defined(HMASSERTXX_H)
    #include "hmAssertxx.h"                      /* For contract assertions. */
#endif
#if defined(__XC__)                              /* XC compiler */
    #if !defined(__XC_H)                         /* xc.h not already included */
        #include <xc.h>                          /* For processor-specific definitions. */
    #endif
#endif

/* Std C Lib */
#include <stddef.h>                              /* For NULL definition. */



/**************************************************************************
 * Private Macros & Typedefs
 ***************************************************************************/

/*-------------------------------------------------------------------------
 * Sanity Checks
 *-------------------------------------------------------------------------*/



/**************************************************************************
 * Storage Definitions
 ***************************************************************************/



/**************************************************************************
 * Function Definitions
 ***************************************************************************/



--------------( End Template )---------------------

Except for those sections specifically labeled “optional”, these sections are preserved, even when empty, since they may wind up containing something later. This also provides a quick visual reference to know when a section is empty.

Exception for .H files: occasionally a set of #define’s (macros) are meant to ONLY be used with certain typedef’d data types. When this is the case, they are defined WITH the typedef’d data type in the Typedefs section, again for clarity of designer-intended usage in code. This doesn’t happen frequently, but when it does, you’ll know it and will know what to do. Again: the senior policy when there is a choice is to favor readability and understandability.

20.7. The Main C File Documents The Project

In a firmware C project, somewhere there is a main() function that provides the entry point for an application. The file that contains this function definition shall have a header comment block that provides overview documentation for the project — material that someone who has never seen the project before would need to know to become quickly oriented.

At minimum, this comment block includes:

  1. (top) Copyright notice.

  2. (top) Name of the project with the Doxygen mainpage command.

  3. (top) Editor set-up.

  4. (early on) The microcontroller or SoC (system-on-a-chip) in use.

  5. (early on) The tool chain used, the version of the compiler in use and any libraries (this is so that it provides an easy-to-find reference for others who might need to compile the project).

  6. Project configuration.

  7. Project Glossary. Here domain-specific terms are defined where they apply to the whole application, and are not otherwise covered in the documentation of a particular cluster or file.

  8. System description.

    • Purpose

    • Inputs

    • Outputs

    • Features

  9. Storage and all files involved.

  10. Organization of the source code.

  11. A rundown of the tasks (if it is running under a multi-tasking OS, or major independent subsystems if not), and detailed descriptions of the peripherals used by each. This section also includes the flow of data through the application, and where applicable, flow of control.

  12. A cross-reference of peripherals, ordered in groups by peripheral type (minus descriptions since the detailed peripheral-use descriptions are under tasks) and what tasks use them (or what operations use them if not under a multi-tasking OS).

  13. A rundown of the important resources and how they are used (brief if already covered under tasks), such as Queues (Mailboxes under TI-RTOS), Semaphores Interrupts, etc..

  14. Sequence of major mode changes, such as high-power to low-power mode, etc..

  15. Any important checklists for dealing with the software, such as for changing clock speeds (many operations and peripheral settings can require changes). This is early in the documentation sequence because these are used for quick references, whereas main orientation with the application lies below.

  16. Any major whole-application TODO’s such as steps that could be taken to reduce RAM usage, planned refactorings, improvements, etc.

This is carried out in Doxygen by using the \mainpage, \section, \subsection, \subsubsection, \paragraph, and other relevant commands as well as Markdown where needed. Note that these 5 commands automatically create anchors that can be linked to from elsewhere in the documentation using the \ref command, such as \ref index (links to mainpage), \ref section_name, etc..

/** ***********************************************************************
 * \copyright  Copyright (c) 2011-2023 HM QuickShifter UK, Ltd.
 *      All rights reserved.  Duplication and distribution prohibited
 *      except with explicit written permission.
 *
 * \par  Coding Style:
 *      See Coding Style documentation in README_CODING_STYLE.txt in HM Library.
 *
 * \file
 *
 * \brief   This file and its .H counterpart contains all definitions,
 *          storage and operations related to main (top-level) operations.
 *
 * \mainpage  project_title
 * \tableofcontents
 *
 * Set editor as follows:
 *
 *     - Options > Formatting Tab > Language [All Languages] (or at least 'C'):
 *       - Expand Tabs to Spaces [ ] <== UNCHECKED!  (This is not the default for the MPLABX IDE.)
 *       - Tab Size:  4
 *     - Options > On Save Tab > Language [All Languages] (or at least 'C'):
 *       - Reformat:  [None].
 *       - Remove Trailing Whitespace From:  [All Lines].
 *     - Options > On Save Tab > Language [C]:
 *       - [x] Use All Languages Settings.
 *
 * \mainpage [(project title)]
 *   ...
 * \section  mainpage_processor  Processor
 *   ...
 * \section  mainpage_toolchain  Tool Chain
 *   ...
 * \section  mainpage_project_configuration  Project Configuration
 *   ...
 * \section  mainpage_vocabulary  System Glossary:
 *   ...
 * \section  mainpage_system_description  System Description:
 *   ...
 * #### Inputs:
 *   ...
 * #### Outputs:
 *   ...
 * #### Features:
 *   ...
 * \section  mainpage_design  Design:
 *   ...
 * \section  mainpage_files  Files
 *   ...
 * \section  mainpage_peripheral_use_summary  Peripheral Use Summary
 *   ...
 * \section  mainpage_tasks  Tasks
 *   ...
 * \section  mainpage_libraries  Libraries Used
 *   ...
 * \subsection  <subsection-name>  (subsection title)
 *   ...
 * \subsubsection  <subsubsection-name>  (subsubsection title)
 *   ...
 * \paragraph  <subsubsubsection-name>  (subsubsubsection title)
 *   ...
 * Whatever else is needed that documents the project.
 */

20.8. Clusters

See definition of cluster.

Source code in a larger project is organized into “clusters” (subdirectories) of related code. An example of this is all source code files used by a single task in an application running under a multi-tasking OS. This often includes publicly-available function calls that are used by other tasks to send/receive data to/from that task. Another example is a group of classes that are designed to work together as a unit in a subsystem or library.

In such clusters, there is usually one “main” file that coordinates the rest, and when this is not obvious, the file name is usually preceded by “_” to mark it as the main file for the cluster (e.g. _ShiftTask.c). This makes it clear which file is the “coordinating” file for the cluster, as well as causes it and its .H counterpart to sort to the top (or bottom) of the list in a directory listing.

When such files exist, cluster documentation answers, at minimum, how the parts of the cluster are designed to work together. Remember the audience can be someone who has never seen nor heard of that cluster before and is going to need some orientation with how it is designed to interact with or serve the rest of the application, and how it is meant to be used. This documentation can be in one of two places:

  1. For tasks that have a _...Task.c file, it will normally go in that file after the file’s main documentation (comes after the Doxygen \file command in the header comment) using the Doxygen \dir command, to document the directory — the Cluster.

  1. Lacking the file above, it can go in a _DIRNAME_README.md file. Its content may look something like this:

    \dir  tasks\ui
    
    \brief  User Interface Subsystem
    
    \copyright  Copyright (c) 2024 Copyright Owner.  All rights reserved.
                Duplication and distribution prohibited except with explicit
                written permission.
    
    _UITask() handles all interaction with:
    
    +  LCD panel
    
    +  Touch-Screen Detection and Response
    
    +  LEDs
    
    +  All calls to, and management of LVGL graphics library, which is not
       thread safe, but thread safety is guarded by the fact that _UITask() is
       its gatekeeper task.
    

20.9. Doxygen Comments

The most important structs, typedefs, variables and other entities are commented like this in order to be recognized by Doxygen as input:

/**------------------------------------------------------------------------
 * \brief  <brief description>
 *
 * <more details where warranted>
 *//*----------------------------------------------------------------------*/

or

/**
 * \brief  <brief description>
 *
 * <more details where warranted>
 */

Note that there are 2 Doxygen oddities with the first of the above two examples.

  1. The line after the /**. This line causes the effect in the Doxygen-generated documentation that it places a single-line horizontal rule beneath the Brief Description and above the Detailed Description. This actually looks good and makes the text more readable, so we are keeping it for these style of block comments.

  2. Because Doxygen uses a Markdown interpreter in its formatting, if the following line is within the Doxygen comment, it can be interpreted in one of two ways:

    *-------------------------------------------------------------------------*/
    

    If there is text immediately above it, it takes this to mean that the text is a heading and gives it an <h2> style, which does not work as the last line in a comment block.

    If there is a blank line immediately above it, it takes it to mean that a single-line horizontal rule at the end of the text. This too looks out of place.

    So instead we move it outside the Doxygen comment like this:

    *//*----------------------------------------------------------------------*/
    

    This gets Doxygen to stop parsing when it reaches the end of the comment block at the */ at the beginning of the line, and remaining part of the line (/*--- ... ---*/) is merely for human readability within the source code itself.

Other entities that don’t need to be a part of generated documentation are commented with normal block comments like this:

/*-------------------------------------------------------------------------
 * <brief description>
 *
 * <more details where warranted>
 *-------------------------------------------------------------------------*/

or

/* <brief description>
 *
 * <more details where warranted>
 */

or

/* <brief description> */

Again, we try very hard to stay away from C++ style end-of-line comments (// ...) because we like to have the “strict ANSI C” features turned on in our compiler to catch errors we miss, and it does not allow C++ style end-of-line comments.

20.10. Commenting Out Blocks of Code

To comment out large blocks of code, we do it with preprocessor directives for conditional compilation, like this:

#ifdef NOT_USED
/* <reason we are keeping the commented-out source code in the file so
 * someone later knows if/when it is okay to delete it> */

<...unused source code that may have valuable knowledge retained in it, or
may be uncommented later...>

#endif /* NOT_USED */

In the above, the “NOT_USED” name can be anything appropriate that is not a defined symbol.

To NOT comment out large blocks, but merely tell Doxygen not to process it, we do this instead:

/** \cond  some_undefined_symbol */
...section we want to be skipped...
/** \endcond */

Then if the symbol is not defined in the ENABLED_SECTIONS tag in the configuration file, Doxygen will skip that section.

20.11. Function Definitions

Function definitions in .C files (not the prototypes in .H files) have heading comments above them that fully document the functions. This is especially important for exported public interface functions for library code. For internal-use-only (static) functions that are very simple, where placing fully-descriptive comment blocks isn’t really useful, this policy is not strictly enforced.

The work of building such detailed comment blocks is usually done with code templates that can be inserted in code with a few keystrokes.

To support Doxygen documentation generation for such functions, Doxygen commands are included and are meant to keep the comments readable directly in the source code, as well as maximize usefulness of generated documentation.

Example:

/**------------------------------------------------------------------------
 * \brief Set Window
 *
 * This function is used by different higher-level functions to define a window.
 * Specifically, it is called to:
 *   - Establish the \ref s1d13517_write_window_def "Write Window" to load pixels
 *     (to the buffer specified by Input Mode register), or
 *   - to establish location and dimensions where the \ref s1d13517_pip_def "PIP1"
 *     or \ref s1d13517_pip_def "PIP2" window will be overlaid on the current
 *     \ref s1d13517_display_buffer_def "Display Buffer" when the display is drawn.
 *
 * All of these use the same pattern (sequence) of register values, but simply
 * start at different register indexes.
 *
 * This function fulfills a pattern that the S1D13517 uses in defining:
 *   - "Window X Start Position" (uses 1 register to hold bits 9-3 of X coordinate)
 *   - "Window Y Start Position" (uses 2 registers to hold bits 9-0 of Y coordinate)
 *   - "Window X End Position"   (uses 1 register to hold bits 9-3 of X coordinate)
 *   - "Window Y End Position"   (uses 2 registers to hold bits 9-0 of Y coordinate)
 *
 * In each case, 6 registers are populated.  The only variable is which register to
 * start at, which is different for \ref s1d13517_write_window_def "Write Window"
 * and for \ref s1d13517_pip_def "PIP1" and \ref s1d13517_pip_def "PIP2" windows.
 *
 * \param  aui8RegIndex  register index for X Start Position register of the sequence.  Constraints:
 *              - Must be one of the following:
 *                - mS1D13517__SET_WINDOW__WRITE_WINDOW_X_START_POSITION_REG5A,
 *                - mS1D13517__SET_WINDOW__PIP1_WINDOW_X_START_POSITION_REG32,
 *                - mS1D13517__SET_WINDOW__PIP2_WINDOW_X_START_POSITION_REG44.
 * \param  x0  zero-based X coordinate of upper-left pixel of window.  Constraints:
 *              - x0 < x1
 *              - x0 evenly-divisible by 8.
 * \param  y0  zero-based Y coordinate of upper-left pixel of window.  Constraints:
 *              - y0 <= y1 (unlike x0 and x1, y0 CAN equal y1, giving minimum window height of 1)
 * \param  x1  zero-based X coordinate of lower-right pixel of window.  Constraints:
 *              - x0 < x1
 *              - (x1 + 1) evenly-divisible by 8
 *              - x1 <= mS1D13517__CFG__DISPLAY_WIDTH - 1
 * \param  y1  zero-based Y coordinate of lower-right pixel of window.  Constraints:
 *              - y1 <= mS1D13517__CFG__DISPLAY_HEIGHT - 1
 *
 * \pre  require(lcd_ctrlr_io_settings, mS1D13517__IS_SET_FOR_S1D13517());
 * \pre  require(chip_not_selected, mS1D13517__IS_DESELECTED());
 * \pre  Window must be within display buffer.
 * \pre  Minimum window dimensions:  width: 8, height: 1.
 * \pre  Window left edge and width constrained to values evenly divisible by 8.
 *
 * \note  This function requires S1D13517_Display_FlushPendingSettingChanges()
 *        be called after all related settings have been established.
 *//*----------------------------------------------------------------------*/
void           S1D13517_SetWindow(register BYTE aui8Register, register uint16_t x0, register uint16_t y0, register uint16_t x1, register uint16_t y1) {
    /* x1 and y1 have to be ON the lower-right corner of the rectangle -- not just past it! */
    require(lcd_ctrlr_io_settings, mS1D13517__IS_SET_FOR_S1D13517());
    require(chip_not_selected, mS1D13517__IS_DESELECTED());
    ...
}

Example Function-Block-Comment Template:

/**------------------------------------------------------------------------
 * \brief
 *
 * <descr>
 *
 * \param  aui8RegIndex  register index for X Start Position register of the sequence.  Constraints:
 *              - Must be one of the following:
 *                - mS1D13517__SET_WINDOW__WRITE_WINDOW_X_START_POSITION_REG5A,
 *                - mS1D13517__SET_WINDOW__PIP1_WINDOW_X_START_POSITION_REG32,
 *                - mS1D13517__SET_WINDOW__PIP2_WINDOW_X_START_POSITION_REG44.
 * \param  x0  zero-based X coordinate of upper-left pixel of window.  Constraints:
 *              - x0 < x1
 *              - x0 evenly-divisible by 8.
 *
 * \pre  require(lcd_ctrlr_io_settings, mS1D13517__IS_SET_FOR_S1D13517());
 * \pre  require(chip_not_selected, mS1D13517__IS_DESELECTED());
 * \pre  Window must be within display buffer.
 * \pre  Minimum window dimensions:  width: 8, height: 1.
 * \pre  Window left edge and width constrained to values evenly divisible by 8.
 *
 * \note  This function requires S1D13517_Display_FlushPendingSettingChanges()
 *        be called after all related settings have been established.
 *//*----------------------------------------------------------------------*/

20.12. Cross-Reference Pages

Aside from TODO- and BUG lists (which are handled separately by the Doxygen \todo and \bug commands respectively) separate cross-reference pages (e.g. pages with lists of error codes, or anything else) can be generated by simply including, anywhere in a comment block, something that looks like this:

/** \page  error_code_page  My Errors
 *  \brief Errors page
 *
 *  Page content here.
 */

\page <name> (title) is used to create any page that is not directly related to one specific class, file or member.

In Doxygen’s config file (the one or more used to generate your documentation from a body of source code), a line that looks like this creates a “new” Doxygen command called \error:

ALIASES += "error=\xrefitem  error_code_page  \"\"  \"\""

This makes \error into an alias for \xrefitem  error_code_page  ""  "".

After this is defined, the \error command can be used anywhere to build the contents of the cross-reference page like this:

/** \error ERROR 101: in case a file can not be opened.
    Check file system read/write access. */
#define MY_ERR_CANNOT_OPEN_FILE                   101

/** \error ERROR 102: in case a file can not be closed.
    Check file system read/write access. */
#define MY_ERR_CANNOT_CLOSE_FILE                  102

20.13. Doxygen Use Policy

  1. There are a few Structural Commands that we use intentionally:

    \file (mandatory or else no other entities that are found in files get documented).
    
    \mainpage      [(project title)]
    \section       <section-name>        (section title)
    \subsection    <subsection-name>     (subsection title)
    \subsubsection <subsubsection-name>  (subsubsection title)
    \paragraph     <paragraph-name>      (paragraph title)
        We use these to document a whole project (project overview & design).
        The commands '\section' through '\paragraph' can be used to document
        major subsystems.  They equate to HTML '<h2>' through '<h5>' respectively.
    
    \page     <name>  (title)
    \subpage  <name>  ["(text)"]
        We occasionally use these to build an independent page structure that is
        not related to any single entity (file, function, etc.) in the project.
    
    \dir  [<project-unique path fragment>]
        We use this command to document clusters (directories) of related source code,
        i.e. the design rationale of how the parts are meant to work together and how
        they are meant to be used.
    
  2. Structural Commands other than the above (e.g. \fn, \struct, etc.) are not used. These are commands that permit you to document, for example, a function, in a place where the function is not. Since function names, structure names, variable names, typedef names, etc. can and do change frequently, the policy of NOT using such commands avoids a documentation maintenance headache by keeping the documentation of the “thing” (function, struct, variable, typedef, etc.) together with the thing itself.

    The items in the code are documented WHERE THEY ARE in the code. This permits Doxygen’s parsing capabilities to shine, by extracting current data (e.g. function names) directly from the code where they are defined.

  3. Function definitions and storage definitions are documented where they are defined (in the .C file, not the .H file where the prototype or an external declaration is). This does many things, among which is permits the reader to visually verify documentation against code, and vice versa, which directly and indirectly helps to reduce bugs.

  4. A very important thing to understand about Doxygen is that it has several interpreters operating at the same time: it’s own command set (Doxygen commands start with \ or @), HTML, XML commands (a subset of the .NET language XML documentation formatting), and last, but not least: Markdown!

    For all but Markdown, to include formatting commands you have to do so intentionally. But because Markdown is in this list, you can easily cause formatting to occur UNINTENTIONALLY with things like:

    * Design:
    * -------
    *     This application ...
    *     ...
    *
    * \note  This function requires S1D13517_Display_FlushPendingSettingChanges()
    *        be called after all related settings have been established.
    *--------------------------------------------*/
    

    Here, “Design:” is converted to a section heading with <h2> font, the underline removed, and the subsequent text following in that section. In this case the heading does not create an anchor that can be linked to. This may not be what you want.

    But surprise, surprise! The last line of the last paragraph:

    “be called after all related settings have been established.”

    is now also in the large <h2> font, and it’s not a heading! This is most certainly not what you’d want.

    Therefore, a good understanding of Markdown is important before documentation begins in order to save time doing things that don’t work as you would expect.

    Please understand and practice with the details in

    to understand Markdown well. However, the following is a quick summary. Of special importance are when existing documentation contains Markdown in a way that will not cause the expected results.

    ------- or - - - - or ***** or ____ a line by itself without text immediately above it (having at least 3 hyphens, asterisks or underscores) is converted to a horizontal rule.

    This is a level 1 header
    ========================
    
    This is a level 2 header
    ------------------------
    

    (Only 2 ‘=’ or ‘-’ are required to cause this.)

    *italics*  _italics_
    
    **boldface**  __boldface__
    

    In range 3--5 will reproduce the “--” as an en dash.

    Some ideas---especially this one---can be useful, will reproduce the “---” as an em dash.

    Use of Asterisks:

    Special care has to be taken when using *’s in a comment block to start a list or make a rule.

    Doxygen will strip off any leading *’s from the comment before doing Markdown processing. So although the following works fine:

    /** A list:
     *  * item1
     *  * item2
     */
    

    If you remove the leading *’s doxygen will strip the other asterisks as well, making the list disappear! Use of - as a list-item prefix solves this:

    /** A list:
     *  - item1
     *  - item2
     */
    

    Surrounding comment blocks with rules created with *’s or -‘s causes a horizontal rule between Brief- and Detailed descriptions by the top line, and cause the last line to be interpreted as a section heading and given <h2> font! Not what you would expect.

    Paragraphs indented by 1 tab or at least 4 spaces relative to the paragraph above it, will be interpreted as a literal block, and placed in typewriter font and surrounded by a box.

  5. Sometimes a group of functions are related and the group itself is documented by one comment block above them. This type of documentation can be replicated in the generated documentation by heading each of these functions with a Doxygen Special Comment Block like this:

    /** \ingroup  storage_settings */
    

    and then have the comment block that documents all of them contain this:

    /**
     * \defgroup  storage_settings  Storage Settings
     *
     * This group of functions ...<description>...
     * ...possibly detailed documentation about the group, potentially using all
     * types of markup except structural commands...
     */
    

    Or even easier than having an \ingroup command at each function, if the functions are all grouped together in the .C file, have this just below the \defgroup command:

    * @{
    

    and this comment at the end of the group of functions:

    /** @} */
    

    Such groups can also be nested.

    These end up being listed under the topic of “Modules” in the generated documentation. This does not seem to be the right term for such groups, unless you use a rare definition of “Module” but the term seems to be hardcoded inside Doxygen. If I find out otherwise, I’ll change it to something more appropriate. If not, we’ll probably want to make the “special” definition of “Modules” (since it is a prominent tab in the documentation) visible in the documentation itself, like on the main page, to orient the reader to Doxygen-generated format).

Further Reading

Comments