EMBEDDED DESIGN FOR A -BASED DATA LOGGER FOR STEPPER MOTOR FAULT DIAGNOSIS

A Thesis Presented

by

Michael Thomas Daniel

to

The Department of Electrical and Computer Engineering

in partial fulfillment of the requirements for the degree of

Master of Science

in

Electrical Engineering

in the field of

Power Systems

Northeastern University Boston, Massachusetts

April 2012 ABSTRACT

Two independent pieces of embedded software have been developed to operate a data log- ging system based on a PIC18F8722 microcontroller for a stepper motor diagnosis appli- cation. The first piece of embedded software is implemented with several state machines in a foreground/background (superloop) structure, while the second is implemented as an RTOS-enabled software based on the Salvo Lite cooperative real time . The superloop implementation results in data for up to 8 stepper motor channels being logged to an SD card and RS-232 link at a rate of 10 Hz, with approximately 50% over- head time reserved for RS-232 communications. The RTOS-enabled implementation results in an improved high priority interrupt service routine execution time, but is overall slower to respond to interrupts compared to the superloop implementation due to the cooperative nature of the chosen operating system. A custom binary-to-ASCII conversion software is also developed for converting the resulting data log file produced by both software imple- mentations to human readable text, as the result of both implementations is a binary data log file containing data of interest for up to 8 stepper motors. This page intentionally left blank as a place holder for the Approval Record. 2012 Michael Thomas Daniel ALL RIGHTS RESERVED

Northeastern University, the faculty adviser, and the author assume no legal and/or financial liability for any statement in this thesis.

i Dedicated to my parents.

ii ACKNOWLEDGEMENTS

While there were countless people who helped me with this work, there are a few who deserve special thanks. Without them, the success of this work would not have been possible. First of all, thank you to Michael Taranowski1 for hiring me to work at LLNL and assigning me to the project that would become the inspiration for this work. I would also like to express my gratitude to Mark Miller1 and Danielle Doane1 who both made my work at LLNL possible. Thanks to Matthew Dayton1 and LeRoy Bennett1 for their help and advice on programming for embedded systems with C. I also extend thanks to Jim McBride1, Eric Imhoff1, and Timm Wulff1 for their help and advice on developing hardware test set-ups for embedded systems. Thank you to Professor Ali Abur2 for his continuous guidance throughout the course of this work, and to Professor Gunar Schirner2 for his practical advice on programming for embedded systems. Thanks to Professor Dionisio Bernal2 for his encouragement to take on this work. Thanks are also extended to Professor Sara Wadia-Fascetti2 for hiring me to my position with the IGERT Intelligent Diagnostics group at Northeastern University, which has made this work possible. The ability to test my designs in the ECE laboratories, which was essential to this work, was made possible by Dr. Amir Farhat2, Edward Tyrance2, and Stephen Keane2. This work has been supported by NSF Grant Number DGE - 0654176.

1Lawrence Livermore National Laboratory 2Northeastern University

iii Contents

1 Introduction 1

1.1 Thesis Statement and Objectives ...... 1

1.2 Significance and Importance of Work ...... 3

1.3 Background Information Leading to Thesis Formulation ...... 5

1.4 Summary of the Relevant Literature ...... 6

1.5 Organizational Summary of Thesis ...... 10

2 Technical Background 12

2.1 Stepper Motors, Drivers, and Faults ...... 12

2.2 Signals of Interest and System Technical Requirements ...... 19

2.3 Early Design Decisions: Laying the Foundation ...... 23

2.3.1 The Microcontroller and Its Interfaces ...... 23

2.3.2 Development Hardware and Constraints ...... 32

2.3.3 Development Software and Constraints ...... 34

3 Application Software - Superloop Version 38

3.1 Structure and Function ...... 38

3.1.1 The Background Loop ...... 42

3.1.2 The Foreground Loop ...... 57

3.1.3 Overall Superloop Version Software Structure ...... 63

iv 3.2 Data Log Conversion Software ...... 65

3.3 Testing Methods ...... 68

3.4 Results and Evaluation ...... 71

4 Application Software - RTOS Version 89

4.1 RTOS Fundamentals ...... 89

4.2 Structure and Function ...... 93

4.2.1 Initialization Tasks ...... 99

4.2.2 Get Data Log Task ...... 103

4.2.3 Put Data Log Task ...... 106

4.2.4 Handle UART Task ...... 109

4.2.5 Date/Time Task ...... 112

4.2.6 RTOS Version Software Structure and Function in Steady State . . . 115

4.3 Testing Methods ...... 116

4.4 Results and Evaluation ...... 118

5 Concluding Remarks 137

5.1 Comparison of the Results and Evaluations ...... 137

5.2 Opportunities for Future Work ...... 140

5.3 Broader Impacts ...... 141

A Hardware Schematics 142

B Application Specific Source Code 148

B.1 Application Source Code - Superloop Version ...... 149

B.1.1 MainSrcCode.c ...... 149

B.1.2 AuxSrcCode.c ...... 158

B.1.3 CustomHeader.h ...... 171

v B.1.4 18f8722_MDDFS.lkr ...... 172 B.2 Application Source Code - RTOS Version ...... 174 B.2.1 main.c ...... 174 B.2.2 auxiliary.c ...... 187 B.2.3 main.h ...... 198 B.2.4 salvocfg.h ...... 199 B.3 Binary to ASCII Converter Source Code ...... 200 B.3.1 DataLogBin2ASCIIVer4.c ...... 200 B.4 MATLAB Files ...... 204 B.4.1 StepCountingApproximation.m ...... 204

vi List of Figures

2.1 Basic operation of a two phase permanent magnet stepper motor with four poles ..... 14

2.2 Functional block diagram of TI DRV8811 from the DRV8811 data sheet...... 15

2.3 Detailed block diagram of TI DRV8811 output stage from the DRV8811 data sheet. .... 16

2.4 Stepper motor using a linear gear and rails for a linear positioning application...... 17

2.5 Functional block diagram of the PIC18F8722 device from the PIC18F8722 data sheet .. 25

2.6 Pin diagram of the PIC18F8722 device from the PIC18F7822 data sheet ...... 26

2.7 Physical interfaces between NIF motor drive board, data logger, SD card, and PC. .... 27

2.8 Pin arrangements for both SD card and RS-232 physical interfaces ...... 28

2.9 Operation of the SPI bus using shift registers...... 29

2.10 Microcontroller internal hardware configuration for this application...... 29

2.11 Ideal function of each MCU pin given the data logging application at hand...... 31

2.12 Hardware development tools used in this application. Top left is PICkit3 debugger, top right

is SD card, bottom left is PICDEM PIC18 board, and bottom right is PICtail Daughter

Board for MMC/SD Cards...... 32

2.13 Function of each MCU pin given the data logging application at hand combined with devel-

opment tool constraints...... 37

3.1 Visual representations of the superloop program structure used by the embedded application

software...... 39

3.2 Project structure in MPLAB IDE for implementing superloop embedded application software. 40

vii 3.3 State machine used in main() to implement initialization and background loop...... 43

3.4 Flow chart showing the possible paths of background loop execution; line numbers refer to

MainSrcCode.c which is found in Appendix B.1.1...... 44

3.5 Circular buffer represented both as a ring as well as linearly; the circular buffer is first-in

first-out or “FIFO”...... 47

3.6 Linear representations of some key circular buffer conditions for this application. .... 48

3.7 Data log structure; note that one “datalog” structure includes an array of eight “channel”

structures, which in turn each include a “bitfield” structure...... 49

3.8 Structure of the binary data log file...... 51

3.9 Flow chart describing the handling of RS-232 commands received via UART...... 56

3.10 State machine used to implement foreground loop in HPISR; note that control is returned

to the background upon completion of each state...... 58

3.11 Diagram showing the order and manner in which commanded motor steps are counted by

the MCU ...... 59

3.12 Theoretical motor velocity and position given 200 steps per second squared acceleration for

50 seconds. The MATLAB script used to generate this figure can be found in Appendix B.4.1. 59

3.13 Detail of Figure 3.12 showing motor position from t=49.9s to t=50.0s...... 60

3.14 Flow chart describing the execution of foreground state three...... 61

3.15 High level overall description of superloop application software structure and function .. 64

3.16 Flow chart describing the execution of the data log binary to ASCII conversion application. 66

3.17 Lab bench set up for testing embedded application software. Note the DC supply limited to

25mA for analog inputs, and signal generator for step inputs. The development tools are

on the bench in front of the oscilloscope; the PC to the right is used to control software

execution on the PIC18F8722 via the PICkit3 debugger interface and MPLAB IDE. ... 69

3.18 Binary data log file when interpretted directly as ASCII characters by Notepad...... 72

viii 3.19 Win32 console displayed by binary-to-ASCII conversion program during execution. Note

that the program waits for the user to acknowledge conversion complete before exiting. .. 72

3.20 Final data log correctly converted to ASCII text...... 73

3.21 Channels 5 throught 8 of the final text data log file...... 74

3.22 Beginning of hyperterminal output for superloop test case...... 76

3.23 HyperTerminal output when data logging operations are stopped with both outputs enabled.

Terminal output is disabled before data logging is restarted...... 78

3.24 HyperTerminal output when data logging is stopped with only SD card output enabled. SD

card output is disabled before resuming data logging operations...... 79

3.25 10 kHz step signal supplied to input for channels 1-4 (top, yellow), and 32.768 kHz step

signal supplied to input for channels 5-8 (bottom, green) ...... 80

3.26 Terminal output resulting from 10 kHz step input to channels 1-4 and 32.768 kHz input to

channels 5-8. Step counts of last data log highlighted in red for easy identification. .... 80

3.27 Superloop HPISR execution period. High level indicates HPISR is executing...... 82

3.28 Detail of shorter HPISR execution time showing execution time measurements...... 82

3.29 Detail of longer HPISR execution time showing execution time measurements...... 83

3.30 Detail of data logging to SD card period. A high level indicates this portion of the software

is executing...... 84

3.31 Detail of typical SD card write time showing execution time measurement...... 84

3.32 Detail of non-typical SD card write time showing execution time measurement...... 85

3.33 Data logging to Terminal period. A high level indicates this portion of the software is executing. 85

3.34 Detail of Terminal write time showing execution time measurement...... 86

3.35 Superlooper version initialization time as captured by the oscilloscope. High value indicates

initialization in progress...... 87

3.36 Visual representation of superloop embedded application software memory usage...... 87

ix 4.1 Example of context switching between two tasks resulting in multitasking ...... 91

4.2 Possible task states and possible paths between states. All states are initially destroyed. .. 92

4.3 Project structure in MPLAB IDE for implementing RTOS-enabled embedded application

software...... 97

4.4 Visual description of main.c for RTOS-enabled software showing order of task creation

and function of ISRs; red highlight indicates steady state tasks during regular data logging

operations...... 98

4.5 Possible task states and paths between task states for InitPeripheralsTask...... 100

4.6 Possible task states and paths between task states for InitSDCardTask...... 101

4.7 Flow chart describing the basic execution of both peripheral hardware module and SD card

initialization tasks...... 102

4.8 Possible task states and paths between task states for GetDataLogTask...... 104

4.9 Flow chart describing the basic execution of data gathering task GetDataLogTask. .... 105

4.10 Possible task states and paths between task states for PutDataLogTask...... 106

4.11 Flow chart describing the basic execution of data logging task PutDataLogTask...... 108

4.12 Possible task states and paths between task states for HandleUARTTask...... 110

4.13 Flow chart describing the basic execution of RS-232 command handling task HandleUART-

Task...... 111

4.14 Possible task states and paths between task states for DateTimeTask...... 113

4.15 Flow chart describing the basic execution of DateTimeTask...... 114

4.16 Steady state multitasking between gathering data from inputs and logging data to outputs

with RTOS-enabled embedded application software...... 115

4.17 Original binary data log as produced by RTOS-enabled software...... 118

4.18 Win32 console during conversion of data log file produced by RTOS-enabled software. .. 119

4.19 Resulting text data log from RTOS-enabled software after conversion...... 119

4.20 Motor channels 5-8 of the converted data log file...... 120

x 4.21 HyperTerminal output during RS-232 command testing of RTOS-enabled software. Note

that the software has just finished initialization...... 123

4.22 HyperTerminal output during RS-232 command testing of RTOS-enabled software. Note

that data logging is stopped before disabling Terminal output...... 124

4.23 HyperTerminal output during RS-232 command testing of RTOS-enabled software. Note

that data logging is stopped before disabling SD card output...... 126

4.24 Terminal output resulting from 10 kHz step input to channels 1-4 and 32.768 kHz input to

channels 5-8. Step counts of last data log highlighted in red for easy identification. .... 127

4.25 RTOS-enabled HPISR execution period. High level indicates HPISR is executing. .... 128

4.26 Detail of HPISR execution time showing execution time measurement...... 129

4.27 Execution period of GetDataLogTask...... 129

4.28 GetDataLogTask execution period when only step counts are gathered...... 130

4.29 GetDataLogTask execution period when step counts and other digital and analog data must

be gathered...... 131

4.30 Detail of data logging to SD card period...... 131

4.31 Detail of typical SD card write time showing execution time measurement...... 132

4.32 Detail of non-typical SD card write time showing execution time measurement...... 132

4.33 Data logging to Terminal period...... 133

4.34 Detail of Terminal write time showing execution time measurement...... 134

4.35 RTOS-enabled version initialization time as captured by the oscilloscope. High value indi-

cates initialization in progress...... 135

4.36 Visual representation of RTOS-enabled embedded application software memory usage. .. 135

A.1 Part one of PICDEM PIC18 Explorer Board schematic from its user manual...... 143

A.2 Part two of PICDEM PIC18 Explorer Board schematic from its user manual...... 144

A.3 PICtail Daughter Board for SD/MMC Cards schematic from its data sheet...... 145

xi A.4 Part one of PICkit3 debugger interface schematic from its user manual...... 146

A.5 Part two of PICkit3 debugger interface schematic from its user manual...... 147

xii List of Tables

2.1 Summary of the characteristics of signals of interest ...... 20

3.1 Summary of MCU outputs used to measure superloop software execution periods of interest. 70

4.1 Summary of MCU outputs used to measure RTOS-enabled software execution periods of

interest...... 116

xiii Chapter 1

Introduction

1.1 Thesis Statement and Objectives

To begin this thesis, it is appropriate to introduce a few things that are necessary for the understanding of the purpose and objectives of this work, such as data loggers, stepper motors, and the National Ignition Facility.

What is a data logger? Combining several sources[25][9][8][10][21][6][7], a data logger can be summarized as a device that records signals of interest obtained from instruments and sensors and relates them to a time or location for later use. Data loggers are often confused with data acquisition devices; note that these two terms do not necessarily describe the same device. The former refers to a device that records data over relatively long periods of time for analysis of slow-varying trends or infrequent anomalies, whereas the latter refers to a device that records data at a much higher sampling rate and shorter time. The first data loggers were simply people who recorded data by hand to a physical logbook, however modern data loggers are typically based on digital electronics such as a , microcontroller (MCU), field-programmable gate array (FPGA), or personal computer. and are popular choices for stand-alone devices; the differences between these

1 two devices, as well as the advantages of using a microcontroller will be outlined in the follow- ing chapter. Today’s data loggers are used to record such properties as acceleration, altitude, pressure, voltage, current, temperature, frequency, and overall system status. They come in many shapes, sizes, and styles, and are used in countless applications across many industries. Some of the most noted applications are aircraft flight recorders, on-board diagnostic units in automobiles, and remote weather stations. Before going any further, it is also important to understand the basic function of stepper motors. A more detailed description will be given in the following chapter, but for now it is sufficient to consider a stepper motor as a brushless DC motor that may divide its full revolu- tion into several discrete steps[24][19]. These motors are popular in many applications such as pick-and-place machines, CNC machines, disk drives, computer printers, and especially applications that require precise open loop position control[19]. One unique application of stepper motors is for various control applications at the National Ignition Facility (NIF) located at Lawrence Livermore National Laboratory (LLNL) in Livermore, California. The National Ignition Facility is an experimental laser Inertial Confinement Fusion (ICF) facility[13]. Using the world’s largest and most energetic laser[13], NIF creates the in- tense temperatures and pressures necessary for nuclear fusion at the focus of 192 individual beams (derived from a single laser pulse) in the center of its ten-meter-wide spherical target chamber[12]. These ICF experiments are used for research in high energy density physics, national nuclear security, fusion as a viable energy source, and many other fields[13]. NIF is a large and complex facility and employs approximately 10,000 stepper motors of various sizes and specifications for beam alignment, sensor packages, as well as beam amplifiers and splitters, among other things[27]. In short, reliable operation of these motors is necessary for the safe and efficient operation of the facility. A more detailed technical description of NIF is beyond the scope of this thesis, but can be found at https://lasers.llnl.gov/.

2 The problem this thesis addresses is that when a NIF stepper motor experiences a fault (to be defined in the following chapter), there is no data log (sometimes referred to as sequence-of-events, or SOE, log) that can be used to reconstruct the internal and external conditions surrounding the motor at the time of the fault, which makes fault diagnosis time consuming and inefficient, which in turn can lead to a delay in NIF experiments. It has been proposed that a custom microcontroller-based data logging system could be implemented to create an SOE log that could be consulted to more rapidly diagnose motor faults[2].

Therefore, the objective of this work is to design the embedded software to operate the chosen microcontroller as the basis of a data logger that can be used in a NIF stepper motor diagnosis application to increase the overall operational availability of the facility, and to reduce the duration and frequency of delays due to motor faults. For this work two pieces of embedded software are developed for this task: one that follows a basic foreground- background (superloop) structure, and a second that is based on a real-time operating system (RTOS). Any auxiliary software needed to allow a user to interpret the resulting data log will also be designed. More detailed requirements will be developed in the following chapter.

1.2 Significance and Importance of Work

The National Ignition Facility is a unique facility that can achieve conditions for scientific experiments that cannot be achieved anywhere else on Earth[13]. The result is a long list of important experiments with applications in astrophysics, lasers, radiation, national nuclear security, and fusion energy, that all require the use of one facility[14]. It follows then, that a system that seeks to increase the overall operational availability of NIF has significant merit.

Furthermore, as NIF continues the National Ignition Campaign (NIC) towards achieving controlled fusion ignition, NIF shots1 are becoming more frequent and must adhere to a tight

1The term “shot” is used to describe the act of energizing some or all of NIF’s laser beams and directing them into the center of the target chamber. Not all shots include the use of a target. Also note that target

3 schedule that may leave as little as 15 hours between shots[15]. Because stepper motors are integral to many NIF operations, diagnosis and correction of motor faults must occur at a faster pace than before in order to keep up with the aggressive NIC shot schedule[15]. Again, a system that would allow motor faults to be more rapidly and efficiently diagnosed is significant in this situation, as it would decrease delays in the NIF shot schedule due to motor faults. In fact, accumulated diagnostic data could be used diagnose chronic problems with particular motors or drive circuits, and to act as a virtual odometer for motors to allow replacement or maintenance at periodic intervals, ideally avoiding faults before problems occur. It is obvious that it is important to maintain the operational availability of NIF, but it may not be as obvious why avoiding delays to individual experiments may be just as impor- tant. The exact number and duration of NIF delays is beyond the scope of this document; however, shot delays due to stepper motor faults at NIF are not unheard of. While the NIC is increasing shot frequency, many hours may still be required between shots[15][27]. This necessarily limits the number of experiments that can be performed at the facility, so a delay in a single shot may have a significant impact on experiments further down the line. To compound the issue, shots are often scheduled at night[11]. Therefore, a shot initially delayed for a few hours may need to be delayed to the following evening. These delays can be even more costly when a cryogenic-cooled target is being used, as the target tempera- ture must be controlled within one thousandth of a Kelvin at cryogenic levels[16], and the target must wait until only 5-7 minutes before the shot when it is inserted into the target chamber[27]. Because each target is a precise and complicated assembly requiring a special- ized in-house assembly facility[16], and each cryogenic target must be shot within 36 hours of its fabrication[16], a delayed shot could incur more delays and expenses if the cryogenic target is lost during the initial delay. composition, beam power, beams used, and other characteristics often vary from shot to shot.

4 The ability to increase the overall operational availability of NIF and decrease the dura- tion and frequency of delays that would be given by this system is of significant importance.

1.3 Background Information Leading to Thesis Formu-

lation

In the spring of 2011 I was hired to a Summer Scholar position at the National Ignition Facility located at LLNL. Upon arriving at LLNL I was assigned the project of evaluating the feasibility of using a PIC18F8722 microcontroller as the basis of a diagnostic data logger for NIF stepper motors. This project2 included significant embedded software development and testing, as well as testing with NIF stepper motors and drive circuits. The project was largely successful, was awarded “Best Summer Project 2011” by NIF Director Edward Moses, and showed (among other things) that it would be possible to implement the proposed data logger using a pair of PIC18F87K22 MCUs[2]. The results of this project were released to a public audience in a poster[2] under NIF review and release number NIF-POST-492233. Any descriptions of my work2 performed at LLNL that appear in this thesis have already been released under NIF-POST-492233.

At the conclusion of my time at LLNL my work seemed to provide a good basis for a relevant and practical master’s thesis. After speaking with my supervisors at LLNL and my thesis adviser at Northeastern University (NU) it was decided that I would pursue a thesis based on (but not including) my work at LLNL. That is to say this thesis uses the publicly released results of my work at LLNL as its inspiration, but does not include any work performed at LLNL. To clarify: this thesis is not sponsored by, endorsed by, or otherwise affiliated with Lawrence Livermore National Laboratory or the National Ignition Facility in

2Performed under the auspices of the U.S. Department of Energy by Lawrence Livermore National Lab- oratory under Contract DE-AC52-07NA27344

5 any way, except that it takes the results of my work, which were publicly released, as its inspiration.

The thesis and objectives given in Chapter 1, Section 1 of this document were developed from the results of my work at LLNL from June 2011 through September 2011.

1.4 Summary of the Relevant Literature

Besides the poster summarizing my work at LLNL from June 2011 through September 2011, there has been no published work on developing a diagnostic data logging system for stepper motors at the National Ignition Facility.

However, there is extensive literature on various data logging applications for microcon- trollers, as well as on the more specific hardware and software elements that are necessary to use an MCU is such an application. A summary of the more relevant literature is given here.

Two papers by Dr. Dogan Ibrahim of Near East University are quite relevant to this work. The first, titled “Microcontroller and SD-Card Based Multichannel Data Logger” describes the use of a PIC18F8520 MCU to log multiple analog and digital inputs to an SD card using a real time clock/calender (RTCC) module to generate the time stamps[6]. In this particular application, the logged data is related to ambient temperature, with the temperature being converted to voltage by a dedicated sensor external to the MCU. The resulting analog voltage is then input to the analog-to-digital converter (ADC) on board the MCU. This paper provides a good example of the external interface capabilities of an MCU needed for a data logging application, such as the inter- (I2C) bus communication between the MCU and the RTCC module, and the serial peripheral interface (SPI) bus communications between the MCU and the SD card. The embedded software used for this application is a basic background loop structure, with apparently no interrupts being

6 used to alter the flow of the main program execution; this is a fairly simple software structure for a non-trivial application. Dr. Ibrahim also proposes a format for the resulting data log such that all the data associated with a given time stamp appears in the same row, and each row has a unique time stamp. The second paper, titled “GPS Data Logger with SD Card Storage and GOOGLE EARTH Map Interface”, describes the use of a PIC18F4520 to gather GPS time and position data received from an external GPS receiver module and record the data to an SD card[7]. Much of the work in this paper is similar to that of the previous one, such as the SPI in- terface to the SD card and the use of a background loop software. However, the universal asynchronous receiver/transmitter (UART) is used as the communication interface between the GPS receiver and the MCU. Among other things, this paper proposes a method for converting the relatively complex National Marine Electronics Association (NMEA) code output by the GPS receiver into a more efficient code that can be logged to the SD card. Both of these papers feature the use of the C programming language and the mikroC compiler for software development, and use built-in mikroC library functions for implement- ing a FAT16 file system on the SD card, which is convenient. However, in neither case is communication between the MCU and a PC implemented; the logged data must be extracted directly from the SD card. Dr. Ibrahim has also published a thorough text on the PIC18F series of micrcontrollers[5], which includes significant material relevant to this work. It includes a complete chapter on interfacing PIC18F devices with SD cards, albeit exclusively using the mikroC library func- tions, which includes much of the technical detail omitted in shorter papers. Also included is a very brief introduction to the function of real time operating systems (RTOS) on PIC18F devices, although it focuses on a very basic RTOS supported by the CCS PIC C compiler, which is of limited use.

7 A similar text by Tim Wilmshurst of the University of Derby also includes a wealth of practical literature on the PIC18F series, although this text is focused more on autonomous control applications[28]. However, its relevance to this work is in its thorough and practical treatment of RTOSes for embedded systems. Not only does Wilmshurst discuss the “the- oretical niceties” of an RTOS, but he focuses practically on the Salvo real time operating system developed by Pumpkin Inc. While it may be a little short on theory compared to a dedicated text on operating systems, it is extremely relevant for embedded systems engi- neers looking to implement an RTOS, especially on a PIC18F device. Also note, Wilmshurst prefers Microchip’s C18 compiler in this text, in contract to the mikroC compiler used by Ibrahim.

A paper by Nkom and Musa describes how microcontrollers often offer the best com- bination of reliability, size, efficiency, and development flexibility for remote data logging applications[20]. In their application, a PIC16F628A MCU is used to time stamp and log a single TTL logic level transition to the MCU’s internal electrically erasable programmable read only memory (EEPROM). While this necessarily limits the amount of data that can be logged, the authors point out the design’s benefits in terms of reliability and efficiency (both in cost and energy). While the data logging application in this thesis is not as remote as in this paper, it must still operate independently of a host PC for extended periods of time, necessitating a high degree of reliability, which is addressed by Nkom and Musa.

A paper by Purwadi et al discusses the use an Atmega 8 MCU in a photovoltaic LED street lighting data logging application[3]. While this paper is a little light on technical detail, it does provide a concrete practical example of the usefulness of MCUs other than the Microchip PIC family of devices in data logging applications.

Dr. Phillip LaPlante’s text on real time systems, titled “Real-Time Systems Design and Analysis: An Engineer’s Handbook”, became extremely relevant to this thesis work as development began on the real time operating system phase[17]. Dr. LaPlante develops the

8 theory needed to understand complex real time systems and operating systems from very basic concepts. While the later chapters of the text go into subject matter that is beyond the scope of this thesis, the chapters on real time specification and design techniques, real time kernels, and intertask communication were all relevant to (and extremely helpful in) the design of the real time embedded software for this thesis. LaPlante’s work on compiling and interpreting the many methods used to specify real time systems and provide intertask communication was of great value to the development of RTOS-based software for this thesis.

There has also been significant work by Peter Reen and Naveen Mohanswamy of Mi- crochip Technology on implementing an SD card compatible file system using the C18 compiler[23]. This work is summarized in the Microchip application note AN1045, which describes the “Memory Disk Drive ” or MDDFS. The MDDFS is actually a FAT16 (FAT32 is also supported) file system, which is typically used on SD cards. Reen and Mohanswamy describe the MDDFS as being able to run on SD and MMC cards via the SPI bus, as well as Compact Flash cards via the parallel master port (PMP) bus. Work has been done to make the MDDFS capable of running on most PIC18, PIC24 and PIC32 devices, although sufficient on-board RAM must be available for the file allocation table (FAT) buffer, as well as the read/write buffer, each of which are one 512-byte sector in size. This necessarily places some limits on what devices can implement this file system. The authors claim to have successfully implemented the MDDFS on an SD card via the SPI bus using a PIC18F8722.

Work by Chris Valenti of Microchip Technology and Dr. Andrew Kalman of Pumpkin, Inc. has also shown the ability of PICmicro devices to interface with the Salvo real time operating system[26]. While the work of designing the Salvo RTOS for PICmicro devices was already completed, Valenti and Kalman show how even relatively simple 8-bit MCUs operating at low clock frequencies can handle much more complicated tasks when using an operating system instead of a foreground/background (superloop) interrupt-driven routine.

9 They do this by implementing a program to take user input for hi and low temperature settings on four thermistors, while simultaneously monitoring the four temperature zones and indicated abnormal temperature zones via a buzzer and UART. It may be possible to implement a similar program using a superloop, but Valenti and Kalman demonstrate that using an operating system and clearly defined tasks allows for the MCU to run tasks con- currently, run higher priority tasks more frequently, and have clearly defined communication between tasks.

In short, while there hasn’t been any previously published work (besides this author’s) on developing the embedded software for a NIF stepper motor diagnosis application, there has been quite a bit of previously published work on using MCUs in various data logging applications, as well as on the use of the hardware and software tools necessary for an MCU-based data logging application, all of which have relevance to the work at hand.

1.5 Organizational Summary of Thesis

The remainder of this document is organized as follows:

Chapter 2, titled “Technical Background”, develops the detailed technical background necessary to understand design and development decisions throughout the rest of the docu- ment. Attention is given to what the stepper motor driver might look like, how the diagnostic system would interface with it, and what we mean by a motor “fault”. From there the various signals of interest to be logged are defined and described, which leads to the development of more specific and detailed technical requirements to guide the development of the embedded system. Finally, the early design decisions in hardware, software, and microcontroller selec- tion are described to lay the software development foundation for the rest of the document.

Chapter 3, titled “Application Software - Superloop Version”, begins by analyzing the structure and function of the resulting foreground/background (superloop) implementation

10 of the embedded application software. After analyzing both foreground and background, the set up and methods used for testing the functionality of this software implementation are presented. The results of testing are then discussed. Also note, this chapter includes an analysis of the binary-to-ASCII conversion software required to interpret the data log file produced by both the superloop and RTOS-enabled software implementations. Chapter 3, titled “Application Software - RTOS Version”, presents an analysis of the structure and function of the resulting RTOS-enabled implementation of the embedded ap- plication software. After analyzing the individual tasks and how they interact, the set up and methods used for testing the functionality of this software implementation are presented, which are essentially the same as those presented in Chapter 3. The results of testing are then discussed. This chapter also contains some brief discussion of RTOS fundamentals. Chapter 5, titled “Concluding Remarks”, compares the two software implementations and draws conclusions. Several opportunities for future work in this area are presented, and a brief section titled “Broader Impacts” relates the work performed for this thesis to applications in the intelligent diagnosis of civil infrastructure. Also note, two appendices follow Chapter 5. Appendix A includes hardware schematics of the various development tools used in this work, which are referred to in Chapter 2. Appendix B contains all of the application specific code, most of it written by the author3, for both embedded software implementations.

3The only file in Appendix B not written by the author is 18f8722_MDDFS.lkr, which was only modified from its original form by the author for this application.

11 Chapter 2

Technical Background

2.1 Stepper Motors, Drivers, and Faults

Before presenting the detailed technical requirements of the data logging system, it is nec- essary to discuss stepper motor operation in more detail, define what we mean by stepper motor “faults”, and determine the signals of interest that must be recorded by the data logger.

As described in the previous chapter, a stepper motor is capable of dividing a full rev- olution into several discrete steps[24], hence the term “stepper”. It is difficult to describe stepper motor operation without a visual aid, so Figure 2.1 is used to visually demonstrate basic stepper motor operation. By exciting the stator windings in the sequence shown in Figure 2.1, the rotor turns in the clockwise (CW) direction is discrete steps of 45 degrees (called the “step angle”) from Position I through Position V. While Figure 2.1 shows only 180 degrees of rotation, a full 360 degrees of CW rotation could be achieved by continued application of the same phase excitation sequence. The rotor would turn in the counter- clockwise (CCW) direction if the opposite phase excitation sequence were applied[19]. This is only a simple example, and while there are numerous varieties of stepper motors[24], this

12 thesis will only consider the two phase permanent magnet type, which is the type shown in Figure 2.1. This discrete-step operation of these motors allows them to be controlled by using discrete pulses, where each pulse is a command to move the motor one step. A separate signal can be used to control direction, and the frequency of the pulse train can control velocity. Stepper motor control pulses are not input directly to the motor itself, but to a drive circuit that can interpret the step pulses and direction commands and apply the proper phase excitation to the stator windings. In this way, stepper motors are often considered to be digital electromechanical devices[19], and are operated without feedback, since the position is known by simply counting the number of steps (pulses) and knowing the direction of rotation.

Dedicated integrated circuits (ICs) have been designed specifically for the task of driving stepper motors based on step, direction, and other control signals. A common IC for this task is the Texas Instruments (TI) DRV8811 Stepper Motor Controller. For the purposes of this thesis, it is assumed that the DRV8811 is used as the stepper motor driver. A functional block diagram of this IC is shown in Figure 2.2, and a more detailed view of the output stage is shown in Figure 2.3. It is beyond the scope of this thesis to analyze the DRV8811 in detail, but it is important to note a few things about this circuit. First of all, its digital inputs include both step and direction elements, where the level of the direction pin determines the direction the motor will be driven in, and a rising edge on the step input causes the DRV8811 to drive the connected motor one step. Secondly, an external resistor must be connected to ground from the bottom of the H-bridge output for each phase for proper operation. This resistor can also be used as a current sense device (hence the labelling in Figure 2.3 as ISENA and ISENB) allowing the current through each phase to be measured. Also note that the current sense voltage across these external resistors is referenced to ground, and so does not necessarily need to be measured differentially. Finally, the voltage used to create the drive current in the motor windings is not the same as the voltage used for the DRV8811

13 Figure 2.1: Basic operation of a two phase permanent magnet stepper motor with four poles 14 Figure 2.2: Functional block diagram of TI DRV8811 from the DRV8811 data sheet.

15 Figure 2.3: Detailed block diagram of TI DRV8811 output stage from the DRV8811 data sheet.

16 Figure 2.4: Stepper motor using a linear gear and rails for a linear positioning application. logic. This motor drive voltage (labelled VM in Figure 2.2) can be anywhere between 8V to 38V according the DRV8811 data sheet; for the purposes of this thesis, let us say that the nominal motor drive voltage is 24V.

Note that each driver IC is only capable of driving a single motor, while in many appli- cations it may be desirable to drive several stepper motors simultaneously or in some sort of coordinated fashion. To accomplish this efficiently throughout NIF, stepper motor driver ICs are integrated into larger and more complex “motor drive boards” that can be used to control and drive up to eight stepper motors simultaneously[2]. This necessitates the use of eight individual motor drive ICs, and will influence the technical requirements of the data logging system. Throughout the rest of this document the signals associated with motor 1 of a motor drive board will be labelled “Ch1”, motor 2 will be “Ch2”, etc.

As mentioned in Chapter 1, NIF stepper motors are used in various position control applications such as beam and sensor package alignment[27]. Position control applications often require the motor itself to move along a track using a linear gear, as shown in Figure 2.4. While the motor may be operated without feedback from the motor itself, digital feedback signals from limit switches (shown in Figure 2.4) are often desired in this application. The state of these switches (open or closed) is fed back to the motor controller (which is providing control signals to the motor driver) and is used to sense whether the motor has reached the

17 points on the track where the switches are located. The purpose is to limit the motor from hitting the end of the track (hence the term “limit” switch), and to determine when the motor is at a predetermined “home” or “origin” position. Note that limit switches may be used in applications other than the one depicted in Figure 2.4[1]. Now that we have examined the basic operation of a stepper motor, as well as a typical drive circuit and application, let’s describe some of the faults that may occur in such a system. The most obvious fault would be for the motor to stall during stepping or fail to start, which could be the result of any number of causes. Other faults may include “lost steps” or “microstalls”[2], which occur when the motor fails to move the commanded number of steps. For example, the controller may send 1000 rising edges to the DRV8811, but the motor only moves 950 steps. Obviously, this could indicate an error with the motor or drive circuitry. Other similar faults include mis-operations, where the motor may step more than commanded, or in the wrong direction, which may indicate a mis-wired connection between the driver and the motor. Though not directly a motor fault, other faults that the system could succumb to include failure of the limit switches or the motor drive power supply, both of which would likely cause a subsequent motor fault. So, to summarize, the faults we are interested in diagnosing include:

• Motor stalls; either fails to start or stalls during stepping

• Motor lost steps

• Various motor mis-operations

• Limit switch failure

• Motor drive power supply failure

While the actual diagnosis of the above faults of interest is beyond the scope of this thesis, they do give some guidance in the selection of signals of interest to be recorded by the data logging system. For example, to diagnose a motor drive power supply failure, it would be

18 wise to record the motor drive power supply voltage VM. Granted this voltage is far above the analog input voltage maximum for most microcontrollers, but a linear voltage divider could be used to bring the nominal VM of 24V down to an acceptable range. To diagnose motor stalls, the motor phase currents (from ISENA and ISENB) are probably important and should be recorded. In order to diagnose lost steps and various mis-operations the phase current measurements would likely need to be combined with motor step and direction data, hence those signals should be recorded as well. Finally, diagnosis of limit switch failures requires that the limit switch status signals also be recorded by the data logging system. To summarize, the signals of interest to be recorded from the motor, driver, and limit switches by the data logging system include:

• Motor drive voltage

• Motor phase A current

• Motor phase B current

• Number of commanded steps

• Direction of commanded steps

• Status of CW limit switch

• Status of CCW limit switch

• Status of HOME limit switch

2.2 Signals of Interest and System Technical Require-

ments

Now, consider the details of the signals of interest defined in the previous section, and how they guide the development of the system technical requirements. Given that the motor step, direction, and limit switch status signals are all either inputs or outputs of a controller that

19 Table 2.1: Summary of the characteristics of signals of interest

Signal Name Signal Type Dynamic Range or Logic Family Signal Frequency Motor Drive Voltage Analog 60.2dB (4.88mV - 5V) DC Phase A Current Sense Analog 60.2dB (4.88mV - 5V) DC Phase B Current Sense Analog 60.2dB (4.88mV - 5V) DC Motor Direction Digital 5V CMOS DC CW Limit Status Digital 5V CMOS DC CCW Limit Status Digital 5V CMOS DC HOME Limit Status Digital 5V CMOS DC Motor Steps Digital 5V CMOS DC - 10kHz

is interfacing with the DRV8811 motor drive circuit, it will be assumed that these signals belong to a 5V CMOS logic family. Furthermore, the motor direction and limit switch signals vary so infrequently compared to the data logging system sampling frequency (to be defined later in this section) that they may be considered to be DC. The motor step signal, however, may vary between DC and 10kHz, which is therefore the maximum motor step frequency[2]. The three analog signals, phase A current sense, phase B current sense, and motor drive voltage are assumed to exhibit 60.2dB of dynamic range from 4.88mV to 5V, and also vary slowly enough as to be considered DC. A summary of these signal characteristics is shown in Table 2.1. Also note that the data logging system would interface with a single NIF motor drive board, which would be driving up to eight motors using a common motor drive bus. Therefore, the data logging system is required to log motor phase currents, direction, steps, and limit switch statuses for eight motors - a total of 56 signals - as well as the motor

1 drive bus voltage. Regarding the motor step signal, only 4 of the commanded motor steps are required to be counted for each motor; simply multiplying this count by 4 will yield an appropriate approximation of the commanded steps for each motor. Note that the data logging system must be capable of counting these steps for all motors up to the maximum step frequency of 10kHz.

Now let’s consider the data logging frequency, which will be the same for all signals of interest. Given the maximum speed that NIF stepper motors are driven at[2], and assuming

20 a reasonable maximum acceleration of 200 steps per second per second for the purposes of this thesis, a data logging frequency of 10Hz should be sufficient to capture useful data for motor diagnostic purposes[2]. Furthermore, each data log is required to be time stamped to the nearest tenth of a second. Given the commercial availability of SD cards and their popularity in data logging ap- plications, the logged data will be required to be stored on a removable SD card. For the sake of balancing cost, availability, storage space, and simplicity of embedded software de- sign, this requirement will be further restricted to 2GB SD cards. A file system will also be required on the SD card for ease of implementing file input/output operations; a FAT16 file system will be used for this task due to its widespread application on removable flash media of this kind. Communication with the SD card (and between other ICs, if necessary) will be implemented using the SPI bus, which is supported by all SD cards and allows data to be logged to the SD card over a four wire bus, versus the six wire needed to implement the SD bus. The data logger is also required to communicate with a personal computer (PC). This communication will be carried out using RS-232 via a universal asynchronous receiver trans- mitters (UARTs) on the data logger and PC. A user may start or stop data logging operations by sending a command via RS-232 link. If data logging operations are stopped, the user may also select the logged data to be output to the SD card, the PC (via RS-232), both, or neither. All RS-232 communications are required to be subordinate to (and can be inter- rupted by) necessary data logging operations, although 50 percent processor overhead time will be reserved for possible RS-232 communication. Also important to note is that this data logging system is required to have no effect on the motor drive circuits. It is simply a passive recording system, and its failure or malfunction shall not have any impact on the ability of the motor drive circuits.

21 It should also be mentioned that while this system is being designed to interface with a NIF motor drive board, no NIF motor drive board is available to test the final design with. At first this may seem problematic, however all the signals of interest can be simulated using a function generator, and some can even be simulated using a DC power supply instead. Since these signals of interest would be the only connection (besides power and ground) between the data logging system and the NIF motor drive board, almost all of the system requirements can be successfully tested by simulating these signals. The only requirement that remains difficult to test without a NIF motor drive board is that the data logging system has no effect on the motor drive circuits.

In summary, the data logging system must meet the following requirements:

1. Data logger must log signals of interest for up to eight stepper motors

2. Assume that the signals described in Table 2.1 are available to be logged for each motor, except Motor Drive Voltage, which is common to all eight motors

3. Data logger must be capable of counting at least one quarter of commanded motor steps up to 10kHz for all eight motors

4. All signals of interest are gathered ten times per second

5. All signals of interest associated with a particular sampling time will be time stamped to the nearest tenth of a second

6. Logged data is stored on removable 2GB SD card

7. FAT16 file system will be used to manage data storage on SD card

8. SPI bus will be used to interface with SD card, as well as for any inter-IC communi- cation if necessary

9. Data currently being logged will be communicated to the user upon request via RS-232

10. Reserve 50 percent processor overhead time for RS-232 communication

11. RS-232 communication is subordinate to, and may be interrupted by, necessary data gathering operations

12. Data logging operations may be started and stopped via RS-232 command

22 13. Data logger output to both SD card and RS-232 link may be toggled independently via RS-232; recording must be stopped for this to occur

14. Data logger must have no effect on motor drive circuits

Note that data “gathering” will describe the recording of input data to MCU memory, while “logging” will describe the writing of gathered data to SD card and/or Terminal.

2.3 Early Design Decisions: Laying the Foundation

Even with the system technical requirements fully specified, the development of the embed- ded software cannot proceed without making some early design decisions regarding both hardware and software. For one thing, we must specify a what the data logging system will be based around: microcontroller, microprocessor, FPGA, application specific integrated cir- cuit (ASIC), PC, or something else? Once this is done, a decision must be made regarding the development hardware and software to be used, as this will place specific constraints on the embedded software design and development.

2.3.1 The Microcontroller and Its Interfaces

For this application, it was decided to use a microcontroller as the of the data logger. Compared to the other options mentioned in the beginning of this section (2.3) a microcontroller offers the best combination of price, available development tools, development flexibility, and integrated hardware modules.

While in theory an ASIC could be designed to perform this application more efficiently by eliminating unnecessary hardware modules and pins, ASICs are far more expensive and must be custom designed to some extent. FPGAs allow for more rapid prototyping and have a lower cost compared to ASICs, however FPGA cost may still be on the order of 5 to 100 times that of a microcontroller.

23 Now, what exactly is the difference between a microcontroller and a microprocessor? These terms are often used interchangeably, and though the devices are indeed similar in several ways such as packaging and cost, they are not the same. A microprocessor is typ- ically designed to only (fetch and execute) instructions from an external memory. Although this is an over-simplification, any functionality such as RAM, timers, and inter- rupt processing are typically not included in the microprocessor and must be implemented with discrete ICs separate from the microprocessor. While this may allow the micropro- cessor to process instructions faster, it necessitates a complex hardware design if additional functionality is required. On the other hand, a microcontroller (MCU) typically includes many general purpose hardware modules integrated with a central processor, some ROM for storing program memory (instructions for the processor to fetch and execute), and some amount of RAM for storing data, all in the same IC package. The general purpose hardware modules may include timers, interrupt controllers, communication modules, and analog-to- digital converters, among other things. Compared to a microprocessor, the central processor of an MCU may not be able to fetch and execute instructions as rapidly, and may have a reduced instruction set. However, unlike a microprocessor, the integrated hardware modules allow the implementation of complex designs and significant functionality without additional ICs. For this reason, it was decided to implement the data logger in this application based on a microcontroller.

The PIC18F8722 MCU is an 8 bit microcontroller, capable of operating at frequencies up to 40MHz. A functional block diagram of this device is given in Figure 2.5, and a pin diagram is given in Figure 2.6.

Note that “8 bit” refers only to the width of the data bus between the integrated RAM, peripheral hardware modules, and central processor; while instructions stored in ROM are 16 bits wide, and a 12 bit wide bus is used to address the RAM[18]. This MCU also integrates a 10 bit ADC module, both UART and SPI communication modules, programmable timer

24 Figure 2.5: Functional block diagram of the PIC18F8722 device from the PIC18F8722 data sheet 25 Figure 2.6: Pin diagram of the PIC18F8722 device from the PIC18F7822 data sheet

26 Figure 2.7: Physical interfaces between NIF motor drive board, data logger, SD card, and PC. modules, counter modules with selectable edge external input, and an interrupt controller, which are all necessary for this application. Furthermore, the PIC18F8722 includes up to 70 general purpose input/output (GPIO) pins, which is more than sufficient for this application. The PIC18F8722 (as well as most other PIC devices from Microchip Technology) is well- supported by a variety of hardware and software development tools. While these tools do place some constraints on the development process, as is discussed in the following two subsections, they integrate and simplify much of the embedded software development and testing.

With the selection of a microcontroller to base the data logger on, and with the assump- tion that the data logger will be interfacing with a single NIF motor drive board to log 7 signals of interest from 8 motors as well as one common signal of interest, we can develop the basic layout of how the MCU will interface with the rest of the system as seen in Figure 2.7. Note that for the sake of simplicity the power and ground signals have not been shown routed to the SD card and MCU. The details of the physical interface between the driver board and data logger are beyond the scope of this thesis, but it is assumed to be a directly

27 Figure 2.8: Pin arrangements for both SD card and RS-232 physical interfaces mating connector with sufficient pins such that the data logger may be installed and removed easily, and is supplied with power from the drive board.

The physical interface between the PC and MCU is a three wire RS-232 connection having one line for data flow in each direction, and a common ground line. Note that no hardware handshaking is employed to save wires and simplify transmissions, and that while the MCU functions as a data communicating equipment (DCE) and the PC functions as a data terminating equipment (DTE), there is not a true master-slave relationship. The DCE and DTE designations simply indicate the physical connection made at each device (and hence the type of cable needed to make the connection) as shown in Figure 2.8. The physical interface between the SD card and MCU is a four wire SPI connection having one line for data flow in each direction, a clock line from MCU (master) to SD card (slave), and a slave select line from MCU to SD card. Unlike the RS-232 interface, the SPI interface clearly defines a master device and a slave device; as the master, the MCU selects a slave to communicate with (in this case the SD card) via the low active slave select signal. The master also provides the clock signal to allow data exchange between the two devices by shifting data across the MISO and MOSI lines one bit at a time. This is implemented using two shift registers (one in the master and one in the slave) that are connected in a loop as shown in Figure 2.9, and shift on a given edge of SCK. The PIC18F8722 includes two integrated serial

28 Figure 2.9: Operation of the SPI bus using shift registers.

Figure 2.10: Microcontroller internal hardware configuration for this application. communications modules that can individually be configured as an SPI interface, in which case the MCU has four pins dedicated to SPI communications. These groups of four pins can be seen in the pin diagram in Figure 2.6. The SD card physical interface and pin designation for operation in SPI mode is shown in Figure 2.8. Applying the requirements set forth in the previous section to the PIC18F8722 and its integrated hardware modules, a more detailed view of how the MCU will be configured can be developed, as seen in Figure 2.10, which is necessary prerequisite for developing the embedded software. It is important to understand Figure 2.10, as the embedded software is developed based on this configuration. Within the MCU, note that several timer modules are used; timer 3 is assumed to be providing a constant period for the 10Hz recording cycle, while timers 0 and 1 are configured as counters to increment on the rising edge of incoming step signals. General purpose input pins are

29 used to collect the states of the digital signals of interest, while the integrated ADC is used to convert the analog signals of interest to 10 bit digital values. The UART module is configured to interface with the PC’s UART via RS-232, and the master synchronous (MSSP) module is configured to operate in SPI mode to facilitate communications with the SD card. All of these modules, along with the integrated RAM, communicate with each other via the CPU and 8 bit data bus.

External to the MCU are the signals of interest (far left), the PC and SD card (far right), and several (MUXes). While the embedded software is designed without these MUXes in place, it is assumed they would exist in a hardware implementation of the data logging system, and so the MUX control signals are implemented in the embedded software. The two 4:1 digital MUXes are used to multiplex the 8 motor step signals to the two MCU timer modules that are capable of incrementing on an external rising edge. This will allow

1 the embedded software to meet the requirement of logging at least 4 of the commanded steps for each motor. The single 2:1 analog MUX is necessary due to 17 total analog signals of interest and only 16 analog input channels available on the MCU. By multiplexing two of the analog signals of interest and using a single GPIO pin for MUX control allows all 17 signals to be made available to the integrated ADC.

Observing Figure 2.6 it is clear that each MCU pin may be configured for more than one function. By combining the internal MCU hardware configuration of Figure 2.10 with the pin diagram shown in Figure 2.6 allows the function of each MCU pin to be determined for this application, as shown in Figure 2.11. Note that Figure 2.11 shows the function of each pin if the MCU were part of the data logger board in Figure 2.7. However, the data logger board in Figure 2.7 is only an assumed hardware design for this application, and doesn’t exist to develop the embedded software with. Therefore, to successfully develop, test, and evaluate the embedded software for this application, specific development tools suited to this task will be used, which is the subject of the following two subsections. These development

30 Figure 2.11: Ideal function of each MCU pin given the data logging application at hand. 31 Figure 2.12: Hardware development tools used in this application. Top left is PICkit3 debugger, top right is SD card, bottom left is PICDEM PIC18 board, and bottom right is PICtail Daughter Board for MMC/SD Cards. tools will place constraints on the embedded software development, partly by predetermining the function of MCU pins to be other than those in Figure 2.11.

2.3.2 Development Hardware and Constraints

Several hardware development tools were utilized in the development of the embedded soft- ware for this application, and are shown in Figure 2.12. Note that while it is not truly a development tool, and SD card is included in Figure 2.12 for scale.

Arguably the most important of the tools in Figure 2.12 is the PICDEM PIC18 Ex- plorer development board. This development tool integrates the PIC18F8722 with several hardware peripherals and as well as hardware interfaces for programming/debugging and communications. A detailed schematic of the PICDEM PIC18 development board is shown

32 in Appendix A. The large IC slightly elevated above the main PC board near the center of the PICDEM PIC18 board is in fact a PIC18F87J11 MCU; the PIC18F8722 resides beneath the PIC18F87J11 on the main PC board. Both devices have the same physical form factor. The layout of the development board (see Figures A.1 and A.2) places several constraints on the development of the embedded software for this application by predetermining the function of MCU pins. For example, the in-circuit serial programming (ICSP) functions must be used on pins 47 and 52, while pins 33 and 58 are hard wired to push button inputs. Furthermore, all 8 pins associated with PORTD on the MCU are hard wired to LED outputs, and pins 35 and 36 are used by a secondary oscillator used for RTCC applications.

Also shown in Figure 2.12, the PICtail Daughter Board for MMC/SD Cards (schematic in Appendix A), allows for the interface of an SD card and the MCU via a 0.1 inch header pin connection. The configuration of the PICtail daughter board (see Figure A.3) places further constraints on the software development by predetermining pin function, in this case pins are set as the card detect and card write enable sense pins, as well as SPI bus lines.

Finally, while the PICkit3 debugger (schematic in Appendix A) shown in Figure 2.12 doesn’t really place any new constraints on the embedded software design (other than the already predetermined ICSP interface) it is still a development tool worth mentioning, as it forms the interface between the software development tools and hardware development tools. In a way, the PICkit3 debugger does constrain the software development because it is only able to program a maximum of 3 software breakpoints into the embedded code on the MCU. While this doesn’t directly affect what software can be developed, it does limit the debugging and testing ability, which is part of the development process. The constraints imposed by the hardware development tools are summarized in Figure 2.13, which is similar to Figure 2.11. The difference is that in Figure 2.13 the pin functions listed are those that are used in the actual development of the embedded software, which must account for the pin functions predetermined by the development tools. Most of the pin functions do not

33 change from Figure 2.11, except for the digital direction and limit switch inputs have been collapsed to only four pins, allowing for simpler embedded software development that will still be representative of the final application code for testing and evaluation purposes.

2.3.3 Development Software and Constraints

In addition to the hardware development tools that were used to test the embedded software on the physical microcontroller, several software development tools were used to write and compile the software, as well as to link and program the software into the MCU. The core software development tool for this application was the MPLAB Integrated Development Environment (IDE). The IDE allows for several other development tools (discussed below) to be utilized together through a simplified and efficient interface.

These other software development tools include the C18 C language compiler, the Salvo libraries and source code needed to implement the Salvo RTOS, the Memory Disk Drive File System, and various MCU-specific header files needed to simplify the function of peripheral hardware modules on the MCU. These additional tools impose several other constraints on the development, the most obvious being that the software must be developed in the C programming language, and with all the compiler-specific quirks of C18. However, there are some less obvious constraints, such as the MDDFS requires significant blocks of the MCU’s RAM (1,024 bytes out of 3,936 bytes total) be reserved for buffers required by the file system. Both the MDDFS and Salvo libraries constrain the optimizations and code models that may be used by C18 when compiling the source code, which will be discussed in detail in the following chapters on the embedded software structure, testing, and evaluation. At the time of this writing, all of these software development tools are available free of charge at the web addresses specified at the beginning of Appendix B.

Another software development tool used for development of data logging system software was the Microsoft Visual Studio. This tool was used to development a PC-based executable

34 to convert the SD card based data log into a human readable format for analysis. While this has little effect on the embedded software development, it is still worth mentioning for the sake of completeness.

35 In summary, the constraints imposed by the software development tools are:

1. Embedded software must be developed in C language

2. Procedural abstraction optimization must be disabled

3. 512 bytes of MCU RAM reserved for file allocation table (FAT) buffer

4. 512 bytes of MCU RAM reserved for read/write buffer

5. C18 must use large code model

6. C18 must use large data model

7. C18 must use multi-bank stack model

8. Must use Salvo libraries for RTOS versions

9. Must use large-code-model Salvo library that supports multitasking, delays, and events

36 Figure 2.13: Function of each MCU pin given the data logging application at hand combined with develop- ment tool constraints.

37 Chapter 3

Application Software - Superloop Version

Now we begin the detailed analysis of the resulting embedded software for this application, starting with the version based on a superloop structure.

3.1 Structure and Function

The first implementation of the embedded software for this application is structured at the highest level as a foreground/background program. That is to say, there are two levels at which the program may execute, the background and the foreground. The background is the default level, while the foreground is invoked only when special conditions are satisfied and/or special events occur. If these special events and conditions do not occur, then the software will execute the background program flow from beginning to end, and then start back at the beginning. This entire background loop will execute forever until interrupted by the foreground, assuming no abnormal fault conditions such as a power failure occur. In fact, even when interrupted by the foreground, the background continues with its execution

38 Figure 3.1: Visual representations of the superloop program structure used by the embedded application software. exactly where it was interrupted once the foreground has completed executing. As a result of this looping behavior exhibited by the background, this style of embedded software is often referred to as a “superloop”[22]. The basic relationship between the background and foreground is shown visually in Figure 3.1. There are several advantages to this high level structure. Firstly, the structure is fairly simple and easily understood if the program size isn’t too large, and it may be quicker/cheaper to implement without special software development tools. The simplicity of the structure also allows for easier debugging, but again only if the program size isn’t too large. Secondly, program execution can quickly change to the foreground to handle time critical tasks.

However, the relative simplicity of the structure results in some important disadvantages as well. Firstly, the timing of both foreground and background execution may be affected

39 by changes in the program code. This is important because it means the timing of program execution must be re-evaluated each time a change is made. Secondly, due to the sequential execution of the structure, each program task must wait for its turn to execute, even if its execution is immediately required (i.e. time critical). Finally, communication between different program tasks is usually poorly defined, which rapidly complicates the program if extensive inter-task communication is required.

Based on the system requirements and constraints, it was anticipated that the embedded application software would be appropriate for implementation with a superloop struc- ture. While the application likely requires a reasonable amount of inter-task communication (moving data and data logs around, and handling RS-232 commands), and does have some time critical elements (10Hz data logging rate), it does not have an extensive scope and must fit within the lim- ited computing resources of the PIC18F8722 MCU. There- fore, a superloop was chosen as the basic structure for the first implementation of the embedded application software.

In the embedded application software the background is implemented within the main() portion of the program, while the foreground is implemented as the high priority in- terrupt service routine (HPISR) and invoked using a timer Figure 3.2: Project structure in MPLAB IDE for implementing su- overflow interrupt. The low priority interrupt service routine perloop embedded application soft- ware. (LPISR) is unused in this implementation. In the MPLAB IDE, source code and header files used by the MDDFS are combined with those designed specifically for this application, as well as the modified linker script in the project structure shown in Figure 3.2. The source file MainSrcCode.c includes the main() code and ISRs,

40 which account for the overall software structure and program flow. AuxSrcCode.c includes much of the application specific software functionality in compartmentalized functions, while CustomHeader.h defines configuration options for the integrated hardware modules, as well as application specific data structures. The linker script 18f8722_MDDFS.lkr describes the configuration and disposition of the available MCU RAM, among other things. Note the orig- inal PIC18F8722 linker script supplied with the C18 compiler has been modified to accom- modate the MDDFS by reserving 512 bytes (1 sector) each for the file allocation table (FAT) buffer and the read/write buffer, as seen on lines 54, 55, 76, and 77 of 18f8722_MDDFS.lkr (Appendix B.1.4). All of the files mentioned in the above paragraph were developed (Main- SrcCode.c, AuxSrcCode.c, CustomHeader.h) or modified (18f8722_MDDFS.lkr) specifically for this application, and are included in Appendix B.1.

The other files included in the project shown in Figure 3.2 are required to implement the MDDFS, and while some did require slight modification (commenting and uncommenting of lines), they are quite lengthy and not as relevant to the analysis of the embedded application software and so are omitted from the Appendix. However, web addresses where these other files can be found are given at the beginning of Appendix B. A brief description of each of these files will be given here for the sake of completeness.

The file FSIO.c defines functions to be used by the MDDFS for implementing input/output and other functionality. Salloc.c defines functions to implement dynamic memory allocation. The final source file SD-SPI.c provides functions specifically for using the SPI hardware inter- face with an SD card. Compiler.h defines which compiler and MCU family is being used for the rest of the file system to know. FSConfig.h defines file system configuration options, and links the physical layer functions with the appropriate physical layer configuration. FSDefs.h defines various macros and type definitions to be used by the file system. FSIO.h includes function prototypes for the functions defined in FSIO.c. GenericTypeDefs.h is exactly what one would expect, a header file full of often-used type definitions. The only MDDFS header

41 file modified for this application, HardwareProfile.h, defines the physical layer configuration between the SD card and MCU for the PIC18F8722, as well as special function registers in the MCU that must be manipulated by the MDDFS. Salloc.h provides prototypes for the functions defined in salloc.c, and SD-SPI.h provides prototypes for the functions defined in SD-SPI.c.

Upon inspection of the first few lines of MainSrcCode.c (Appendix B.1.1) it can be seen that several other header files are included beyond those listed above. These are stdio.h, adc.h, timers.h, and usart.h. while stdio.h is included to implement basic I/O function on the MCU, the other three include predefined (and very useful) functions and macros for configuring and operating the MCU’s integrated hardware modules; specifically the ADC, timer modules, and UART. They are not included in the project structure for the sake of simplicity.

Now that we have analyzed the structure and implementation of the superloop version of the embedded software at a very high level, let’s analyze the more detailed structure and functionality to see how the requirements set forth in the previous chapter are addressed by this implementation of the embedded software.

3.1.1 The Background Loop

As stated above, the background loop is implemented in main() which begins on line 160 of Appendix B.1.1 and extends to the end of Appendix B.1.1. Except for the first four lines, all of main() is part of a state machine implemented with a switch/case structure, as shown in Figure 3.3 beginning on line 164 of B.1.1. Observing Figure 3.3 one can see that the main() state machine performs initialization on start-up in state zero before proceeding to state one which contains the background loop. The loop is then realized by having the next state of state one be state one itself. Using a state machine structure instead of a simple “while(1==1)do forever” allows for easier debugging and modification of the software in case

42 Figure 3.3: State machine used in main() to implement initialization and background loop. more states are desired. Note that main state machine will return to the background loop if it finds itself in any unknown state, although this is unlikely as the next state variable is an enumerated type (see line 24 of B.1.3)and can only take values of zero, one, two, or three.

While the background will loop forever, it does not necessarily execute identically on each iteration. That is to say, while it does execute from beginning to end on each iteration, it is free to take a number of paths between those two points based on user input among other things, such as whether or not new data is available to log to the SD card. The flow of background execution is shown in Figure 3.4. The flow chart in Figure 3.4 focuses on the more relevant parts of the background execution, and does not describe every line of background loop code in detail. The details of each element of the flow chart will be discussed during the rest of this subsection as part of the detailed analysis of the embedded application software.

Key Loop Observations

Observing the flow chart, it is important to notice that at no time is data gathered from inputs. At first, this seems odd, because the whole point is to gather input data and then log them. However, if one considers the requirement that “signals of interest are gathered ten times per second”, it is clear that this is a time critical task. Since the background loop is

43 Figure 3.4: Flow chart showing the possible paths of background loop execution; line numbers refer to MainSrcCode.c which is found in Appendix B.1.1.

44 not appropriate for time critical tasks due to its strictly sequential execution, the gathering of data from inputs must be placed in the foreground, in this case the HPISR, which is discussed in the following subsection. This also helps to satisfy the requirement that data gathering operations may interrupt any RS-232 communications. Another key thing to notice in Figure 3.4 is that the gathered data may be logged to both the SD card and/or the Terminal1. This may also seem odd that these tasks are not included in the HPISR with the data gathering function. After all, that would allow data logging to the SD card or Terminal to interrupt RS-232 communications. First, let’s consider data logging to the SD card. Writing a data log to the SD card it an unbounded operation, meaning that its execution time may vary. Placing SD card write operations in the HPISR would make the ISR execution time unpredictable, and could lead to a loss of data if SD card write operations overflow into the next data gathering time. This would also lead to a loss of responsiveness to RS-232 commands. Due to these reasons, data logging to the SD card is executed in the background. Second, let’s consider data logging to the Terminal. The same UART that is used for receiving commands from the PC is used to send data logs to the Terminal on the same PC. Because the RS-232 communication used is asynchronous and does not include any handshaking, error checking, or error correcting, it is a bounded operation with a predictable execution time. However, even though the execution time is predictable, it is long2 and would slow the response of the MCU to incoming RS-232 commands and potentially cause data to be lost (as described above) if it were executed in the HPISR. Therefore, data logging to the Terminal is also executed in the background. Specific execution times and performance will be discussed later in this chapter.

1Here “Terminal” refers to a serial communications terminal program on a PC, such as HyperTerminal. 2Long meaning on the order of tens of milliseconds

45 Beginning the Loop

Again observing Figure 3.4, once the main() state machine has entered State One (flowchart element 1) and begun execution of the background loop, it immediately proceeds to check if data logging operations are enabled (element 2). This is implemented with a single flag bit that is either set or cleared depending on user input via RS-232, and is included to fulfill the requirement that data logging operations will be stopped/started by an RS-232 command. Next, the loop immediately checks whether data logging operations are locked out (element 3), which is also implemented with a single flag bit which is set and reset control access to the buffer where gathered data to be logged is stored. For example, when a function is accessing the buffer it sets the lock out bit to prevent other functions from accessing the same buffer, which could cause data to be corrupted. By locking out all other access to the buffer, data logging operations are allowed to execute atomically, that is, without the possibility of the data log buffer being modified during an SD card or terminal write. Because data logging operations are included in main, and the only other operations that can access the same buffer (the data gathering operations) are located in the HPISR, it is unlikely (pretty much impossible, actually) that the data logging operations would interrupt other operations using the same buffer, but the lock out functionality is included anyway as a precaution.

Managing Data Between Gathering and Logging

Two of the major operations of this application are the gathering of data from the inputs, and the logging of that data to an SD card or Terminal. However, the former is a time critical task and must be carried out once every 100ms with a reasonably predictable execution time, while the later is not time critical and can have some variation in execution time. These two operations must be decoupled so that a delay in data logging does not cause the current data to be lost when new data is gathered. Therefore, a buffer that can accumulate data from several gathering times and hold them until they can be logged (as mentioned in the

46 Figure 3.5: Circular buffer represented both as a ring as well as linearly; the circular buffer is first-in first-out or “FIFO”.

previously) is placed between the two operations to decouple them and solve the problem. The buffer is implemented as a circular buffer (Figure 3.5) so that once data is placed in the last buffer slot, new data will be placed back in the first slot, hence the term “circular”. Note that the new data is always placed in the “tail” slot of the buffer, while data to be logged is taken from the “head” slot of the buffer. Based on available MCU RAM and gathering/logging execution time, the circular buffer between data gathering and logging was designed to hold 5 data logs. Element 4 of the flow chart in Figure 3.4 determines whether new data has been gathered and is waiting in the buffer to be logged. This is implemented by checking the head and tail indices of the buffer. The two indices3 are both initialized to zero upon start-up, indicating that the buffer is empty. When data is gathered and placed into a buffer slot, the tail index is incremented, and when data is taken from a buffer slot to be logged the head index is incremented4. This means that any time after initialization, if incrementing the head index causes both indices to be equal then the buffer is empty. However, if at any time the head and tail indices are not equal then new data to be logged is available in the buffer. Also note that we must always leave one buffer slot empty so that the tail index doesn’t wrap around

3”k” is the head index, “j” is the tail index 4both indices roll over to zero when incremented past the size of the buffer

47 Figure 3.6: Linear representations of some key circular buffer conditions for this application.

onto the same slot with the head index, which would be interpreted as an empty buffer. This means that a buffer with N slots is full when N-1 slots are occupied with data; hence an array of six data log structures is used to implement the circular buffer for five data logs in this application. All of these buffer conditions are summarized in Figure 3.6. Therefore, to implement the check for new data in flow chart element 4, the background must simply check if the head and tail indices are equal, as seen on line 298 of Appendix B.1.1. Observe that the sequential nature of the superloop means the background execution will halt at line 298 and poll the circular buffer indices until new data is available to log. Also note that if a slot was not kept empty between the tail index and head index, when the tail rolled over onto the head it would be interpreted as an empty buffer and all the data in the buffer would be lost. The logic for determining available buffer slots will be discussed with the data gathering operations in the foreground loop subsection.

48 Logging Data to the SD Card Data Log File

Once new data is available in the circular buffer, the background then checks to see if logging data to the SD card is enabled. This is again implemented with a single flag bit controlled by RS-232 commands from the user, and fulfills the requirement that data logging to the SD card and Terminal shall be independently selectable. Assuming data logging to the SD card is enabled, the first available (remember the circular buffer is FIFO) data log from the circular buffer is written to the open binary5 SD card data log Figure 3.7: Data log structure; note that one “dat- file, in this case called “DATALOG0.bin”. alog” structure includes an array of eight “channel” structures, which in turn each include a “bitfield” At this point it seems appropriate to discuss structure. the details of a data log for this application, beyond the simple requirements given in Chapter 2. For the purposes of this application, a “data log” has the structure shown in Figure 3.7, which is also shown on lines 42-67 of Appendix B.1.3. Note that the “datalog” structure contains all required signals of interest for all eight motor channels, as well as a unique time stamp in the form of a 32 bit unsigned integer repre- senting the tenths of a second elapsed since the initial date and time programmed into the source code6. The circular buffer discussed above is implemented as an array of “datalog” structures. Each subsequent data log is taken FIFO from the buffer appended to the end of

5Since the file is binary, if “200” was a 16 bit unsigned integer, it would be represented in the data log file at “00000000011001000” 6lines 53 and 54 in B.1.1.

49 the data log file by the function “PutDataLogSD”7. Writing the data log to the file in binary decreases the SD card write period8, but sacrifices the human readability of the resulting data log file on the SD card. This necessitates the use a custom binary-to-ASCII conversion program that operates on the binary data log file, which is discussed in a later section. In order to facilitate this conversion, and include information required to successfully interpret the resulting human readable text file, the binary data log file has the structure shown in Figure 3.8. As on can see in Figure 3.8, there is quite a bit of information that precedes the actual data logs. As stated above, this information is either to facilitate the conversion process or will be required by the end user to effectively interpret the logged data. First comes the version stamp, which is simply a string of ASCII characters used to identify the embedded software that created the data log, which is likely important information to the end user. The next two blocks of information are also in the form of ASCII strings: the initial date stamp and the initial time stamp, both of which are required to usefully interpret the resulting data logs, whose relative time stamps are referenced to this initial date and time. Next comes the media roll over count, which indicates how many times (if any) the 2 GB SD card media has rolled over since the last time the embedded software was initialized. Roll over will be discussed more later. The headline block size simply describes the size of the headline block that immediately precedes the data logs. The data log size comes next, which (as one might expect) simply provides the size of a single data log in bytes. Both the data log size and headline size are necessary for conversion, as the data log structure and elements names may change in a future software version, and are not necessarily available to the conversion program. Next comes the descriptor block size, providing the size of the following descriptor block in bytes. The descriptor block allows the conversion program to identify each data log element from the string of binary numbers as either a 4, 3, 2, or 1

7prototype is: unsigned int PutDataLogSD(FSFILE *pSDFile); definition on lines 579-788 of B.1.2 8writing binary to a *.bin file was found to be about 10 times faster than converting to ASCII with a printf and writing the result to a *.txt file

50 Figure 3.8: Structure of the binary data log file.

51 byte unsigned integer, or a 1 byte unsigned integer taken bitwise. Because the descriptor block directly describes the data log structure (like the headline block) it may change in a future software version, which necessitates the descriptor block size information directly preceding it. Finally, the headline block is a string of ASCII characters that provides a short descriptive title to each data log element, and is comma delimited. The data logs begin immediately following the headline block. All the information preceding the data logs in the binary file is written to the top of the file when it is opened upon initialization, and again if the SD card media rolls over.

Checking for Roll Over

As mentioned previously, the storage medium for this application is a 2 GB SD card9. In fact, this is the largest practical storage size for this application due to the MDDFS being a FAT16 file system that can only address up to 65,536 clusters of memory, each containing 64 sectors of 512 bytes each; a total of 2,147,483,648 bytes. After writing a data log to the SD card, the background checks the current position of the file pointer10 used in writing the binary file, which equals the number of bytes written since the beginning of the file. Assuming there is no other content on the SD media besides the binary data log file, all bytes not used for FAT16 formatting are available for the file. Therefore, a successful call of FSftell that returns a value of 2,002,452,480 indicates that the media is completely full. In order to build in a margin of safety, the media is considered to be full when FSftell returns a value >=1,999,999,999. When this roll over condition is met, the file pointer is rewound to the beginning of the binary file11, the media roll over count is incremented, and the information that precedes the data logs is again written to the beginning of the file12, and any new data logs are written over the old ones, starting with the first old data log all

9actual storages capacity with FAT16 formatting: 2,002,452,480 bytes 10Using the function “FSftell”, part of the MDDFS function set[23]. 11Using the function “FSfrewind”, part of the MDDFS function set[23]. 12See lines 310-325 of B.1.1.

52 the way to the end of the file. Note that the old data logs are not automatically lost when the media rolls over, only when they are overwritten by a new data log. Considering that each data log using the structure in Figure 3.7 is 62 bytes, and 554 bytes13 of information precede the data logs, 32,258,055 data logs can be logged to the media before

1 rolling over. With a 10 Hz recording rate, that translates to about 37 3 days worth of data logs. Since old data is not written over until the media has rolled over and new data is ready to replace it, when a motor faults occurs and the data logs associated with that time are needed, the binary data log file must be recovered (or data logging must be stopped) within about 37 days for the data to be available. All of this assumes that the file pointer position check was successful. If the check fails (background flow chart element 20) the binary file on the SD card is closed, output is switched to the terminal, and the error indicator LED is illuminated. In either case, the background proceeds to transmit a capital “X” to the Terminal via UART to indicate data has been logged to the SD card, and immediately checks if data logging to the Terminal is enabled.

Logging Data to the Terminal

Similar to the control on logging to the SD card, a single flag bit is used to enable or dis- able data logging to the terminal. This bit is controlled via RS-232 commands from the user, and fulfills the requirement that data logging to the SD card and Terminal shall be independently selectable. Assuming that data logging the Terminal is enabled, the func- tion “PutDataLogUARTASCII”14 takes the first available (FIFO) data log from the circular buffer, and writes it to the terminal. However, when a data log is written to the terminal it’s not being recorded for later conversion. In fact, if it is being sent to the terminal, it is

1338 bytes for first 7 blocks, then 34 bytes for descriptor, then 482 bytes for headline 14prototype is: void PutDataLogUARTASCII(void); definition on lines 790-829 of B.1.2

53 likely that the user is viewing the data logs in real time as they are recorded15, meaning that the data logs written to the terminal must already be human readable. To facilitate this, the PutDataLogUARTASCII function converts each of the data log elements to an ASCII character strings using sprintf commands16 and places the string in a buffer to be output to the Terminal via UART. Because the data logs are not being recorded on the receiving PC as they are on the SD card, there is no need for the type of feedback used with SD card writes to detect media roll overs. Similarly, the size of the data log and the contents of each element are not important when writing to the Terminal, because the conversion to human readable ASCII characters has already been accomplished within the MCU. This of course makes writing to the Terminal more time consuming than writing to the SD card, which will be examined in detail when the results of the superloop version of the software are evaluated. Once writing to the terminal is complete (or even if it was never enabled at all) the back- ground proceeds to increment the circular buffer head index and roll it over if necessary17. Please note that even if both SD card and Terminal data logging are disabled, the head index of the circular buffer will still be incremented if data logging is enabled. This allows the circular buffer to always be filled with the latest data logs, even if they aren’t currently being output to the SD card or Terminal.

Handling RS-232 Commands Received via UART

Once all data logging operations have been completed, the background immediately proceeds to check if an RS-232 command has been received18 by checking the UART receive buffer flag, which is set upon reception of a character and must be cleared by reading the UART receive buffer. Note that because the PIC18F8722 has only an 8 bit UART receive buffer, each command is a single ASCII character. Also of importance is the MCU UART baud

15This would be desirable for diagnosing a motor or the embedded software 16This necessitates the inclusion of the stdio.h header 17background flow chart elements 14,15, and 16 18background flow chart element 17

54 rate, which is set to 115.2 kbaud. If no command has been received when the background checks the receive flag, the current iteration of the background loop is complete, and it begins executing from the beginning19. However, if a command is received, the UART receive buffer is read to an unsigned character variable and then cleared. A switch/case structure then handles the received command, which is described by the flow chart shown in Figure 3.9. Note that the possible commands are:

• command “s” stops data logging operations.

• command “g” begins data logging operations.

• command “c” toggles output to SD card.

• command “t” toggles output to Terminal.

• command “v” displays version name to Terminal.

• command “d” displays initial date/time to Terminal.

Note that when recording is stopped (that is, all data logging is disabled) the possible RS-232 commands are output to the Terminal to be displayed to the user. If a command character is received by the MCU that is not of the command characters in the above list, it is ignored and nothing happens. Observing the flow chart in Figure 3.9, the quoted capital lettering in elements 9, 10, 11, and 12 represent the MCU’s immediate response to the command via UART. Once the received command has been appropriately handled, the current background iteration is complete, and the background loop executes from the beginning of main() state one on line 295.

19That is, the background returns immediately to element 1 in the background flow chart

55 Figure 3.9: Flow chart describing the handling of RS-232 commands received via UART.

56 3.1.2 The Foreground Loop

As mentioned above, the foreground loop is implemented with an ISR, in this case it is the high priority ISR (HPISR) of the embedded software. When the foreground is invoked by an interrupt, all other interrupts are disabled and the HP interrupt vector (0x0008) is loaded into the MCU . The HP interrupt vector contains an instruction to jump to the beginning of the HPISR, where execution of the foreground begins20. While the HPISR will execute on any enabled HP interrupt that occurs, we only want the foreground to execute when the Timer3 overflow interrupt occurs. To ensure this, all interrupt sources other than the Timer3 overflow interrupt are disabled. Like the background loop, the foreground loop is designed as a state machine to control the flow of execution, as seen in Figure 3.10. However, unlike the background loop which continuously executes once it has started, the forground loop executes only once each time it is invoked. Once the foreground has finished its current execution, control is returned to the background loop exactly where it was interrupted.

Counting Commanded Motor Steps

Observing Figure 3.10, it may seem odd that the Timer3 overflow interrupt is occurring every 25ms even though data is only required to be logged for every increment of 100ms. However, due to the PIC18F8722 having only two inputs that can be configured to increment a register on a rising edge, and the fact that steps must be counted for eight motors, we can

1 only count each motor for 4 of the 100ms associated with each data log. This is the reason behind the use a state machine in the foreground and a 25ms timer interrupt. As one can see in Figure 3.10 steps are counted for a pair of motors for 25ms, then the steps are recorded to memory, then the next pair of motor step signals are multiplexed to the step inputs21, and finally the step counting registers are cleared to begin counting for the next pair of motors.

20line 80, Appedix B.1.2. 21Digital outputs are RG4:RG3 as shown in Figure 2.13

57 Figure 3.10: State machine used to implement foreground loop in HPISR; note that control is returned to the background upon completion of each state.

58 Figure 3.11: Diagram showing the order and manner in which commanded motor steps are counted by the MCU

Figure 3.12: Theoretical motor velocity and position given 200 steps per second squared acceleration for 50 seconds. The MATLAB script used to generate this figure can be found in Appendix B.4.1.

In this way, motors steps are counted in the manner shown in Figure 3.11. Assuming the maximum motor acceleration is limited to a rate of 200 steps per second squared, the 25ms step counts can be multiplied by 4 to achieve a reasonable approximation of the number of steps commanded during the whole 100ms period. Figure 3.12 shows the theoretical motor velocity and position for the worst case of a 200 steps per second squared acceleration up to the maximum motor velocity of 10,000 steps per second22. When counting motor steps we

22acceleration is held constant at 200 steps per second squared for 50 seconds, then drops to zero for the last ten seconds. Velocity calculated as: vt1 = vt0 + (at1)(t1 − t0). Position calculated as: xt1 = 1 2 xt0 + (vt0)(t1 − t0) + 2 at1(t1 − t0) .

59 Figure 3.13: Detail of Figure 3.12 showing motor position from t=49.9s to t=50.0s. are interested in the position of the motor, which in this case is measured in steps. Notice in Figure 3.12 that motor steps do not vary linearly with time over the entire period of acceleration, which at first seems like it might be a problem for the method of step counting employed in this application. However, the motor steps are very nearly linear over intervals of 100ms, which is the period of interest. For example, the interval from 49.9s to 50.0s that is shown in detail in Figure 3.13 appears to be quite linear. Also note that during this interval the motor is still accelerating at 200 steps per second squared and has nearly reached its maximum velocity of 10,000 steps per second. To validate the step counting approximation used in this application, consider that between t=49.900s and t=49.925s the motor stepped 250 times. Using the approximation, we would multiply this count by four and claim that the motor stepped 1000 times between 49.9s and 50.0s23. However, the exact count of steps between t=49.9s and 50.0s is 999. So in this case the error is 0.1%.

23This is expected as the maximum step velocity is 10,000 steps per second

60 Figure 3.14: Flow chart describing the execution of foreground state three.

Gathering Data from Inputs for the Current Data Log

After the last pair of motor step signals has been counted in foreground state three and the step counting multiplexer controls and registers have been configured for the next pair of motor step signals, the foreground increments the time stamp and begins the task of gathering all the other necessary data from inputs for the data log associated with the current 100ms interval. This process is summarized in by the flow chart shown in Figure 3.14. As described previously, the time stamp is implemented with a 32 bit unsigned integer; given that it is incremented every 100ms to provide a unique time stamp for each data log, up to 4,294,967,29624 data logs can be uniquely identified.

24enough unique time stamps for about 13.6 years worth of data logs

61 Once the time stamp has been incremented, the foreground proceeds to check if any slots are available in the circular buffer, as shown in element 3 of Figure 3.14. Recalling that one slot must always remain empty between the head and tail indices to prevent a loss of data, the foreground uses the following logic to check for available slots:

if (tail+16= head) is true and (head+circbuffersize-16= tail) is true → tail is pointing to next available slot

else → tail is pointing to last empty slot, buffer is full

Should the buffer be full, “DATALOG BUFF FULL” is output to the Terminal, and no data is gathered to the buffer. The next foreground state is set to zero and control is returned to the background to log data from the buffer to SD card or Terminal, thereby freeing up buffer slots for new data. Assuming a slot is available for the next data log, checks are made to verify that data logging is enabled and not locked out. Data logging control is established with the same flag bit used in the background, and is controlled by RS-232 commands from the user. Data gathering lock out is also achieved with a single flag bit, but is separate from the data logging lockout used by the background. Here in the foreground data gathering lock out is more important, because it is possible for data gathering operations to interrupt data logging operations in the background, possibly modifying circular buffer contents that are in the process of being logged to the SD card. To prevent this, the foreground is locked out from the circular buffer during background data logging operations. Once the foreground has verified that data logging is enabled and not locked out, a call is made to the function “GetDataLog”25 which multiplexes and converts all necessary analog signals through the integrated ADC module, and records the results to the appropriate ele- ments of the data log structure in the circular buffer element pointed to by the tail index.

25prototype is: void GetDataLog(void); definition on lines 151-248 of B.1.2

62 After all analog signals have been converted and placed in the buffer, all digital inputs are recorded and placed in their appropriate locations in the same element of the buffer26. Also note that the time stamp is placed in the data log structure in the same circular buffer element by the GetDataLog function. Upon completion of placing the latest data log into the circular buffer, the foreground incre- ments the circular buffer tail index, and wraps it to the beginning of the buffer if necessary. Following either case, the next state of the foreground is set to zero and control is returned to the background following the completion of foreground execution.

3.1.3 Overall Superloop Version Software Structure

To summarize this section, the overall software structure and function can be described at a high level by Figure 3.15. The background and foreground have already been analyzed in detail in the previous subsections. However, note that two separate arrows are used to de- scribe the relationship between the two functional background elements in Figure 3.15. This is to emphasize that when handling RS-232 commands is complete, the data logging element is returned to at a point different than when it handed control to the RS-232 command han- dling element. This is in contrast to the relationship between the background elements and the foreground, where a single bidirectional arrow indicates that when foreground execution is complete, control is returned to the background element exactly where it was interrupted. In previous subsections the initialization phase was mostly ignored, so let’s take a moment now to describe and analyze it. As shown in Figure 3.15, the first initialization task is to ini- tial all the necessary integrated MCU hardware modules, such as the ADC, UART, Timers, etc. This is done on line 171 of MainSrcCode.c with a call to the function “InitPeripherals”27. The next task is to initialize the circular buffer that will hold completed data log structures

26Recall from Chapter 2 that not all digital inputs are available on the development tools used for this work, so all digital inputs are collapsed onto RH2, RH3, RE0, and RE1. 27prototype is: void InitPeripherals(void); definition on lines 15-120 of B.1.2

63 Figure 3.15: High level overall description of superloop application software structure and function

64 waiting to be logged to SD card or Terminal. The function “InitDataLog”28 is called on line 172 of MainSrcCode.c to initialize all data log elements in all buffer slots to zero. Following buffer initialization, the software proceeds with initialization of the MDDFS on the SD card. However, before initializing the MDDFS the card must first be successfully detected (line 174 of MainSrcCode.c) and not physically write protected29. Assuming the SD card is present and not write protected, the MDDFS is initialized with a call to function “FSInit()”30 on line 189 of MainSrcCode.c. Once successful initialization of the file system has taken place, the binary data log file is opened in write mode and all information preceding the actual data logs31 is written to the top of the file before being closed. Finally, before turning control over to the background loop, the initialization block disables data logging operations, as well as output to both SD card and Terminal, such that the user may control when to begin data logging, and to which outputs.

3.2 Data Log Conversion Software

Considering that the resulting data log file on the SD card is binary in nature, it must be converted to human readable ASCII characters and formatted in such a way that the logged data is convenient to interpret and analyze. To accomplish this, a custom 32 bit Windows console application was developed specifically for this binary-to-ASCII conversion task. The high level structure and function of this conversion software is described by the flow chart in Figure 3.16. Note that for the conversion software to operate on the binary file, it must be located in the same directory as the conversion software executable, and must have the name “DATALOG”. The resulting text file is always titled “DATALOG” as well, and is placed in the same

28prototype is: void InitDataLog(void); definition on lines 122-149 of B.1.2 29SD cards have a small switch that can lock the card and protect its contents from being overwritten 30Part of the MDDFS function set. 31See Figure 3.8.

65 Figure 3.16: Flow chart describing the execution of the data log binary to ASCII conversion application.

66 directory. It is assumed that the conversion software knows the structure of the overall binary file (Figure 3.8), but that the size and format of the actual data log can vary as long as all data logs adhere to that size and format. The source code used to generate the binary to ASCII converter executable can be found in Appendix B.3.1. Observing the conversion software execution flow chart in Figure 3.16, note that first the binary file is opened in binary read mode and most all of the information preceding the descriptor block is read to variables in local memory. This information allows the application to seek to the beginning of the first data log, and mark that point as the data log start position. Similarly, the application then seeks to the end of the file, marking that position as the data log end point. Knowing the total bytes occupied by data logs and the size of each data log in bytes, the number of data logs contained in the binary file can be determined. The application then reads the contents of the descriptor block to a character array in local memory for later use in determining the format of each data log element. Before the actual conversion of data logs begins, the application closes the binary file, opens the text file, and writes the pre- data log information to the top of the text file, as well as to the Win32 console. Next, the application proceeds to transcribe the headline block from the binary file to the text file character-by-character. With the headline block written to the text file, conversion of each data log can begin. Conversion of each data log proceeds element-by-element with a comma separating each element in the text file, and each data log beginning on a new row of the text file. After each data log is converted, the percent of data logs converted is updated and displayed to the Win32 console. The result of the conversion is an intuitively organized human readable text data log file, which will be further discussed in Section 3.5: Results and Evaluation.

67 3.3 Testing Methods

To test the functionality of the embedded application software, the various analog and digital inputs that would be produced by the stepper motors and associated drive board must be simulated. This includes:

• Motor Drive Voltage (analog)

• Phase A Current Sense for all eight channels (analog)

• Phase B Current Sense for all eight channels (analog)

• Step Inputs for channels 1-4 (digital)

• Step Inputs for channels 5-8 (digital)

• Direction input for all channels (digital)

• CW,CCW, and HOME limit switch statuses for all channels (digital)

For the sake of simplicity, all analog inputs will be tied to the same analog test signal, and all digital inputs (not including step inputs) will be tied to the same digital test signal. Considering the analog test signal should be variable from 0V to 5V, a current limited32 DC power supply was used to simulate the analog signals. Recalling that all non-step digital inputs are collapsed onto the four pins RH3, RH4, RE0 and RE1, all non-step digital signals were simulated by tying these pins to either VDD or ground on the PICDEM PIC18 development board.

The step input signals, requiring a variable frequency up to 10 kHz, necessitated a sep- arate testing method. Observing the PICDEM PIC18 schematic in Figure A.2 of Appendix A, note that the pin used for Ch5 - Ch8 step inputs (pin 36 - T13CKI/T1OSO) is used for a

32Limited to 25mA output, maximum sink current for MCU I/O pins.

68 Figure 3.17: Lab bench set up for testing embedded application software. Note the DC supply limited to 25mA for analog inputs, and signal generator for step inputs. The development tools are on the bench in front of the oscilloscope; the PC to the right is used to control software execution on the PIC18F8722 via the PICkit3 debugger interface and MPLAB IDE.

32.768 kHz RTCC oscillator input. While this is not ideal for this application, it does provide a convenient 32.768 kHz test signal to the Ch5-Ch8 step input. Testing the Ch1-Ch4 step input is slightly more difficult, as the PICtail Daughter Board for SD/MMC Cards uses pin 34 (RA4/T0CKI) as a digital input for SD card physical write detect. Therefore, this input may only be tested when data logging output is directed only to the Terminal, as any step signal applied to pin 34 will cause erratic SD card write behavior. When tested, the step signal applied to pin 34 was provided by a signal generator as a 0V-5V square wave of 50% duty cycle and variable frequency from DC to 10 kHz. A picture of the overall test set-up and equipment is shown below in Figure 3.17. With all signals of interest simulated, the embedded software can be tested to verify functionality. This primarily includes verifying

69 Table 3.1: Summary of MCU outputs used to measure superloop software execution periods of interest.

Period of Interest MCU Output HPISR RD4 Data Gathering RD4 Logging to SD Card RD5 Logging to Terminal RD6 Initialization RD7 successful gathering of input signals and successful output to both the SD card data log file and Terminal, together and separately. Also of interest is the HPISR execution time in all states, the SD card write period, and Terminal write period, all of which are required to verify the 50% processor idle time requirement. In order to test software execution times such as the HPISR, MCU output pins are set and cleared to measure these periods of in- terest. Table 3.1 details the output pins used for this purpose. Output pins are always set on entrance to the period of interest and cleared on exit. The initialization time from start-up to being ready to begin data logging is also verified, although a requirement for this time is not specified. Correct and prompt response to RS-232 commands is also of interest in verifying functionality. To verify this, the following command sequence is issued to the MCU via RS-232 following initialization:

1. “t” - enable Terminal output

2. “c” - enable SD card output

3. “g” - begin data logging

4. “s” - stop data logging

5. “t” - disable Terminal output

70 6. “g” - begin data logging

7. “s” - stop data logging

8. “c” - disable SD card output

9. “g” - begin data logging

10. “s” - stop data logging

11. “v” - display software version

12. “d” - display initial date and time

Finally, the ability of the binary to ASCII data log conversion application to correctly convert the binary data log file to a properly formatted human readable text file is tested.

The results of these tests is reported on and evaluated in the following section.

3.4 Results and Evaluation

Possibly the most important result is the data log file. This is the file that holds the data logs for later interpretation and analysis for the purpose of diagnosing stepper motor faults. The resulting binary data log file produced by the MCU and contained on the SD card is not human readable, as seen in Figure 3.18. Placing the binary data log file in a directory with DataLogBin2ASCIIVer4.exe and executing the conversion program (Win32 console during conversion shown in Figure 3.19) yields the text data log file shown in Figure 3.20. Note that the converted data log file begins with the initial date and time stamp. In this way, a program could be developed to further convert the unique data log time stamps from a relative value to a real date/time value based on the initial data and time provided in the file. Following the initial date and time, the text file includes much of the data required for

71 Figure 3.18: Binary data log file when interpretted directly as ASCII characters by Notepad.

Figure 3.19: Win32 console displayed by binary-to-ASCII conversion program during execution. Note that the program waits for the user to acknowledge conversion complete before exiting.

72 Figure 3.20: Final data log correctly converted to ASCII text. conversion, such as data log and headline size. The ninth line of the text data log displays the descriptive headline for each element. Note that some headline elements are single dashes; this indicates that the data log element is and unused bit of a byte taken bitwise. Beginning immediately after the headline, each data log is reported on a new line, starting with its unique time stamp. Note the each data log element is comma delimited. Combining this with the descriptive headline should allow for convenient interpretation and analysis of the data log for analyzing stepper motor faults. Note that Figure 3.20 does not show the entire data log; each data log (and the headline) extend off the page to the right. This other portion of the text data log file are shown in Figure 3.21. Observing Figure 3.20, the first column (the relative time stamp) increments down the rows, as expected. The next column represents the motor drive voltage, and as expected it is nearly 1,023 at all times, which is expected, since it is connected to +5V during testing and the maximum ADC reading is 1,023 and internal reference voltage is +5V. The next two columns represent the Phase A and Phase B current sense for channel 1. Note that these also indicate +5V as expected. The following column

73 Figure 3.21: Channels 5 throught 8 of the final text data log file. indicates 0 commanded steps on channel 1, as expected. In fact, commanded motor steps is 0 as expected for channels 1,2,3 and 4, and approximately 3,276 for channels 5, 6, 7, and 8. This is due to the inability to place a step counting signal on the channel 1,2,3, and 4 input, as it is used as the SD card physical detect bit. The step input for channels 5, 6, 7, and 8 is excited by the 32.768 kHz RTCC oscillator on the PICDEM PIC18 Explorer development board. The four columns following the channel 1 current sense elements indicate direction and limit switch statuses, and all indicate high, as expected, since all digital inputs used for testing were tied to +5V.

Observing the rest of the motor channels in Figures 3.20 and 3.21, it can be seen that all motor channel direction and limit switch statuses indicate high. However, not all current sense elements indicate approximately 1,023. Phase A of channels 5 and 6, and Phase B of channels 4 and 5 indicate an analog voltage lower than +5V. Phase B of channel 4 takes input from pin RA3; pin RA3 is occupied by a connection to the PICtail Daughter Board for SD/MMC Cards, and so was not excited to +5V with the rest of the analog inputs. The

74 same is true for phase A of channel 5, except that its input is taken from pin RA5. Phase B of channel 5 takes input from RA1, which is directly connected to a temperature sensor on the PICDEM board, and so was not excited to +5V. Similarly, phase A of channel 6 takes input from RA0, which is directly connected to a potentiometer on the PICDEM board. These results illustrate some of the constraints imposed by the use of development tools.

Notice that the last four elements of each data log (those corresponding to “-” headline elements) vary between indicating low and indicating high. These elements do not contain any information, and are simply the extra bits of data log bytes taken bitwise. Also note a discontinuity in the data log time stamps in Figure 3.20 between time stamp 77 and 116. This is a result of data logging being stopped and restarted via an RS-232 command.

In short, the resulting text data log is exactly as expected given the test set up and testing method. This indicates that the superloop version of the embedded application software meets requirements 1, 2, 4, 5, 6, 7, and 8 for logging data for all eight motor channels, as set forth in Chapter 2.

Correct response to RS-232 commands was also verified during testing. Consider Figure 3.22, which shows HypertTerminal output during the RS-232 command testing described in the previous section. Note that in all RS-232 command tests involving HyperTerminal, local characters are not echoed. Note that the software reports version and initial date/time to the Terminal after initialization, and the default data logging setting is “stopped”. The two commands issued to the MCU are “t” and “c” to enable both data logging outputs. The MCU acknowledges these commands, but does not begin data logging until it receives the next command, “g”. From there, the data logs are reported to the Terminal (seen in Figure 3.22) and to the SD card, which is indicated by a capital “X” reported to the Terminal. Notice that the data log time stamps do not start from zero; this is due to elapsed time between the end of initialization (when the time stamp begins incrementing every 10th of a second) and the beginning of data logging operations. After some time, the command “s” is

75 Figure 3.22: Beginning of hyperterminal output for superloop test case.

76 issued to the MCU to stop data logging, as seen in Figure 3.23. Note that RS-232 command options are successfully displayed to the Terminal when data logging operations are stopped. Output to the terminal is disabled with the “t” command before data logging operations are resumed with the “g” command. Aftwerwards, only capital “X” is written to the Terminal to indicate data logs output to the SD card binary data log file. Note that this pause in data logging accounts for the discontinuity observed between time stamps 77 and 116 in Figure 3.20, as the time stamp continues to increment when data logging in stopped. After allowing data logging to the SD card to continue for some time, the command “s” is again issued to stop data logging, as shown in Figure 3.24. With data logging stopped, output to the SD card is disabled with the “c” command. With both outputs now disabled, data logging is resumed by issuing the “g” command. Note that no data logs are written to the Terminal, and no “X”s indicating data logging to the SD card are output to the Terminal either. Finally, “s” is issued to stop data logging for the final time, and commands “v” and “d” are used to display both the software version and initial date/time to the Terminal.

All RS-232 commands were handled and responded to correctly, and without any delay noticeable to the user. Therefore, the superloop version of the embedded application software meets requirements 9, 11, 12, and 13 regarding RS-232 communications as set forth in Chapter 2.

To verify that the software can log commanded step counts up to 10 kHz for all 8 motor channels simultaneously, a 10 kHz, 50% duty cycle, 5V peak square wave was connected to the commanded step input for channels 1-4. Note that the commanded step input for channels 5-8 is already connected to a 32.768 kHz oscillator, which exceeds to 10 kHz minimum requirement. The step signals are shown in Figure 3.25. With the 10 kHz step signal applied to the same pin used for SD card write protect, the test results can only be observed via the Terminal. The Terminal output resulting from these step input signals is shown in Figure 3.26. Note that for channels 1-4 each data log indicated 1000 steps; this is expected, as

77 Figure 3.23: HyperTerminal output when data logging operations are stopped with both outputs enabled. Terminal output is disabled before data logging is restarted.

78 Figure 3.24: HyperTerminal output when data logging is stopped with only SD card output enabled. SD card output is disabled before resuming data logging operations.

79 Figure 3.25: 10 kHz step signal supplied to input for channels 1-4 (top, yellow), and 32.768 kHz step signal supplied to input for channels 5-8 (bottom, green)

Figure 3.26: Terminal output resulting from 10 kHz step input to channels 1-4 and 32.768 kHz input to channels 5-8. Step counts of last data log highlighted in red for easy identification.

80 each data log counts commanded steps for each motor for only 25ms (250 steps at 10 kHz) and multiplies by four to get the final result. The resulting step counts for channels 5-8 are somewhat more variable, most likely due to the lower and more variable signal amplitude of the oscillator output. Regardless, these results indicate that the superloop version of the embedded application software meets requirement 3 regarding commanded motor step counting, as set forth in Chapter 2.

To verify that 50% processor overhead time is reserved for RS-232 communications, the execution times of several blocks of the software must be examined. Since the software execution is fairly periodic33 during data logging (i.e. data is gathered, then logged, then the process is repeated), it must be verified that data gathering and logging operations occupy no more than 50ms during each 100ms period.

The first software execution period of interest is the HPISR, which contains all data gathering operations. The HPISR period is shown in Figure 3.26, where a high value indicates the HPISR is executing. Notice the regular HPISR frequency of 25ms, as expected, however the execution duty cycle is so small it is difficult to measure execution time using Figure 3.26. It is clear, however, that every fourth execution period is slightly longer than those surrounding it due to increased data gathering requirements. A detailed view of the shorter HPISR period, when only step counts must be gathered, is shown in Figure 3.27. A detailed view of the longer HPISR period, when all other digital and analog data must be gathered and placed in the circular buffer, is shown in Figure 3.28. With a three short execution periods of 25.2us and one long execution period of 752us every 100ms, the HPISR has a very low duty cycle. However, it still occupies approximately 827.6us of processor time every 100ms. Again, note that this includes all data gathering operations.

The second software execution period of interest is data logging to the SD card. The SD card write period is shown in Figure 3.29, where a high value indicates this block of software

33With a period of 100ms due to 10 Hz sampling time.

81 Figure 3.27: Superloop HPISR execution period. High level indicates HPISR is executing.

Figure 3.28: Detail of shorter HPISR execution time showing execution time measurements.

82 Figure 3.29: Detail of longer HPISR execution time showing execution time measurements.. is executing. Notice in Figure 3.29 that one of the SD card write periods is slightly longer that the others. This was observed several times, although rarely; perhaps in about 5% of SD card write periods. The typical SD card write time is shown in detail in Figure 3.30. Note the typical execution time of data logging to the SD card is 5.28ms in every 100ms period. The longer, non-typical, execution time that makes an appearance in Figure 3.29 is shown in detail in Figure 3.31. Only the typical SD card write time is considered for the purposes of calculating the reserved processor overhead time.

Finally, the last execution period of interest is time required for data logging to the Terminal. The Terminal write period is shown in Figure 3.32, where a high value indicates this block of software is executing. Notice that the Terminal write time is much more regular than the SD card write time. This is due to the fact that the RS-232 communications in this case take place only at the specified baud rate (115.2 kbaud), and no handshaking or flow control is implemented to change the rate of communication. Figure 3.33 details the Terminal write time. The Terminal write time is approximately 43.8ms during each 100ms

83 Figure 3.30: Detail of data logging to SD card period. A high level indicates this portion of the software is executing.

Figure 3.31: Detail of typical SD card write time showing execution time measurement.

84 Figure 3.32: Detail of non-typical SD card write time showing execution time measurement.

Figure 3.33: Data logging to Terminal period. A high level indicates this portion of the software is executing.

85 Figure 3.34: Detail of Terminal write time showing execution time measurement. period, which accounts for the bulk of the MCU processing time. Combining the HPISR period, SD card write period and Terminal write period, the MCU non-idle time during each 100ms period is 49.9076ms34. Therefore, 50.0924% processor overhead time is reserved for RS-232 communications, thereby fulfilling requirement 10 as set forth in Chapter 2.

Although no requirement on initialization time is dictated in Chapter 2, it may still be interesting to observe, and to compare to the RTOS-enabled version of the embedded application software. The initialization time was measured from very beginning of embedded software execution to the time it was ready to begin logging data. This initialization time was captured by the oscilloscope and measured to be 960ms, as shown in Figure 3.34.

The total MCU program and data memory occupied by the software may also be of interest for comparing with the RTOS-enabled version. This software occupies 50,608 bytes of program memory out of a possible 65,536 bytes available on the PIC18F8722 MCU. For data memory, the software requires 2,694 bytes out of a possible 3,936 bytes available on the PIC18F8722 MCU. This is represented visually in Figure 3.35.

34(3x25.2us)+752us+5.28ms+43.8ms = 49.9076ms

86 Figure 3.35: Superlooper version initialization time as captured by the oscilloscope. High value indicates initialization in progress.

Figure 3.36: Visual representation of superloop embedded application software memory usage.

87 As mentioned in Chapter 2, the last dictated requirement, regarding the effect of the data logging system on motor drive circuits, can’t be readily tested without a working NIF motor drive board. However, all the test performed indicate that the embedded application software would likely not have any effect on the motor drive circuits, as its only outputs are to a PC via RS-232, SD card via SPI, and several multiplexers via GPIO. In short, the testing shows that the superloop version of the embedded application soft- ware operates the PIC18F8722 to fulfill the requirements dictated in Chapter 2.

88 Chapter 4

Application Software - RTOS Version

We now begin the detailed analysis of the second version of embedded application software, which is implemented with a real-time operating system (RTOS). One might ask “but a working embedded application code has already been successfully developed (based on the superloop structure), so why would we develop another software for the same application?”. This is a valid question, and there are several answers. First, the embedded software required for this application is a good candidate for implementation with an RTOS, as we will see later in this chapter. Second, implementation with an RTOS provides several important advantages over a simple background/foreground loop. Lastly, implementing essentially the same embedded application software functionality with both a superloop and RTOS allows the two methods to be compared and evaluated against each other under reasonably fair conditions. Before continuing, a few words on RTOS fundamentals are in order.

4.1 RTOS Fundamentals

One of the major characteristics of an RTOS-based software (compared to a superloop- based software) is that functionality is partitioned into separate tasks, each having its own context, and able to be executed independently of other tasks. Here, “context” refers to

89 the state of the MCU surrounding the task during its execution, such as the contents of the program counter, stack, and special function registers. A real-time kernel can then implement multitasking by executing a task within its context for some time, then switching the MCU to execution of a different task and its context. This is referred to as a context switch, and allows several tasks to share MCU processing time, giving the appearance of concurrent task execution as shown in Figure 4.1.

Tasks can exist in one of several states at any time as shown in Figure 4.2, and are usually assigned priorities that determine the order in which eligible tasks are scheduled for execution; typically highest priority first. Unless otherwise forced, a context switch typically occurs when a task of higher priority than the one currently executing becomes eligible, or the current task delays, waits, stops, or destroys itself.

By surrounding each task with its own unique context, multitasking is transparent to the tasks involved. However, this also means that a way of communicating between tasks must be established. This is accomplished using well-defined inter-task communication devices such as semaphores, messages, and message queues.

The above description is true for many operating systems, so what makes it “real-time”? There are several definitions of what makes a software “real-time”[17], but in general this implies that the software must meet deadlines in some area such as response to events, processing of data, execution of a task, etc, or risk system failure or degraded performance.

Hopefully it is now easier to see how the embedded software for this application is a good candidate for implementation with an RTOS. First, it can be partitioned into distinct tasks to take advantage of multitasking. Second, paths of intertask communication can be well-defined. Third, the software must meet deadlines for proper operation. As mentioned previously, an RTOS implementation includes several advantages of superloop implemen- tations, such as less time wasted polling for events, easy addition of new functionality via tasks, and clearly defined communications between tasks.

90 Figure 4.1: Example of context switching between two tasks resulting in multitasking

91 Figure 4.2: Possible task states and possible paths between states. All states are initially destroyed.

92 This has been a very brief introduction to RTOS-enabled embedded software, but will hopefully be useful for the purposes of this thesis. The curious reader can find more informa- tion in some of the literature referenced in the development of the RTOS-based embedded software for this application[5][17][28][4].

4.2 Structure and Function

Of the possible RTOSs that could be used for this implementation1, Salvo Lite2 for PICmicro devices was selected as the RTOS for implementing the RTOS-enabled embedded applica- tion code. The Salvo RTOS was chosen due to the high degree of functionality included in the free Lite version, as well as its uncomplicated implementation on PICmicro devices using C18 and comprehensive documentation in a user manual[22] and other texts[28]. Salvo Lite is a priority-based cooperative multitasking RTOS; that is, eligible tasks are scheduled highest priority first, but tasks must yield to the scheduler for a context switch to occur. Note that Salvo Lite refers to intertask communication methods such as binary and counting semaphores, messages, and message queues as “events”. While Salvo Lite does include sig- nificant functionality in a free RTOS, it does limit the user to a maximum of 3 task control blocks (TCBs)3, and 5 event control blocks (ECBs)4. Furthermore, Salvo Lite can only be implemented with a library build, as the RTOS source code is not included with the free Lite version. Even with the these limitations, Salvo Lite provides a more than adequate platform for implementing the RTOS-enabled embedded application software.

Before developing the RTOS-enabled application software, it must first be decided how the necessary functionality from the superloop software version will fit into the task-and-

1Micropier Spindle RTOS, FreeRTOS, Pumpkin Salvo RTOS, and Segger embOS. 2Available at http://www.pumpkininc.com/. 3Meaning a maximum of 3 tasks can be multitasking at any one time, although more than three tasks can be used throughout the software. 4Meaning a maximum of 5 events can be used simultaneously, although ECBs can be reassigned to new events at any time.

93 event structure of the Salvo Lite RTOS, given the limitations described above. As identified in the previous chapter, the major functional blocks of this application are gathering data from inputs, logging data to outputs, and handling RS-232 commands. Functionality is also provided to initialize MCU integrated hardware modules as well as the MDDFS and associated binary data log file on the SD card. Therefore, it seems reasonable to designate a task to handle each of these major blocks of functionality. Although not included in the superloop software version (and not explicitly required), a task is also created to allow the initial date and time of the data log file to be updated via RS-232 inputs from the user. In summary, the RTOS-enabled version of the embedded application software consists of the following tasks:

• Hardware module initialization task, called “InitPeripheralsTask”

• SD card initialization task, called “InitSDCardTask”

• Data gathering task, called “GetDataLogTask”

• Data logging task, called “PutDataLogTask”

• RS-232 communications handling task, called “HandleUARTTask”

• Initial date/time update task, called “DateTimeTask”

Now that the tasks for implementing the necessary functionality are defined, they will need a way to communicate with each other between their separate contexts. Most of the communi- cation will simply be passing data log structures from GetDataLogTask to PutDataLogTask, which still needs some sort of buffer. For this, a message queue will be used, which is han- dled by the RTOS as a circular buffer of messages. By having each message be a pointer to the beginning of a data log structure, the circular buffer between GetDataLogTask and PutDataLogTask is automatically created and handled by the RTOS, which is convenient.

94 Communication to the HandleUARTTask that an RS-232 character has been received is also necessary. This is implemented as binary semaphore (basically a flag bit that can be sig- nalled(set) or waited(cleared)) that is signalled by the LPISR when a character is received (see Figure 4.4). Finally, communication between the HandleUARTTask and PutDataLog- Task for data logging, SD card, and Terminal enable/disable is also implemented with binary semaphores. To summarize, the following events are used for intertask communication:

• RS-232 character received binary semaphore, called “BinSemUART”

• Data logging enable/disable binary semaphore, called “BinSemPutDataLog”

• SD card output enable/disable binary semaphore, called “BinSemSDCard”

• Terminal output enable/disable binary semaphore, called “BinSemTerminal”

• Data log message queue, called “MsgQDataLog”

To implement the RTOS-enabled software, the Salvo header file, Salvo configuration header file, and Salvo library file are combined with the MDDFS header and source files, linker script, hardware module header files, and application-specific header and source files in the MPLAB IDE project structure shown in Figure 4.3. Most of the files shown in Figure 4.3 are the same as those included in the superloop project structure shown in Figure 3.2, such as all MDDFS header and source files, hardware module header files, and the modified linker script. The main.c (see Appendix B.2.1) is much different than its superloop counterpart, although it performs essentially the same function. Main.c includes the main() portion of the application where the RTOS is initialized and tasks are scheduled, as well as both LPISR and HPISR stub functions and definitions, task prototypes, and task definitions. Auxiliary.c (see Appendix B.2.2) is quite similar to its superloop counterpart, and includes user-defined application-specific function definitions. The user-defined header file main.h

95 (see Appendix B.2.3) is also quite similar to its superloop counterpart; in addition to defin- ing hardware module configuration options, it also creates definitions for event control block pointers (ECBPs). New files for the RTOS-enabled software include salvo.h, salvocfg.h, and sfc18lfa.lib. Salvo.h is a read-only file provided with the Salvo Lite distribution, and includes prototyped for RTOS services, as well as pre-set RTOS configuration options. Salvocfg.h (see Appendix B.2.4) is actually a user-defined header file used to set RTOS configuration op- tions such as which library will be used, and how many tasks, events, etc, will be used. Finally, the library file sfc18lfa.lib includes the Salvo Lite RTOS services and functional- ity for C18 using a large code model and far data type for global Salvo Lite objects[28]. Due to the limited number of TCBs compared to tasks, the order in which tasks are created and destroyed must be managed such that no more than 3 TCBs are occupied simulta- neously; additionally, task priority must be managed for proper execution of the required functionality. To accomplish this once the RTOS has been initialized, InitPeripheralsTask is created with TCB1 before all other tasks and assigned the highest priority of 0. Once the peripherals5 have been initialized InitPeripheralsTask creates GetDataLogTask with priority 1 using TCB2, and HandleUARTTask with priority 3 using TCB3. Finally, InitPeripher- alsTask destroys itself, and creates InitSDCardTask with priority 0 in its place (TCB1). Because InitSDCardTask is the highest priority eligible task at this point, it executes to completion, destroys itself, and creates PutDataLogTask with priority 2 in its place (TCB1). At this point all initialization has been completed and no RS-232 commands have been received, which will be considered the “steady state” for this application. Note that dur- ing steady state operation the three TCBs are occupied by GetDataLogTask (priority 1, TCB2), PutDataLogTask (priority 2, TCB1), and HandleUARTTask (priority 3, TCB3). If HandleUARTTask receives a command to update the initial date and time, it destroys itself and creates DateTimeTask in its place with priority 3. If the user chooses to abort or

5Used interchangeably with “integrated hardware modules”

96 follow through with the date/time update, DateTimeTask destroys itself upon completion and creates HandleUARTTask in its place; however, if the user follows trough and initial date and time are updated, then DateTimeTask also destroys PutDataLogTask and creates InitSDCardTask with priority 0 using TCB1. Possible paths to task creation, as well as TCB and priority management are described in Figure 4.4. Before analyzing the function of each task in more detail, it might be helpful to discuss the two ISRs briefly.

First of all, the HPISR in this version of the software does not contain any data gathering functions, not even gathering step counts. This is a result of the task priorities and mul- titasking offered by the RTOS. In this way, data gathering can be placed in a relatively high priority task which will be scheduled to execution as soon as it becomes eligible and any running tasks of lower priority yield to the scheduler. This implies that the HPISR should have some way of making the data gathering task eligible. This is done by delaying the data gathering task for a set number of “system ticks”; in this case a system tick is 25ms, and is provided by the high priority interrupt on Timer 3 overflow from the superloop Figure 4.3: Project structure version, combined with a call to OSTimer() in the HPISR. in MPLAB IDE for implementing RTOS-enabled embedded application OSTimer makes eligible delayed tasks whose delay period software. has expired. The HPISR is also used for incrementing the time stamp variable.

The LPISR is invoked by a low priority interrupt on character receive from the UART. When this occurs, the UART receive buffer is read to clear the interrupt flag, and the result is placed in an unsigned character variable. The LPISR then signals BinSemUART, which makes HandleUARTTask eligible after successfully waiting this binary semaphore.

97 Figure 4.4: Visual description of main.c for RTOS-enabled software showing order of task creation and function of ISRs; red highlight indicates steady state tasks during regular data logging operations.

98 While the ISRs are still important in the RTOS-enabled software version (in fact, both ISRs are used in this version compared to only the HPISR in the superloop version), they do not have to handle as much functionality due to the task priorities and multitasking features of the RTOS.

4.2.1 Initialization Tasks

Providing the initialization functionality in tasks instead of a state machine (as in the su- perloop version) allows more flexibility in when initialization can be performed during the course of program execution. However, the functionality implemented by the two initializa- tion tasks (InitPeripheralsTask and InitSDCardTask) is identical to the initialization func- tionality provided in state zero of the background state machine in the superloop version of the application software. Therefore, a detailed analysis of these tasks will not be carried out in this chapter. Instead, the possible states that InitPeripheralsTask and InitSDCardTask can exist in, and the possible paths between those states is examined.

Consider Figure 4.5, which describes how InitPeripheralsTask moves between task states. Note that all tasks are initially destroyed before they are created. In this case, InitPeripher- alsTask is always created in Main() as soon as the RTOS has been initialized on the MCU. Whenever it is created it is immediately eligible, and is not only the highest priority eligible task, but the only task. Therefore, it executes to completion, then destroys and replaces itself with InitSDCardTask. Figure 4.7 offers more detail on the execution of InitPeripher- alsTask. Note that the InitPeripherals function on line 124 of Appendix B.2.1 is the same as its counterpart in the superloop version. Now consider Figure 4.6, which describes how InitSDCardTask moves between task states. It can be created by either completion of Init- PeripheralsTask or DateTimeTask if the user chooses to update the date and time. This is reinitialize the binary data log file to include the updated initial date and time. Note that all data is lost when the date and time are updated. Unlike InitPeripheralsTask, InitSDCard-

99 Figure 4.5: Possible task states and paths between task states for InitPeripheralsTask.

100 Figure 4.6: Possible task states and paths between task states for InitSDCardTask.

Task is never the only task when it is created, however it is always the highest priority task. It is always eligible once created, so it will scheduled to run as soon as any other running tasks of lower priority yield to the scheduler. Also observe Figure 4.7, which describes the execution of InitSDCardTask. Note that it is implementing the same functionality as the SD card initialization in the superloop version. Both initialization tasks execute to comple- tion without yielding. The command OS_Replace6 automatically yields to scheduler after destroying the current task and replacing it with the new task of the given new priority.

6See lines 127 and 186 of Appendix B.2.1

101 Figure 4.7: Flow chart describing the basic execution of both peripheral hardware module and SD card initialization tasks.

102 4.2.2 Get Data Log Task

Like the initialization tasks, GetDataLogTask implements nearly identical functionality com- pared to its superloop counterpart. The main difference is GetDataLogTask places assembled data log structures into an array, and signals MsgQDataLog with a pointer to the appropriate array element instead of incrementing an array tail index. It also includes all the data gath- ering functionality from the superloop HPISR, such as gathering step inputs, and controlling the step input multiplexer. However, using a task for this purpose instead of an ISR allows the ISR period to be decreased; the drawback is that even though GetDataLogTask is of priority 1, when it becomes eligible it may have to wait briefly for any running tasks of lower priority to yield. The 25ms period between pairs of step input measurements is achieved by delaying GetDataLogTask for 1 system tick (25ms) between step input measurements. Each call to OS_Delay includes a yield. As in the superloop version, all other inputs associated with the current time stamp are gathered at the end of 4 consecutive 25ms intervals immedi- ately after step input registers are cleared to begin counting channel 1 and channel 5 steps. The possible task states and state transitions for GetDataLogTask are shown in Figure 4.8. Observing Figure 4.8, note that once created, GetDataLogTask is never destroyed. In steady state it is the highest priority task and becomes eligible as soon as OSTimer checks for timed out tasks in the HPISR after receiving a 25ms interrupt. In this way, whether data is being logged to outputs or not, GetDataLogTask is always gathering the data from inputs to fill the latest data log structures. The flow chart of GetDataLogTask execution in Figure 4.9 demonstrates how similar in function this task is to the original superloop HPISR. Again, both implement the same functionality, but in this case the HPISR only make the task eligible to gather data instead of gathering data itself.

103 Figure 4.8: Possible task states and paths between task states for GetDataLogTask.

104 Figure 4.9: Flow chart describing the basic execution of data gathering task GetDataLogTask.

105 4.2.3 Put Data Log Task

This task handle the logging of data logs to both the SD card and Terminal outputs. Again, this is very similar to the superloop functionality, but the task implements this functionality in a slightly different way. First of all, the output to both the SD card an Terminal is implemented identically to the superloop version. However, control over which outputs are enabled, and whether data logging as a whole is enabled, is implemented with binary semaphores instead of individual flag bits.

The possible states and state transitions for PutDataLogTask are shown in Figure 4.10. Figure 4.10 helps to describe the relationship between GetDataLogTask and PutDataLog- Task, and the role of MsgQDataLog. A flow chart describing the execution of PutDataLog- Task can be found in Figure 4.11.

Figure 4.10: Possible task states and paths between task states for PutDataLogTask.

106 The binary semaphore BinSemPutDataLog is used to enable and disable all data logging operations. PutDataLogTask always reads this binary semaphore7 before outputting the latest data log to the SD card or Terminal. If the binary semaphore indicates data logging enabled, the task proceeds to wait for new data to be available from MsgQDataLog. However, if the binary semaphore indicates data logging is to be disabled, the task closes the current binary data log file, displays RS-232 command options to the Terminal screen, and places itself in the stopped state. In this state the task still maintains its priority and TCB, but will execute from the beginning of the task once it is started again.

Assuming data logging is enabled, the task will proceed to wait the messages queue MsgQDataLog. This will place the task in the waiting state, and cause an automatic yield to the scheduler, allowing other tasks, such as GetDataLogTask, to run. Once the message queue has been signalled with a pointer to a new data log, PutDataLogTask will be made eligible. Once higher priority tasks are ineligible and all lower priority tasks have yielded, PutDataLogTask runs and dereferences the pointer in the message queue, and stores the result in a local data log structure.

Two binary semaphores, BinSemSDTerminal and BinSemSDCard, are used to control data logging output. The state of these semaphores, either signalled or unsignalled, deter- mines whether data logging outputs are enabled or disabled. Note that when data is to be logged to an output, the associated binary semaphore is tried8 instead of read. The same binary semaphore is signalled once data logging to the output is completed.

Note that this task is of priority 2, meaning that tasks of priority 0 (such as initialization tasks) or priority 1 (such as GetDataLogTask) should run before this task if they are eligible. This implies that PutDataLogTask must yield periodically to check for higher priority eligible

7Reading a semaphore only returns its value and does not affect the state of the semaphore. 8Trying the semaphore obtains the semaphore, thereby decrementing the semaphore value by one. The semaphore must then be signalled for it to be available again.

107 Figure 4.11: Flow chart describing the basic execution of data logging task PutDataLogTask.

108 tasks. Observing the task in Appendix B.2.19 it can be seen that yields occur before each sprintf statement when outputting data logs to the Terminal. Also observe that the function PutDataLogSD10 is called by the task to output data logs to the SD card. Note that no yields occur within this function.

4.2.4 Handle UART Task

As in the superloop version of this software, the handling of RS-232 commands is imple- mented with a switch/case statement on the received command. The difference in this case, however, is that the task for handling these commands is only eligible when BinSemUART is signalled from the LPISR upon receipt of a command. Also note that the possible com- mands and responses are the same as in the superloop case, save for one exception, which is discussed later. While the command handling switch/case statement in HandleUARTTask deals with binary semaphores instead of flag bits, the implementation is so similar to the superloop command handling that it would be redundant to analyze it in detail here. This task is given a priority of 3, so RS-232 command handling can be interrupted by either Put- DataLogTask or GetDataLogTask if HandleUARTTask yields when either of them is eligible. Conversely, HandleUARTTask will not run immediately when it becomes eligible until all other tasks become ineligible. A diagram describing possible states and paths between states for HandlUARTTask is given in Figure 4.12, while a flow chart describing task execution is given in Figure 4.13. The exception in possible commands and responses mentioned above is the additional RS-232 command “u”, which has been included in the RTOS-enabled embedded software to allow the user to update the initial date and time stamp in the binary data log file. This requires a dedicated task, which is the subject of the next section.

9Lines 339-501. 10Prototype is: void PutDataLogTask(struct datalog DataLog, FSFILE *ptrSDFile); definition on lines 454-532 of Appendix B.2.2

109 Figure 4.12: Possible task states and paths between task states for HandleUARTTask.

110 Figure 4.13: Flow chart describing the basic execution of RS-232 command handling task HandleUART- Task.

111 4.2.5 Date/Time Task

As seen in Figure 4.13, when HandleUARTTask receives the command “u” to update the initial date and time in the binary data log file, it destroys itself, and creates DateTimeTask with priority 3 in its place (TCB3). This task immediately prompts the user (via the Terminal) to input a new date in DDMMMYYYY format. So, for example, if the user wanted the new date to be July 4th, 1776, the correct input to DateTimeTask (again, via the Terminal) would be 04JUL1776. Once the new date has been saved to a character array by the task, it is echoed back to the terminal. A new time is then requested from the user in HH:MM:SS format. That is, if the user wanted the new time to be two minutes to midnight, the correct input would be 23:58:00, including colons. As with the date, the time is echoed back to the user.

Finally, the task prompts the user to either abort the date/time update (leaving the original date and time in place in the binary data file) or to update the initial date and time with the new input, thereby reinitializing the binary data log file and resetting the relative time stamp. If the user elects to update the date and time by pressing enter, the task will acknowledge the selection, reset the relative time stamp to zero, destroy PutDataLogTask, create InitSDCardTask in its place, and finally destroy itself and create HandleUARTTask in its own place. As with the other tasks, a description of possible task states and paths between states is given in Figure 4.14, while a description of task execution is given in Figure 4.15. Observing the source code that defines this task11, note that is yields quite frequently. This helps to avoid priority inversions should data gathering or data logging tasks become eligible while this task is still waiting for user input.

11Lines 683-791 in Appendix B.2.1

112 Figure 4.14: Possible task states and paths between task states for DateTimeTask.

113 Figure 4.15: Flow chart describing the basic execution of DateTimeTask.

114 Figure 4.16: Steady state multitasking between gathering data from inputs and logging data to outputs with RTOS-enabled embedded application software.

4.2.6 RTOS Version Software Structure and Function in Steady

State

As described in a previous section, once the software has initialized the Salvo Lite RTOS, integrated hardware modules, MDDFS and binary data log file on the SD card, and begins regular data logging operations, it has entered the steady state. This steady state cooper- ative multitasking is described visually in Figure 4.16. Note that in this state no RS-232 commands are received, and TCBs do not change tasks. This is the typical operation of the RTOS-enabled software, and deserves this brief description. During this operation, the priority 1 task GetDataLogTask is made eligible every 25ms by a call to OSTimer in the HPISR. As soon as the priority 2 task PutDataLogTask yields to the scheduler, it will be held as eligible or waiting (depending on what caused the yield), and GetDataLogTask will begin executing from where it left off. Note that as the highest priority task in the steady state, GetDataLogTask must delay, wait, stop, or destroy itself to yield to other tasks. Once

115 GetDataLogTask delays itself for the next 25ms, PutDataLogTask will resume if eligible. Eventually this multitasking will result in GetDataLogTask signalling MsgQDataLog with a pointer to the latest assembled data log. Once GetDataLogTask delays after signalling the message queue, PutDataLogTask will be eligible even if it was waiting on MsgQDataLog. This multitasking process between GetDataLogTask and PutDataLogTask continues indefi- nitely in the steady state; for the resulting binary data log file to be of use, the an RS-232 command must be issued to stop data logging and close the file so that is can be copied from the SD card to a PC and converted to the desired human readable text data log file.

4.3 Testing Methods

The goal of testing the RTOS-enabled version is the same as the superloop version: to verify proper functionality and successful fulfillment of requirements. The test set up for the RTOS- enabled software was identical to that used for the superloop version. The same equipment was used to provide the same signals to the same inputs on the PIC18F8722 MCU. Please refer to Chapter 3, Section 3 for details on the test set up. However, due to the different structure of the RTOS-enabled software, the MCU outputs used to measure software execution periods of interest are not the same as in the superloop case. Table 4.1 details the MCU outputs used for this purpose. Furthermore, due to the

Table 4.1: Summary of MCU outputs used to measure RTOS-enabled software execution periods of interest.

Period of Interest MCU Output HPISR RD4 Data Gathering RD7 Logging to SD Card RD5 Logging to Terminal RD6 Initialization RD2 difference in possible RTOS-enabled RS-232 commands, the following command sequence is issued to the MCU after initialization to verify correct handling of RS-232 commands:

116 1. “v” - display software version

2. “u” - request to update initial date and time

3. “16FEB2012” - update initial date to February 16th, 2012

4. “09:30:00” - update initial time to 9:30 am

5. “enter” - save updated date and time

6. “t” - enable Terminal output

7. “c” - enable SD card output

8. “g” - begin data logging

9. “s” - stop recording

10. “t” - disable terminal output

11. “g” - begin data logging

12. “s” - stop data logging

13. “c” - disable SD card output

14. “g” - begin data logging

15. “s” - stop data logging

The results of the testing outlined here are reported on and evaluated in the following section.

117 4.4 Results and Evaluation

As in the superloop case, arguably the most important result of the RTOS-enabled software is the data log itself. The original binary data log file generated by the RTOS-enabled software is shown in Figure 4.17. Note that it is not human readable when directly interpreted as

Figure 4.17: Original binary data log as produced by RTOS-enabled software.

ASCII characters. Applying the custom binary-to-ASCII conversion application in the same manner as described in Chapter 3, section 4, the resulting text data log file shown in Figure 4.19 is obtained. Figure 4.18 shows the Win32 console during the conversion. Note that the version field indicates the data log file originates from the RTOS-enabled version of the embedded application software. As in the previous case, the channels 5-8 of the data log fall off the right side of the page. Figure 4.20 shows the portion of the data log that includes data for motor channels 5-8. Observing Figure 4.19, the converted data log begins correctly with the software version, date, and time. Following that are various other information fields used during the conversion process, such as headline and data log size. Below that, the headline

118 Figure 4.18: Win32 console during conversion of data log file produced by RTOS-enabled software.

Figure 4.19: Resulting text data log from RTOS-enabled software after conversion.

119 Figure 4.20: Motor channels 5-8 of the converted data log file. row is printed, with dashes to indicate unused data log elements. As expected, the data logs begin immediately after the headline, with each beginning on a new row. Each element is comma delimited and described by its associated headline element.

The first column contains the relative time stamp, and can be seen incrementing with each new data log. The time stamp discontinuity between data log 203 and 251 is accounted for by the RS-232 command testing, and will be discussed later. Also notice that the time stamps do not begin at zero, but at 176, indicating that 17.6 seconds elapsed between when initialization completed and data logging operations began.

The next three columns of the data log file are the motor drive voltage, channel 1 phase A current sense, and channel 1 phase B current sense, respectively. Note that all three are nearly 1,023 in all data logs. This is as expected, since all three of these signals were connected to the current-limited DC power supply at +5V during testing. In fact, inspection of both Figures 4.19 and 4.20 reveals that almost all motor current sense readings indicate approximately 1,023 in all logs, and for the same reason. The only motor current sense

120 readings that do not follow this pattern are channel 4 phase B, channel 5 phases A and B, and channel 6 phase A. Both channel 4 phase B and channel 5 phase A pins are not easily accessible when using the PICtail board for SD cards, and so were not excited during this test. The input for channel 5 phase B comes from a temperature sensor mounted to the PICDEM development board, while the input for channel 6 phase A comes from a similarly mounted potentiometer. Therefore, all motor current sense readings in the data log are as expected.

The following column contains the commanded motor steps for motor channel 1. Notice that for channels 1-4 this reading is zero, compared to approximately 3,276 for channels 5-8. Due to the connection of the SD card write protect input to pin RA4, the same pin used as the step input for channels 1-4, no step input can be simulated on these channels when recording to the SD card is enabled. Furthermore, the connection of the 32 kHz RTCC oscillator on pin RC0, the same pin as the step input for channels 5-8, these motor channels always count approximately 3,276 steps in one tenth of a second. Hence, all motor step count readings are as expected.

The next four columns of the data log file contain the motor direction and limit switch status information for channel 1. For testing purposes, all channels derive this information from the same four inputs (see figure 2.13), which are all tied to +5V on the PICDEM development board. Observing that all direction and limit switch statuses indicate a high level (1) in the data log file, these results are as expected.

The next four columns, and the last four columns of each motor channel, represent extra bits of bytes taken bitwise for data logging purposes, and do not contain relevant information. In short, the resulting text data log is exactly as expected given the test set up and testing method. This indicates that the RTOS-enabled version of the embedded application software meets requirements 1,2,4,5,6,7, and 8 for logging data for all eight motor channels, as set forth in Chapter 2.

121 Correct response to RS-232 commands was also verified during testing. Consider Figure 4.21, which shows HypertTerminal output during the RS-232 command testing described in the previous section. Note that in all RS-232 command tests involving HyperTerminal, characters are not echoed locally. The software initializes and reports its version name, as well as the initial date and time to the Terminal. Note that the initial date and time are DDMMMYYYY and HH:MM:SS, respectively. This isn’t very useful on its own, but notice an additional RS-232 command listed under “RECORDING STOPPED” to update the initial date and time.

The first command issued to the MCU for this test is “v” to display the software version. Observing Figure 4.21, the MCU correctly responds with the software version name. Follow- ing that correct response, a “u” is issued to request an update to the initial date and time. To this command the MCU responds by prompting the user for a date, and provides the desired format. The date “16FEB2012” is sent to the MCU, which is waiting for 9 characters to arrive. Once all characters are received, the MCU echoes them back to the Terminal and prompts the user for a time, and provides the desired format. The time “09:30:00” is sent to the MCU, which is only waiting for 8 characters to arrive this time. Once all characters are received, the MCU echoes them back to the Terminal and prompts the user to either save the updated date and time via the enter key, or abort by pressing any other key. In this test, the save option is chosen, which is acknowledged by the MCU. Note that the “RECORD- ING STOPPED” information is again displayed to the Terminal after the update due to the re-initialization of the SD card.

Following the initial date/time update, the “t” and “c” commands are issued to the MCU to enable both Terminal and SD card data logging output, which is acknowledged by the MCU. Data logging operations are then started via the “g” command. Data logging operations continue for a time until the “s” command is issued to stop data logging, as shown in Figure 4.22. Output to the Terminal is disabled at this time by issuing a “t” command to

122 Figure 4.21: HyperTerminal output during RS-232 command testing of RTOS-enabled software. Note that the software has just finished initialization.

123 Figure 4.22: HyperTerminal output during RS-232 command testing of RTOS-enabled software. Note that data logging is stopped before disabling Terminal output.

124 the MCU, and data logging operations are restarted with a “g” command. Notice that whole data logs are no longer output to the Terminal past this point; only “X”s to indicate data logging to the SD card. Note that this pause in data logging accounts for the discontinuity observed between time stamps 203 and 251 in Figure 4.19, as the time stamp continues to increment when data logging in stopped. Data logging proceeds in this way for some time before being stopped again by issuing an “s” to the MCU, as shown in Figure 4.23. Once the SD card output is also disabled with a “c” command, data logging operations are resumed by issuing a “g” to the MCU. With both outputs now disabled, data logging is resumed by issuing the “g” command. Note that no data logs are written to the Terminal, and no “X”s indicating data logging to the SD card are output to the Terminal either. Finally, “s” is issued to stop data logging for the final time.

All RS-232 commands were handled and responded to correctly, and without any delay noticeable to the user. Therefore, the RTOS-enabled version of the embedded application software meets requirements 9,11,12, and 13 regarding RS-232 communications as set forth in Chapter 2.

To verify that the software can log commanded step counts up to 10 kHz for all 8 motor channels simultaneously, a 10 kHz, 50% duty cycle, 5V peak square wave was connected to the commanded step input for channels 1-4. Note that the commanded step input for channels 5-8 is already connected to a 32.768 kHz oscillator, which exceeds to 10 kHz minimum requirement. These step signals are the same as those supplied to test the superloop version of the embedded software, and can be found in Figure 3.25

With the 10 kHz step signal applied to the same pin used for SD card write protect, the test results can only be observed via the Terminal. The Terminal output resulting from these step input signals is shown in Figure 4.24. Note that for channels 1 counts 1004 steps, channel 2 counts 996 steps, channel 3 counts 1000 steps, and channel 4 counts 1004 steps. This is likely due to the nature of the cooperative RTOS. Consider that after counting

125 Figure 4.23: HyperTerminal output during RS-232 command testing of RTOS-enabled software. Note that data logging is stopped before disabling SD card output.

126 Figure 4.24: Terminal output resulting from 10 kHz step input to channels 1-4 and 32.768 kHz input to channels 5-8. Step counts of last data log highlighted in red for easy identification.

motor steps for 25ms, the GetDataLogTask becomes eligible; however, if another task such as PutDataLogTask is running, it must yield before GetDataLogTask can gather step counts and clear the counting registers for the next 25ms. This would cause the phenomenon seen in these step counting results. In this case the total number of steps counted would not be affected, but would simply be attributed to an incorrect motor channel. Unfortunately without access to the Salvo Lite RTOS source code, the only way to know when a task is running at any given time is to toggle and output at every yield point, which makes this explanation difficult to prove. The resulting step counts for channels 5-8 are somewhat more variable, most likely due to the lower and more variable signal amplitude of the oscillator output. Regardless, these results indicate that the RTOS-enabled version of the embedded application software meets requirement 3 regarding commanded motor step counting, as set forth in Chapter 2.

To verify that 50% processor overhead time is reserved for RS-232 communications, the execution times of several blocks of the software must be examined. Since the software

127 Figure 4.25: RTOS-enabled HPISR execution period. High level indicates HPISR is executing. execution is fairly periodic12 during data logging (i.e. data is gathered, then logged, then the process is repeated), it must be verified that data gathering and logging operations occupy no more than 50ms during each 100ms period. Note that in the RTOS-enabled software, the data gathering operations are separate from the HPISR.

The first software execution period of interest is the HPISR, which increments the relative time stamp and checks for timed out tasks. The HPISR period is shown in Figure 4.25, where a high value indicates the HPISR is executing. The HPISR in this case has a duty cycle even less than the superloop version, and has a much more regular execution time. Figure 4.26 gives detailed view of the HPISR execution time. Considering that the HPISR executes four times in every 100ms period, approximately 29.6us13 of each 100ms period is occupied by the HPISR.

The second software execution period of interest is gathering data from inputs, which is handled by GetDataLogTask. The execution of GetDataLogTask is shown in figure 4.27, where a high level indicates the task is executing. It is clear from Figure 4.27 that every

12With a period of 100ms due to 10 Hz sampling time. 134x7.4us

128 Figure 4.26: Detail of HPISR execution time showing execution time measurement.

Figure 4.27: Execution period of GetDataLogTask.

129 Figure 4.28: GetDataLogTask execution period when only step counts are gathered. fourth GetDataLogTask execution period is longer than the others, as expected. The shorter task execution period can be seen in detail in Figure 4.28, while the longer task execution period can be seen in detail in Figure 4.29. Given that the task will have three short execution times of 16.4us each and one long execution time of 752us during each 100ms period, GetDataLogTask and data gathering operations occupy approximately 801.2us of each data logging period.

The third software execution period of interest is data logging to the SD card, which is carried out by a portion of PutDataLogTask. The execution period of this portion of PutDataLogTask is shown in Figure 4.30. Notice in Figure 4.30 that one of the SD card write periods is slightly longer that the others. This was observed several times, although rarely; perhaps in about 5% of SD card write periods. The typical SD card write time is shown in detail in Figure 4.31. Note the typical execution time of data logging to the SD card is 5.16ms in every 100ms period. The longer, non-typical, execution time that makes an appearance in Figure 4.30 is shown in detail in Figure 4.32. Only the typical SD card write time is considered for the purposes of calculating the reserved processor overhead time.

130 Figure 4.29: GetDataLogTask execution period when step counts and other digital and analog data must be gathered.

Figure 4.30: Detail of data logging to SD card period.

131 Figure 4.31: Detail of typical SD card write time showing execution time measurement.

Figure 4.32: Detail of non-typical SD card write time showing execution time measurement.

132 Figure 4.33: Data logging to Terminal period.

Finally, the last execution period of interest is time required for data logging to the Terminal, which is also carried out by a portion of PutDataLogTask. The Terminal write period is shown in Figure 4.33. Notice that the Terminal write time is much more regular than the SD card write time. This is due to the fact that the RS-232 communications in this case take place only at the specified baud rate (115.2 kbaud), and no handshaking or flow control is implemented to change the rate of communication. Figure 4.34 details the Terminal write time. The Terminal write time is approximately 44.4ms during each 100ms period, which accounts for the bulk of the MCU processing time. Combining the HPISR period, SD card write period and Terminal write period, the MCU non-idle time during each 100ms period is 49.9076ms14. Therefore, it appears here that only 49.6092% processor overhead time is reserved for RS-232 communications. However, there are points during data logging to the Terminal when PutDataLogTask yields without clearing its output signalling execution, indicating that this portion of PutDataLogTask may be executing for slightly

14(3x7.4us)+752us+5.16ms+44.4ms = 50.3908ms

133 Figure 4.34: Detail of Terminal write time showing execution time measurement. less than 44.4ms during each data logging period, which may increase reserved processor overhead time by a small amount. Without access to the Salvo Lite RTOS source code, the only way to test this would be to toggle the execution output at every yield point within the task. Regardless, a reserved processor overhead time of approximately 49.6092% is not far from the somewhat arbitrary requirement of 50%.

Although no requirement on initialization time is dictated in Chapter 2, it may still be interesting to observe, and to compare to the superloop version of the embedded application software. The initialization time was measured from very beginning of embedded software execution to the time it was ready to begin logging data. This initialization time was captured by the oscilloscope and measured to be 960ms, as shown in Figure 4.35.

The total MCU program and data memory occupied by the software may also be of interest for comparing with the superloop version. This software occupies 52,223 bytes of program memory out of a possible 65,536 bytes available on the PIC18F8722 MCU. For data memory, the software requires 2,854 bytes out of a possible 3,936 bytes available on the PIC18F8722 MCU. This is represented visually in Figure 4.36.

134 Figure 4.35: RTOS-enabled version initialization time as captured by the oscilloscope. High value indicates initialization in progress.

Figure 4.36: Visual representation of RTOS-enabled embedded application software memory usage.

135 As mentioned in Chapter 2, the last dictated requirement, regarding the effect of the data logging system on motor drive circuits, can’t be readily tested without a working NIF motor drive board. However, all the test performed indicate that the embedded application software would likely not have any effect on the motor drive circuits, as its only outputs are to a PC via RS-232, SD card via SPI, and several multiplexers via GPIO. In short, the testing shows that the RTOS-enabled version of the embedded application software operates the PIC18F8722 to fulfill nearly all of the requirements dictated in Chapter 2.

136 Chapter 5

Concluding Remarks

5.1 Comparison of the Results and Evaluations

In summary, two independent pieces of embedded application software have been successfully developed. The first is implemented with a foreground/background (superloop) structure, while the second is RTOS-enabled based on the Salvo Lite RTOS. Both pieces of software successfully carry out the task of gathering data of interest and logging it to a binary data log file on an SD card, which can be converted to a text data log file for convenient interpretation and analysis in a NIF stepper motor fault diagnosis application.

While both software implementations were generally successful, each is unique and pro- vides its own set of advantages and disadvantages compared to the other.

Consider first the memory requirements of each implementation. The RTOS-enabled software requires 79.69% of the available MCU program memory and 72.51% of the available MCU data memory1. In contrast, the superloop software requires only 77.22% of the MCU program memory and 68.45% of the MCU data memory. This difference is slight, and could possibly be accounted for by the additional date/time update functionality of the RTOS-

1See Figure 4.36 for details

137 enabled software; nonetheless, it is an advantage of the superloop software that it implements the required functionality with fewer program and data memory resources.

Another feature of the two pieces of software in which the difference is slight is the ini- tialization time. In both cases, this time is approximately 1 second. The superloop software initializes in just 0.96 seconds, while the RTOS-enabled software requires 1.18 seconds, a dif- ference of approximately a fifth of a second. The difference is hardly noticeable to a user, and only occurs during initialization, so this really isn’t an important advantage or disadvantage for either piece of software.

In terms of overall functionality, the RTOS-enabled software includes the ability to update the initial date via user RS-232 command. Even though all previously logged data is lost when the date and time are updated, this is still a huge advantage over the superloop software, where the date and time can only be updated by modifying the source code, and reprogramming the MCU.

There are also some clear differences in step counting functionality between the two im- plementations. In the superloop version, step counting occurs directly in the HPISR, which immediately interrupts the current main() execution, with the other data gathering opera- tions. However, in the RTOS-enabled software, the HPISR only makes the data gathering task eligible; it must still wait for any other running task to yield before resuming execution and gathering step counts. This may account for the slightly skewed step counts shown in Figure 4.24 compared to the superloop step counts shown in Figure 3.26. The ability to gather step counts almost immediately after receiving a 25ms interrupt is an advantage of the superloop software version.

In both versions, the time required to output a data log to the Terminal2 and to the SD card3 are comparable. There is not clear advantage here for either software version,

243.8ms for superloop, 44.4ms for RTOS-enabled. 35.28ms for superloop, 5.16ms for RTOS-enabled.

138 which makes sense, as these times are dictated more by the communication method than the software driving it.

The HPISR execution time is greatly improved in the RTOS-enabled version over the superloop version, as data gathering operations have been moved to a dedicated task. An interesting observation is that the RTOS-enabled HPISR execution time combined with the shorter GetDataLogTask execution time (23.8us total) is approximately equal to the shorter superloop HPISR execution time of 25.2us. This should be expected, as very similar func- tionality is being performed in both cases. Also of interest is that the longer GetDataLogTask execution time of 752us is equal to the longer superloop HPISR execution time of 752us; again, both are implementing nearly identical functionality.

Regarding the processor overhead time, in both cases the software reserves approximately the required 50% overhead time4. However, the superloop software has the advantage of clearly meeting the 50% processor overhead time requirement. As stated in the previous chapter, it is likely that the RTOS-enabled Terminal write period appears slightly longer in Figure 4.34 due to yields to other tasks.

A big advantage of the RTOS-enabled software is the ease of code maintenance and improvement. The software functionality is contained in well-defined tasks that communicate via well-defined methods. Compared to the superloop implementation where all functionality exists either in the foreground or background, it is much easier to add new functionality or modify existing functionality.

In short, both embedded software implementations successfully perform the required data logging functions for a NIF stepper motor diagnosis application. In fact, it seems that the level of embedded software complexity required for this application allows for both superloop and RTOS-enabled implementations to be appropriate. That is to say, if much

450.0924% processor overhead time reserved in superloop case; 49.6092% processor overhead time reserved in RTOS-enabled case.

139 more complexity were required by the application, a superloop implementation may no longer be appropriate. Conversely, if much less complexity were required, an RTOS-enabled implementation may longer be appropriate.

Finally, to clarify once again, this thesis is not sponsored by, endorsed by, or otherwise affiliated with Lawrence Livermore National Laboratory or the National Ignition Facility in any way; it is inspired solely by work performed by the author5 that is summarized in a poster[2] that has been released to a public audience6.

5.2 Opportunities for Future Work

There are several opportunities for future work on this microcontroller-based system for diagnosing NIF stepper motors, both in hardware and software.

For starters, the counting of commanded motor steps could be implemented with external shift registers that increment on the rising edge of an input. These shift registers could communicate with the primary MCU via the second SPI bus, thereby allowing all motor steps to be counted instead of time multiplexing the limited step counting inputs on the primary MCU. Furthermore, this would allow data gathering operations to only take place once during each 100ms period.

Additionally, he RTOS-enabled software could be implemented with a pre-emptive schedul- ing RTOS. This would allow higher priority tasks to interrupt lower priority tasks when they become eligible. In this case, GetDataLogTask would be able to interrupt PutDataLogTask once eligible instead of waiting for PutDataLogTask to yield.

A dedicated RTCC hardware module could also be added. Either as a separate IC communicating via the SPI bus, or by choosing a different MCU with an integrated RTCC

5Performed under the auspices of the U.S. Department of Energy by Lawrence Livermore National Lab- oratory under Contract DE-AC52-07NA27344 6LLNL Information Management Review and Release Number: NIF-POST-492233

140 hardware module. This allows the date and time to be tracked independent of software execution. In this case, the date and time would be available to be attached to each data log, instead of referencing a relative time stamp to an initial date and time. The communications between the MCU and PC could be implemented with USB protocol in place of RS-232. This may speed up the communication rate between the PC and MCU, but might require the development of a custom user interface on the PC, and would likely require more MCU resources to manage. However, this would take advantage of the fact that USB cables are much easier to come by these days compared to serial RS-232 cables. Lastly, an auxiliary MCU could be added to handle binary-to-ASCII data log conversion in real time. That is, once incoming data has been assembled into a data log structure, it can be sent to the auxiliary MCU for conversion to ASCII characters while the primary MCU continues gathering data and outputting it to the Terminal and SD card. Once the auxiliary MCU has completed its conversion, the result would be passed back to the primary MCU for logging to outputs.

5.3 Broader Impacts

This work has potential applications in fields where data for intelligent diagnosis of systems is required. These applications may include logging of structural vibration and acceleration data, and logging of power system events such as faults. Also note, the NIF stepper motor diagnosis application addressed in this work falls under the category of civil infrastructure diagnosis. This is due to the fact that one the primary missions of the National Ignition Facility is to be a test bed for future energy production facilities[13], and energy production facilities are considered part of broadly-defined civil infrastructure.

141 Appendix A

Hardware Schematics

This appendix includes schematics of the relevant development hardware used for this work. These schematics are available free of charge from Microchip Technology at the following web addresses.

Data sheet, including schematics, for PICDEM PIC18 Explorer Board:

http://ww1.microchip.com/downloads/en/DeviceDoc/51721b.pdf

Data sheet, including schematics, for PICtail Daughter Board for SD/MMC Cards:

http://ww1.microchip.com/downloads/en/DeviceDoc/DS-51583b.pdf

Data sheet, including schematics, for PICkit3 debugger interface:

http://ww1.microchip.com/downloads/en/DeviceDoc/51795B.pdf

142 Figure A.1: Part one of PICDEM PIC18 Explorer Board schematic from its user manual.

143 Figure A.2: Part two of PICDEM PIC18 Explorer Board schematic from its user manual.

144 Figure A.3: PICtail Daughter Board for SD/MMC Cards schematic from its data sheet.

145 Figure A.4: Part one of PICkit3 debugger interface schematic from its user manual.

146 Figure A.5: Part two of PICkit3 debugger interface schematic from its user manual.

147 Appendix B

Application Specific Source Code

The source code presented in this appendix is that which was written specifically for this application. It does not include header files for specific hardware modules, file systems, or operating systems, as these would add tens of thousands of lines of code, most of which are unnecessary to understand the function of the embedded software for this application. For the curious reader, the rest of the project source code not included here can be found free of charge at the following web addresses.

Microchip C18 compiler and associated hardware module header files (C18 Peripheral Li- braries), as well as MCU-specific header files and linker scripts:

http://www.microchip.com/stellent/idcplg?IdcService=SS_unGET_PAGE& nodeId=1406& dDocName=en010014

Microchip Application Libraries which includes source code and header files to implement Memory Disk Drive File System:

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE& nodeId=2680& dDocName=en547784

Pumpkin Salvo Lite distribution for PICmicro devices; includes libraries, source code and header files to implement Salvo Lite RTOS:

http://www.pumpkininc.com/

Although it does not natively include any of the source code used in this application, it is worth mentioning where the MPLAB IDE can be found:

http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE& nodeId=1406& dDocName=en019469& part=SW007002

148 B.1 Application Source Code - Superloop Version

The primary superloop source code for this application consists of a main source code file, an auxiliary source code file, a custom header file, and a modified linker script.

B.1.1 MainSrcCode.c

1 //Michael Daniel : 4 February 2012 : EECE 7990 2 //Application Code Version 2.00.000 3 //Main Src Code File 4 5 //pre-processor directives: 6 #include 7 #include 8 #include "adc.h" 9 #include "timers.h" 10 #include "usart.h" 11 #include "FSIO.h" 12 #include "CustomHeader.h" 13 14 //configuration bits: 15 #pragma config OSC = HSPLL 16 #pragma config FCMEN = OFF 17 #pragma config IESO = OFF 18 #pragma config PWRT = OFF 19 #pragma config BOREN = OFF 20 #pragma config WDT = OFF 21 #pragma config CCP2MX = PORTC 22 #pragma config ECCPMX = PORTE 23 #pragma config LVP = OFF 24 #pragma config MCLRE = ON 25 #pragma config XINST = OFF 26 27 //global variables: 28 FSFILE *pSDFile; 29 int initialize = 0; 30 int detect = 0; 31 int close = 0; 32 int bytes_written = 0; 33 int buf_length = 0; 34 int DataLogSize = 0; 35 int8 DataLogElementSize = 0; 36 char NewFile[13] = "DATALOG0.bin"; 37 char WriteArg[2] = "w"; 38 char ReadArg[2] = "r"; 39 char AppendArg[2] = "a"; 40 long FilePosition = 0; 41 char SD_TX_Buf[11] = {0}; 42 enum state main_ns = zero; 43 enum state hp_isr_ns = zero; 44 struct datalog CurrentDataLog[CircBuffSize]; 45 struct flags UserDefinedFlags; 46 unsigned int j = 0; 47 unsigned int k = 0; 48 unsigned int DescriptorSize = 0; 49 unsigned int HeadlineSize = 0; 50 unsigned int MediaRollOver = 0; 51 unsigned int StepCount[8] = {0}; 52 unsigned char Version[14] = "LOOP 2.00.000"; 53 unsigned char InitialDateStamp[10] = "4FEB2012"; //initial date stamp, format DDMMMYYYY

149 54 unsigned char InitialTimeStamp[9] = "11:58:00"; //initial time stamp, format HH:MM:SS 55 unsigned char UART_CMD_Buf[19] = {0}; 56 unsigned char UART_TX_Buf[11] = {0}; 57 unsigned char UART_RX_Char = 0; 58 unsigned long TimeStamp = 0; 59 60 //ISR stub functions: 61 void HP_ISR(void); 62 #pragma code hp_vector = 0x0008 63 void hp_interrupt(void) 64 { 65 _asm GOTO HP_ISR _endasm 66 } 67 #pragma code 68 #pragma interrupt HP_ISR 69 70 void LP_ISR(void); 71 #pragma code lp_vector = 0x0018 72 void lp_interrupt(void) 73 { 74 _asm GOTO LP_ISR _endasm 75 } 76 #pragma code 77 #pragma interruptlow LP_ISR 78 79 //ISRs: 80 void HP_ISR(void) 81 { 82 PORTDbits.RD4 = ~PORTDbits.RD4; //invert RD4 (set) 83 if(PIR2bits.TMR3IF==1) 84 { 85 PIR2bits.TMR3IF = 0b0; //clear TMR0 interrupt flag 86 WriteTimer3(TMR3_LOAD); //load timer3 87 switch(hp_isr_ns) 88 { 89 case zero: 90 StepCount[hp_isr_ns] = ReadTimer0()<<2; 91 StepCount[hp_isr_ns+4] = ReadTimer1()<<2; 92 PORTGbits.RG3 = 0b1; 93 WriteTimer0(0x0000); 94 WriteTimer1(0x0000); 95 hp_isr_ns = one; 96 break; 97 case one: 98 StepCount[hp_isr_ns] = ReadTimer0()<<2; 99 StepCount[hp_isr_ns+4] = ReadTimer1()<<2; 100 PORTGbits.RG3 = 0b0; 101 PORTGbits.RG4 = 0b1; 102 WriteTimer0(0x0000); 103 WriteTimer1(0x0000); 104 hp_isr_ns = two; 105 break; 106 case two: 107 StepCount[hp_isr_ns] = ReadTimer0()<<2; 108 StepCount[hp_isr_ns+4] = ReadTimer1()<<2; 109 PORTGbits.RG3 = 0b1; 110 WriteTimer0(0x0000); 111 WriteTimer1(0x0000); 112 hp_isr_ns = three; 113 break; 114 case three: 115 StepCount[hp_isr_ns] = ReadTimer0()<<2; 116 StepCount[hp_isr_ns+4] = ReadTimer1()<<2; 117 PORTGbits.RG3 = 0b0; 118 PORTGbits.RG4 = 0b0;

150 119 WriteTimer0(0x0000); 120 WriteTimer1(0x0000); 121 TimeStamp++; 122 if(((j+1)!=k)&&((k+CircBuffSize-1)!=j)) //check circular buffer conditions 123 { 124 if((CurrentDataLog[0].ch[0].limits.RecordData!=0)&&(CurrentDataLog[0].ch[0].limits.GetDataLogLockOut==0)) 125 { 126 GetDataLog(); //assemble data into record 127 j++; 128 if(j>=CircBuffSize) 129 { 130 j=0; 131 } 132 } 133 } 134 else 135 { 136 UART_CRLF; 137 puts1USART(UART_TX_Buf); 138 buf_length = sprintf(UART_CMD_Buf,"DATALOG BUFF FULL"); 139 puts1USART(UART_CMD_Buf); 140 UART_CRLF; 141 puts1USART(UART_TX_Buf); 142 } 143 hp_isr_ns = zero; 144 break; 145 default: 146 PORTDbits.RD0 = 0b1; 147 hp_isr_ns = zero; 148 break; 149 } 150 } 151 PORTDbits.RD4 = ~PORTDbits.RD4; //invert RD4 (clear) 152 } 153 154 void LP_ISR(void) 155 { 156 Nop(); 157 } 158 159 //Main: 160 void main(void) 161 { 162 TRISDbits.TRISD7 = 0b0; //RD7 is output: measure initialization period 163 PORTDbits.RD7 = 0b1; //set RD7 output 164 switch(main_ns) 165 { 166 case zero: 167 UserDefinedFlags.ElementSizeOn = 0b0; //clear element size on bit 168 UserDefinedFlags.ElementCountOn = 0b0; //clear element count bit 169 INTCONbits.GIEH = 0b0; //globally disable high priority interrupts 170 INTCONbits.GIEL = 0b0; //globally disable low priority interrupts 171 InitPeripherals(); //initialize hardware peripherals 172 InitDataLog(); //initialize record structure 173 DataLogSize = sizeof(CurrentDataLog[0]); //determine the size of a data log 174 detect = MDD_MediaDetect(); //detect SD card 175 if(detect!=1) //if media not detected 176 { 177 UserDefinedFlags.SDCardOutputOn = 0b0; //disable SD card output 178 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 179 UART_CRLF; 180 puts1USART(UART_TX_Buf); 181 buf_length = sprintf(UART_CMD_Buf,"CARD NOT DETECTED"); 182 puts1USART(UART_CMD_Buf); 183 UART_CRLF;

151 184 puts1USART(UART_TX_Buf); 185 PORTDbits.RD0 = 0b1; //illuminate error led 186 } 187 else 188 { 189 initialize = FSInit(); //initialize FAT file system 190 if(initialize!=1) //if media not initialized 191 { 192 UserDefinedFlags.SDCardOutputOn = 0b0; //disable SD card output 193 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 194 UART_CRLF; 195 puts1USART(UART_TX_Buf); 196 buf_length = sprintf(UART_CMD_Buf,"FS NOT INITIALIZED"); 197 puts1USART(UART_CMD_Buf); 198 UART_CRLF; 199 puts1USART(UART_TX_Buf); 200 PORTDbits.RD0 = 0b1; //illuminate error led 201 } 202 else 203 { 204 pSDFile = FSfopen(NewFile,WriteArg); //open new *.bin file 205 if(pSDFile==0) //if file not opened 206 { 207 UserDefinedFlags.SDCardOutputOn = 0b0; //disable SD card output 208 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 209 UART_CRLF; 210 puts1USART(UART_TX_Buf); 211 buf_length = sprintf(UART_CMD_Buf,"FILE NOT OPENED"); 212 puts1USART(UART_CMD_Buf); 213 UART_CRLF; 214 puts1USART(UART_TX_Buf); 215 PORTDbits.RD0 = 0b1; //illuminate error led 216 } 217 else 218 { 219 HeadlineSize = PutHeadlineSDASCII(pSDFile); 220 FSfseek(pSDFile,0,SEEK_SET); 221 PutVersionSD(Version,pSDFile); 222 PutInitialDateStampSD(InitialDateStamp,pSDFile); 223 PutInitialTimeStampSD(InitialTimeStamp,pSDFile); 224 PutRollOverSD(MediaRollOver,pSDFile); 225 PutHeadlineSizeSD(HeadlineSize,pSDFile); 226 PutDataLogSizeSD(DataLogSize,pSDFile); 227 UserDefinedFlags.ElementCountOn = 0b1; 228 DescriptorSize = PutDataLogSD(pSDFile); 229 UserDefinedFlags.ElementCountOn = 0b0; 230 FSfseek(pSDFile,-DataLogSize,SEEK_CUR); 231 PutDescriptorSizeSD(DescriptorSize,pSDFile); 232 UserDefinedFlags.ElementSizeOn = 0b1; 233 PutDataLogSD(pSDFile); 234 UserDefinedFlags.ElementSizeOn = 0b0; 235 PutHeadlineSDASCII(pSDFile); 236 close = FSfclose(pSDFile); //close SD card file 237 if(close==-1) //if file not closed 238 { 239 UART_CRLF; 240 puts1USART(UART_TX_Buf); 241 buf_length = sprintf(UART_CMD_Buf,"FILE NOT CLOSED"); 242 puts1USART(UART_CMD_Buf); 243 UART_CRLF; 244 puts1USART(UART_TX_Buf); 245 PORTDbits.RD0 = 0b1; //illuminate error led 246 } 247 UART_CRLF; 248 puts1USART(UART_TX_Buf);

152 249 buf_length = sprintf(UART_CMD_Buf,"RECORDING STOPPED"); //transmit "STOPPED" to terminal 250 puts1USART(UART_CMD_Buf); 251 UART_CRLF; 252 puts1USART(UART_TX_Buf); 253 buf_length = sprintf(UART_CMD_Buf,"s = recording Stop"); 254 puts1USART(UART_CMD_Buf); 255 UART_CRLF; 256 puts1USART(UART_TX_Buf); 257 buf_length = sprintf(UART_CMD_Buf,"g = recording Go"); 258 puts1USART(UART_CMD_Buf); 259 UART_CRLF; 260 puts1USART(UART_TX_Buf); 261 buf_length = sprintf(UART_CMD_Buf,"t = toggle Termina"); 262 puts1USART(UART_CMD_Buf); 263 buf_length = sprintf(UART_CMD_Buf,"l"); 264 puts1USART(UART_CMD_Buf); 265 UART_CRLF; 266 puts1USART(UART_TX_Buf); 267 buf_length = sprintf(UART_CMD_Buf,"c = toggle sd Card"); 268 puts1USART(UART_CMD_Buf); 269 UART_CRLF; 270 puts1USART(UART_TX_Buf); 271 buf_length = sprintf(UART_CMD_Buf,"v = display Versi"); 272 puts1USART(UART_CMD_Buf); 273 buf_length = sprintf(UART_CMD_Buf,"on"); 274 puts1USART(UART_CMD_Buf); 275 UART_CRLF; 276 puts1USART(UART_TX_Buf); 277 buf_length = sprintf(UART_CMD_Buf,"d = Display date/"); 278 puts1USART(UART_CMD_Buf); 279 buf_length = sprintf(UART_CMD_Buf,"time"); 280 puts1USART(UART_CMD_Buf); 281 UART_CRLF; 282 puts1USART(UART_TX_Buf); 283 } 284 } 285 } 286 T3CONbits.TMR3ON = 0b1; //start running timer... 287 T1CONbits.TMR1ON = 0b1; //enable step counter... 288 T0CONbits.TMR0ON = 0b1; //enable step counter... 289 INTCONbits.GIEH = 0b1; //globally enable high priority interrupts 290 INTCONbits.GIEL = 0b0; //globally disable low priority interrupts 291 main_ns = one; //update next state 292 PORTDbits.RD7 = 0b0; //clear RD7 output 293 break; 294 295 case one: 296 if((CurrentDataLog[0].ch[0].limits.RecordData!=0)&&(CurrentDataLog[0].ch[0].limits.PutDataLogLockOut==0)) 297 { 298 while(j==k); //check circular buffer conditions 299 if(UserDefinedFlags.SDCardOutputOn!=0) //if SD card output is enabled 300 { 301 PORTDbits.RD5 = ~PORTDbits.RD5; //invert RD5 (set) 302 PutDataLogSD(pSDFile); //write record to SD card 303 FilePosition = FSftell(pSDFile); //returns the file pointer position 304 if(FilePosition==-1) //file position fails 305 { 306 close = FSfclose(pSDFile); //close file 307 CurrentDataLog[0].ch[0].limits.RecordOutput=0b0; //switch to terminal 308 PORTDbits.RD0 = 0b1; //illuminate error led 309 } 310 if(FilePosition>=0x773593FF) //if file size reaches (2GB-1byte) limit 311 { 312 FSrewind(pSDFile); //reset file pointer to beginning of file 313 MediaRollOver++;

153 314 PutVersionSD(Version,pSDFile); 315 PutInitialDateStampSD(InitialDateStamp,pSDFile); 316 PutInitialTimeStampSD(InitialTimeStamp,pSDFile); 317 PutRollOverSD(MediaRollOver,pSDFile); 318 PutHeadlineSizeSD(HeadlineSize,pSDFile); 319 PutDataLogSizeSD(DataLogSize,pSDFile); 320 PutDescriptorSizeSD(DescriptorSize,pSDFile); 321 UserDefinedFlags.ElementSizeOn = 0b1; 322 PutDataLogSD(pSDFile); 323 UserDefinedFlags.ElementSizeOn = 0b0; 324 PutHeadlineSDASCII(pSDFile); 325 } 326 UART_CRLF; 327 puts1USART(UART_TX_Buf); 328 putc1USART(0x58); //transmit "X" over UART 329 UART_CRLF; 330 puts1USART(UART_TX_Buf); 331 PORTDbits.RD5 = ~PORTDbits.RD5; //invert RD5 (clear) 332 } 333 if(UserDefinedFlags.TerminalOutputOn!=0) //if terminal output is enabled 334 { 335 PORTDbits.RD6 = ~PORTDbits.RD6; //invert RD6 (set) 336 PutDataLogUARTASCII(); //write record to Terminal 337 PORTDbits.RD6 = ~PORTDbits.RD6; //invert RD6 (clear) 338 } 339 k++; 340 if(k>=CircBuffSize) 341 { 342 k=0; 343 } 344 } 345 if(DataRdy1USART()) //if data in in UART RX buffer 346 { 347 UART_RX_Char = getc1USART(); //read the UART RX buffer 348 RCREG1 = 0x00; //clear the UART RX buffer 349 PIR1bits.RC1IF = 0b0; //clear UART RX interrupt flag 350 switch(UART_RX_Char) 351 { 352 case 0x73: //received "s", stop recording 353 if((UserDefinedFlags.SDCardOutputOn!=0)&&(CurrentDataLog[0].ch[0].limits.RecordData!=0)) 354 { 355 close = FSfclose(pSDFile); //close SD card file 356 if(close==-1) //if file not closed 357 { 358 UserDefinedFlags.SDCardOutputOn = 0b0; //disable SD card output 359 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 360 UART_CRLF; 361 puts1USART(UART_TX_Buf); 362 buf_length = sprintf(UART_CMD_Buf,"FILE NOT CLOSED"); 363 puts1USART(UART_CMD_Buf); 364 UART_CRLF; 365 puts1USART(UART_TX_Buf); 366 PORTDbits.RD0 = 0b1; //illuminate error led 367 } 368 } 369 CurrentDataLog[0].ch[0].limits.RecordData = 0b0; //stop recording data 370 InitDataLog(); //clear circular buffer 371 j=0; //reset circular buffer write index 372 k=0; //reset circular buffer read index 373 UART_CRLF; 374 puts1USART(UART_TX_Buf); 375 buf_length = sprintf(UART_CMD_Buf,"RECORDING STOPPED"); //transmit "STOPPED" to terminal 376 puts1USART(UART_CMD_Buf); 377 UART_CRLF; 378 puts1USART(UART_TX_Buf);

154 379 buf_length = sprintf(UART_CMD_Buf,"s = recording Stop"); 380 puts1USART(UART_CMD_Buf); 381 UART_CRLF; 382 puts1USART(UART_TX_Buf); 383 buf_length = sprintf(UART_CMD_Buf,"g = recording Go"); 384 puts1USART(UART_CMD_Buf); 385 UART_CRLF; 386 puts1USART(UART_TX_Buf); 387 buf_length = sprintf(UART_CMD_Buf,"t = toggle Termina"); 388 puts1USART(UART_CMD_Buf); 389 buf_length = sprintf(UART_CMD_Buf,"l"); 390 puts1USART(UART_CMD_Buf); 391 UART_CRLF; 392 puts1USART(UART_TX_Buf); 393 buf_length = sprintf(UART_CMD_Buf,"c = toggle sd Card"); 394 puts1USART(UART_CMD_Buf); 395 UART_CRLF; 396 puts1USART(UART_TX_Buf); 397 buf_length = sprintf(UART_CMD_Buf,"v = display Versi"); 398 puts1USART(UART_CMD_Buf); 399 buf_length = sprintf(UART_CMD_Buf,"on"); 400 puts1USART(UART_CMD_Buf); 401 UART_CRLF; 402 puts1USART(UART_TX_Buf); 403 buf_length = sprintf(UART_CMD_Buf,"d = Display date/"); 404 puts1USART(UART_CMD_Buf); 405 buf_length = sprintf(UART_CMD_Buf,"time"); 406 puts1USART(UART_CMD_Buf); 407 UART_CRLF; 408 puts1USART(UART_TX_Buf); 409 break; 410 411 case 0x67: //received "g", start recording 412 if((UserDefinedFlags.SDCardOutputOn!=0)&&(CurrentDataLog[0].ch[0].limits.RecordData==0)) 413 { 414 if((detect&&initialize)!=0) 415 { 416 pSDFile = FSfopen(NewFile,AppendArg); //open *.bin file in append mode 417 if(pSDFile==0) //if file not opened 418 { 419 close = FSfclose(pSDFile); //close file 420 UserDefinedFlags.SDCardOutputOn = 0b0; //disable SD card output 421 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 422 UART_CRLF; 423 puts1USART(UART_TX_Buf); 424 buf_length = sprintf(UART_CMD_Buf,"FILE NOT OPENED"); 425 puts1USART(UART_CMD_Buf); 426 UART_CRLF; 427 puts1USART(UART_TX_Buf); 428 PORTDbits.RD0 = 0b1; //illuminate error led 429 } 430 } 431 else 432 { 433 detect = MDD_MediaDetect(); //detect SD card 434 if(detect!=1) //if media not detected 435 { 436 UserDefinedFlags.SDCardOutputOn = 0b0; //disable sd card output 437 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 438 UART_CRLF; 439 puts1USART(UART_TX_Buf); 440 buf_length = sprintf(UART_CMD_Buf,"CARD NOT DETECTED"); 441 puts1USART(UART_CMD_Buf); 442 UART_CRLF; 443 puts1USART(UART_TX_Buf);

155 444 PORTDbits.RD0 = 0b1; //illuminate error led 445 } 446 else 447 { 448 initialize = FSInit(); //initialize FAT file system 449 if(initialize!=1) //if media not initialized 450 { 451 UserDefinedFlags.SDCardOutputOn = 0b0; //disable sd card output 452 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 453 UART_CRLF; 454 puts1USART(UART_TX_Buf); 455 buf_length = sprintf(UART_CMD_Buf,"FS NOT INITIALIZED"); 456 puts1USART(UART_CMD_Buf); 457 UART_CRLF; 458 puts1USART(UART_TX_Buf); 459 PORTDbits.RD0 = 0b1; //illuminate error led 460 } 461 else 462 { 463 pSDFile = FSfopen(NewFile,WriteArg); //open new *.bin file 464 if(pSDFile==0) //if file not opened 465 { 466 UserDefinedFlags.SDCardOutputOn = 0b0; //disable sd card output 467 UserDefinedFlags.TerminalOutputOn = 0b1; //switch to terminal 468 UART_CRLF; 469 puts1USART(UART_TX_Buf); 470 buf_length = sprintf(UART_CMD_Buf,"FILE NOT OPENED"); 471 puts1USART(UART_CMD_Buf); 472 UART_CRLF; 473 puts1USART(UART_TX_Buf); 474 PORTDbits.RD0 = 0b1; //illuminate error led 475 } 476 else 477 { 478 HeadlineSize = PutHeadlineSDASCII(pSDFile); 479 FSfseek(pSDFile,0,SEEK_SET); 480 PutVersionSD(Version,pSDFile); 481 PutInitialDateStampSD(InitialDateStamp,pSDFile); 482 PutInitialTimeStampSD(InitialTimeStamp,pSDFile); 483 PutRollOverSD(MediaRollOver,pSDFile); 484 PutHeadlineSizeSD(HeadlineSize,pSDFile); 485 PutDataLogSizeSD(DataLogSize,pSDFile); 486 UserDefinedFlags.ElementCountOn = 0b1; 487 DescriptorSize = PutDataLogSD(pSDFile); 488 UserDefinedFlags.ElementCountOn = 0b0; 489 FSfseek(pSDFile,-DataLogSize,SEEK_CUR); 490 PutDescriptorSizeSD(DescriptorSize,pSDFile); 491 UserDefinedFlags.ElementSizeOn = 0b1; 492 PutDataLogSD(pSDFile); 493 UserDefinedFlags.ElementSizeOn = 0b0; 494 PutHeadlineSDASCII(pSDFile); 495 } 496 } 497 } 498 } 499 } 500 CurrentDataLog[0].ch[0].limits.RecordData = 0b1; //start recording data 501 UART_CRLF; 502 puts1USART(UART_TX_Buf); 503 buf_length = sprintf(UART_CMD_Buf,"RECORDING STARTED"); //transmit "STARTED" to terminal 504 puts1USART(UART_CMD_Buf); 505 UART_CRLF; 506 puts1USART(UART_TX_Buf); 507 break; 508

156 509 case 0x63: //received "c", output to card 510 if(CurrentDataLog[0].ch[0].limits.RecordData==0) //if recording is stopped 511 { 512 UserDefinedFlags.SDCardOutputOn = ~UserDefinedFlags.SDCardOutputOn; //toggle sd card output 513 if(UserDefinedFlags.SDCardOutputOn==0) 514 { 515 UART_CRLF; 516 puts1USART(UART_TX_Buf); 517 buf_length = sprintf(UART_CMD_Buf,"SD CARD DISABLED"); //transmit "SD DISABLED" to terminal 518 puts1USART(UART_CMD_Buf); 519 UART_CRLF; 520 puts1USART(UART_TX_Buf); 521 } 522 else 523 { 524 UART_CRLF; 525 puts1USART(UART_TX_Buf); 526 buf_length = sprintf(UART_CMD_Buf,"SD CARD ENABLED"); //transmit "SD ENABLED" to terminal 527 puts1USART(UART_CMD_Buf); 528 UART_CRLF; 529 puts1USART(UART_TX_Buf); 530 } 531 } 532 break; 533 534 case 0x74: //received "t", output to terminal 535 if(CurrentDataLog[0].ch[0].limits.RecordData==0) //if recording is stopped 536 { 537 UserDefinedFlags.TerminalOutputOn = ~UserDefinedFlags.TerminalOutputOn; //toggle output to terminal 538 if(UserDefinedFlags.TerminalOutputOn==0) 539 { 540 UART_CRLF; 541 puts1USART(UART_TX_Buf); 542 buf_length = sprintf(UART_CMD_Buf,"TERMINAL DISABLED"); //transmit "TERMINAL DISABLED" to terminal 543 puts1USART(UART_CMD_Buf); 544 UART_CRLF; 545 puts1USART(UART_TX_Buf); 546 } 547 else 548 { 549 UART_CRLF; 550 puts1USART(UART_TX_Buf); 551 buf_length = sprintf(UART_CMD_Buf,"TERMINAL ENABLED"); //transmit "TERMINAL ENABLED" to terminal 552 puts1USART(UART_CMD_Buf); 553 UART_CRLF; 554 puts1USART(UART_TX_Buf); 555 } 556 } 557 break; 558 559 case 0x76: //receive "v", display version 560 if(CurrentDataLog[0].ch[0].limits.RecordData==0) 561 { 562 UART_CRLF; 563 puts1USART(UART_TX_Buf); 564 puts1USART(Version); 565 UART_CRLF; 566 puts1USART(UART_TX_Buf); 567 } 568 break; 569 570 case 0x64: //receive "d", display date/time 571 if(CurrentDataLog[0].ch[0].limits.RecordData==0) 572 { 573 UART_CRLF;

157 574 puts1USART(UART_TX_Buf); 575 puts1USART(InitialDateStamp); 576 UART_CRLF; 577 puts1USART(UART_TX_Buf); 578 puts1USART(InitialTimeStamp); 579 UART_CRLF; 580 puts1USART(UART_TX_Buf); 581 } 582 break; 583 default: 584 break; 585 } 586 } 587 main_ns = one; 588 break; 589 590 default: 591 PORTDbits.RD0 = 0b1; 592 main_ns = one; 593 break; 594 } 595 }

B.1.2 AuxSrcCode.c

1 //Michael Daniel : 4 February 2012 : EECE 7990 2 //Application Code Version 2.00.000 3 //Aux Src Code file 4 5 //pre-processor directives: 6 #include 7 #include 8 #include "adc.h" 9 #include "timers.h" 10 #include "usart.h" 11 #include "FSIO.h" 12 #include "CustomHeader.h" 13 14 //function definitions: 15 void InitPeripherals(void) 16 { 17 extern struct datalog CurrentDataLog[]; 18 extern struct flags UserDefinedFlags; 19 extern int buf_length; 20 extern unsigned char UART_RX_Char; 21 extern unsigned char UART_TX_Buf[]; 22 extern unsigned char Version[]; 23 extern unsigned char InitialDateStamp[]; 24 extern unsigned char InitialTimeStamp[]; 25 26 PIR2bits.TMR3IF = 0b0; //clear timer3 interrupt flag 27 PIE2bits.TMR3IE = 0b0; //disable timer3 interrupt 28 PIR1bits.RC1IF = 0b0; //clear UART RX interrupt flag 29 PIE1bits.RC1IE = 0b0; //disable UART1 RX interrupt 30 PIR1bits.ADIF = 0b0; //clear ADC interrupt flag 31 PIE1bits.ADIE = 0b0; //disable ADC interrupt 32 RCONbits.IPEN = 0b1; //enable interrupt priority levels 33 34 TRISFbits.TRISF0 = 0b1; //analog input 35 TRISFbits.TRISF1 = 0b1; //analog input 36 TRISFbits.TRISF2 = 0b1; //analog input 37 TRISFbits.TRISF3 = 0b1; //analog input 38 TRISFbits.TRISF4 = 0b1; //analog input

158 39 TRISFbits.TRISF5 = 0b1; //analog input 40 TRISFbits.TRISF6 = 0b1; //analog input 41 TRISHbits.TRISH4 = 0b1; //analog input 42 TRISHbits.TRISH5 = 0b1; //analog input 43 TRISHbits.TRISH6 = 0b1; //analog input 44 TRISHbits.TRISH7 = 0b1; //analog input 45 TRISAbits.TRISA0 = 0b1; //analog input 46 TRISAbits.TRISA1 = 0b1; //analog input 47 TRISAbits.TRISA2 = 0b1; //analog input 48 TRISAbits.TRISA3 = 0b1; //analog input 49 TRISAbits.TRISA5 = 0b1; //analog input 50 51 TRISHbits.RH2 = 0b1; //digital input 52 TRISHbits.RH3 = 0b1; //digital input 53 TRISEbits.RE1 = 0b1; //digital input 54 TRISEbits.RE0 = 0b1; //digital input 55 56 TRISDbits.TRISD0 = 0b0; //RD0 is output: Error LED 57 TRISGbits.TRISG4 = 0b0; //RG4 is output: digital MUX control 58 TRISGbits.TRISG3 = 0b0; //RG3 is output: digital MUX control 59 TRISFbits.TRISF3 = 0b0; //RF7 is output: analog MUX control 60 61 TRISDbits.TRISD6 = 0b0; //RD6 is output: measure terminal write period 62 TRISDbits.TRISD5 = 0b0; //RD5 is output: measure SD card write period 63 TRISDbits.TRISD4 = 0b0; //RD4 is output: measure HP ISR period 64 65 PORTDbits.RD6 = 0b0; //clear RD6 66 PORTDbits.RD5 = 0b0; //clear RD5 67 PORTDbits.RD4 = 0b0; //clear RD4 68 69 PORTGbits.RG4 = 0b0; //clear MUX control bits 70 PORTGbits.RG3 = 0b0; 71 PORTFbits.RF7 = 0b0; 72 PORTDbits.RD0 = 0b0; //clear error LED bit 73 74 CurrentDataLog[0].ch[0].limits.RecordData = 0b0; //set RecordData bit, start recording data 75 UserDefinedFlags.SDCardOutputOn = 0b0; //set sd card bit, record to SD media 76 UserDefinedFlags.TerminalOutputOn = 0b0; //do not print to terminal by default 77 78 CloseADC(); //close ADC if previously open 79 OpenADC(ADC_CONFIG,ADC_CONFIG2,ADC_PORTCONFIG); //configure and enable ADC 80 ADCON0bits.GO = 0b0; //stop ADC from converting 81 82 Close1USART(); //close usart if previously open 83 baud1USART(USART1_BAUDCONFIG); //configure usart baud rate 84 Open1USART(USART1_CONFIG,USART1_SPBRG); //configure and enable usart 85 UART_RX_Char = getc1USART(); //read the UART RX buffer 86 UART_RX_Char = 0x00; //clear the UART RX character 87 RCREG1 = 0x00; //clear the UART RX buffer 88 PIR1bits.RC1IF = 0b0; //clear UART RX interrupt flag 89 90 CloseTimer0(); 91 OpenTimer0(TMR0_CONFIG); 92 T0CONbits.TMR0ON = 0b0; 93 WriteTimer0(0x0000); 94 95 CloseTimer1(); 96 OpenTimer1(TMR1_CONFIG); 97 T1CONbits.TMR1ON = 0b0; 98 WriteTimer1(0x0000); 99 100 CloseTimer3(); //close timer3 if previously open 101 OpenTimer3(TMR3_CONFIG); //open timer3 with pre-configured settings 102 T3CONbits.TMR3ON = 0b0; //disable timer until loaded 103 WriteTimer3(TMR3_LOAD); //load timer3

159 104 105 RCONbits.IPEN = 0b1; //enable interrupt priority levels 106 IPR2bits.TMR3IP = 0b1; //timer0 interrupt is high priority 107 PIE2bits.TMR3IE = 0b1; //enable timer0 interrupt 108 109 UART_CRLF; 110 puts1USART(UART_TX_Buf); 111 puts1USART(Version); 112 UART_CRLF; 113 puts1USART(UART_TX_Buf); 114 puts1USART(InitialDateStamp); 115 UART_CRLF; 116 puts1USART(UART_TX_Buf); 117 puts1USART(InitialTimeStamp); 118 UART_CRLF; 119 puts1USART(UART_TX_Buf); 120 } 121 122 void InitDataLog(void) 123 { 124 unsigned int m = 0; 125 unsigned int i = 0; 126 extern struct datalog CurrentDataLog[]; 127 128 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b1; //main src code locked out from getting records 129 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b1; //main src code locked out from putting records 130 for(m=0;m<=4;m++) 131 { 132 CurrentDataLog[m].TimeStamp = 0; 133 CurrentDataLog[m].Drive24V = 0; 134 for(i=0;i<=7;i++) 135 { 136 CurrentDataLog[m].ch[i].PhaseA = 0; 137 CurrentDataLog[m].ch[i].PhaseB = 0; 138 CurrentDataLog[m].ch[i].StepCount = 0; 139 CurrentDataLog[m].ch[i].limits.Direction = 0; 140 CurrentDataLog[m].ch[i].limits.CWlimit = 0; 141 CurrentDataLog[m].ch[i].limits.CCWlimit = 0; 142 CurrentDataLog[m].ch[i].limits.HOMElimit = 0; 143 } 144 i=0; 145 } 146 m=0; 147 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b0; //main src code released to get records 148 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b0; //main src code released to put records 149 } 150 151 void GetDataLog(void) 152 { 153 unsigned int i = 0; 154 extern unsigned int j; 155 extern unsigned int StepCount[]; 156 extern struct datalog CurrentDataLog[]; 157 extern unsigned long TimeStamp; 158 159 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b1; //main src code locked out from getting records 160 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b1; //main src code locked out from putting records 161 162 CurrentDataLog[j].TimeStamp = TimeStamp; 163 164 PORTFbits.RF7 = 0b0; //external analog mux control 165 SetChanADC(ADC_CH11); //set ADC to channel zero, internal mux control 166 ConvertADC(); //begin ADC conversion 167 while(BusyADC()); //wait here while ADC is busy 168 CurrentDataLog[j].Drive24V = ReadADC(); //record ADC result

160 169 170 PORTFbits.RF7 = 0b1; //external analog mux control 171 SetChanADC(ADC_CH11); 172 ConvertADC(); 173 while(BusyADC()){}; 174 CurrentDataLog[j].ch[0].PhaseA = ReadADC(); 175 SetChanADC(ADC_CH10); 176 ConvertADC(); 177 while(BusyADC()){}; 178 CurrentDataLog[j].ch[0].PhaseB = ReadADC(); 179 SetChanADC(ADC_CH9); 180 ConvertADC(); 181 while(BusyADC()){}; 182 CurrentDataLog[j].ch[1].PhaseA = ReadADC(); 183 SetChanADC(ADC_CH8); 184 ConvertADC(); 185 while(BusyADC()){}; 186 CurrentDataLog[j].ch[1].PhaseB = ReadADC(); 187 SetChanADC(ADC_CH7); 188 ConvertADC(); 189 while(BusyADC()){}; 190 CurrentDataLog[j].ch[2].PhaseA = ReadADC(); 191 SetChanADC(ADC_CH15); 192 ConvertADC(); 193 while(BusyADC()){}; 194 CurrentDataLog[j].ch[2].PhaseB = ReadADC(); 195 SetChanADC(ADC_CH5); 196 ConvertADC(); 197 while(BusyADC()){}; 198 CurrentDataLog[j].ch[3].PhaseA = ReadADC(); 199 SetChanADC(ADC_CH3); 200 ConvertADC(); 201 while(BusyADC()){}; 202 CurrentDataLog[j].ch[3].PhaseB = ReadADC(); 203 SetChanADC(ADC_CH2); 204 ConvertADC(); 205 while(BusyADC()){}; 206 CurrentDataLog[j].ch[4].PhaseA = ReadADC(); 207 SetChanADC(ADC_CH1); 208 ConvertADC(); 209 while(BusyADC()){}; 210 CurrentDataLog[j].ch[4].PhaseB = ReadADC(); 211 SetChanADC(ADC_CH0); 212 ConvertADC(); 213 while(BusyADC()){}; 214 CurrentDataLog[j].ch[5].PhaseA = ReadADC(); 215 SetChanADC(ADC_CH4); 216 ConvertADC(); 217 while(BusyADC()){}; 218 CurrentDataLog[j].ch[5].PhaseB = ReadADC(); 219 SetChanADC(ADC_CH14); 220 ConvertADC(); 221 while(BusyADC()){}; 222 CurrentDataLog[j].ch[6].PhaseA = ReadADC(); 223 SetChanADC(ADC_CH13); 224 ConvertADC(); 225 while(BusyADC()){}; 226 CurrentDataLog[j].ch[6].PhaseB = ReadADC(); 227 SetChanADC(ADC_CH12); 228 ConvertADC(); 229 while(BusyADC()){}; 230 CurrentDataLog[j].ch[7].PhaseA = ReadADC(); 231 SetChanADC(ADC_CH6); 232 ConvertADC(); 233 while(BusyADC()){};

161 234 CurrentDataLog[j].ch[7].PhaseB = ReadADC(); 235 236 for(i=0;i<=7;i++) //gather digital data for all eight channels 237 { 238 CurrentDataLog[j].ch[i].StepCount = StepCount[i]; //reading for step count 239 240 CurrentDataLog[j].ch[i].limits.Direction = PORTHbits.RH2; //direction and limits 241 CurrentDataLog[j].ch[i].limits.CWlimit = PORTHbits.RH3; 242 CurrentDataLog[j].ch[i].limits.CCWlimit = PORTEbits.RE1; 243 CurrentDataLog[j].ch[i].limits.HOMElimit = PORTEbits.RE0; 244 } 245 i=0; //clear i so it can be used by other functions 246 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b0; //main src code released to get records 247 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b0; //main src code released to put records 248 } 249 250 unsigned int PutHeadlineSDASCII(FSFILE *pSDFile) 251 { 252 unsigned int i = 0; 253 unsigned int HeadlineSize = 0; 254 extern char SD_TX_Buf[]; 255 extern struct datalog CurrentDataLog[]; 256 extern int buf_length; 257 extern int bytes_written; 258 extern unsigned char UART_TX_Buf[]; 259 extern unsigned char UART_CMD_Buf[]; 260 extern int buf_length; 261 262 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b1; //main src code locked out from getting records 263 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b1; //main src code locked out from putting records 264 265 HeadlineSize = 0; 266 ClrSDTXBuf(); 267 buf_length = sprintf(SD_TX_Buf,"TIMESTMP,"); 268 bytes_written = FSfwrite((void*)SD_TX_Buf,1,9,pSDFile); 269 HeadlineSize += bytes_written; 270 if(bytes_written!=9) 271 { 272 UART_CRLF; 273 puts1USART(UART_TX_Buf); 274 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 275 puts1USART(UART_CMD_Buf); 276 UART_CRLF; 277 puts1USART(UART_TX_Buf); 278 PORTDbits.RD0 = 0b1; //illuminate error LED 279 } 280 buf_length = sprintf(SD_TX_Buf,"24VDRIVE,"); 281 bytes_written = FSfwrite((void*)SD_TX_Buf,1,9,pSDFile); 282 HeadlineSize += bytes_written; 283 if(bytes_written!=9) 284 { 285 UART_CRLF; 286 puts1USART(UART_TX_Buf); 287 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 288 puts1USART(UART_CMD_Buf); 289 UART_CRLF; 290 puts1USART(UART_TX_Buf); 291 PORTDbits.RD0 = 0b1; //illuminate error LED 292 } 293 294 for(i=0;i<=7;i++) 295 { 296 buf_length = sprintf(SD_TX_Buf,"CH%dPHA,",i+1); 297 bytes_written = FSfwrite((void*)SD_TX_Buf,1,7,pSDFile); 298 HeadlineSize += bytes_written;

162 299 if(bytes_written!=7) 300 { 301 UART_CRLF; 302 puts1USART(UART_TX_Buf); 303 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 304 puts1USART(UART_CMD_Buf); 305 UART_CRLF; 306 puts1USART(UART_TX_Buf); 307 PORTDbits.RD0 = 0b1; //illuminate error LED 308 } 309 buf_length = sprintf(SD_TX_Buf,"CH%dPHB,",i+1); 310 bytes_written = FSfwrite((void*)SD_TX_Buf,1,7,pSDFile); 311 HeadlineSize += bytes_written; 312 if(bytes_written!=7) 313 { 314 UART_CRLF; 315 puts1USART(UART_TX_Buf); 316 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 317 puts1USART(UART_CMD_Buf); 318 UART_CRLF; 319 puts1USART(UART_TX_Buf); 320 PORTDbits.RD0 = 0b1; //illuminate error LED 321 } 322 buf_length = sprintf(SD_TX_Buf,"CH%dSTEP,",i+1); 323 bytes_written = FSfwrite((void*)SD_TX_Buf,1,8,pSDFile); 324 HeadlineSize += bytes_written; 325 if(bytes_written!=8) 326 { 327 UART_CRLF; 328 puts1USART(UART_TX_Buf); 329 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 330 puts1USART(UART_CMD_Buf); 331 UART_CRLF; 332 puts1USART(UART_TX_Buf); 333 PORTDbits.RD0 = 0b1; //illuminate error LED 334 } 335 buf_length = sprintf(SD_TX_Buf,"CH%dDIR,",i+1); 336 bytes_written = FSfwrite((void*)SD_TX_Buf,1,7,pSDFile); 337 HeadlineSize += bytes_written; 338 if(bytes_written!=7) 339 { 340 UART_CRLF; 341 puts1USART(UART_TX_Buf); 342 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 343 puts1USART(UART_CMD_Buf); 344 UART_CRLF; 345 puts1USART(UART_TX_Buf); 346 PORTDbits.RD0 = 0b1; //illuminate error LED 347 } 348 buf_length = sprintf(SD_TX_Buf,"CH%dCW,",i+1); 349 bytes_written = FSfwrite((void*)SD_TX_Buf,1,6,pSDFile); 350 HeadlineSize += bytes_written; 351 if(bytes_written!=6) 352 { 353 UART_CRLF; 354 puts1USART(UART_TX_Buf); 355 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 356 puts1USART(UART_CMD_Buf); 357 UART_CRLF; 358 puts1USART(UART_TX_Buf); 359 PORTDbits.RD0 = 0b1; //illuminate error LED 360 } 361 buf_length = sprintf(SD_TX_Buf,"CH%dCCW,",i+1); 362 bytes_written = FSfwrite((void*)SD_TX_Buf,1,7,pSDFile); 363 HeadlineSize += bytes_written;

163 364 if(bytes_written!=7) 365 { 366 UART_CRLF; 367 puts1USART(UART_TX_Buf); 368 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 369 puts1USART(UART_CMD_Buf); 370 UART_CRLF; 371 puts1USART(UART_TX_Buf); 372 PORTDbits.RD0 = 0b1; //illuminate error LED 373 } 374 buf_length = sprintf(SD_TX_Buf,"CH%dHOME,",i+1); 375 bytes_written = FSfwrite((void*)SD_TX_Buf,1,8,pSDFile); 376 HeadlineSize += bytes_written; 377 if(bytes_written!=8) 378 { 379 UART_CRLF; 380 puts1USART(UART_TX_Buf); 381 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 382 puts1USART(UART_CMD_Buf); 383 UART_CRLF; 384 puts1USART(UART_TX_Buf); 385 PORTDbits.RD0 = 0b1; //illuminate error LED 386 } 387 buf_length = sprintf(SD_TX_Buf,"-,"); 388 bytes_written = FSfwrite((void*)SD_TX_Buf,1,2,pSDFile); 389 HeadlineSize += bytes_written; 390 if(bytes_written!=2) 391 { 392 UART_CRLF; 393 puts1USART(UART_TX_Buf); 394 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 395 puts1USART(UART_CMD_Buf); 396 UART_CRLF; 397 puts1USART(UART_TX_Buf); 398 PORTDbits.RD0 = 0b1; //illuminate error LED 399 } 400 buf_length = sprintf(SD_TX_Buf,"-,"); 401 bytes_written = FSfwrite((void*)SD_TX_Buf,1,2,pSDFile); 402 HeadlineSize += bytes_written; 403 if(bytes_written!=2) 404 { 405 UART_CRLF; 406 puts1USART(UART_TX_Buf); 407 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 408 puts1USART(UART_CMD_Buf); 409 UART_CRLF; 410 puts1USART(UART_TX_Buf); 411 PORTDbits.RD0 = 0b1; //illuminate error LED 412 } 413 buf_length = sprintf(SD_TX_Buf,"-,"); 414 bytes_written = FSfwrite((void*)SD_TX_Buf,1,2,pSDFile); 415 HeadlineSize += bytes_written; 416 if(bytes_written!=2) 417 { 418 UART_CRLF; 419 puts1USART(UART_TX_Buf); 420 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 421 puts1USART(UART_CMD_Buf); 422 UART_CRLF; 423 puts1USART(UART_TX_Buf); 424 PORTDbits.RD0 = 0b1; //illuminate error LED 425 } 426 buf_length = sprintf(SD_TX_Buf,"-,"); 427 bytes_written = FSfwrite((void*)SD_TX_Buf,1,2,pSDFile); 428 HeadlineSize += bytes_written;

164 429 if(bytes_written!=2) 430 { 431 UART_CRLF; 432 puts1USART(UART_TX_Buf); 433 buf_length = sprintf(UART_CMD_Buf,"HEADLINE MISWRITE"); 434 puts1USART(UART_CMD_Buf); 435 UART_CRLF; 436 puts1USART(UART_TX_Buf); 437 PORTDbits.RD0 = 0b1; //illuminate error LED 438 } 439 } 440 i=0; //clear i so it can be used by other functions 441 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b0; //main src code released to get records 442 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b0; //main src code released to put records 443 return HeadlineSize; 444 } 445 446 void PutVersionSD(unsigned char *Version,FSFILE *pSDFile) 447 { 448 extern int bytes_written; 449 extern int buf_length; 450 extern unsigned char UART_TX_Buf[]; 451 extern unsigned char UART_CMD_Buf[]; 452 bytes_written = FSfwrite(Version,1,13,pSDFile); 453 if(bytes_written!=13) 454 { 455 UART_CRLF; 456 puts1USART(UART_TX_Buf); 457 buf_length = sprintf(UART_CMD_Buf,"VERSION MISWRITE"); 458 puts1USART(UART_CMD_Buf); 459 UART_CRLF; 460 puts1USART(UART_TX_Buf); 461 PORTDbits.RD0 = 0b1; 462 } 463 } 464 465 void PutInitialDateStampSD(unsigned char *DateStamp,FSFILE *pSDFile) 466 { 467 extern int bytes_written; 468 extern int buf_length; 469 extern unsigned char UART_TX_Buf[]; 470 extern unsigned char UART_CMD_Buf[]; 471 bytes_written = FSfwrite(DateStamp,1,9,pSDFile); 472 if(bytes_written!=9) 473 { 474 UART_CRLF; 475 puts1USART(UART_TX_Buf); 476 buf_length = sprintf(UART_CMD_Buf,"DATE STMP MISWRITE"); 477 puts1USART(UART_CMD_Buf); 478 UART_CRLF; 479 puts1USART(UART_TX_Buf); 480 PORTDbits.RD0 = 0b1; 481 } 482 } 483 484 void PutInitialTimeStampSD(unsigned char *TimeStamp,FSFILE *pSDFile) 485 { 486 extern int bytes_written; 487 extern int buf_length; 488 extern unsigned char UART_TX_Buf[]; 489 extern unsigned char UART_CMD_Buf[]; 490 bytes_written = FSfwrite(TimeStamp,1,8,pSDFile); 491 if(bytes_written!=8) 492 { 493 UART_CRLF;

165 494 puts1USART(UART_TX_Buf); 495 buf_length = sprintf(UART_CMD_Buf,"TIME STMP MISWRITE"); 496 puts1USART(UART_CMD_Buf); 497 UART_CRLF; 498 puts1USART(UART_TX_Buf); 499 PORTDbits.RD0 = 0b1; 500 } 501 } 502 503 void PutRollOverSD(unsigned int RollOver,FSFILE *pSDFile) 504 { 505 extern int bytes_written; 506 extern int buf_length; 507 extern unsigned char UART_TX_Buf[]; 508 extern unsigned char UART_CMD_Buf[]; 509 bytes_written = FSfwrite(&RollOver,1,2,pSDFile); 510 if(bytes_written!=2) 511 { 512 UART_CRLF; 513 puts1USART(UART_TX_Buf); 514 buf_length = sprintf(UART_CMD_Buf,"ROLL OVER MISWRITE"); 515 puts1USART(UART_CMD_Buf); 516 UART_CRLF; 517 puts1USART(UART_TX_Buf); 518 PORTDbits.RD0 = 0b1; //illuminate error LED 519 } 520 } 521 522 void PutHeadlineSizeSD(unsigned int HeadlineSZ,FSFILE *pSDFile) 523 { 524 extern int bytes_written; 525 extern int buf_length; 526 extern unsigned char UART_TX_Buf[]; 527 extern unsigned char UART_CMD_Buf[]; 528 bytes_written = FSfwrite(&HeadlineSZ,1,2,pSDFile); 529 if(bytes_written!=2) 530 { 531 UART_CRLF; 532 puts1USART(UART_TX_Buf); 533 buf_length = sprintf(UART_CMD_Buf,"HLINE SZ MISWRITE"); 534 puts1USART(UART_CMD_Buf); 535 UART_CRLF; 536 puts1USART(UART_TX_Buf); 537 PORTDbits.RD0 = 0b1; 538 } 539 } 540 541 void PutDataLogSizeSD(unsigned int DataLogSZ,FSFILE *pSDFile) 542 { 543 extern int bytes_written; 544 extern int buf_length; 545 extern unsigned char UART_TX_Buf[]; 546 extern unsigned char UART_CMD_Buf[]; 547 bytes_written = FSfwrite(&DataLogSZ,1,2,pSDFile); 548 if(bytes_written!=2) 549 { 550 UART_CRLF; 551 puts1USART(UART_TX_Buf); 552 buf_length = sprintf(UART_CMD_Buf,"LOG SIZE MISWRITE"); 553 puts1USART(UART_CMD_Buf); 554 UART_CRLF; 555 puts1USART(UART_TX_Buf); 556 PORTDbits.RD0 = 0b1; //illuminate error LED 557 } 558 }

166 559 560 void PutDescriptorSizeSD(unsigned int DescriptorSZ,FSFILE *pSDFile) 561 { 562 extern int bytes_written; 563 extern int buf_length; 564 extern unsigned char UART_TX_Buf[]; 565 extern unsigned char UART_CMD_Buf[]; 566 bytes_written = FSfwrite(&DescriptorSZ,1,2,pSDFile); 567 if(bytes_written!=2) 568 { 569 UART_CRLF; 570 puts1USART(UART_TX_Buf); 571 buf_length = sprintf(UART_CMD_Buf,"DESC SIZE MISWRITE"); 572 puts1USART(UART_CMD_Buf); 573 UART_CRLF; 574 puts1USART(UART_TX_Buf); 575 PORTDbits.RD0 = 0b1; 576 } 577 } 578 579 unsigned int PutDataLogSD(FSFILE *pSDFile) 580 { 581 int DataLogElementSize; 582 unsigned int i = 0; 583 unsigned int DataLogElements = 0; 584 extern int bytes_written; 585 extern struct datalog CurrentDataLog[]; 586 extern struct flags UserDefinedFlags; 587 extern unsigned int k; 588 extern unsigned char UART_TX_Buf[]; 589 extern unsigned char UART_CMD_Buf[]; 590 extern int buf_length; 591 592 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b1; //main src code locked out from getting records 593 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b1; //main src code locked out from putting records 594 595 bytes_written = FSfwrite(&CurrentDataLog[k].TimeStamp,1,4,pSDFile); 596 if(bytes_written!=4) 597 { 598 UART_CRLF; 599 puts1USART(UART_TX_Buf); 600 buf_length = sprintf(UART_CMD_Buf,"DATALOG MISWRITE"); 601 puts1USART(UART_CMD_Buf); 602 UART_CRLF; 603 puts1USART(UART_TX_Buf); 604 PORTDbits.RD0 = 0b1; //illuminate error LED 605 } 606 if(UserDefinedFlags.ElementCountOn!=0) 607 { 608 DataLogElements++; 609 } 610 if(UserDefinedFlags.ElementSizeOn!=0) 611 { 612 DataLogElementSize = (int8)bytes_written; 613 FSfseek(pSDFile,-bytes_written,SEEK_CUR); 614 bytes_written = FSfwrite(&DataLogElementSize,1,1,pSDFile); 615 if(bytes_written!=1) 616 { 617 UART_CRLF; 618 puts1USART(UART_TX_Buf); 619 buf_length = sprintf(UART_CMD_Buf,"DESC MISWRITE"); 620 puts1USART(UART_CMD_Buf); 621 UART_CRLF; 622 puts1USART(UART_TX_Buf); 623 PORTDbits.RD0 = 0b1;

167 624 } 625 } 626 bytes_written = FSfwrite(&CurrentDataLog[k].Drive24V,1,2,pSDFile); 627 if(bytes_written!=2) 628 { 629 UART_CRLF; 630 puts1USART(UART_TX_Buf); 631 buf_length = sprintf(UART_CMD_Buf,"DATALOG MISWRITE"); 632 puts1USART(UART_CMD_Buf); 633 UART_CRLF; 634 puts1USART(UART_TX_Buf); 635 PORTDbits.RD0 = 0b1; //illuminate error LED 636 } 637 if(UserDefinedFlags.ElementCountOn!=0) 638 { 639 DataLogElements++; 640 } 641 if(UserDefinedFlags.ElementSizeOn!=0) 642 { 643 DataLogElementSize = (int8)bytes_written; 644 FSfseek(pSDFile,-bytes_written,SEEK_CUR); 645 bytes_written = FSfwrite(&DataLogElementSize,1,1,pSDFile); 646 if(bytes_written!=1) 647 { 648 UART_CRLF; 649 puts1USART(UART_TX_Buf); 650 buf_length = sprintf(UART_CMD_Buf,"DESC MISWRITE"); 651 puts1USART(UART_CMD_Buf); 652 UART_CRLF; 653 puts1USART(UART_TX_Buf); 654 PORTDbits.RD0 = 0b1; 655 } 656 } 657 for(i=0;i<=7;i++) 658 { 659 bytes_written = FSfwrite(&CurrentDataLog[k].ch[i].PhaseA,1,2,pSDFile); 660 if(bytes_written!=2) 661 { 662 UART_CRLF; 663 puts1USART(UART_TX_Buf); 664 buf_length = sprintf(UART_CMD_Buf,"DATALOG MISWRITE"); 665 puts1USART(UART_CMD_Buf); 666 UART_CRLF; 667 puts1USART(UART_TX_Buf); 668 PORTDbits.RD0 = 0b1; //illuminate error LED 669 } 670 if(UserDefinedFlags.ElementCountOn!=0) 671 { 672 DataLogElements++; 673 } 674 if(UserDefinedFlags.ElementSizeOn!=0) 675 { 676 DataLogElementSize = (int8)bytes_written; 677 FSfseek(pSDFile,-bytes_written,SEEK_CUR); 678 bytes_written = FSfwrite(&DataLogElementSize,1,1,pSDFile); 679 if(bytes_written!=1) 680 { 681 UART_CRLF; 682 puts1USART(UART_TX_Buf); 683 buf_length = sprintf(UART_CMD_Buf,"DESC MISWRITE"); 684 puts1USART(UART_CMD_Buf); 685 UART_CRLF; 686 puts1USART(UART_TX_Buf); 687 PORTDbits.RD0 = 0b1; 688 }

168 689 } 690 bytes_written = FSfwrite(&CurrentDataLog[k].ch[i].PhaseB,1,2,pSDFile); 691 if(bytes_written!=2) 692 { 693 UART_CRLF; 694 puts1USART(UART_TX_Buf); 695 buf_length = sprintf(UART_CMD_Buf,"DATALOG MISWRITE"); 696 puts1USART(UART_CMD_Buf); 697 UART_CRLF; 698 puts1USART(UART_TX_Buf); 699 PORTDbits.RD0 = 0b1; //illuminate error LED 700 } 701 if(UserDefinedFlags.ElementCountOn!=0) 702 { 703 DataLogElements++; 704 } 705 if(UserDefinedFlags.ElementSizeOn!=0) 706 { 707 DataLogElementSize = (int8)bytes_written; 708 FSfseek(pSDFile,-bytes_written,SEEK_CUR); 709 bytes_written = FSfwrite(&DataLogElementSize,1,1,pSDFile); 710 if(bytes_written!=1) 711 { 712 UART_CRLF; 713 puts1USART(UART_TX_Buf); 714 buf_length = sprintf(UART_CMD_Buf,"DESC MISWRITE"); 715 puts1USART(UART_CMD_Buf); 716 UART_CRLF; 717 puts1USART(UART_TX_Buf); 718 PORTDbits.RD0 = 0b1; 719 } 720 } 721 bytes_written = FSfwrite(&CurrentDataLog[k].ch[i].StepCount,1,2,pSDFile); 722 if(bytes_written!=2) 723 { 724 UART_CRLF; 725 puts1USART(UART_TX_Buf); 726 buf_length = sprintf(UART_CMD_Buf,"DATALOG MISWRITE"); 727 puts1USART(UART_CMD_Buf); 728 UART_CRLF; 729 puts1USART(UART_TX_Buf); 730 PORTDbits.RD0 = 0b1; //illuminate error LED 731 } 732 if(UserDefinedFlags.ElementCountOn!=0) 733 { 734 DataLogElements++; 735 } 736 if(UserDefinedFlags.ElementSizeOn!=0) 737 { 738 DataLogElementSize = (int8)bytes_written; 739 FSfseek(pSDFile,-bytes_written,SEEK_CUR); 740 bytes_written = FSfwrite(&DataLogElementSize,1,1,pSDFile); 741 if(bytes_written!=1) 742 { 743 UART_CRLF; 744 puts1USART(UART_TX_Buf); 745 buf_length = sprintf(UART_CMD_Buf,"DESC MISWRITE"); 746 puts1USART(UART_CMD_Buf); 747 UART_CRLF; 748 puts1USART(UART_TX_Buf); 749 PORTDbits.RD0 = 0b1; 750 } 751 } 752 bytes_written = FSfwrite(&CurrentDataLog[k].ch[i].limits,1,1,pSDFile); 753 if(bytes_written!=1)

169 754 { 755 UART_CRLF; 756 puts1USART(UART_TX_Buf); 757 buf_length = sprintf(UART_CMD_Buf,"DATALOG MISWRITE"); 758 puts1USART(UART_CMD_Buf); 759 UART_CRLF; 760 puts1USART(UART_TX_Buf); 761 PORTDbits.RD0 = 0b1; //illuminate error LED 762 } 763 if(UserDefinedFlags.ElementCountOn!=0) 764 { 765 DataLogElements++; 766 } 767 if(UserDefinedFlags.ElementSizeOn!=0) 768 { 769 DataLogElementSize = 0; //0 element size indicates this byte is taken bitwise 770 FSfseek(pSDFile,-bytes_written,SEEK_CUR); 771 bytes_written = FSfwrite(&DataLogElementSize,1,1,pSDFile); 772 if(bytes_written!=1) 773 { 774 UART_CRLF; 775 puts1USART(UART_TX_Buf); 776 buf_length = sprintf(UART_CMD_Buf,"DESC MISWRITE"); 777 puts1USART(UART_CMD_Buf); 778 UART_CRLF; 779 puts1USART(UART_TX_Buf); 780 PORTDbits.RD0 = 0b1; 781 } 782 } 783 } 784 i=0; //clear i so it can be used by other functions 785 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b0; //main src code released to get records 786 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b0; //main src code released to put records 787 return DataLogElements; 788 } 789 790 void PutDataLogUARTASCII(void) 791 { 792 unsigned int i = 0; 793 extern struct datalog CurrentDataLog[]; 794 extern unsigned int k; 795 extern int buf_length; 796 extern unsigned char UART_TX_Buf; 797 798 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b1; //main src code locked out from getting records 799 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b1; //main src code locked out from putting records 800 UART_CRLF; 801 puts1USART(UART_TX_Buf); 802 buf_length = sprintf(UART_TX_Buf,"%ld,",CurrentDataLog[k].TimeStamp); 803 puts1USART(UART_TX_Buf); 804 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].Drive24V); 805 puts1USART(UART_TX_Buf); 806 807 for(i=0;i<=7;i++) 808 { 809 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].PhaseA); 810 puts1USART(UART_TX_Buf); 811 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].PhaseB); 812 puts1USART(UART_TX_Buf); 813 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].StepCount); 814 puts1USART(UART_TX_Buf); 815 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].limits.Direction); 816 puts1USART(UART_TX_Buf); 817 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].limits.CWlimit); 818 puts1USART(UART_TX_Buf);

170 819 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].limits.CCWlimit); 820 puts1USART(UART_TX_Buf); 821 buf_length = sprintf(UART_TX_Buf,"%d,",CurrentDataLog[k].ch[i].limits.HOMElimit); 822 puts1USART(UART_TX_Buf); 823 } 824 buf_length = sprintf(UART_TX_Buf,"\r\n"); 825 puts1USART(UART_TX_Buf); 826 i=0; //clear i so it can be used by other functions 827 CurrentDataLog[0].ch[0].limits.GetDataLogLockOut = 0b0; //main src code released to get records 828 CurrentDataLog[0].ch[0].limits.PutDataLogLockOut = 0b0; //main src code released to put records 829 } 830 831 void ClrSDTXBuf(void) 832 { 833 unsigned int n = 0; 834 extern char SD_TX_Buf[]; 835 836 for(n=0;n<=11;n++) 837 { 838 SD_TX_Buf[n] = 0x00; 839 } 840 n=0; //clear n so it can be used by other functions 841 }

B.1.3 CustomHeader.h

1 #ifndef CUSTOMHEADER_H 2 #define CUSTOMHEADER_H 3 //Michael Daniel : 4 February 2012 : EECE 7990 4 //Application Code Version 2.00.000 5 //Custom Header File 6 7 //definitions: 8 #define FALSE 0 9 #define UART_CRLF buf_length = sprintf(UART_TX_Buf,"\r\n") 10 #define UART_TX puts1USART(UART_TX_BUF) 11 #define ADC_CONFIG (ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_4_TAD) 12 #define ADC_CONFIG2 (ADC_CH0 & ADC_INT_OFF & ADC_REF_VDD_VSS) 13 #define ADC_PORTCONFIG (ADC_15ANA) 14 #define USART1_CONFIG (USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_CONT_RX\ 15 & USART_BRGH_HIGH & USART_ADDEN_OFF) 16 #define USART1_SPBRG 86 17 #define USART1_BAUDCONFIG (BAUD_16_BIT_RATE & BAUD_WAKEUP_OFF & BAUD_AUTO_OFF) 18 #define TMR0_CONFIG (TIMER_INT_OFF & T0_16BIT & T0_SOURCE_EXT & T0_EDGE_RISE & T0_PS_1_1) 19 #define TMR1_CONFIG (TIMER_INT_OFF & T1_16BIT_RW & T1_SOURCE_EXT & T1_PS_1_1 & T1_SYNC_EXT_OFF) 20 #define TMR3_CONFIG (TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8) 21 #define TMR3_LOAD 0x85EF 22 #define CircBuffSize 6 23 24 //structures, unions, typedefs, enumerations, etc: 25 enum state 26 { 27 zero = 0, 28 one = 1, 29 two = 2, 30 three = 3 31 }; 32 33 struct flags 34 { 35 unsigned char ElementSizeOn : 1; 36 unsigned char ElementCountOn: 1; 37 unsigned char SDCardOutputOn : 1;

171 38 unsigned char TerminalOutputOn : 1; 39 unsigned char : 4; 40 }; 41 42 struct bitfield 43 { 44 unsigned char Direction : 1; 45 unsigned char CWlimit : 1; 46 unsigned char CCWlimit : 1; 47 unsigned char HOMElimit : 1; 48 unsigned char RecordData : 1; 49 unsigned char RecordOutput : 1; 50 unsigned char GetDataLogLockOut : 1; 51 unsigned char PutDataLogLockOut : 1; 52 }; 53 54 struct channel 55 { 56 unsigned int PhaseA; 57 unsigned int PhaseB; 58 unsigned int StepCount; 59 struct bitfield limits; 60 }; 61 62 struct datalog 63 { 64 unsigned long TimeStamp; 65 unsigned int Drive24V; 66 struct channel ch[8]; 67 }; 68 69 typedef unsigned char int8; 70 71 //function prototypes: 72 void InitPeripherals(void); 73 void InitDataLog(void); 74 void GetDataLog(void); 75 unsigned int PutHeadlineSDASCII(FSFILE *pSDFile); 76 void PutVersionSD(unsigned char *Version,FSFILE *pSDFile); 77 void PutInitialDateStampSD(unsigned char *DateStamp,FSFILE *pSDFile); 78 void PutInitialTimeStampSD(unsigned char *TimeStamp,FSFILE *pSDFile); 79 void PutRollOverSD(unsigned int RollOver,FSFILE *pSDFile); 80 void PutHeadlineSizeSD(unsigned int HeadlineSZ,FSFILE *pSDFile); 81 void PutDataLogSizeSD(unsigned int DataLogSZ,FSFILE *pSDFile); 82 void PutDescriptorSizeSD(unsigned int DescriptorSZ,FSFILE *pSDFile); 83 unsigned int PutDataLogSD(FSFILE *pSDFile); 84 void PutDataLogUARTASCII(void); 85 void ClrSDTXBuf(void); 86 87 #endif

B.1.4 18f8722_MDDFS.lkr

1 // File: 18f8722_MDDFS.lkr 2 // Generic linker script for the PIC18F8722 processor 3 // Modified by Michael Daniel 7 October 2011: 4 // Includes 0x200 bytes RAM reserved for data buffer 5 // Includes 0x200 bytes RAM reserved for FAT buffer 6 // Stack size increased to 0x200 from 0x100 bytes 7 8 #DEFINE _CODEEND _DEBUGCODESTART - 1 9 #DEFINE _CEND _CODEEND + _DEBUGCODELEN 10 #DEFINE _DATAEND _DEBUGDATASTART - 1

172 11 #DEFINE _DEND _DATAEND + _DEBUGDATALEN 12 13 LIBPATH . 14 15 #IFDEF _CRUNTIME 16 #IFDEF _EXTENDEDMODE 17 FILES c018i_e.o 18 FILES clib_e.lib 19 FILES p18f8722_e.lib 20 21 #ELSE 22 FILES c018i.o 23 FILES clib.lib 24 FILES p18f8722.lib 25 #FI 26 27 #FI 28 29 #IFDEF _DEBUGCODESTART 30 CODEPAGE NAME=page START=0x0 END=_CODEEND 31 CODEPAGE NAME=debug START=_DEBUGCODESTART END=_CEND PROTECTED 32 #ELSE 33 CODEPAGE NAME=page START=0x0 END=0x1FFFF 34 #FI 35 36 CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED 37 CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED 38 CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED 39 CODEPAGE NAME=eedata START=0xF00000 END=0xF003FF PROTECTED 40 41 #IFDEF _EXTENDEDMODE 42 DATABANK NAME=gpre START=0x0 END=0x5F 43 #ELSE 44 ACCESSBANK NAME=accessram START=0x0 END=0x5F 45 #FI 46 47 DATABANK NAME=gpr0 START=0x60 END=0xFF 48 DATABANK NAME=gpr1 START=0x100 END=0x1FF 49 DATABANK NAME=gpr2 START=0x200 END=0x2FF 50 DATABANK NAME=gpr3 START=0x300 END=0x3FF 51 DATABANK NAME=gpr4 START=0x400 END=0x4FF 52 DATABANK NAME=gpr5 START=0x500 END=0x5FF 53 DATABANK NAME=gpr6 START=0x600 END=0x6FF 54 DATABANK NAME=buffer1 START=0x700 END=0x8FF PROTECTED 55 DATABANK NAME=buffer2 START=0x900 END=0xAFF PROTECTED 56 //DATABANK NAME=gpr7 START=0x700 END=0x7FF 57 //DATABANK NAME=gpr8 START=0x800 END=0x8FF 58 //DATABANK NAME=gpr9 START=0x900 END=0x9FF 59 //DATABANK NAME=gpr10 START=0xA00 END=0xAFF 60 //DATABANK NAME=gpr11 START=0xB00 END=0xBFF 61 //DATABANK NAME=gpr12 START=0xC00 END=0xCFF 62 DATABANK NAME=gpr13 START=0xB00 END=0xCFF 63 64 #IFDEF _DEBUGDATASTART 65 DATABANK NAME=gpr14 START=0xD00 END=_DATAEND 66 DATABANK NAME=dbgspr START=_DEBUGDATASTART END=_DEND PROTECTED 67 #ELSE //no debug 68 DATABANK NAME=gpr14 START=0xD00 END=0xEFF 69 #FI 70 71 DATABANK NAME=gpr15 START=0xF00 END=0xF5F 72 ACCESSBANK NAME=accesssfr START=0xF60 END=0xFFF PROTECTED 73 74 #IFDEF _CRUNTIME 75 SECTION NAME=CONFIG ROM=config

173 76 SECTION NAME=dataBuffer RAM=buffer1 77 SECTION NAME=FATBuffer RAM=buffer2 78 #IFDEF _DEBUGDATASTART 79 STACK SIZE=0x200 RAM=gpr13 80 #ELSE 81 STACK SIZE=0x200 RAM=gpr14 82 #FI 83 #FI

B.2 Application Source Code - RTOS Version

Like the superloop version, the RTOS version of the application code is composed of a main source code file, an auxiliary source code file, a custom header file. The linker script is the same as that used in the superloop version, so it is not listed again in this section. The RTOS version also includes a configuration header file used to define the settings to be used by Salvo Lite.

B.2.1 main.c

1 //Michael Daniel : 4 February 2012 : EECE 7990 2 //Salvo Application Code Version 2.00.000 3 4 //preprocessor directives: 5 #include 6 #undef OSC //remove definition so it can be used by p18f8722.h 7 #undef FALSE 8 #undef TRUE 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include "main.h" 16 17 //configuration bits: 18 #pragma config OSC = HSPLL 19 #pragma config FCMEN = OFF 20 #pragma config IESO = OFF 21 #pragma config PWRT = OFF 22 #pragma config BOREN = OFF 23 #pragma config WDT = OFF 24 #pragma config CCP2MX = PORTC 25 #pragma config ECCPMX = PORTE 26 #pragma config LVP = OFF 27 #pragma config MCLRE = ON 28 #pragma config XINST = OFF 29 30 //global variables: 31 OSgltypeMsgQP MsgQDataLogBuff[SizeOfMsgQDataLog]; //create buffer for MsgQ pointers 32 OStypeMsgP msgP; 33 OStypeErr CreateGetDataLog; 34 OStypeErr CreatePutDataLog; 35 OStypeErr CreateHandleUART; 36 OStypeErr CreateInitSDCard; 37 OStypeErr CreateInitPeripherals; 38 FSFILE *pSDFile;

174 39 int initialize = 0; 40 int detect = 0; 41 int close = 0; 42 int bytes_written = 0; 43 int DescriptorSize = 0; 44 char NewFile[13] = "DATALOG0.bin"; 45 char WriteArg[2] = "w"; 46 char ReadArg[2] = "r"; 47 char AppendArg[2] = "a"; 48 unsigned int buf_length = 0; 49 unsigned int Count25ms = 0; 50 unsigned int MediaRollOver = 0; 51 unsigned int HeadlineSize = 0; 52 unsigned int DataLogSize = 0; 53 unsigned char RS232Cmd = 0; 54 unsigned char Version[14] = "RTOS 2.00.000"; 55 unsigned char InitialDateStamp[10] = "DDMMMYYYY"; 56 unsigned char InitialTimeStamp[9] = "HH:MM:SS"; 57 unsigned char UARTCmdBuf[19] = {0}; 58 unsigned char UARTTxBuf[12] = {0}; 59 unsigned long TimeStamp = 0; 60 61 62 //task prototypes: 63 void InitPeripheralsTask(void); 64 void InitSDCardTask(void); 65 void GetDataLogTask(void); 66 void PutDataLogTask(void); 67 void HandleUARTTask(void); 68 void DateTimeTask(void); 69 70 //labels for tasks, required for context switch: 71 _OSLabel(GetDataLogTask1) 72 _OSLabel(GetDataLogTask2) 73 _OSLabel(GetDataLogTask3) 74 _OSLabel(GetDataLogTask4) 75 _OSLabel(PutDataLogTask1) 76 _OSLabel(PutDataLogTask2) 77 _OSLabel(PutDataLogTask3) 78 _OSLabel(PutDataLogTask4) 79 _OSLabel(PutDataLogTask5) 80 _OSLabel(PutDataLogTask6) 81 _OSLabel(PutDataLogTask7) 82 _OSLabel(PutDataLogTask8) 83 _OSLabel(PutDataLogTask9) 84 _OSLabel(PutDataLogTask10) 85 _OSLabel(PutDataLogTask11) 86 _OSLabel(PutDataLogTask12) 87 _OSLabel(PutDataLogTask13) 88 _OSLabel(HandleUARTTask1) 89 _OSLabel(DataTimeTask1) 90 _OSLabel(DataTimeTask2) 91 _OSLabel(DataTimeTask3) 92 _OSLabel(DataTimeTask4) 93 _OSLabel(DataTimeTask5) 94 _OSLabel(DataTimeTask6) 95 _OSLabel(DataTimeTask7) 96 _OSLabel(DataTimeTask8) 97 _OSLabel(DataTimeTask9) 98 _OSLabel(DataTimeTask10) 99 _OSLabel(DataTimeTask11) 100 _OSLabel(DataTimeTask12) 101 _OSLabel(DataTimeTask13) 102 _OSLabel(DataTimeTask14) 103 _OSLabel(DataTimeTask15)

175 104 _OSLabel(DataTimeTask16) 105 _OSLabel(DataTimeTask17) 106 _OSLabel(DataTimeTask18) 107 _OSLabel(DataTimeTask19) 108 _OSLabel(DataTimeTask20) 109 _OSLabel(DataTimeTask21) 110 _OSLabel(DataTimeTask22) 111 _OSLabel(DataTimeTask23) 112 _OSLabel(DataTimeTask24) 113 _OSLabel(DataTimeTask25) 114 _OSLabel(DataTimeTask26) 115 _OSLabel(DataTimeTask27) 116 _OSLabel(DataTimeTask28) 117 _OSLabel(DataTimeTask29) 118 _OSLabel(DataTimeTask30) 119 120 //task definitions: 121 void InitPeripheralsTask(void) 122 { 123 OSDi(); 124 InitPeripherals(); 125 CreateGetDataLog = OSCreateTask(GetDataLogTask,OSTCBP(2),1); //create task, priority 1 126 CreateHandleUART = OSCreateTask(HandleUARTTask,OSTCBP(3),3); //create task, priority 3 127 OS_Replace(InitSDCardTask,0); 128 } 129 130 void InitSDCardTask(void) 131 { 132 static struct datalog TestDataLog; 133 OSDi(); 134 DataLogSize = sizeof(TestDataLog); //determine the size of a data log 135 detect = MDD_MediaDetect(); //detect SD card 136 if(detect!=1) //if media not detected 137 { 138 UARTCRLF; 139 puts1USART(UARTTxBuf); 140 buf_length = sprintf(UARTCmdBuf,"CARD NOT DETECTED"); 141 puts1USART(UARTCmdBuf); 142 UARTCRLF; 143 puts1USART(UARTTxBuf); 144 PORTDbits.RD0 = 0b1; //illuminate error led 145 } 146 initialize = FSInit(); //initialize FAT file system 147 if((initialize!=1)) //if media not initialized 148 { 149 UARTCRLF; 150 puts1USART(UARTTxBuf); 151 buf_length = sprintf(UARTCmdBuf,"FS NOT INITIALIZED"); 152 puts1USART(UARTCmdBuf); 153 UARTCRLF; 154 puts1USART(UARTTxBuf); 155 PORTDbits.RD0 = 0b1; //illuminate error led 156 } 157 pSDFile = FSfopen(NewFile,WriteArg); //open new *.bin file 158 if((pSDFile==0)) //if file not opened 159 { 160 UARTCRLF; 161 puts1USART(UARTTxBuf); 162 buf_length = sprintf(UARTCmdBuf,"FILE NOT OPENED"); 163 puts1USART(UARTCmdBuf); 164 UARTCRLF; 165 puts1USART(UARTTxBuf); 166 PORTDbits.RD0 = 0b1; //illuminate error led 167 } 168 if((detect==1)&&(initialize==1)&&(pSDFile!=0))

176 169 { 170 HeadlineSize = PutHeadlineSDASCII(pSDFile); //writes headline block and returns its size 171 FSfseek(pSDFile,0,SEEK_SET); //return to beginning of file 172 PutVersionSD(Version,pSDFile); //write version 173 PutInitialDateStampSD(InitialDateStamp,pSDFile); //write the initial date stamp 174 PutInitialTimeStampSD(InitialTimeStamp,pSDFile); //write the initial time stamp 175 PutMediaRollOverSD(MediaRollOver,pSDFile); //write how many times card has rolled over 176 PutHeadlineSizeSD(HeadlineSize,pSDFile); //write headline block size 177 PutDataLogSizeSD(DataLogSize,pSDFile); //write the data log size (from sizeof) 178 DescriptorSize = DescribeDataLogSD(pSDFile); //writes descriptor block and returns its size 179 FSfseek(pSDFile,-DescriptorSize,SEEK_CUR); //rewind the length of descriptor block 180 PutDescriptorSizeSD(DescriptorSize,pSDFile); //write the descriptor size 181 DescribeDataLogSD(pSDFile); //write the descriptor block 182 PutHeadlineSDASCII(pSDFile); //write the headline block 183 } 184 OSEi(); 185 PORTDbits.RD2 = 0b0; //clear RD2 186 OS_Replace(PutDataLogTask,2); 187 } 188 189 void GetDataLogTask(void) 190 { 191 static struct datalog GetCurrentDataLog[SizeOfMsgQDataLog]; 192 static unsigned int i=0; 193 static unsigned int k=0; 194 static unsigned int StepCount[8] = {0}; 195 static OStypeMsgQSize MsgQDataLogUsedSlots; 196 while(1) 197 { 198 OS_Delay(1,GetDataLogTask1); 199 PORTDbits.RD3 = 0b1; 200 StepCount[0] = ReadTimer0()<<2; 201 StepCount[4] = ReadTimer1()<<2; 202 PORTGbits.RG3 = 0b1; 203 WriteTimer0(0x0000); 204 WriteTimer1(0x0000); 205 PORTDbits.RD3 = 0b0; 206 207 OS_Delay(1,GetDataLogTask2); 208 PORTDbits.RD3 = 0b1; 209 StepCount[1] = ReadTimer0()<<2; 210 StepCount[5] = ReadTimer1()<<2; 211 PORTGbits.RG3 = 0b0; 212 PORTGbits.RG4 = 0b1; 213 WriteTimer0(0x0000); 214 WriteTimer1(0x0000); 215 PORTDbits.RD3 = 0b0; 216 217 OS_Delay(1,GetDataLogTask3); 218 PORTDbits.RD3 = 0b1; 219 StepCount[2] = ReadTimer0()<<2; 220 StepCount[6] = ReadTimer1()<<2; 221 PORTGbits.RG3 = 0b1; 222 WriteTimer0(0x0000); 223 WriteTimer1(0x0000); 224 PORTDbits.RD3 = 0b0; 225 226 OS_Delay(1,GetDataLogTask4); 227 PORTDbits.RD3 = 0b1; 228 StepCount[3] = ReadTimer0()<<2; 229 StepCount[7] = ReadTimer1()<<2; 230 PORTGbits.RG3 = 0b0; 231 PORTGbits.RG4 = 0b0; 232 WriteTimer0(0x0000); 233 WriteTimer1(0x0000);

177 234 235 GetCurrentDataLog[k].TimeStamp = TimeStamp; 236 237 PORTFbits.RF7 = 0b0; //external analog mux control 238 SetChanADC(ADC_CH11); //set ADC to channel zero, internal mux control 239 ConvertADC(); //begin ADC conversion 240 while(BusyADC()); //wait here while ADC is busy 241 GetCurrentDataLog[k].Drive24V = ReadADC(); //record ADC result 242 PORTFbits.RF7 = 0b1; //external analog mux control 243 SetChanADC(ADC_CH11); 244 ConvertADC(); 245 while(BusyADC()){}; 246 GetCurrentDataLog[k].ch[0].PhaseA = ReadADC(); 247 SetChanADC(ADC_CH10); 248 ConvertADC(); 249 while(BusyADC()){}; 250 GetCurrentDataLog[k].ch[0].PhaseB = ReadADC(); 251 SetChanADC(ADC_CH9); 252 ConvertADC(); 253 while(BusyADC()){}; 254 GetCurrentDataLog[k].ch[1].PhaseA = ReadADC(); 255 SetChanADC(ADC_CH8); 256 ConvertADC(); 257 while(BusyADC()){}; 258 GetCurrentDataLog[k].ch[1].PhaseB = ReadADC(); 259 SetChanADC(ADC_CH7); 260 ConvertADC(); 261 while(BusyADC()){}; 262 GetCurrentDataLog[k].ch[2].PhaseA = ReadADC(); 263 SetChanADC(ADC_CH15); 264 ConvertADC(); 265 while(BusyADC()){}; 266 GetCurrentDataLog[k].ch[2].PhaseB = ReadADC(); 267 SetChanADC(ADC_CH5); 268 ConvertADC(); 269 while(BusyADC()){}; 270 GetCurrentDataLog[k].ch[3].PhaseA = ReadADC(); 271 SetChanADC(ADC_CH3); 272 ConvertADC(); 273 while(BusyADC()){}; 274 GetCurrentDataLog[k].ch[3].PhaseB = ReadADC(); 275 SetChanADC(ADC_CH2); 276 ConvertADC(); 277 while(BusyADC()){}; 278 GetCurrentDataLog[k].ch[4].PhaseA = ReadADC(); 279 SetChanADC(ADC_CH1); 280 ConvertADC(); 281 while(BusyADC()){}; 282 GetCurrentDataLog[k].ch[4].PhaseB = ReadADC(); 283 SetChanADC(ADC_CH0); 284 ConvertADC(); 285 while(BusyADC()){}; 286 GetCurrentDataLog[k].ch[5].PhaseA = ReadADC(); 287 SetChanADC(ADC_CH4); 288 ConvertADC(); 289 while(BusyADC()){}; 290 GetCurrentDataLog[k].ch[5].PhaseB = ReadADC(); 291 SetChanADC(ADC_CH14); 292 ConvertADC(); 293 while(BusyADC()){}; 294 GetCurrentDataLog[k].ch[6].PhaseA = ReadADC(); 295 SetChanADC(ADC_CH13); 296 ConvertADC(); 297 while(BusyADC()){}; 298 GetCurrentDataLog[k].ch[6].PhaseB = ReadADC();

178 299 SetChanADC(ADC_CH12); 300 ConvertADC(); 301 while(BusyADC()){}; 302 GetCurrentDataLog[k].ch[7].PhaseA = ReadADC(); 303 SetChanADC(ADC_CH6); 304 ConvertADC(); 305 while(BusyADC()){}; 306 GetCurrentDataLog[k].ch[7].PhaseB = ReadADC(); 307 308 for(i=0;i<=7;i++) //gather data for all eight channels 309 { 310 GetCurrentDataLog[k].ch[i].StepCount = StepCount[i]; //reading for step count 311 312 GetCurrentDataLog[k].ch[i].limits.Direction = PORTHbits.RH2; //direction and limits 313 GetCurrentDataLog[k].ch[i].limits.CWlimit = PORTHbits.RH3; 314 GetCurrentDataLog[k].ch[i].limits.CCWlimit = PORTEbits.RE1; 315 GetCurrentDataLog[k].ch[i].limits.HOMElimit = PORTEbits.RE0; 316 } 317 i=0; 318 MsgQDataLogUsedSlots = OSMsgQCount(MsgQDataLog); 319 if(((SizeOfMsgQDataLog-MsgQDataLogUsedSlots)<=0)&&(OSReadBinSem(BinSemPutDataLog)==0)) 320 { 321 UARTCRLF; 322 puts1USART(UARTTxBuf); 323 buf_length = sprintf(UARTCmdBuf,"DATALOG BUFF FULL"); 324 puts1USART(UARTCmdBuf); 325 UARTCRLF; 326 puts1USART(UARTTxBuf); 327 PORTDbits.RD0 = 0b1; 328 } 329 OSSignalMsgQ(MsgQDataLog,(OStypeMsgP)&GetCurrentDataLog[k]); 330 k++; 331 if(k>=(SizeOfMsgQDataLog-1)) 332 { 333 k=0; 334 } 335 PORTDbits.RD3 = 0b0; 336 } 337 } 338 339 void PutDataLogTask(void) 340 { 341 static struct datalog PutCurrentDataLog; 342 static unsigned int j=0; 343 static long FilePosition = 0; 344 OSEi(); 345 while(1) 346 { 347 if(OSReadBinSem(BinSemPutDataLog)!=0) 348 { 349 close = FSfclose(pSDFile); 350 if(close==-1) //if file not closed 351 { 352 UARTCRLF; 353 puts1USART(UARTTxBuf); 354 buf_length = sprintf(UARTCmdBuf,"FILE NOT CLOSED"); 355 puts1USART(UARTCmdBuf); 356 UARTCRLF; 357 puts1USART(UARTTxBuf); 358 PORTDbits.RD0 = 0b1; //illuminate error led 359 } 360 UARTCRLF; 361 puts1USART(UARTTxBuf); 362 buf_length = sprintf(UARTCmdBuf,"RECORDING STOPPED"); 363 puts1USART(UARTCmdBuf);

179 364 UARTCRLF; 365 puts1USART(UARTTxBuf); 366 buf_length = sprintf(UARTCmdBuf,"s = recording Stop"); 367 puts1USART(UARTCmdBuf); 368 UARTCRLF; 369 puts1USART(UARTTxBuf); 370 buf_length = sprintf(UARTCmdBuf,"g = recording Go"); 371 puts1USART(UARTCmdBuf); 372 UARTCRLF; 373 puts1USART(UARTTxBuf); 374 buf_length = sprintf(UARTCmdBuf,"t = toggle Termina"); 375 puts1USART(UARTCmdBuf); 376 buf_length = sprintf(UARTCmdBuf,"l output"); 377 puts1USART(UARTCmdBuf); 378 UARTCRLF; 379 puts1USART(UARTTxBuf); 380 buf_length = sprintf(UARTCmdBuf,"c = toggle sd Card"); 381 puts1USART(UARTCmdBuf); 382 buf_length = sprintf(UARTCmdBuf," output"); 383 puts1USART(UARTCmdBuf); 384 UARTCRLF; 385 puts1USART(UARTTxBuf); 386 buf_length = sprintf(UARTCmdBuf,"v = display Versi"); 387 puts1USART(UARTCmdBuf); 388 buf_length = sprintf(UARTCmdBuf,"on"); 389 puts1USART(UARTCmdBuf); 390 UARTCRLF; 391 puts1USART(UARTTxBuf); 392 buf_length = sprintf(UARTCmdBuf,"d = Display date/"); 393 puts1USART(UARTCmdBuf); 394 buf_length = sprintf(UARTCmdBuf,"time"); 395 puts1USART(UARTCmdBuf); 396 UARTCRLF; 397 puts1USART(UARTTxBuf); 398 buf_length = sprintf(UARTCmdBuf,"u = Update date/"); 399 puts1USART(UARTCmdBuf); 400 buf_length = sprintf(UARTCmdBuf,"time"); 401 puts1USART(UARTCmdBuf); 402 UARTCRLF; 403 puts1USART(UARTTxBuf); 404 OS_Delay(0,PutDataLogTask1); 405 } 406 OS_WaitMsgQ(MsgQDataLog,&msgP,OSNO_TIMEOUT,PutDataLogTask2); 407 PutCurrentDataLog = *(struct datalog*)msgP; 408 if(OSTryBinSem(BinSemTerminal)!=0) 409 { 410 PORTDbits.RD6 = 0b1; 411 OS_Yield(PutDataLogTask3); 412 UARTCRLF; 413 puts1USART(UARTTxBuf); 414 OS_Yield(PutDataLogTask4); 415 buf_length = sprintf(UARTTxBuf,"%ld,",PutCurrentDataLog.TimeStamp); 416 puts1USART(UARTTxBuf); 417 OS_Yield(PutDataLogTask5); 418 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.Drive24V); 419 puts1USART(UARTTxBuf); 420 421 for(j=0;j<=7;j++) 422 { 423 OS_Yield(PutDataLogTask6); 424 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].PhaseA); 425 puts1USART(UARTTxBuf); 426 OS_Yield(PutDataLogTask7); 427 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].PhaseB); 428 puts1USART(UARTTxBuf);

180 429 OS_Yield(PutDataLogTask8); 430 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].StepCount); 431 puts1USART(UARTTxBuf); 432 OS_Yield(PutDataLogTask9); 433 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].limits.Direction); 434 puts1USART(UARTTxBuf); 435 OS_Yield(PutDataLogTask10); 436 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].limits.CWlimit); 437 puts1USART(UARTTxBuf); 438 OS_Yield(PutDataLogTask11); 439 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].limits.CCWlimit); 440 puts1USART(UARTTxBuf); 441 OS_Yield(PutDataLogTask12); 442 buf_length = sprintf(UARTTxBuf,"%d,",PutCurrentDataLog.ch[j].limits.HOMElimit); 443 puts1USART(UARTTxBuf); 444 } 445 j=0; 446 OS_Yield(PutDataLogTask13); 447 buf_length = sprintf(UARTTxBuf,"\r\n"); 448 puts1USART(UARTTxBuf); 449 OSSignalBinSem(BinSemTerminal); 450 PORTDbits.RD6 = 0b0; 451 } 452 if(OSTryBinSem(BinSemSDCard)!=0) 453 { 454 PORTDbits.RD5 = 0b1; 455 PutDataLogSD(PutCurrentDataLog,pSDFile); 456 FilePosition = FSftell(pSDFile); //returns the file pointer position 457 if(FilePosition==-1) //file position fails 458 { 459 UARTCRLF; 460 puts1USART(UARTTxBuf); 461 buf_length = sprintf(UARTCmdBuf,"FILE POS FAILED"); 462 puts1USART(UARTCmdBuf); 463 UARTCRLF; 464 puts1USART(UARTTxBuf); 465 close = FSfclose(pSDFile); //close file 466 if(close==-1) 467 { 468 UARTCRLF; 469 puts1USART(UARTTxBuf); 470 buf_length = sprintf(UARTCmdBuf,"FILE NOT CLOSED"); 471 puts1USART(UARTCmdBuf); 472 UARTCRLF; 473 puts1USART(UARTTxBuf); 474 PORTDbits.RD0 = 0b1; 475 } 476 PORTDbits.RD0 = 0b1; //illuminate error led 477 } 478 if(FilePosition>=0x773593FF) //if file size reaches (2GB-1byte) limit 479 { 480 FSrewind(pSDFile); //reset file pointer to beginning of file 481 MediaRollOver++; //increment the rollover count 482 PutVersionSD(Version,pSDFile); //write version 483 PutInitialDateStampSD(InitialDateStamp,pSDFile); //write the initial date stamp 484 PutInitialTimeStampSD(InitialTimeStamp,pSDFile); //write the initial time stamp 485 PutMediaRollOverSD(MediaRollOver,pSDFile); //write the updated rollover count 486 PutHeadlineSizeSD(HeadlineSize,pSDFile); //write the headline size (already known) 487 PutDataLogSizeSD(DataLogSize,pSDFile); //write the data log size (from sizeof) 488 PutDescriptorSizeSD(DescriptorSize,pSDFile); //write the descriptor block size 489 DescribeDataLogSD(pSDFile); //write the descriptor block 490 PutHeadlineSDASCII(pSDFile); //write the headline block 491 } 492 UARTCRLF; 493 puts1USART(UARTTxBuf);

181 494 putc1USART(0x58); //transmit "X" over UART 495 UARTCRLF; 496 puts1USART(UARTTxBuf); 497 OSSignalBinSem(BinSemSDCard); 498 PORTDbits.RD5 = 0b0; 499 } 500 } 501 } 502 503 void HandleUARTTask(void) 504 { 505 while(1) 506 { 507 OS_WaitBinSem(BinSemUART,OSNO_TIMEOUT,HandleUARTTask1); 508 switch(RS232Cmd) 509 { 510 case 0x73: //received "s", stop data logging 511 if(OSReadBinSem(BinSemPutDataLog)!=0) 512 { 513 UARTCRLF; 514 puts1USART(UARTTxBuf); 515 buf_length = sprintf(UARTCmdBuf,"RECORDING STOPPED"); 516 puts1USART(UARTCmdBuf); 517 UARTCRLF; 518 puts1USART(UARTTxBuf); 519 buf_length = sprintf(UARTCmdBuf,"s = recording Stop"); 520 puts1USART(UARTCmdBuf); 521 UARTCRLF; 522 puts1USART(UARTTxBuf); 523 buf_length = sprintf(UARTCmdBuf,"g = recording Go"); 524 puts1USART(UARTCmdBuf); 525 UARTCRLF; 526 puts1USART(UARTTxBuf); 527 buf_length = sprintf(UARTCmdBuf,"t = toggle Termina"); 528 puts1USART(UARTCmdBuf); 529 buf_length = sprintf(UARTCmdBuf,"l output"); 530 puts1USART(UARTCmdBuf); 531 UARTCRLF; 532 puts1USART(UARTTxBuf); 533 buf_length = sprintf(UARTCmdBuf,"c = toggle sd Card"); 534 puts1USART(UARTCmdBuf); 535 buf_length = sprintf(UARTCmdBuf," output"); 536 puts1USART(UARTCmdBuf); 537 UARTCRLF; 538 puts1USART(UARTTxBuf); 539 buf_length = sprintf(UARTCmdBuf,"v = display Versi"); 540 puts1USART(UARTCmdBuf); 541 buf_length = sprintf(UARTCmdBuf,"on"); 542 puts1USART(UARTCmdBuf); 543 UARTCRLF; 544 puts1USART(UARTTxBuf); 545 buf_length = sprintf(UARTCmdBuf,"d = Display date/"); 546 puts1USART(UARTCmdBuf); 547 buf_length = sprintf(UARTCmdBuf,"time"); 548 puts1USART(UARTCmdBuf); 549 UARTCRLF; 550 puts1USART(UARTTxBuf); 551 buf_length = sprintf(UARTCmdBuf,"u = Update date/"); 552 puts1USART(UARTCmdBuf); 553 buf_length = sprintf(UARTCmdBuf,"time"); 554 puts1USART(UARTCmdBuf); 555 UARTCRLF; 556 puts1USART(UARTTxBuf); 557 } 558 else

182 559 { 560 OSSignalBinSem(BinSemPutDataLog); 561 } 562 break; 563 case 0x67: //received "g", start data logging 564 if(OSTryBinSem(BinSemPutDataLog)!=0) 565 { 566 OSStartTask(OSTCBP(1)); 567 OSCreateMsgQ(MsgQDataLog,MsgQDataLogCB,MsgQDataLogBuff,SizeOfMsgQDataLog); 568 if(OSReadBinSem(BinSemSDCard)!=0) 569 { 570 if((detect!=1)||(initialize!=1)||(pSDFile==0)) 571 { 572 OSDestroyTask(OSTCBP(1)); 573 CreateInitSDCard = OSCreateTask(InitSDCardTask,OSTCBP(1),0); 574 } 575 else 576 { 577 pSDFile = FSfopen(NewFile,AppendArg); //open *.bin file for appending 578 if(pSDFile==0) //if file not opened 579 { 580 UARTCRLF; 581 puts1USART(UARTTxBuf); 582 buf_length = sprintf(UARTCmdBuf,"FILE NOT OPENED"); 583 puts1USART(UARTCmdBuf); 584 UARTCRLF; 585 puts1USART(UARTTxBuf); 586 PORTDbits.RD0 = 0b1; //illuminate error led 587 } 588 } 589 } 590 } 591 UARTCRLF; 592 puts1USART(UARTTxBuf); 593 buf_length = sprintf(UARTCmdBuf,"RECORDING STARTED"); 594 puts1USART(UARTCmdBuf); 595 UARTCRLF; 596 puts1USART(UARTTxBuf); 597 break; 598 case 0x74: //received "t", toggle terminal output 599 if(OSReadBinSem(BinSemPutDataLog)!=0) 600 { 601 if(OSReadBinSem(BinSemTerminal)!=0) //terminal enabled? 602 { 603 OSTryBinSem(BinSemTerminal); //yes, disable 604 UARTCRLF; 605 puts1USART(UARTTxBuf); 606 buf_length = sprintf(UARTCmdBuf,"TERMINAL DISABLED"); 607 puts1USART(UARTCmdBuf); 608 UARTCRLF; 609 puts1USART(UARTTxBuf); 610 } 611 else 612 { 613 OSSignalBinSem(BinSemTerminal); //no, enable 614 UARTCRLF; 615 puts1USART(UARTTxBuf); 616 buf_length = sprintf(UARTCmdBuf,"TERMINAL ENABLED"); 617 puts1USART(UARTCmdBuf); 618 UARTCRLF; 619 puts1USART(UARTTxBuf); 620 } 621 } 622 break; 623 case 0x63: //received "c", toggle SD card output

183 624 if(OSReadBinSem(BinSemPutDataLog)!=0) 625 { 626 if(OSReadBinSem(BinSemSDCard)!=0) //card enabled? 627 { 628 OSTryBinSem(BinSemSDCard); //yes, disable 629 UARTCRLF; 630 puts1USART(UARTTxBuf); 631 buf_length = sprintf(UARTCmdBuf,"SD CARD DISABLED"); 632 puts1USART(UARTCmdBuf); 633 UARTCRLF; 634 puts1USART(UARTTxBuf); 635 } 636 else 637 { 638 OSSignalBinSem(BinSemSDCard); //no, enable 639 UARTCRLF; 640 puts1USART(UARTTxBuf); 641 buf_length = sprintf(UARTCmdBuf,"SD CARD ENABLED"); 642 puts1USART(UARTCmdBuf); 643 UARTCRLF; 644 puts1USART(UARTTxBuf); 645 } 646 } 647 break; 648 case 0x76: //received "v", display version 649 if(OSReadBinSem(BinSemPutDataLog)!=0) 650 { 651 UARTCRLF; 652 puts1USART(UARTTxBuf); 653 puts1USART(Version); 654 UARTCRLF; 655 puts1USART(UARTTxBuf); 656 } 657 break; 658 case 0x64: //received "d", display initial date/time 659 if(OSReadBinSem(BinSemPutDataLog)!=0) 660 { 661 UARTCRLF; 662 puts1USART(UARTTxBuf); 663 puts1USART(InitialDateStamp); 664 UARTCRLF; 665 puts1USART(UARTTxBuf); 666 puts1USART(InitialTimeStamp); 667 UARTCRLF; 668 puts1USART(UARTTxBuf); 669 } 670 break; 671 case 0x75: //received "u", update date/time 672 if(OSReadBinSem(BinSemPutDataLog)!=0) 673 { 674 OS_Replace(DateTimeTask,3); 675 } 676 break; 677 default: 678 break; 679 } 680 } 681 } 682 683 void DateTimeTask(void) 684 { 685 static unsigned char NewDateStamp[10] = {0}; 686 static unsigned char NewTimeStamp[9] = {0}; 687 static unsigned int CharacterCount = 0; 688 OS_Yield(DateTimeTask1);

184 689 UARTCRLF; 690 OS_Yield(DateTimeTask2); 691 puts1USART(UARTTxBuf); 692 OS_Yield(DateTimeTask3); 693 buf_length = sprintf(UARTCmdBuf,"ENTER DATE"); 694 OS_Yield(DateTimeTask4); 695 puts1USART(UARTCmdBuf); 696 OS_Yield(DateTimeTask5); 697 buf_length = sprintf(UARTCmdBuf," IN DDMMMYYYY"); 698 OS_Yield(DateTimeTask6); 699 puts1USART(UARTCmdBuf); 700 OS_Yield(DateTimeTask7); 701 buf_length = sprintf(UARTCmdBuf," FORMAT:"); 702 OS_Yield(DateTimeTask8); 703 puts1USART(UARTCmdBuf); 704 OS_Yield(DateTimeTask9); 705 UARTCRLF; 706 OS_Yield(DateTimeTask10); 707 puts1USART(UARTTxBuf); 708 for(CharacterCount=0;CharacterCount<=8;CharacterCount++) 709 { 710 OS_WaitBinSem(BinSemUART,OSNO_TIMEOUT,DateTimeTask1); 711 NewDateStamp[CharacterCount] = RS232Cmd; 712 } 713 CharacterCount = 0; 714 puts1USART(NewDateStamp); 715 OS_Yield(DateTimeTask11); 716 UARTCRLF; 717 OS_Yield(DateTimeTask12); 718 puts1USART(UARTTxBuf); 719 OS_Yield(DateTimeTask13); 720 buf_length = sprintf(UARTCmdBuf,"ENTER TIME"); 721 OS_Yield(DateTimeTask14); 722 puts1USART(UARTCmdBuf); 723 OS_Yield(DateTimeTask15); 724 buf_length = sprintf(UARTCmdBuf," IN HH:MM:SS"); 725 OS_Yield(DateTimeTask16); 726 puts1USART(UARTCmdBuf); 727 OS_Yield(DateTimeTask17); 728 buf_length = sprintf(UARTCmdBuf," FORMAT:"); 729 OS_Yield(DateTimeTask18); 730 puts1USART(UARTCmdBuf); 731 OS_Yield(DateTimeTask19); 732 UARTCRLF; 733 OS_Yield(DateTimeTask20); 734 puts1USART(UARTTxBuf); 735 for(CharacterCount=0;CharacterCount<=7;CharacterCount++) 736 { 737 OS_WaitBinSem(BinSemUART,OSNO_TIMEOUT,DateTimeTask2); 738 NewTimeStamp[CharacterCount] = RS232Cmd; 739 } 740 CharacterCount = 0; 741 puts1USART(NewTimeStamp); 742 OS_Yield(DateTimeTask21); 743 UARTCRLF; 744 OS_Yield(DateTimeTask22); 745 puts1USART(UARTTxBuf); 746 OS_Yield(DateTimeTask23); 747 buf_length = sprintf(UARTCmdBuf,"PRESS ENTER TO"); 748 OS_Yield(DateTimeTask24); 749 puts1USART(UARTCmdBuf); 750 OS_Yield(DateTimeTask25); 751 buf_length = sprintf(UARTCmdBuf," SAVE, ANY OTHER "); 752 OS_Yield(DateTimeTask26); 753 puts1USART(UARTCmdBuf);

185 754 OS_Yield(DateTimeTask27); 755 buf_length = sprintf(UARTCmdBuf,"KEY TO CANCEL"); 756 OS_Yield(DateTimeTask28); 757 puts1USART(UARTCmdBuf); 758 OS_Yield(DateTimeTask29); 759 UARTCRLF; 760 OS_Yield(DateTimeTask30); 761 puts1USART(UARTTxBuf); 762 OS_WaitBinSem(BinSemUART,OSNO_TIMEOUT,DateTimeTask3); 763 switch(RS232Cmd) 764 { 765 case 0x0D: //receive enter, save and replace task 766 for(CharacterCount=0;CharacterCount<=8;CharacterCount++) 767 { 768 InitialDateStamp[CharacterCount] = NewDateStamp[CharacterCount]; 769 } 770 CharacterCount = 0; 771 for(CharacterCount=0;CharacterCount<=8;CharacterCount++) 772 { 773 InitialTimeStamp[CharacterCount] = NewTimeStamp[CharacterCount]; 774 } 775 TimeStamp = 0; 776 buf_length = sprintf(UARTCmdBuf,"SAVED"); 777 puts1USART(UARTCmdBuf); 778 UARTCRLF; 779 puts1USART(UARTTxBuf); 780 OSSignalBinSem(BinSemPutDataLog); 781 OSDestroyTask(OSTCBP(1)); //destroy put data log task 782 CreateInitSDCard = OSCreateTask(InitSDCardTask,OSTCBP(1),0); //create task, priority 0 (highest) 783 OS_Replace(HandleUARTTask,3); 784 break; 785 default: //receive any other key, do not save 786 buf_length = sprintf(UARTCmdBuf,"NOT SAVED"); 787 puts1USART(UARTCmdBuf); 788 UARTCRLF; 789 puts1USART(UARTTxBuf); 790 OS_Replace(HandleUARTTask,3); 791 break; 792 } 793 794 } 795 796 797 //ISR stub functions: 798 void HP_ISR(void); 799 #pragma code hp_vector = 0x0008 800 void hp_interrupt(void) 801 { 802 _asm GOTO HP_ISR _endasm 803 } 804 #pragma code 805 #pragma interrupt HP_ISR 806 807 void LP_ISR(void); 808 #pragma code lp_vector = 0x0018 809 void lp_interrupt(void) 810 { 811 _asm GOTO LP_ISR _endasm 812 } 813 #pragma code 814 #pragma interruptlow LP_ISR 815 816 //ISRs: 817 void HP_ISR(void) 818 {

186 819 PORTDbits.RD4 = 0b1; //set RD4 820 if(PIR2bits.TMR3IF!=0) 821 { 822 PIR2bits.TMR3IF = 0b0; //clear timer IF 823 WriteTimer3(TMR3_LOAD); //reload timer 824 Count25ms++; //increment 25ms count 825 if(Count25ms>=4) //if 4 25ms periods are counted 826 { 827 Count25ms = 0; //reset 25ms count 828 TimeStamp++; //increment the 100ms time stamp 829 } 830 OSTimer(); //check for timed out tasks 831 } 832 PORTDbits.RD4 = 0b0; //clear RD4 833 } 834 835 void LP_ISR(void) 836 { 837 PORTDbits.RD7 = 0b1; //set RD7 838 if(PIR1bits.RC1IF!=0) //incoming UART character? 839 { 840 RS232Cmd = Read1USART(); //read the incoming character 841 OSSignalBinSem(BinSemUART); //signal binary semaphore 842 } 843 PORTDbits.RD7 = 0b0; //clear RD7 844 } 845 846 //begin main: 847 void main(void) 848 { 849 TRISDbits.TRISD2 = 0b0; //RD2 is output; measure initialize period 850 PORTDbits.RD2 = 0b1; //set RD2 851 852 OSInit(); //initialize the OS 853 854 OSDi(); //disable global interrupts 855 856 CreateInitPeripherals = OSCreateTask(InitPeripheralsTask,OSTCBP(1),0); //create task, priority 0 (highest) 857 858 OSCreateBinSem(BinSemPutDataLog,1); //create binary semaphore, signalled 859 OSCreateBinSem(BinSemUART,0); //create binary semaphore, unsignalled 860 OSCreateBinSem(BinSemTerminal,0); //create binary semaphore, unsignalled 861 OSCreateBinSem(BinSemSDCard,0); //create binary semaphore, unsignalled 862 OSCreateMsgQ(MsgQDataLog,MsgQDataLogCB,MsgQDataLogBuff,SizeOfMsgQDataLog); //create message queue 863 864 while(1) 865 { 866 OSSched(); //begin scheduler 867 } 868 }

B.2.2 auxiliary.c

1 //Michael Daniel : 4 February 2012 : EECE 7990 2 //Salvo Application Code Version 2.00.000 3 4 //pre-processor directives: 5 #include 6 #undef OSC 7 #undef FALSE 8 #undef TRUE 9 #include 10 #include

187 11 #include 12 #include 13 #include 14 #include 15 #include "main.h" 16 17 //function definitions: 18 void InitPeripherals(void) 19 { 20 extern unsigned int buf_length; 21 extern unsigned char UARTTxBuf[12]; 22 extern unsigned char Version[14]; 23 extern unsigned char InitialDateStamp[10]; 24 extern unsigned char InitialTimeStamp[9]; 25 26 RCONbits.IPEN = 0b1; //enable interrupt priority levels 27 INTCONbits.GIEH = 0b0; //globally disable HP interrupts 28 INTCONbits.GIEL = 0b0; //globally disable LP interrupts 29 PIE2bits.TMR3IE = 0b0; //disable timer3 interrupt 30 PIR2bits.TMR3IF = 0b0; //clear timer3 interrupt flag 31 PIE1bits.RC1IE = 0b0; //disable UART1 RX interrupt 32 PIR1bits.RC1IF = 0b0; //clear UART RX interrupt flag 33 PIE1bits.ADIE = 0b0; //disable ADC interrupt 34 PIR1bits.ADIF = 0b0; //clear ADC interrupt flag 35 PIE2bits.HLVDIE = 0b0; //disable hi/lo voltage detect 36 PIR2bits.HLVDIF = 0b0; //clear hi/lo voltage detect flag 37 INTCONbits.RBIE = 0b0; //disable change on PORTB 38 INTCONbits.RBIF = 0b0; //clear change on PORTB flag 39 40 TRISFbits.TRISF0 = 0b1; //analog input 41 TRISFbits.TRISF1 = 0b1; //analog input 42 TRISFbits.TRISF2 = 0b1; //analog input 43 TRISFbits.TRISF3 = 0b1; //analog input 44 TRISFbits.TRISF4 = 0b1; //analog input 45 TRISFbits.TRISF5 = 0b1; //analog input 46 TRISFbits.TRISF6 = 0b1; //analog input 47 TRISHbits.TRISH4 = 0b1; //analog input 48 TRISHbits.TRISH5 = 0b1; //analog input 49 TRISHbits.TRISH6 = 0b1; //analog input 50 TRISHbits.TRISH7 = 0b1; //analog input 51 TRISAbits.TRISA0 = 0b1; //analog input 52 TRISAbits.TRISA1 = 0b1; //analog input 53 TRISAbits.TRISA2 = 0b1; //analog input 54 TRISAbits.TRISA3 = 0b1; //analog input 55 TRISAbits.TRISA5 = 0b1; //analog input 56 57 TRISHbits.TRISH2 = 0b1; //digital input 58 TRISHbits.TRISH3 = 0b1; //digital input 59 TRISEbits.TRISE1 = 0b1; //digital input 60 TRISEbits.TRISE0 = 0b1; //digital input 61 62 TRISDbits.TRISD0 = 0b0; //RD0 is output: Error LED 63 TRISGbits.TRISG4 = 0b0; //RG4 is output: digital MUX control 64 TRISGbits.TRISG3 = 0b0; //RG3 is output: digital MUX control 65 TRISFbits.TRISF3 = 0b0; //RF7 is output: analog MUX control 66 67 TRISDbits.TRISD6 = 0b0; //RD6 is output: measure terminal write period 68 TRISDbits.TRISD5 = 0b0; //RD5 is output: measure SD card write period 69 TRISDbits.TRISD5 = 0b0; //RD4 is output: measure HP ISR period 70 TRISDbits.TRISD7 = 0b0; //RD7 is output: measure LP ISR period 71 TRISDbits.TRISD3 = 0b0; //RD3 is output: measure data gather period 72 73 PORTDbits.RD6 = 0b0; //clear RD6 74 PORTDbits.RD5 = 0b0; //clear RD4 75 PORTDbits.RD4 = 0b0; //clear RD4

188 76 PORTDbits.RD7 = 0b0; //clear RD7 77 PORTDbits.RD3 = 0b0; //clear RD3 78 79 PORTGbits.RG4 = 0b0; //clear MUX control bits 80 PORTGbits.RG3 = 0b0; 81 PORTFbits.RF7 = 0b0; 82 PORTDbits.RD0 = 0b0; //clear error LED bit 83 84 CloseADC(); //close ADC if previously open 85 OpenADC(ADC_CONFIG,ADC_CONFIG2,ADC_PORTCONFIG); //configure and enable ADC 86 ADCON0bits.GO = 0b0; //stop ADC from converting 87 88 Close1USART(); //close usart if previously open 89 baud1USART(USART1_BAUDCONFIG); //configure usart baud rate 90 Open1USART(USART1_CONFIG,USART1_SPBRG); //configure and enable usart 91 RCREG1 = 0x00; //clear the UART RX buffer 92 PIR1bits.RC1IF = 0b0; //clear UART RX interrupt flag 93 94 CloseTimer0(); 95 OpenTimer0(TMR0_CONFIG); 96 T0CONbits.TMR0ON = 0b0; 97 WriteTimer0(0x0000); 98 99 CloseTimer1(); 100 OpenTimer1(TMR1_CONFIG); 101 T1CONbits.TMR1ON = 0b0; 102 WriteTimer1(0x0000); 103 104 CloseTimer3(); //close timer3 if previously open 105 OpenTimer3(TMR3_CONFIG); //open timer3 with pre-configured settings 106 T3CONbits.TMR3ON = 0b0; //disable timer until loaded 107 WriteTimer3(TMR3_LOAD); //load timer3 108 109 IPR2bits.TMR3IP = 0b1; //timer3 interrupt is high priority 110 IPR1bits.RC1IP = 0b0; //UART RX is low priority 111 112 T3CONbits.TMR3ON = 0b1; //start running timer... 113 T1CONbits.TMR1ON = 0b1; //enable step counter... 114 T0CONbits.TMR0ON = 0b1; //enable step counter... 115 116 UARTCRLF; 117 puts1USART(UARTTxBuf); 118 puts1USART(Version); 119 UARTCRLF; 120 puts1USART(UARTTxBuf); 121 puts1USART(InitialDateStamp); 122 UARTCRLF; 123 puts1USART(UARTTxBuf); 124 puts1USART(InitialTimeStamp); 125 UARTCRLF; 126 puts1USART(UARTTxBuf); 127 } 128 129 void PutVersionSD(unsigned char *Version,FSFILE *ptrSDFile) 130 { 131 int BytesWritten = 0; 132 extern unsigned int buf_length; 133 extern unsigned char UARTTxBuf[12]; 134 extern unsigned char UARTCmdBuf[19]; 135 BytesWritten = FSfwrite(Version,1,13,ptrSDFile); 136 if(BytesWritten!=13) 137 { 138 UARTCRLF; 139 puts1USART(UARTTxBuf); 140 buf_length = sprintf(UARTCmdBuf,"VERSION MISWRITE");

189 141 puts1USART(UARTCmdBuf); 142 UARTCRLF; 143 puts1USART(UARTTxBuf); 144 PORTDbits.RD0 = 0b1; 145 } 146 } 147 148 void PutInitialDateStampSD(unsigned char *InitDS,FSFILE *ptrSDFile) 149 { 150 int BytesWritten = 0; 151 extern unsigned int buf_length; 152 extern unsigned char UARTTxBuf[12]; 153 extern unsigned char UARTCmdBuf[19]; 154 BytesWritten = FSfwrite(InitDS,1,9,ptrSDFile); 155 if(BytesWritten!=9) 156 { 157 UARTCRLF; 158 puts1USART(UARTTxBuf); 159 buf_length = sprintf(UARTCmdBuf,"DATE STMP MISWRITE"); 160 puts1USART(UARTCmdBuf); 161 UARTCRLF; 162 puts1USART(UARTTxBuf); 163 PORTDbits.RD0 = 0b1; 164 } 165 } 166 167 void PutInitialTimeStampSD(unsigned char *InitTS,FSFILE *ptrSDFile) 168 { 169 int BytesWritten = 0; 170 extern unsigned int buf_length; 171 extern unsigned char UARTTxBuf[12]; 172 extern unsigned char UARTCmdBuf[19]; 173 BytesWritten = FSfwrite(InitTS,1,8,ptrSDFile); 174 if(BytesWritten!=8) 175 { 176 UARTCRLF; 177 puts1USART(UARTTxBuf); 178 buf_length = sprintf(UARTCmdBuf,"TIME STMP MISWRITE"); 179 puts1USART(UARTCmdBuf); 180 UARTCRLF; 181 puts1USART(UARTTxBuf); 182 PORTDbits.RD0 = 0b1; 183 } 184 } 185 186 void PutMediaRollOverSD(unsigned int MediaRO,FSFILE *ptrSDFile) 187 { 188 int BytesWritten = 0; 189 extern unsigned int buf_length; 190 extern unsigned char UARTTxBuf[12]; 191 extern unsigned char UARTCmdBuf[19]; 192 BytesWritten = FSfwrite(&MediaRO,1,2,ptrSDFile); 193 if(BytesWritten!=2) 194 { 195 UARTCRLF; 196 puts1USART(UARTTxBuf); 197 buf_length = sprintf(UARTCmdBuf,"MEDIA RO MISWRITE"); 198 puts1USART(UARTCmdBuf); 199 UARTCRLF; 200 puts1USART(UARTTxBuf); 201 PORTDbits.RD0 = 0b1; //illuminate error LED 202 } 203 } 204 205 void PutHeadlineSizeSD(unsigned int HeadlineSZ,FSFILE *ptrSDFile)

190 206 { 207 int BytesWritten = 0; 208 extern unsigned int buf_length; 209 extern unsigned char UARTTxBuf[12]; 210 extern unsigned char UARTCmdBuf[19]; 211 BytesWritten = FSfwrite(&HeadlineSZ,1,2,ptrSDFile); 212 if(BytesWritten!=2) 213 { 214 UARTCRLF; 215 puts1USART(UARTTxBuf); 216 buf_length = sprintf(UARTCmdBuf,"HDLN SIZE MISWRITE"); 217 puts1USART(UARTCmdBuf); 218 UARTCRLF; 219 puts1USART(UARTTxBuf); 220 PORTDbits.RD0 = 0b1; 221 } 222 } 223 224 void PutDataLogSizeSD(unsigned int DataLogSZ,FSFILE *ptrSDFile) 225 { 226 int BytesWritten = 0; 227 extern unsigned int buf_length; 228 extern unsigned char UARTTxBuf[12]; 229 extern unsigned char UARTCmdBuf[19]; 230 BytesWritten = FSfwrite(&DataLogSZ,1,2,ptrSDFile); 231 if(BytesWritten!=2) 232 { 233 UARTCRLF; 234 puts1USART(UARTTxBuf); 235 buf_length = sprintf(UARTCmdBuf,"DTLG SIZE MISWRITE"); 236 puts1USART(UARTCmdBuf); 237 UARTCRLF; 238 puts1USART(UARTTxBuf); 239 PORTDbits.RD0 = 0b1; //illuminate error LED 240 } 241 } 242 243 void PutDescriptorSizeSD(unsigned int DescriptorSZ,FSFILE *ptrSDFile) 244 { 245 int BytesWritten = 0; 246 extern unsigned int buf_length; 247 extern unsigned char UARTTxBuf[12]; 248 extern unsigned char UARTCmdBuf[19]; 249 BytesWritten = FSfwrite(&DescriptorSZ,1,2,ptrSDFile); 250 if(BytesWritten!=2) 251 { 252 UARTCRLF; 253 puts1USART(UARTTxBuf); 254 buf_length = sprintf(UARTCmdBuf,"DESC SZ MISWRITE"); 255 puts1USART(UARTCmdBuf); 256 UARTCRLF; 257 puts1USART(UARTTxBuf); 258 PORTDbits.RD0 = 0b1; 259 } 260 } 261 262 unsigned int PutHeadlineSDASCII(FSFILE *ptrSDFile) 263 { 264 unsigned int itr_p = 0; 265 unsigned int HeadlineSZ = 0; 266 unsigned char SDTXBuf[12] = {0}; 267 int BytesWritten = 0; 268 int BufLength = 0; 269 extern unsigned int buf_length; 270 extern unsigned char UARTTxBuf[12];

191 271 extern unsigned char UARTCmdBuf[19]; 272 273 for(itr_p=0;itr_p<=11;itr_p++) 274 { 275 SDTXBuf[itr_p] = 0x00; 276 } 277 itr_p = 0; 278 BufLength = sprintf(SDTXBuf,"TIMESTMP,"); 279 BytesWritten = FSfwrite((void*)SDTXBuf,1,9,ptrSDFile); 280 HeadlineSZ += BytesWritten; 281 if(BytesWritten!=9) 282 { 283 UARTCRLF; 284 puts1USART(UARTTxBuf); 285 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 286 puts1USART(UARTCmdBuf); 287 UARTCRLF; 288 puts1USART(UARTTxBuf); 289 PORTDbits.RD0 = 0b1; //illuminate error LED 290 } 291 BufLength = sprintf(SDTXBuf,"24VDRIVE,"); 292 BytesWritten = FSfwrite((void*)SDTXBuf,1,9,ptrSDFile); 293 HeadlineSZ += BytesWritten; 294 if(BytesWritten!=9) 295 { 296 UARTCRLF; 297 puts1USART(UARTTxBuf); 298 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 299 puts1USART(UARTCmdBuf); 300 UARTCRLF; 301 puts1USART(UARTTxBuf); 302 PORTDbits.RD0 = 0b1; //illuminate error LED 303 } 304 for(itr_p=0;itr_p<=7;itr_p++) 305 { 306 BufLength = sprintf(SDTXBuf,"CH%dPHA,",itr_p+1); 307 BytesWritten = FSfwrite((void*)SDTXBuf,1,7,ptrSDFile); 308 HeadlineSZ += BytesWritten; 309 if(BytesWritten!=7) 310 { 311 UARTCRLF; 312 puts1USART(UARTTxBuf); 313 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 314 puts1USART(UARTCmdBuf); 315 UARTCRLF; 316 puts1USART(UARTTxBuf); 317 PORTDbits.RD0 = 0b1; //illuminate error LED 318 } 319 BufLength = sprintf(SDTXBuf,"CH%dPHB,",itr_p+1); 320 BytesWritten = FSfwrite((void*)SDTXBuf,1,7,ptrSDFile); 321 HeadlineSZ += BytesWritten; 322 if(BytesWritten!=7) 323 { 324 UARTCRLF; 325 puts1USART(UARTTxBuf); 326 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 327 puts1USART(UARTCmdBuf); 328 UARTCRLF; 329 puts1USART(UARTTxBuf); 330 PORTDbits.RD0 = 0b1; //illuminate error LED 331 } 332 BufLength = sprintf(SDTXBuf,"CH%dSTEP,",itr_p+1); 333 BytesWritten = FSfwrite((void*)SDTXBuf,1,8,ptrSDFile); 334 HeadlineSZ += BytesWritten; 335 if(BytesWritten!=8)

192 336 { 337 UARTCRLF; 338 puts1USART(UARTTxBuf); 339 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 340 puts1USART(UARTCmdBuf); 341 UARTCRLF; 342 puts1USART(UARTTxBuf); 343 PORTDbits.RD0 = 0b1; //illuminate error LED 344 } 345 BufLength = sprintf(SDTXBuf,"CH%dDIR,",itr_p+1); 346 BytesWritten = FSfwrite((void*)SDTXBuf,1,7,ptrSDFile); 347 HeadlineSZ += BytesWritten; 348 if(BytesWritten!=7) 349 { 350 UARTCRLF; 351 puts1USART(UARTTxBuf); 352 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 353 puts1USART(UARTCmdBuf); 354 UARTCRLF; 355 puts1USART(UARTTxBuf); 356 PORTDbits.RD0 = 0b1; //illuminate error LED 357 } 358 BufLength = sprintf(SDTXBuf,"CH%dCW,",itr_p+1); 359 BytesWritten = FSfwrite((void*)SDTXBuf,1,6,ptrSDFile); 360 HeadlineSZ += BytesWritten; 361 if(BytesWritten!=6) 362 { 363 UARTCRLF; 364 puts1USART(UARTTxBuf); 365 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 366 puts1USART(UARTCmdBuf); 367 UARTCRLF; 368 puts1USART(UARTTxBuf); 369 PORTDbits.RD0 = 0b1; //illuminate error LED 370 } 371 BufLength = sprintf(SDTXBuf,"CH%dCCW,",itr_p+1); 372 BytesWritten = FSfwrite((void*)SDTXBuf,1,7,ptrSDFile); 373 HeadlineSZ += BytesWritten; 374 if(BytesWritten!=7) 375 { 376 UARTCRLF; 377 puts1USART(UARTTxBuf); 378 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 379 puts1USART(UARTCmdBuf); 380 UARTCRLF; 381 puts1USART(UARTTxBuf); 382 PORTDbits.RD0 = 0b1; //illuminate error LED 383 } 384 BufLength = sprintf(SDTXBuf,"CH%dHOME,",itr_p+1); 385 BytesWritten = FSfwrite((void*)SDTXBuf,1,8,ptrSDFile); 386 HeadlineSZ += BytesWritten; 387 if(BytesWritten!=8) 388 { 389 UARTCRLF; 390 puts1USART(UARTTxBuf); 391 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 392 puts1USART(UARTCmdBuf); 393 UARTCRLF; 394 puts1USART(UARTTxBuf); 395 PORTDbits.RD0 = 0b1; //illuminate error LED 396 } 397 BufLength = sprintf(SDTXBuf,"-,"); 398 BytesWritten = FSfwrite((void*)SDTXBuf,1,2,ptrSDFile); 399 HeadlineSZ += BytesWritten; 400 if(BytesWritten!=2)

193 401 { 402 UARTCRLF; 403 puts1USART(UARTTxBuf); 404 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 405 puts1USART(UARTCmdBuf); 406 UARTCRLF; 407 puts1USART(UARTTxBuf); 408 PORTDbits.RD0 = 0b1; //illuminate error LED 409 } 410 BufLength = sprintf(SDTXBuf,"-,"); 411 BytesWritten = FSfwrite((void*)SDTXBuf,1,2,ptrSDFile); 412 HeadlineSZ += BytesWritten; 413 if(BytesWritten!=2) 414 { 415 UARTCRLF; 416 puts1USART(UARTTxBuf); 417 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 418 puts1USART(UARTCmdBuf); 419 UARTCRLF; 420 puts1USART(UARTTxBuf); 421 PORTDbits.RD0 = 0b1; //illuminate error LED 422 } 423 BufLength = sprintf(SDTXBuf,"-,"); 424 BytesWritten = FSfwrite((void*)SDTXBuf,1,2,ptrSDFile); 425 HeadlineSZ += BytesWritten; 426 if(BytesWritten!=2) 427 { 428 UARTCRLF; 429 puts1USART(UARTTxBuf); 430 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 431 puts1USART(UARTCmdBuf); 432 UARTCRLF; 433 puts1USART(UARTTxBuf); 434 PORTDbits.RD0 = 0b1; //illuminate error LED 435 } 436 BufLength = sprintf(SDTXBuf,"-,"); 437 BytesWritten = FSfwrite((void*)SDTXBuf,1,2,ptrSDFile); 438 HeadlineSZ += BytesWritten; 439 if(BytesWritten!=2) 440 { 441 UARTCRLF; 442 puts1USART(UARTTxBuf); 443 BufLength = sprintf(UARTCmdBuf,"HEADLINE MISWRITE"); 444 puts1USART(UARTCmdBuf); 445 UARTCRLF; 446 puts1USART(UARTTxBuf); 447 PORTDbits.RD0 = 0b1; //illuminate error LED 448 } 449 } 450 itr_p=0; //clear itr_p so it can be used by other functions 451 return HeadlineSZ; 452 } 453 454 void PutDataLogSD(struct datalog DataLog,FSFILE *ptrSDFile) 455 { 456 unsigned int itr_p = 0; 457 int BytesWritten = 0; 458 extern unsigned int buf_length; 459 extern unsigned char UARTTxBuf[12]; 460 extern unsigned char UARTCmdBuf[19]; 461 462 BytesWritten = FSfwrite(&DataLog.TimeStamp,1,4,ptrSDFile); 463 if(BytesWritten!=4) 464 { 465 UARTCRLF;

194 466 puts1USART(UARTTxBuf); 467 buf_length = sprintf(UARTCmdBuf,"DATALOG MISWRITE"); 468 puts1USART(UARTCmdBuf); 469 UARTCRLF; 470 puts1USART(UARTTxBuf); 471 PORTDbits.RD0 = 0b1; //illuminate error LED 472 } 473 BytesWritten = FSfwrite(&DataLog.Drive24V,1,2,ptrSDFile); 474 if(BytesWritten!=2) 475 { 476 UARTCRLF; 477 puts1USART(UARTTxBuf); 478 buf_length = sprintf(UARTCmdBuf,"DATALOG MISWRITE"); 479 puts1USART(UARTCmdBuf); 480 UARTCRLF; 481 puts1USART(UARTTxBuf); 482 PORTDbits.RD0 = 0b1; //illuminate error LED 483 } 484 for(itr_p=0;itr_p<=7;itr_p++) 485 { 486 BytesWritten = FSfwrite(&DataLog.ch[itr_p].PhaseA,1,2,ptrSDFile); 487 if(BytesWritten!=2) 488 { 489 UARTCRLF; 490 puts1USART(UARTTxBuf); 491 buf_length = sprintf(UARTCmdBuf,"DATALOG MISWRITE"); 492 puts1USART(UARTCmdBuf); 493 UARTCRLF; 494 puts1USART(UARTTxBuf); 495 PORTDbits.RD0 = 0b1; //illuminate error LED 496 } 497 BytesWritten = FSfwrite(&DataLog.ch[itr_p].PhaseB,1,2,ptrSDFile); 498 if(BytesWritten!=2) 499 { 500 UARTCRLF; 501 puts1USART(UARTTxBuf); 502 buf_length = sprintf(UARTCmdBuf,"DATALOG MISWRITE"); 503 puts1USART(UARTCmdBuf); 504 UARTCRLF; 505 puts1USART(UARTTxBuf); 506 PORTDbits.RD0 = 0b1; //illuminate error LED 507 } 508 BytesWritten = FSfwrite(&DataLog.ch[itr_p].StepCount,1,2,ptrSDFile); 509 if(BytesWritten!=2) 510 { 511 UARTCRLF; 512 puts1USART(UARTTxBuf); 513 buf_length = sprintf(UARTCmdBuf,"DATALOG MISWRITE"); 514 puts1USART(UARTCmdBuf); 515 UARTCRLF; 516 puts1USART(UARTTxBuf); 517 PORTDbits.RD0 = 0b1; //illuminate error LED 518 } 519 BytesWritten = FSfwrite(&DataLog.ch[itr_p].limits,1,1,ptrSDFile); 520 if(BytesWritten!=1) 521 { 522 UARTCRLF; 523 puts1USART(UARTTxBuf); 524 buf_length = sprintf(UARTCmdBuf,"DATALOG MISWRITE"); 525 puts1USART(UARTCmdBuf); 526 UARTCRLF; 527 puts1USART(UARTTxBuf); 528 PORTDbits.RD0 = 0b1; //illuminate error LED 529 } 530 }

195 531 itr_p = 0; //clear itr_p so it can be used by other functions 532 } 533 534 unsigned int DescribeDataLogSD(FSFILE *ptrSDFile) 535 { 536 unsigned int itr_p = 0; 537 unsigned int DataLogEL = 0; 538 int BytesWritten = 0; 539 unsigned char DataLogElementSize = 0; 540 struct datalog TestDL; 541 extern unsigned int buf_length; 542 extern unsigned char UARTTxBuf[12]; 543 extern unsigned char UARTCmdBuf[19]; 544 545 BytesWritten = FSfwrite(&TestDL.TimeStamp,1,4,ptrSDFile); 546 if(BytesWritten!=4) 547 { 548 UARTCRLF; 549 puts1USART(UARTTxBuf); 550 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 551 puts1USART(UARTCmdBuf); 552 UARTCRLF; 553 puts1USART(UARTTxBuf); 554 PORTDbits.RD0 = 0b1; //illuminate error LED 555 } 556 DataLogEL++; 557 DataLogElementSize = (unsigned char)BytesWritten; 558 FSfseek(ptrSDFile,-BytesWritten,SEEK_CUR); 559 BytesWritten = FSfwrite(&DataLogElementSize,1,1,ptrSDFile); 560 if(BytesWritten!=1) 561 { 562 UARTCRLF; 563 puts1USART(UARTTxBuf); 564 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 565 puts1USART(UARTCmdBuf); 566 UARTCRLF; 567 puts1USART(UARTTxBuf); 568 PORTDbits.RD0 = 0b1; 569 } 570 BytesWritten = FSfwrite(&TestDL.Drive24V,1,2,ptrSDFile); 571 if(BytesWritten!=2) 572 { 573 UARTCRLF; 574 puts1USART(UARTTxBuf); 575 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 576 puts1USART(UARTCmdBuf); 577 UARTCRLF; 578 puts1USART(UARTTxBuf); 579 PORTDbits.RD0 = 0b1; //illuminate error LED 580 } 581 DataLogEL++; 582 DataLogElementSize = (unsigned char)BytesWritten; 583 FSfseek(ptrSDFile,-BytesWritten,SEEK_CUR); 584 BytesWritten = FSfwrite(&DataLogElementSize,1,1,ptrSDFile); 585 if(BytesWritten!=1) 586 { 587 UARTCRLF; 588 puts1USART(UARTTxBuf); 589 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 590 puts1USART(UARTCmdBuf); 591 UARTCRLF; 592 puts1USART(UARTTxBuf); 593 PORTDbits.RD0 = 0b1; 594 } 595 for(itr_p=0;itr_p<=7;itr_p++)

196 596 { 597 BytesWritten = FSfwrite(&TestDL.ch[itr_p].PhaseA,1,2,ptrSDFile); 598 if(BytesWritten!=2) 599 { 600 UARTCRLF; 601 puts1USART(UARTTxBuf); 602 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 603 puts1USART(UARTCmdBuf); 604 UARTCRLF; 605 puts1USART(UARTTxBuf); 606 PORTDbits.RD0 = 0b1; //illuminate error LED 607 } 608 DataLogEL++; 609 DataLogElementSize = (unsigned char)BytesWritten; 610 FSfseek(ptrSDFile,-BytesWritten,SEEK_CUR); 611 BytesWritten = FSfwrite(&DataLogElementSize,1,1,ptrSDFile); 612 if(BytesWritten!=1) 613 { 614 UARTCRLF; 615 puts1USART(UARTTxBuf); 616 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 617 puts1USART(UARTCmdBuf); 618 UARTCRLF; 619 puts1USART(UARTTxBuf); 620 PORTDbits.RD0 = 0b1; 621 } 622 BytesWritten = FSfwrite(&TestDL.ch[itr_p].PhaseB,1,2,ptrSDFile); 623 if(BytesWritten!=2) 624 { 625 UARTCRLF; 626 puts1USART(UARTTxBuf); 627 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 628 puts1USART(UARTCmdBuf); 629 UARTCRLF; 630 puts1USART(UARTTxBuf); 631 PORTDbits.RD0 = 0b1; //illuminate error LED 632 } 633 DataLogEL++; 634 DataLogElementSize = (unsigned char)BytesWritten; 635 FSfseek(ptrSDFile,-BytesWritten,SEEK_CUR); 636 BytesWritten = FSfwrite(&DataLogElementSize,1,1,ptrSDFile); 637 if(BytesWritten!=1) 638 { 639 UARTCRLF; 640 puts1USART(UARTTxBuf); 641 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 642 puts1USART(UARTCmdBuf); 643 UARTCRLF; 644 puts1USART(UARTTxBuf); 645 PORTDbits.RD0 = 0b1; 646 } 647 BytesWritten = FSfwrite(&TestDL.ch[itr_p].StepCount,1,2,ptrSDFile); 648 if(BytesWritten!=2) 649 { 650 UARTCRLF; 651 puts1USART(UARTTxBuf); 652 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 653 puts1USART(UARTCmdBuf); 654 UARTCRLF; 655 puts1USART(UARTTxBuf); 656 PORTDbits.RD0 = 0b1; //illuminate error LED 657 } 658 DataLogEL++; 659 DataLogElementSize = (unsigned char)BytesWritten; 660 FSfseek(ptrSDFile,-BytesWritten,SEEK_CUR);

197 661 BytesWritten = FSfwrite(&DataLogElementSize,1,1,ptrSDFile); 662 if(BytesWritten!=1) 663 { 664 UARTCRLF; 665 puts1USART(UARTTxBuf); 666 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 667 puts1USART(UARTCmdBuf); 668 UARTCRLF; 669 puts1USART(UARTTxBuf); 670 PORTDbits.RD0 = 0b1; 671 } 672 BytesWritten = FSfwrite(&TestDL.ch[itr_p].limits,1,1,ptrSDFile); 673 if(BytesWritten!=1) 674 { 675 UARTCRLF; 676 puts1USART(UARTTxBuf); 677 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 678 puts1USART(UARTCmdBuf); 679 UARTCRLF; 680 puts1USART(UARTTxBuf); 681 PORTDbits.RD0 = 0b1; //illuminate error LED 682 } 683 DataLogEL++; 684 DataLogElementSize = 0; //0 element size indicates this byte is taken bitwise 685 FSfseek(ptrSDFile,-BytesWritten,SEEK_CUR); 686 BytesWritten = FSfwrite(&DataLogElementSize,1,1,ptrSDFile); 687 if(BytesWritten!=1) 688 { 689 UARTCRLF; 690 puts1USART(UARTTxBuf); 691 buf_length = sprintf(UARTCmdBuf,"DESC MISWRITE"); 692 puts1USART(UARTCmdBuf); 693 UARTCRLF; 694 puts1USART(UARTTxBuf); 695 PORTDbits.RD0 = 0b1; 696 } 697 } 698 itr_p = 0; //clear itr_p so it can be used by other functions 699 return DataLogEL; 700 }

B.2.3 main.h

1 #ifndef MAIN_H 2 #define MAIN_H 3 //Michael Daniel : 4 February 2012 : EECE 7990 4 //Salvo Application Code Version 2.00.000 5 6 //definitions: 7 #define UARTCRLF buf_length = sprintf(UARTTxBuf,"\r\n") 8 #define UARTTx puts1USART(UARTTxBuf) 9 #define ADC_CONFIG (ADC_FOSC_32 & ADC_RIGHT_JUST & ADC_4_TAD) 10 #define ADC_CONFIG2 (ADC_CH0 & ADC_INT_OFF & ADC_REF_VDD_VSS) 11 #define ADC_PORTCONFIG (ADC_15ANA) 12 #define USART1_CONFIG (USART_TX_INT_OFF & USART_RX_INT_ON & USART_ASYNCH_MODE & USART_EIGHT_BIT\ 13 & USART_CONT_RX & USART_BRGH_HIGH & USART_ADDEN_OFF) 14 #define USART1_SPBRG 86 15 #define USART1_BAUDCONFIG (BAUD_16_BIT_RATE & BAUD_WAKEUP_OFF & BAUD_AUTO_OFF) 16 #define TMR0_CONFIG (TIMER_INT_OFF & T0_16BIT & T0_SOURCE_EXT & T0_EDGE_RISE & T0_PS_1_1) 17 #define TMR1_CONFIG (TIMER_INT_OFF & T1_16BIT_RW & T1_SOURCE_EXT & T1_PS_1_1 & T1_SYNC_EXT_OFF) 18 #define TMR3_CONFIG (TIMER_INT_ON & T3_16BIT_RW & T3_SOURCE_INT & T3_PS_1_8) 19 #define TMR3_LOAD 0x85EF 20 #define SizeOfMsgQDataLog 5

198 21 #define BinSemPutDataLog OSECBP(1) 22 #define BinSemUART OSECBP(2) 23 #define BinSemTerminal OSECBP(3) 24 #define BinSemSDCard OSECBP(4) 25 #define MsgQDataLog OSECBP(5) 26 #define MsgQDataLogCB OSMQCBP(1) 27 28 //unions, structs, typedefs, enums: 29 struct bitfield 30 { 31 unsigned char Direction : 1; //LSB 32 unsigned char CWlimit : 1; 33 unsigned char CCWlimit : 1; 34 unsigned char HOMElimit : 1; 35 unsigned char : 4; //MSB 36 }; 37 38 struct channel 39 { 40 unsigned int PhaseA; 41 unsigned int PhaseB; 42 unsigned int StepCount; 43 struct bitfield limits; 44 }; 45 46 struct datalog 47 { 48 unsigned long TimeStamp; 49 unsigned int Drive24V; 50 struct channel ch[8]; 51 }; 52 53 //function prototypes: 54 //void InitPeripherals(void); 55 //void PutVersionSD(unsigned char *Version,FSFILE *ptrSDFile) 56 //void PutInitialDateStampSD(unsigned char *InitDS,FSFILE *ptrSDFile); 57 //void PutInitialTimeStampSD(unsigned char *InitTS,FSFILE *ptrSDFile); 58 //void PutMediaRollOverSD(unsigned int MediaRO,FSFILE *ptrSDFile); 59 //void PutHeadlineSizeSD(unsigned int HeadlineSZ,FSFILE *ptrSDFile); 60 //void PutDataLogSizeSD(unsigned int DataLogSZ,FSFILE *ptrSDFile); 61 //void PutDescriptorSizeSD(unsigned int DataLogEL,FSFILE *ptrSDFile); 62 //unsigned int PutHeadlineSDASCII(FSFILE *ptrSDFile); 63 //void PutDataLogSD(struct datalog DataLog,FSFILE *ptrSDFile); 64 //unsigned int DescribeDataLogSD(FSFILE *ptrSDFile); 65 66 #endif

B.2.4 salvocfg.h

1 #ifndef SALVOCFG_H 2 #define SALVOCFG_H 3 //Michael Daniel : 4 February 2012 : EECE 7990 4 //Salvo Application Code Version 2.00.000 5 6 //Library configuration: 7 #define OSUSE_LIBRARY TRUE 8 #define OSLIBRARY_TYPE OSF 9 #define OSLIBRARY_GLOBALS OSF 10 #define OSLIBRARY_CONFIG OSA 11 #define OSLIBRARY_VARIANT OSNONE 12 13 //Task, event, and message configuration: 14 #define OSEVENTS 5

199 15 #define OSEVENT_FLAGS 0 16 #define OSMESSAGES 0 17 #define OSMESSAGE_QUEUES 1 18 #define OSTASKS 3 19 20 #endif

B.3 Binary to ASCII Converter Source Code

This section contains the single source code file used to build the executable (using MS Vi- sual Studio) for converting binary data log files to human-readable text files.

B.3.1 DataLogBin2ASCIIVer4.c

1 //Michael Daniel : 13 January 2012 : Data Log Binary to ASCII Converter 2 //Version 4.0 : View ReadMe for version details 3 //Main Source Code File 4 5 //Begin with preprocessor directives: 6 #include 7 #include 8 #include 9 #include 10 11 //Definitions: 12 #define BINFILENAME "DATALOG.bin" 13 #define TXTFILENAME "DATALOG.txt" 14 15 //Structures, enumerations, unions, type definitions, etc... 16 typedef unsigned char int8; 17 18 //Begin main: 19 int main(void) 20 { 21 FILE *pBINFile; //declare and initialize variables, including file pointers 22 FILE *pTXTFile; 23 int8 DescriptorArray[500] = {0}; 24 int8 BitWiseTemp[8]; 25 int8 DataLogTemp1 = 0; 26 short int k = 0; 27 unsigned char SoftwareVersion[14] = {0}; 28 unsigned char InitialDateStamp[10] = {0}; 29 unsigned char InitialTimeStamp[9] = {0}; 30 unsigned char HeadlineTemp = 0; 31 unsigned long DataLogTemp4 = 0; 32 unsigned long StartPos = 0; 33 unsigned long EndPos = 0; 34 unsigned long BinaryFileSize = 0; 35 unsigned long MediaRollOverCount = 0; 36 unsigned long DataLogCount = 0; 37 unsigned long BinaryDataLogSize = 0; 38 unsigned long BinaryHeadlineSize = 0; 39 unsigned long BinaryDescriptorSize = 0; 40 unsigned long ElementSeek = 0; 41 unsigned long j = 0; 42 unsigned short DataLogTemp2 = 0; 43 unsigned short i = 0; 44 float test = 0;

200 45 unsigned short BytesRead = 0; 46 unsigned short BytesWritten = 0; 47 48 pBINFile = fopen(BINFILENAME,"rb"); //open binary file 49 if(pBINFile == NULL) 50 { 51 return 1; //error opening file 52 } 53 BytesRead = fread(&SoftwareVersion,1,13,pBINFile); //read the MCU software version 54 BytesRead = fread(&InitialDateStamp,1,9,pBINFile); //read the initial date stamp 55 BytesRead = fread(&InitialTimeStamp,1,8,pBINFile); //read the initial time stamp 56 BytesRead = fread(&MediaRollOverCount,1,2,pBINFile); //read the rollover bytes 57 BytesRead = fread(&BinaryHeadlineSize,1,2,pBINFile); //read the headline size bytes 58 BytesRead = fread(&BinaryDataLogSize,1,2,pBINFile); //read the data log size 59 BytesRead = fread(&BinaryDescriptorSize,1,2,pBINFile); //read the descriptor size 60 fseek(pBINFile,(25+BinaryDescriptorSize+BinaryHeadlineSize),SEEK_SET); 61 StartPos = ftell(pBINFile); 62 fseek(pBINFile,0,SEEK_END); 63 EndPos = ftell(pBINFile); 64 fseek(pBINFile,0,SEEK_SET); //reset to the beginning of binary file 65 BinaryFileSize = EndPos - StartPos; 66 DataLogCount = (BinaryFileSize/BinaryDataLogSize)-1; //calculate number of data logs in binary file 67 fseek(pBINFile,38,SEEK_SET); //skip past the first bytes of binary file to the descriptor block 68 for(j=1;j<=BinaryDescriptorSize;j++) //read the descriptor block to the descriptor array 69 { 70 BytesRead = fread(&DescriptorArray[j],1,1,pBINFile); 71 } 72 fseek(pBINFile,0,SEEK_SET); //reset to beginning of binary file 73 fclose(pBINFile); //close binary file 74 75 pTXTFile = fopen(TXTFILENAME,"w"); //open text file 76 if(pTXTFile == NULL) 77 { 78 return 1; //error opening file 79 } 80 fprintf(pTXTFile,"SoftwareVersion = %s\r\n",SoftwareVersion); //print MCU software version to text file 81 printf("SoftwareVersion = %s\r\n",SoftwareVersion); //print MCU software version to console 82 fprintf(pTXTFile,"InitialDateStamp = %s\r\n",InitialDateStamp); //print initial date stamp to text file 83 printf("InitialDateStamp = %s\r\n",InitialDateStamp); //print initial date stamp to console 84 fprintf(pTXTFile,"InitialTimeStamp = %s\r\n",InitialTimeStamp); 85 printf("InitialTimeStamp = %s\r\n",InitialTimeStamp); 86 fprintf(pTXTFile,"MediaRollOverCount = %u\r\n",MediaRollOverCount); //print rollover to text file 87 printf("MediaRollOverCount = %u\r\n",MediaRollOverCount); //print rollover to console 88 fprintf(pTXTFile,"BinaryFileSize = %u bytes\r\n",BinaryFileSize); 89 printf("BinaryFileSize = %u bytes\r\n",BinaryFileSize); 90 fprintf(pTXTFile,"BinaryDataLogSize = %u bytes\r\n",BinaryDataLogSize); //print record size to text file 91 printf("BinaryDataLogSize = %u bytes\r\n",BinaryDataLogSize); //print record size to console 92 fprintf(pTXTFile,"DataLogCount = %u\r\n",DataLogCount); 93 printf("DataLogCount = %u\r\n",DataLogCount); 94 fprintf(pTXTFile,"BinaryDescriptorSize = %u bytes\r\n",BinaryDescriptorSize); 95 printf("BinaryDescriptorSize = %u bytes\r\n",BinaryDescriptorSize); 96 fclose(pTXTFile); 97 98 for(j=0;j

201 110 if(pTXTFile == NULL) 111 { 112 return 1; 113 } 114 BytesWritten = fprintf(pTXTFile,"%c",HeadlineTemp); 115 fclose(pTXTFile); 116 } 117 118 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 119 if(pTXTFile == NULL) 120 { 121 return 1; 122 } 123 BytesWritten = fprintf(pTXTFile,"\r\n"); //CRLF 124 fclose(pTXTFile); 125 126 BytesWritten = printf("Converting...\r\n"); 127 128 for(j=0;j4) //check valid entry in descriptor array 145 { 146 return 1; 147 } 148 if(DescriptorArray[i]!=0) //if valid non-zero entry 149 { 150 switch (DescriptorArray[i]) 151 { 152 case 0x04: 153 BytesRead = fread(&DataLogTemp4,1,4,pBINFile); //read the number of bytes specified in descriptor array 154 fclose(pBINFile); //close the binary file 155 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 156 if(pTXTFile == NULL) 157 { 158 return 1; //error opening file 159 } 160 BytesWritten = fprintf(pTXTFile,"%u,",DataLogTemp4); //write element to file 161 fclose(pTXTFile); 162 DataLogTemp4 = 0; //clear the temporary data log variable 163 break; 164 case 0x03: //note that this compiler does not support three byte ints, but C18 does. 165 BytesRead = fread(&DataLogTemp4,1,3,pBINFile); //read the number of bytes specified in descriptor array 166 fclose(pBINFile); //close the binary file 167 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 168 if(pTXTFile == NULL) 169 { 170 return 1; //error opening file 171 } 172 BytesWritten = fprintf(pTXTFile,"%u,",DataLogTemp4); //write element to file 173 fclose(pTXTFile); 174 DataLogTemp4 = 0; //clear the temporary data log variable

202 175 break; 176 case 0x02: 177 BytesRead = fread(&DataLogTemp2,1,2,pBINFile); //read the number of bytes specified in descriptor array 178 fclose(pBINFile); //close the binary file 179 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 180 if(pTXTFile == NULL) 181 { 182 return 1; //error opening file 183 } 184 BytesWritten = fprintf(pTXTFile,"%u,",DataLogTemp2); //write element to file 185 fclose(pTXTFile); 186 DataLogTemp2 = 0; //clear the temporary data log variable 187 break; 188 case 0x01: 189 BytesRead = fread(&DataLogTemp1,1,1,pBINFile); //read the number of bytes specified in descriptor array 190 fclose(pBINFile); //close the binary file 191 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 192 if(pTXTFile == NULL) 193 { 194 return 1; //error opening file 195 } 196 BytesWritten = fprintf(pTXTFile,"%u,",DataLogTemp1); //write element to file 197 fclose(pTXTFile); 198 DataLogTemp1 = 0; //clear the temporary data log variable 199 break; 200 default: 201 fclose(pTXTFile); 202 return 1; 203 break; 204 } 205 } 206 else //if zero valued descriptor array entry 207 { 208 BytesRead = fread(&DataLogTemp1,1,1,pBINFile); //read one byte 209 fclose(pBINFile); 210 BitWiseTemp[0] = (DataLogTemp1 & 0x01); //read the bitwise elements 211 BitWiseTemp[1] = (DataLogTemp1 & 0x02)>>1; 212 BitWiseTemp[2] = (DataLogTemp1 & 0x04)>>2; 213 BitWiseTemp[3] = (DataLogTemp1 & 0x08)>>3; 214 BitWiseTemp[4] = (DataLogTemp1 & 0x10)>>4; 215 BitWiseTemp[5] = (DataLogTemp1 & 0x20)>>5; 216 BitWiseTemp[6] = (DataLogTemp1 & 0x40)>>6; 217 BitWiseTemp[7] = (DataLogTemp1 & 0x80)>>7; 218 DataLogTemp1 = 0; 219 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 220 if(pTXTFile == NULL) 221 { 222 return 1; //error opening file 223 } 224 for(k=0;k<8;k++) 225 { 226 BytesWritten = fprintf(pTXTFile,"%u,",BitWiseTemp[k]); //print each bitwise element to the file. 227 BitWiseTemp[k] = 0; 228 } 229 fclose(pTXTFile); 230 } 231 } 232 pTXTFile = fopen(TXTFILENAME,"a"); //open text file 233 if(pTXTFile == NULL) 234 { 235 return 1; 236 } 237 fprintf(pTXTFile,"\r\n"); 238 fclose(pTXTFile); 239 test = ((((float)j+1)/(float)DataLogCount)*(float)100);

203 240 printf("%2.1f%s\r",test,"% complete"); 241 ElementSeek = 0; 242 } 243 fclose(pTXTFile); //close text file 244 printf("\r\nConversion complete.\r\nPress any key to exit.\r\n"); 245 getchar(); //pause here for key stroke to exit console 246 247 return 0; 248 }

B.4 MATLAB Files

This section contains a single *.m file used to validate the motor step counting approxima- tion used in this application.

B.4.1 StepCountingApproximation.m

1 %Michael Daniel : EECE 7990 : Motor Step Counting Approximation 2 3 clear all; 4 close all; 5 clc; 6 7 %define a time vector over which motors accelerate: 8 deltat = .001; 9 tfinal = 60; 10 t = 0:deltat:tfinal; %time vector in seconds 11 12 %define the constant acceleration: 13 acc = 200.*ones(1,50000); %steps per second per second 14 a = [acc,zeros(1,10001)]; %acceleration holds velocity to 10 ksteps/second 15 16 %define the step velocity and accumulated steps: 17 v = zeros(1,60001); 18 s = zeros(1,60001); 19 for i=2:60001 20 v(1,i) = v(1,i-1)+a(1,i)*deltat; 21 s(1,i) = s(1,i-1)+v(1,i-1)*deltat+(0.5*a(1,i)*(deltat^2)); 22 end 23 24 %plot the results: 25 figure(1); 26 subplot(3,1,1); 27 plot(t,s); 28 grid on 29 axis([0 60 0 450000]) 30 title(’Motor Position over Time’); 31 ylabel(’Position in Steps’); 32 xlabel(’Time in Seconds’); 33 subplot(3,1,2); 34 plot(t,v); 35 grid on 36 axis([0 60 0 12000]) 37 title(’Motor Velocity over Time’); 38 ylabel(’Velocity in Steps per Second’); 39 xlabel(’Time in Seconds’) 40 subplot(3,1,3); 41 plot(t,a);

204 42 grid on 43 axis([0 60 0 225]) 44 title(’Motor Acceleration over Time’); 45 ylabel(’Acceleration in Steps per Second Squared’); 46 xlabel(’Time in Seconds’)

205 Bibliography

[1] Parker Hannifin Corporation. Engineering reference a: Application examples. http://www.parkermotion.com/catalog/cataloga/A72-A96.pdf, 2005.

[2] Michael Daniel and Michael Taranowski. A microcontroller-based system for diagnosing stepper motors at the national ignition facility. In Research and Industrial Collaboration Conference 2011, 2011.

[3] Agus Purwadi et al. Prototype development of a low cost data logger for pv based led street lighting system. In International Conference on Electrical Engineering and Informatics, 2011.

[4] Sylvia Goldsmith. A Practical Guide to Real-Time Systems Development. Prentice Hall, 1993.

[5] Dogan Ibrahim. Advanced PIC Microcontroller Projects in C: From USB to RTOS with the PIC18F Series. Newnes, 2008.

[6] Dogan Ibrahim. Microcontroller and sd-card based multichannel data logger. Electronics World, November 2008.

[7] Dogan Ibrahim. Gps data logger with sd card storage and google earth map interface. Electronics World, November 2009.

[8] National Instruments. Advantages of pc-based data logging. http://www.ni.com/data logger/advantages.htm, 2012.

[9] National Instruments. Data loggers. http://www.ni.com/data logger/, 2012.

[10] National Instruments. What is data logging? http://www.ni.com/data log- ger/whatis.htm, 2012.

[11] Lawrence Livermore National Laborartory. The people of nif: Rod saunders, each day is an adventure. https://lasers.llnl.gov/about/people/people of nif/rod saunders.php.

[12] Lawrence Livermore National Laboratory. How nif works. https://lasers.llnl.gov/about/nif/how nif works/index.php.

206 [13] Lawrence Livermore National Laboratory. Nif: The “crown joule” of laser science. https://lasers.llnl.gov/about/nif/about.php. [14] Lawrence Livermore National Laboratory. Project status - 2011. https://lasers.llnl.gov/about/missions/. [15] Lawrence Livermore National Laboratory. Project status - 2011. https://lasers.llnl.gov/newsroom/project status/index.php. [16] Lawrence Livermore National Laboratory. Target fabrication. https://lasers.llnl.gov/programs/nic/target fabrication.php. [17] Phillip A. LaPlante. Real-Time Systems Design and Analysis: An Engineer’s Handbook. Newnes, 2004. [18] Inc Microchip Technology. Pic18f8722 family data sheet. http://ww1.microchip.com/downloads/en/DeviceDoc/39646c.pdf, 2008. [19] Ned Mohan. Electric Machines and Drives: A First Course. Wiley, 2012. [20] B. Nkom and H. Musa. Development of a novel microcontroller-based data logger. In 2nd International Conference on Adaptive Science and Technology, pages 314–324, 2009. [21] Mark Panzer and Gang Feng. Pic microcontroller data acquisition system. www.uwplatt.edu/ fengg/378 FinalProj/EE378-final-proj-Report.doc, 2005. [22] Inc Pumpkin. Salvo user manual. http://www.pumpkininc.com/content/doc/ man- ual/SalvoUserManual.pdf, 2010. [23] Peter Reen and Naveen Mohanswamy. Implementing file i/o functions using microchip’s memory disk drive file system. http://www.microchip.com/stellent/idcplg?IdcService=SS GET PAGE nodeId=1824 appnote=en532040, 2008. [24] Mulukutla S. Sarma. Electric Machines: Steady State Theory and Dynamic Perfor- mance. West, 1994. [25] Computer Aided Solutions. Data loggers and data acquisition systems. http://www.dataloggerinc.com/, 2012. [26] Chris Valenti and Andrew Kalman. Multi-tasking on the pic16f877 with the salvo rtos. http://www.microchip.com/stellent/idcplg?IdcService=SS GET PAGE nodeId=1824 appnote=en011944, 2001. [27] Paul VanArsdall. Nif’s computer control system. Science and Technology Review, November 1998. [28] Tim Wilmhurst. Designing Embedded Systems with PIC Microcontrollers: Principles and Applications. Newnes, 2010.

207