In our last episode, we learned how to create an open source project on Google Code using avrtoolbox as an example [http://code.google.com/p/avrtoolbox]. We also learned how to manage the software versions using TortoiseSVN. This time, we'll look at the design philosophy for avrtoolbox, and a project C coding and documenting style guide. One of the things we intend for avrtoolbox (remember this is a we project — ‘collaborative’ and ‘open’) is that it will be usable by everyone interested in AVR microcontrollers — from the greenest novice to the most experienced professional. Oh yes, we are ambitious!
We want an elementary library with tutorials designed for novices (someone who has never ever used a microcontroller or programmed in C) so they are easy to learn and use just like systems such as the Arduino or the BASIC Stamp. We want to build this elementary library from lower level libraries that can be easily expanded by advanced users who need professional quality AVR C programming tools. These goals may seem at odds — mixing novice and professional libraries in the same project — but they are actually just the ends of a programmer competence spectrum that doesn’t have any formal dividers. All real people are somewhere in between. Oh, and a word of warning. Most of what follows this episode is for avrtoolbox developers, so novices might want to skim a lot of this and consider it a sort of coming attractions featurette – but please do read the last section.
avrtoolbox is a collection of open source educational tools for learning about and using Atmel AVR (eight-bit) microcontrollers with the standard Atmel AVR Toolchain: AVRStudio/WinAVR/avrdude.
Because the AVR architecture varies among sub-families, it would be very difficult to create compiled hardware libraries that could be run on all possible AVR variants. Our initial approach will be to create a single source code repository where all the code will be developed for and tested on three different AVRs available on inexpensive development boards. This will demonstrate concepts for writing code that can be compiled for multiple devices and leave it to the users to follow the methods shown to add additional devices as needed.
The three AVRs we will use are the ATmega169, ATmega328, and ATmega644; we will test our tools on three development boards: Butterfly (ATmega169), Arduino Duemilanove or Uno Boards (ATmega328), and the BeAVR (ATmega644)
For the Arduino board, this system does not use the Arduino IDE nor is it compatible with the existing Arduino libraries. The BeAVR – Breadboard enabled AVR – was shown in the May ‘10 Smiley’s Workshop.
The lower level directory structures will evolve over time, but to begin, the libraries’ directory will have five subdirectories.
libraries – Useful C functions.
The term ‘library’ has a confused use with microcontrollers sometimes referring to source code listings and sometimes referring to object module archives. We hope to avoid confusion by using source library for the former and archive library for the latter.
Archive libraries are the simplest to use. The user needs only to include the library in a project and think of the individual library functions as black-boxes promised to work as described by the API (Application Programmer’s Interface) document. These were discussed in the Jan ‘11 column.
The source library is only intended for use by the avrtoolbox developers. However, the source code is made available to everyone since many folks will prefer to rip off, er, re-use the source in their own project rather than using the archive libraries.
AVR Libraries Directory Structure
The reader should be aware that it is early in the evolution of avrtoolbox and changes will take place over time. However, we have to start somewhere so the planned directory structure with the library names will be shown on the home page of the avrtoolbox project and updated as libraries are completed.
The C programming guide is modified from the avrcore library suggested guide by Ruddick Lawrence at www.nongnu.org/avr-libc/avr-corelib/style_guidelines.html. We will try to use this guide as much as possible since we would like to have our software usable with the avr-corelib project. However, several good (or at least adamant) suggestions from AVRFreaks led to differences. [Link: http://tinyurl.com/2uwjfrq.]
Public Versus Private
We will conceptually divide our code into public and private modules, functions, and variables. The public materials are meant to be used in archive libraries and are documented in the API and the .h file supplied with the library. The private materials are those used to build the libraries and are not intended for casual users. However, the source code is available for advanced users.
Each module will have a short descriptive name such as spi or uart. Short module names is a subjective requirement with the example of spi_init() versus serial_peripheral_interface_initiation().
Public files will use lower_case and will have the module name as a prefix. For example: spi.c and spi.h. Private files will have the first letter of the module in UPPER_CASE: Spi.c.
Public function names and arguments will all use lower_case and have the module name as a prefix: module_init(). Private functions will have the first letter of the module in UPPER_CASE: Module_init(). Module initialization functions will all have _init as a suffix. For example: uart_init(uint32_t baud_rate).
Variable names will all use lower_case. Keep global variables to a minimum and make sure they are well justified and documented.
Data typedefs use lower_case and end in _t (i.e., long_timer_t). Any member variables follow the guidelines for variables specified above.
As a general guideline, code will use BSD Allman [http://tinyurl.com/5tfhav] formatting. Tab characters are allowed, and the suggested setting is four spaces for consistent appearance, especially in the documentation. Use braces for all statements that could have them, even for enclosing a single line. An example from Wikipedia:
while (x == y)
Public constants (in header files) use #define for single values because it will not reserve memory space. Private constants use static const values to limit the scope to the module. Constants will be UPPER_CASE and have the module name as a prefix. For example: #define UART_BAUD 19200.
Whenever possible, use the read/modify/write paradigm to change registers in order to avoid overwriting other parts of the register. This is best done by using standard bitwise operator techniques.
Although C has no “private” definition, any functions and otherwise global variables not meant to be used by client code should be declared static.
Each module’s API is defined in a single header file, named after the module (i.e., uart.h).
Documentation requirements vary with the type of user. A developer will be intimately familiar with the code and not need much other than the code itself. A maintainer should have the same level of programming skill as a developer, but will need information about the software that may not be obvious from reading the raw code. So, he will want in-line comments sufficient to help him quickly understand what the code is doing. A user may just want to use the functions and not care how they were generated, so all he’ll need is an overview of the module and the specifics of how to use each function – what goes in and what comes out. Generally speaking, he doesn’t care what happens in between.
This leads to two conceptually separable types of documentation: in-line comments in the C source for the developer/maintainer, and a separate API document for the casual user. The developer should just use common sense and let the obvious things be self-documenting, while adding comments on things that might not be immediately obvious. The casual user will need a bit more handholding, so we will use Doxygen comments in the public header file to generate the API document and will try to anticipate the real needs of an average user.
A big part of being organized is being able to identify what you’ve got. If you sort through the pile of papers on your desk and find three printed copies of widget.c, you’ll probably want to know which one is the most current so you can toss the other two. SVN provides lots of information attached to the digital file on your PC and in the repository, but none of that information is in the text of the file unless you specifically tell SVN to add it (which is what we will learn in this section).
Select the avrtoolbox directory on your PC and right-click to open the TortoiseSVN menu. Then, select Settings as shown in Figure 1.
FIGURE 1. TortoiseSVN Settings.
Next, click the Edit button as shown in Figure 2.
FIGURE 2. TortoiseSVN Select Edit Configuration.
Finally, the configuration file will open in NotePad as shown in Figure 3.
FIGURE 3. TortoiseSVN Configuration File.
Scroll down to the ### Section for configuring automatic properties: #[auto-props]. Remove the ‘#’ from the [auto-props] item. Next, scroll down and remove the ‘#’ from the *.c and *.h item. Finally, copy the svn:keywords from the *.h item to the *.c item and you should have:
*.c = svn:keywords=Author Date Id Rev URL;svn:eol-style=native
*.h = svn:keywords=Author Date Id Rev URL;svn:eol-style=native
When you commit a *.c file or an *.h file with this block at the beginning of the file, such as:
Repository path: $HeadURL$
Last committed: $Revision$
Last changed by: $Author$
Last changed date: $Date$
TortoiseSVN will substitute something like this:
Repository path: $HeadURL: [url=https://avrtoolbox.googlecode.com/svn/trunk/documentation/joe.c]https://avrtoolbox.googlecode.com/svn/trunk/documentation/joe.c[/url] $
Last committed: $Revision: 54 $
Last changed by: $Author: [email protected] $
Last changed date: $Date: 2011-01-04 16:41:18 -0500 (Tue, 04 Jan 2011) $
ID: $Id: joe.c 54 2011-01-04 21:41:18Z [email protected] $
Now when you look at two files with the same name, you can see which is the most recent.
This license block will follow each title block:
* BSD License
* Copyright (c) YEAR, AUTHOR NAME, All rights
* Redistribution and use in source and binary
* forms, with or without modification, are
* permitted provided that the following
* conditions are met:
* - Redistributions of source code must retain
* the above copyright notice, this list of
* conditions and the following disclaimer.
* - Redistributions in binary form must
* reproduce the above copyright notice,
* this list of conditions and the following
* disclaimer in the documentation and/or
* other materials provided with the
* - Neither the name of the AUTHOR NAME nor
* the names of its contributors may be used
* to endorse or promote products derived
* from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT
* HOLDERS AND CONTRIBUTORS “AS IS” AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
where the YEAR has the year the code was first released and the AUTHOR NAME has the initial author’s name in both locations.
Comments in the source code are there for the experienced programmer, not the casual user who should stick with the API document. Comments that explain the source code are as idiosyncratic as the individual programmer. Too many comments just wastes time; too few jeopardize future maintenance. We will assume that the reader of a C function is a skilled programmer and just wants to quickly be told about anything that isn’t obvious. Novice level comments are to be reserved for the API Doxygen comments in the header file. However, since too few comments are more likely to cause future harm than too many, we recommend that the programmer opt for being verbose. The following function is provided as an example of reasonable in-function comments:
// Set pullups with output high
PORTB |= (1<< MISO_HARDWARE_PIN) \
| (1<< MOSI_HARDWARE_PIN) \
| (1<< SCLK_HARDWARE_PIN) \
| (1<< SS_HARDWARE_PIN);
// Set MOSI, SCK AND SS to outputs
DDRB |= (1<< MOSI_HARDWARE_DDR) \
| (1<< SCLK_HARDWARE_DDR) \
| (1<< SS_HARDWARE_DDR);
// Set Miso to input
DDRB &= ~(1<< MISO_HARDWARE_DDR);
// Enable SPI, Set as master, set clock to
SPCR = ( 1 << SPE ) | ( 1 << MSTR ) |
( 1 << SPR0 );
Doxygen comments will only be used in the public header file that will contain complete Application Programmers Interface information for each public function. This is the only document that the library user needs.
Module Title Block
This block contains a general description of the module, along with whatever API style information seems appropriate for ease of use. This block will always include:
The following example block is from SPI.h:
/* ******************************************* */
\image html avrtoolbox.gif
\mainpage SPI (Serial Peripheral Interface)
\brief This is for single master only
hardware SPI for either 8 or 16-bit
There are three SPI functions that you will
normally use:\n void spi_init_master(void);\n
uint8_t spi_master_rw8(uint8_t to_slave);\n
uint16_t spi_master_rw16(uint16_t to_slave);\n
The user is reminded that SPI reads and
writes in the same operation, for example
8-bits are clocked out to the slave while
8-bits are clocked in from the slave. For the
spi_master_rw8 function an 8-bit byte is
taken as a parameter to send to the slave and
a byte is returned from the slave. The _rw16
function sends and receives 16-bits.
This code was tested on the ATmega169 (AVR
Butterfly, ATmega328 (Arduino), and ATmega644
\todo test it for the ATmega644
\author Joe Pardue
\license New BSD
\date December 1, 2010
/* ****************************************** */
This generates a Doxygen page as shown in Figure 4.
FIGURE 4. spi API document.
Each function declaration in the *.h file will have a preceding block of Doxygen comments. This block will always include:
This block may include:
The following example block is from SPI.h:
/* ******************************************* */
\brief Writes and reads an 8-bit byte via
\return 8-bit byte from the slave device.
\param to_slave - 8-bit byte to send to the
\author Joe Pardue
\date October 29, 2010
/* ******************************************* */
uint8_t spi_master_rw8(uint8_t to_slave)
This lets Doxygen create the API text shown in Figure 5.
FIGURE 5. spi API for a function.
In the above case, stating the obvious that the \param and \return are eight-bit may be overkill, but it is used to differentiate between this function and the nearly identical 16-bit version of the function. These comments will be used by Doxygen to generate the avrtoolbox user manual. That process is explained elsewhere.
I intend for the avrtoolbox to grow into a community-based tool that is not dependent on any individual. That is why I keep emphasizing we. I am taking the lead at the moment by writing this Workshop series and supplying the base code to get things going. Other folks have done similar projects in the past and then disappeared, leaving some really good tools laying around unsupported and with restrictive licenses. I want this project to be so useful that folks will continue to use it and keep it up-to-date even if I get run over by a truck. This will take a community that sees the value of the project and commits to helping out. If you are interested, please start a thread on www.avrfreaks.net and let’s start discussing what avrtoolbox can be and what you can contribute. Also, AVRFreaks is the place to go for questions about avrtoolbox and AVRs in general.
Well, you’ll get no argument from me on that point, since it is on the Internet that IMHO is also a sewer. Nonetheless, the Internet is the best learning tool ever invented despite the stench. Some guy who I suspect was just looking for trouble recently started a thread on AVRFreaks asking if folks would laugh at him if he posted pictures of his project. I responded:
Let’s assume for a moment that you are sincere with your question. My answer is OF COURSE PEOPLE WILL LAUGH AT YOU — THIS IS THE INTERNET! To make matters worse, AVRFreaks is a nearly unmoderated forum. We have folks here who will not only laugh at you, if you give them the opportunity they will give you advice that will kill you if you take it.
The best way to succeed with microcontrollers is to grow a pair, and use the Internet and sites like AVRFreaks using the good and ignoring the bad. At times, it is like panning for gold in a stream of sewage but it is also the best way to get the gold. If you really are afraid of being laughed at on the Internet, you’ll just make your own life harder and the [expletive deleted]s win.
Maybe that is harsh, but the Internet is just too good of a tool to give up over dainty sensitivity. So, I’m hoping some folks will put on their waders and gas masks, and join in the fun. I will generally look at posts on AVRFreaks when the title has avrtoolbox, Butterfly, or Smiley’s Workshop. I may not respond since AVRFreaks has members all around the world and someone usually beats me to providing a good answer. Also, don’t PM me. I participate in a community and public discussion is available to everyone who might have the same question, so I only need to answer it once.
If you just can’t wait and want to get a good leg up on C and the AVR (while helping support your favorite magazine and technical writer), then buy my C Programming book and Butterfly projects kit through the Nuts & Volts webstore. Next time, if all goes well, we will continue with avrtoolbox by writing an elementary serial function library. NV