ECE 477 Final Report Spring 2009Team 4 das Autotünr
Team Members:
#1: Joe Blubaugh Signature: ____________________ Date: _________
#2: Diana Mui Signature: ____________________ Date: _________
#3: David Sutherland Signature: ____________________ Date: _________
#4: Matthew Swallow Signature: ____________________ Date: _________
CRITERION SCORE MPY PTSTechnical content 0 1 2 3 4 5 6 7 8 9 10 3Design documentation 0 1 2 3 4 5 6 7 8 9 10 3Technical writing style 0 1 2 3 4 5 6 7 8 9 10 2Contributions 0 1 2 3 4 5 6 7 8 9 10 1Editing 0 1 2 3 4 5 6 7 8 9 10 1Comments: TOTAL
ECE 477 Final Report Spring 2009
TABLE OF CONTENTS
Abstract 1
1.0 Project Overview and Block Diagram 1
2.0 Team Success Criteria and Fulfillment 3
3.0 Constraint Analysis and Component Selection 4
4.0 Patent Liability Analysis 10
5.0 Reliability and Safety Analysis 13
6.0 Ethical and Environmental Impact Analysis 16
7.0 Packaging Design Considerations 20
8.0 Schematic Design Considerations 23
9.0 PCB Layout Design Considerations 27
10.0 Software Design Considerations 30
11.0 Version 2 Changes 36
12.0 Summary and Conclusions 36
13.0 References 37
Appendix A: Individual Contributions A-1
Appendix B: Packaging B-1
Appendix C: Schematic C-1
Appendix D: PCB Layout Top and Bottom Copper D-1
Appendix E: Parts List Spreadsheet E-1
Appendix F: Software Listing F-1
Appendix G: FMECA Worksheet G-1
-ii-
ECE 477 Final Report Spring 2009
Abstractdas Autotünr is an automatic guitar tuning and music transcription system that will bring ease
to musicians by providing a quick and easy way to tune their instrument. It has preset and
user-customizable tunings in order to quickly retune the guitar for different songs. The music
transcription system will allow users to store anything they play to a USB mass storage
device as a MIDI file.
1.0 Project Overview and Block Diagram1.1 Design/Functionality Overview
das Autotünr has two modes of operation: tuning mode and transcription mode.
Figure 1-1. Motor Assembly and User Console
-1-
ECE 477 Final Report Spring 2009
Tuning mode allows the user to automatically tune their guitar to a default or stored
tunings by simply strumming the guitar strings. This is accomplished by attaching a
motor assembly to the headstock of the guitar. The motor assembly consists of six servo
motors and a bracket to hold to motors to the guitar. The servos can be arranged in many
ways to accommodate the different peg configurations of guitar headstocks. The servos
are also fitted with a peg holder which attaches to the guitar pegs for tuning. Sound data
can come from either the on-box microphone or by plugging in a stereo phono jack from
the guitar. This allows the user to tune both electric and acoustic guitars. While tuning,
the string being tuned, as well as frequency and tuning status, is displayed on the user
interface. The user is also able to program and store their custom tunings to the user
console for future use.
Transcription mode gives the user an easy way of recording the songs they play for
future use. The sound data from the guitar is recorded as a MIDI file and is saved to a
USB mass storage device. The mass storage device must be of the FAT32 format in
order for the file to be stored. About 6KB of sound data can be recorded which is around
a seven minute song. The MIDI file can then be imported into songwriting software such
as Finale for additional editing.
1.2 Block Diagram
das Autotünr takes audio data input from either a microphone or stereo phono jack. It
amplifies the audio input and feeds the amplified audio signal to the dsPIC where peak
detection frequency analysis is performed. Frequency data is then sent to the PIC24
microcontroller which sends the appropriate control signals to the motors for tuning. The
PIC24 also communicates with the USB host controller and outputs to the LED drivers
and LCD.
-2-
ECE 477 Final Report Spring 2009
Figure 1-2. Block Diagram
2.0 Team Success Criteria and Fulfillment2.1 Project-Specific Success Criteria
1. An ability to concurrently control six motors.
2. An ability to analyze the frequencies in a sound signal and compare them to desired
frequencies.
3. An ability to write music description files (MIDI) to a mass storage device.
4. An ability to display operating information including tuning status on a user
interface.
5. An ability to store user-input tuning preferences to onboard nonvolatile storage.
-3-
ECE 477 Final Report Spring 2009
2.2 Results
1. Successfully completed. All six motors are able to be controlled concurrently via
PWM signals from the microcontroller. Motors can also be driven individually.
Individual motor direction (clockwise/counter-clockwise) and speed could be
adjusted independently of the other motors.
2. Successfully completed. Frequencies in a sound signal are identified using peak
detection. The frequencies are then compared to a table of frequency values for note
identification (used for transcription). It is also used to identify which string needs to
be tuned in tuning mode.
3. Successfully completed. MIDI format data generated from note and duration data
gathered from the dsPIC. The data is sent over SPI to the USB host controller and
written as a MIDI file.
4. Successfully completed. The LCD displays the user menu and writes out status
messages in transcription mode. In tuning mode, it displays the current string
frequency, the target string frequency, and the direction the motor is turning.
5. Successfully completed. Five user-customizable tunings are built into das Autotünr.
The user can program the note value for each string and save the tuning to Flash
memory on the microcontroller.
3.0 Constraint Analysis and Component SelectionBecause of its musical nature, das Autotünr has real-time responsiveness in transcription
mode and a fast response in tuning mode as well. The tasks of tuning and transcription are
fundamentally sound analysis problems; therefore, the device has enough power to perform
signal processing techniques quickly and at a high resolution. The device also interfaces to
consumer USB storage products and requires enough power to drive six small motors.
3.1 Design Constraint Analysis
There are a number of design constraints acting on the project that were considered. The
computational requirements of signal processing algorithms necessitated a powerful
digital signal processor (DSP). General purpose input/output (GPIO) were used to power
-4-
ECE 477 Final Report Spring 2009
a user interface. Numerous on-chip peripherals, including analog to digital converters
(ADCs) and pulse-width modulators (PWMs) were also required. An external USB
controller was also needed to connect to storage devices. The power requirements
created by driving motors was addressed, as was the packaging constraints derived from
the need to connect the device to a guitar. Finally, cost constraints were also considered
by comparing das Autotünr to other tuning devices that were both simpler and more
complicated than it.
3.2 Computation Requirements
In order to tune a guitar, the product needed to be able to sample analog audio and
determine frequency characteristics of the sound very accurately. When it comes to
musical notes, the human ear can distinguish between frequencies as fine as 2 Hz apart
[1]. In addition, small imperfections in tuning can lead to an interference phenomenon
called 'beating.' For these reasons, it is essential to have a fine frequency resolution in
tuning mode. This requires a large number of samples: in the Discrete Fourier Transform
(DFT), the frequency resolution of samples is the sampling frequency divided by the
number of samples computed [2]. While zero-padding can be used to reduce the number
of samples that actually need to be taken, memory space for these zeros must still be
reserved in order for most available DFT libraries to compute correctly. In order to
obtain good tuning performance, a frequency resolution of 1 Hz or less should be used,
and a sampling rate of at least 2*440Hz or 880 Hz should be used. Consequently
memory space for at least 1024 samples should be available.
In transcription mode, absolute accuracy becomes less important, as long as notes in a
musical scale can be distinguished. Real-time performance, on the other hand, becomes
imperative so that accurate timing information can be captured. Taking these two
requirements together, it becomes apparent that both a large amount of data memory (for
fine-resolution spectrum analysis) and a fast processor core (for real-time performance)
are necessary to successfully execute the functions of the device. For this reason, it is
useful to separate the DSP functions from the 'device management' functions.
-5-
ECE 477 Final Report Spring 2009
3.3 Interface Requirements
The device will interface with a character-mode LCD to provide feedback to the user
and will accept pushbutton inputs to allow the user to control device function.
Interfacing to the LCD will require one GPIO pin and a software routine to control the
issuing of commands. It may also be possible to use a built-in serial communication
peripheral to handle this. At least four pushbuttons will be needed to allow the user to
navigate a nested menu structure, so at least four GPIO pins will be needed to handle
inputs from these. Because these are simply switches, a high-value resistor will be used
to keep sink current on these pins to a negligible value. In addition, three GPIO pins will
be used to control an RGB LED. These will essentially be software PWMs utilizing one
of our microcontroller's on-board timers. Most RGB LEDs require 2.5Vf to operate
correctly, so the remaining voltage will be dissipated by a resistive element on each line.
Since each line will be driving only one LED, amplification circuitry will not be
necessary.
3.4 On-Chip Peripheral Requirements
In order to sample audio one channel of ADC is required. In order to have fine-grained
amplitude numbers, a 10-bit or greater ADC is desired. To simultaneously drive the
servo motors that will tune the strings, six channels of PWM output are necessary. A
pair of matching serial interfaces will be needed on the DSP and microcontroller in order
to create a communication channel. In addition, two more serial interfaces will be
needed to interface with the USB host controller and the user interface LCD. Several on-
chip timers will be needed on both the DSP and the microcontroller in order to maintain
real-time communications and accurately control motors.
3.5 Off-Chip Peripheral Requirements
There was one required off-chip peripheral: a USB host controller to manage loading
MIDI files to an external USB storage device. The controller will communicate with the
microcontroller through a serial communication interface.
-6-
ECE 477 Final Report Spring 2009
3.6 Power Constraints
Because the device includes motors which consume much more power than the electronic
components, the project is A.C. powered without a battery backup. The motors required
for the project can run on unregulated DC voltage but have a maximum 6VDC source
voltage. The supply also needs to be able to provide several amperes of current in order
to drive the motors, supply power to the electronics, and supply the USB specified 500
mA of supply current. Next to this, the current draw of digital hardware is only a few
hundred milliamps. A supply should be able to provide at least 2.5 amps of current.
3.7 Packaging Constraints
The device includes a piece that can be attached to the headstock of an acoustic guitar, as
well as a second piece containing the user interface, microphone, and USB port. The
cabling connecting these pieces includes both power and control lines for the tuning
motors. The control lines need to be adequately shielded to prevent cross-talk. The
project is packaged as a single unit that the guitar is laid on or a multi-piece unit
connected by cables.
3.8 Cost Constraints
das Autotünr fills the void between two commercially available products: electronic
chromatic tuners like the Korg ABCDERF that can be found for $14.99 at jr.com and
integrated automatic tuning systems like the Gibson Robot guitar. These integrated
solutions are very expensive, adding about $800 in the Gibson guitars. das Autotünr is
intended to work with any guitar without modification and should cost less than $400 in
order to make it a compelling tool for guitar enthusiasts and technicians.
3.9 Component Selection Rationale
There are four major electronic components in this design. A microcontroller directs the
motors, sends data to a USB controller, and controls the user interface. A DSP samples
analog audio, performs a spectral analysis and communicates tuning and note information
to the microcontroller through a serial interface. A USB controller takes MIDI data sent
to it by the microcontroller and writes it to a USB storage device. An audio amplifier is
-7-
ECE 477 Final Report Spring 2009
needed to bring the low level signals from a microphone or phono cable to a higher-
voltage signal that is better for AD conversion.
3.9.1 Microcontroller
Since the microcontroller is responsible for simultaneously controlling six
separate motors, the number of PWM outputs is very important. Timer channels
were also at a premium. Serial devices for interprocessor communication were
another key feature guiding microprocessor selection. Two microcontrollers that
fit within the essential design constraints are Microchip's PIC24HJ128GP306 and
Freescale's MC9S12DT256. Each microcontroller has eight PWM channels, at
least eight timers, and at least two serial communication channels. Each also has a
number of peripherals we will not be using: ADCs and CAN interfaces are
available on both processors [3],[4].
In most categories, the PIC processor is superior. It is available with 16 kilobytes
(KB) of data memory as opposed to the MC9S12's 12 KB. It has 64 pins
compared to the MC9S12's 112 [3],[4], making it easier to solder to the board.
The PIC's unit price of $5 is very favorable compared to the MC9S12's price of
$13 [5],[6]. Microprocessor and DSP selection also influenced each other. PIC
offers DSP chips that have a very similar programming model to their
microprocessors. This shared environment helps speed software development, so
the PIC24 was selected for this design.
3.9.2 Digital Signal Processor
The decision to include a separate DSP was made because of the computationally
intensive DFT, which was needed in order to analyze the frequency spectrum of
the guitar signal. Completing this computation quickly is essential to the
responsiveness of the system. Two families of DSP were evaluated: Analog
Device's ADSP-21XX DSPs and Microchip's dsPIC family. Candidates included
the ADSP-21992 and the dsPIC33FJ64GP306.
-8-
ECE 477 Final Report Spring 2009
In terms of raw performance, the ADSP-21992 is a superior chip. It has more
available RAM, at 64 KB than the dsPIC's 16 KB. The ADSP is also capable of
operating at 160 MHz, much greater than the dsPIC's 7.5 MHz. However, the
ADSP has a number of disadvantages for this project: it has 176 leads, requires an
external clock circuit, and costs $30 per chip [7]. The dsPIC, which still meets our
computational needs, is a 64 lead device with an internal oscillator [8] and a unit
price of $3.81 [9]. It also shares a development environment with the PIC24
microprocessor discussed above. This combination of factors led to the choice of
the dsPIC as the project's DSP.
3.9.3 USB Interface Component
While there are microprocessors available with USB functionality built-in as a
peripheral device, most USB interfaces are currently handled by a specialized
microprocessor or other device. Two different solutions were considered for this
component: FTDI's Vinculum USB Host Controller VDIP1 and the Cypress
Technology SL811HS. The SL811HS operates at 3.3V, the same supply voltage
as our other major components [10], while the VDIP1 requires a 5V input [11].
Both take 3.3V logic inputs, so no translation is required. The most compelling
difference between the two is that the VDIP1 has free firmware available that
implements a FAT file system interface while the SL811HS does not. Because
this reduces the software workload, the VDIP1 was selected.
3.9.4 Audio Amplifier
Signals from microphones and electric guitar cables are very low voltage, so an
amplifier is needed in order to bring the device to a level that is compatible with
our DSP's ADC. An audio amplifier is required because their circuitry is designed
to have a flat frequency response in the audible frequencies of 20 Hz – 30 KHz
and they produce very clean signals. Two candidate devices are the LM386
(available from National Semiconductor, Contek, and FCI) and the NJW1105
from New Japan Radio Company. Both are designed to operate on supply
voltages of 4 to 12 volts and do not require bipolar power supplies [12], [13]. The
-9-
ECE 477 Final Report Spring 2009
NJW1105's primary advantage is its unification of two amplifiers in one package.
It also costs more than twice as much as an LM386 and does not provide an
externally settable gain. Because of these facts, the LM386 is more suitable, even
though two of them will need to be populated and routed.
4.0 Patent Liability Analysis
There are several aspects of the device which could be at risk for patent liability. One of the
main features involves tuning the six strings of a guitar concurrently. Another main function
involves performing a frequency analysis on the incoming sound signal from the guitar to
identify notes. Information such as tuning status is displayed through a user interface. Lastly,
the device can be in transcription mode when not tuning so that it records incoming sound
data in the MIDI file format and stores the file on a USB mass storage device.
4.1 Results of Patent and Product Search
4.1.1 Automatic string instrument tuner (Patent No. 5,767,429) [14]
Filing date: November 9, 1995
Abridged Abstract: This is a tuning system for automatically tuning a stringed
musical instrument (such as guitars, harps, pianos, etc.) The tuning system tunes
the string of a musical instrument to a user-selected predetermined frequency.
Key Claims: 1 – The tuning apparatus removes predetermined signal harmonics
from the signal and converts the signal from analog to digital for processing.
2 – The signal processing is done in a central processing unit with RAM & ROM.
3 – There are musical pickup sensors which include at least one microphone.
4 – There is a plurality of motors which are driven by an electrical control signal.
The electrical control signal is a function of the difference between the incoming
signal and its reference value.
4.1.2 System and method for automatically detecting a set of fundamental frequencies
simultaneously present in an audio signal (Patent No. 6,140,568) [15]
Filing Date: November 5, 1998
Abridged Abstract: This is a method for detecting and identifying several
frequencies simultaneously present in an audio signal. It also identifies the
-10-
ECE 477 Final Report Spring 2009
duration, amplitude, and phase of the frequencies and filters out the harmonic
components to determine the fundamental frequency. This system also creates a
computer-readable instruction code that decomposes the signal into its component
sine waves.
Key Claims: 1 – This system identifies one or more fundamental frequencies
simultaneously present in a complex signal by decomposing the signal into sine
wave components and filtering our harmonic frequencies.
2 – Decomposing is carried out by obtaining correlation scores by comparing the
complex signal to the reference frequencies. One of the steps in obtaining the
correlation score involves subtracting the reference frequencies from the complex
signal.
3 – The reference frequencies are stored and utilized as a sine wave table.
4 – The system filters our harmonic frequencies by subtracting a portion of the
amplitude for multiples of the fundamental frequencies.
5 – The detected frequencies as well as their respective durations and amplitudes
are recorded as MIDI data.
4.1.3 Tuning apparatus for stringed instruments (Patent No. 4,889,029) [16]
Filing Date: September 2, 1988
Abridged Abstract: This is a guitar tuner with a slot-shaped opening in the head
used for grasping the key of an instrument such as a guitar. The head is driven by
a motor which rotates about an axis. An input sensor detects the tone of the string
of the instrument and converts it to a square wave of the detected frequency. A
microprocessor compares this frequency with the closest adjacent defined
frequency for the instrument and drives the motor accordingly. A digital user
interface is provided to show tuning information and to allow user overrides while
tuning.
Key Claims: 1 – There is a sensor that detects the note produced by a string when
it is plucked and calculates the fundamental frequency of the note by comparing
against a table of predetermined required frequencies. The motor associated with
the string then rotates the peg clockwise or counterclockwise in response.
2 – There is a visual indicator that indicates whether the string is tuned to the
-11-
ECE 477 Final Report Spring 2009
predetermined frequency.
3 – The sensor generates from the tone of the string a rectangular wave in which
the comparator uses to compare against the required frequencies.
4 – There is a manual override so that the user can choose to tune the string to
another one of the stored frequencies.
4.2 Analysis of Patent Liability
4.2.1 Automatic string instrument tuner .
There are many different parts of the claim in which literal infringement could be
claimed. Our device uses a microphone as a pickup device and converts the
analog sound to a digital signal for processing. All of the computation is
performed on a central processing unit which has RAM and ROM, and electrical
control signals are sent out to motors for tuning. However, these functions can be
considered “obvious” since they are present in most devices, not uniquely
instrument tuners. The main difference between our device and this patent is that
we do not remove the harmonics from the sound signal during the signal
processing. Instead, we analyze the whole frequency spectrum of the signal and
identify the peaks of the signal as the fundamental frequency. This difference in
how the frequency analysis is performed shows that the same function is not
performed in substantially the same way.
4.2.2 System and method for automatically detecting a set of fundamental frequencies
simultaneously present in an audio signal.
The MIDI transcription mode of our device most infringes upon this patent. An
area in which literal infringement may occur is that we both analyze a complex
signal, meaning that the signal contains one or more notes and store the sound
data (i.e. frequency, duration, and amplitude) in a MIDI file. However, the actual
process in how the devices process the sound information for storage is very
different. Our device performs a discrete Fourier transform (DFT) on the sound
signal and uses peak detection to filter out the fundamental frequencies. This is
very similar to the patented system which decomposes the sound signal into sine
waves and filters out the harmonics by subtracting them from the sine waves. The
-12-
ECE 477 Final Report Spring 2009
frequency analysis is performed in substantially the same way.
4.2.3 Tuning apparatus for stringed instruments .
das Autotünr is extremely similar to this tuning apparatus in many aspects, which
could form a basis for literal infringement. Our device has a user console which
indicates tuning status to the user. It has a user-override mode where you can
make custom tunings for your guitar. It also compares the note from the sensor
input to a value of reference frequencies stored in a table and uses that data to
form a motor control signal for the associated peg. As with the previous patent,
there is a substantial similarity in how the frequency analysis is performed in this
apparatus. In the apparatus, the sensor generates a square wave from the input
tone of the string and feeds the square wave into a comparator to compare against
the reference frequencies. This is a different approach to our DFT/peak detection
method, but because both are methods of frequency analysis, there is an issue
under the doctrine of equivalence. Therefore, it could be said that both devices are
substantially similar.
4.3 Action Recommended
The analysis between das Autotünr and the three patents in question show that the
devices are not substantially different. There are definite similarities between das
Autotünr and all of the other devices and systems, and in many cases, literal
infringement could be inferred. There may be other patents in which our device would
infringe upon since our method of frequency analysis is used in other applications, so if
we were going to commercially distribute this product, we would need future research.
In the case of patent infringement, which is very likely for our device, we would try to
acquire a license in order to manufacture our product.
5.0 Reliability and Safety Analysis The main safety issue that concerns possible user injuries is the possibility of breaking strings
under tension. Broken strings can swing at high speeds and could cause injury if they strike a
person in the eye for example. Other safety issues concern only the health of components in
das Autotünr itself.
-13-
ECE 477 Final Report Spring 2009
5.1 Reliability Analysis
The components in das Autotünr most likely to fail are the PIC24, dsPIC33, 5V voltage
regulator, and the 3.3V voltage regulators. The PIC23 and dsPIC33 were selected as
likely to fail because of their complexity and large number of pins [3],[8]. The voltage
regulators were selected because they are the hottest running components on the board
[18],[19]. Below are tables for each of these devices explaining the calculations and
assumptions in order to find the number of failures per million hours [17].
PIC24 λp = (C1 * πT + C2 * πE) * πQ * πL
Parameter name
Description Value Comments
C1 Die Complexity 0.28 16 Bit microcontroller
πT Temperature Factor 0.35
TC = 50C, θJC = 10,
P = .29W
TJ = TC + θJC*P = 52.9
C2 Package Failure Rate 0.032 Nonhermetic SMT
πE Environment Factor 4.0Similar to handheld communications
equipment (Ground Mobile)
πQ Quality Factors 10.0 Commercial product
πL Learning Factor 1.0 In production for > 2 years
Entire design: λp = 2.26 MTTF ≈ 50 years
Table 5-1. PIC24 MTTF Analysis
dsPIC33 λp = (C1 * πT + C2 * πE) * πQ * πL
Parameter name
Description Value Comments
C1 Die Complexity 0.28 16 Bit microcontroller
πT Temperature Factor 0.35
TC = 50C, θJC = 10,
P = .29W
TJ = TC + θJC*P = 52.9
C2 Package Failure Rate 0.032 Nonhermetic SMT
πE Environment Factor 4.0Similar to handheld communications
equipment (Ground Mobile)
πQ Quality Factors 10.0 Commercial product
πL Learning Factor 1.0 In production for > 2 years
Entire design: λp = 2.26 MTTF ≈ 50 years
-14-
ECE 477 Final Report Spring 2009
Table 5-2. dsPIC33 MTTF Analysis
L4941 λp = (C1 * πT + C2 * πE) * πQ * πL
Parameter name
Description Value Comments
C1 Die Complexity .01 1 to 100 transistors
πT Temperature Factor 2.8
TC = 50C, θJC = 10,
P = 1.975W
TJ = TC + θJC*P = 69.8
C2 Package Failure Rate .0012 Nonhermetic DIP
πE Environment Factor 4.0Similar to handheld communications
equipment (Ground Mobile)
πQ Quality Factors 10.0 Commercial product
πL Learning Factor 1.0 In production for > 2 years
Entire design: λp = .328 MTTF ≈ 300 years
Table 5-3. L4941 Linear Regulator MTTF Analysis
AP1117 λp = (C1 * πT + C2 * πE) * πQ * πL
Parameter name
Description Value Comments
C1 Die Complexity .01 1 to 100 transistors
πT Temperature Factor 1.4
TC = 50C, θJC = 10,
P = 1.975W
TJ = TC + θJC*P = 58.2
C2 Package Failure Rate .0012 Nonhermetic DIP
πE Environment Factor 4.0Similar to handheld communications
equipment (Ground Mobile)
πQ Quality Factors 10.0 Commercial product
πL Learning Factor 1.0 In production for > 2 years
Entire design: λp = .188 MTTF ≈ 600 years
Table 5-4. AP1117 Linear Regulator MTTF Analysis
Based on the calculations above there is little to no concern for our “hot” parts. Both of
the linear voltage regulators have failure rates well below one failure per million hours,
the standard “acceptable” failure rate. At first glance, it would appear that the
microcontrollers do not meet acceptable failure rates. However, the calculations above
-15-
ECE 477 Final Report Spring 2009
include a quality factor of 10 due to the fact that the controllers were acquired
commercially. Both controllers would have acceptable failure rates with quality factors
of 4 or less. This is still a conservative quality factor and should not yield an inaccurate
result. Based on these conclusions it appears that the current design itself needs no
further refining with respect to reliability.
5.2 Failure Mode, Effects, and Criticality Analysis (FMECA)
das Autotünr can be divided into five connected subsystems: power, audio,
microcontroller, user Interface, and motor. Failures that are considered to be of a high
criticality rate involve the risk of user injury. The four failures listed in the FMECA
chart below that warrant high criticality are overvoltages that could cause components to
overheat and lead to fires and the loss of motor control to the point that strings are
broken. Because user safety is of utmost concern, these failure modes would be required
to have a failure rate of no more that 1 in 109 hours. Failure modes that will only cause
harm to individual components or cause inconveniences to the user are labeled as having
low criticality. Although these failures are unlikely to cause harm to the end user, das
Autotünr is expected to perform well for years to come so a failure rate of no more than
1 in 106 hours is acceptable.
6.0 Ethical and Environmental Impact Analysis
As is with any commercial product, ethical and environmental concerns are paramount to its
inception. Since our product is utilized by interfacing with the user’s own unique guitar, special
care is taken when testing the possible ways individual guitars can vary to ensure correct,
reliable and safe operation. In addition, many failsafes have been built into the design with the
intent to lessen the probability of device failure. With regards to environmental impact, thought
was put into how to minimize the inclusion of toxic substances in the design. The user also
should be informed of how to properly dispose of the device at the end of its lifetime.
6.1 Ethical Impact Analysis
When considering the ethical impact of das Autotünr, it is important to first look at how
the device will be used. The device will be attached to the headstock of a user’s guitar
-16-
ECE 477 Final Report Spring 2009
with one motor firmly attached to each tuning peg, totaling six motors. In order to
ensure the tuning assembly does not slip off the guitar, it will be secured by tightening
wing nuts on the underside of the motor brackets. Since removing the motors in case of
a failure is not feasible, there must be a failsafe to ensure that harm does not come to the
guitar or the user if the device becomes unresponsive and the motors are stuck rotating .
If the failsafe is not engaged, and if all other designed safety mechanisms have failed,
the most likely scenarios would involve breaking a guitar string, resulting in injury to
the user or causing permanent damage to the guitar itself. Since user safety is
paramount, the safety mechanisms and failsafes must be designed in such a way that the
probability this situation occurs is negligible.
The design includes many different routines to ensure the correct, safe and reliable
operation of the tuner. During normal operation, das Autotünr should tune all six strings
concurrently, but in the case of any discrepancy in the frequency detection or perceived
motor operation, the device will enter what is called “single string” tuning mode. The
screen will instruct the user to play one string at a time, starting with the lowest string.
This way, the device can more reliably detect the frequency of the string and motor
control can further be verified. If the frequency of the string does not change when the
motor is set to rotate, the device will exit tuning mode and the user will be prompted to
check the motor connections. In addition, if any of the string frequencies indicate the
guitar is being tuned beyond safe string tension levels, the tuning routine will exit. Also,
at any time during the tuning process, if the user feels that the device is operating
unsafely or that they want to halt the tuning process, they can press a button on the
control box which will stop all motor operation. Given all of these safety mechanisms,
the design of das Autotünr is certainly conscious of failures, but extensive testing must
follow to make certain the device’s proper functioning under a variety of conditions.
To this end, it is important to analyze and test the set of variables involved its intended
operation. If das Autotünr were to go into production, it might be assumed that it would
tune any guitar, but further testing would need to be performed to verify the breadth of
its capabilities. This testing would involve confirming the tuning capabilities of the
-17-
ECE 477 Final Report Spring 2009
device when used with different guitar types (acoustic and electric), string materials,
string gauges and pickups (in the case of an electric guitar). If it was found that
performance was sub-par or that it would be dangerous to use das Autotünr given any
combination of these variables, further investigation of the failure would be needed. If
the device design could be easily, quickly and unobtrusively modified, it would be a
reasonable course of action to go forward with simple changes to increase the likelihood
of operational success. If the changes would be too drastic, difficult or impossible given
the present design, this incompatibility would have to be printed on the packaging, to
prevent the purchase of a product for which the consumer would have no use. During
the course of testing, if it was shown that the number of cases where das Autotünr failed
to perform its function was unacceptably high, or if any test case resulted in high
likelihood of injury to the user, the design would require significant modifications
before it would be ready for commercial sale.
When the device passed all testing of its intended functionality, documentation must
clearly provide directions for safe operation of das Autotünr. Additionally, it should be
documented which instruments das Autotünr is not intended to be used to tune. This
device was intended to be an electric or acoustic guitar tuner and its performance will
not be validated on other similar string instruments. There should also be a warning in
the manual stating that there is a risk of string breakage resulting in personal injury and
which steps should be taken to lessen these risks. It should also be mentioned that das
Autotünr is not a toy and that it is not intended to be used by children.
Additionally, the user should be warned against opening the box, due to the possibility
of device alteration. If something were to become unplugged from the board, or if some
metallic object were to be set loose within the control box, there would be a possibility
that the device would not work or that two traces could short, causing a fire. To prevent
this possibility, there should be a warning sticker placed across the box seam stating this
box should not be opened. Also, this warning should be echoed in the user manual,
further listing the possible complications. For the same reasons stated above, if das
Autotünr were to be mass produced, the motor breakout circuit attached to the motor
-18-
ECE 477 Final Report Spring 2009
assembly should be encased in plastic to ensure no accidental shorting of traces.
6.2 Environmental Impact Analysis
In order to present an ethically-sound product, the environmental impact of the device’s
manufacture and disposal must be thoroughly considered. The foundation of an
environmentally-friendly product begins with how it is manufactured and which parts go
into its production. One of the greatest health hazards present in most electronic devices
is lead, and if ingested in sufficient quantities, can cause neurological damage, hearing
loss or even cancer [20]. It is commonly found in solder and is frequently used to create
the traces on a printed circuit board. Fortunately, there are alternatives which eliminate
the use for leaded products. Lead-free solder is frequently used by manufacturers
looking to eliminate their environmental impact. For this reason, it is seen as positive,
but it has not been adopted as an industry-standard due to its relatively-inferior
performance. This shortcoming is especially evident with regard to solder joint
reliability, a common failure mode for electronic devices [21]. Furthermore, both lead-
free solder and lead-free PCBs come at a price premium as compared to their leaded
counterparts [22]. In addition to manufacturing the PCB using lead-free processes,
RoHS approved ICs can be employed. Devices carrying this label must have less than
0.1% by weight of lead, mercury, chromium, PPBs and PBDEs and less than .01% by
weight of cadmium [23]. Another upside to using these parts is that they are frequently
priced the same as non-RoHS parts. This means that there will be minimal impact to the
bottom line, but a net decrease in toxic substances contained in the device.
During normal use of das Autotünr, any direct environmental impacts would be small.
One possibility might be that if the LCD on the control box were to be broken, the
mercury in the fluorescent backlight could spill out. Another impact might be that the
device, when left plugged in, will continue to draw electricity due to the losses in the
AC/DC power supply and idle current draw of the circuitry. The user manual should
recommend that the unit is unplugged when not in use to reduce unnecessary power
consumption.
-19-
ECE 477 Final Report Spring 2009
In contrast to the relatively slight impacts of usage, the environmental impact at the end
of das Autotünr’s lifetime has the potential to be significant. Proper disposal of the
device is key in minimizing both its landfill footprint and its potential to release toxic
substances to the environment. Both the Lexan brackets used for the motor mounting
and the plastic control box are able to be recycled at most community recycling centers.
Both the motors and the PCB can be recycled either at electronics-accepting local
recycling centers or at specialized electronic recycling centers [24]. Unfortunately, the
availability will vary by location, but the user manual should list a URL to the EPA site
which provides a search for recycling centers by city. This should increase the
likelihood that the end-user will actually recycle the device, rather than putting it in the
trashcan.
7.0 Packaging Design Considerations
das Autotünr has two packaging pieces connected by a cable. The main package is a
box that is 6.3” x 6.3” x 3.5”. The top of the box contains an LCD screen and four push
buttons to facilitate user interfacing, two RGB LEDs, and a cutout for a microphone.
The sides of the box have access points for DC power from a wall wart, a USB
receptacle for a flash drive, power and communication to the six servo motors, and a ¼”
stereo phone jack for input from an electric guitar. The secondary package is a set of
plastic ‘L’ shaped brackets that is held together with wing nuts to form a ‘U’ shape to
hold the guitar’s headstock. Along the vertical section of each plastic piece is a cutout
that the six servos (three on each side) will slide into. Two vertical pieces of plastic
connect to the “L” shaped brackets to hold the servos in place.
7.1 Commercial Product Packaging
There are two commercial products that currently on the market that are similar to das
Autotünr. The first product, String Master, is less sophisticated and less expensive than
das Autotünr. The second product, Robot Guitar, is much more sophisticated and more
expensive than das Autotünr. There is an open area between these two products that das
Autotünr will be able to fill with a packaging concept that merges the two extremes
-20-
ECE 477 Final Report Spring 2009
existing on the market.
7.1.1 String Master by Action Tuners
String Master is a hand-held battery operated device that will tune one guitar
string at a time. With the microphone attachment to allow for tuning of acoustic
guitars, the String Master costs approximately $125. String Master’s packaging is
very small and has little user interface. das Autotünr will have slightly larger
packaging, but will have a user interface to change to different tunings (String
Master only does standard) as well transcribe music.
To tune a guitar with String Master the user would select the string to be tuned
with a button, place the peg-holder on the string’s tuning peg, and strum that
specific string. A motor in the String Master will turn the peg while the user must
hold the assembly still and continue strumming. This process will then be
repeated for the 5 remaining strings. The only similarity to das Autotünr’s
packaging here is the motor and peg holder. das Autotünr has a similarly shaped
peg-holder attached to a motor, but it will have six sets of them. A major
difference is that the user of a String Master will have to hold their guitar, hold
the String Master steady, and strum a string all at the same time. The same user
only has to strum their guitar with das Autotünr.
7.1.2 Robot Guitar by Gibson
Robot Guitar is not equipment used to tune a guitar, but actually a guitar that has
motors installed in its tuning pegs and circuitry associated with controls in its
body. This is a high-end design and will cost over $1,000 more than an electric
guitar of the same type without the robot technology. The packaging for this
guitar is sleek and one would not know there was anything different about a robot
guitar simply by looking at it because everything that makes it different is housed
inside the guitar. das Autotünr’s packaging is on the other extreme in that all
equipment will be external to the guitar. The Robot Guitar tunes one peg at a
-21-
ECE 477 Final Report Spring 2009
time when instructed using a knob on the body of the guitar. There is no need for
the user to strum at all because the Robot Guitar uses the tension of the strings for
finding the proper tone. However, one major downside of the Robot Guitar is that
it only works on a single electric guitar. This gives das Autotünr an edge because
it can be used on multiple acoustic and electric guitars.
7.2 Project Packaging Specifications
das Autotünr consists of two separate parts connected by a cable. The first part is a
plastic box with dimensions 6.3” x 6.3” x 3.5” (Appendix B-7). The top of the box
holds two RGB LEDs, an LCD screen, four push-buttons for menu navigation, and a
microphone for acoustic guitar audio input. Along the sides of the box there are four
connections between the internal PCB and the outside world. There is a cut-out for a
user supplied USB storage device to be connected to an on-chip USB receiver, a cut-out
for a power and communication cable to drive the six servos, a jack for DC power input
from a wall-wart, and a jack for stereo audio input from an electric guitar.
The second piece of packaging for das Autotünr consists of four pieces of 3mm thick
Lexan. The first piece is an “L” shape 400mm long, 94.8mm tall, and 127mm wide
(Appendix B-1). The vertical section of the bracket has a cutout that allows the servo
body and tab (Appendix B-5) to slide through the middle of the plastic at one end, but
only the body through to the other side. The horizontal part of the piece has two long
and narrow slits that has wing nuts to connect the two brackets together. The second
piece is a rectangle 400mm long and 94.8mm tall (Appendix B-2). It has a cutout that
matches the vertical section of the first piece exactly. The third piece of plastic is like
the first except that it is only be 200mm long and 91.8m tall (Appendix B-4). Finally,
the fourth piece is a rectangle 200mm long and 91.8mm tall (Appendix B-3). It has a
cutout that matches the vertical section of the first piece exactly.
The packaging comes together with three servos between each bracket and backplate
and the shorter bracket on top of the longer one. The guitar to be tuned will have its
headstock laid into the created “U” shape and the servos adjusted to match the peg
-22-
ECE 477 Final Report Spring 2009
locations. The brackets can then be pushed together and secured with wing nuts
underneath the assembly.
7.3 PCB Footprint Layout
The major components selected for das Autotünr are a microcontroller, DSP chip, USB
controller, analog amplifier, and two voltage regulators as well as several headers for
connections outside of the PCB. The footprint choices for the microcontroller and DSP
chip were similar (QFP or QFN and 64 or 122 pins). The choice of 64 pin QFP
packaging was made for both chips. QFP was chosen over QFN because of the relative
ease of soldering a QFP package. The 64 pin models were chosen over the 122 pin ones
because there was no functional benefit added by increasing the number of pins that
would need to be soldered. We chose the through-hole package for our USB controller
instead of the QFP chip because it had a low pin count and through-hole solder joints are
not difficult to make. It also minimized the amount of pins to only the ones that would
be necessary for communication with all of the other extraneous connections made.
The outside dimensions of the PCB for das Autotünr is 5.872” x 4.352”. This board fits
into the proposed box and has enough surface area for our major components and
estimated trace. The larger dimensioned components included on the layout are the
USB controller (43mm x 18mm), microcontroller (10mm x 10mm), DSP chip (10mm x
10mm), and the audio amplifier (10mm x 6mm). The location of the components on the
PCB is such that signals can be segregated. High current traces mainly run in the upper
left hand corner. Analog signals are mostly be contained in the lower left hand corner,
and all digital signals remain on the right side of the board. Power connects to the board
near the upper center and runs along the division lines between the signals. The voltage
regulators are located on the left side of the board so they can be close to the power
source and drive out to their respective components.
8.0 Schematic Design Considerations
The design of the product thereby necessarily includes substantial current consumption in the
motors and analog circuitry for the audio inputs. In addition, there are a number of digital
-23-
ECE 477 Final Report Spring 2009
interfaces: an LCD, USB storage device, and DSP-microprocessor communication channel.
Because of the mixed-signal nature of the design, care was to be taken to couple the
subsections appropriately.
8.1 Theory of Operation
8.1.1 Analog Input, Amplification, and Output
Analog audio is input to the circuit through either an electret condenser
microphone or a 1/4” phone jack. These inputs are each mV-level signal and need
to be amplified before being sampled by the dsPIC. A stereo phone jack serves as
a mechanical switch that indicates when a plug is inserted. This switch provides
an enable signal that drives an SPDT IC, connecting either the microphone or the
jack through an RC low-pass filter to the input of an LM386 low-voltage audio
amplifier operating off of a 5VDC rail. 5VDC is used here because of its use in
other circuits and the LM386's incompatibility with a 3.3VDC supply [13]. The
amplifier's output is voltage divided to adjust its range from 0 – 5VDC to and
ADC-compatible 0 – 3.3VDC. This output is then coupled to the dsPIC's ADC
input 9.
8.1.2 Power System
There are three supply voltages present in this device. A rail of 6V unregulated
DC comes from a wall-wart power supply and provides direct power to the
device's servos. From the 6V rail, two regulated voltages are produced by LDO
linear regulators: 5VDC and 3.3VDC. Bulk capacitance on the 6V line provides
instantaneous current for motor startup draw, and similar capacitors are used on
the 5VDC and 3.3VDC lines in order to provide current for logic switching. If all
six motors stall at once, they can collectively pull 2.4 amperes. The 5VDC line
needs to supply up to 700 mA: 160 mA for the LCD screen, 520 mA to power the
USB bus and USB controller, and 20 mA for the audio amplifier circuit. The 3.3V
rail could be drawn up to a few hundred mA if all digital devices are drawing their
maximum safe current.
8.1.3 LED Output
Two bi-color common cathode red/green LEDs are used to provide feedback to
the user. Because the Microchip components in use can only source and sink a
-24-
ECE 477 Final Report Spring 2009
maximum of 4 milliamps (mA), a BJT circuit was designed to provide up to 20
mA of current to each of the four LED anodes. The transistor base input is a
GPIO output from the PIC24HJ128GP306. The circuit uses two dual BJT PNP
array ICs in 6 pin packages to save layout space on the board.
8.1.4 dsPIC
The dsPIC33FJ64GP306 runs on a supply voltage of 3.3VDC and uses its internal
oscillator set to its maximum instruction cycle frequency of 40 MHz [8]. This is
to ensure that the device's DSP processing completes as quickly as possible. The
dsPIC has two major connections: audio input and microcontroller
communication. One ADC pin is connected to the output of the audio amplifier
and couple by a bypass capacitor according to Microchip's recommendation. The
communication with the microcontroller is handled over an SPI interface. The
dsPIC is the master for this channel.
8.1.5 PIC24
The PIC24HJ128GP306 also runs on a supply voltage of 3.3VDC and will run
using its internal oscillator [3]. The instruction cycle on this device will also be 40
MHZ. The PIC24 will be responsible for several circuit interfaces. It will have
four pushbutton inputs on GPIO pins. It will communicate with the LCD via a
UART, with the DSP as an SPI slave, and with the USB as an SPI master. It will
also connect to each of the six servos and control it with a PWM output.
Functionally, PIC24 will also be responsible for creating a MIDI file based on
DSP input and writing it to the USB host.
8.1.6 Programming and In-circuit debugging
Both the PIC24 and dsPIC33 have in-circuit programming and debugging
capabilities. The Microchip-designed ICD2 programmer/debugger will be used
for these functions. The global reset button is isolated from the ICD2 by a switch
debouncer. In this way, the programmer can use the reset pin in its programming
mode without affecting the other components.
8.1.7 LCD
The CrystalFontz LCD used in the circuit takes a 5VDC supply voltage and
accepts commands via a 0 – 5VDC modified RS-232 signal [27]. It is connected
-25-
ECE 477 Final Report Spring 2009
to the PIC24's UART output through a logic level converter configured for
3.3VDC and 5VDC I/O. The device is essentially a 'dumb terminal' that writes
characters to a position on the screen based on the current cursor position and the
character input over the UART.
8.1.8 USB Host
The VDIP1 USB Host in the design contains its own oscillator circuit and
onboard voltage regulator for the USB microcontroller. The part requires a 5VDC
supply in order to provide USB standard voltage and current to attached devices
[11]. It communicates with the PIC24 devices through a 3.3V-level 4-wire SPI
connection.
8.2 Hardware Design Narrative
8.2.1 dsPIC
The dsPIC employs two of the device's major subsystems. The analog-to-digital
converter (ADC) is used to sample analog audio at a rate of 1024 Hz and place it
in data memory. Only one ADC pin is required for this input, and input AN9 was
selected because it is distant from the SPI pins, which should reduce audio
distortion. The SPI interface will be used to send detected peaks from the dsPIC
to the PIC24, with the dsPIC acting as master on the bus. Also, one GPIO pin will
be connected between the dsPIC and the PIC24. This will serve as a 'mode select'
line for the dsPIC and will be configured as an input. Additionally, Microchip's
supplied DSP library will be used to perform the discrete Fourier transform and
apply a filter to the input data. The library takes advantage of the chip's
specialized architecture in order to speed computation.
8.2.2 PIC24
Four major subsystems will be employed on the PIC24. The first SPI link will be
used to communicate with the dsPIC. The PIC24 will serve as a slave for this
connection so that the dsPIC can initiate communications only after it has
detected peaks. This SPI will use 16-bit words because both chip architectures use
it as their data word length. The second SPI will communicate with the USB host
controller. The PIC24 will be the master of this connection, since it will be issuing
-26-
ECE 477 Final Report Spring 2009
commands to the USB host. The UART will communicate data to the LCD
screen. This block will be set to communicate at a rate of 9600 baud with 8-bit
words and even parity, a configuration supported by both the LCD and the
peripheral. The PWM outputs will be used to control servo rotation. Additionally,
four GPIO pins on Port D (RD8 – RD11) will be used as pushbutton inputs. These
pins were chosen because they can be configured either as GPIO pins or hardware
interrupts, allowing them to be polled or triggered depending on which is most
convenient for the system software.
8.2.3 Audio Input
The audio circuit produces outputs that vary between zero and five volts, biased at
2.5 volts. This is due to the required operating characteristics of the LM386 audio
amplifier. The maximum voltage that the dsPIC can sample is its analog supply
voltage, 3.3VDC. To couple the two circuits, a voltage divider is used. This
reduces the amplitude of the signal but also re-biases it so that clipping will not
occur during ordinary operation.
8.2.4 LCD
The CrystalFontz serial LCD will be configured to accept 0-5V CMOS RS-232
inputs by soldering a jumper on the device closed. Its baud rate will be set to 9600
baud using a set of on-board DIP switches. Using this interface allows us to
design the circuit board without an RS-232 (-10 to 10V) level translator, using
instead a CMOS 3.3 – 5V level translator, both of which are already in use in the
circuit.
9.0 PCB Layout Design Considerationsdas Autotünr is an automatic guitar tuner and MIDI transcription system. It has a motor
assembly consisting of six servo motors which attach to the headstock of a guitar to turn the
pegs for tuning. The controls and power for this assembly are routed to the control module
via a cable which may be well over a foot long. Because of this, the control module circuitry
must be robust enough to drive the required current to the servos as well as maintain the
signal integrity of the control signals. The control module also functions as the user interface
and holds the microphone and ¼” phone plug inputs from the instrument. Because the input
-27-
ECE 477 Final Report Spring 2009
signals are on the order of millivolts, care must be taken to ensure accurate frequency
analysis results.
9.1 PCB Layout Design Considerations – Overall
The PCB is 5.872” x 4.352” and is divided into three major sections – power, analog,
and digital [28]. The power section includes our power input and output as well as our
linear regulators for the different voltage rails being supplied to our devices. It will be
located on the upper left hand corner of the PCB. The analog section contains our audio
amplifier as well as connectors for the microphone/jack input and speaker output. This
will be located directly under the power section in the lower left hand corner of the PCB.
Lastly, the digital section is the biggest section consisting of our PIC24 microcontroller,
dsPIC, USB host controller, and digital I/O. This takes up the whole right half of the
PCB. The power and ground traces will be 40 – 60 mils wide for most traces, going
down to 12 mils for the PIC24 and dsPIC connections. The rest of the signal traces will
be 12 mils wide. The ground trace will be routed from the power section such that the
analog section gets one branch, the servo motor output gets one branch, and the digital
section gets two branches.
The audio input from the microphone and the ¼” phone plug are the most affected by
EMI. The signal is on the order of millivolts, so it is important that these lines are short
and shielded from noise and environmental effects. The audio signal gets amplified
before being routed to the dsPIC. This signal will also need to be as short as possible and
have some protection from noise and environment effects so the dsPIC is supplied with a
clean signal for frequency analysis. To shield these signals, they will most likely have
ground traces running by them or have a ground pour underneath.
9.2 PCB Layout Design Considerations – Microcontroller
The PIC24 has multiple power and ground pins along all sides of the chip. 0.1uF bypass
capacitors were placed underneath the chip, and power and ground were routed to them
by putting a short trace to a via to connect to the capacitor pins. A 1uF low ESR
capacitor is located close to the chip on the top left hand corner to stabilize the core
-28-
ECE 477 Final Report Spring 2009
logic’s internal voltage regulator [3]. The microcontroller’s internal oscillator is used so
no oscillator circuit is needed. As stated before, the power and ground traces for the
PIC24 will be 12 mils due to the pin pitch on the package. The PIC24 is responsible for
supplying the servo control signals. To help maintain the signal integrity, there are 10k
pull-up resistors attached to the servo control lines, placed close to the PIC24 on the
back of the PCB.
9.3 PCB Layout Design Considerations – dsPIC
The dsPIC also requires multiple 0.1uF bypass capacitors between the power and ground
which will be located underneath the chip. These are routed in the same way as the
PIC24 [8]. The dsPIC also has a 1uF low ESR capacitor to stabilize its internal voltage
regulator. The dsPIC’s internal oscillator is used so no oscillator circuit is needed. There
are also multiple headers attached to certain pins on the dsPIC for monitoring when
debugging the layout, as well as test points for probing. The chip is positioned close to
the analog circuitry to minimize the length of the audio signal path.
9.4 PCB Layout Design Considerations - Power Supply
Due to the assortment of devices used in das Autotünr, there will be three different
voltage rails – 6VDC (unregulated), 5VDC (regulated), and 3.3VDC (regulated). The
6VDC is an input from the wall-wart and will be input into two linear regulators to get
the 5VDC and 3.3VDC lines. The servo motors will be run at 6VDC so that maximum
torque is achievable [25]. By experimentation, the current draw from running all six
motors simultaneously was in the range of 2.5-3.0 amps. There is a 100 mil trace from
the 6VDC input to the servo power output [29]. Most of the other power traces are 40-60
mils. Bulk capacitors would also be needed around the 6VDC input and the servo power
output to provide an instantaneous source of current when the motors are driving [28].
The 5VDC rail is used to power the analog ICs as well as the USB host controller, LCD,
and LED drivers. These components will be concentrated on the bottom of the board
with the digital components to the left of the analog circuitry. This allows for one main
5VDC branch to power all of the ICs. The 3.3VDC rail is used mostly to power the
PIC24 and dsPIC. The linear regulator will drive to the right where it branches to the
-29-
ECE 477 Final Report Spring 2009
PIC24 and the dsPIC as well as providing power for the servo motor control pull-up
resistors. The 5VDC and 3.3VDC also have bulk capacitors by the linear regulators.
10.0 Software Design Considerations das Autotünr is a six-string concurrent guitar tuner with the capability to record and save
MIDI files to a USB mass storage device. To achieve this goal, two separate processing
units must work together in accomplishing their tasks. Additionally, they must
communicate with multiple I/O devices which interface with the user. The dsPIC is in
charge of handling the signal analysis calculations which include a discrete Fourier
transform (DFT) and peak detection algorithms. The PIC24 is in charge of controlling
actions of the system and interfacing with the user. This includes communicating with the
LCD, USB controller and servo motors, as well as the dsPIC. Challenges will arise in
synchronizing these two processors, as well as in having them balance their own time-
critical tasks. Success will be accomplished if efficient time sharing of intra-processor
resources and stable inter-processor communication can be achieved.
10.1 Software Design Considerations
For this project, there are many software design considerations that must be kept in
mind while designing. The first of these is that there are two independent pieces of
code to be designed– one for each individual processor. Since both processors are
doing drastically different operations, each will be covered separately.
One of the first concerns that arose when choosing parts was whether or not the
processors would have enough memory to complete their tasks efficiently without
having to interface with an external memory source. Fortunately, there were easily
accessible microcontrollers which fit these criteria. Another fact that was attractive
about these microcontrollers was that the development tool that they utilize allows
programming in C. A main benefit of this is that it serves as a level of abstraction
between the programmer and the memory system. The compiler takes care of what is
placed where in SRAM and in flash as well as where the stack pointer is located. The
only thing that should be of concern that amount of memory used must not exceed the
-30-
ECE 477 Final Report Spring 2009
maximum amount available in the device (16KB for the both the dsPIC and PIC24).
The dsPIC doesn’t have much in the way of external devices to interface with. It takes
the audio input from either the microphone or the mono plug, extracts useful frequency
information and then transmits that data to the PIC24 over its SPI1 link. Before audio
sampling can begin, the ADC module must be initialized to the correct sampling rate
and its interrupt must be enabled. After it has taken enough samples, the interrupt will
be disabled, a flag will be set, and the DFT and frequency analysis portions of the code
will run. After the conclusion of the frequency analysis code, a packet will be
assembled and transmitted over the SPI1 link. It should be noted that the dsPIC is the
master of this SPI link because it will need to initiate all transactions once a given
frequency calculation is complete. This SPI is a four wire, full-duplex interface where
mostly uni-directional communication will occur. The communication can be
considered uni-directional mostly because although data will be transmitted both from
the dsPIC to the PIC24 and from the PIC24 to the dsPIC on any given transfer, the PIC
will be transmitting bytes which the dsPIC will not take action on. The only case where
the dsPIC will take action on a received byte is when it is either a “mode change” byte
or an “error” byte.
On the other hand, the PIC24 is interfaces with multiple I/O devices. It communicates
with the dsPIC over the SPI1 link, the Vinculum USB controller over the SPI2 link, and
the serial LCD over UART1. The PIC24 will act as the SPI1 slave and the SPI2
master. It will also take input from four externally-debounced push buttons to be used
for menu control attached to external interrupt (EXTINT) pins. This will allow the
push buttons to trigger an interrupt when they’re depressed and immediately will
trigger an interrupt service routine (ISR). Multiple timer (TIM) modules need to be
utilized to trigger various ISRs, as well as to facilitate PWM operation. TIM1 is
initialized to 50Hz and its interrupt is enabled to the lowest priority, in order to trigger
the LCD write routine. TIM2 serves as the time base for the PWM channels, so it is
initialized to 50Hz. Additionally, it will have its interrupt used for triggering the motor
control routine. The PWM is initialized to a duty cycle of 7.5% to allow for a 1.5ms
-31-
ECE 477 Final Report Spring 2009
pulse with a frequency of 50Hz (20ms). The figure below depicts the interrupt priority
on the PIC24.
Figure 10-1. PIC24 intterupt priorities
Both the dsPIC and the PIC24 use a hybrid interrupt/polling loop code organization, but the
extent to which each uses the polling loop differs. For the dsPIC, the ADC sampling rate is
timing critical, necessitating an interrupt-driven approach. After the samples have been
taken, the ADC interrupt is disabled and a flag will be set allowing the main loop to proceed.
The DFT and frequency calculations must then happen in order and as fast as possible.
Since no interrupts will be occurring during this phase, a flag-driven main loop is the easiest
implementation to ensure that only one of these calculations happens at once. For the
PIC24, it is almost entirely interrupt-driven due to the fact that interrupts can be prioritized
[3]. Since the processor allows for interrupts to be programmed to take precedence over
others, it makes time-sharing a lot easier as compared to a large main loop. When an
interrupt needs servicing, it will either supersede a currently being serviced lower-priority
interrupt, or it will be queued behind a higher-priority interrupt. This means that routines
which must not be interrupted, like the USB transmission ISR, will not be interrupted.
Furthermore, interrupts which do not have a time-critical need for service can be taken care
of when processor time becomes available, for example the LCD output timer ISR.
10.2 Software Design Narrative
10.2.1 dsPIC
-32-
ECE 477 Final Report Spring 2009
Discrete Fourier transform – This code module takes the analog audio input
data, samples it and performs a Fourier transform on it so that the frequency
components can be analyzed by the next code module. In “tuning mode,” the
ADC module is used to take 1024 samples at a rate of 2800Hz. In “MIDI
mode,” the ADC will take samples four times as fast of samples, but it will not
need to take as many because discerning half-step note variations requires much
less precision. After the samples are taken, a pointer to the starting address,
along with the number of samples, is sent to the DFT function. This code will
output a new set of data into a specified memory location which will serve as
the input to the next block. The sampling of this module has been tested and
verified, but the DFT portion has yet to be proven. Note: the code for the DFT
was obtained from a Microchip DSP library.
Peak detection – This module will be responsible for taking the frequency
information stored by the DFT and interpreting the results into a meaningful
data set to be transmitted to the PIC24. The code will be responsible for finding
six distinct frequency peaks, each of which should correspond to a guitar string.
This logic will be complicated because the frequency spectrum is littered with
overtones and other non-fundamental frequency components. Therefore, this
algorithm must be intelligent enough to determine which peaks should be
strings and which ones can be neglected. An initial design of this code involves
taking the top 12 or so frequency peaks from the output of the DFT and doing a
quick comparison amongst the values to determine which peaks may be integer
multiples of each other. This should help filter out many (hopefully all) of the
harmonics. A more informed decision can then be made about which peaks
appear to be fundamental frequencies of strings themselves. This code is
complete, functional and tested.
SPI transmission – The SPI is used to communicate the frequency information
to the PIC24. Data is transmitted in the form of an eight-byte packet, with a
start byte and a stop byte, with each of the six data bytes corresponding to the
-33-
ECE 477 Final Report Spring 2009
frequency of a guitar string. The dsPIC serves as the SPI master and will
initiate all transactions. There is also be a line connected to a GPIO pin on the
dsPIC from the micro to allow the PIC to tell the dsPIC that it has data it needs
to be sent in the case of a mode change. Since a dsPIC-initiated data
transmission also serves as a data reception from the PIC24, this will give the
PIC an opportunity to send the dsPIC status messages. Ordinarily, the message
received should be the “everything is okay” byte. But, if an error is encountered
by the PIC24, it will inform the dsPIC which will take the necessary action
(more than likely a retransmission). This code is complete, functional and
tested.
10.2.2 PIC24
SPI1 – This code is almost identical to that of the dsPIC with the error detection
and transmission logic added. This code is complete, functional and tested.
Menu/UI/EXTINT ISR – This module is in charge of keeping the state of the
LCD menu and responding to user input via four push buttons. When a push
button is pressed, the menu state will be updated by adding LCD commands to a
128-bit circular buffer to be handled by a separate ISR. This routine is the
second-lowest interrupt priority because it is not a time-critical routine. This
code is complete, functional and tested.
UART (TIM1 ISR) – The UART is used to communicate information to the
serial LCD display. Individual byte-long commands are added to a 128-bit
circular buffer by the menu/UI ISR triggered by the push buttons attached to the
external interrupt pins. The UART ISR is triggered by a 50Hz timer interrupt
with the lowest interrupt priority. When the ISR is able to be serviced, it will
transmit all data residing within the circular buffer and then exit. This code is
complete, functional and tested.
MIDI assembly/USB communication/ SPI2 – This module is in charge of
-34-
ECE 477 Final Report Spring 2009
assembling the MIDI file and communicating with the Vinculum USB
controller via SPI2. It takes frequency data, translates that data into a MIDI
note value and corresponding note duration, assembles USB commands and
MIDI file data, places this data into a large circular buffer and then initiates the
SPI transmission by writing the SPI2BUF register, triggering an ISR. This ISR
will empty the circular buffer, transmitting all of the data to the external USB
controller and subsequently writing it all to a USB mass storage device. This
code is complete, functional and tested.
Motor Control/PWM/TIM2 ISR – The motor control module performs all
calculations as to which commands to send to each one of the six servo motors
via individual PWM lines. Every 20ms, the ISR will check if the SPI1 rx buffer
is full. If new data is waiting, the motor control logic will pull each string
frequency out of the buffer, compare it to previous frequencies to determine
which direction it is turning and how far it has turned, and then determine how
far the motor has to go before it reaches its intended frequency. One of the
difficult part of this algorithm is that the code cannot assume a specific rotation
direction will result in a frequency increase, so it must be determined by
feedback. Additionally, each string has a corresponding PWM channel that
must be controlled separately. So, this routine is run in a six-iteration for loop.
See Figure 10-2 for a more detailed explanation.
-35-
ECE 477 Final Report Spring 2009
Figure 10-2. Motor Control Flowchart
11.0 Version 2 ChangesOne hardware change comes immediately to mind: replace the dsPIC with a codec and more
powerful DSP, along with high speed external RAM. Data memory and FFT computation
time limitations lowered the resolution of our frequency spectrum and consequently
diminished the effectiveness of our frequency analysis. A higher quality audio sampling and
amplification system, coupled with a device capable of computing longer DFTs may have
been helpful here.
Another approach to tuning should be investigated as well: replace the audio processing with
a set of six stress sensors, one for each string. This approach is used successfully in the
Gibson robotic guitar and may have been a more effective method to assess the tuning of
each string. However, this would have significantly increased the mechanical complexity of
the project.
12.0 Summary and Conclusionsdas Autotünr was overall a success. All of the project-specified success criteria were met, and
the device actually functions as intended. It is capable of tuning a guitar and storing different
tuning settings. The device was also successful in transcribing sound data to a MIDI file and
storing the file to a USB mass storage device. However, we did not get to implement
everything that we had wanted to do originally, specifically tuning all of the strings at once
-36-
ECE 477 Final Report Spring 2009
by strumming an open chord. This was mostly due to the constraints set by the parts we had
picked for our design.
After completing this project, we all had a deeper knowledge of musical harmonics and
overtones on stringed instruments. We also learned a lot of circuit design principles such as
bulk and bypass capacitor sizing as well as power supply design. The MIDI format was also
extensively studied for file creation. Knowledge on how to interface with a USB host
controller was also gained. Lastly, we gained knowledge about the other areas involved with
the design of a product, such as reliability and safety as well as patent liability.
-37-
ECE 477 Final Report Spring 2009
References
[1] Shera, et. al. “Revised estimates of human cochlear tuning from otoacoustic and behavioral measurements”. PNAS vol. 99 no. 5 3318-3323, March 5, 2002.
[2] “Discrete Fourier Transform.” Internet: http://www.diracdelta.co.uk/science/source/d/i/discrete%20fourier%20transform/source.html, [February 2, 2009].
[3] Microchip, Inc, “High Performance, 16-bit Microcontrollers,” PIC24HJXXXGPX06/X08/X10 Data Sheet, June 2007. http://www.engineering.purdue.edu/477grp4/files/datasheets/PIC24HJ128.pdf
[4] Freescale, “MC9S12DT256 User Guide,” MC9S12DT256 datasheet, March 2003 [Revised January 2006].
[5] Microchip. “PIC24HJ128GP306.” Internet: http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en024689#2, [February 3, 2009].
[6] Freescale. “MC9S12DT256 Product Summary Page.” Internet: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MC9S12DT256&nodeId=null&tab=Buy_Parametric_Tab&fromSearch=false, [February 4, 2009].
[7] Analog Devices. “Mixed-Signal DSP Controller with CAN.” ADSP-21992 datasheet, Aug 2007.
[8] Microchip, Inc, “High Performance, 16-bit Digital Signal Controllers,” dsPIC33FJXXXGPX06/X08/X10 datasheet, Dec. 2006. http://www.engineering.purdue.edu/477grp4/files/datasheets/dsPIC33FJdatasheet.pdf
[9] Microchip. “dsPIC33FJ64GP306.” Internet: http://www.microchip.com/wwwproducts/Devices.aspx?dDocName=en024665, [February 3, 2009]
[10] Cypress Semiconductor, “Embedded USB Host/Slave Controller,” SL811HS datasheet, Mar 2002.
[11] FTDI, “Vinculum VNC1L Prototyping Module,” VDIP1 datasheet, Aug. 2006 [Revised March 2007] http://www.engineering.purdue.edu/477grp4/files/datasheets/DS_VDIP1.pdf
[12] New Japan Radio, “Dual Audio Power Amplifier,” NJW1105 datasheet, Feb. 2005.
[13] National Semiconductor, “Low Voltage Audio Power Amplifier,” LM386 datasheet,
-38-
ECE 477 Final Report Spring 2009
August 2000. http://www.engineering.purdue.edu/477grp4/files/datasheets/LM386.pdf
[14] L.M. Milano, J. Rastegar, and F. Khorrami, “Automatic string instrument tuner,” U.S. Patent 5,767,429, November 9, 1995.
[15] J.L. Kohler, “System and method for automatically detecting a set of fundamental frequencies simultaneously present in an audio signal,” U.S. Patent 6,140,568, November 5, 1998.
[16] C.R. St. Denis, “Tuning apparatus for stringed instruments,” U.S. Patent 4,889,029, September 2, 1988.
[17] Department of Defense. “Military Handbook: Reliability Prediction of Electronic Equipment.” December 2, 1991. https://engineering.purdue.edu/ece477/Homework/CommonRefs/Mil-Hdbk-217F.pdf
[18] ST Microelectronics, “L4941: Very low 1A Dropout Regulator,” 2008. L4941 datasheet. http://www.st.com/stonline/products/literature/ds/2142.pdf
[19] Diodes, Inc., “AP1117: 1A Low Dropout Positive Adjustable or Fixed-Mode Regulator,” March 2009. AP1117 Datasheet. http://www.diodes.com/datasheets/AP1117.pdf
[20] Agency for Toxic Substances & Disease Registry, “ATSDR – ToxFAWs: Lead,” August 2007. Internet: http://www.atsdr.cdc.gov/tfacts13.html [April 16, 2009]
[21] Vandevelde, Gonzalez, Limaye, et al., “Lead Free Solder Join Reliability Estimation by Finite Element Modelling Advantages, Challenges and Limitations,” IMEC, 2004. http://www.imec.be/IMECAT/documents/16_2004_IPC_Frankfurt_Vandevelde_paper.pdf
[22] Advanced Circuits, “Printed Circuit Boards Instant Quote Online,” Internet: http://www.4pcb.com/instant_quote/ [April 16, 2009]
[23] RoHS Compliance, “RoHS Compliance in the EU.” Internet: http://www.rohs.eu/english/index.html [April 16, 2009]
[24] Environmental Protection Agency, “Basic Information | eCycling | US EPA.” Internet: http://www.epa.gov/waste/conserve/materials/ecycling/basic.htm [April 16, 2009]
[25] Parallax, Inc., “Continuous Rotation Servo (#900-00008)”, 2004. http://www.parallax.com/dl/docs/prod/motors/crservo.pdf
[26] Panasonic, “Omnidirectional Back Electret Condenser Microphone Cartridge.” P9955-ND datasheet
[27] CrystalFontz America, Inc, “Serial LCD Module Specifications,” CFA-634 Data Sheet,
-39-
ECE 477 Final Report Spring 2009
Oct. 2005. http://www.engineering.purdue.edu/477grp4/files/datasheets/CFA-634_v2.0.pdf
[28] M. Glenewinkel, “System Design and Layout Techniques for Noise Reduction in MCU-Based Systems,” 1995. https://engineering.purdue.edu/ece477/Homework/CommonRefs/AN1259.pdf
[29] “PCB Trace Width Calculator,” CircuitCalculator.com, Jan. 2006. http://www.circuitcalculator.com/wordpress/2006/01/31/pcb-trace-width-calculator
-40-
ECE 477 Final Report Spring 2009
Appendix A: Individual Contributions
A.1 Contributions of Joe Blubaugh:
As team leader, I coordinated our efforts by arranging team meetings, assigning tasks, and
getting progress updates from each member. Especially as we moved into the end of the
semester and tasks became less discrete and well-defined, this coordination became
important to our success. By establishing a regular meeting time early in the semester, I
helped us make steady progress towards completion throughout the course.
I took the lead in creating our schematic and in understanding the operational modes of our
selected parts. For example, I designed our switch debouncing and in-circuit programming
circuits, which required me to develop an understanding of the exact voltage and currents
that both our switch debouncer and the Microchip ICD2 would supply and tolerate at their
port pins.
My major contribution to the project was my work with our dsPIC and my understanding of
digital signal processing concepts. I developed all the code on our dsPIC, including ADC
and SPI drivers, DSP library calls, and frequency analysis. This required a strong
understanding of Microchip’s DSP library along with the chip’s architecture and memory
layout. I designed our frequency analysis algorithm and tested it rigorously. I evaluated
several approaches, coding and testing each one. I came up with concept of a note-
characterization and comparison algorithm independently and designed the weighting
equation used in the algorithm myself. This component of the project was absolutely
essential.
I also developed our functions to communicate with and control the Vinculum USB
controller. This included low level functions like ‘send command’ all the way up high level
file manipulation functions like file_open, file_write, and file_seek. This involved
developing a very close familiarity with the communication protocols used by the device, and
the exact formatting and structure of commands and responses.
A-1
ECE 477 Final Report Spring 2009
A.2 Contributions of Diana Mui:
My major contribution to the project was the design of the PCB. When we had started
assigning tasks for the project, my assigned area of expertise on the team was the PCB layout
because I had prior experience with designing PCBs. I handled all of the layout of elements,
trace sizing and footprint creation by myself. I worked with Joe to modify the schematic to
make laying out traces easier for certain parts of the design. I also worked with Matt to
ensure that the PCB would fit into our enclosure and that the positioning of the elements
worked well with the packaging. My biggest efforts involved routing the power efficiently
and in keeping the signal integrity of our audio and motor control signals. This involved
several design iterations to accomplish. There was also an issue with our PCB during
fabrication, and I dealt with that quickly and effectively. I was on a college visit when our
PCB arrived, but I made sure to leave notes with Matt to check and populate the PCB so that
our team would not lose more time.
After the PCB was finished and populated, I worked on the MIDI generation code as my
major software contribution. I did a lot of research on the MIDI format and became the
principle expert on my team of the MIDI format, by knowing which bytes meant what in the
file. I worked on the code to generate the MIDI file from an array of note & duration data
sent from the dsPIC and used the USB functions written by Joe to write the MIDI file to a
USB storage device. I integrated the MIDI code into our microcontroller code and did a lot of
debugging with our USB host controller to ensure that the correct data was being written.
Outside of my technical assigned tasks, I completed the patent liability homework for our
team. For this, I did a lot of research on patents and spent a lot of time filtering through the
jargon. I also contributed a lot of work on team documentation. I wrote most of the user
manual with help on the product operation from Matt. I also compiled our final report and
senior design report, merging together everybody’s contributions. The final poster and
website for our team was also designed by me. I also took a lot of the pictures documenting
the progress of our project for insertion into our lab notebooks and made sure to remind my
team members to update their team notebooks as well.
A-2
ECE 477 Final Report Spring 2009
A.3 Contributions of David Sutherland:
During the conception of the project, I spent much of my time determining the feasibility of
using servo motors to tune with. After hacking one of my own servos for continuous
rotation, and after looking at the torque specs and control methodologies, I chose the servos
we are now using. Also, I built an amplifier circuit which increased the output voltage of an
electric guitar so that we could see it on the spectrum analyzer to determine whether or not
there were distinct frequency peaks within a guitar waveform. Furthermore, I contributed to
the final design of our motor bracket which allows motors to be both firmly secured and
easily removed.
After we had chosen our parts and begun the design phase of the project, my primary
contribution to the team was serving as team software lead. I was in charge of learning how
to use the MPLAB IDE and the C30 C compiler and writing most of the PIC24 code and
infrastructure. Early on, I was tasked with learning how to program and use the Explorer 16
development board for initial code development. Although the PIC24 included with the
board was not our exact model, it still served as the birthplace of the micro routines. The
first routines I wrote included our LED heartbeat routine, timer ISRs, PWM control ISR and
ADC ISR. I also proved that our servos could be speed controlled via pulse-width
modulation on that board. Additionally, I wrote the SPI communication routines that both
the dsPIC and PIC24 use to communicate with each other and the USB controller. I tested
these routines by emulating a full-duplex SPI link by looping back SPI1 to SPI2 on the
Explorer 16.
Once our PCB arrived, I began porting the code from the PIC24F we were using on the dev
board to the PIC24H we were actually using on our board. Some complications, specifically
timer periods, arose from the fact that the dev board micro was running at 10 MHz, whereas
the PCB micro was running at some then-unknown frequency. After configuring our micro
to run at its maximum rated frequency of 40MHz (more complex of a task than I had
previously estimated), I had to modify my timer and PWM routines accordingly. With the
PWM routines functional, I tried to use a potentiometer measured through the ADC to test
some initial motor control feedback (these did not prove very successful). I also coordinated
A-3
ECE 477 Final Report Spring 2009
with Joe on the way that the dsPIC and PIC24 would communicate over SPI, specifically the
packet structure we were agreeing upon. Once we had determined the packet structure, I was
able to begin writing the frequency data reception routines. Initially this meant an SPI ISR
which wrote to a circular buffer, but after realizing a few drawbacks of this approach, I wrote
an SPI DMA routine to receive the information in the “background” of code execution. The
next major functional piece of code actually was tied directly to one of our PSSCs. It
required us to be able to save tuning settings to non-volatile storage, flash in this case.
Although there were two library functions which allowed a lot of the erasing and writing to
occur, I still had to set up the aligned, correctly-sized data structures to allocate the flash
sector to be erased. Also, since the PIC24’s SRAM and flash memories do not share address
and data busses, I had to write an assembly routine to fetch data from flash and pull it into the
data space. After the flash routine was finished, I switched my focus to writing the
supporting code to the MIDI transcription process. I wrote the MIDI note buffer read and
write buffer routines, as well as the frequency-to-MIDI note conversion function. Most
importantly, I designed the variable-speed, frequency-controlled motor control logic. This
function takes data from the SPI DMA, analyzes it and varies the speed and direction of each
PWM channel accordingly.
Once all of the code was written, we had to integrate all of our code. Of course, I did not do
this by myself, but I did contribute to integrating my code with Joe’s USB code, Diana’s
MIDI code and Matt’s LCD code. I also helped test all of the integration of the code, as well
as debug some issues related to various workings of the PIC24 code.
A-4
ECE 477 Final Report Spring 2009
A.4 Contributions of Matthew Swallow:
My contributions to das Autotµnr consisted of the homeworks that I completed and the work
that I did involving the menu structure code, the mechanical design, and the population of
the PCB. I completed homework number 4, the packaging design homework. This
homework required me to complete many scaled AutoCAD drawings of the motor brackets
and the main box. I also had to find two existing products similar to our project. I found the
String Master and the Robot Guitar.
I also spent a significant portion of my time on the initial mechanical design as well as the
many changes to the design. I had to design and document the motor brackets. I was
involved in the brainstorming of how to hold our servos onto the guitar and how to hold the
guitar pegs to the servos. It was decided that we would have a bracket consisting of two
sides and each side consists of an “L” shaped piece and a flat piece. Every one of the bracket
pieces has a cut-out portion that will allow the six servos to slide into the brackets at one end,
but not slip out anywhere else. I decided to use Lexan for the bracket over metal mainly
because of the worry that a metal bracket would eventually cut through the plastic servos.
Lexan was also available from the machine shop and was assumed to be cheaper than metal
would be. As for attaching the pegs to the servos, it was decided to cut peg-wrenches bought
from a music store. My original plan was to drill a hole in the center of the peg-wrenches
into the center of the servos’ shafts. This ended up being problematic though. The screw
alone was unable to hold the peg-wrenches in place while attempting to tune the guitar. In
the end I had to super glue the screws, washers, peg-wrenches, and the servo shafts together.
The last part of the mechanical design was selecting a box for our user interface that would
be able to contain the LCD screen and other objects need in the box. I was able to find one
that was large enough to fit what we needed, but not so large that it would be a pain. Finally,
I marked up the box so that the machine shop could complete cut outs for each of our
components.
When we received our completed PCB, I was the one who checked all of the vias for proper
connectivity as well as checked all of the traces under the microscope for traces. I found no
issues with the vias or traces so I took on the task of populating the board. I soldered on all
A-5
ECE 477 Final Report Spring 2009
but 6 components on the board. I also created headers for the motors, LCD screen, LEDs,
microphone, stereo jack, ICD2, and push buttons.
The last thing that I spent a large amount of my time on was programming for the menu
structure. I decided that the easiest way to control the display on the LCD screen would be to
use a state machine. I created a master function (LCD_Button) that is called to update the
screen’s display every time a button is called. LCD_Button checks what state the display is
currently in and then changes the state and calls other functions based on what button was
pushed. These other functions updates the display based on the display state. After seeing
how long it took for the screen to update. The screen appeared to scroll and not display the
entire screen at once. After this discovery I changed the display code to know what needs to
be updated. After I made these changes only the characters that needed to be revised were
changed. This made transitions through the same screen appear to be instant and only during
changes between two different displays does the screen appear to scroll. My code was
originally meant to simply control the LCD screen, but it soon became obvious that it would
be easiest to add functional control into the display code. Function calls and new code were
added to control the speed of motors and the writing of MIDI codes to the USB storage
device.
These are the categories of my contribution worth the most amount of mention. However,
there I did add to the project in smaller ways. I helped Joe and David with code testing and
revising several times. I also helped Diana with the best dimensions for the PCB based on
the box dimensions that I selected.
A-6
ECE 477 Final Report Spring 2009
Appendix B: Packaging
Figure B-1. Large side frontplate
Figure B-2. Large side backplate
Figure B-3. Small side backplate
B-1
ECE 477 Final Report Spring 2009
Figure B-4. Small side frontplate
Figure B-5. Servo with peg-holder
B-2
ECE 477 Final Report Spring 2009
Appendix C: Schematic
Figure C-1. Power Regulator Schematic
Figure C-2. USB Interface Device Schematic
C-1
ECE 477 Final Report Spring 2009
Figure C-3. Switch Debouncing Schematic
Figure C-4. User Interface LED Schematic
C-2
ECE 477 Final Report Spring 2009
Figure C-5. Audio Input & Amplification Schematic
Figure C-6. UART/LCD Communication Schematic
C-3
ECE 477 Final Report Spring 2009
Figure C-7. On-board LED Indicator Circuits
Figure C-8. Microcontroller Connections Circuit
C-4
ECE 477 Final Report Spring 2009
Appendix D: PCB Layout Top and Bottom Copper
Figure D-1. PCB Top Copper and Silkscreen
D-1
ECE 477 Final Report Spring 2009
Appendix E: Parts List Spreadsheet
Manufacturer Part Number Description QuantityMicrochip dsPIC33FJ64GP306 DSP Microcontroller 1Microchip PIC24HJ128GP306 Microcontroller 1Parallax 900-00008 Continuous rotation servo 6CrystalFontz CFA-634 LCD with LED Backlight 1OSRAM LG T67K Low current LED 4ST Microelectronics STG3157 SPDT Switch IC 1National Semiconductor LM386 Low-power audio amplifier 1Maxim MAX6818 Switch debouncer 1Diodes inc AP1117 LDO Regulator, 3.3V 1ST Microelectronics L4941 VLDO Regulator, 5V 1Vinculum VDIP1 USB Host Controller DIP package 1NXP PBSS5160DS Dual PNP transistor IC 2Texas Instruments SN74LVC1T45 bidirectional level translator 1Panasonic LN11WP23 bi-color red/green LEDs 2Panasonic WM64K Electret condenser microphone 1
E-1
ECE 477 Final Report Spring 2009
Appendix F: Software Listing
PIC 24 CODE
HEADER FILES
COMMUNICATE.H#define TUNING_START 0xFF01#define MIDI_START 0xFF03#define MODE_STOP 0xFF04#define FREQIDLE 0xFFFF#define MULTIMODE 1#define DEMOMODE 2
void changeMode(int mode);
LCD_FUNCTIONS.H#ifndef LCD_Functions#define LCD_Functions
#define CLKFREQ 40000000#define BAUDRATE 19200 // Adjustable on the LCD screen#define BRGVAL (((CLKFREQ/BAUDRATE) >> 4)-1)#define UART2TXBUFSIZE 512#define T1PERIOD 6000
#define CURSORHOME 1 // ASCII code constants#define HIDECURSOR 4#define BOOTSCREEN 9#define LINEFEED 10#define CLEARDISP 12#define CURSORPOS 17#define SCROLLOFF 20#define WRAPON 23#define FANCYU 94#define MUSICNOTE 144#define UPARROW 222#define DOWNARROW 224
#define bufEmptyChk(BUFSIZE,BUFHEAD,BUFTAIL) ((BUFHEAD) == (BUFTAIL) ? 1 : 0)//returns 1 if empty
#define bufFullChk(BUFSIZE,BUFHEAD,BUFTAIL) ((((BUFHEAD) + 1) & ((BUFSIZE) - 1))== (BUFTAIL) ? 1 : 0) //returns 1 if full
void changeMode(int mode);
void LCD_Button(void);void uart2TxBufWr(unsigned int wr_data);void uart2TxBufWrStr(char *data, int len);unsigned int uart2TxBufRead(void);void __attribute__((__interrupt__, auto_psv)) _U1TXInterrupt(void);void UART_Init(void);void LCD_Init(void);void LCD_Instructions(void);void LCD_Main(unsigned int);void LCD_Setting(unsigned int);void LCD_User(unsigned int);void LCD_Edit(unsigned int);
F-1
ECE 477 Final Report Spring 2009
void LCD_Tune(unsigned int);void LCD_Transcribe(void);void LCD_PSSC(unsigned int);void LCD_Motors(unsigned int);void LCD_Sound(unsigned int);void LCD_USB(void);void LCD_FLASH(unsigned int);void LCD_UI(unsigned int);void tunings_from_flash(void);void tunings_to_flash(void);
int usbBufferWriteChar(char addToBuff);int usbCommandSend(char *responseBuf, int maxLen);int usbErrorCodeCheck(char *input, int len);
#endif
midi.hfile_desc* MIDI_Create_File(char *fname); //modify global var midi_tkindex, returns a file descriptorint MIDI_Write_Data(file_desc *fd); //Returns bytes writtenint MIDI_Form_Demo(void);int MIDI_Finalize(file_desc *fd); //returns USB_SUCCESS or USB_FAILvoid midi_init(void);void n(int n, int d);#define BUFFER_SIZE 6400
motor.h
//PWM DEFS#define T2PERIOD 12500#define PWMOFF 0#define PWMMAX T2PERIOD#define PWMNEUTRAL 941 //=7.5% * T2PERIOD#define PWMFAST 17#define PWMMEDIUM 16#define PWMSLOW 14#define HISTLENGTH 8void writePWM(int,int);void tuningEnd(int);
read_flash.hvoid read_flash_byte(int flashAddr, unsigned int *destAddr);
usb_funcs.h//Messages we can read from status alerts#define CR 0x0d#define PROMPT 0x3e#define NODISK 0x4e44
//Error Messages returned from commands
#define COMMANDFAIL 0x4346#define DISKFULL 0x4446#define INVALID 0x4649#define READONLY 0x524f#define FILEOPEN 0x464f#define DIRNOTEMPT 0x4e45
F-2
ECE 477 Final Report Spring 2009
#define INVALIDFNAM 0x464e#define NOUPGRADE 0x4e55
//Monitor events#define DEVIND1 "DD1"#define DEVOUTD1 "DR1"#define DEVIND2 "DD2"#define DEVOUTD2 "DR2"
#define USB_HEADER 0x1000#define USB_DATA_READ 0x0800#define USB_DATA_WRITE 0x0000#define USB_STATUS_READ 0x0C00
//Constant definitions#define USB_CMD_BUFF_SIZE 512#define USB_SUCCESS 1#define USB_FAIL -1#define MAX_STRLEN USB_CMD_BUFF_SIZE#define MAX_READ_TRIES 20000#define MAX_FNAME_LEN 15#define ONE_SPI_CYCLE 800#define USB_COMMAND_DELAY 4200#define USB_STATUS_MASK 0x04#define USB_BYTE_OUT_MASK 0x0FF#define USB_BYTE_IN_MASK 0x0FF#define USB_BYTE_OUT_OFFSET 3#define USB_BYTE_IN_OFFSET 2#define USB_OUTPUT_BYTE(x) (((x) >> USB_BYTE_OUT_OFFSET) & USB_BYTE_OUT_MASK)#define USB_INPUT_BYTE(x) (((x) & USB_BYTE_IN_MASK) << USB_BYTE_IN_OFFSET)#define NULL 0x00#define PSSC_FNAME "FRJ.MID"#define PSSC_FNAME_LEN 7#define DEFAULT_FNAME "OUT.MID"#define DEFAULT_FNAME_LEN 7#define BEGIN 0#define END 1#define CURRENT 2
typedef struct file_desc { unsigned long pos; char open; char fname[MAX_FNAME_LEN]; int fname_len;} file_desc;
file_desc* file_open(char* fname, int fname_len, char mode); //'w' or 'r'int file_close(file_desc *file);int file_write(file_desc *file, char* data, int len);int file_read(file_desc *file, char* data, int len);int file_seek(file_desc *file, int position, int offset); //position is: BEGIN, END, CURRENT
int usbCommandSend(char *responseBuf, int maxLen); //Writes a command out of the buffer, into SPIint usbInitRead(char *resultBuf, int maxLen); //returns 'new_data' flag, places result in resultBufchar usbStatusRead(void);
int usbBufferWrite(char *string, int Len);int usbBufferWriteChar(char addToBuff); //writes a single char to buffer, pads appropriatelyint usbBufferWriteInt(int addToBuff); //Writes an integer at two chars to buffer
F-3
ECE 477 Final Report Spring 2009
char usbBufferReadChar(void);void usbResponseDelay(void);void usb_init(void);int usbErrorCodeCheck(char *input, int len);
SOURCE FILES
main.c//SPI init
#include "p24HJ128GP306.h"#include "LCD_Functions.h"#include "usb_funcs.h"#include "read_flash.h"#include "midi.h"#include <libpic30.h>#include "communicate.h"#include "motor.h"
#define FLASH_START_ADDR 0x800 //if changing, change read_flash.s
//PACKET DEFS#define STARTWORD 0xFFFF#define STOPWORD 0x7777
//MIDI DEFS#define MIDIBUFSIZE 32#define TICSPERINTR 30#define BADSTARTRCVD 0xBAD0
//SPI BUF DEFS#define SPI2TXBUFSIZE 16#define SPI1RXBUFSIZE 16#define BUFEMPTY 0xFFFF#define HISTLENGTH 8//#define bufEmptyChk(BUFSIZE,BUFHEAD,BUFTAIL) (BUFHEAD == BUFTAIL ? 1 : 0) //In LCD_Functions.h//#define bufFullChk(BUFSIZE,BUFHEAD,BUFTAIL) (((BUFHEAD + 1) & (BUFSIZE - 1)) == BUFTAIL ? 1 : 0)
//TUNING DEFS#define MOTOR_TIMEOUT 25 //1 = 20ms, 25 = 500ms#define TUNING_PRECISION 1#define DONE_HALT 1#define NO_TURN_HALT 2
//NOTE DEFS#define STR0MIN 58#define STR1MIN 79 #define STR2MIN 106#define STR3MIN 133#define STR4MIN 178#define STR5MIN 235
#define STR0MAX 102 #define STR1MAX 135#define STR2MAX 182#define STR3MAX 239#define STR4MAX 302#define STR5MAX 406
F-4
ECE 477 Final Report Spring 2009
//MIDI VARIABLES AND ROUTINESint midi_buf_head;int midi_buf_tail;int transcribeFlag;int midi_bytecnt;file_desc *midi_file;
int stallCount = 0;
typedef struct midistruct { int duration; //this value is in "tics" int note; //this is the midi note #} midiint;midiint midi_buffer[32];
int midi_tics = 0;float midi_avg;int midi_acc = 0;int midi_note = 0;
int freqToMidi(int); //declare freqToMidi in this region
//MIDI buffer writevoid midiBufWr(int wr_duration,int wr_note) { midi_buffer[midi_buf_head].duration = wr_duration; midi_buffer[midi_buf_head].note = wr_note; midi_buf_head = (midi_buf_head + 1) & (MIDIBUFSIZE - 1);}
//MIDI buffer readmidiint midiBufRead() { midiint read_dat;
read_dat = midi_buffer[midi_buf_tail]; midi_buf_tail = (midi_buf_tail + 1) & (MIDIBUFSIZE - 1); return read_dat;}
//FLASH VARIABLES//declare flash spaceunsigned int __attribute__((space(prog),address(FLASH_START_ADDR))) flash_start[512] = {28,33,38,43,47,52};
//declare tuning array in data memoryunsigned int tuning_array[64] = {0};unsigned int *default_tuning = tuning_array;unsigned int *user_tuning1 = tuning_array + 6;unsigned int *user_tuning2 = tuning_array + 12;unsigned int *user_tuning3 = tuning_array + 18;unsigned int *user_tuning4 = tuning_array + 24;unsigned int *user_tuning5 = tuning_array + 30;unsigned int *flash_pssc = tuning_array + 36;
//MIDI globalsextern int wrindex;extern int rdindex;extern int dsize;extern char midi[BUFFER_SIZE] __attribute__((far));
//Disk plugged in flagint diskFlag;
F-5
ECE 477 Final Report Spring 2009
void tunings_from_flash() { //get tunings from flash, store to tuning_array[] int i; for(i=0;i<38;i++){ read_flash_byte(FLASH_START_ADDR + (i * 2),tuning_array + i); }}void tunings_to_flash() { //store tunings to flash, from tuning_array[] _erase_flash(FLASH_START_ADDR); _write_flash16(FLASH_START_ADDR,tuning_array);}
//SPI VARIABLESint spi1_bits_rcvd = 0;int spi1_err_code = 0;int spi1_rx_buf_holder = 0;int spi1_rx_buffer[SPI1RXBUFSIZE];int spi1_rx_buf_tail = 0;int spi1_rx_buf_head = 0;
int spi2_newdata_flag = 0;int spi2_tx_buffer[SPI2TXBUFSIZE];int spi2_tx_buf_tail = 0;int spi2_tx_buf_head = 0;int spi_test = 0;unsigned int spi2_rx_buf_a[8] __attribute__((space(dma)));unsigned int spi2_rx_buf_b[8] __attribute__((space(dma)));
//PWM VARIABLESint motor_dir[6] = {-1,-1,-1,-1,-1,-1};int motor_state = 0;int motor_wait = 0;int motor_init_cnt = 0;int single_string = 0;int halt_tuning = 0;volatile unsigned int *pwm_ptr;
//BUTTON VARIABLESvolatile int button_pushed = 0;volatile int top_button_pushed = 0;volatile int right_button_pushed = 0;volatile int left_button_pushed = 0;volatile int bottom_button_pushed = 0;
// menu testing variablesunsigned int String_Values[6][5][2];int Freq_Values[6][5];int String_State[6];extern unsigned int Menu_State;
//TUNING VARIABLES & FUNCTIONSvolatile int freq_history[6][HISTLENGTH] = {{0}}; //[str_num][0 = recent, 7 = old]int note_history[8] = {0};int user_tuning[6] = {82,110,147,196,247,330};int tuning_mode = MODE_STOP;int currently_tuning = 0;struct limitstr { int min; int max;} freq_limits[6];
void changeMode(int mode) {
F-6
ECE 477 Final Report Spring 2009
if (mode == DEMOMODE) { SPI2BUF = TUNING_START; } else { SPI2BUF = mode; } DMA1CONbits.AMODE = 0b01; //Post-increment off DMA1CNT = 0; IEC0bits.DMA1IE = 0;
PORTBbits.RB15 = 1;
while(SPI2STATbits.SPITBF == 1); while(SPI2STATbits.SPIRBF != 1);
PORTBbits.RB15 = 0; if (mode != MODE_STOP) { DMA1CONbits.AMODE = 0b00; //Post-increment on DMA1CNT = 7; IEC0bits.DMA1IE = 1; } tuning_mode = mode; halt_tuning = 0; motor_state = 0;}
void writePWM(int string,int speed) { if(string == 0) { //get pointer to PWM register pwm_ptr = &OC1RS; } else if(string == 1){ pwm_ptr = &OC2RS; } else if(string == 2){ pwm_ptr = &OC3RS; } else if(string == 3){ pwm_ptr = &OC6RS; } else if(string == 4){ pwm_ptr = &OC5RS; } else if(string == 5){ pwm_ptr = &OC4RS; }
if(speed == 0) { *pwm_ptr = PWMOFF; String_State[string] = 0; } else if(speed > 0) { *pwm_ptr = PWMNEUTRAL + (speed * motor_dir[string]); String_State[string] = 1; } else if(speed < 0) { *pwm_ptr = PWMNEUTRAL + (speed * motor_dir[string]); String_State[string] = -1; }}
void tuningEnd(int exit_code) { writePWM(0,0); writePWM(1,0);
F-7
ECE 477 Final Report Spring 2009
writePWM(2,0); writePWM(3,0); writePWM(4,0); writePWM(5,0); halt_tuning = exit_code; motor_state = 0; changeMode(MODE_STOP); currently_tuning = 0;}
// Select FRC osc w/out PLL_FOSCSEL(FNOSC_FRCPLL);// Enable Clock Switching and Configure Primary Oscillator in HS mode_FOSC(FCKSM_CSECMD & OSCIOFNC_OFF & POSCMD_HS);_FWDT(FWDTEN_OFF);
void osc_init() { PLLFBD=148; CLKDIVbits.PLLPOST = 0; CLKDIVbits.PLLPRE = 5; OSCCONbits.NOSC = 1; //switch to FRC w/ PLL OSC OSCCONbits.OSWEN = 1; //enable OSC change while(OSCCONbits.OSWEN != 0); //wait for OSC change while(OSCCONbits.LOCK != 1); //wait for PLL lock}
//master configurationvoid spi1_init() { SPI1BUF = 0; IFS0bits.SPI1IF = 0; //Clear the Interrupt Flag IEC0bits.SPI1IE = 0; //Disable The Interrupt // SPI1CON1 Register Settings SPI1CON1bits.DISSCK = 0; //Internal Serial Clock is Enabled SPI1CON1bits.DISSDO = 0; //SDOx pin is controlled by the module SPI1CON1bits.MODE16 = 1; //Communication is word-wide (16 bits)
SPI1CON1bits.CKE = 1; //Serial output data changes on transition from active to idle (high to low) SPI1CON1bits.SSEN = 0; SPI1CON1bits.CKP = 0; //Idle state for clock is a low level; active state is a high level SPI1CON1bits.MSTEN = 1; //Master Mode enabled SPI1CON1bits.SMP = 1; //Input Data is sampled at the end of data output time //In this case, falling clock edge //Setup ~SS1 control. TRISBbits.TRISB2 = 0; PORTBbits.RB2 = 0;
//SPI1STAT Register Settings SPI1STATbits.SPIROV = 0; //No overflow SPI1STATbits.SPIEN = 1; //Enable SPI Module //Interrupt Controller Settings IFS0bits.SPI1IF = 0; //Clear the Interrupt Flag IEC0bits.SPI1IE = 0; //Enable The Interrupt - no interrupts}
//slave configurationvoid spi2_init() { TRISBbits.TRISB15 = 0; //set mode change pin low PORTBbits.RB15 = 0;
F-8
ECE 477 Final Report Spring 2009
IFS2bits.SPI2IF = 0; //Clear the Interrupt Flag IEC2bits.SPI2IE = 0; //Disable the Interrupt // SPI2CON1 Register Settings SPI2CON1bits.DISSCK = 0; //Internal Serial Clock is Enabled SPI2CON1bits.DISSDO = 0; //SDOx pin is controlled by the module SPI2CON1bits.MODE16 = 1; //Communication is word-wide (16 bits) SPI2CON1bits.SMP = 0; //Input Data is sampled at the middle of data output time SPI2CON1bits.CKE = 0; //Serial output data changes on transition from idle clock state to active clock state SPI2CON1bits.CKP = 0; //Idle state for clock is a low level active state is a high level SPI2CON1bits.MSTEN = 0; //Slave Mode Enabled //SPI2STAT Register Settings SPI2STATbits.SPIROV = 0; //No overflow SPI2STATbits.SPIEN = 1; //Enable SPI Module SPI2BUF = 0x0000; //Write data to be transmitted
//DMA Initializations IFS0bits.DMA1IF =0; IEC0bits.DMA1IE = 0; //interrupt enable off initially DMA1CONbits.MODE = 0x0; DMA1CONbits.AMODE = 0b01; //Post-increment off initially DMA1STA = __builtin_dmaoffset(spi2_rx_buf_a); DMA1STB = __builtin_dmaoffset(spi2_rx_buf_b); DMA1PAD = (volatile unsigned int) &SPI2BUF; DMA1CNT = 0; //count = 0 initially DMA1REQ = 0x0021; //Interrupt when SPI2 transfer done DMA1CONbits.CHEN=1;
//Interrupt Controller Settings IFS2bits.SPI2IF = 0; //Clear the Interrupt Flag}
void pwm_init() { TMR2 = 0; /* clear timer1 register */ PR2 = T2PERIOD; /* set period1 register */ T2CONbits.TCS = 0; //set clock as fosc/2 T2CONbits.TCKPS = 2; //set 1:8 prescale IPC1bits.T2IP = 4; /* set priority level */ IFS0bits.T2IF = 0; /* clear interrupt flag */ SRbits.IPL = 3; /* enable CPU priority levels 4-7 */
OC1CONbits.OCM = 6; //enable PWM OC mode w/out fault pin OC2CONbits.OCM = 6; OC3CONbits.OCM = 6; OC4CONbits.OCM = 6; OC5CONbits.OCM = 6; OC6CONbits.OCM = 6;/* OC1RS = PWMNEUTRAL; //set PWM duty latch (write only to this) OC1R = PWMNEUTRAL; //initialize PWM duty cycle OC2RS = PWMNEUTRAL; OC2R = PWMNEUTRAL; OC3RS = PWMNEUTRAL; OC3R = PWMNEUTRAL; OC4RS = PWMNEUTRAL; OC4R = PWMNEUTRAL; OC5RS = PWMNEUTRAL; OC5R = PWMNEUTRAL; OC6RS = PWMNEUTRAL; OC6R = PWMNEUTRAL;*/
F-9
ECE 477 Final Report Spring 2009
OC1RS = PWMOFF; //set PWM duty latch (write only to this) OC1R = PWMOFF; //initialize PWM duty cycle OC2RS = PWMOFF; OC2R = PWMOFF; OC3RS = PWMOFF; OC3R = PWMOFF; OC4RS = PWMOFF; OC4R = PWMOFF; OC5RS = PWMOFF; OC5R = PWMOFF; OC6RS = PWMOFF; OC6R = PWMOFF;
T2CONbits.TON = 1; /* start the timer */ IEC0bits.T2IE = 1; /* enable interrupts */}
void LED_init() { TRISGbits.TRISG12 = 0; TRISGbits.TRISG13 = 0; TRISGbits.TRISG14 = 0; PORTGbits.RG12 = 1; PORTGbits.RG13 = 1; PORTGbits.RG14 = 1; TRISFbits.TRISF0 = 1; //left red off TRISFbits.TRISF1 = 0; //left green on TRISGbits.TRISG0 = 0; //right green on TRISGbits.TRISG1 = 1; //right red off
}
//SPI 1 receive buffer writevoid spi1RxBufWr(int wr_data) { spi1_rx_buffer[spi1_rx_buf_head] = wr_data; spi1_rx_buf_head = (spi1_rx_buf_head + 1) & (SPI1RXBUFSIZE - 1);}
//SPI 1 transmit buffer readint spi1RxBufRead() { int read_dat;
read_dat = spi1_rx_buffer[spi1_rx_buf_tail]; spi1_rx_buf_tail = (spi1_rx_buf_tail + 1) & (SPI1RXBUFSIZE - 1); return read_dat;}
void freq_init() { freq_limits[0].min = STR0MIN; freq_limits[0].max = STR0MAX; freq_limits[1].min = STR1MIN; freq_limits[1].max = STR1MAX; freq_limits[2].min = STR2MIN; freq_limits[2].max = STR2MAX; freq_limits[3].min = STR3MIN; freq_limits[3].max = STR3MAX; freq_limits[4].min = STR4MIN; freq_limits[4].max = STR4MAX; freq_limits[5].min = STR5MIN; freq_limits[5].max = STR5MAX;}
F-10
ECE 477 Final Report Spring 2009
//***************************//**START MAIN*********//***************
int main() { unsigned int i = 0; int len; volatile int j=0; volatile int j2=0;
char response[200]; midiint note;
tunings_from_flash(); //this acts as the flash init osc_init(); midi_init(); freq_init(); //init flags, etc diskFlag = 0; transcribeFlag = 0; midi_buf_head = 0; midi_buf_tail = 0; midi_bytecnt = 0;
spi1_init(); spi2_init(); UART_Init(); LCD_Init(); LED_init();
pwm_init(); usb_init();
//Configure Pushbuttons as inputs. TRISDbits.TRISD8 = 1; TRISDbits.TRISD9 = 1; TRISDbits.TRISD10 = 1; TRISDbits.TRISD11 = 1;
while (i < 30000) i++;
// Initialize the default tuning setting for (i=0;i<6;i++) tuning_array[i] = 2; tunings_to_flash();
// Character values for each string tuning option String_Values[0][0][0] = 'C'; String_Values[0][0][1] = ' '; String_Values[1][0][0] = 'F'; String_Values[1][0][1] = ' '; String_Values[2][0][0] = 'A'; String_Values[2][0][1] = '#'; String_Values[3][0][0] = 'D'; String_Values[3][0][1] = ' '; String_Values[4][0][0] = 'G'; String_Values[4][0][1] = ' '; String_Values[5][0][0] = 'C'; String_Values[5][0][1] = ' ';
String_Values[0][1][0] = 'D'; String_Values[0][1][1] = ' '; String_Values[1][1][0] = 'G'; String_Values[1][1][1] = ' ';
F-11
ECE 477 Final Report Spring 2009
String_Values[2][1][0] = 'C'; String_Values[2][1][1] = ' '; String_Values[3][1][0] = 'E'; String_Values[3][1][1] = ' '; String_Values[4][1][0] = 'A'; String_Values[4][1][1] = ' '; String_Values[5][1][0] = 'D'; String_Values[5][1][1] = ' ';
String_Values[0][2][0] = 'E'; String_Values[0][2][1] = ' '; String_Values[1][2][0] = 'A'; String_Values[1][2][1] = ' '; String_Values[2][2][0] = 'D'; String_Values[2][2][1] = ' '; String_Values[3][2][0] = 'G'; String_Values[3][2][1] = ' '; String_Values[4][2][0] = 'B'; String_Values[4][2][1] = ' '; String_Values[5][2][0] = 'E'; String_Values[5][2][1] = ' ';
String_Values[0][3][0] = 'F'; String_Values[0][3][1] = ' '; String_Values[1][3][0] = 'B'; String_Values[1][3][1] = 'b'; String_Values[2][3][0] = 'D'; String_Values[2][3][1] = '#'; String_Values[3][3][0] = 'G'; String_Values[3][3][1] = '#'; String_Values[4][3][0] = 'C'; String_Values[4][3][1] = ' '; String_Values[5][3][0] = 'F'; String_Values[5][3][1] = ' ';
String_Values[0][4][0] = 'F'; String_Values[0][4][1] = '#'; String_Values[1][4][0] = 'B'; String_Values[1][4][1] = ' '; String_Values[2][4][0] = 'E'; String_Values[2][4][1] = ' '; String_Values[3][4][0] = 'A'; String_Values[3][4][1] = ' '; String_Values[4][4][0] = 'C'; String_Values[4][4][1] = '#'; String_Values[5][4][0] = 'F'; String_Values[5][4][1] = '#';
// Frequency values for each string tuning option Freq_Values[0][0] = 65; Freq_Values[0][1] = 73; Freq_Values[0][2] = 82; Freq_Values[0][3] = 87; Freq_Values[0][4] = 92;
Freq_Values[1][0] = 87; Freq_Values[1][1] = 98; Freq_Values[1][2] = 110; Freq_Values[1][3] = 117; Freq_Values[1][4] = 123;
Freq_Values[2][0] = 117; Freq_Values[2][1] = 131;
F-12
ECE 477 Final Report Spring 2009
Freq_Values[2][2] = 146; Freq_Values[2][3] = 156; Freq_Values[2][4] = 165;
Freq_Values[3][0] = 146; Freq_Values[3][1] = 165; Freq_Values[3][2] = 196; Freq_Values[3][3] = 208; Freq_Values[3][4] = 220;
Freq_Values[4][0] = 197; Freq_Values[4][1] = 220; Freq_Values[4][2] = 247; Freq_Values[4][3] = 262; Freq_Values[4][4] = 277;
Freq_Values[5][0] = 262; Freq_Values[5][1] = 294; Freq_Values[5][2] = 330; Freq_Values[5][3] = 349; Freq_Values[5][4] = 370;
while(1) { i++; j++; if (i == 100) { i = 0; //Poll Pushbuttons
top_button_pushed = !PORTDbits.RD8; right_button_pushed = !PORTDbits.RD9; bottom_button_pushed = !PORTDbits.RD10; left_button_pushed = !PORTDbits.RD11;
if ((top_button_pushed || right_button_pushed || bottom_button_pushed || left_button_pushed) && (button_pushed == 0)) { LCD_Button(); }
//Poll for MIDI data if (transcribeFlag == 1) {
//Is data buffer full? if (dsize >= BUFFER_SIZE) {
//If so, end MIDI transcribe and write to disk Menu_State = 103; LCD_Transcribe(); continue;
}
//Data in the PWM->MIDI buffer?while (!bufEmptyChk( MIDIBUFSIZE ,midi_buf_head, midi_buf_tail ) ) {
//Translate, put in write buffernote = midiBufRead();n(note.note, note.duration);
} } }
if (j == 20000) { j = 0; j2 += 1; if (j2 == 200) { //about 1 per sec j2 = 0;
F-13
ECE 477 Final Report Spring 2009
usbBufferWriteChar(CR); len = usbCommandSend(response, 200); if (len != 0) { if ( usbErrorCodeCheck(response, len) == 0 ) { diskFlag = 1; } else { diskFlag = 0; } } } } }}
void spi1ErrHandler(int spi1_err_code) { if(spi1_err_code == 0) { SPI1BUF = BADSTARTRCVD; }}
//***************************//**START ISRS*********//***************
void __attribute__ ((interrupt,auto_psv)) _SPI2Interrupt(void) { int temp; temp = SPI2BUF; PORTGbits.RG12 = PORTGbits.RG12 ^ 1; PORTGbits.RG13 = PORTGbits.RG13 ^ 1; PORTGbits.RG14 = PORTGbits.RG14 ^ 1; IFS2bits.SPI2IF = 0; //clear SPI2 interrupt }
void __attribute__((interrupt,auto_psv)) _DMA1Interrupt(void){ int min,max; int new_freq; int badsample; badsample = 0; new_freq = spi2_rx_buf_a[1];
if(tuning_mode == TUNING_START){ min = freq_limits[motor_state].min; max = freq_limits[motor_state].max; if(new_freq >= max) { if((new_freq >> 1) <= max && (new_freq >> 1) >= min) { new_freq >>= 1; } else if((new_freq >> 2) <= max && (new_freq >> 2) >= min) { new_freq >>= 2; } else if((new_freq / 3) <= max && (new_freq / 3) >= min) { new_freq /= 3; } else { badsample = 1; } } } else if(tuning_mode == MIDI_START) { //MIDI note buffer writing
note_history[7] = note_history[6]; note_history[6] = note_history[5]; note_history[5] = note_history[4]; note_history[4] = note_history[3]; note_history[3] = note_history[2]; note_history[2] = note_history[1];
F-14
ECE 477 Final Report Spring 2009
note_history[1] = note_history[0]; note_history[0] = freqToMidi(new_freq); if(note_history[0] != note_history[1]) { if(note_history[1] == FREQIDLE) { // Diana edit midiBufWr(midi_tics,0); } else { midiBufWr(midi_tics, note_history[1]); midi_note = note_history[0]; } midi_tics = TICSPERINTR >> 1; } else { midi_tics += TICSPERINTR; } }
PORTGbits.RG12 = PORTGbits.RG12 ^ 1; PORTGbits.RG13 = PORTGbits.RG13 ^ 1; PORTGbits.RG14 = PORTGbits.RG14 ^ 1;
if(badsample == 0) { if (stallCount <= 0) { freq_history[0][7] = freq_history[0][6]; freq_history[0][6] = freq_history[0][5]; freq_history[0][5] = freq_history[0][4]; freq_history[0][4] = freq_history[0][3]; freq_history[0][3] = freq_history[0][2]; freq_history[0][2] = freq_history[0][1]; freq_history[0][1] = freq_history[0][0]; freq_history[0][0] = new_freq; spi2_newdata_flag = 1; motor_wait = 0; } } else { spi2_newdata_flag = 0; }
IFS0bits.DMA1IF = 0; // Clear the DMA1 Interrupt Flag}
//PWM and Timer 2 ISRvoid __attribute__ ((interrupt,shadow,auto_psv)) _T2Interrupt(void){ //Note to Joe: Add some filtering here to ignore 'glitch' packets // static int str_initialized = 0; int min,max,farfreq,closefreq; int ideal_freq; static int current_freq=0; static int i = 0; static int interrupt_count = 0; int oneSecond = 75; int freqAcc = 0; float freqAvg = 0;
stallCount--;
if(spi2_newdata_flag == 1 && halt_tuning == 0 && tuning_mode == TUNING_START && stallCount <= 0){ stallCount = 0;
F-15
ECE 477 Final Report Spring 2009
spi2_newdata_flag = 0; currently_tuning = 1; ++interrupt_count; motor_wait++; if (interrupt_count == 5) { interrupt_count = 0; LCD_Tune(0); } if(motor_wait < 7) { if(freq_history[0][0] == user_tuning[motor_state]) { //string in tune to start writePWM(motor_state,0); if(motor_state == 5) { Menu_State = 51; LCD_Tune(1); //display tuning complete! tuningEnd(DONE_HALT); } else { motor_state++; stallCount = oneSecond; for(i=0;i<8;i++) { freq_history[0][i] = 0; } LCD_Tune(0); //display next string tuning information str_initialized = 0; } } else { freqAcc = 0; for (i=0;i<8;i++) { freqAcc += freq_history[0][i]; } freqAvg = (float)freqAcc / 8; ideal_freq = user_tuning[motor_state]; if (abs((float)(freq_history[0][0]) - freqAvg) / (freqAvg+1) < 0.15) { current_freq = freq_history[0][0]; } else {
current_freq = freqAvg;}if(current_freq == 0) {
//Do nothing, because we're waiting for the first value; asm volatile("nop");
} else if(current_freq < (ideal_freq - TUNING_PRECISION)) { min = freq_limits[motor_state].min; max = ideal_freq; farfreq = min + ((max - min) >> 1); closefreq = farfreq + ((max - min) >> 2); if(current_freq < farfreq) { writePWM(motor_state,PWMFAST); } else if(current_freq < closefreq) { writePWM(motor_state,PWMMEDIUM); } else { writePWM(motor_state,PWMSLOW); } } else if(current_freq > (ideal_freq + TUNING_PRECISION)) { min = ideal_freq; max = freq_limits[motor_state].max; farfreq = min + ((max - min) >> 1); closefreq = min + ((max - min) >> 2); if(current_freq > farfreq) { writePWM(motor_state,-1 * PWMFAST); } else if(current_freq > closefreq) { writePWM(motor_state,-1 * PWMMEDIUM); } else { writePWM(motor_state,-1 * PWMSLOW); }
F-16
ECE 477 Final Report Spring 2009
} else { writePWM(motor_state,0); if(motor_state != 5) { writePWM(motor_state,0); for(i=0;i<8;i++){ freq_history[0][i] = 0; } motor_state++; stallCount = oneSecond; LCD_Tune(0); //display next string tuning information } else { Menu_State = 51; LCD_Tune(1); //display tuning complete! tuningEnd(DONE_HALT); } } } } else{ asm volatile("nop");//motor_wait--; writePWM(motor_state, 0); } } else if(tuning_mode == DEMOMODE) { i++; spi2_newdata_flag = 0; if (i == 50) { LCD_Sound(0); i = 0; } }
IFS0bits.T2IF = 0; //clear interrupt flag}
freq_lookup.c#include "communicate.h"
#define FLOOKUPLEN 64#define FLOOKSTARTNUM 32int freq_lookup[FLOOKUPLEN] = {52,55,58,62,65,69,73,77, 82,87,92,98,104,110,117,123,131,139, 147,156,165,175,185,196,208,220,233,247, 262,277,294,311,330,349,370,392,415,440, 466,494,523,554,587,622,659,698,698,740, 784,831,880,932,988,1047,1109,1175,1245,1319, 1397,1480,1568,1661,1760,1865};
int freqToMidi(int freq_in) { int midi_num = FLOOKUPLEN >> 1; int increment = FLOOKUPLEN >> 2; int sum; while(increment != 0) { if(freq_lookup[midi_num] < freq_in) { midi_num += increment; } else if(freq_lookup[midi_num] > freq_in) { midi_num -= increment; } else { increment = 0; } increment = increment >> 1; }
if(freq_lookup[midi_num] > freq_in) {
F-17
ECE 477 Final Report Spring 2009
sum = freq_lookup[midi_num] + freq_lookup[midi_num - 1]; if(freq_in < (sum >> 1)) { midi_num--; } } else { sum = freq_lookup[midi_num] + freq_lookup[midi_num + 1]; if(freq_in > (sum >> 1)) { midi_num++; } }
if(freq_in == FREQIDLE) { midi_num = 0; } else { midi_num += FLOOKSTARTNUM; } return midi_num;}
LCD_functions.c#include "p24HJ128GP306.h"#include "LCD_Functions.h"#include "communicate.h"#include "uart.h"#include "usb_funcs.h"#include "midi.h"#include <string.h>#include "motor.h"
unsigned int Menu_State; // LCD Func. variables usedunsigned int User_Selection;unsigned int Temp[6];unsigned int uart2_tx_buffer[UART2TXBUFSIZE];unsigned int uart2_tx_buf_tail = 0;unsigned int uart2_tx_buf_head = 0;unsigned int uart2_tx_buf_holder;
extern volatile int button_pushed; // Global variables usedextern volatile int top_button_pushed;extern volatile int right_button_pushed;extern volatile int left_button_pushed;extern volatile int bottom_button_pushed;
extern unsigned int String_Values[6][5][2];extern int Freq_Values[6][5];extern int String_State[6];extern int motor_state;extern volatile int freq_history[6][HISTLENGTH];extern int user_tuning[6];
extern unsigned int tuning_array[64];extern unsigned int *default_tuning;extern unsigned int *user_tuning1;extern unsigned int *user_tuning2;extern unsigned int *user_tuning3;extern unsigned int *user_tuning4;extern unsigned int *user_tuning5;extern unsigned int *flash_pssc;
//MIDI flagextern int transcribeFlag;
F-18
ECE 477 Final Report Spring 2009
//MIDI file nameextern file_desc *midi_file;
extern int diskFlag;
void LCD_Button(void) { // Called when a button is pressed. int i, j, j2, len; int status; char response[200];
button_pushed = 6;
if (Menu_State < 10){ Menu_State = 10; LCD_Main(1); } else if (Menu_State < 20) { // Main screen is displayed if (bottom_button_pushed == 1) { ++Menu_State; if (Menu_State == 13) { Menu_State = 10; } LCD_Main(0); } else if (top_button_pushed == 1) { --Menu_State; if (Menu_State == 9) { Menu_State = 12; } LCD_Main(0); } else if (right_button_pushed == 1) { // Tune, Transcribe, or PSSC demo if (Menu_State == 10) { // Tune chosen Menu_State = 20; LCD_Setting(1); } else if (Menu_State == 11) { // Transcribe chosen if (diskFlag == 0) { Menu_State = 100; LCD_Transcribe(); //Clear all previous statuses status = usbStatusRead(); while (status & 0x2) { usbResponseDelay(); len = usbInitRead(response, 200); if (len == 0) break; status = usbStatusRead(); for (i=0;i<20;i++) { usbResponseDelay(); } }
while (diskFlag == 0) { j++; if (j == 20000) { j = 0; j2 += 1; if (j2 == 1000) { //about 1 per sec j2 = 0; usbBufferWriteChar(CR); len = usbCommandSend(response, 200); if (len != 0) { if ( usbErrorCodeCheck(response, len) == 0 ) { diskFlag = 1; } } } }
F-19
ECE 477 Final Report Spring 2009
} } Menu_State = 101; LCD_Transcribe(); } else if (Menu_State == 12) { // PSSC chosen Menu_State = 200; LCD_PSSC(1); } } } else if (Menu_State < 30) { // Tuning Settings Screen is displayed if (bottom_button_pushed == 1){ ++Menu_State; if (Menu_State == 26){ Menu_State = 20; } LCD_Setting(0); } else if (top_button_pushed == 1){ --Menu_State; if (Menu_State == 19){ Menu_State = 25; } LCD_Setting(0); } else if (left_button_pushed == 1) { // Cancel back to Main Screen Menu_State = 10; LCD_Main(1); } else if (right_button_pushed == 1){ User_Selection = Menu_State - 20; if (Menu_State == 20) { // Default setting chosen for (i=0;i<6;i++) { user_tuning[i] = Freq_Values[i][tuning_array[i]]; } for(i=0;i<8;i++){ freq_history[0][i] = 0; } Menu_State = 50; LCD_Tune(1); } else { // User Setting chosen tunings_from_flash(); Menu_State = 30; LCD_User(1); } } } else if (Menu_State < 40) { // User tuning setting screen is displayed if (bottom_button_pushed == 1){ ++Menu_State; if (Menu_State > 31){ Menu_State = 30; } LCD_User(0); } else if (top_button_pushed == 1){ --Menu_State; if (Menu_State < 30){ Menu_State = 31; } LCD_User(0); } else if (left_button_pushed == 1) { // Cancel back to tuning setting screen Menu_State = 20; LCD_Setting(1); } else if (right_button_pushed == 1){ if (Menu_State == 30) { // Tune with current user setting for (i=0;i<6;i++) { user_tuning[i] = Freq_Values[i][tuning_array[User_Selection*6+i]]; } for(i=0;i<8;i++){
F-20
ECE 477 Final Report Spring 2009
freq_history[0][i] = 0; } Menu_State = 50; LCD_Tune(1); } else { Menu_State = 40; // Edit current user setting LCD_Edit(1); } } } else if (Menu_State < 50){ // Edit user settings screen is displayed if (bottom_button_pushed == 1){ if (Menu_State == 42){ // Cycle through options Menu_State = 40; } else if (Menu_State < 42){ ++Menu_State; } else if (Menu_State == 43){ // Adjust string tunning setting if (tuning_array[User_Selection*6] > 0){ --tuning_array[User_Selection*6]; } } else if (Menu_State == 44){ if (tuning_array[User_Selection*6+1] > 0){ --tuning_array[User_Selection*6+1]; } } else if (Menu_State == 45){ if (tuning_array[User_Selection*6+2] > 0){ --tuning_array[User_Selection*6+2]; } } else if (Menu_State == 46){ if (tuning_array[User_Selection*6+3] > 0){ --tuning_array[User_Selection*6+3]; } } else if (Menu_State == 47){ if (tuning_array[User_Selection*6+4] > 0){ --tuning_array[User_Selection*6+4]; } } else if (Menu_State == 48){ if (tuning_array[User_Selection*6+5] > 0){ --tuning_array[User_Selection*6+5]; } } LCD_Edit(0); } else if (top_button_pushed == 1) { if (Menu_State == 40) { // Cycle through options Menu_State = 42; } else if (Menu_State < 43) { --Menu_State; } else if (Menu_State == 43){ // Adjust string tunning setting if (tuning_array[User_Selection*6] < 4) { ++tuning_array[User_Selection*6]; } } else if (Menu_State == 44){ if (tuning_array[User_Selection*6+1] < 4) { ++tuning_array[User_Selection*6+1]; } } else if (Menu_State == 45){ if (tuning_array[User_Selection*6+2] < 4) { ++tuning_array[User_Selection*6+2]; } } else if (Menu_State == 46) { if (tuning_array[User_Selection*6+3] < 4) { ++tuning_array[User_Selection*6+3]; } } else if (Menu_State == 47) { if (tuning_array[User_Selection*6+4] < 4) {
F-21
ECE 477 Final Report Spring 2009
++tuning_array[User_Selection*6+4]; } } else if (Menu_State == 48) { if (tuning_array[User_Selection*6+5] < 4) { ++tuning_array[User_Selection*6+5]; } } LCD_Edit(0); } else if (left_button_pushed == 1) { tunings_from_flash(); if (Menu_State < 43) { // Cancel back to the User setting screen Menu_State = 30; LCD_User(1); } else { // Cancel from string adjustments to options section Menu_State = 40; LCD_Edit(0); } } else if (right_button_pushed == 1) { if (Menu_State == 40) { // Select option Menu_State = 43; LCD_Edit(0); } else if (Menu_State == 41) { tunings_to_flash(); Menu_State = 40; LCD_Edit(0); } else if (Menu_State == 42) { for (i=0;i<6;i++){ tuning_array[User_Selection*6 + i] = tuning_array[i]; } LCD_Edit(0); } else if (Menu_State < 48) { // Lock in current setting, move next ++Menu_State; LCD_Edit(0); } else { Menu_State = 40; LCD_Edit(0); } } } else if (Menu_State < 60) { // Autotuning mode status screens are displayed if (left_button_pushed == 1) { // Cancel back to the Tuning settings screen
OC1RS = PWMOFF; //set PWM duty latch (write only to this) OC1R = PWMOFF; //initialize PWM duty cycle OC2RS = PWMOFF; OC2R = PWMOFF; OC3RS = PWMOFF; OC3R = PWMOFF; OC4RS = PWMOFF; OC4R = PWMOFF; OC5RS = PWMOFF;
OC5R = PWMOFF; OC6RS = PWMOFF; OC6R = PWMOFF; changeMode(MODE_STOP); if (Menu_State == 51) { Menu_State = 10; LCD_Main(1); } else {
Menu_State = 20; LCD_Setting(1); } } } else if (Menu_State == 101) { // Waiting to record audio screen is displayed if (left_button_pushed == 1) { // Cancel back to the Main Menu screen Menu_State = 10;
F-22
ECE 477 Final Report Spring 2009
LCD_Main(1); } else if (right_button_pushed == 1) { // Start recording Menu_State = 102; LCD_Transcribe(); } } else if (Menu_State == 102) { // Recording audio screen is displayed if (left_button_pushed == 1) { // Cancel recording, back to Waiting screen Menu_State = 101; LCD_Transcribe(); } else if (right_button_pushed == 1) { // Stop recording, save MIDI file. Menu_State = 103; LCD_Transcribe(); } } else if (Menu_State == 103) { // Writing MIDI file screen displayed if (left_button_pushed == 1) { Menu_State = 101; LCD_Transcribe(); } } else if (Menu_State < 210) { // PSSC demo screen is displayed if (bottom_button_pushed == 1) { ++Menu_State; if (Menu_State == 205) { Menu_State = 200; } LCD_PSSC(0); } else if (top_button_pushed == 1) { --Menu_State; if (Menu_State == 199) { Menu_State = 204; } LCD_PSSC(0); } else if (left_button_pushed == 1) { Menu_State = 10; LCD_Main(1); } else if (right_button_pushed == 1) { if (Menu_State == 200) { Menu_State = 210;
String_State[0] = 1; String_State[1] = 1; String_State[2] = 1; String_State[3] = 1; String_State[4] = 1; String_State[5] = 1;
LCD_Motors(1); } else if (Menu_State == 201) { Menu_State = 220; LCD_Sound(1); } else if (Menu_State == 202) { //MIDI demo if (diskFlag == 0) { Menu_State = 230; LCD_USB(); while (diskFlag == 0) { j++; if (j == 20000) { j = 0; j2 += 1; if (j2 == 1000) { //about 1 per sec j2 = 0; usbBufferWriteChar(CR); len = usbCommandSend(response, 200); if (len != 0) {
F-23
ECE 477 Final Report Spring 2009
if ( usbErrorCodeCheck(response, len) == 0 ){ diskFlag = 1; } } } } } } Menu_State = 231; LCD_USB(); } else if (Menu_State == 203){ Menu_State = 240; tunings_from_flash(); LCD_FLASH(1); } else if (Menu_State == 204){ Menu_State = 250; LCD_UI(1); } } } else if (Menu_State < 220) { // Motor demo screen displayed if (bottom_button_pushed == 1){ String_State[Menu_State-210] = String_State[Menu_State-210] * (-1); LCD_Motors(0); } else if (top_button_pushed == 1) { String_State[Menu_State-210] = String_State[Menu_State-210] * (-1); LCD_Motors(0); } else if (left_button_pushed == 1) { Menu_State = 200; OC1RS = PWMOFF; //Turn off all motors OC2RS = PWMOFF; OC3RS = PWMOFF; OC4RS = PWMOFF; OC5RS = PWMOFF; OC6RS = PWMOFF;
LCD_PSSC(1); } else if (right_button_pushed == 1) { ++Menu_State; if (Menu_State == 216) { Menu_State = 210; } LCD_Motors(0); } } else if (Menu_State < 230) { // Sound Analysis demo screen displayed if (left_button_pushed == 1) { Menu_State = 200; changeMode(MODE_STOP); LCD_PSSC(1); } } else if (Menu_State < 240) { // USB interface demo screen displayed if (left_button_pushed == 1) { Menu_State = 200; LCD_PSSC(1); } } else if (Menu_State < 250){ // FLASH save demo screen displayed if (bottom_button_pushed == 1) { if (*flash_pssc == 0){ *flash_pssc = 9; } else { --(*flash_pssc); } LCD_FLASH(0);
F-24
ECE 477 Final Report Spring 2009
} else if (top_button_pushed == 1) { if (*flash_pssc == 9){ *flash_pssc = 0; } else { ++(*flash_pssc); } LCD_FLASH(0); } else if (left_button_pushed == 1) { Menu_State = 200; tunings_from_flash(); LCD_PSSC(1); } else if (right_button_pushed == 1) { tunings_to_flash(); LCD_FLASH(1); } } else if (Menu_State < 260){ // UI demo screen displayed if (bottom_button_pushed == 1) { --Menu_State; if (Menu_State < 250){ Menu_State = 254; } LCD_UI(0); } else if (top_button_pushed == 1) { ++Menu_State; if (Menu_State > 254) { Menu_State = 250; } LCD_UI(0); } else if (left_button_pushed == 1) { Menu_State = 200; LCD_PSSC(1); } } bottom_button_pushed = 0; // Clear button pressed flags. top_button_pushed = 0; left_button_pushed = 0; right_button_pushed = 0;}
void uart2TxBufWr(unsigned int wr_data) { // UART 1 transmit buffer write while(bufFullChk(UART2TXBUFSIZE,uart2_tx_buf_head,uart2_tx_buf_tail) == 1){ asm volatile("nop"); // wait until the buffer is not full }
uart2_tx_buffer[uart2_tx_buf_head] = wr_data; uart2_tx_buf_head = (uart2_tx_buf_head + 1) & (UART2TXBUFSIZE - 1);}
void uart2TxBufWrStr(char *data, int len) { int i; for (i=0;i<len;i++) { uart2TxBufWr((unsigned int)data[i]); }}
unsigned int uart2TxBufRead(void){ // UART 1 transmit buffer read unsigned int read_dat; read_dat = uart2_tx_buffer[uart2_tx_buf_tail]; uart2_tx_buf_tail = (uart2_tx_buf_tail + 1) & (UART2TXBUFSIZE - 1); return read_dat;}
void __attribute__((interrupt, shadow,auto_psv)) _T1Interrupt(void) { IFS0bits.T1IF = 0;
F-25
ECE 477 Final Report Spring 2009
uart2_tx_buf_holder = bufEmptyChk(UART2TXBUFSIZE,uart2_tx_buf_head,uart2_tx_buf_tail) ? 0xFFFF : uart2TxBufRead();
if(uart2_tx_buf_holder != 0xFFFF) { U2TXREG = uart2_tx_buf_holder; } else { if (button_pushed != 0) { button_pushed--; } } }
void __attribute__((__interrupt__,auto_psv)) _U2TXInterrupt(void) { IFS1bits.U2TXIF = 0; // clear Tx interrupt flag
uart2_tx_buf_holder = bufEmptyChk(UART2TXBUFSIZE,uart2_tx_buf_head,uart2_tx_buf_tail) ? 0xFFFF : uart2TxBufRead();
if(uart2_tx_buf_holder != 0xFFFF) { U2TXREG = uart2_tx_buf_holder; }}
void UART_Init(void) { // Only needs run once at powerup/reset U2MODEbits.ABAUD = 0; // Baud rate measurement disabled U2MODEbits.BRGH = 0; // Low speed baud rate U2MODEbits.PDSEL = 0; // 8 bit data, no parity U2MODEbits.STSEL = 0; // 1 stop bit
U2BRG = BRGVAL;
U2STAbits.UTXINV = 1;
U2STAbits.UTXISEL0 = 1; // Interrupt when one Tx character is transmitted U2STAbits.UTXISEL1 = 1;
IEC1bits.U2TXIE = 0; // Enable UART Tx interrupt (found in Data sheet) TMR1 = 0; T1CONbits.TCKPS = 0b10; PR1 = T1PERIOD; T1CONbits.TON = 1; IEC0bits.T1IE = 1; //Enable T1 interrupt
U2MODEbits.UARTEN = 1; // Enable UART while (U2MODEbits.UARTEN != 1) {}; //Wait for UARTEN U2STAbits.UTXEN = 1; // Enable UART Tx}
void LCD_Init(void) { // Only needs run once at powerup/reset int i, j; Menu_State = 0; User_Selection = 0; uart2_tx_buf_head = 0; uart2_tx_buf_tail = 0;
for (i=0;i<20000;i++) { for (j=0;j<100;j++) {} }
uart2TxBufWr(HIDECURSOR); uart2TxBufWr(SCROLLOFF); uart2TxBufWr(WRAPON);
F-26
ECE 477 Final Report Spring 2009
LCD_Instructions();
uart2TxBufWr(BOOTSCREEN); uart2TxBufWr(5);}
void LCD_Instructions(void) { uart2TxBufWr(CLEARDISP);
uart2TxBufWrStr("For most screens: ", 20); uart2TxBufWrStr("up/down to scroll, ", 20); uart2TxBufWrStr("right to select, ", 20); uart2TxBufWrStr("and left to cancel.", 19);}
void LCD_Main(unsigned int New){ // Called when a change is made to Main screen if (New == 1) {
uart2TxBufWr(CLEARDISP); uart2TxBufWrStr("das Autot", 9); uart2TxBufWr(FANCYU); uart2TxBufWr('n'); uart2TxBufWr('r');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(1); uart2TxBufWrStr("Autot", 5); uart2TxBufWr(FANCYU); uart2TxBufWr('n');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(2); uart2TxBufWrStr("Transcribe", 10);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(3); uart2TxBufWrStr("PSSC Demo", 9); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(1);
if (Menu_State == 10) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(2);
if (Menu_State == 11) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS);
F-27
ECE 477 Final Report Spring 2009
uart2TxBufWr(2); uart2TxBufWr(3);
if (Menu_State == 12){ uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }}
void LCD_Setting(unsigned int New){ // Called when a change is made to the Tuning Settings Selection screen if (New == 1) { uart2TxBufWr(CLEARDISP);
uart2TxBufWrStr(" Tuning Settings", 17); uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1); uart2TxBufWrStr("Default", 7); uart2TxBufWr(CURSORPOS); uart2TxBufWr(12); uart2TxBufWr(1); uart2TxBufWrStr("User 3", 6);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2); uart2TxBufWrStr("User 1", 6); uart2TxBufWr(CURSORPOS); uart2TxBufWr(12); uart2TxBufWr(2); uart2TxBufWrStr("User 4", 6);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(3); uart2TxBufWrStr("User 2", 6); uart2TxBufWr(CURSORPOS); uart2TxBufWr(12); uart2TxBufWr(3); uart2TxBufWrStr("User 5", 6); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(1);
if (Menu_State == 20) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2);
if (Menu_State == 21) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
F-28
ECE 477 Final Report Spring 2009
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(3);
if (Menu_State == 22) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(11); uart2TxBufWr(1);
if (Menu_State == 23) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(11); uart2TxBufWr(2);
if (Menu_State == 24) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(11); uart2TxBufWr(3);
if (Menu_State == 25) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }}
void LCD_User(unsigned int New) { if (New == 1) { uart2TxBufWr(CLEARDISP);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(1); uart2TxBufWrStr("Tune with User ", 15); uart2TxBufWr(User_Selection + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(2); uart2TxBufWrStr("Edit User ", 10); uart2TxBufWr(User_Selection + 48); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1);
if (Menu_State == 30) { uart2TxBufWr(MUSICNOTE);
F-29
ECE 477 Final Report Spring 2009
} else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2);
if (Menu_State == 31) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }}
void LCD_Edit(unsigned int New) { if (New == 1) { uart2TxBufWr(CLEARDISP);
uart2TxBufWrStr("User ", 5); uart2TxBufWr(User_Selection + 48); uart2TxBufWr(CURSORPOS); uart2TxBufWr(8); uart2TxBufWr(0); uart2TxBufWr(UPARROW); uart2TxBufWr(' '); uart2TxBufWr(UPARROW); uart2TxBufWr(' '); uart2TxBufWr(UPARROW); uart2TxBufWr(' '); uart2TxBufWr(UPARROW); uart2TxBufWr(' '); uart2TxBufWr(UPARROW); uart2TxBufWr(' '); uart2TxBufWr(UPARROW);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1); uart2TxBufWrStr("Edit", 4);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2); uart2TxBufWrStr("Save ", 7); uart2TxBufWr(DOWNARROW); uart2TxBufWr(' '); uart2TxBufWr(DOWNARROW); uart2TxBufWr(' '); uart2TxBufWr(DOWNARROW); uart2TxBufWr(' '); uart2TxBufWr(DOWNARROW); uart2TxBufWr(' '); uart2TxBufWr(DOWNARROW); uart2TxBufWr(' '); uart2TxBufWr(DOWNARROW);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(3); uart2TxBufWrStr("Default", 7); }
uart2TxBufWr(CURSORPOS);
F-30
ECE 477 Final Report Spring 2009
uart2TxBufWr(0); uart2TxBufWr(1);
if (Menu_State == 40) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2);
if (Menu_State == 41) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(3);
if (Menu_State == 42) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(8); uart2TxBufWr(3);
if (Menu_State == 43) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' ');
if (Menu_State == 44) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' ');
if (Menu_State == 45) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' ');
if (Menu_State == 46) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' ');
F-31
ECE 477 Final Report Spring 2009
if (Menu_State == 47) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' ');
if (Menu_State == 48) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(8); uart2TxBufWr(1);
uart2TxBufWr(String_Values[0][tuning_array[User_Selection*6]][0]); uart2TxBufWr(String_Values[0][tuning_array[User_Selection*6]][1]);
uart2TxBufWr(String_Values[1][tuning_array[User_Selection*6+1]][0]); uart2TxBufWr(String_Values[1][tuning_array[User_Selection*6+1]][1]);
uart2TxBufWr(String_Values[2][tuning_array[User_Selection*6+2]][0]); uart2TxBufWr(String_Values[2][tuning_array[User_Selection*6+2]][1]);
uart2TxBufWr(String_Values[3][tuning_array[User_Selection*6+3]][0]); uart2TxBufWr(String_Values[3][tuning_array[User_Selection*6+3]][1]);
uart2TxBufWr(String_Values[4][tuning_array[User_Selection*6+4]][0]); uart2TxBufWr(String_Values[4][tuning_array[User_Selection*6+4]][1]);
uart2TxBufWr(String_Values[5][tuning_array[User_Selection*6+5]][0]); uart2TxBufWr(String_Values[5][tuning_array[User_Selection*6+5]][1]);}
void LCD_Tune(unsigned int New) { if (New == 1) { uart2TxBufWr(CLEARDISP); }
if (Menu_State == 50) { if (New == 1) { changeMode(TUNING_START);
uart2TxBufWrStr(" 1 2 3 4 5 6", 17); uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2); uart2TxBufWrStr("Current:", 8); uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(3); uart2TxBufWrStr("Setting:", 8); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1);
if (motor_state == 0) { if (String_State[0] == 1) {
F-32
ECE 477 Final Report Spring 2009
uart2TxBufWr(UPARROW); } else if (String_State[0] == 0) { uart2TxBufWr('-'); } else if (String_State[0] == -1) { uart2TxBufWr(DOWNARROW); } } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (motor_state == 1) { if (String_State[1] == 1) { uart2TxBufWr(UPARROW); } else if (String_State[1] == 0){ uart2TxBufWr('-'); } else if (String_State[1] == -1){ uart2TxBufWr(DOWNARROW); } } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (motor_state == 2){ if (String_State[2] == 1){ uart2TxBufWr(UPARROW); } else if (String_State[2] == 0){ uart2TxBufWr('-'); } else if (String_State[2] == -1){ uart2TxBufWr(DOWNARROW); } } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (motor_state == 3){ if (String_State[3] == 1){ uart2TxBufWr(UPARROW); } else if (String_State[3] == 0){ uart2TxBufWr('-'); } else if (String_State[3] == -1){ uart2TxBufWr(DOWNARROW); } } else { uart2TxBufWr(' '); }
uart2TxBufWr(' ');
F-33
ECE 477 Final Report Spring 2009
uart2TxBufWr(' ');
if (motor_state == 4){ if (String_State[4] == 1) { uart2TxBufWr(UPARROW); } else if (String_State[4] == 0){ uart2TxBufWr('-'); } else if (String_State[4] == -1){ uart2TxBufWr(DOWNARROW); } } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (motor_state == 5){ if (String_State[5] == 1){ uart2TxBufWr(UPARROW); } else if (String_State[5] == 0){ uart2TxBufWr('-'); } else if (String_State[5] == -1){ uart2TxBufWr(DOWNARROW); } } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(9); uart2TxBufWr(2); uart2TxBufWr(freq_history[0][0]/100 + 48); uart2TxBufWr(freq_history[0][0]/10 - freq_history[0][0]/100*10 + 48); uart2TxBufWr(freq_history[0][0] - freq_history[0][0]/10*10 + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(9); uart2TxBufWr(3); uart2TxBufWr(Freq_Values[motor_state][tuning_array[User_Selection*6+motor_state]]/100 + 48); uart2TxBufWr(Freq_Values[motor_state][tuning_array[User_Selection*6+motor_state]]/10 - Freq_Values[motor_state][tuning_array[User_Selection*6+motor_state]]/100*10 + 48); uart2TxBufWr(Freq_Values[motor_state][tuning_array[User_Selection*6+motor_state]] - Freq_Values[motor_state][tuning_array[User_Selection*6+motor_state]]/10*10 + 48); }
else if (Menu_State == 51){ uart2TxBufWrStr("Autotune is complete", 20); uart2TxBufWrStr("returning to the", 16);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2); uart2TxBufWrStr("main menu.", 10); }
else if (Menu_State == 52){
F-34
ECE 477 Final Report Spring 2009
uart2TxBufWrStr("Autotune has failed.", 20); uart2TxBufWrStr("Please check motor ", 20); uart2TxBufWrStr("connections.", 12); }
}
void LCD_Transcribe(void) // Called when a change is made to Transcribing screens{ uart2TxBufWr(CLEARDISP);
if (Menu_State == 100){ uart2TxBufWrStr("Please insert an ", 20); uart2TxBufWrStr("acceptable flash ", 20); uart2TxBufWrStr("drive into the right", 20); uart2TxBufWrStr("side of the box.", 16); }
else if (Menu_State == 101){ uart2TxBufWr(CURSORPOS); uart2TxBufWr(5); uart2TxBufWr(1); uart2TxBufWrStr("Waiting...", 10);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(2); uart2TxBufWr(MUSICNOTE); uart2TxBufWrStr("Start Recording", 15); }
else if (Menu_State == 102){ uart2TxBufWr(CURSORPOS); uart2TxBufWr(5); uart2TxBufWr(1); uart2TxBufWrStr("Recording", 9);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(2); uart2TxBufWr(MUSICNOTE); uart2TxBufWrStr("Stop Recording", 14);
midi_file = MIDI_Create_File(DEFAULT_FNAME); transcribeFlag = 1; //Back to the main loop //until we're done recording. changeMode(MIDI_START); }
else if (Menu_State == 103){ changeMode(MODE_STOP);
uart2TxBufWrStr("Writing MIDI file to", 20); uart2TxBufWrStr("flash drive and", 15);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2);
uart2TxBufWrStr("returning to the", 16);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(3);
F-35
ECE 477 Final Report Spring 2009
uart2TxBufWrStr("main menu.", 10);
//Finalize MIDI file if (MIDI_Finalize(midi_file) == USB_SUCCESS) { transcribeFlag = 0; Menu_State = 10; LCD_Main(1); return; } }}
void LCD_PSSC(unsigned int New) { // PSSC demo screen if (New == 1) { uart2TxBufWr(CLEARDISP);
uart2TxBufWrStr("Which one?", 10);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(1); uart2TxBufWrStr("Motors FLASH", 14);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(2); uart2TxBufWrStr("Sound UI", 11);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(2); uart2TxBufWr(3); uart2TxBufWrStr("USB", 3); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1);
if (Menu_State == 200) { uart2TxBufWr(MUSICNOTE); } else{ uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2);
if (Menu_State == 201) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(3);
if (Menu_State == 202) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
F-36
ECE 477 Final Report Spring 2009
uart2TxBufWr(CURSORPOS); uart2TxBufWr(10); uart2TxBufWr(1);
if (Menu_State == 203) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(10); uart2TxBufWr(2);
if (Menu_State == 204) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }}
void LCD_Motors(unsigned int New) { if (New == 1) { uart2TxBufWr(CLEARDISP);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1); uart2TxBufWrStr("1 2 3 4 5 6", 16);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2); uart2TxBufWr('C'); uart2TxBufWr('W');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(7); uart2TxBufWr(2); uart2TxBufWr('C'); uart2TxBufWr('W');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(13); uart2TxBufWr(2); uart2TxBufWr('C'); uart2TxBufWr('W');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(4); uart2TxBufWr(3); uart2TxBufWr('C'); uart2TxBufWr('W');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(10); uart2TxBufWr(3); uart2TxBufWr('C'); uart2TxBufWr('W');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(16); uart2TxBufWr(3);
F-37
ECE 477 Final Report Spring 2009
uart2TxBufWr('C'); uart2TxBufWr('W'); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(0);
if (Menu_State == 210) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (Menu_State == 211) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (Menu_State == 212) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (Menu_State == 213) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (Menu_State == 214) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(' '); uart2TxBufWr(' ');
if (Menu_State == 215) { uart2TxBufWr(MUSICNOTE); } else { uart2TxBufWr(' '); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2);
if (String_State[0] == 1) {
F-38
ECE 477 Final Report Spring 2009
uart2TxBufWr(' '); OC1RS = PWMNEUTRAL + 25; } else { uart2TxBufWr('C'); OC1RS = PWMNEUTRAL - 25; }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(6); uart2TxBufWr(2);
if (String_State[2] == 1) { uart2TxBufWr(' '); OC3RS = PWMNEUTRAL + 25; } else { uart2TxBufWr('C'); OC3RS = PWMNEUTRAL - 25; }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(12); uart2TxBufWr(2);
if (String_State[4] == 1) { uart2TxBufWr(' '); OC5RS = PWMNEUTRAL + 25; } else { uart2TxBufWr('C'); OC5RS = PWMNEUTRAL - 25; }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(3);
if (String_State[1] == 1) { uart2TxBufWr(' '); OC2RS = PWMNEUTRAL + 25; } else { uart2TxBufWr('C'); OC2RS = PWMNEUTRAL - 25; }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(9); uart2TxBufWr(3);
if (String_State[3] == 1) { uart2TxBufWr(' '); OC4RS = PWMNEUTRAL + 25; } else { uart2TxBufWr('C'); OC4RS = PWMNEUTRAL - 25; }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(15); uart2TxBufWr(3);
if (String_State[5] == 1) { uart2TxBufWr(' '); OC6RS = PWMNEUTRAL + 25; } else { uart2TxBufWr('C');
F-39
ECE 477 Final Report Spring 2009
OC6RS = PWMNEUTRAL - 25; }}
void LCD_Sound(unsigned int New) { if (New == 1) { changeMode(DEMOMODE); uart2TxBufWr(CLEARDISP);
uart2TxBufWrStr("Frequency peaks at 1.", 22);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(10); uart2TxBufWr(1); uart2TxBufWr('4'); uart2TxBufWr('.');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(2); uart2TxBufWr('2'); uart2TxBufWr('.');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(10); uart2TxBufWr(2); uart2TxBufWr('5'); uart2TxBufWr('.');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(3); uart2TxBufWr('3'); uart2TxBufWr('.');
uart2TxBufWr(CURSORPOS); uart2TxBufWr(10); uart2TxBufWr(3); uart2TxBufWr('6'); uart2TxBufWr('.'); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(1); uart2TxBufWr(freq_history[0][0]/100 + 48); uart2TxBufWr(freq_history[0][0]/10 - freq_history[0][0]/100*10 + 48); uart2TxBufWr(freq_history[0][0] - freq_history[0][0]/10*10 + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(13); uart2TxBufWr(1); uart2TxBufWr(freq_history[0][3]/100 + 48); uart2TxBufWr(freq_history[0][3]/10 - freq_history[0][3]/100*10 + 48); uart2TxBufWr(freq_history[0][3] - freq_history[0][3]/10*10 + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(2); uart2TxBufWr(freq_history[0][1]/100 + 48); uart2TxBufWr(freq_history[0][1]/10 - freq_history[0][1]/100*10 + 48); uart2TxBufWr(freq_history[0][1] - freq_history[0][1]/10*10 + 48);
uart2TxBufWr(CURSORPOS);
F-40
ECE 477 Final Report Spring 2009
uart2TxBufWr(13); uart2TxBufWr(2); uart2TxBufWr(freq_history[0][4]/100 + 48); uart2TxBufWr(freq_history[0][4]/10 - freq_history[0][4]/100*10 + 48); uart2TxBufWr(freq_history[0][4] - freq_history[0][4]/10*10 + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(3); uart2TxBufWr(3); uart2TxBufWr(freq_history[0][2]/100 + 48); uart2TxBufWr(freq_history[0][2]/10 - freq_history[0][2]/100*10 + 48); uart2TxBufWr(freq_history[0][2] - freq_history[0][2]/10*10 + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(13); uart2TxBufWr(3); uart2TxBufWr(freq_history[0][5]/100 + 48); uart2TxBufWr(freq_history[0][5]/10 - freq_history[0][5]/100*10 + 48); uart2TxBufWr(freq_history[0][5] - freq_history[0][5]/10*10 + 48);}
void LCD_USB(void) { int i, j; file_desc *file; int status = 0x2; char response[200]; int resLen;
//Clear all previous statuses status = usbStatusRead(); while (status & 0x2) { usbResponseDelay(); resLen = usbInitRead(response, 200); if (resLen == 0) break; status = usbStatusRead(); for (i=0;i<20;i++) { usbResponseDelay(); } }
uart2TxBufWr(CLEARDISP);
if (Menu_State == 230) { uart2TxBufWrStr("Please insert an ", 20); uart2TxBufWrStr("acceptable flash ", 20); uart2TxBufWrStr("drive into the right", 20); uart2TxBufWrStr("side of the box.", 16); } else if (Menu_State == 231) { uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(1);
uart2TxBufWrStr("Writing sample MIDI ", 20); uart2TxBufWrStr("file to flash drive.", 20); if ( (file = MIDI_Create_File(PSSC_FNAME)) == NULL) { asm volatile("nop"); uart2TxBufWr(CLEARDISP); uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(1); uart2TxBufWrStr("ERROR", 5); //Delay...
F-41
ECE 477 Final Report Spring 2009
for (i=0;i<20000;i++) { for (j=0;j<150;j++) {} } Menu_State = 10; LCD_Main(1); return; }
if ( (MIDI_Form_Demo()) != 1 ) { asm volatile("nop"); uart2TxBufWr(CLEARDISP); uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(1); uart2TxBufWrStr("ERROR", 5); //Delay... for (i=0;i<20000;i++) { for (j=0;j<150;j++) {} } Menu_State = 10; LCD_Main(1); return; }
if ( MIDI_Finalize(file) == USB_FAIL) { uart2TxBufWr(CLEARDISP); uart2TxBufWr(CURSORPOS); uart2TxBufWr(0); uart2TxBufWr(1); uart2TxBufWrStr("ERROR", 5); //Delay... for (i=0;i<20000;i++) { for (j=0;j<150;j++) {} } Menu_State = 10; LCD_Main(1); return; } Menu_State = 10; LCD_Main(1); }}
void LCD_FLASH(unsigned int New) { if (New == 1) { uart2TxBufWr(CLEARDISP);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1);
uart2TxBufWrStr("Value in FLASH: ", 16); uart2TxBufWr(*flash_pssc + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2); uart2TxBufWrStr("Value to store: ", 17); }
uart2TxBufWr(27); uart2TxBufWr(91); uart2TxBufWr(68); uart2TxBufWr(*flash_pssc + 48);
F-42
ECE 477 Final Report Spring 2009
}
void LCD_UI(unsigned int New) { if (New == 1) { tunings_from_flash();
uart2TxBufWr(CLEARDISP); uart2TxBufWrStr("User Setting", 12);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(1); uart2TxBufWrStr("1 2 3 4 5 6", 17); }
uart2TxBufWr(CURSORPOS); uart2TxBufWr(13); uart2TxBufWr(0); uart2TxBufWr(Menu_State - 249 + 48);
uart2TxBufWr(CURSORPOS); uart2TxBufWr(1); uart2TxBufWr(2); uart2TxBufWr(String_Values[0][tuning_array[(Menu_State-249)*6]][0]); uart2TxBufWr(String_Values[0][tuning_array[(Menu_State-249)*6]][1]); uart2TxBufWr(' '); uart2TxBufWr(String_Values[1][tuning_array[(Menu_State-249)*6+1]][0]); uart2TxBufWr(String_Values[1][tuning_array[(Menu_State-249)*6+1]][1]); uart2TxBufWr(' '); uart2TxBufWr(String_Values[2][tuning_array[(Menu_State-249)*6+2]][0]); uart2TxBufWr(String_Values[2][tuning_array[(Menu_State-249)*6+2]][1]); uart2TxBufWr(' '); uart2TxBufWr(String_Values[3][tuning_array[(Menu_State-249)*6+3]][0]); uart2TxBufWr(String_Values[3][tuning_array[(Menu_State-249)*6+3]][1]); uart2TxBufWr(' '); uart2TxBufWr(String_Values[4][tuning_array[(Menu_State-249)*6+4]][0]); uart2TxBufWr(String_Values[4][tuning_array[(Menu_State-249)*6+4]][1]); uart2TxBufWr(' '); uart2TxBufWr(String_Values[5][tuning_array[(Menu_State-249)*6+5]][0]); uart2TxBufWr(String_Values[5][tuning_array[(Menu_State-249)*6+5]][1]);}
MIDI.C// Based on Norman Hardy's MIDI code. // http://www.cap-lore.com/EnglishSuites/code/code.html// Hacked together with http://www.sphere.ws/blog/?p=33
#include "p24HJ128GP306.h"#include "LCD_Functions.h"#include "uart.h"#include "usb_funcs.h"#include "communicate.h"#include <string.h>#include "midi.h"
typedef unsigned int ui;
int dsize;int rdindex;int wrindex;int midi_trkindex;int bytecnt;
F-43
ECE 477 Final Report Spring 2009
char midi[BUFFER_SIZE] __attribute__((far));const char note[32] = {60, 62, 64, 60, 60, 62, 64, 60, 64, 65, 67, 64, 65, 67, 67, 69, 67, 65, 64, 60, 67, 69, 67, 65, 64, 60, 60, 55, 60, 60, 55, 60};
const char dura[32] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 4, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 4, 2, 2, 4};
void midi_init(void) { dsize = 0; rdindex = 0; wrindex = -1; midi_trkindex = 0; bytecnt = 0;}
// Deposits character in buffer if there is roomvoid pushc(char x){ if(++wrindex >= BUFFER_SIZE) wrindex = 0; midi[wrindex] = x; bytecnt++; dsize++;}
void pullc(void){ if(++rdindex >= BUFFER_SIZE) rdindex = 0; dsize--;}
// Deposits binary v in c bytesvoid pn(int c, ui v) { if(c) { pn(c - 1, v >> 8); pushc(v & 255); }}
// Routine for depositing variable length binary numbersvoid pvlr(ui n) { if(n > 127) pvlr(n >> 7); pushc(0x80 | (n & 127));}
// Routine for depositing variable length binary numbersvoid pvl(ui n) { if(n > 127) pvlr(n >> 7); pushc(n & 127);}
// Plays note n for duration dvoid n(int n, int d){ if (n <= 0x21){ // plays a rest pushc(0); pushc(0); pvl(d); pushc(0); pushc(0); pvl(0); } else { pushc(n); // Emit note value
F-44
ECE 477 Final Report Spring 2009
pushc(64); // Standard loudness pvl(d);//*120); // dt for note duration pushc(n); // Write note to stop pushc(0); // loudness = 0 to stop pvl(0); // Emit dt }}
file_desc *MIDI_Create_File(char *fname) { int fname_len; int data_len; file_desc *file;
fname_len = strlen(fname);
// Creates the header for the MIDI file pushc(0x4d); // don't change pushc(0x54); // don't change pushc(0x68); // don't change pushc(0x64); // don't change pn(4, 6); // don't change pn(2, 0); // 0 = single track, 1 = synchronous multiple tracks pn(2, 1); // number of tracks in the file pn(2, 240); // number of delta time ticks per quarter note
// Start of a track pushc(0x4d); // don't change pushc(0x54); // don't change pushc(0x72); // don't change pushc(0x6B); midi_trkindex = bytecnt; bytecnt = 0;
data_len = dsize; file = file_open(fname, fname_len, 'w');
while (data_len != 0) { if ((file_write(file, &(midi[rdindex]), 1)) != USB_FAIL){ pullc(); data_len--; } } return file;}
int MIDI_Form_Demo(void){ int data_index = 0;
for (data_index=0; data_index < 32; data_index++){ n(note[data_index],dura[data_index]*120); } return 1;}
int MIDI_Finalize(file_desc *fd){ int close_status; int rdindex_old; int wrindex_old; int dsize_old;
pushc(0xFF); pushc(0x2F); pvl(0);
F-45
ECE 477 Final Report Spring 2009
rdindex_old = rdindex; wrindex_old = wrindex; dsize_old = dsize; rdindex = wrindex+1; dsize = 0;
pn(4, bytecnt+5); // placeholder bytes for track size pushc(0); pushc(0xc0); pushc(1); // voice (instrument heard on MIDI) pvl(0); pushc(0x90); // note down, channel 1
// prints out the rest of the header while (dsize > 0){ if ((file_write(fd, &(midi[rdindex]), 1)) == USB_SUCCESS) pullc(); }
rdindex = rdindex_old; wrindex = wrindex_old; dsize = dsize_old;
// Prints out the rest of the buffer; while (dsize > 0){ if ((file_write(fd, &(midi[rdindex]), 1)) == USB_SUCCESS) pullc(); }
close_status = file_close(fd); return close_status;}
READ_FLASH.S.text.global _read_flash_byte
_read_flash_byte:MOV #tblpage(0x800),W2 ; get table page valueMOV W2,TBLPAG ; load TBLPAG register with W2MOV W0,W2 ; Load W2 with prog mem offset pointer;TBLRDH.B [W2],W3 ; Read high byte to W3TBLRDL.B [W2++],W3 ; Read low byte of prog mem pointer into W3TBLRDL.B [W2++],W4 ; Read high byte to W5MOV #0x0008,W5SL W4,W5,W4MOV #0x00FF,W5AND W3,W5,W3IOR W4,W3,W4MOV W4,[W1] ; Write prog mem value in W3 to address in W1RETURN.end
usb_funcs.c#include "p24HJ128GP306.h"#include "LCD_Functions.h" //To get bufferfull, empty checks#include "usb_funcs.h"#include <string.h>
//command set//After each command, add a parameter, then append a newlineconst char BADCOMMAND[] = {0x42, 0x43};
F-46
ECE 477 Final Report Spring 2009
const char CD[] = {0x02, 0x20};const char RDF[] = {0x0B, 0x20}; //RDF dword_bytes CRconst char WRF[] = {0x08, 0x20}; //WRF dword_bytes CR dataconst char OPR[] = {0x0E, 0x20}; //OPR fname CRconst char OPW[] = {0x09, 0x20}; //OPW fname CRconst char CLF[] = {0x0A, 0x20}; //CLF CRconst char SEK[] = {0x28, 0x20}; //SEK dword_abs_pos CRconst char SCS[] = {0x10};const char DIR[] = {0x01};const char IPH[] = {0x91}; //Puts monitor in binary mode
//Global File Descriptor 'n suchfile_desc file1;file_desc* sysFile;
char usbCommandBuffer[USB_CMD_BUFF_SIZE];unsigned int usbCommandBufferHead;unsigned int usbCommandBufferTail;
void usb_init(void) { char bootVal[200]; char commandResponse[200]; int i, j, len, resLen; int status;
//Init file structure file1.pos = 0; file1.open = 0; file1.fname_len = 0; sysFile = &file1;
//Wait while the darn thing boots up for (i=0;i<3000;i++) { for (j=0;j<500;j++) { asm volatile("nop"); } } //Read initial USB status from the device len = usbInitRead(bootVal, 200);
//Send short character set command usbBufferWrite(SCS, 1); usbBufferWriteChar(CR); resLen = usbCommandSend(commandResponse, 200); usbBufferWrite(IPH, 1); usbBufferWriteChar(CR); resLen = usbCommandSend(commandResponse, 200);
//Clear all statuses status = usbStatusRead(); while (status & 0x2) { usbResponseDelay(); resLen = usbInitRead(commandResponse, 200); if (resLen == 0) break; asm volatile("nop"); status = usbStatusRead(); for (i=0;i<20;i++) { usbResponseDelay(); } } i++;}
F-47
ECE 477 Final Report Spring 2009
char usbStatusRead(void) { char status;
PORTBbits.RB2 = 1; SPI1BUF = (USB_HEADER | USB_STATUS_READ ); asm volatile("nop"); //Wait for SPI transmission to complete while (SPI1STATbits.SPITBF) {}; while (!SPI1STATbits.SPIRBF) {}; PORTBbits.RB2 = 0;
status = USB_OUTPUT_BYTE(SPI1BUF);
return status;}
int usbInitRead(char *resultBuf, int maxLen) { //Reads up to maxLen characters from the USB device. //This is useful for status registers, reading files, etc. int len = 0; int status = 0; int delayVal = 0;
//Put the Read bits in the buffer PORTBbits.RB2 = 1; SPI1BUF = (USB_HEADER | USB_DATA_READ );
while (len < maxLen) { //Put the Read bits in the buffer PORTBbits.RB2 = 1; SPI1BUF = (USB_HEADER | USB_DATA_READ ); asm volatile("nop"); //Wait for SPI transmission to complete while (SPI1STATbits.SPITBF) {}; while (!SPI1STATbits.SPIRBF) {}; PORTBbits.RB2 = 0; //Delay for at least one SPIClk Cycle for (delayVal=0;delayVal<ONE_SPI_CYCLE;delayVal++); {}; *resultBuf = USB_OUTPUT_BYTE(SPI1BUF);
//Read Status. Old data? if so, quit status = SPI1BUF & USB_STATUS_MASK; if (status) { //Old data, we don't want it. break; }
//inc len by one bit. len++; resultBuf++; } return len;}
char usbBufferReadChar(void) { //Returns a character from the USB buffer, if available //Otherwise, NULL is returned char readOut = NULL;
if ( !(bufEmptyChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail))){ readOut = usbCommandBuffer[usbCommandBufferTail]; usbCommandBufferTail = (usbCommandBufferTail + 1) & (USB_CMD_BUFF_SIZE - 1); } return readOut;
F-48
ECE 477 Final Report Spring 2009
}
int usbBufferWriteChar(char addToBuff) { while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; usbCommandBuffer[usbCommandBufferHead] = addToBuff; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
return USB_SUCCESS;}
int usbBufferWriteInt(int addToBuff) { char buffVal;
//Write the int as two separate 8 bit values, most significant first. while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; buffVal = addToBuff >> 8; usbCommandBuffer[usbCommandBufferHead] = buffVal; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; buffVal = addToBuff & 0x00FF; usbCommandBuffer[usbCommandBufferHead] = buffVal; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
return USB_SUCCESS;}
int usbBufferWriteLong(long addToBuff) { char buffVal;
while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; buffVal = addToBuff >> 24; usbCommandBuffer[usbCommandBufferHead] = buffVal; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; buffVal = (addToBuff >> 16) & 0x000000FF; usbCommandBuffer[usbCommandBufferHead] = buffVal; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; buffVal = (addToBuff >> 8) & 0x000000FF; usbCommandBuffer[usbCommandBufferHead] = buffVal; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
while (bufFullChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail)){}; buffVal = addToBuff & 0x00FF; usbCommandBuffer[usbCommandBufferHead] = buffVal; usbCommandBufferHead = (usbCommandBufferHead + 1) & (USB_CMD_BUFF_SIZE - 1);
return USB_SUCCESS;}
int usbBufferWrite(char *string, int len) { //Write a string of up to maxLen chars into the buffer. Return bytes copied //on success, -1 on fail int cpyCount = 0; if (len > MAX_STRLEN) return USB_FAIL;
while (cpyCount < len) { if (usbBufferWriteChar(*string) == USB_FAIL) {
F-49
ECE 477 Final Report Spring 2009
return USB_FAIL; } cpyCount++; string++; } return cpyCount;}
int usbCommandSend(char *responseBuf, int maxLen) { char sendChar; char statusResp, dataResp[200]; int dataRespLen; int i; int error = 0; int delayVal, status, len=0;
//Write a command to USB do { sendChar = usbCommandBuffer[usbCommandBufferTail]; PORTBbits.RB2 = 1; SPI1BUF = (USB_HEADER | USB_DATA_WRITE | USB_INPUT_BYTE(sendChar)); while (SPI1STATbits.SPITBF) {}; while (!SPI1STATbits.SPIRBF) {}; PORTBbits.RB2 = 0; //Delay for at least one SPIClk Cycle for (delayVal=0;delayVal<ONE_SPI_CYCLE;delayVal++) {}; status = SPI1BUF & USB_STATUS_MASK; if (status) { //Not accepted, need to repeat loop asm volatile("nop"); usbResponseDelay(); statusResp = usbStatusRead(); usbResponseDelay(); if (statusResp == 0x01) { //RCV buffer full, TX buffer not empty dataRespLen = usbInitRead(dataResp, 200); if ((error = usbErrorCodeCheck(dataResp, dataRespLen)) != 0) { asm volatile("nop"); return USB_FAIL; }
for (i=0;i<200;i++) usbResponseDelay(); } continue; }
//Move head & tail usbBufferReadChar();
} while (!bufEmptyChk(USB_CMD_BUFF_SIZE, usbCommandBufferHead, usbCommandBufferTail));
asm volatile("nop");
//Delay some amount of time while (len == 0) { usbResponseDelay();
//Read Out response len = usbInitRead(responseBuf, maxLen);
F-50
ECE 477 Final Report Spring 2009
} return len;}
//Read Response
void usbResponseDelay(void) { int delayVal;
for (delayVal=0;delayVal<USB_COMMAND_DELAY;delayVal++) {};}
file_desc* file_open(char* fname, int fname_len, char mode) { char response[200]; int error, len;
asm volatile("nop"); if (fname_len > MAX_FNAME_LEN) return NULL;
//Copy fopen command to buffer if (mode == 'r') { usbBufferWrite(OPR, 2); } else if (mode == 'w') { usbBufferWrite(OPW, 2); } else return NULL;
usbBufferWrite(fname, fname_len); usbBufferWriteChar(CR);
//Read Response, match against errors len = usbCommandSend(response, 200); if ( (error = usbErrorCodeCheck(response, len)) != 0) return NULL;
asm volatile("nop");
//No error, seek to start of file usbBufferWrite(SEK, 2); usbBufferWriteInt(0); usbBufferWriteInt(0); usbBufferWriteChar(CR);
len = usbCommandSend(response, 200); if ( (error = usbErrorCodeCheck(response, len)) != 0) return NULL;
asm volatile("nop");
strncpy(file1.fname, fname, fname_len); file1.fname_len = fname_len; file1.open = 1; file1.pos = 0;
return sysFile;}
int file_close(file_desc *file) { char response[200]; int error, len;
//copy fclose command to buffer usbBufferWrite(CLF, 2); usbBufferWriteChar(CR);
F-51
ECE 477 Final Report Spring 2009
len = usbCommandSend(response, 200); if ( (error = usbErrorCodeCheck(response, len)) != 0) return USB_FAIL; file->open = 0; file->pos = 0; file->fname_len = 0; return USB_SUCCESS;}
int file_read(file_desc *file, char* data, int len) { //Assumes 'data' is big enough to contain len bytes int rLen; int error; if (!(file->open)) return USB_FAIL; usbBufferWrite(RDF, 2); usbBufferWriteInt(0); usbBufferWriteInt(len); //32 bit dword type usbBufferWriteChar(CR);
rLen = usbCommandSend(data, len);
if ( (error = usbErrorCodeCheck(data, rLen)) != 0) { return USB_FAIL; } file->pos += rLen - 1;
return rLen;}
int file_write(file_desc *file, char* data, int len) { int rLen; char response[200]; int error; int i;
if (!(file->open)) return USB_FAIL;
usbBufferWrite(WRF, 2); usbBufferWriteInt(0); usbBufferWriteInt(len); usbBufferWriteChar(CR);
asm volatile("nop"); for (i=0;i<len;i++) usbBufferWriteChar(data[i]);
rLen = usbCommandSend(response, 200);
if ( (error = usbErrorCodeCheck(response, rLen)) != 0 ) { return USB_FAIL; } return USB_SUCCESS;}
int file_seek(file_desc *file, int position, int offset) { int rLen; char response[200]; int error;
asm volatile("nop"); usbBufferWrite(SEK, 2);
F-52
ECE 477 Final Report Spring 2009
switch (position) { case CURRENT: offset += (int)file->pos; case BEGIN: default: usbBufferWriteInt(0); usbBufferWriteInt(offset); }
usbBufferWriteChar(CR); rLen = usbCommandSend(response, 200);
if ( (error = usbErrorCodeCheck(response, rLen)) != 0 ) { return USB_FAIL; } return USB_SUCCESS;}
int usbErrorCodeCheck(char *input, int len) { if (input[0] == 0x43 && input[1] == 0x46 && input[2] == 0x0D && len == 3) return 1; //COMMANDFAIL else if (input[0] == 0x4E && input[1] == 0x44 && input[2] == 0x0D && len == 3) return 1; //NO DISK else if (input[0] == 0x44 && input[1] == 0x46 && input[2] == 0x0D && len == 3) return 1; //DISKFULL else if (input[0] == 0x46 && input[1] == 0x49 && input[2] == 0x0D && len == 3) return 1; //INVALID else if (input[0] == 0x52 && input[1] == 0x4f && input[2] == 0x0D && len == 3) return 1; //READONLY else if (input[0] == 0x46 && input[1] == 0x4f && input[2] == 0x0D && len == 3) return 1; //FILEOPEN else if (input[0] == 0x4e && input[1] == 0x45 && input[2] == 0x0D && len == 3) return 1; //DIRNOTEMPTY else if (input[0] == 0x46 && input[1] == 0x4e && input[2] == 0x0D && len == 3) return 1; //INVALIDFNAME else if (input[0] == 0x4e && input[1] == 0x55 && input[2] == 0x0D
F-53
ECE 477 Final Report Spring 2009
&& len == 3) return 1; //NOUPGRADE
return 0;}
DSPIC CODE
HEADER FILES
ADCroutines.h/******* DSP code for ECE 477 Team 4 (das Autotunr)** Initial version: March 6, 2009* Author(s): Joe Blubaugh******/
#ifndef ADCROUTINES#define ADCROUTINES
#define ADC_WINDOW_SIZE 16#define ADC_AVG_FILTER_SIZE 16#define ADC_ONE_OVER_FILTER_LEN 0x1000 //0.0625 = 2^-4#define ADC_WINDOW_SIZE_LOG2 4#define ADC_VECTOR_SIZE 1024 //was 1024#define ADC_HALF_VECTOR_SIZE 512#define ADC_VECTOR_SIZE_LOG2 10 //was 10#define ADC_NUM_SAMPLES 102 //was 102#define ADC_TIMER2_PRESCALE 0b00#define ADC_TIMER3_PERIOD 0#define ADC_TIMER2_PERIOD 9765 //was 9639#define ADC_TIMER4_PRESCALE 0b11#define ADC_TIMER4_PERIOD 9712 //every .0625 secs#define ADC_TUNING_MODE 0#define ADC_MIDI_MODE 1#define ADC_NEUTRAL_MODE 2#define ADC_PICK_DISCARD 128#define ADC_TUNING_DIVIDE 4 //was 4#define ADC_TUNING_DIVIDE_LOG2 2#define ADC_STARTUP_DIVIDE 20#define ADC_MIDI_DIVIDE 1#define ADC_MIDI_DIVIDE_LOG2 0#define ADC_MONITOR_STATE 0#define ADC_SAMPLE_STATE_PICK 1#define ADC_SAMPLE_STATE 2#define ADC_STARTUP_STATE 3#define ADC_FFT_READY_STATE 4#define ADC_FINISHED_STATE 5#define ADC_SAMPLE_THRESH 0x1800 //0.1875
#define ADC_MICROPHONE 0x0004
#define ADC_SCALE_FACT 0x5000 //Empirically determined scale factor to keep //values between -.5 and .5
#define HALF 0x3B00 //Cut it down below -0.5 to 0.5 and see if this helps#define NUM_NOTES 15
F-54
ECE 477 Final Report Spring 2009
#define SUB_AUDIBLE 40#define PEAK_SQUASH_WIDTH 10#define SECOND_HARM_SQUASH_WIDTH 6#define SECOND_HARM_INDEX_THRESH 200#define SECOND_HARM_MAG_THRESH 0x0290#define PEAK_THRESH 0x0180#define TUNING_MAX_FREQ 490#define MIDI_MAX_FREQ 490#define MIDI_FREQ_SHIFT 2 //times 4#define BASE_FREQ_THRESH 0x0290 //0.02002#define BASE_FREQ_SEARCH_WIDTH 12#define MAX_DERIV_WIDTH 16#define NOTE_MAG_THRESH 0x2000 //0.125#define ERR20_THRESH 3#define ERR15_THRESH 2#define SPI_PKT_SIZE 8#define SPI_START_PKT 0xFFFF#define SPI_END_PKT 0x7777
#define MIDI_START 0xFF03#define TUNING_START 0xFF01#define MODE_STOP 0xFF04
#define MODE_CHANGE_DELAY 100
typedef int noteAvg[2];typedef noteAvg notePack[8];
#define MAX(x, y) (x) > (y) ? (x) : (y)#define MIN(x, y) (x) > (y) ? (y) : (x)
void spi1_init(void);void initADC(void);void initTimer(void);void initOSC(void);void ADCChangeMode(int message);void __attribute__((__interrupt__, shadow, auto_psv)) _ADC1Interrupt(void);void __attribute__((__interrupt__, shadow, auto_psv)) _T3Interrupt(void);#endif
peakDetection.h#ifndef PEAKDETECTION#define PEAKDETECTION
typedef fractional meritPack[2];
void AmplifyUFractVector(int, int, fractional*, fractional*);void autoAmplifyUFractVector(int, fractional*, fractional*);void partitionedPeakDetect(int, fractional*, notePack*);void peakDetect(int, int, fractional*, notePack*);void dataDrivenPeakDetect( int vectorSize, fractional *srcV, notePack *note);fractional localDeriv( int vectorSize, int index, int width, fractional *srcV);void calcMerit( int *peaksArray, int numPeaks, int vectorSize, fractional *srcV, meritPack *merit);
#endif
SOURCE FILESmain.c
/*****
F-55
ECE 477 Final Report Spring 2009
** DSP code for ECE 477 Team 4 (das Autotunr)** Initial version: March 6, 2009* Author(s): Joe Blubaugh******/
#include "p33FJ64GP306.h" //Our PCB
#include "dsp.h"#include "ADCroutines.h"#include "peakDetection.h"#include "delay.h"#include "libq.h"#include <stdlib.h>
/* Some of these macros are taken from Microchip example code for dsPIC33F devices. Also, many device initialization lines are taken directly from Microchip example code */
// Internal FRC Oscillator_FGS(GWRP_OFF);_FOSCSEL(FNOSC_FRCPLL);_FOSC(FCKSM_CSECMD & OSCIOFNC_ON & POSCMD_XT);_FWDT(FWDTEN_OFF);
//Global ADC arraysvolatile fractional ADCWindow[ADC_WINDOW_SIZE] __attribute__ ((space(xmemory), far));volatile fractional ADCAverageAmplitude __attribute__ ((space(xmemory), far));volatile fractcomplex ADCSampleVector[ADC_VECTOR_SIZE] __attribute__ ((space(ymemory), far, aligned(ADC_VECTOR_SIZE)));volatile fractcomplex FFTResultVector[ADC_VECTOR_SIZE] __attribute__ ((space(ymemory), far, aligned(ADC_VECTOR_SIZE)));volatile fractional FFTMagnitudeVector[ADC_VECTOR_SIZE] __attribute__ ((space(xmemory), far, aligned(ADC_VECTOR_SIZE)));volatile fractional FFTAverageFilter[ADC_AVG_FILTER_SIZE] __attribute__ ((space(xmemory), far));volatile fractional FFTAverageOutput[ADC_VECTOR_SIZE+ADC_AVG_FILTER_SIZE+1] __attribute__((space(xmemory), far));volatile unsigned int ADCMode __attribute__((space(xmemory)));volatile unsigned int ADCState __attribute__((space(xmemory)));volatile fractional ADCInstVal __attribute__((space(xmemory)));volatile fractional ADCBgNoise __attribute__((space(xmemory)));volatile fractional ADCDownsampleAvg __attribute__((space(xmemory)));fractcomplex twiddles[ADC_VECTOR_SIZE/2] __attribute__((space(xmemory), far, aligned (ADC_VECTOR_SIZE*2)));volatile notePack NoteHistory[NUM_NOTES] __attribute__((space(xmemory), far));volatile unsigned int NotesFound __attribute__((far));volatile unsigned int NoteSent __attribute__((far)); //Set by SPI when it finishes sending a packet//Fractional debug varsvolatile fractional a __attribute__((space(xmemory)));volatile fractional b __attribute__((space(xmemory)));volatile fractional c[2] __attribute__((space(xmemory)));
extern volatile char midi_note_found;
//LCD Textconst char mytext[] __attribute__((space(xmemory))) = " Joe's ADC Test ";const char mytext1[] __attribute__((space(xmemory))) = " S4 starts ADC ";const char mytext2[] __attribute__((space(xmemory))) = " S3 starts samp ";const char sampText[] __attribute__((space(xmemory))) =" Sampling... ";
F-56
ECE 477 Final Report Spring 2009
const char doneText[] __attribute__((space(xmemory))) =" Sampling done ";const char FFTtext[] __attribute__((space(xmemory))) = " Doing FFT... ";volatile int message;
int main (void){
unsigned int i, j; int index; volatile fractional maxi, mini, scaleFact; //remove volatile to improve performance after debug
/* First, set up the oscilator * Fosc = Fin*M/(N1*N2), Fcy = Fosc / 2 * Fosc = 7.37M * 41/(2*2) = 7.92Mhz for 7.37 input clock */
initOSC();
ADCMode = ADC_NEUTRAL_MODE; //sample at 1/4 freqADCState = ADC_STARTUP_STATE; //Watch and average input
//Init Modules, define interrupts, all of that.for (i=0;i<ADC_WINDOW_SIZE;i++) {
ADCWindow[i] = 0;}//init filterfor (i=0;i<ADC_AVG_FILTER_SIZE;i++) {
FFTAverageFilter[i] = ADC_ONE_OVER_FILTER_LEN;}
spi1_init(); initTimer(); initADC(); //Init_LCD(); NotesFound = 0; NoteSent = 0; for (i=0;i<NUM_NOTES;i++) { NoteHistory[i][0][0] = 0xFFFF; NoteHistory[i][0][1] = 0; NoteHistory[i][1][0] = 0; NoteHistory[i][1][1] = 0; NoteHistory[i][2][0] = 0; NoteHistory[i][2][1] = 0; NoteHistory[i][3][0] = 0; NoteHistory[i][3][1] = 0; NoteHistory[i][4][0] = 0; NoteHistory[i][4][1] = 0; NoteHistory[i][5][0] = 0; NoteHistory[i][5][1] = 0; NoteHistory[i][6][0] = 0; NoteHistory[i][6][1] = 0; NoteHistory[i][7][0] = 0x7777; NoteHistory[i][7][1] = 0; }
//Init ADC sample vector to include zerosfor (i=ADC_NUM_SAMPLES;i<ADC_VECTOR_SIZE;i++) {
ADCSampleVector[i].real &= 0x0000;ADCSampleVector[i].imag &= 0x0000;
}//Init Twiddle Factors with no conj flag (forward DFT)TwidFactorInit(ADC_VECTOR_SIZE_LOG2, twiddles, 0);while (ADCState == ADC_STARTUP_STATE) {};
if (SPI1STATbits.SPIRBF == 1) {
F-57
ECE 477 Final Report Spring 2009
message = SPI1BUF; }
i = 0; while (1) {
i++; if (ADCMode == ADC_TUNING_MODE) {
asm volatile("nop");//Check for message from PICif (PORTDbits.RD0 == 1) {
//Initialize an SPI read SPI1BUF = 0; message = 0; //while(message != 100000) { message++;} while(SPI1STATbits.SPITBF == 1){ asm volatile("nop");} while(SPI1STATbits.SPIRBF != 1){ asm volatile("nop");}
message = SPI1BUF;ADCChangeMode(message);continue;
}
//Should we start sampling?if ((ADCAverageAmplitude > ADC_SAMPLE_THRESH)
&& (ADCState == ADC_MONITOR_STATE)) {ADCState = ADC_SAMPLE_STATE_PICK;
}
if ((ADCState == ADC_SAMPLE_STATE) &&(ADCAverageAmplitude < ADC_SAMPLE_THRESH)) { ADCState = ADC_MONITOR_STATE;
}
if (ADCState == ADC_FINISHED_STATE) { //Check for message from PIC
if (PORTDbits.RD0 == 1) {//Initialize an SPI read
SPI1BUF = 0; while(SPI1STATbits.SPITBF == 1){ asm("nop"); } while(SPI1STATbits.SPIRBF != 1){ asm("nop"); } message = SPI1BUF;
ADCChangeMode(message); continue; //Don't send, if we get a mode change
} //Send out one note packet
for (j=0;j<SPI_PKT_SIZE;j++) { SPI1BUF = (NoteHistory[NotesFound - 1][j][0]); //Send note index while(SPI1STATbits.SPITBF == 1){}
while(SPI1STATbits.SPIRBF != 1){} message = SPI1BUF;
}
asm volatile("nop"); NoteSent = 1;
ADCState = ADC_SAMPLE_STATE;}
if (NotesFound == NUM_NOTES) { NotesFound = 0; for (i=0;i<NUM_NOTES;i++) { NoteHistory[i][0][0] = 0xFFFF;
NoteHistory[i][0][1] = 0;
F-58
ECE 477 Final Report Spring 2009
NoteHistory[i][1][0] = 0; NoteHistory[i][1][1] = 0; NoteHistory[i][2][0] = 0;
NoteHistory[i][2][1] = 0; NoteHistory[i][3][0] = 0; NoteHistory[i][3][1] = 0; NoteHistory[i][4][0] = 0;
NoteHistory[i][4][1] = 0; NoteHistory[i][5][0] = 0; NoteHistory[i][5][1] = 0; NoteHistory[i][6][0] = 0;
NoteHistory[i][6][1] = 0; NoteHistory[i][7][0] = 0x7777; NoteHistory[i][7][1] = 0;
}}
if (ADCState == ADC_FFT_READY_STATE) { //Not Sampling//Do FFTasm volatile("nop");
if (NotesFound == 5) {asm volatile("nop");
} maxi = VectorMax(ADC_VECTOR_SIZE*2, ADCSampleVector, &i);
mini = _Q15abs(VectorMin(ADC_VECTOR_SIZE*2, ADCSampleVector, &i)); scaleFact = __builtin_divf(HALF, MAX(maxi, mini));
VectorScale(ADC_VECTOR_SIZE*2, //fractional, 2 ints per element ADCSampleVector, ADCSampleVector, scaleFact);
FFTComplex(ADC_VECTOR_SIZE_LOG2, FFTResultVector, ADCSampleVector, twiddles, COEFFS_IN_DATA);
//Amplify here? Needed if not enough resolution in magnitudeSquareMagnitudeCplx(ADC_VECTOR_SIZE, FFTResultVector, FFTMagnitudeVector);//Amplify here? Simple, just a shift since all values are positiveasm volatile("nop");autoAmplifyUFractVector(ADC_VECTOR_SIZE, FFTMagnitudeVector, FFTMagnitudeVector);
//Filter with 16-tap averageVectorConvolve(ADC_VECTOR_SIZE, ADC_AVG_FILTER_SIZE, FFTAverageOutput, FFTMagnitudeVector, FFTAverageFilter);//ID max frequency//peakDetect(TUNING_MAX_FREQ, 2, FFTAverageOutput, //&(NoteHistory[NotesFound][1]));
if (NotesFound == 3) {asm volatile("nop");
}dataDrivenPeakDetect(TUNING_MAX_FREQ, &(FFTAverageOutput[7]), &(NoteHistory[NotesFound][1]));
//NoteHistory[NotesFound][1][0] = index; NotesFound++; asm volatile("nop");
ADCState = ADC_FINISHED_STATE;
}} //Tuning Mode
if (ADCMode == ADC_MIDI_MODE) {if (PORTDbits.RD0 == 1) {
//Initialize an SPI read SPI1BUF = 0; message = 0; //while(message != 100000) { message++;} while(SPI1STATbits.SPITBF == 1){ asm("nop"); } while(SPI1STATbits.SPIRBF != 1){ asm("nop"); }
F-59
ECE 477 Final Report Spring 2009
message = SPI1BUF; asm volatile("nop");
ADCChangeMode(message);continue;
}
if ((ADCAverageAmplitude > ADC_SAMPLE_THRESH) && (ADCState == ADC_MONITOR_STATE)) {
//Update LCD messageADCState = ADC_SAMPLE_STATE_PICK;
}
if ((ADCState == ADC_SAMPLE_STATE) &&(ADCAverageAmplitude < ADC_SAMPLE_THRESH)) { ADCState = ADC_MONITOR_STATE;
}
if (ADCState == ADC_FFT_READY_STATE) {//Peak Detectmaxi = VectorMax(ADC_VECTOR_SIZE*2, ADCSampleVector, &i);
mini = _Q15abs(VectorMin(ADC_VECTOR_SIZE*2, ADCSampleVector, &i)); scaleFact = __builtin_divf(HALF, MAX(maxi, mini));
VectorScale(ADC_VECTOR_SIZE*2, //fractional, 2 ints per element ADCSampleVector, ADCSampleVector, scaleFact);
FFTComplex(ADC_VECTOR_SIZE_LOG2, FFTResultVector, ADCSampleVector, twiddles, COEFFS_IN_DATA);
//Amplify here? Needed if not enough resolution in magnitudeSquareMagnitudeCplx(ADC_VECTOR_SIZE, FFTResultVector, FFTMagnitudeVector);autoAmplifyUFractVector(ADC_VECTOR_SIZE, FFTMagnitudeVector, FFTMagnitudeVector);VectorConvolve(ADC_VECTOR_SIZE, ADC_AVG_FILTER_SIZE, FFTAverageOutput, FFTMagnitudeVector, FFTAverageFilter);//Detect a single peak, send it outpeakDetect(MIDI_MAX_FREQ, 1, FFTAverageOutput, &(NoteHistory[0][1]));
ADCState = ADC_SAMPLE_STATE;
midi_note_found = 1;}
}
if (ADCMode == ADC_NEUTRAL_MODE) {if (PORTDbits.RD0 == 1) {
//Initialize an SPI read SPI1BUF = 0; message = 0; //while(message != 100000) { message++;} while(SPI1STATbits.SPITBF == 1){ asm volatile("nop");} while(SPI1STATbits.SPIRBF != 1){ asm volatile("nop");}
message = SPI1BUF; asm volatile("nop");
ADCChangeMode(message);}
}}; //while(1)
}
ADCroutines.c/******* DSP code for ECE 477 Team 4 (das Autotunr)** Initial version: March 6, 2009
F-60
ECE 477 Final Report Spring 2009
* Author(s): Joe Blubaugh******/
#include "p33FJ64GP306.h" //Our PCB#include "dsp.h"#include "ADCroutines.h"#include "libq.h"
extern volatile fractional ADCWindow[ADC_WINDOW_SIZE] __attribute__ ((space(xmemory)));extern volatile fractional ADCAverageAmplitude __attribute__ ((space(xmemory)));extern volatile fractcomplex ADCSampleVector[ADC_VECTOR_SIZE] __attribute__ ((space(ymemory), far));extern volatile fractcomplex FFTResultVector[ADC_VECTOR_SIZE] __attribute__ ((space(ymemory), far));extern volatile fractional FFTMagnitudeVector[ADC_VECTOR_SIZE] __attribute__ ((space(xmemory)));extern volatile unsigned int ADCMode __attribute__((space(xmemory)));extern volatile unsigned int ADCState __attribute__((space(xmemory)));extern volatile fractional ADCInstVal __attribute__((space(xmemory)));extern volatile fractional ADCBgNoise __attribute__((space(xmemory)));extern volatile fractional ADCDownsampleAvg __attribute__((space(xmemory)));extern volatile unsigned int NotesFound __attribute__((far));extern volatile notePack NoteHistory[NUM_NOTES] __attribute__((space(xmemory), far));
volatile char midi_note_found;
void ADCChangeMode(int message) {ADCState = ADC_MONITOR_STATE;
int i, j;
switch (message) {case MODE_STOP:
ADCMode = ADC_NEUTRAL_MODE;break;
case MIDI_START:ADCMode = ADC_MIDI_MODE;break;
case TUNING_START:ADCMode = ADC_TUNING_MODE;break;
default:while (1) {
asm volatile("nop");} //break here
}//Delay here
Delay_Us(MODE_CHANGE_DELAY);NotesFound = 0;
}
void spi1_init(void) { IFS0bits.SPI1IF = 0; //Clear the Interrupt Flag IEC0bits.SPI1IE = 0; //disable the Interrupt // SPI1CON1 Register Settings SPI1CON1bits.DISSCK = 0; //Internal Serial Clock is Enabled. SPI1CON1bits.DISSDO = 0; //SDOx pin is controlled by the module. SPI1CON1bits.MODE16 = 1; //Communication is word-wide (16 bits). SPI1CON1bits.SMP = 0; //Input Data is sampled at the middle of data output time. SPI1CON1bits.CKE = 0; //Serial output data changes on transition from //Idle clock state to active clock state SPI1CON1bits.CKP = 0; //Idle state for clock is a low level;
F-61
ECE 477 Final Report Spring 2009
//active state is a high level SPI1CON1bits.MSTEN = 1; //Master Mode Enabled SPI1STATbits.SPIEN = 1; //Enable SPI Module SPI1BUF = 0x0000; //Write data to be transmitted //Interrupt Controller Settings IFS0bits.SPI1IF = 0; //Clear the Interrupt Flag IEC0bits.SPI1IE = 0; //Enable the Interrupt}
void initADC(void) {midi_note_found = 0;
//Set ports to analog input AD1PCFGLbits.PCFG4 = 0; //Port 4 is audio input TRISBbits.TRISB4 = 1; //Port 4 is also RB4//Debugging port pin set up;AD1PCFGLbits.PCFG10 = 1;TRISBbits.TRISB10 = 0;PORTBbits.RB10 = 0;AD1PCFGLbits.PCFG11 = 1;TRISBbits.TRISB11 = 0;PORTBbits.RB11 = 0;AD1PCFGLbits.PCFG12 = 1;TRISBbits.TRISB12 = 0;PORTBbits.RB12 = 0;AD1PCFGLbits.PCFG13 = 1;TRISBbits.TRISB13 = 0;PORTBbits.RB13 = 0;
AD1CON1bits.AD12B = 1; //12 bit operationAD1CON1bits.ADSIDL = 0; //Do not run in Idle modeAD1CON1bits.ADDMABM = 0; //DMA write in order in buffer ADBUF0
//Auto sample, timer convertAD1CON1bits.ASAM = 1;AD1CON1bits.SSRC0 = 0;AD1CON1bits.SSRC1 = 1;AD1CON1bits.SSRC2 = 0;
//Signed fractional outputAD1CON1bits.FORM = 0b11;
//No channel scan, use MuxA, Interrupts on every sampleAD1CON2 = 0x0000;
//Set Clock rate. TAD = (AD1CON3<7:0> + 1) * TCY //For TCY = 1/(40 MHz) = 25 ns, set TAD = 80 * TCY = 2 us AD1CON3 = 0x032F; //Channel select AN4 AD1CHS0 = ADC_MICROPHONE; //Reset ADC interrupt flag IFS0bits.AD1IF = 0; //Enable ADC interrupts IEC0bits.AD1IE = 1; //Enable ADC AD1CON1bits.ADON = 1;}
void initTimer(void) {//Initialize timer 3,2 to get an ADC sample rate of 700 Hz
//Reset T3, T2T3CON = 0;
F-62
ECE 477 Final Report Spring 2009
T2CON = 0;T4CON = 0;
T3CONbits.TSIDL = 0;T4CONbits.TSIDL = 0;
//Clock prescale to 1T2CONbits.TCKPS = ADC_TIMER2_PRESCALE;T4CONbits.TCKPS = ADC_TIMER4_PRESCALE;
TMR3 = 0x00;TMR2 = 0x00;TMR4 = 0x00;
//Concatenate T3:T2T2CONbits.T32 = 1;PR3 = ADC_TIMER3_PERIOD;PR2 = ADC_TIMER2_PERIOD;PR4 = ADC_TIMER4_PERIOD;
//Clear interrupt flag and enable interruptsIPC2bits.T3IP = 4;IFS0bits.T3IF = 0;IEC0bits.T3IE = 1;
IPC6bits.T4IP = 4;IFS1bits.T4IF = 0;IEC1bits.T4IE = 1;
//Turn on T2T2CONbits.TON = 1;T4CONbits.TON = 1;
}
void __attribute__((interrupt, shadow, auto_psv)) _ADC1Interrupt(void) {static int i = 0;static int j = 1;static int L = 0;int k;
static int pickCount = 0; static int divCount = 1;
//Clear interrupt flagIFS0bits.AD1IF = 0;
//Toggle debuggin' line PORTBbits.RB10 = LATBbits.LATB10 ^ 1; if (divCount == ADC_TUNING_DIVIDE) {
PORTBbits.RB11 = LATBbits.LATB11 ^ 1;divCount = 0;
}divCount++;
//Take value from buffer, put in sample array. ADCWindow[i] = ADC1BUF0;
ADCInstVal = ADC1BUF0;i++;i %= ADC_WINDOW_SIZE;
//Put samples in the ADC Sample buffer only when we're in sample mode.//Quit after 1 set of sampling.
if (ADCState == ADC_STARTUP_STATE) {
F-63
ECE 477 Final Report Spring 2009
//On startup, take a background average measurement//We'll use this to compute a threshold for 'loud enough'
if (j == ADC_STARTUP_DIVIDE) {ADCSampleVector[L].real = ADC1BUF0;L++;j = 0;
}j++;if (L >= ADC_WINDOW_SIZE) {
ADCBgNoise = 0;for (k=0;k < ADC_WINDOW_SIZE;k++) {
ADCBgNoise += ADCSampleVector[k].real;}ADCBgNoise /= ADC_WINDOW_SIZE;L = 0;ADCState = ADC_MONITOR_STATE;j = 0;
}}
else if (ADCState == ADC_SAMPLE_STATE_PICK) { L++; if (L == ADC_PICK_DISCARD) { ADCState = ADC_SAMPLE_STATE; L = 0; pickCount = 0; } }
else if (ADCState == ADC_SAMPLE_STATE) {if (ADCMode == ADC_TUNING_MODE) {
if (j == ADC_TUNING_DIVIDE) {//Put divide average values in ADC samp vectorADCDownsampleAvg &= 0x0000;for (k=i;k>i-ADC_TUNING_DIVIDE;k--) {
//This is problematic: could shift all the data out//Also, I don't think this preserves sign.ADCDownsampleAvg += ADCWindow[(k < 0 ? ADC_WINDOW_SIZE + k : k)] >>
ADC_TUNING_DIVIDE_LOG2; }ADCSampleVector[L].real = ADCInstVal;L++;j = 0;
}j++;
} else if (ADCMode == ADC_MIDI_MODE) {if (j == ADC_MIDI_DIVIDE) {
//Put divide average values in ADC samp vectorADCDownsampleAvg &= 0x0000;for (k=i;k>i-ADC_TUNING_DIVIDE;k--) {
ADCDownsampleAvg += ADCWindow[(k < 0 ? ADC_WINDOW_SIZE + k : k)] >> ADC_MIDI_DIVIDE_LOG2;
}ADCSampleVector[L].real = ADCInstVal;L++;j = 0;
}j++;
}if (L >= ADC_NUM_SAMPLES){
ADCState = ADC_FFT_READY_STATE;}
} else {L = 0;
F-64
ECE 477 Final Report Spring 2009
}ADCAverageAmplitude = 0;for (k=0;k<ADC_WINDOW_SIZE;k++) {
ADCAverageAmplitude += _Q15abs(ADCWindow[k]) >> ADC_WINDOW_SIZE_LOG2;}
}
void __attribute__((interrupt, shadow, auto_psv)) _T4Interrupt(void) { //For sending MIDI packets.
int j, message; PORTBbits.RB12 = LATBbits.LATB12 ^ 1;
IFS1bits.T4IF = 0;PR4 = ADC_TIMER4_PERIOD;if (ADCMode == ADC_MIDI_MODE) {
if (midi_note_found == 1) {//Check for message from PICif (PORTDbits.RD0 == 1) {
//Initialize an SPI read SPI1BUF = 0; while(SPI1STATbits.SPITBF == 1){asm("nop");}
while(SPI1STATbits.SPIRBF != 1){asm("nop");} message = SPI1BUF;
ADCChangeMode(message);return;
}for (j=0;j<SPI_PKT_SIZE;j++) {
SPI1BUF = (NoteHistory[0][j][0]); //Send note index, not derivative while(SPI1STATbits.SPITBF == 1){asm("nop");}
while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;
}message = SPI1BUF;midi_note_found = 0;
}else {
//Check for message from PICif (PORTDbits.RD0 == 1) {
//Initialize an SPI read SPI1BUF = 0; while(SPI1STATbits.SPITBF == 1){asm("nop");}
while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;ADCChangeMode(message);return;
}//End of Packet...SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;
F-65
ECE 477 Final Report Spring 2009
SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;SPI1BUF = SPI_START_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;SPI1BUF = SPI_END_PKT;while(SPI1STATbits.SPITBF == 1){asm("nop");}while(SPI1STATbits.SPIRBF != 1){asm("nop");}message = SPI1BUF;
}}
}
void __attribute__((interrupt, shadow, auto_psv)) _T3Interrupt(void) {//Clear interrupt flagIFS0bits.T3IF = 0;
//Reset PER registerPR3 = ADC_TIMER3_PERIOD;PR2 = ADC_TIMER2_PERIOD;
// PORTAbits.RA2 ^= 1;}
void initOSC() { PLLFBD = 148; CLKDIVbits.PLLPOST = 0; CLKDIVbits.PLLPRE = 5; OSCCONbits.NOSC = 1; //FRC w/ PLL OSCCONbits.OSWEN = 1; //Perform a clock switch while (OSCCONbits.OSWEN != 0) {}; //wait for clock switch while (OSCCONbits.LOCK == 0) {}; //wait for PLL lock}
peakDetection.c#include "p33FJ64GP306.h"#include "dsp.h"#include "ADCroutines.h"#include "libq.h"#include "peakDetection.h"
void dataDrivenPeakDetect( int vectorSize, fractional *srcV, notePack *note) {
int Epeaks[4] = {82, 161, 248, 330}; //3 peaks int Fpeaks[4] = {87, 174, 261, 348}; int Fspeaks[4] = {93, 185, 281, 373}; int Gpeaks[4] = {98, 196, 294, 392}; int Gspeaks[3] = {104, 208, 312}; int Apeaks[3] = {110, 220, 330}; int Aspeaks[3] = {117, 233, 350}; int Bpeaks[2] = {123, 247}; int Cpeaks[5] = {65, 129, 196, 262, 307}; int Cspeaks[5] = {69, 139, 208, 277, 347}; int Dpeaks[4] = {73, 147, 220, 293}; int Dspeaks[4] = {78, 156, 233, 311};
F-66
ECE 477 Final Report Spring 2009
int testPeaks[6][4]; fractional peaks[4]; int width; int mIndex, i; meritPack merits[12];
calcMerit(Epeaks, 4, vectorSize, srcV, &merits[0]); calcMerit(Fpeaks, 4, vectorSize, srcV, &merits[1]); calcMerit(Fspeaks, 4, vectorSize, srcV, &merits[2]); calcMerit(Gpeaks, 4, vectorSize, srcV, &merits[3]); calcMerit(Gspeaks, 3, vectorSize, srcV, &merits[4]); calcMerit(Apeaks, 3, vectorSize, srcV, &merits[5]); calcMerit(Aspeaks, 3, vectorSize, srcV, &merits[6]); calcMerit(Bpeaks, 2, vectorSize, srcV, &merits[7]); calcMerit(Cpeaks, 5, vectorSize, srcV, &merits[8]); calcMerit(Cspeaks, 5, vectorSize, srcV, &merits[9]); calcMerit(Dpeaks, 4, vectorSize, srcV, &merits[10]);
calcMerit(Dspeaks, 4, vectorSize, srcV, &merits[11]);
mIndex = 0;for (i=0;i<12;i++) {
if (merits[i][0] > merits[mIndex][0]) {mIndex = i;
}}//This will need to get more complicated: divisions for certain notes, etc.(*(note))[0][0] = merits[mIndex][1];
}
void calcMerit( int *peaksArray, int numPeaks, int vectorSize, fractional *srcV, meritPack* merit) {
int i, width, maxind;int strength[5], indexes[5];int ampler = 4;
merit[0][0] = 0;for (i=0;i<numPeaks;i++) {
//set ampler based on freq? not sure yet...width = peaksArray[i] >> 4;strength[i] = VectorMax(width << 1, &(srcV[peaksArray[i] - width]),
&(indexes[i])); indexes[i] += peaksArray[i] - width;
merit[0][0] += strength[i] - ( abs(peaksArray[i] - indexes[i]) << ampler );}
merit[0][0] = merit[0][0] / numPeaks;
//choose index of strongest strengthmaxind = 0;for (i=0;i<numPeaks;i++) {
if (strength[i] > strength[maxind]) {maxind = i;
}}merit[0][1] = indexes[maxind];
}
void autoAmplifyUFractVector( int vectorSize, fractional* outputVector, fractional* inputVector) { fractional max; unsigned int i;
max = VectorMax(vectorSize, inputVector, &i);
F-67
ECE 477 Final Report Spring 2009
//Find highest bit in max i = 0; while ((max & 0x2000) == 0) {
i++; max = max << 1; if (i > 10) { i = 10; break; } }
AmplifyUFractVector( vectorSize, i, outputVector, inputVector ); return;}
void AmplifyUFractVector( int vectorSize, int poweroftwo, fractional* outputVector, fractional* inputVector) { //In-place capable of amplifying fractional vectors by a power of 2 //Incredibly unprotected, does no bounds-checking, overflow checking, anything. //Really, just left shifts an entire vector by a value unsigned int i;
for (i=0;i<vectorSize;i++) { outputVector[i] = inputVector[i] << poweroftwo; }}
void partitionedPeakDetect( int vectorSize, fractional* srcV, notePack* note) { int lowerBounds[6] = {65 << 1, 87 << 1, 116, 147, 196, 262}; int upperBounds[6] = {93 << 1, 122 << 1, 165, 220, 277, 370}; fractional averageSamples[16]; volatile fractional average; int sampleInterval; int vectorLength; int lbound, ubound, index; int lp1; int lp2; fractional pMaxi;
for (lp1=0;lp1<6;lp1++) { //One for each note vectorLength = upperBounds[lp1] - lowerBounds[lp1]; pMaxi = VectorMax(vectorLength, &(srcV[(lowerBounds[lp1])]), &index);
//Compute (approximate) vector average //Sample 16 members of the index sampleInterval = vectorLength >> 4; for (lp2=0;lp2<16;lp2++) { averageSamples[lp2] = srcV[(lowerBounds[lp1] + lp2*sampleInterval)]; }
//Average two at a time so we don't lose resolution for (lp2=0;lp2<16;lp2+=2) { averageSamples[lp2] = (averageSamples[lp2] + averageSamples[lp2+1]) >> 1; } for (lp2=0;lp2<16;lp2+=4) { averageSamples[lp2] = (averageSamples[lp2] + averageSamples[lp2+2]) >> 1; } averageSamples[0] = (averageSamples[0] + averageSamples[4]) >> 1; averageSamples[8] = (averageSamples[8] + averageSamples[12]) >> 1; average = (averageSamples[0] + averageSamples[8]) >> 1;
if ( (pMaxi - average) > PEAK_THRESH ) { index += lowerBounds[lp1];
F-68
ECE 477 Final Report Spring 2009
lbound = MAX(0, index - PEAK_SQUASH_WIDTH); ubound = MIN(vectorSize -1 , index+PEAK_SQUASH_WIDTH);
if (lp1 < 2) { (*(note))[lp1][0] = index >> 1; } else { (*(note))[lp1][0] = index; } //Squash peak, squash peak*2 for (lp2=lbound; lp2<=ubound; lp2++) { srcV[lp2] = 0; //Squash peak } } else { (*(note))[lp1][0] = -1; } }}
void peakDetect( int vectorSize, int numPeaks, fractional* srcV, notePack* note) { //Make sure vectorSize is only the bottom 1/2 of the total vector int i, j, index, index_by_2; int lbound, ubound; fractional pMaxi, baseMaxi; int err15, err20, error;
for (i=0;i<numPeaks;i++) { (*(note))[i][0] = 0; }
i = 0; while (i<numPeaks) { pMaxi = VectorMax(vectorSize, srcV, &index); (*(note))[i][0] = index;
if (index > SECOND_HARM_INDEX_THRESH) { //Freq is high enough (*(note))[i][1] = 1; //Flag that harmonic detector found this.
index_by_2 = index >> 1; //Might lose some data here, oh well lbound = MAX(0, index_by_2 - BASE_FREQ_SEARCH_WIDTH); ubound = MIN(vectorSize - 1, index_by_2 + BASE_FREQ_SEARCH_WIDTH); baseMaxi = VectorMax(BASE_FREQ_SEARCH_WIDTH << 1, &(srcV[lbound]), &index_by_2); if (baseMaxi > BASE_FREQ_THRESH) {
//Squash upper frequency pMaxi = baseMaxi; index = lbound + index_by_2;
(*(note))[i][0] = index; (*(note))[i][1] = 1; //Flag that harmonic detector found this. lbound = MAX(0, index - PEAK_SQUASH_WIDTH); ubound = MIN(vectorSize -1 , index+PEAK_SQUASH_WIDTH); for (j=lbound; j<=ubound; j++) {
srcV[j] = 0; //Squash peak } } }
if (index < SUB_AUDIBLE) {
(*(note))[i][0] = 0; //Squash section lbound = MAX(0, index - PEAK_SQUASH_WIDTH); ubound = MIN(vectorSize -1 , index+PEAK_SQUASH_WIDTH); for (j=lbound; j<=ubound; j++) {
srcV[j] = 0; //Squash peak
F-69
ECE 477 Final Report Spring 2009
} continue; }
if (pMaxi < NOTE_MAG_THRESH) { asm volatile("nop"); err15 = 0; err20 = 0; for (j=0;j<i;j++) { error = abs( (*(note))[j][0] - index ); err20 += (error < 20) ? 1 : 0; err15 += (error < 15) ? 1 : 0; } if (err20 >= ERR20_THRESH || err15 >= ERR15_THRESH) { (*(note))[i][0] = 0; //Squash section lbound = MAX(0, index - PEAK_SQUASH_WIDTH); ubound = MIN(vectorSize -1 , index+PEAK_SQUASH_WIDTH); for (j=lbound; j<=ubound; j++) {
srcV[j] = 0; //Squash peak }
continue; } }
asm volatile("nop"); lbound = MAX(0, index - PEAK_SQUASH_WIDTH); ubound = MIN(vectorSize -1 , index+PEAK_SQUASH_WIDTH); if ((*(note))[i][1] == 0) { (*(note))[i][1] = pMaxi; } //In the end, this isn't going to work. Peak squash width needs to be a func of frequency, I think
asm volatile("nop"); for (j=lbound; j<=ubound; j++) {
srcV[j] = 0; //Squash peak}
//also remove 2nd harmonic index_by_2 = index << 1; lbound = MAX(0, index_by_2 - BASE_FREQ_SEARCH_WIDTH); ubound = MIN(vectorSize - 1, index_by_2 + BASE_FREQ_SEARCH_WIDTH); baseMaxi = VectorMax(BASE_FREQ_SEARCH_WIDTH << 1, &(srcV[lbound]), &index_by_2); if (baseMaxi < SECOND_HARM_MAG_THRESH) { lbound = MAX(0, index_by_2 - BASE_FREQ_SEARCH_WIDTH); ubound = MIN(vectorSize - 1, index_by_2 + BASE_FREQ_SEARCH_WIDTH); for (j=lbound; j<=ubound; j++) {
srcV[j] = 0; //Squash peak } }
(*(note))[i][0] = (*(note))[i][0] << MIDI_FREQ_SHIFT; i++; } //while (i < numPeaks) asm volatile("nop");}
fractional localDeriv( int vectorSize, int index, int width, fractional *srcV) { //Compute the local average derivative over a width. This is helpful for gauging how 'peak-like' a note is. //Width should always be a power of two and less than MAX_DERIV_WIDTH fractional derivatives[MAX_DERIV_WIDTH], avg_derivative; int i; int halfWidth;
F-70
ECE 477 Final Report Spring 2009
int widthLog2 = 0; int lbound, ubound;
avg_derivative = 0x0000; halfWidth = width >> 1;
while (((width >> widthLog2) & 0x1) == 0){ widthLog2++; }
//Need to protect against vector over/underflow lbound = MAX((index - halfWidth), 0); ubound = MIN((index + halfWidth+1), vectorSize); ubound = ubound - lbound;
for (i=0;i<ubound;i++){ derivatives[i] = srcV[index - halfWidth + i + 1] - srcV[index - halfWidth + i]; avg_derivative = _Q15add(__builtin_divf(derivatives[i], width), avg_derivative); } return avg_derivative;}
F-71
ECE 477 Final Report Spring 2009
Appendix G: FMECA Worksheet
Failure No.
Failure Mode Possible Causes Failure Effects Method of Detection
Criticality Remarks
A1 6V rail = 0V Bad connections or not connected
Absolutely nothing happens Observation Low
A2 6V rail > 6V Wall Wart fails shorted Potential damage to voltage regulators and servos
Observation High Possible injury because of overheating of components.
A3 6V rail out of tolerance Wall Wart failure Causes 5V and/or 3.3V rails to be out of tolerance (see A6, A7)
Observation Low
A4 5V rail = 0V LP4941 fails open LCD screen does not turn on, cannot connect to USB device
Observation Low Everything but the LCD screen and the USB controller should work.
A5 5V rail > 5V LP4941 fails shorted Potential damage to LCD screen and USB controller
Observation High Possible subsequent component failures.
A6 5V rail out of tolerance LP4941 failure Unpredictable operation of the LCD screen and USB controller
Observation Low
A7 3.3V rail = 0V AP1117 fails open LCD screen turns on, but no UI operation
Observation Low The LCD will display the main screen, but buttons will be nonfunctional.
A8 3.3V rail > 3.3V AP1117 fails shorted Potential damage to the PIC and dsPIC
Observation High Possible subsequent component failures.
A9 3.3V rail out of tolerance
AP1117 failure Unpredictable operation of the PIC and dsPIC
Observation Low Causes unpredictable displays on the LCD screen.
Table G-1. Power Supply Subsystem
Failure No.
Failure Mode Possible Causes Failure Effects Method of Detection
Criticality Remarks
B1 Analog line = 0V LM386 fails open, stereo jack not connected, mic. out of range
Unable to Tune guitar or Transcribe music
Observation Low
B2 Inaccurate data on analog line
LM386 failure, IC switch failure, mic. out of range
Unable to Tune guitar or Transcribe music accurately
Observation Low May be difficult to identify.
Table G-2. Audio Input Subsystem
G-1
ECE 477 Final Report Spring 2009
Failure No.
Failure Mode Possible Causes Failure Effects Method of Detection
Criticality Remarks
C1 dsPIC to PIC communication breakdown
dsPIC or PIC stuck at fault or failure
Unable to Tune guitar or Transcribe music
Incorrect signal observed on SPI line
Low User interface will still function, but no meaningful operations will be performed.
C2 PIC to USB communication breakdown
PIC stuck at fault or failure, USB controller failure
Unable to store MIDI file onto a flash drive
Incorrect signal observed on SPI line
Low User will still be able to tune their guitar.
C3 PIC to LCD communication breakdown
PIC stuck at fault or failure, LCD screen failure
Unpredictable or meaningless displays on the LCD screen
Incorrect signal observed on UART line
Low User will still be able to use the product (in theory), but would have no display to view menus.
C4 Inaccurate Tuning or Note Detection
Software error in the dsPIC or PIC
Guitar will not be tuned correctly or MIDI file will not match notes played
Observation Low May be difficult to detect.
C5 Inaccurate motor control
Software error in the PIC Motors may turn continuously Observation High Could cause strings to break if PIC continues to output the same PWM signal.
Table G-3. Microcontroller Subsystem
Failure No.
Failure Mode Possible Causes Failure Effects Method of Detection
Criticality Remarks
D1 Push button(s) signal stuck open
Button failure or debounce IC failure
User interface does not recognize the button press
Observation Low Will severely limit what the user can do based on which button(s) is(are) affected.
D2 Push button(s) signal stuck closed
Short circuit in button or debounce IC
User interface will constantly change states
Observation Low User will be unable to control the menu/product
D3 Button press counted more than once
Debounce IC failure User interface changes more than one state at a time
Observation Low User will be unable to accurately control the menu/product.
Table G-4. User Interface Subsystem
Failure No.
Failure Mode Possible Causes Failure Effects Method of Detection
Criticality Remarks
E1 Motors do not spin Motors not connected or have failed
No tuning can be done Observation Low User will still be able to transcribe.
Table G-5. Motor Subsystem
G-2
Top Related