106
University of Technology, Sydney Faculty of Engineering and Information Technology DEVELOPMENT OF A MODERNISED SOFTWARE PLATFORM FOR AN EDUCATIONAL ROBOT Christopher James Kerr Student Number: 10826598 Project Number: S13-123 Major: Electrical Engineering Supervisor: Dr Peter McLean A 12 Credit Point Project submitted in partial fulfilment of the requirements for the Degree of Bachelor of Engineering 20 June 2014

Capstone Report

Embed Size (px)

Citation preview

Page 1: Capstone Report

University of Technology, Sydney

Faculty of Engineering and Information Technology

DEVELOPMENT OF A MODERNISED SOFTWARE PLATFORM

FOR AN EDUCATIONAL ROBOT

Christopher James Kerr

Student Number: 10826598

Project Number: S13-123

Major: Electrical Engineering

Supervisor: Dr Peter McLean

A 12 Credit Point Project submitted in partial fulfilment of the requirements for

the Degree of Bachelor of Engineering

20 June 2014

Page 2: Capstone Report

Christopher Kerr

ii

STATEMENT OF ORIGINALITY

I, Christopher James Kerr, hereby declare that I am the sole author of this report.

I assert that:

All work shown in the body of this report is my own

All Appendices have been properly attributed to their original author

All theories, ideas, and results of others have been correctly referenced

All sources of assistance have been acknowledged by name in this report

Please be aware that some small samples of code have been reused from a

previous subject, 48434 Embedded Software. This code comprises an

insignificant portion of the finished project.

I must also acknowledge that this project is substantially based on the source

code of the original Maze Rover, developed and supplied by Dr Peter McLean.

Appendix B contains material created in collaboration with other students in

48580 Advanced Control, during the Autumn 2014 semester. They have been

acknowledged at the beginning of that chapter. However, I wish to clarify that the

related work presented in Chapter 7: “Control Implementation” is entirely mine.

Signed: __________________________________________

Christopher James Kerr

Date: 20 June 2014

Page 3: Capstone Report

Christopher Kerr

iii

ABSTRACT

In 48540 Signals and Systems, a robot platform, called the “Maze Rover”, is used

to teach signal and control theory concepts to Electrical Engineering students.

This robot is built upon obsolete technology, and a replacement is under

development.

The current Maze Rover platform is based upon the Freescale MC9S12A512, a 16-

bit fixed-point microcontroller. Whilst this chip is very capable, its current

application as the controller for the “Maze Rover” pushes its capabilities to the

limit, leaving no capacity for future expansion of features.

The new platform is built on a Freescale Kinetis 32-bit microcontroller, which

provides vastly improved performance and a number of additional capabilities.

This project demonstrates the reimplementation of the Maze Rover software on

this new platform, performed in C99 using the Freescale CodeWarrior toolchain.

A literature review of best practices in embedded development was conducted,

leading to the development of an Embedded Software Coding Standard. This

standard has been extensively documented and rigorously adhered to, to

demonstrate the development of high-reliability embedded software.

The core of the project is the development of a Hardware Abstraction Layer

(HAL) for this new platform. The HAL provides serial communications via UART

and SPI, data acquisition, analogue output, and EEPROM emulation. It

demonstrates the application of several interesting features of the new platform,

including the highly configurable hardware SPI implementation and use of the

Direct Memory Access peripheral.

The project also implements the original Maze Rover features, including

modulated wave synthesis, tone detection, and a phase locked loop. These

features have been tested to be functionally identical to the original platform.

Future development may extend this work by implementing a real-time

operating system. The new platform’s improved speed provides ample capacity

to run additional tasks, such as driving an LCD-based Human-Machine Interface

or performing network communications over TCP/IP.

Page 4: Capstone Report

Christopher Kerr

iv

ACKNOWLEDGEMENTS

I would like to acknowledge the support of Dr Peter McLean, whose excellent

teaching has kept me sane over the course of this degree. I must also acknowledge

that this project is substantially based on the source code of the original Maze

Rover, developed and supplied by Dr McLean, and I thank him for this resource.

My friends at UTS are owed thanks for putting up with me, far more than I

deserved! It’s been a long degree and I couldn’t have done it without you.

Finally, I express the utmost gratitude for my family, who have supported me in

more ways than I could possibly count.

ACKNOWLEDGEMENT OF PRIOR WORK

The Analog Interface board used in this project was designed during a previous

capstone project by Rosario Vambuca. Rosario provided the assembled board and

its design schematics for this project.

I must also acknowledge that this project is substantially based on the source

code of the original Maze Rover, developed and supplied by Dr Peter McLean.

Appendix B contains material created in collaboration with other students in

48580 Advanced Control, during the Autumn 2014 semester. They have been

acknowledged at the beginning of that chapter.

Page 5: Capstone Report

Christopher Kerr

v

TABLE OF CONTENTS

Statement of Originality ............................................................................................................... ii

Abstract ............................................................................................................................................ iii

Acknowledgements ....................................................................................................................... iv

List of figures ................................................................................................................................... vi

1 Introduction ............................................................................................................................ 1

2 Embedded C Code Quality ................................................................................................. 5

3 Project Setup ........................................................................................................................ 15

4 Hardware Abstraction Layer ......................................................................................... 25

5 High Level Functionality ................................................................................................. 43

6 Communication Lab Testing .......................................................................................... 49

7 Control Implementation .................................................................................................. 55

8 Conclusions........................................................................................................................... 63

References ...................................................................................................................................... 67

Table of Appendices ................................................................................................................... 69

Page 6: Capstone Report

Christopher Kerr

vi

LIST OF FIGURES

Figure 1: The ModCon Board ..................................................................................................... 1

Figure 2: Freescale Tower System (Freescale, 2012) ...................................................... 2

Figure 3: New Project Options 1 ............................................................................................ 15

Figure 4: Language and Build Tools Options .................................................................... 16

Figure 5: Extract from CodeWarrior Edition Comparison Table .............................. 17

Figure 6: Download Size Limit Error (Styger, 2012) ..................................................... 17

Figure 7: Librarian Model Setting for C99 ......................................................................... 18

Figure 8: Kinetis Clocking Overview (Freescale Semiconductor, 2011) ................ 22

Figure 9: MCG State Diagram .................................................................................................. 23

Figure 10: PUSHR Register Layout ....................................................................................... 28

Figure 11: Analog Interface Board Top Level Schematic (Vambuca, 2013) ......... 29

Figure 12: ModCon Analog Interface Breakout Panel ................................................... 30

Figure 13: AD7609 Serial Timing (Analog Devices, 2013, p. 10) .............................. 31

Figure 14: AD7609 Acquisition Timing (Analog Devices, 2013, p. 9) ..................... 33

Figure 15: AD5754R Serial and Load Timing ................................................................... 34

Figure 16: DAC Input Register Layout (Analog Devices, 2011, p. 26)..................... 34

Figure 17: DAC Input Loading (Analog Devices, 2011, p. 22) .................................... 35

Figure 18: Schematic Extract Showing Reset 0Ω Link (Freescale, 2011) .............. 36

Figure 19: Major and Minor Loops in DMA Requests (Freescale, 2011) ............... 37

Figure 20: Eeprom_Write Logic Flowchart ....................................................................... 41

Figure 21: Serial Protocol Packet Structure ...................................................................... 43

Figure 22: Coherent Demodulation Scheme ..................................................................... 47

Figure 23: SSB-Lower Time Domain .................................................................................... 49

Figure 24: SSB-Lower Frequency Domain ......................................................................... 49

Figure 25: SSB-Upper Time Domain .................................................................................... 50

Figure 26: SSB-Upper Frequency Domain ......................................................................... 50

Figure 27: SSB-HI Time Domain ............................................................................................ 51

Figure 28: SSB-HI Frequency Domain ................................................................................. 51

Figure 29: Voltage Controlled Oscillator Characteristic ............................................... 52

Figure 30: Phase-Locked Loop Operation.......................................................................... 53

Figure 31: Demodulated Message Signal Time Domain ............................................... 54

Figure 32: Demodulated Message Signal Frequency Domain .................................... 54

Figure 33: Quanser MAGLEV Apparatus ............................................................................ 55

Figure 34: Control Module Overview .................................................................................. 56

Figure 35: Non-Inverting Op-amp Buffer ........................................................................... 58

Figure 36 : Controller Architecture ...................................................................................... 59

Figure 37 : Feedforward Current Relationships.............................................................. 60

Figure 38 : Lab Response to Step Input .............................................................................. 61

Figure 39 : Lab Tracking of Triangle Wave Input ........................................................... 62

Figure 40 : Lab Rejection of External Disturbance ......................................................... 62

Page 7: Capstone Report

Christopher Kerr

vii

NOMENCLATURE

ADC Analog to Digital Converter

DAC Digital to Analog Converter

DMA Direct Memory Access

EEPROM A type of Non-Volatile Memory

Flash NAND Flash, a type of Non-Volatile Memory

ISR Interrupt Service Routine

LSB Least Significant Bit/Byte

Maze Rover An educational robotics platform used at UTS

MCG Master Clock Generator

ModCon Modular Controller microcontroller board

MSB Most Significant Bit/Byte

NVM Non-Volatile Memory

PLL Phase-Locked Loop

RAM, SRAM Random Access Memory. Volatile memory type.

RX Receive

SPI Serial Peripheral Interface

TCD Transmission Control Descriptor (for DMA)

TX Transmit

UART Universal Asynchronous Reciever/Transmitter

UTS University of Technology, Sydney

VCO Voltage Controlled Oscillator

Table 1: Project Nomenclature

Page 8: Capstone Report
Page 9: Capstone Report

Chapter 1: Introduction Christopher Kerr Background

1

1 INTRODUCTION

1.1 BACKGROUND

At the University of Technology, Sydney (UTS), engineering is taught with a

practice-based curriculum. Theoretical concepts learnt in lectures are reinforced

by application in the laboratory.

At present a robot platform, called the “Maze Rover”, is used in 48540 “Signals

and Systems” to teach signal theory concepts to Electrical Engineering students.

The core of this device is a microcontroller board called ModCon (MODular

CONtroller), a flexible platform deployed in laboratories for several subjects.

ModCon acts as the “brain” of the robot, controlling its functionality. Whilst in

other subjects ModCon is user-programmable, its use in the Maze Rover is strictly

as an embedded appliance running preloaded firmware.

1.2 PROJECT OVERVIEW

Figure 1: The ModCon Board

The microcontroller used on the ModCon (the large black microchip visible in

Figure 1) is obsolete, and a replacement microcontroller board (ModCon 2.0) has

been developed to modernise the platform. This has prompted the design of a

new revision of the Maze Rover robot (Maze Rover 2.0) built around ModCon 2.0.

This project involves the development of the control software for that

Page 10: Capstone Report

Chapter 1: Introduction Christopher Kerr Technical Outline

2

replacement robot. The purpose of this project is replicate the capabilities of the

current Maze Rover using a development prototype of Maze Rover 2.0. During

this process a methodology for development of high-quality embedded software

will be developed and explored.

1.3 TECHNICAL OUTLINE

The current ModCon platform is based upon an obsolete device, the Freescale

MC9S12A512, a 16-bit fixed-point microcontroller with a 25MHz clock, 512KB of

non-volatile Flash storage, 14KB of RAM and many peripherals and interfaces.

Whilst this chip is very capable, its current application as the controller for the

Maze Rover pushes its capabilities to the limit.

ModCon 2.0 is based on a modern device, the Freescale MK50DX256CLL10. A

member of the Kinetis K50 family, this device uses a 32-bit ARM Cortex-M4 core

clocked at up to 100MHz. The specific model chosen provides 256KB of Flash

storage and 128KB of RAM.

At the commencement of this project, the final ModCon 2.0 hardware was not

available, so development was performed using a Freescale Kinetis

TWR-K70F120M development board. This development board is designed to

integrate into the Freescale Tower System (shown in Figure 2), which provides a

modular system for development of embedded projects. In this project, an Analog

Interface Board developed by a previous student is used as a tower peripheral.

Figure 2: Freescale Tower System (Freescale Semiconductor, 2012)

Page 11: Capstone Report

Chapter 1: Introduction Christopher Kerr Project Goals

3

The TWR-K70F120M module is designed around the high-end Freescale

MK70FN1M0VMJ12. This model provides a number of additional capabilities

when compared to K50 family devices, including a hardware floating-point unit.

For the purposes of developing software for Maze Rover 2.0 the use of these

features has been restricted. Where the development of this project diverges

from the limitations imposed by a K50 family device, a note has been made to aid

future development.

1.4 PROJECT GOALS

The goal of this project is to completely re-implement the functionality of the

existing Maze Rover firmware on a prototype of the ModCon 2.0 platform. Major

functionality units of the Maze Rover firmware (as deployed in Signals and

Systems) to be re-implemented include:

Hardware Abstraction Layer

o SPI and UART interface drivers

o Analogue Inputs and Outputs

o Periodic timers

o Low level functions such as clock generation

Serial Communications Protocol

Discrete Fourier Transform

Phase Locked Loop, including

o Numerically controlled oscillator

Generation of modulated waveforms

Demodulation of acquired waveforms

It also features a Model Reference Adaptive Motor Controller. This advanced

controller is applied transparently, such that the complex transfer function of the

Maze Rover’s motor appears to be a simple first order system when measured

without an external controller. This allows for a relatively simple controller to be

developed by students in “Signals and Systems” using the first order model

developed from the observed system.

As finalised Maze Rover 2.0 hardware was not available during the development

of this project, an alternative control task was implemented on this platform

using Quanser MAGLEV apparatus, demonstrating the viability of ModCon 2.0 for

performing complex control tasks.

Page 12: Capstone Report

Chapter 1: Introduction Christopher Kerr Document Outline

4

1.5 DOCUMENT OUTLINE

Chapter 2: Literature review – Embedded C Code Quality

An overview of research conducted prior to commencing software development

for this project, including a discussion of code quality and its applicability in an

embedded software project.

Chapter 3: Project Setup

Getting started with the Freescale Codewarrior toolchain and understanding the

fundamentals of the Freescale Kinetis architecture.

Chapter 4: Hardware Abstraction Layer

Understanding the hardware of the Maze Rover 2.0 platform, and implementing

drivers for its UART, Periodic Timers and Analog Input/Output systems.

Chapter 5: High Level Functionality

Design and implementation of the Maze Rover 2.0’s application-specific software,

including the ModCon serial protocol and the Signals and Systems

communication functions.

Chapter 6: Communication Lab Testing

Verification of the Maze Rover 2.0’s ability to replicate the communications

functions of the original Maze Rover.

Chapter 7: Control Implementation

Implementation details and test results for a control system implemented using

the prototype Maze Rover 2.0 platform.

Chapter 8: Conclusions

A discussion of the final status of the project. A subsection on further work gives

an outline of the work required to use this project’s results in the Signals and

Systems laboratory, and suggestions for potential future refinements.

Page 13: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr Defining Quality Code

5

2 EMBEDDED C CODE QUALITY

2.1 DEFINING QUALITY CODE

For the purposes of this project, we shall consider quality code to be:

Stable: The software should have no known bugs, and all necessary

precautions must be taken to avoid introducing common errors.

Maintainable: The intent of code must be clear to the reader, and the

code’s purpose in the larger system must be thoroughly documented.

Portable: The code should not make unnecessary assumptions about

the underlying architecture or compiler, to permit porting between systems.

2.2 THE NEED FOR QUALITY CODE IN EMBEDDED SYSTEMS

The embedded environment is fundamentally different from the personal

workstation environment with which most programmers are familiar. The

operation of embedded systems is largely autonomous and unsupervised. In a

workstation environment some kinds of failure are annoying, but acceptable –

programs become unresponsive or crash, but the user is there to restart them.

This is not typically the case for an embedded system, where the user has little

awareness or control over the internal state of the system.

Furthermore, many embedded systems control safety-critical processes, such as

automobiles and industrial automatons. In this environment, unreliable software

can result in injury or death. Unfortunately, this is not hypothetical – in the 1980’s

at least three deaths were inflicted by Therac-25 radiation therapy machines,

which suffered from a race condition in a safety interlock. During the first Gulf

War, 28 US soldiers were killed when a Patriot Missile system failed to destroy a

SCUD missile because of a poorly designed timekeeping system which compared

incompatible data formats. More recently, it has been argued that some of the

“unintended acceleration” issues affecting the Toyota Camry may have been

caused by faulty software and inadequately designed fail-safes.

Page 14: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr The C Programming Language

6

2.3 THE C PROGRAMMING LANGUAGE

The C programming language is a popular choice for embedded software

development. Firstly, and most prosaically, it is often the only high-level language

with a supported toolchain for the target microcontroller architecture.

Conversely, few (if any) architectures support another high-level language but do

not support C. This makes C the de facto choice when code portability is required

or desirable. Secondly, C provides an appropriately minimalist level of

abstraction. The higher level languages currently popular on desktop computers,

such as Java and Python, do not provide good support for fast hardware I/O

operations, whereas C's arrays, structs, and bitfields translate neatly to low-level

manipulation of hardware.

Unfortunately, as a language, the C standard tends towards permissiveness. It is

certainly possible to write code which will compile as valid C despite being

difficult to understand, perverse, or not correctly reflecting the intentions of the

programmer. For instance, C has a system of type promotions which, ultimately,

permits a floating point number to be assigned to a boolean variable.

Furthermore, many edge cases in the C standard are “implementation defined”,

resulting in unpredictable program operation across compilers and

architectures.

Other sections of the language are well defined but prone to programmer error.

A misplaced semicolon after the declaration of a for loop or the test in an if

statement can completely alter the intended logical flow without causing

compilation errors. It is very easy to mistype the assignment operator = in place

of the comparison operator == (or vice versa) and in most cases these

substitutions still produce valid C. The operator precedence rules in C are

particularly notorious for their illogical design – the bitwise operators | and &

have lower rank than the comparison operator ==. This results in errors when

evaluating simple expressions such as:

if (x & y == 0)

In this example, most readers would assume the intended logic to be:

if ((x & y) == 0)

Page 15: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr The C Programming Language

7

However, C’s operator precedence rules have this result:

if (x & (y == 0))

Finally, we should consider that the same feature that originally made C

attractive, its minimal abstraction, can also be a hazard at runtime. C assumes that

programmers know what they are doing, and does not protect against run time

errors such as division by zero or dereferencing of invalid pointers.

Given these limitations of C, it is clear that the language must be applied carefully

when used for embedded systems. Used without restriction, C has features which

can negatively impact the stability, maintainability, and portability of code,

resulting in poor code quality.

A subtle but important consideration is the version of the C language used.

Modern compilers support two main options – ANSI C (commonly known as C89,

and published as ISO/IEC 9899:1990) and C99 (ISO/IEC 9899:1999). C99 was

recently withdrawn by the International Standards Organisation in favour of C11,

a new standard ratified in 2011, but compiler support for this standard is

essentially non-existent at this time.

Until relatively recently it was common to advise against the use of C99 (as of

2004, no commercial compiler supported C99), but it is now well supported by

most C compilers. C99 brings valuable features to the C language, such as:

The ability to mix declarations and code, i.e. to declare a variable at any

point in a function,

The first expression in a for loop may be a declaration, as in C++,

The inline keyword, which hints to the compiler that a function should be

included inline rather than called

Support for a Boolean type,

C++ style // one line comments,

More flexible initialisation of arrays and structs.

The utility of these features is seen as sufficient justification for the use of C99.

Page 16: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr Best Practices in Embedded Development

8

2.4 BEST PRACTICES IN EMBEDDED DEVELOPMENT

2.4.1 LITERATURE REVIEW

In order to develop a suitable coding standard for this project, I conducted a

review of the existing literature on embedded development using the C

programming language.

The following titles should be considered representative rather than exhaustive.

Many alternative coding standards exist.

2.4.2 MISRA-C:2004 (MIRA LIMITED, 2004)

“MISRA-C:2004 – Guidelines for the use of the C language in critical systems” is

primarily interested in addressing the reliability of embedded systems. Published

by the Motor Industry Software Reliability Association for application in the

automotive industry, MISRA-C is a highly restrictive coding standard which

defines a “safe” subset of the C programming language.

MISRA-C recognises the utility of the C programming language, but seeks to

eliminate the insecurities inherent to the language by defining a rule set

amenable to enforcement by static analysis tools.

Rules in MISRA-C are classified as either “Required” or “Advisory”. In order to

claim to be compliant with MISRA-C, all required rules must be followed.

Advisory rules are considered to be less important – it is not necessary to follow

any advisory rule, but it is strongly recommended that they be considered.

Required rules largely deal with eliminating the use of language features whose

behaviour (under C89) is unspecified, undefined, or implementation defined. Use

of such features can result in unintended behaviour, especially when porting

between compilers or architectures. Note that several required rules also

explicitly ban the use of C99 features – these rules are known to be retracted in

MISRA-C:2012, but no copy of that document could be located for review.

Further rules aim to restrict cases where C is considered to be overly permissive.

For instance, it is permissible in C for the same identifier to be used for both a

typedef name and a variable name. This is potentially confusing, so reuse of a

typedef name is banned by Rule 5.3.

Page 17: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr Best Practices in Embedded Development

9

Advisory rules are typically stricter cases of required rules. For instance, where

Rule 5.3 (required) bans the reuse of typedef names, and Rule 5.2 (required) bans

naming reusing a variable name such that a variable in the current scope hides a

variable in outer scope, Advisory Rule 5.7 issues a blanket ban on reuse of any

identifier regardless of namespace or scope.

The vast majority of the recommendations made by MISRA-C:2004 are sensible

and practical, but some specific Rules are considered to be too strict for

practicality in a typical embedded system. For example, Rule 14.7 requires that

functions have a single point of return. Such a rule can significantly complicate

the design of functions which should return early in case of error. Rule 17.1

forbids the use of pointer arithmetic outside of array addressing, a restriction

which is unhelpful when addressing hardware registers known to be at a

particular offset from a peripheral base address.

Portability is addressed by MISRA-C in an indirect but conclusive manner – by

banning the use of all non-standard language extensions and eliminating all

aspects of the language which are poorly defined, portability is guaranteed.

However, considering the thoroughly considered nature of the MISRA-C:2004

rule set, it shall be considered as a definitive reference – any explicit contradiction

of MISRA-C rules shall be documented in the developed coding standard.

2.4.3 BARR GROUP EMBEDDED C CODING STANDARD (BARR, 2013)

The Barr Group standard may be considered as a companion text to

MISRA-C:2004. Where the two documents overlap they are largely compatible –

the only significant difference is the adoption of C99 and its features. However,

the Barr Group standard moves beyond examining language features to concern

itself with style, and thus concerns itself largely with code maintainability.

The author’s hypothesis is that many bugs are introduced by maintenance

programmers, and that these bugs may be reduced by “the disciplined use of

consistent commenting and stylistic practices”. Style is a highly personal aspect

of coding, but I acknowledge that code does not belong to the original author, but

to those future programmers who will maintain it. As such, it follows that there

is value in a strictly codified style to maximise consistency.

Page 18: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr Best Practices in Embedded Development

10

The Barr Group standard is exhaustive in its codification of style. It incorporates

all the usual features of a style guide – naming conventions, rules for the

placement of braces, guidelines for comments and so on – but goes much further,

rigorously defining the acceptable use of whitespace.

This document is much easier to follow than MISRA-C, taking a more colloquial

style. It uses a clear and logical structure, with chapters based on common

language features (Procedures, Variables, Expressions, and so on). It thus serves

as a useful model for this project’s coding standard. This is permitted by the legal

notices of the document, requiring only the inclusion of a particular paragraph

from that section.

I chose to adapt this standard, rather than adopting it wholesale, for three

reasons: firstly, much of the document is concerned with style, and style is highly

personal. I would prefer to enforce my own style, and thus will codify it using this

document as a template. Secondly, there are points of significant disagreement

between this document and the Embedded Software Style Guide discussed in

section 2.4.5, and I am aware that many of my own conventions were strongly

influenced by that document. Adaptation has permitted the reconciliation and

incorporation of useful material from the Embedded Software Style Guide.

Finally, I believe that rewriting the standard has provided a deep familiarity with

the details of the result, superior to that gained by simply examining a document

prepared by another person.

2.4.4 THE ART OF DESIGNING EMBEDDED SYSTEMS (GANSSLE, 2008)

The Art of Designing Embedded Systems concerns itself with the entire process

of embedded software development. It is largely unsuccessful – it covers a wide

variety of topics in scattershot fashion, with topics ranging from debouncing to

team management philosophies. However, it does contain a thorough discussion

of quality code that informed this project, and Appendix A presents a Firmware

Standard.

By comparison to the previous two documents, this standard is dreadful –

imprecise, incomplete, and laden with bizarrely specific advice. Much of it is

copied verbatim from the discussion of code quality in Chapter 3. Jack Ganssle is

Page 19: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr Best Practices in Embedded Development

11

a highly respected embedded software engineer. The high quality of his published

articles and essays led me to review this text, but in review it is clear that this

book is largely assembled from summaries of Ganssle’s previous writing. The

longer form is used here merely to cover more topics rather than discuss topics

in greater depth.

The value in The Art of Designing Embedded Systems is not in its coding standard,

but in its discussions of code quality and particularly encapsulation. In this

context, encapsulation is taken to be the pattern of design which avoids global

variables by preferring C++-style Get and Set functions in the public interface of

a module. This pattern is useful, and not idiomatic in C.

The Art of Designing Embedded Systems is also notable for being the text that

originally drew my attention to the very useful MISRA-C standard reviewed in

section 2.4.2.

2.4.5 48434 EMBEDDED SOFTWARE: SOFTWARE STYLE GUIDE (MCLEAN, 2013)

Peter McLean’s Software Style Guide is included in this review for two reasons.

Firstly, it was my first exposure to a formalised coding standard, and has thus

influenced some of my most fundamental assumptions about coding style.

Secondly, it loosely describes the style used in the existing Maze Rover codebase.

The Software Style Guide was not written until several years after the Maze Rover

firmware, but the common ancestry of their respective styles is clearly apparent.

This style guide is concerned almost entirely with consistency, in the interest of

maintainability. It is less comprehensive than the Barr Group standard, giving

more general guidelines than specific rules. The Software Style Guide is a more

human document, giving guidance as to what a programmer “should” do rather

than forbidding the use of the incorrect version. This is because it is designed for

enforcement by a human reader, rather than a software static analysis tool.

From this document, I have adopted many of the naming convention and code

structure rules, and much of its advice on coding best practices such as scope

limitation (data hiding) and const correctness.

Page 20: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr Best Practices in Embedded Development

12

One notable conflict relates to single-line conditional or loop constructs, such as:

if (y < z)

x = y;

The Software Style guide requires the format shown above, without braces, but

with the following line indented as if it were wrapped in braces. The Barr Group

argues against this style, on the basis that single-line conditional constructs are

often expanded into multi-line conditional blocks. This introduces the potential

for error – the braces may not be added, and the indenting may trick the reading

into believe the braces are present. Instead, they require the following style, in

which even single lines are wrapped with braces:

if (y < z)

x = y;

This has the potential to prevent error, but adds two lines of mostly empty space

to every such construct. It does however have a beneficial effect on

documentation, as it removes the pressure to force a comment to fit in the space

available to the right of the single line.

In the original draft of my coding standard, I had preferred the Software Style

Guide approach, as I believe the more concise form is easier to read. However,

during the development of this project, I became aware of a bug discovered in

Apple Inc.’s SSL implementation, which affected a range of platforms, including

arguably embedded devices such as their iPhone mobile phones.

if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)

goto fail;

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)

goto fail;

goto fail; // << duplicated line

if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)

goto fail;

The bug consists of a single duplicated line, which thanks to the omitted braces is

unconditionally executed unless the preceding test has failed. A goto sends

execution directly to a label named “fail”, bypassing several tests. Worse, the “fail”

label leads to logic which does not always signal failure - it is simply the function

Page 21: Capstone Report

Chapter 2: Embedded C Code Quality Christopher Kerr The Maze Rover Embedded C Coding Standard

13

return, which returns the error code in the err variable. As no error has yet

occurred, this causes the function to prematurely return without error.

Reading about this error (“GotoFail”), and a similar error involving a case

statement found in the OpenSSL codebase (“Heartbleed”) has changed my

position on the inclusion of such braces, and the final revision of my coding

standard requires them unconditionally.

2.5 THE MAZE ROVER EMBEDDED C CODING STANDARD

The resulting document can be reviewed in Appendix A. It contains discussions

of the rationale behind interesting, complex, or unintuitive rules, and has been

footnoted with justifications for diverging from MISRA-C:2004 and the Barr

Group coding standard.

Page 22: Capstone Report
Page 23: Capstone Report

Chapter 3: Project Setup Christopher Kerr Freescale CodeWarrior

15

3 PROJECT SETUP

3.1 FREESCALE CODEWARRIOR

Freescale CodeWarrior Development Studio is an Integrated Development

Environment (IDE) for Freescale microcontroller products. The latest major

version, CodeWarrior 10, takes the form of modular plugins running in the

popular open-source Eclipse IDE. The CodeWarrior IDE provides a complete

environment for development of Kinetis applications; including wizard-driven

project creation, a modern code editor, a Flash programmer, and a well-featured

debugger.

Development for this project was conducted using CodeWarrior 10.4 (later 10.5)

Special Edition, which is available at no cost but imposes a code size limit of

128KB for Kinetis K-series devices. This limit was not significant for this project

– the final optimised executable is well under 20KB, and this includes known

inefficiencies which were not addressed because they did not affect run-time

performance.

3.1.1 STARTING A CODEWARRIOR PROJECT

Starting a new project in CodeWarrior (File>New>Bareboard Project) firsts

prompts for a name and location, then for the target processor (MK70FN1M0 for

the TWR-K70F120M development board), then the programmer/debugger

connection to be used. The development board integrates a P&E USB Multilink

debugger. The next step gives our first meaningful choices – Figure 3 shows the

available options for Floating Point and I/O Support.

Figure 3: New Project Options 1

Page 24: Capstone Report

Chapter 3: Project Setup Christopher Kerr Freescale CodeWarrior

16

The Kinetis K70 used on my development board includes a floating point unit,

but for comparability with the target K50 architecture I used software floating

point emulation in this project.

The I/O support option specifies which version of the Freescale Embedded

Warrior Library is linked into the project. Freescale supply versions of stdio.h

which include basic hardware drivers, permitting easy usage of printf() for

debugging using the UART or Debugger Console. The third option, “No I/O”, does

not link any such drivers. Including stdio.h introduces significant code

complexity and size to the project, and I do not consider it appropriate for an

embedded environment. As such, I did not make use of this feature and selected

the “No I/O” option.

3.1.2 LANGUAGE AND COMPILER – EXPLORING THE USE OF C++

There are two other options in this step of the wizard – Figure 4 shows the

Language and Built Tools options. The final project used the settings shown in

Figure 4 – the C language with the GCC-ARM toolchain – but this was not my

initially preferred configuration.

Figure 4: Language and Build Tools Options

My original project proposal revolved less around code quality and more around

applying modern programming paradigms to the embedded world. To this end, I

had proposed that I would undertake the project using C++ and thus explore the

applicability of Object Oriented Programming to embedded environment.

In the version that was current in July 2013, CodeWarrior 10.4, the Freescale

tools were the default. Thus my first attempt at creating the project used C++ with

Freescale’s own build tools, on the assumption that this would be the most

thoroughly supported configuration.

Page 25: Capstone Report

Chapter 3: Project Setup Christopher Kerr Freescale CodeWarrior

17

Unfortunately, this configuration is not supported in CodeWarrior Special

Edition. Attempting to compile the created project gives the error “C++ is not

supported by the current set of licences and is disabled”. Examining Figure 5 it

can be seen that only the Professional Edition of CodeWarrior supports the use

of C++.

Figure 5: Extract from CodeWarrior Edition Comparison Table

However, the project creation wizard specifically presents GCC as an option, and

GCC is an open-source C++ compiler. Recreating the project using C++ with GCC

initially appeared successful – the project would compile without error. However,

trying to launch the project in the debugger would result in an error similar to

that shown in Figure 6 – “Download size limit has been exceeded. Please check

your license”. This was clearly nonsensical, as the empty application compiles to

less than 6KB.

Figure 6: Download Size Limit Error (Styger, 2012)

Investigation ultimately lead me to a blog entry by Erich Stryger (2012) which

confirmed that the debugger is capable of detecting the use of C++, resulting in

the error message shown when no C++ permitting license is found. I confirmed

this hypothesis by attempting debugging using the time-limited CodeWarrior

Professional Evaluation version. The only way to use C++ with CodeWarrior is to

purchase a licence for the professional edition. This option was discussed with

Dr Peter McLean and deemed unworkable due to the costs – Freescale does not

offer an academic discount (recommending the use of the free Special Edition).

and a one person-year license for CodeWarrior Professional costs US$1,995.00.

Page 26: Capstone Report

Chapter 3: Project Setup Christopher Kerr Freescale CodeWarrior

18

Whilst attempting to work around this issue I had already begun to write code

using GCC conventions, thus ultimately resulting in my use of the GCC compiler

even after switching to the C language.

3.1.3 USING C99

As discussed in Chapter 2.3 (The C Programming Language), it is desirable to use

the latest supported version of ISO C. CodeWarrior, the Embedded Warrior

Library, and GCC support the use of C99, but it is not the default and no option is

given during project creation.

If I attempt to make use of a C99 construction on an unmodified project, errors

result – for instance, including stdbool.h and attempting to define a bool results

in the error “unknown type name ‘bool’” and the warning “EWL support for C99

is not enabled”. Examining stdbool.h shows this construction:

#if !_EWL_C99

#warning "EWL support for C99 is not enabled"

The obvious solution to this issue, and the one I used for most of the development,

is to simply redefine the _EWL_C99 symbol prior to the #include for any C99

headers, like so:

#if !(_EWL_C99)

#undef _EWL_C99

#define _EWL_C99 (1)

#endif

#include <stdbool.h>

However, late in development I learnt that there is a more correct method. In the

Project Properties -> C/C++ Build -> Settings pane, under the Tool Settings tab,

there is a folder in the navigation tree named “Librarian”. Changing the Model

setting from “ewl_noio” to “c9x_noio” results in the correct symbol definition,

thus compiling the included C99 headers in the correct mode.

Figure 7: Librarian Model Setting for C99

Page 27: Capstone Report

Chapter 3: Project Setup Christopher Kerr Using the Kinetis Platform

19

3.2 USING THE KINETIS PLATFORM

3.2.1 DEVELOPMENT ON FREESCALE KINETIS

Freescale offer two options for rapid development on the Kinetis platform.

Processor Expert

Freescale Processor Expert is a GUI-driven automatic code generation tool which

is tightly integrated with Freescale CodeWarrior. Processor Expert has a database

of all peripherals available on the target microprocessor. Users add “Embedded

Components” to their project. Components provide a modular object-oriented

approach to embedded development, with each Component encapsulating the

functionality of an on-chip peripheral, popular external peripheral (such as an

LCD display), or pure software algorithm.

The cost for this simplicity and modularity is reduced flexibility. Processor Expert

generates an event-driven application framework, and the user must adapt their

process to suit. As Processor Expert ultimately generates C code for compilation,

I also considered using Processor Expert to produce driver code for integration

into my own application. However, inspection of the generated code showed that

this was not feasible – the generated code was tightly coupled with the Processor

Expert environment, and would have required substantial rework to function

independently.

Bare-Metal Sample Code

Freescale provide a package of Bare-Metal sample code for the K70 platform.1

This package is interesting, because it contains a number of driver libraries for

K70 peripherals. However, no documentation (beyond the sample programs) is

supplied for these libraries, and I have been unable to locate a version-controlled

copy of this resource.

Ultimately I preferred to use these libraries as a reference source, alongside the

Kinetis Quick Reference User Guide (Freescale Semiconductor, 2012), to develop

my own drivers which meet my needs and conform to my own quality standards.

1 Download (Freescale login required) from https://www.freescale.com/webapp/Download?colCode=KINETIS_120MHZ_SC

Page 28: Capstone Report

Chapter 3: Project Setup Christopher Kerr Using the Kinetis Platform

20

3.2.2 INTERRUPTS

Kinetis microcontrollers are designed around an ARM Cortex-M4 CPU, which

provides very powerful and flexible interrupt management through its Nested

Vectored Interrupt Controller (NVIC). It has a number of interesting features,

including the ability to dynamically allocate interrupt priorities, and interrupt-

tail chaining, which permits the next pending interrupt to be rapidly entered

without performing a complete pop/push sequence on the stack.

The NVIC Vector Table

Unlike most other architectures, Interrupt Service Routines (ISRs) on ARM

Cortex-M based devices are not special. An ISR may be any function which takes

no parameters and has return type void.

On the MC9S12 microcontroller used by the original ModCon, interrupts are

defined using the non-ANSI keyword interrupt followed by the interrupt vector

number of the peripheral. This tells the compiler that the function is a special

interrupt service routine, and to place a call to this function in the corresponding

position of the vector table.

void interrupt 26 PeriodicTimer_ISR(void)

On a Kinetis microcontroller, the ISR function is not special. ISR’s are connected

to their respective vector by placing a function pointer in the vector table, which

is located at <projectDir>\Project_Settings\Startup_Code\kinetis_sysinit.c.

void (* const InterruptVector[])()

__attribute__ ((section(".vectortable"))) =

/* Processor exceptions */

(void(*)(void)) &_estack, // 0 Initial Stack Pointer

__thumb_startup, // 1 Initial Program Counter

/* and so on up to vector 15 */

/* Interrupts */

Analog_ADC_RxComplete_ISR, // 16 0 DMA Channel 0

Analog_DAC_TxComplete_ISR, // 17 1 DMA Channel 1

// and so on up to vector 121

The non-standard attribute __attribute__ ((section(".vectortable"))) directs

the linker to place this table at an address defined by the linker map. The

CodeWarrior-generated Kinetis boilerplate initialises the NVIC with this address.

Page 29: Capstone Report

Chapter 3: Project Setup Christopher Kerr Using the Kinetis Platform

21

Interrupt Priority and the BASEPRI Register

The NVIC provides the capacity to set interrupt priorities, thus determining the

order in which interrupts are handled if they queue during the execution of

another ISR.

There are 16 possible priority levels, with 0 the highest priority, and 15 the

lowest. CPU_SetIrqPriority() exposes this functionality, taking as parameters an

IRQ number and a priority from 0-15. Note that Kinetis interrupts have both a

vector number and an IRQ. The first peripheral interrupt vector is 16,

corresponding to an IRQ of 0. Other IRQs may be calculated in this way by

subtracting 16 from the vector number.

The NVIC provides the capacity to “mask” execution of interrupts below a certain

priority level whilst preserving execution of higher priorities. This feature is

enabled by writing to the BASEPRI register. When BASEPRI is equal to 0, all

interrupts are executed. When BASEPRI is non-zero, interrupts with priorities

lower than or equal to BASEPRI are masked. Thus, when BASEPRI is equal to 1,

only interrupts of the highest priority will execute. Conversely, when BASEPRI is

equal to 15, all interrupts except the lowest priority will execute.

Enabling and Disabling Interrupts

Interrupts are enabled by writing a 1 to the relevant bit in the corresponding

NVIC Interrupt Set Enabled Register (NVICISER), and disabled by writing a 1 in

the corresponding NVIC Interrupt Clear Enabled Register (NVICICER). Before

enabling an interrupt, the pending flag for that interrupt should be cleared. This

can be achieved by writing 1 to the relevant bit of the corresponding NVIC

Interrupt Clear Pending Register (NVICICPR).

This functionality is handled by CPU_EnableIRQ() and CPU_DisableIRQ().

Critical Sections

Critical section inline-functions are defined which simply enable and disable

interrupts, because it is known that interrupts are never otherwise disabled in

this project. A more refined approach could make use of interrupt priorities and

the BASEPRI mask, but care must be taken to truly disable interrupts when it is

required (an example is seen in section 4.5.3).

Page 30: Capstone Report

Chapter 3: Project Setup Christopher Kerr Using the Kinetis Platform

22

3.2.3 CLOCKING

Freescale Kinetis microcontrollers have an extremely complex clocking system,

an overview of which is shown in Figure 8 below.

Figure 8: Kinetis Clocking Overview (Freescale Semiconductor, 2011, p. 214)

The Master Clock Generator (MCG) module controls clock generation. The System

Integration Module (SIM) controls clock dividers and clock gating.

Main Clocks

Ultimately, there are four main output clocks, derived from MCGOUTCLK via the

dividers OUTDIV1 – OUTDIV4 respectively:

Core / System Clock: The ARM Cortex-M4 CPU core clock (max 150 MHz)

Bus Clock: The source of most peripheral clocks (max 75 MHz)

FlexBus Clock: Clock for the external FlexBus (max 50 MHz)

Flash Clock: Clock for the program flash memory (max 25 MHz)

An interface for setting these dividers is exposed by MCG_SetClockDividers().

Additionally, many peripherals can be clocked directly from the MCG – see the

outputs on the right hand side of Figure 8, which provide access to the output of

the Oscillators, PLLs, FLL, and the internal reference clocks.

Clock Gating

All module clocks are disabled after reset. Before any peripheral can be used, it is

necessary to enable the clock to its module by setting the appropriate bit in the

corresponding SCGCx register.

Page 31: Capstone Report

Chapter 3: Project Setup Christopher Kerr Using the Kinetis Platform

23

Generating MCGOUTCLK

After reset, MCGOUTCLK is equal to 20.97MHz, sourced from a Frequency Locked

Loop driven by the internal 32.768 kHz clock (FEI mode). The maximum 2

supported clock speed for the microprocessor on the TWR-K70F120M is

120MHz. To achieve this frequency, it is necessary to transition the MCG to source

MCGOUTCLK from a Phase Locked Loop (PLL) driven by an external 50 MHz

oscillator (PEE mode).

Figure 9: MCG State Diagram

It can be seen in Figure 9 than in order to transition from FEI mode to PEE mode,

it is necessary to pass through a minimum of two intermediate states.

To enter FLL Bypassed External (FBE) mode, the MCG is configured to source

MCGOUTCLK directly, from either an external clock source or an internal

oscillator driven by an external crystal. The FLL remains locked, but neither the

FLL nor PLL output is used. This mode is used to transition the MCG from an

internal clock source to an external one.

To transition to PLL Bypassed External (PBE) mode, we configure and lock the

PLL, but leave its output unused. This involves configuring the appropriate

dividers, and this functionality is handled by MCG_calculatePLLDividers(). The

MCGOUTCLK is still sourced directly from the external reference.

Finally, the MCG is configured to use the now-configured PLL as the source for

MCGOUTCLK, transitioning the system to full-speed PLL Engaged External mode.

2 150 MHz core clock is unofficially supported within a reduced range of operating temperatures, but the part used on the TWR-K70F120M is specified for use at 120 MHz.

Page 32: Capstone Report
Page 33: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Periodic Timers

25

4 HARDWARE ABSTRACTION LAYER

4.1 PERIODIC TIMERS

The Freescale Kinetis platform provides the Periodic Interrupt Timer (PIT)

module for implementing periodic timers. There are 4 independent PIT channels

available on the MK70FN1M0, each of which has its own interrupt vector.

The PIT channels are clocked from the Bus Clock. The value given by the LDVALn

register is loaded as the start value, then decremented by 1 on each clock tick

until it reaches 0. At zero, an interrupt is asserted and the value is reloaded.

To use the module, it must first be enabled. This involves enabling the clock to

the module by writing a 1 to the relevant bit in the SIM_SCGC6 register, then

clearing the Module Disabled flag in the PIT_MCR register. This functionality is

handled by PTimer_Setup().

Configuring a timer channel is then a three step process. First, the timer period is

set using PTimer_SetPeriod(). This function takes as input a timer channel and a

value to write to the register PIT_LDVALn. To minimise inter-module coupling,

calculating the required value is left to the user. To generate interrupts at a

frequency Fint Hz:

𝐿𝐷𝑉𝐴𝐿 =𝐵𝑢𝑠 𝐶𝑙𝑜𝑐𝑘 (𝐻𝑧)

𝐹𝑖𝑛𝑡 (𝐻𝑧)

To perform this calculation at runtime, the current Bus Clock may be retrieved

using MCG_GetClocks().

Once the period is set, the PIT channel’s interrupt may optionally be enabled

using PTimer_InterruptSetup(). This step is not required if the PIT is being used

to trigger a DMA event.

Finally, the timer may be started with PTimer_Start(). A running timer may be

halted using PTimer_Halt(). Note that the downcounter is reset when the timer is

started, restarting the timer period. If the period is changed while the timer is

running, the new period will take effect in the next timer cycle.

Page 34: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Serial Communications Interface

26

4.2 SERIAL COMMUNICATIONS INTERFACE

4.2.1 UNIVERSAL ASYNCHRONOUS RECEIVER/TRANSMITTER (UART)

The UART peripheral is used to provide communications with a PC. UART2 on the

TWR-K70F120M is connected to the USB debugging interface, which enumerates

on the PC side as a USB Communications Device Class (CDC) device. This behaves

like a COM port in essentially all respects.

Setup of the UART peripheral is performed in UART_Init(). This driver exposes

only one parameter – the target baud rate. All other configurable settings remain

at the default configuration: 8 data bits, 1 stop bit, and no parity.

Baud Rate Calculation

In order to successfully communicate between a pair of asynchronous

receiver/transmitters, the baud clock on each device must match to a high degree

of accuracy. Any inaccuracy will result in the receiver sampling its input out of

synchronisation with the transmitter, resulting in framing errors.

In order to facilitate the required degree of accuracy whilst accommodating the

very flexible Kinetis clocking system, the UART peripheral uses a 13 bit modulus

counter (SBR) and 5 bit fractional counter (BRFD) to divide the incoming module

clock down to the desired baud rate:

𝐵𝑎𝑢𝑑 𝑅𝑎𝑡𝑒 = 𝑀𝑜𝑑𝑢𝑙𝑒 𝐶𝑙𝑜𝑐𝑘

(16 ∗ (𝑆𝐵𝑅 + 𝐵𝑅𝐹𝐷))

To implement this, UART_Init() first calculates SBR, the integer part of the divisor,

by dividing the module clock by 16 times the desired baud rate (the receiver is

run with 16 times oversampling).

uint16_t calcSbr = (uint16_t) ((moduleClkHz) /

(targetBaudRate * k_oversamplingRate));

As a 5-bit register, BRFD stores a value from 0-31, representing the numerator

of a fraction with denominator 32.

(moduleClkHz * 32) / (targetBaudRate * 16) gives an accurate total divisor for

32 times the required baud rate. Subtracting 32 times the calculated integer SBR

then leaves the required fractional numerator, BRFD.

Page 35: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Serial Communications Interface

27

4.2.2 INTERRUPT-DRIVEN SCI

The UART peripheral can generate interrupts upon completion of transmission

or reception of a byte. This permits the implementation of an interrupt-driven

Serial Communication Interface (SCI). Rather than polling the UART’s status flags,

transmission and reception can occur asynchronously in an interrupt service

routine.

Buffers: First-In, First-Out (FIFO)

Information needs to be sent and received by the Maze Rover asynchronously.

The simplest way to meet this requirement is the addition of send and receive

buffers. Simple First-In, First-Out (FIFO) buffers are implemented by fifo.c.

In this configuration, the reception of data by the UART triggers the UART ISR. If

the Received Data Ready flag is set, the data in the UART data register is stored in

the Receive FIFO. This data can later be retrieved by SCI0_InChar();

When data is ready for transmission, it is added to the Transmit FIFO. If no

transmission is already in progress, the UART’s Transmission Complete interrupt

is enabled. This will immediately trigger an interrupt, as the Transmit Data

Register Empty flag will test true. Data is retrieved from the Transmit FIFO and

placed in the UART’s data register, beginning a transmission. As each

transmission completes, the ISR will be triggered again. If data is successfully

retrieved from the Transmit FIFO, the cycle continues. When no data remains in

the Transmit FIFO, the Transmission Complete interrupt is disabled.

Page 36: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Serial Peripheral Interface (SPI)

28

4.3 SERIAL PERIPHERAL INTERFACE (SPI)

4.3.1 SPI BACKGROUND

Serial Peripheral Interface (SPI) is a full-duplex synchronous serial bus. In this

project, it is used to communicate with the external DAC and ADC peripherals

supplied by the Analog Interface Board.

The Freescale Kinetis DSPI peripheral is very configurable, and offers support

both interrupt-driven and Direct Memory Access usage models. These are

discussed further in Section 4.4: “Analog Interface Board”.

4.3.2 SPI IMPLEMENTATION

Because of the divergent protocol requirements of the DAC and ADC (see Section

4.4), no attempt was made to produce a generalised SPI driver. Instead, spi.c

provides support functions for managing SPI data frames.

The DSPI uses an interesting configuration scheme, in which many configuration

variables are duplicated between the CTARn (Clock and Transfer Attributes)

registers. This allows the module to be reconfigured on a frame by frame basis.

When queuing a frame for transmission, half of the 32-bit PUSHR register is

reserved for configuration bits, including which set of Clock and Transfer

Attributes is to be used for the transmission of that frame (see Figure 10).

Figure 10: PUSHR Register Layout

The function SPI_MakeTxFrame() has been written to assist in handling this data

structure. It has the definition:

uint32_t SPI_MakeTxFrame(uint16_t txData, tSPI_holdPCS holdPCS,

tSPI_CTAR transferSettings, tSPI_EOQ endOfQueue,

tSPI_clearCnt clearCnt, uint8_t assertPCS)

It takes data and configuration settings, and returns a uint32_t which is properly

formatted for assignment to the PUSHR register. This function is as generalised as

can be useful in this project, generating transmission frames which are applicable

to any implementation whether it uses flag polling, interrupts, or DMA.

Page 37: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

29

4.4 ANALOG INTERFACE BOARD

4.4.1 OVERVIEW

Figure 11: Analog Interface Board Top Level Schematic (Vambuca, 2013)

The ModCon 2.0 Analog Interface Board is a module for the Freescale Tower

System providing high-quality analog-to-digital (data acquisition) and digital-to-

analogue (data distribution) capabilities. It was designed during a previous

capstone project by Rosario Vambuca, who provided me with an assembled

prototype unit for use in this project.

Data Acquisition

Analog data acquisition is provided by an Analog Devices AD7609 ADC connected

to a SPI interface. This part has the following features:

Eight simultaneously sampled true differential inputs

18 bit resolution

Acquisition rate up to 200,000 samples per second

Data Distribution

Analog data distribution is provided by an Analog Devices AD5754R DAC

connected to a SPI interface. This part has the following features:

Four single-ended channels

Programmable output range (+5V, +10V, +10.8V, ±5V, ±10V, ±10.8V)

16 bit resolution (pin-compatible with 12 bit AD5724R and 14 bit AD5734R)

Page 38: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

30

4.4.2 PHYSICAL INTERFACE

The ModCon 2.0 Analog Interface Board was designed to interface to the same

breakout panel as used with the original ModCon in Embedded Software and the

control laboratory. It connects via a 26 pin header cable, carrying signals for 8

differential inputs, 4 single-ended outputs, and ±12V power rails. The remaining

4 pins are connected to analog ground. The breakout panel permits connection of

these signals via standard 4mm banana jacks as seen in Figure 12.

Figure 12: ModCon Analog Interface Breakout Panel

The Analog Interface Board was supplied assembled. I assumed that it had been

demonstrated functional during Rosario’s capstone, but was later informed that

only the power supply had been tested. Initial tests of the Analog-to-Digital

converter revealed an issue – the signals I had connected at the breakout panel

were not present at the ADC input pins. Further investigation showed that the

pinout of the ribbon cable was flipped at the breakout panel. Rosario had copied

the PCB footprint used on the original ModCon Analog Interface, which has the

header soldered on the underside of the board, but populated his board with the

header on the upper side.

IDC header connectors are designed such that you cannot flip the pinout along

either axis by manipulating the connector’s orientation – it is possible to produce

a cable equivalent to having connected Pin 1 to Pin 26, Pin 2 to 25 (i.e. rotated

180 degrees) but not one where Pin 1 connects to Pin 2 and vice versa. The

problem was instead resolved by desoldering the header and installing a new

header on the underside of the board (thanks to Russell Nicholson for his

assistance with the desoldering process).

Page 39: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

31

4.4.3 ANALOG TO DIGITAL CONVERTER

As discussed in Section 4.3.2, the DSPI peripheral duplicates the Clock and

Transfer Attributes register, providing CTAR0 and CTAR1. This is convenient,

because the Analog Interface Board has two SPI peripherals, each with its own

format and timing requirements. CTAR0 is configured for use with the AD7609

Analog to Digital Converter.

Handling the Frame Size

AD7609’s serial protocol uses an 18 bit SPI frame, but in master mode the Kinetis

DSPI peripheral only supports frames up to 16 bits. To accommodate this

requirement, I emulate an 18 bit frame by using two 9 bit “sub-frames”.

Figure 13: AD7609 Serial Timing (Analog Devices, 2013, p. 10)

The timing requirements of the AD7609 are shown in Figure 13. It can be seen

that /CS must remain low for the duration of the 18 bit transfer. This can be

achieved using the Continuous Peripheral Chip Select Enable (CONT) flag, which

is located in the PUSHR register and thus may be set independently for each

transmission.

On the first sub-frame, the CONT flag is set. This tells the DSPI to keep the chip

select signal asserted after the transfer completes. On the second sub-frame the

CONT flag is unset, and the chip select returns to the idle state once the transfer

completes.

Baud Rate

The AD7609 is specified for an SCLK frequency of 15MHz when configured with

Vdrive = 3.3V as on the Analog Interface Board.

Page 40: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

32

The DSPI peripheral is clocked from the Bus Clock3 (60 MHz). The DSPI baud rate

is set by:

𝑆𝐶𝐾 𝑏𝑎𝑢𝑑 𝑟𝑎𝑡𝑒 = (𝑓𝑆𝑌𝑆

𝑃𝐵𝑅) ∗

1 + 𝐷𝐵𝑅

𝐵𝑅

Where:

PBR is the baud rate prescaler.

Valid values correspond to dividers of 1

2,

1

3,

1

5, and

1

7

DBR is a flag which sets double baud rate mode

BR is the baud rate scaler.

Valid values are 1

2𝑛 from ½ through

1

65536

To achieve 15MHz, we need a total scaling factor of 15

60 =

1

4 , which can be

achieved by setting PBR to ½ and BR to ½, with double baud rate mode disabled.

However, in testing this configuration was found to be unreliable – the ADC

shifted data too slowly, resulting in only 17 bits of data being received. The next

fastest normal baud rate is 10Mhz (PBR = 1

3 , BR = ½), but it is possible to achieve

12MHz by enabling double baud rate mode (DBR = 1, PBR = 1

5 , BR = ½). This

configuration was reliable in testing.

Note that using double baud rate (DBR) mode can result in an asymmetric clock

waveform. This setup gives the worst-case result of 60% high, 40% low.

However, in this case the asymmetry is beneficial. The issue observed at higher

clock rates was that the clock high time was too short for the data to be shifted

out before the sample was taken on the falling edge of the clock. Using DBR gives

a high time roughly equivalent to a 10MHz clock whilst maintaining 12MBit/s

throughput, so transfer times are extended by only 25% compared to the 15MHz

theoretical maximum.

3 The reference manual claims that this module is clocked from the System Clock. This is incorrect.

Page 41: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

33

Performing an Analog to Digital Conversion

Figure 14: AD7609 Acquisition Timing (Analog Devices, 2013, p. 9)

The timing of a data conversion is shown in Figure 14. To begin a conversion, the

CONVST4 pin is brought high until the BUSY pin is seen to go high in response.

The chip reacts only to the rising edge of CONVST, so it is permissible for CONVST

to remain high throughout the conversion process. The conversion is complete

on the falling edge of the BUSY line, and can now be read out by supplying up to

8 frames of 18 clock cycles on the SPI bus. Channels must be read sequentially,

but if less channels are required it is permissible to send only the required

number of frames – after a new conversion is performed, the first data shifted

onto the SPI bus is always channel 1.

Implementation Details

A periodic timer is set up to provide an interrupt at the desired sample rate. This

ISR calls Analog_AdcTrigger(), which takes CONVST low. The GPIO pin connected

to BUSY is configured to generate an interrupt on a falling edge, triggering the ISR

Analog_ADC_ConvComplete_ISR() when the falling edge of busy indicates the

conversion is complete. This function sets up the Direct Memory Access (DMA)

module, which reads the results from the SPI without further CPU intervention,

storing them in the array RawResults[]. When the DMA transfer is complete

(having read the requested number of channels) an ISR is triggered which sets

the boolean flag FreshData, which the application may poll to determine when

new data is available. The use of the DMA peripheral is discussed further in the

following section.

The function Analog_Get() takes a channel number as a parameter, and returns

the reassembled 18-bit sample as a signed 32-bit integer.

4 AD7609 provides two CONVST lines. CONVST A starts the acquisition process for the lower four channels, and CONVST B the upper four. The Analog Interface Board ties both pins together, so this document will refer only to CONVST in the singular.

Page 42: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

34

4.4.4 DIGITAL TO ANALOG CONVERTER

The AD5754R is part of the AD57x4R family of quad channel Digital to Analog

converters, all of which have a compatible pinout and interface. The cheapest

device, AD5724R, provides a 12-bit converter. The midrange AD5734R provides

a 14-bit converter, and the AD5754R provides a 16-bit converter. No changes to

the circuit or software are required when substituting these parts.

SPI Configuration

The DSPI’s CTAR1 register is configured for use with the AD5754R. Two 12-bit sub-

frames with continuous chip select are used to simulate the required 24-bit

frame. The AD5754R is specified for use with a 30MHz SCLK, and operates

correctly at that speed. See Section 4.4.3 for more details on CTAR configuration.

Figure 15: AD5754R Serial and Load Timing

Serial Protocol

Figure 16: DAC Input Register Layout (Analog Devices, 2011, p. 26)

The AD5754R uses a 24-bit SPI frame, as shown in Figure 16. The most significant

bit is used to indicate whether this is a read to or a write from the addressed

register. Bits REG2-REG0 specify which register is being addressed. Bits A2-A0,

and the data bits, vary in their function depending on which register is addressed.

If addressing the DAC register, A2-A0 specify which DAC channel is to be updated

with the following data. On AD5754R, all 16 bits of data are used for the DAC. On

the cheaper models, the least significant bits are treated as don’t-care values.

Page 43: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

35

Startup

When the DAC is reset, it must be initialised before use. Firstly, the output range

must be set in the Output Range Select Register. Both unipolar and bipolar ranges

can be selected, with symmetrical limits at 5V, 10V, or 10.8V.

Secondly, each DAC and the internal voltage reference must be powered up by

writing to the Power Control Register. The DAC’s require 10µs to power up, and

the DAC input register must not be loaded to the DAC during this time.

Optionally, the Control register may be written to disable Serial Data Output. This

feature, which echoes the previous input to the MISO line, is designed to permit

daisy-chaining of DAC’s, but it is not useful in this project.

Loading the DAC

Figure 17: DAC Input Loading (Analog Devices, 2011, p. 22)

After data has been loaded from the SPI input register to the DAC register, the

DAC’s may be updated in one of two modes. If /LDAC is held low whilst the data

transfer is ongoing, the DAC (and thus the output value) is immediately updated

on the rising edge of /SYNC. Alternatively, if /LDAC is high whilst data is loaded,

the update does not take effect until LDAC is held low for at least 20ns. This allows

for all the DAC’s to be updated simultaneously (see visualisation in Figure 15).

This is used in the Maze Rover 2.0 firmware to ensure that DAC updates occur

monotonically.

Page 44: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

36

Problems Solved During Testing

In early testing, no output was observed from the DAC. Observation on a mixed

signal oscilloscope showed that whenever /LDAC was high, so was the Analog

Interface Board’s RESET line. Probing with a multimeter confirmed that there

was an electrical connection between /LDAC and RESET, but only when the board

was assembled as part of the Tower System with the TWR-K70F120M.

On the Tower System Connectors, /LDAC is connected to pin B35. The

documentation for the TWR-K70F120M board shows that pin B35 is designated

GPIO4, and is connected to PTB 8 on the microcontroller. However, PTB 8 is also

seen to be used for “RSTOUT_b” on pin A63, which is connected to the RESET line

on the Analog Interface Board. Inspection of the TWR-K70F120M schematics

showed a 0Ω link (see R140 in Figure 18) connecting PTB 8 to the RESET line.

Removing this resistor corrected the behaviour.

Because this modification leaves the RESET line floating, I added a 10kΩ

pulldown resistor from RESET to ground on the Analog Interface board to ensure

RESET was in a known (logic low) state.

Figure 18: Schematic Extract Showing Reset 0Ω Link (Freescale Semiconductor, 2011, p. 8)

In subsequent tests, output from the DAC was erratic. Sometimes the intended

waveform could be seen, but at the wrong scale or offset. Inspecting the Analog

Interface Board schematic, I thought I had found the issue when I saw that the

REFIN pin was not connected to a 2.5V reference. Soldering a connection from

the ADC’s REFOUT pin to the DAC’s REFIN pin resolved the issue, seemingly

confirming my belief. However, it later came to my attention that I had been

reading the wrong datasheet – Analog Devices produce both an AD5754, which

requires an external reference, and an AD5754R. The –R suffixed model includes

an internal reference, but it is powered down by default. Adding the necessary

code to power up the internal reference allowed the DAC to operate correctly

without the additional wire.

Page 45: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

37

4.4.5 USING THE ENHANCED DIRECT MEMORY ACCESS PERIPHERAL

Benefits of DMA

The Enhanced Direct Memory Access (eDMA) permits data transfer operations to

be conducted without the intervention of the host CPU. It is capable of performing

its own source and destination offset calculations, permitting it to execute

complex data movements, such as reading incoming SPI values into a buffer. To

perform such operations using interrupts requires a lot of CPU time – every

received SPI frame triggers an interrupt, invoking a context switch every time.

This is particularly significant for the ADC, where transferring the conversion

results for eight channels results in the reception of 16 9-bit subframes.

Furthermore, because the host CPU is fast (clocked at 120MHz), even SPI

transfers are relatively slow – assuming the above transfer incurs no overhead

and takes exactly 144 cycles at 12MHz to complete, in the meantime 1440 cycles

have elapsed at the CPU. In reality, context switching overhead would slow this

further, and some of the remaining CPU time would be spent in the ISR

supervising the data transfer. By replacing such an interrupt-driven system with

DMA, the total transfer time is reduced and the CPU is free to do useful work.

Using DMA with the ADC

Transmission for the ADC is very simple – the MOSI line of the SPI bus is not even

connected to the ADC, so it doesn’t matter what you send provided you supply

the right clocks and framing as discussed in Section 4.4.2. As such, the TX data can

be pre-allocated to an array (AdcQueue[]).

Figure 19: Major and Minor Loops in DMA Requests (Freescale Semiconductor, 2011, p. 586)

Page 46: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

38

The DMA Transmission Control Block (TCD) for the ADC TX is configured to move

4 bytes per minor loop (to understand major and minor loops, see Figure 19). The

destination is the SPI2 PUSHR register, and no offset is applied to the destination

address – the PUSHR register never moves. The source address is configured to be

the array of uint32_t AdcQueue[], advancing the source pointer by four bytes after

each transfer (to reach the next 32 bit entry). Only two channels need to be

fetched, so the major loop count is set to 4 (2 frames of 2 sub-frames each), and

the source address is moved back 16 bytes after the major loop is complete,

resetting to the start of the array. The TCD is configured to disable the DMA

request upon completion by setting the Disable Request (DREQ) bit.

For the ADC RX, the configuration is similar, but the source address remains

constant (the SPI2 POPR register) and the destination is the array RawResults[].

Upon completion of the major loop, the RX channel triggers an interrupt (to set

the boolean FreshData flag) and disables the DMA request.

Using DMA with the DAC

For the DAC, the TX configuration is slightly more complex. In order to permit

asynchronous queuing of DAC commands, the TX DMA TCD is configured to use

the DMA’s modulo feature to implement a FIFO circular buffer for the source. The

circular buffer contains 16 uint32_t entries, for a total of 64 bytes, addressed as

0 to 63. 63 is 26 − 1, or 1111112 , so the DMA is configured for a 6 bit Source

Modulo (SMOD). Note that this requires that the array starts aligned with a 64

byte boundary. I initially overlooked this requirement, resulting in very

unpredictable behaviour as the SPI attempted to transmit based on an

uninitialized region of memory. This is fixed by using the proprietary GCC

attribute “aligned”, like so:

uint32_t TxBuffer[BUFFER_SIZE] __attribute__ ((aligned (64)));

This project does not make use of the DAC’s readback feature, so the DMA RX

channel is not configured when using the DAC. However, some code stubs are

included to make implementation of this feature simple if it is desired in the

future. Search for the symbol USE_DAC_RX_DMA.

Page 47: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Analog Interface Board

39

DMA Arbitration

During testing I discovered an issue wherein attempting to connect multiple DMA

channels to the same trigger (i.e. connecting the transmit DMA channels for both

the DAC and the ADC to the SPI2 transmission complete trigger) would result in

unreliable behaviour. Despite having only one channel enabled at a time,

sometimes a channel would simply fail to respond to a trigger.

To solve this issue, I redesigned the DMA system to use only one transmit channel

and one receive channel. This required adding a state machine to manage

arbitration of the DMA channels. In summary, it worked as follows:

At startup, the DMA channels are not configured, and the state machine is in

MODE_UNITIALISED.

When either the DAC or the ADC is used, it checks the current state to see if the

DMA is configured for the active peripheral. Let us assume, for example, that the

ADC is used first after startup. MODE_IDLE_ADC would indicate that the DMA

channels are already configured correctly, but we are in MODE_UNITIALISED, so

the DMA channels are reconfigured for ADC use. Whilst the transfer is ongoing,

the state is set to MODE_ACTIVE, preventing use by the other peripheral. When

the transfer is complete, the mode is set to MODE_IDLE_ADC. If no DAC operations

are performed before the next ADC operation, no reconfiguration will be

required. When a DAC operation is performed, MODE_IDLE_ADC will indicate

that reconfiguration is required, and when the operation is complete the DMA

arbitration state is MODE_IDLE DAC.

Page 48: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Non-Volatile Memory (EEPROM Emulation)

40

4.5 NON-VOLATILE MEMORY (EEPROM EMULATION)

The original Maze Rover software stores many user-configurable constants in

non-volatile memory (NVM). The MC9S12A512 microcontroller at the heart of

the original ModCon provides 4KB of integrated EEPROM, which may be erased

in 4 byte sectors and written in 2 byte words.

The MK70FN1M0 microcontroller used on the TWR-K70F120M development

board does not offer true or emulated EEPROM. The N in the part number

indicates that this model has Program Flash only, whereas an X would indicate

the presence of Freescale’s FlexMemory feature. FlexMemory permits the

designer to partition the available flash between Program Flash and emulated

EEPROM. This feature is present on the MK50DX256 used on ModCon 2.0, but for

this project an alternative solution was required.

The TWR-K70F120M development board offers two external sources of non-

volatile memory – 256MB of NAND flash, and a Secure Digital (SD) card slot.

However, neither of these features is present on ModCon 2.0, so these solutions

would be non-portable. Furthermore, a cursory examination of the relevant

datasheet sections indicated that development of drivers would be non-trivial.

This leaves only the internal source of NVM, the microcontroller’s Program Flash.

Using the Program Flash has the advantage of being available on any Kinetis

derivative, but complicates write operations – writes can be as small as 8 bytes

(a “phrase”) but erases must be a whole 4KB sector. Since NVM is used in this

project to store 1, 2, and 4 byte values which may be updated at runtime, an

EEPROM emulation layer is required to give byte-level read/write access.

4.5.1 USING THE FREESCALE 90NM TFS FLASH DRIVER

Freescale has a driver for interacting with the Program Flash on Kinetis devices,

but it is strangely difficult to find. It can be located by searching for “C90TFS” on

the Freescale website, and locating the entry “TFS Flash Driver Software for

Kinetis and ColdFire+ Microcontrollers”5.

5 At the time of writing the package can be downloaded directly from: http://cache.freescale.com/files/32bit/software/C90TFS_FLASH_DRIVER.exe

Page 49: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Non-Volatile Memory (EEPROM Emulation)

41

The driver package includes a User Manual (Freescale Semiconductor, 2014) and

example projects for various derivatives of the Kinetis architecture including the

MK70FN1M0. Integrating the driver into my project was straightforward – the

driver source and include files were copied into the project tree, and the paths

repaired to correctly reflect the new file layout. The configuration files demo_cfg.h

and user_cfg.h were copied directly from the MK70FN1M0xxx12 demo provided

with the driver.

4.5.2 EEPROM EMULATION

The EEPROM emulation module (Eeprom.c) provides byte-level write access to

the Program Flash by buffering and modifying whole 4KB sectors. The core

functionality of the module is the function Eeprom_Write(), which takes as

parameters a pointer to the first byte of data, the data length in bytes, and a

pointer to the destination address in Flash memory.

Eeprom_Write() is responsible for determining which Flash sector a write affects,

buffering that sector, modifying the relevant part of the buffer, erasing the Flash

sector, and then writing the buffer to the Flash. This process is performed inside

a loop, so the write may cross sector boundaries. The logic is shown in Figure 20.

Figure 20: Eeprom_Write Logic Flowchart

Page 50: Capstone Report

Chapter 4: Hardware Abstraction Layer Christopher Kerr Non-Volatile Memory (EEPROM Emulation)

42

4.5.3 IMPLEMENTATION DETAILS

Program Flash on Kinetis devices is divided into large regions named “blocks” –

on the MK70FN1M0 there are four program flash blocks, dividing the 1MB flash

into 256KB regions. Attempting to read from a block whilst a write is in progress

will result in an access violation error, and the result of the read is invalid. This is

important because program instructions and constant data are stored in the

program flash. Executing instructions or reading data from the same block as an

ongoing write will cause the program to behave unpredictably. This can be

avoided by placing simulated EEPROM variables in a different block to the

program instructions. CodeWarrior Special Edition imposes a 128KB limit on

program size, and the CodeWarrior new project wizard generates a linker map

which places the program at the start of the address space. Thus it may be

assumed, since 128KB is less than 256KB, that any block beyond the first is

available at any time.

However, I wished to provide a more generalised solution. This involves

executing the function which actually performs the Flash operations from RAM,

thus avoiding the need to read from Flash whilst the operation is ongoing. The

Freescale flash drivers provide a function designed for this purpose named

RelocateFunction(), and use of this function to store FlashCommandSequence() in

RAM was simple to integrate into Eeprom_Init().

However, during testing, writes to the first block still caused the program to

crash. I initially assumed the debugger was giving faulty data – it indicated that

execution had followed a function pointer to address 0x00000000. However,

debugging showed that this was exactly the case – an interrupt had caused a read

from the interrupt vector table, which returned invalid data resulting in an

invalid function pointer dereference. Interrupts must be completely disabled

around the write operation if it is to complete safely.

This functionality is not enabled by default, because it presents the risk of

overwriting the program data. The functionality permitting writing to the first

block may be configured by defining or undefining the symbol EEPROM_ANY_BLOCK.

The limits of the simulated EEPROM region are set by the pointers

k_EepromStartPtr and k_EepromEndPtr.

Page 51: Capstone Report

Chapter 5: High Level Functionality Christopher Kerr Maze Rover to PC Communications

43

5 HIGH LEVEL FUNCTIONALITY

5.1 MAZE ROVER TO PC COMMUNICATIONS

The Maze Rover is configured using the Maze Rover PC Interface, an application

running on a Windows PC. The PC Interface communicates with the Rover via an

emulated UART, using a modified version of Dr McLean’s ModCon Serial Protocol.

5.1.1 MODCON SERIAL PROTOCOL

Structure

The ModCon Serial Protocol (McLean, 2013) defines a 5-byte packet structure as

shown in Figure 21 below.

Command Parameter 1 Parameter 2 Parameter 3 Checksum

Figure 21: Serial Protocol Packet Structure

The most significant bit of the command byte is reserved for packet

acknowledgement requests. The remaining 7 bits store the command number,

thus permitting 128 unique commands (0x00 – 0x7F).

The function of the three parameter bytes may vary depending on the command.

The final byte carries a checksum, which is simply the exclusive-or (XOR) of the

preceding four bytes. Whilst not very theoretically robust (compared to popular

checksum methods like Cyclic Redundancy Checking), this checksum is

computationally inexpensive and quite adequate in this application. The

emulated UART used to communicate with the Maze Rover is implemented using

the USB Communications Device Class, which includes its own robust methods

for ensuring data integrity.

Packet Acknowledgement

The sender requests acknowledgement from the receiver by setting the most

significant bit (the ACK bit) of the Command byte. The receiver first attempts to

complete the requested command. If successful (or if no action is required), it

returns a copy of the packet with the ACK bit set. If unsuccessful, the packet is

modified to unset the ACK bit before being returned, thus sending a NACK.

Page 52: Capstone Report

Chapter 5: High Level Functionality Christopher Kerr Maze Rover to PC Communications

44

5.1.2 IMPLEMENTATION DETAILS

Most of the implementation is straightforward, and neither novel nor interesting.

packet.c contains functions for recognising valid received packets (Packet_Get())

and assembling packets for transmission (Packet_Put()). When a valid packet is

received, it is then passed to Protocol_HandlePacket(). This function handles

processing received packets, selecting the appropriate function based on the

received command. Implemented as a switch statement, it looks like this:

switch(maskedCommand)

case CMD_STARTUP:

retVal = Packet_HandleStartup();

break;

case CMD_TIME:

retVal = Packet_HandleTime();

break;

... // and so on, for many cases

Whilst this construct is simple and easily maintained at this size, the function may

potentially be required to scale to 128 entries. The resulting function would be

long and difficult to maintain. I prefer to address this problem with an array of

function pointers, and discuss the practicality of this solution below.

Function Pointers

In C, a pointer named funcPtr to a function which takes a Packet_t as a parameter,

and returns a bool, can be declared like so:

bool (*funcPtr)(Packet_t);

This syntax, which arises from C’s “declaration follows usage” design pattern, is

difficult for readers to parse. It is typically preferred to use a typedef to name

function pointers of a given signature (parameters and return type) as a type,

permitting a more typical declaration. The following extract creates a pointer

named funcPtr to a function Protocol_HandleStartup(), then dereferences that

pointer to call the function (note that in C, &fooFunc and fooFunc are equivalent –

either gives the address of the function fooFunc):

typedef bool(*Protocol_jumpPtr_t)(const Packet_t);

Protocol_jumpPtr_t funcPtr = Protocol_HandleStartup;

bool retVal = *funcPtr(fooPacket);

Page 53: Capstone Report

Chapter 5: High Level Functionality Christopher Kerr Maze Rover to PC Communications

45

Switch Statements vs Function Pointer Arrays

An array of function pointers named jumpTable may be created like so:

typedef bool(*Protocol_jumpPtr_t)(const Packet_t);

static const Protocol_jumpPtr_t jumpTable[] =

Packet_HandleStartup, // 0x00

Packet_handleDefault, // 0x01 – Unused

Packet_HandleTime // 0x02

;

Now the appropriate function for handling the command stored in an

appropriately range-tested variable named maskedCommand can be called by:

bool retVal = *jumpTable[maskedcommand](fooPacket);

The advantages of using an array of function pointers are:

Reduced logic complexity – new functionality can be added by altering only

the statically-allocated array, without need to modify the program logic

Improved speed – each case in a switch statement is tested sequentially, and

conditional logic is slow. For switches with many cases, the time spent

processing the switch becomes significant, so such designs scale poorly.

However, there are also some disadvantages:

C does not test if an array index actually lies within the bounds of the array,

so the index value must be correctly range limited before use.

Unused cases must be handled – there is no safe way to skip inclusion of an

element in a function pointer array, so a dummy function must be inserted for

all unused indices. For Protocol_HandlePacket(), the dummy function simply

returns false without performing any other action.

As a result of this second drawback, the function pointer array must be at least as

large as the difference between the lowest expected index (which can be offset to

0 by a subtraction) and the highest. However, if the array is declared static const

it will be stored in Flash, where bytes are plentiful. Thus, I chose to include the

full range of possible values from 0x00 to 0x7F in my jump table, with each entry

commented as shown in the example above. This makes it easy to find the entry

for a given command code, and to visually distinguish unused codes.

Page 54: Capstone Report

Chapter 5: High Level Functionality Christopher Kerr Maze Rover Communications Functions

46

5.2 MAZE ROVER COMMUNICATIONS FUNCTIONS

The communications functions are a direct port of those written by Dr McLean

for the original Maze Rover. I will briefly outline their function, and discuss the

adaptations required to have them function correctly in my project.

5.2.1 MODULATED WAVEFORM GENERATION

Comms_MakeWave() pre-calculates an amplitude modulated waveform, storing it in

the array WaveData[]. Interestingly, this function does not actually perform

amplitude modulation, but rather emulates it by adding sinusoidal components.

When considered in the frequency domain, it can be seen that this produces the

same output signal as amplitude modulation, but only if the message signals are

sinusoids.

The only significant changes made to this function were:

Updating the cosine-wave table to provide full 16 bit resolution. This was not

strictly necessary, but seemed proper – the new platform uses a proper DAC

in place of the smoothed PWM of the original Maze Rover, making it capable

of much higher fidelity.

Updated data types to ensure results would not be truncated as a result of the

16-bit cos wave input

A third change which was not made, but may be considered, relates to the divisor

used to scale the summed waveforms to an int16_t for output. I have kept the

same value as the original ModCon (21 * 2 = 42), but this results in an output

which is 10Vpp rather than the 5Vpp of the original system. It is my opinion that

the 5Vpp output used in the original Maze Rover was merely the result of its

technical limitations, and thus have made no deliberate effort to replicate this

characteristic.

5.2.2 VOLTAGE CONTROLLED OSCILLATOR

The function Comms_UpdateVCO() implements a numerically controlled sinusoidal

oscillator. When provided with ADC samples as input, it may be considered a

Voltage Controlled Oscillator. No modifications to this function were performed,

but it is necessary to modify the way in which it is called in order to preserve its

Page 55: Capstone Report

Chapter 5: High Level Functionality Christopher Kerr Maze Rover Communications Functions

47

intended performance. When the numerical input is supplied by the Analog

Interface Board’s 18-bit ADC, it is necessary to divide by 64 to preserve the

intended voltage-to-frequency relationship. However, this change should not be

made internal to the function, as this would render it incompatible with the

Phase-Locked Loop.

5.2.3 PHASE LOCKED LOOP

No changes to the PLL were required, but during testing (see Section 0) it was

noted that the PLL performed well over only a very limited range of frequencies,

perhaps 250Hz either side of the VCO’s centre frequency. This may merit further

investigation, but I spent no further time on it given that the demodulator, which

relies upon the PLL to resynthesise the carrier wave, operates without issue.

5.2.4 DEMODULATION

Comms_DetectDemod() implements a coherent demodulation system as outlined in

Figure 22. The modulated waveform is multiplied by the oscillator recovered by

the phase-locked loop, then filtered by a comb-resonator cascade.

Figure 22: Coherent Demodulation Scheme

However, the resulting signal is not used as the output. Instead, a Sliding Discrete

Fourier Transform is used to determine the magnitudes of the expected message

frequencies, and these magnitudes are used by Comms_MakeDemod() to synthesise a

clean version of the demodulated signal.

5.2.5 TESTING

This project is focused on embedded software development, not signal theory. As

such, I have used the pre-solved “Automatic” coefficients for testing, rather than

presenting my own derivation.

Page 56: Capstone Report
Page 57: Capstone Report

Chapter 6: Communication Lab Testing Christopher Kerr Modulated Wave Generation

49

6 COMMUNICATION LAB TESTING

6.1 MODULATED WAVE GENERATION

Single Sideband Lower

Figure 23: SSB-Lower Time Domain

Figure 24: SSB-Lower Frequency Domain

Rover Spec Carrier Freq. Msg 1 Freq. Msg 2 Freq.

Odd Autumn A 1050 Hz 375 Hz 425 Hz

Page 58: Capstone Report

Chapter 6: Communication Lab Testing Christopher Kerr Modulated Wave Generation

50

Single Sideband Upper

Figure 25: SSB-Upper Time Domain

Figure 26: SSB-Upper Frequency Domain

Rover Spec Carrier Freq. Msg 1 Freq. Msg 2 Freq.

Odd Autumn I 1100 Hz 75 Hz 125 Hz

Page 59: Capstone Report

Chapter 6: Communication Lab Testing Christopher Kerr Modulated Wave Generation

51

Double Sideband

Figure 27: SSB-HI Time Domain

Figure 28: SSB-HI Frequency Domain

Rover Spec Carrier Freq. Msg 1 Freq. Msg 2 Freq.

Odd Autumn E 700 Hz 425 Hz 375 Hz

Page 60: Capstone Report

Chapter 6: Communication Lab Testing Christopher Kerr Voltage-Controlled Oscillator

52

6.2 VOLTAGE-CONTROLLED OSCILLATOR

For this test I have used the configuration of Maze Rover B, Spring Semester 2013,

in order to permit comparison to the known results of an original Maze Rover.

The centre frequency for this configuration is 1100Hz.

Input Voltage Frequency (tested) Frequency (sample)

-2.5 1474.1 1497

-2.25 1436.7 1452

-2 1400.0 1417

-1.5 1324.2 1341

-1 1249.1 1267

-0.5 1174.2 1192

0 1099.6 1110

0.5 1024.5 1035

1 949.4 958

1.5 874.3 885.3

2 799.3 812.7

2.25 761.8 774

2.5 724.3 749.9

Notice that the tested values which are multiples of 0.5 volts give frequencies

which are very close to multiples of 25. In my opinion, this suggests that my

measurements are more accurate than those I am comparing against. This is

supported by the linear regression shown in Figure 29.

Figure 29: Voltage Controlled Oscillator Characteristic

Sample:y = -150.77x + 1114.7

Tested:y = -150x + 1099.3

600

700

800

900

1000

1100

1200

1300

1400

1500

1600

-2.5 -1.5 -0.5 0.5 1.5 2.5

Ou

tpu

t F

req

uen

cy (

Hz)

Input Voltage (V)

Frequency (sample)

Frequency (tested)

Page 61: Capstone Report

Chapter 6: Communication Lab Testing Christopher Kerr Phase Locked Loop

53

A note on the testing procedure: It is typically difficult to measure the frequency

of unfiltered DAC-synthesised sine waves, since features are not typically placed

consistently between cycles. However, when using a digital storage oscilloscope

such as the Agilent DSO-X 2004A used in these tests, the Fast Fourier Transform

functionality provides a useful method for making accurate measurement

measurements of frequency. The high-order frequency components which make

up the “stair-step” characteristic are easily distinguished from the fundamental

when viewed in the frequency domain.

6.3 PHASE LOCKED LOOP

Figure 30: Phase-Locked Loop Operation

The Phase-Locked Loop works very well over a limited range of frequencies, but

performance rapidly degrades as the input frequency diverges from that shown

in Figure 30. Debugging did not reveal any obvious inconsistency in operation.

Both the Voltage Controlled Oscillator and the demodulator operate correctly,

and these components are closely related to the Phase Locked Loop.

Page 62: Capstone Report

Chapter 6: Communication Lab Testing Christopher Kerr Demodulator

54

6.4 DEMODULATOR

For this test, the same configuration was used as in Section 6.2 (Rover B, Spring

Semester 2013). This configuration uses single-sideband upper modulation, with

messages frequencies 300Hz and 350Hz. These frequencies have been perfectly

reconstructed, as seen in Figure 32.

Figure 31: Demodulated Message Signal Time Domain

Figure 32: Demodulated Message Signal Frequency Domain

Page 63: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Controlled Plant: Quanser MAGLEV

55

7 CONTROL IMPLEMENTATION

Finalised Maze Rover 2.0 hardware was not available during the development of

this project, so implementation and testing of the Maze Rover’s Model Reference

Adaptive Controller could not be performed. Instead, an alternative control task

was implemented on this platform using the Quanser MAGLEV apparatus,

demonstrating the viability of ModCon 2.0 for performing complex control tasks.

7.1 CONTROLLED PLANT: QUANSER MAGLEV

Figure 33: Quanser MAGLEV Apparatus

The Quanser MAGLEV plant, seen in Figure 33, is an electromagnetic suspension

system. One pole of an electromagnet faces downward towards a post on which

a stainless steel ball sits when at rest. The pole contains a photo-transistor based

sensor which measures the distance from the top of the post to the surface of the

ball. A one Ohm current-sense resistor is provided, giving a 1 VA-1 response.

Using this apparatus, the goal of the controller is to modulate the input voltage of

the electromagnet such that ball tracks to a specified point in the vertical axis.

The derivation of a system model and LQR controller for this plant can be found

in Appendix B.

Page 64: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Control Framework

56

7.2 CONTROL FRAMEWORK

In order to perform control tasks on the ModCon 2.0 platform, a generalised

control framework was constructed, mimicking the public interface of the control

library provided by Dr McLean for the original ModCon. An overview of the

control framework software flow is shown below in Figure 34.

Figure 34: Control Module Overview

The original ModCon control library is supplied in binary form, hiding its

implementation details. However, its public interface allowed me to infer a likely

structure and implement a similar system. Note that the original ModCon control

library is designed to be controlled by the ModCon PC Interface, which uses

automatic port detection logic incompatible with ModCon 2.0. Due to time

limitations functionality related to the PC interface, such as input/output channel

visualisation and runtime controller configuration, was not implemented. Only

the most basic functionality required to control a system is presented here.

As discussed in 4.4.3: “Analog to Digital Converter”, data acquisition is performed

as a background DMA task triggered by a timer interrupt. When acquisition is

complete, a flag is set which is tested in the main loop. When the test is successful,

Control_BackgroundTasks() is called, starting a control cycle.

Dr McLean’s control template project includes one user-editable file, control.c,

containing only one function, Control_DoControl(). This file exposes three two

dimensional arrays – the input, output, and intermediate values. For each array,

the first dimension corresponds to a channel (or intermediate state). The second

dimension stores past values, with the current state indexed as 0, the value from

the previous control period at index 1 and so on. The control algorithm is

implemented by the user in Control_DoControl(), writing the calculated outputs

to the 0 index of the output channel array members. The library then invisibly

handles the details. My control framework defines four functions which mimic

the known behaviour of Dr McLean’s library.

Page 65: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Control Framework

57

Control_getData()

This function converts and scales the new ADC samples to a floating point value

measured in volts, storing the result in Input[chanNb][0].

Control_doControl()

As in Dr McLean’s original template, this function is modified by the user to

perform the actual control algorithm, writing the results to Output[chanNb][0].

The array Var[][] is provided to store intermediate values and their history,

which is useful for implementation of difference equations for filtering,

differentiation, integration, and so on.

Control_putData()

This function scales the Output[chanNb][0] values by 3276.7, converting their

range from ±10V to the scale used by the DAC. Values are hard-limited to ±32,767

(the bounds of a 16 bit signed integer), then converted to an int16_t for

Analog_Write(). After all channels have been calculated and buffered by

Analog_Write(), the function calls Analog_Push() to start the SPI transfer to the

DAC.

Control_rotate()

This function maintains the history states of Input[][], Output[][] and Var[][],

moving each Variable[i][n] to Variable[i][n+1]. It takes parameters for the

number of input, output, and intermediate channels, and the number of history

states to keep. These parameters are, in practice, always set from a const

configuration value, but exposing parameters is more in line with the project

coding standard than use of a file-scope constant.

Other Functions

In addition to the functions described above, I have included a function for

performing discrete integration, which is passed a pointer to a struct containing

the accumulated integral and the maximum and minimum limits for that value.

Such limits are useful for preventing integral windup, and encapsulation as a

function cleans up the control code considerably. Such a function was not

included for discrete differentiation, because it is typical to perform difference-

equation-based filtering rather than simply hard-limiting the discrete derivative.

Page 66: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Controller Implementation

58

7.3 CONTROLLER IMPLEMENTATION

7.3.1 HARDWARE FLOATING POINT

For maximum flexibility, a new CodeWarrior project was created using the

Kinetis K70’s Hardware Floating Point capabilities. The entire source tree of the

Maze Rover 2.0 project was then imported. No modifications (other than those

described for the creation of a new project in 3.1.3: Using C99) were required.

The new project compiled and ran without issue.

7.3.2 SAMPLE RATE

A sampling rate of 1ms was selected, based on simulations showing that the

system was unstable at 2.5ms or greater. Testing with a Mixed Signal Oscilloscope

showed that the control cycle was complete in less than one-fifth of that time,

with the time spent processing the control algorithm insignificant compared to

the time required to interface with the analog components over SPI. Despite the

possibility of increasing the sample rate, I chose to keep the sample rate at the

conservative value of 1ms because the equipment required to visualise and debug

digital logic was not available in the control laboratory.

7.3.3 ANALOGUE BUFFER

During testing, some issues were observed with the digital to analogue converter.

Rapid voltage swings exceeding approximately 11V, as seen when the ball first

overshoots the setpoint, would result in the DAC’s protection circuitry being

triggered. This would cause the output voltage to be clamped to ground until the

system was reset.

There was insufficient time to fully investigate this phenomenon, which I believe

may have been due to the characteristic impedance of the test leads in use. The

symptoms were resolved by the addition of an op-amp configured as a simple

voltage follower (non-inverting buffer) like that shown in Figure 35.

Figure 35: Non-Inverting Op-amp Buffer

Page 67: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Controller Implementation

59

7.3.4 ARCHITECTURE

Figure 36 : Controller Architecture

The controller architecture as implemented is shown in Figure 36. This

architecture has several minor modifications to a typical state feedback system,

described below.

PID Form

Inspecting the state-space form of the system, it is clear that a full-state feedback

controller has the form of a PID controller – a gain applied to the proportional,

integral, and derivative terms of the ball position. This form has a simple

advantage over a typical full-state-feedback design – the reference is compared

directly to the system output, rather than to the sum of all state variables, thus

avoiding the need to precompensate the reference.

A typical modification to PID systems, to improve stability, is to apply the

derivative gain directly to the process variable, rather than using the error

between the system output and the reference. Recognising that this system is

essentially a PID controller allows this optimisation to be applied here.

It can be shown in Matlab that the modified feedback system produces the same

closed-loop poles as designed – the systems are equivalent, but this version is

simpler to conceptualise.

Page 68: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Controller Implementation

60

Feedforward Gain

The controller as designed relies on the assumption that it is correcting small

deviations in the “linear” region around the setpoint.

For a given position reference, the Feedforward Gain supplies the approximate

steady-state current required to maintain that position. If we assume the

characteristic to be linear, we may calculate the Feedforward Gain, Kff, based on

the linearisation parameters:

𝐾𝑓𝑓 =𝑖𝑠𝑠

𝑥𝑠𝑠

To improve the performance, the real average current for a variety of positions

was measured and plotted, and a linear fit to the data was found by regression.

This works well over a limited range of the ball’s travel, but poorly at the

extremes. To improve the performance, the real average current for a variety of

positions was measured and plotted, and a linear fit to the data was found by

regression (see Figure 37 below).

Figure 37 : Feedforward Current Relationships

It can be seen in Figure 37 that the relationship is not linear, but is a good fit to a

second-order relationship. However, it can also be seen that a good piecewise-

linear approximation can be made from two sections. For processing efficiency,

this second option was used in the final implementation.

y = 84x + 0.7466R² = 0.9962

y = 165.6x + 0.3377R² = 0.9978

y = -8.8x2 + 217.3x + 266.8R² = 0.9978

0

0.2

0.4

0.6

0.8

1

1.2

1.4

1.6

1.8

2

0 2 4 6 8 10 12

Page 69: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Controller Implementation

61

7.3.5 LAB TESTING RESULTS

Figure 38 : Lab Response to Step Input

Unfortunately, a hardware flaw made large step inputs untestable – when the

control effort swung rapidly from a large positive output to a large negative

output, the Digital to Analogue converter’s protections were triggered, shutting

down the device. However, small steps could be tested successfully. We observe

a maximum steady state error of 2%, rise time less than 100ms, peak overshoot

of 6.7% and 5% settling time less than 200ms.

Note that some low-amplitude oscillation is visible in the steady state. This is not

a fault in the performance of the controller, but rather reflects the limitations of

the apparatus. Throughout this report it is assumed that the ball travels in only

one axis (vertically), but in reality the ball is unconstrained in all 3 axes. Motion

on the horizontal axes is uncontrolled, as no actuator capable of effecting such

motion is provided. Due to the nature of the distance measurement (an optical

sensor pointing at the bottom of the ball) side-to-side motions are erroneously

detected as movement in the vertical axis. Unfortunately this causes the

controller to attempt correction of apparent vertical motion which doesn’t

actually exist.

Page 70: Capstone Report

Chapter 7: Control Implementation Christopher Kerr Controller Implementation

62

Figure 39 : Lab Tracking of Triangle Wave Input

Over most of the ball’s range of motion the setpoint is tracked very accurately.

However, the tracking becomes inaccurate when the ball gets close to the

electromagnet – in this region the linearised model is no longer valid, so the

controller operates poorly. This is expected, but steps have been taken to

minimise its impact (see Feedforward Gain in the previous section).

Figure 40 : Lab Rejection of External Disturbance

In this test an external disturbance was introduced by sharply striking the bench

near the apparatus. This imparted a measurable vibration, seen above at t = 1.5

seconds. By t = 2.5 seconds the oscillation has been completely corrected and the

system returns to the steady state.

7.3.6 CONTROL IMPLEMENTATION CONCLUSIONS

The successful implementation of this controller indicates that the ModCon 2.0 is

an appropriate platform for complex control tasks.

-2.0

0.0

2.0

4.0

6.0

8.0

10.0

12.0

0.0 2.0 4.0 6.0 8.0 10.0

Time (seconds)

Tracking Triangle Wave Input

Position (mm)

Setpoint (mm)

Error (mm)

5.6

5.8

6.0

6.2

6.4

0.0 1.0 2.0 3.0 4.0

Rejection of External Disturbance

Position (mm)

Setpoint (mm)

Page 71: Capstone Report

Chapter 8: Conclusions Christopher Kerr Final Project Status

63

8 CONCLUSIONS

8.1 FINAL PROJECT STATUS

At the conclusion of this project, I have achieved my core goals. To quickly review:

My review of the literature and development of an Embedded Software Coding

Standard has brought me a thorough understanding of shortcomings of the C

language, the need for quality code in embedded systems, and how these issues

can be addressed by rigorous application of best practice.

I have learnt and documented the process of developing software for Kinetis

microcontrollers using the Freescale CodeWarrior IDE. Even if I do not work with

this platform again in future, the process of learning a new environment and

architecture has developed skills which will be transferrable to further

architectures in the future. Furthermore, as Kinetis is based on the commonly

used 32-bit ARM Cortex-M4, I will have some familiarity when developing on

other ARM-based microcontrollers (such as the Texas Instruments Tiva-C and

ST Microelectronics STM32 platforms). I hope that my documentation will also

help the process of integrating the ModCon 2.0 into the UTS laboratories.

A hardware abstraction layer providing access to the peripherals required by the

Maze Rover 2.0 has been developed, and its functionality demonstrated by

porting the Maze Rover’s communications functionality to this platform. The

communications functions are now partially documented, improving upon their

state in the original Maze Rover.

Finally, a complex control algorithm has been demonstrated on the Maze Rover

2.0 platform. This task was performed in lieu of developing the Model Reference

Adaptive Controller for the Maze Rover’s motors, as final Maze Rover 2.0

hardware was not available as of the conclusion of this project.

Page 72: Capstone Report

Chapter 8: Conclusions Christopher Kerr Project Management and Scheduling

64

8.2 PROJECT MANAGEMENT AND SCHEDULING

This project encountered a number of difficulties which resulted in a significant

alteration of project focus and scope.

The first was simple optimism – despite the warnings of my supervisor, the

original scope was extremely ambitious, aiming to complete the objectives

covered by this report in half the time. My only concession to contingency

planning was describing the project in discrete stages which could easily be

removed – in this final version of the report, none of the “extensibility” aspects of

the original proposal are addressed, but the core functionality is complete.

Secondly, I encountered an early setback related to my plans to use C++. This was

discussed further in 3.1.2: “Language and Compiler – Exploring the Use of C++”.

Investigating this issue and exploring alternatives unexpectedly consumed

approximately two weeks of my scheduled project time, and ultimately lead to a

significant redirection of the project focus. Where originally I had intended to

explore the applicability of Object Oriented and Functional programming

paradigms to embedded development, instead the project focus turned to the

process of developing quality embedded code.

However, the major impact on this project’s progress relates to the Analog

Interface Board. My original proposed schedule was based on a faulty

assumption: namely that the Analog Interface Board had been fully tested and

documented during the project that produced it. I scheduled only a brief period

for developing the Hardware Abstraction module for this device on the

assumption that it would only involve the integration of existing drivers. These

assumptions proved to be unfounded – the board was untested and no driver

interface existed. Producing this module ultimately took several months of work,

and was delayed over the Christmas break due to Dr McLean’s scheduled leave.

Ultimately these delays were largely my own fault, despite the influence of factors

out of my control. Had I put more care into the preparation of my proposal, these

difficulties could have been anticipated and contingencies prepared. The

management of these issues by reduction of project scope has produced a merely

satisfactory outcome (the delivery of core Maze Rover functionality).

Page 73: Capstone Report

Chapter 8: Conclusions Christopher Kerr Further Work

65

8.3 FURTHER WORK

Model Reference Adaptive Motor Controller

As final Maze Rover 2.0 was not available in time for this project, the Model

Reference Adaptive Controller for the motor driver was not implemented. This

will need to be addressed before Maze Rover 2.0 is deployed.

Use of C++

During the writing of this report, I became aware of a new software release from

Freescale – the Kinetis Design Studio IDE. Currently in beta, this software has one

important difference to CodeWarrior – it is completely free, but supports C and

C++ without code size restrictions on all Kinetis devices. I recommend that future

development efforts on the Kinetis platform use this hobbyist-targeted tool

instead of the crippled “Special Edition” of CodeWarrior.

Analog Interface Board

Using the ModCon 2.0 Analog Interface Board in this project revealed some quirks

which may merit a new revision before laboratory deployment.

Firstly, the board provides no overvoltage protection beyond that the ADC

includes internally. The ADC input clamps tolerate only the limited range ±16.5V,

which is easily exceeded when interfaced with common UTS laboratory

equipment such as the MiniLab which can produce up to +30V. I recommend that

a more robust protection, such as an external clipper diode network, be

implemented in a future revision.

Secondly, the DAC has weak output drive. During testing its overcurrent

protections were activated when interfacing with equipment in the control

laboratory. I recommend that future revisions include an output buffer for each

channel, which could be as simple as a quad op-amp.

Finally, I note that the addition of a single trace from the ADC’s RefOut pin to the

DAC’s RefIn pin would allow the substitution of the cheaper non-suffixed AD57x4

DAC’s. These parts are pin-compatible with the AD57x4R models. As the internal

reference on the –R models is disabled by default, either version could be used

based on availability.

Page 74: Capstone Report

Chapter 8: Conclusions Christopher Kerr Further Work

66

Potential Improvements and Future Projects

The Maze Rover 2.0 platform is built on the Freescale Tower System, opening the

opportunity for a number of potential future projects. Some examples include:

Human Machine Interface using touch-screen LCD backpack

Wireless network interface providing remote configuration and control

using Wi-Fi module

Furthermore, the Kinetis platform at the heart of Maze Rover 2.0 provides the

opportunity to exploit Freescale’s MQX Real-Time Operating System. In addition

to the scheduling and timing flexibility provided by an RTOS, MQX includes

drivers for TCP/IP networking, SD cards, USB device support, and other high-

level functionality facilitating the development of complex projects on this new

platform.

Page 75: Capstone Report

References Christopher Kerr

67

REFERENCES

Analog Devices, 2011. AD57x4R Datasheet Rev. E. [Online]

Available at:

http://www.analog.com/static/imported-files/data_sheets/AD5724R_5734R_5754R.pdf

[Accessed 20 December 2013].

Analog Devices, 2013. AD7609 Datasheet Rev. B. [Online]

Available at: http://www.analog.com/static/imported-files/data_sheets/AD7609.pdf

[Accessed 20 December 2013].

Barr, M., 2013. Barr Group Embedded C Coding Standard. Gaithersburg, MD: Barr Group.

Freescale Semiconductor, 2011. K70 Family Reference Manual Rev. 2. [Online]

Available at:

http://cache.freescale.com/files/microcontrollers/doc/ref_manual/K70P256M150SF3RM.pdf

[Accessed 30 July 2013].

Freescale Semiconductor, 2011. TWR-K70F120M Schematic Rev. B1. [Online]

Available at:

http://cache.freescale.com/files/32bit/hardware_tools/schematics/TWR-K70F120M-SCH.pdf

[Accessed 26 February 2014].

Freescale Semiconductor, 2012. Kinetis Quick Reference User Guide Rev. 2. [Online]

Available at: http://cache.freescale.com/files/32bit/doc/quick_ref_guide/KQRUG.pdf

[Accessed 17 October 2013].

Freescale Semiconductor, 2012. TWR-K70F120M Tower Module User's Manual Rev. 1.1. [Online]

Available at:

http://cache.freescale.com/files/microcontrollers/doc/user_guide/TWRK70F120MUM.pdf

[Accessed 1 June 2013].

Freescale Semiconductor, 2014. Standard Software Driver for C90TFS/FTFx Flash User's Manual.

[Online]

Available at: http://cache.freescale.com/files/32bit/software/C90TFS_FLASH_DRIVER.exe

[Accessed 15 April 2014].

Ganssle, J., 2008. Art of Designing Embedded Systems. 2nd ed. Burlington, MA: Elsevier.

McLean, P., 2013. ModCon Serial Protocol. [Online]

Available at:

http://services.eng.uts.edu.au/pmcl/embsw/Downloads/ModConSerialProtocol.pdf

[Accessed 1 July 2013].

Page 76: Capstone Report

Chapter 0: References Christopher Kerr Further Work

68

McLean, P., 2013. Software Style Guide. [Online]

Available at: http://services.eng.uts.edu.au/pmcl/embsw/Downloads/SoftwareStyleGuide.pdf

[Accessed 1 November 2013].

MIRA Limited, 2004. MISRA-C:2004 Guidelines for the use of the C language in critical systems.

2nd ed. Nuneaton, UK: MIRA Limited.

Styger, E., 2012. MCU On Eclipse - Please Check Your License. [Online]

Available at: http://mcuoneclipse.com/2012/08/06/please-check-your-license/

[Accessed 1 August 2013].

Vambuca, R., 2013. Analog Board Schematics, s.l.: s.n.

Page 77: Capstone Report

Table of Appendices Christopher Kerr

69

TABLE OF APPENDICES

Appendix A: Embedded C Coding Standard .................................................................... A-1

Appendix B: Control System Modelling and Design ................................................... A-29

Appendix C: Datasheets ......................................................................................................... A-43

The Appendix CD should be found affixed to the inside rear cover of this report.

If the CD is missing, damaged, or otherwise unreadable, a copy can be found on

my personal website at:

http://cjk.net.au/capstone/

Appendix D: Maze Rover 2.0 Code .................................................................................On CD

..................................................................................On CD Appendix E: Control System Code

Appendix F: Maze Rover 2.0 Technical Reference....................................................On CD

Page 78: Capstone Report
Page 79: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 1: Preamble

A-1

Appendix A: Embedded C Coding Standard

1 PREAMBLE 1.1 VERSION CONTROL

Rev. Date Comment Authors

1.2 2014-04-28 Added Floating Point Types Christopher Kerr

1.1 2014-04-11 Updated rules regarding parenthesis

use after considering the Apple

“GotoFail” and OpenSSL “Heartbleed”

vulnerabilities.

Christopher Kerr

1.0 2013-12-06 Initial Revision. Incomplete but in

use.

Christopher Kerr

1.2 INTRODUCTION The purpose of this document is to assist in the production of quality code,

defined here to be:

Stable: The software should have no known bugs, and all necessary

precautions must be taken to avoid introducing common errors.

Maintainable: The intent of code must be clear to the reader, and the

code’s purpose in the larger system must be thoroughly documented.

Portable: The code should not make unnecessary assumptions about the

underlying architecture or compiler, to permit porting between systems.

The embedded environment is fundamentally different from the personal

workstation environment with which most programmers are familiar. The

operation of embedded systems is largely autonomous and unsupervised.

Furthermore, many embedded systems control safety-critical processes, such as

automobiles and industrial automatons. In this environment, unreliable software

can result in injury or death.

Page 80: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 1: Preamble

A-2

Unfortunately, as a language, the C standard tends towards permissiveness. It is

certainly possible to write code which will compile as valid C despite being

difficult to understand, perverse, or not correctly reflecting the intentions of the

programmer. Other sections of the language are well defined but prone to

programmer error, such as the operator precedence rules.

Given these limitations of C, it is clear that the language must be applied carefully

when used for embedded systems. Used without restriction, C has features which

can negatively impact the stability, maintainability, and portability of code,

resulting in poor code quality.

1.3 SOURCES This Coding Standard has been assembled using the following references:

i. MISRA-C:2004 – Motor Industry Software Reliability Association

ii. Barr Group Embedded C Coding Standard – Michael Barr

iii. The Art of Designing Embedded Systems – Jack Ganssle

iv. Embedded Software Style Guide – Peter McLean

It incorporates significant material from each of the listed sources, with

modifications reflecting my own preferences.

Of these, the first two are considered to be definitive – where this document

contradicts either of these standards, the contradiction is to be noted and

rationalised.

The second two documents are considered to be advisory – they contain much

material which is in contradiction with the first two, and with each other.

However, they have nonetheless influenced this document and deserve to be

acknowledged here.

MISRA-C:2004 has since been superseded by MISRA-C:2012, but no copy could

be located for review. However, it is known that MISRA-C:2012 extends support

to C99 – as such, for the purposes of this document the use of C99 features shall

not be considered to be contradictory to MISRA-C.

Page 81: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 1: Preamble

A-3

The format of this document, and some of its content, is adapted from the Barr

Group Embedded C Coding Standard. This usage is permitted, but requires the

inclusion of the following paragraph:

This document as well as the selection and arrangement of the rules it comprises is

Copyright © 2013 by Barr Group. It is permissible for individuals, companies, and

institutions to adapt all or a subset of the coding rules herein; indeed we hope that

many more will. This may be done simply by identifying the “Barr Group

Embedded C Coding Standard” as the source of your rules and retaining this

paragraph in its entirely. All other rights in copyright law are reserved by Barr

Group.

Page 82: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: General Rules

A-4

2 GENERAL RULES 2.1 LANGUAGE RULES:

a. All programs shall be written in ISO C, where ISO C is defined to be either:

i. The latest available ISO Standard for the C Programming Language (currently C99) OR

ii. The most recent ISO Standard for the C Programming Language supported by the compiler.

b. Appropriate compiler options are to be used to restrict the feature set to ISO C

c. The use of proprietary language extensions (including additional keywords and #pragma directives) is forbidden, with the following exceptions:

i. Modules which are platform-specific and require special features, e.g. to create an Interrupt Service Routine.

ii. Low-level initialisation code requiring linker directives, e.g. for placement of data at a specific location in memory.

iii. Inline assembler using the asm keyword (for GCC) or equivalent. Use of inline assembler should be minimised to maximise portability and readability.

RATIONALE: C99 brings valuable features to the C language, including:

iv. The ability to mix declarations and code, i.e. to declare a variable at any point in a function

v. The first expression in a for loop may be a declaration, as in C++

vi. The inline keyword, which hints to the compiler that a function should be included inline rather than called (i.e. to avoid function-calling overhead for a short functions)

vii. Boolean type _Bool, typedef’d as bool in <stdbool.h> with macros for true and false

viii. C++ style // one line comments

ix. More flexible initialisation of arrays and structs

Furthermore, C99 clarifies several points that were poorly defined in previous versions of ISO C:

i. Integer division and modulus operators always truncate towards 0

ii. Unspecified declarations no longer default to int e.g. the declaration f(); is illegal rather than being equivalent to int f();

The remainder of this document will assume that these features are available.

EXCEPTIONS: None. The only relevant objection (compiler support) is explicitly allowed in Rule 2.1a.ii

Page 83: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: General Rules

A-5

1.2 VERSION CONTROL RULES:

a. Use of a version control system such as Subversion is required.

RATIONALE:

The benefits of version-controlled source code are many. It gives programmers

the ability to:

i. Revert changes made in error

ii. Return to a known-working state

iii. Review the history of a piece of code

iv. Collaborate with other developers

v. Add new features without destabilising the existing codebase

Finally, and most prosaically, a remote version control system is an effective off-

site backup solution.

EXCEPTIONS:

None. Even projects with only a single developer benefit from the above-listed

features.

1.3 PARENTHESES RULES:

a. Be a parenthesis zealot. Do not rely on C’s operator precedence rules – use

parentheses to indicate intent and to ensure proper execution order. Consider

breaking long statements into multiple lines of code to aid readability.

b. Each operand of the logical operators II and && shall be surrounded by

parentheses, unless said operand is a single identifier or constant.

RATIONALE:

C’s operator precedence rules are unintuitive and difficult to remember. Relying

on these rules makes code harder to maintain – it is preferable that code

communicates intent clearly.

EXAMPLE: if ((depth > 0) && (depth < MAX_DEPTH))

fillFactor = (radius * depthCm) / (viscosity % SOME_CONST);

EXCEPTIONS:

None.

Page 84: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: General Rules

A-6

1.4 LINES RULES:

a. No line of code shall contain more than one statement.

i. Use of the comma , operator is forbidden except in the initialisation of a for

loop

b. No line of code shall exceed 80 characters in width.

c. Blank lines shall be used to separate natural blocks of code.

d. Each source file shall have a blank line at the end.

RATIONALE:

Lines which contain more than one statement allow some statements to be easily

overlooked. This is particularly the case for use of the comma , operator.

Separating code into natural blocks improves comprehension of code structure

and permits more natural documentation.

Some compilers require source files to end in a blank line. Comply with this for

portability.

EXCEPTIONS:

Legacy modules which breach these rules should be left as-is if the violation is

purely cosmetic, unless significant modification to these modules is otherwise

required. Rule 1.4a is not cosmetic and breaches should always be corrected.

Page 85: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: General Rules

A-7

1.5 BRACES RULES:

a. Braces shall surround blocks of code following if, else, switch, while, do, and for statements, including any single or empty statements.

i. An empty statement must include an explanatory comment.

b. Each left brace shall appear by itself on the line following the statement. The corresponding right brace shall appear on a line by itself following the code block, and should be aligned with its corresponding left brace.

RATIONALE:

A previous version of this document contained the following wording:

Braces should not be used to surround single or empty statements

If a single statement has an associated comment, and that comment is too long to

place to the right of the statement, the statement and its associated comment

should be treated as a block and surrounded with braces as per normal.

Although there is a risk associated with single statements which are not

surrounded by braces, this risk is considered to be small and outweighed by the

benefits of improved code density and readability.

In light of recent vulnerabilities, including the Apple “GotoFail” SSL bug and the

OpenSSL “Heartbleed” bug, this exception has been removed. The previous

rationale is no longer considered adequate.

EXAMPLE: bool example(void) for (int i = 0; i < 10; i++) if (doSomething() != ERROR_CATASTROPHIC) return true; else doSomethingElse();

return false;

EXCEPTIONS:

None.

Page 86: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: White Space

A-8

2 WHITE SPACE 2.1 INDENTATION RULES:

a. Each indentation level is to consist of 1 tab character

b. Indentation is to be increased by 1 level for code blocks inside braces, however:

i. Within a switch statement, each case statement should be at the same level

of indentation as the switch; the contents of each case should be indented

by 1 level.

c. Pairs of braces shall be aligned (i.e. indented to the same level)

d. Whenever a line of code is broken onto multiple lines for readability, indent the

second and any following lines in the most readable manner possible.

RATIONALE:

Indentation is required to maintain code readability, but indentation levels are a

personal preference. Consistent usage of tabs permits indentation levels to be set

to the programmer’s preference.

EXAMPLE: void example(uint8_t errCode)

// Switch statements per 2.1b.i

switch (errCode)

case ERROR_ONE:

doSomething();

break;

case ERROR_TWO:

doSomethingElse();

break;

default:

break;

// Multi-line indentation as per 2.1d

if (first_very_long_comparison_here

&& second_very_long_comparison_here

&& third_very_long_comparison_here)

// Single statement in braces as per 1.5a

doSomething();

Page 87: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: White Space

A-9

EXCEPTIONS:

Legacy modules which breach these rules should be left as-is unless significant

modification to these modules is otherwise required – altering indentation will

result in every line appearing modified to difference-tracking version control

systems. Should such an update be required, indentation should be changed in a

single distinct version control revision.

2.2 ALIGNMENT RULES:

a. Variable names within a block of related declarations shall have their first

characters aligned.

b. The names of struct and union members shall have their first characters aligned.

c. The assignment operators within a block of adjacent assignment statements shall

be aligned.

d. The # of a pre-processor directive shall always be located in column 1, except when

indenting within an #if or #ifdef sequence

RATIONALE:

Visual alignment should be used as a cue for similarity and relatedness.

EXCEPTIONS:

None.

Page 88: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 2: White Space

A-10

2.3 SPACES RULES:

a. The keywords if, else, while, for, switch, and return shall always be followed by

one space.

b. The following shall always be preceded and followed by one space:

i. The assignment operators =, +=, -=, *=, /=, %=, &=, |=, ^=, ~=, and !=

ii. The binary operators +, -, *, /, %, <, <=, >, >=, ==, !=, <<, >>, &, |, ^, &&, and ||

c. Each of the unary operators +, -, ++, --, !, and ~ shall always be written without a

space on the operand side, and with one space on the other side.

d. The pointer operator * shall:

i. Be written with a space on each side when used in declarations

ii. Otherwise be written without a space on the operand side

e. The “address of” operator & shall be written without a space on the operand side.

f. The ternary operator shall have each character ( ? and : ) preceded and followed

by one space.

g. The structure member and structure pointer operators ( . and -> ) shall always be

written without surrounding spaces.

h. The left and right brackets of the array subscript operator ( [ and ] ) shall always

be written without surrounding spaces.

i. Expressions within parentheses shall always have no spaces adjacent to the left

and right parenthesis characters.

j. The left and right parentheses of a function call shall always be without

surrounding spaces.

k. In function definitions, there shall be one space between the function name and

the left parenthesis of the argument list.

l. Each comma separating function parameters shall be followed by one space.

m. Each semicolon separating the elements of a for statements shall be followed by

one space.

n. Semicolons shall directly follow the statement they terminate, without a preceding

space.

RATIONALE:

These rules are given purely for consistency and readability. Consistent use of

whitespace makes code more readable and bugs easier to spot.

EXCEPTIONS:

None.

Page 89: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 3: Comments

A-11

3 COMMENTS 3.1 RESTRICTIONS ON USAGE RULES:

a. Both C style (i.e. /* ... */) and C++ style (single line preceded by //) comments

are acceptable.

b. Comments shall never be nested.

c. Comments shall never be used to disable a block of code.

i. Use of a C++ style // comment to temporarily disable a single line of code is

permitted, but must be accompanied by an explanatory comment prefixed

with TEMP:.

ii. For blocks of multiple lines, instead use preprocessor conditional

compilation (e.g. #if 0 … #endif). The block must be preceded by an

explanatory comment prefixed with TEMP:.

iii. No disabled code shall remain in the source code of a release or release

candidate. Note that this is distinct from other uses of conditional

compilation – code blocks which may sometimes be enabled (e.g. #ifdef

ENABLE_FEATURE ... #endif) are permitted.

RATIONALE:

Nested comments, specifically C-style comments nested within C-style

comments, are unsupported - the outer comment will be unintentionally closed

by the */ of the inner pair.

Given that comments will be used to document most code blocks, this behaviour

makes “commenting out” code blocks using C-style comments impossible. C++

style comments used for this purpose do not share the same fault, but prefixing

every line in a code block is time-consuming, error-prone and pointless given the

existence of a better solution.

EXCEPTIONS:

None.

Page 90: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 3: Comments

A-12

3.2 LOCATION AND CONTENT RULES:

a. All comments shall be written in English.

b. Comments should be written clearly and concisely, with correct spelling and

grammar.

c. Comments which document the purpose or function of a natural code block shall

be located directly above the relevant block, at the same level of indentation.

d. Comments which document a detail of a particular line shall be located to the right

of the line. All such comments within a code block shall be aligned at the same level

of indentation.

e. Assume that the reader knows the C programming language. Avoid documenting

the obvious.

f. Comments should cite sources for technical details, e.g. reference manual page

numbers.

g. All assumptions shall be documented in comments.

h. Each module and function shall be documented in a manner suitable for processing

by an automatic documentation generator such as Doxygen.

i. The following capitalised prefixes shall be used as appropriate:

i. WARNING: highlights sections of code with subtle dependencies which

require consideration before changes are made.

ii. NOTE: provides descriptive “why” comments, e.g. explaining a non-obvious

decision or an assumption made by the original programmer.

iii. TODO: indicates code still under construction, and explains what work

remains to be done.

iv. TEMP: marks code which has been temporarily disabled.

RATIONALE:

Clear commenting encourages clear thinking, which produces better code.

Use of Doxygen keeps documentation near the relevant code, increasing the

likelihood that it will remain relevant and correct.

Page 91: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 4: Modules

A-13

4 MODULES 4.1 MODULE NAMING CONVENTIONS RULES:

a. All module filenames shall consist only of lowercase letters, numbers and underscores.

i. No spaces shall ever appear within a filename.

b. Module names shall be unique within their first 8 characters.1

c. Source and header files shall have the extensions .c and .h respectively.

d. No module shall share the name of a C Standard Library header file.

e. Any module containing a main() function shall have the word “main” in its

filename.

RATIONALE:

Consistent use of lowercase file naming ensures cross-platform and cross-

toolchain compatibility – on Unix-like systems, sci.h and SCI.h are two different

files, but these names cannot be distinguished by Windows.

Rule 4.1e is intended to bring clarity to project structure. For example, given a

project Foo, both main.c and foo_main.c are permissible filenames for the module

which contains the function main().

EXCEPTIONS:

None.

1 C99 specifies (section 5.2.4.1) that the minimum supported “significant initial characters” for external identifiers be in excess of 31 characters. ANSI C (C89) required only 6 characters. Although the new limit is much higher, 8 characters has been chosen for readability.

Page 92: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 4: Modules

A-14

4.2 HEADER FILES RULES:

a. There shall always be precisely one header file for each source file, and both shall

always have the same root name.

b. Each header file shall contain a preprocessor #ifndef guard against multiple

inclusion.

c. The header file shall define only the external interface of a module.

i. Functions, data types and structures, constants, and macros internal to a

module should not be placed in the header file.

ii. No storage for any variable shall be declared in the header file.

iii. Use of internal datatypes outside of their module is strongly discouraged –

public interface functions should use only standard datatypes. Note that this

is not intended to forbid the use of enumerated types in public interfaces.

iv. A header may use the extern keyword to share a constant with another

module, and this is encouraged as the correct way to share constants when

necessary.

v. Use of the extern keyword within a header file to share a global variable

with other modules is strongly discouraged. When cross-module access to a

global variable is required, it is preferable to provide public “Get” and “Set”

functions. If direct access must be provided, the global variable shall be

declared volatile and protected from race conditions wherever it is used.

d. Headers should not include other headers.

i. An exception may be made for header files whose sole purpose is to collate

common includes to reduce code clutter. The number of such header files

should be small.

e. The prototype of an Interrupt Service Routine shall not be included in the public

interface presented by the module’s header file. ISR’s are special and should never

be called by other functions. See also Rule 4.3b.x.

Page 93: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 4: Modules

A-15

RATIONALE:

C defaults to giving variables and functions global scope. This increases coupling

between modules, which can result in unexpected and dangerous side effects.

Minimising scope reduces this unnecessary coupling.

EXAMPLE:

An appropriate preprocessor guard against multiple inclusion is shown below:

#ifndef _EXAMPLE_H

#define _EXAMPLE_H

...

#endif /* _EXAMPLE_H */

EXCEPTIONS:

None.

Page 94: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 4: Modules

A-16

4.3 SOURCE FILES RULES:

a. Each source file shall contain only functionality related to one logical module.

Examples of appropriate logical modules include peripheral drivers and

communication protocols.

i. It may be appropriate to divide complex modules into smaller sub-modules.

b. Each source file shall be comprised of some subset of the following list of sections,

ordered as listed. Each section shall be introduced by a comment.

i. Introductory comment block

ii. #include directives

iii. Datatype definitions

iv. Constant definitions

v. Macro definitions (but note the restrictions on macro use in 6.2g)

vi. Static data declarations (globals)

vii. Private function prototypes

viii. Public function bodies

ix. Private function bodies

x. Interrupt Service Routines

c. Each source file shall #include the header file of the same name if it exists.

d. #include directives shall always use relative paths.

e. Each source file shall include only header files which are used in that module.

f. No source file shall #include another source file.

RATIONALE:

Consistent internal layout makes the purpose and structure of modules clear to

the maintainer.

Rule 4.3c ensures that the compiler checks each public function for consistency

with its prototype.

Rule 4.3b.x is complementary to Rule 4.2e – by both hiding the ISR’s prototype

and placing the ISR at the end of the source file, no normal function can

inadvertently call the ISR.

EXCEPTIONS:

None.

Page 95: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 5: Data Types

A-17

5 DATA TYPES 5.1 NAMING CONVENTIONS RULES:

a. All new structures, unions, and enumerations shall be named as a type via typedef

b. All new types shall be named with the suffix _t

c. Type names shall begin with a lowercase letter and use camel case to separate

words (i.e. capitalising the first letter of each subsequent word)

i. An underscore may be used to separate words if required for

legibility.

d. Any datatype that forms part of a module’s public interface shall have its name

prefixed by the name of the module (with the first letter capitalised) followed by

an underscore.

RATIONALE:

Type names, variable names, and function names within a module may all be very

similar. Adding a suffix creates an appropriate distinct name.

EXAMPLE: // New type defined in header file: Part of public interface per

5.1d

typedef struct

uint8_t frequency;

uint16_t anotherVar;

Audio_dataExample_t

EXCEPTIONS:

It is not necessary or desirable to use typedef on structures and unions defined

within another structure or union. Note, however, that anonymous structs and

unions are not supported in ISO C versions prior to C11, so such structs and

unions must be named for portability.

Page 96: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 5: Data Types

A-18

5.2 INTEGER TYPES RULES:

a. Prefer the use of the following fixed-width types, supplied by <stdint.h>:

Width Signed Unsigned

8 bits int8_t uint8_t

16 bits int16_t uint16_t

32 bits int32_t uint32_t

64 bits int64_t uint64_t

b. The keywords short and long shall not be used.

c. Use of the char type shall be restricted to operations concerning strings.

d. None of the bit-wise operations shall be used to manipulate signed data.

e. Signed integers shall not be combined with unsigned integers in comparisons or

expressions.

i. In general, signed types should be considered the default choice for all

mathematical operations including loop counters.

RATIONALE:

The size of C standard types is implementation defined. C99 adds the <stdint.h>

header which provides cross-platform support for fixed-width types.

EXCEPTIONS:

If the compiler does not support C99, it is appropriate to create the necessary

typecasts to provide the expected fixed-width types based on char, short, int,

long, and long long

5.3 FLOATING POINT TYPES RULES:

a. Avoid the usage of floating point types wherever possible. Fixed-point math may be a useful alternative.

b. Never test for equality or inequality of any floating point value.

RATIONALE:

Many microcontrollers do not natively support floating point math, but silently

implement it by linking in a large and slow emulation library.

The nature of floating point math is such that values cannot accurately be

compared without first rounding to a known precision.

EXCEPTIONS:

None.

Page 97: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 6: Procedures and Functions

A-19

6 PROCEDURES AND FUNCTIONS 6.1 NAMING CONVENTIONS RULES:

a. No procedure shall have a name that is a keyword or base type in C or C++, including K&R C, C89, C99, C++98, C11 and C++11. Restricted names include

interrupt, inline, restrict, class, true, false, public, private, friend, and protected

b. No procedure shall have a name that overlaps with a procedure from the C

standard library, e.g. printf or strlen

c. No procedure shall have a name that begins with an underscore.

d. Each procedure’s name shall be descriptive of its function.

i. Appropriate names will often include verbs that describe what the function

does, e.g. Adc_Read()

ii. Alternatively, functions may be named based on the question they answer,

e.g. IsDataNew()

e. Capitalisation shall be used to separate words in procedure names.

i. An underscore may be used to aid readability, but only if the resulting name

is clearly not of the form used in rule 6.1g

f. Initial capitalisation shall be used to indicate scope:

i. Initial lowercase letters indicate a private function, e.g. doThis()

g. The names of all public functions shall be prefixed with the name of their module

followed by an underscore and an initial capital, e.g. Uart_StatusFlag

RATIONALE:

Naming rules exist to ensure consistent usage for clarity.

EXCEPTIONS:

None.

Page 98: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 6: Procedures and Functions

A-20

6.2 FUNCTIONS RULES:

a. A prototype shall be defined for each public function in the module header file.

b. A prototype shall be defined for each private function in the module code file. To

ensure consistency checking, the prototypes shall be placed before any public

functions, and the private functions shall be placed after any public functions.

c. All private functions shall be declared static

d. Each parameter shall be explicitly declared and meaningfully named.

e. Functions with a return value shall have at least one return statement located at

the end of the function.

i. Additional return statements are typically permissible, but note that they

are forbidden by several standards for safety-critical systems including

MISRA-C and IEC61508

f. Functions should be kept to a reasonable length – around 50-100 lines, or brief

enough to print on an A4 page.

g. Function-like macros shall not be used if an inline function can be used to the same

ends.

h. If a function-like macro must be used, its design is subject to the following rules:

i. Surround the entire macro body with parentheses.

ii. Surround each use of a parameter with parentheses.

iii. Use each argument only once, to avoid unintended side effects.

RATIONALE:

Producing code that uses neither break statements nor multiple exit points is

difficult, so this standard chooses to allow the lesser of two evils.

Function-like macros are largely unnecessary given the introduction of the inline

keyword in C99. If they must be used, it is important to avoid side effects such as

multiple incrementation.

EXCEPTIONS:

None.

Page 99: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 6: Procedures and Functions

A-21

6.3 INTERRUPT SERVICES ROUTINES RULES:

a. Interrupt service routines shall be named with the suffix _isr

b. To ensure ISR’s are not inadvertently called from other parts of the code, ISR’s shall

not have a prototype in either their module code file or their module header file,

and shall be placed at the end of the associated module.

c. If possible on the implementation architecture, all ISR’s should be declared static.

i. Note that this is not possible on architectures which make use of a vector

table of pointers to ordinary functions, such as ARM Cortex-M.

d. If required by the implementation architecture, ISR’s should be indicated to the

compiler using the appropriate #pragma or non-ANSI keyword.

e. A stub or default interrupt handler shall be installed in the vector table for all

unused interrupt sources.

RATIONALE:

ISR’s are not normal functions.

EXCEPTIONS:

None.

Page 100: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 7: Variables

A-22

7 VARIABLES 7.1 NAMING CONVENTIONS RULES:

a. No variable shall have a name that is a keyword or base type in C or C++, including K&R C, C89, C99, C++98, C11 and C++11. Restricted names include interrupt,

inline, restrict, class, true, false, public, private, friend, and protected

b. No variable shall have a name that overlaps with a variable from the C standard

library.

c. No variable shall have a name that begins with an underscore.

d. No variable name shall contain references to data subject to external change – for

example, the number of bits in the underlying type.

e. Each variable’s name shall be descriptive of its purpose.

i. Loop counters may make use of common single-letter abbreviations (i, j, k)

provided that these variables are declared and initialised in the loop control

expression, thus limiting their scope to the loop itself.

f. Capitalisation shall be used to separate words in variable names.

i. An underscore may be used to aid readability.

g. Initial capitalisation shall be used to indicate scope:

i. Initial lowercase letters indicate a local variable, e.g. someTempVar

ii. Initial uppercase letters indicate a variable with module scope, e.g. SomeModuleVar

h. The names of all constants shall be prefixed with k_

i. The names of all variables (including constants) visible outside their own module

shall be prefixed with the name of their module followed by an underscore, e.g. Uart_StatusFlag

i. Conversely, no variable of local or global scope may be named following this

pattern.

j. The names of all pointer variables shall be suffixed with Ptr. Pointers to pointers

shall be suffixed with PtPtr

RATIONALE:

Many compilers reserve names beginning with an underscore for internal use.

Other naming rules exist to ensure consistent usage for clarity.

EXCEPTIONS:

None.

Page 101: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 7: Variables

A-23

7.2 INITIALISATION RULES:

a. All variables shall be initialised before use.

b. Variables shall be created near to where they are used and with the minimum

possible scope.

RATIONALE:

C does not perform compile or run-time checking for initialised data, nor does the

specification call for declared variables to be automatically initialised to a known

value.

Initialisation of variables near where they are used aids readability. C99 permits

for variables to be created anywhere in a function, not just after an opening brace

as in C89;

EXCEPTIONS:

None.

Page 102: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 8: Expressions and Statements

A-24

8 EXPRESSIONS AND STATEMENTS 8.1 VARIABLE DECLARATIONS RULES:

a. Pointers and arrays shall be declared each on their own line.

i. If a pointer to a type has been named using typedef , multiple declaration of

the resulting type is permitted.

RATIONALE:

See the example – it is very easy in C to unintentionally declare only a single

variable as a pointer in a multiple declaration.

Declaration of multiple arrays is forbidden for readability.

EXAMPLE: // It is very easy to accidentally do this:

int * x, y, z; // Only x is a pointer

// This is correct, but forbidden by the rule for consistency and

readability:

int * x, * y, * z;

EXCEPTIONS:

None.

Page 103: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 8: Expressions and Statements

A-25

8.2 IF-ELSE STATEMENTS RULES:

a. The shortest (in lines of code) of the if and else if clauses should be placed first.

b. If-else statements shall not be nested deeper than two levels.

c. Assignments shall not be made within an if or else if expression.

d. Long switch statements should be avoided in favour of arrays of constants or

function pointers.

RATIONALE:

If-else structures should be formatted for readability. Complex nested structures

should be redesigned (e.g. as switch statements or function calls) to be easier to

follow, more readable, and more robust.

Assignments within an if expression are never necessary, and are nearly always

unintended errors. Intentional use is thus forbidden to aid code clarity and

automated code inspection.

EXCEPTIONS:

It may, in efficiency-driven applications, be necessary to reorder if statements to

ensure that the most critical case is executed fastest.

8.3 SWITCH STATEMENTS RULES:

a. All switch statements shall contain a default block.

b. When the break; is omitted from a case block to intentionally produce “fall-

through”, this shall be documented in a comment on the line where the break;

would normally appear.

c. The break; for each case shall be indented to the same level as the associated case

statement, rather than aligning with the contents of the enclosed case block.

d. Long switch statements should be avoided in favour of arrays of constants or

function pointers.

RATIONALE:

Bugs may be introduced by unhandled cases.

A missing break statement causes unintended fall-through to the following case.

This is a simple and common error, but the proposed alignment allows this error

to be easily spotted as an anomaly.

EXCEPTIONS:

None.

Page 104: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 8: Expressions and Statements

A-26

8.4 LOOPS RULES:

a. Constants shall be used (in preference to magic numbers) in the controlling

expressions of for and while loops.

b. Except for initialising the loop counter in the first clause of a for statement,

assignments shall not be made in a loop’s controlling expression.

c. Loops with an empty body (“busy wait” loops) shall include a comment explaining

their purpose.

d. Infinite loops shall be implemented using the expression for (;;) rather than while (1)

RATIONALE:

C does not range-check accesses to arrays. Loops which are not synchronised to

the size of the addressed data structure thus risk corrupting data outside of the

array. Such bugs can be prevented by use of a single constant for the array

declaration and its subsequent use in a loop controlling expression. Note that C99

does not permit const integers to be used as array dimensions, so this will require

the use of a #define.

Assignments in a loop’s controlling expression (except for the counter

initialisation) are never necessary, and create confusion regarding when the

expression will be evaluated. Include them inside the loop instead.

Regarding rule 8.4d: for (;;) and while (1) are technically equivalent, but the

latter includes a condition which always evaluates as true. Some compilers will

flag this with a warning, so the former style is preferred.

EXCEPTIONS:

It is frequently appropriate to start a loop with the counter initialised to zero, and

it is similarly appropriate to use zero as the end condition of a loop which counts

down (e.g. when iterating towards the base of an array). Used in these contexts,

the integer value 0 shall not be considered a magic number.

Page 105: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 8: Expressions and Statements

A-27

8.5 UNCONDITIONAL JUMPS RULES:

a. Unconditional jumps shall not be used:

i. All use of the goto keyword is forbidden.

ii. All use of the continue keyword is forbidden.

iii. Use of the break keyword outside of switch statement is forbidden.

RATIONALE:

Use of unconditional jumps is associated with “spaghetti code” – that is, code

which is difficult to follow and test. These language structures are unnecessary

and potentially dangerous.

EXCEPTIONS:

Use of the break keyword within switch statements is permitted.

Page 106: Capstone Report

Appendix A: Coding Standard Christopher Kerr Chapter 8: Expressions and Statements

A-28

8.6 EQUIVALENCE TESTS RULES:

a. When testing for equivalence between a variable and a constant value, always

place the constant on the left side of the comparison ( == ) operator.

i. For visual consistency, this rule should also be followed when testing

inequality ( != ).

RATIONALE:

Assignment instead of comparison is an extremely simple and common error.

Adherence to this rule permits errors to be detected at compile-time.

EXAMPLE: // Testing for equivalence:

const uint8_t someConstant = 5;

uint8_t testVar = 5;

if (k_someConstant == testVar)

// This is the correct method

if (testVar == k_someConstant)

// rule breach, because of the following possibility:

if (testVar = k_someConstant)

// Assignment in condition will always evaluate as true,

// but is perfectly legal C

if (k_someConstant = testVar)

// Following the rule prevents this issue, because

// assignment to a constant will cause a compiler error

EXCEPTIONS:

None.