You are on page 1of 88

University Politehnica of Bucharest

Faculty of Electronics, Telecommunications and Information Technology

Measuring the Thiele-Small characteristics of a speaker

Diploma Thesis
submitted in partial fulfillment of the requirements for the Degree of
Engineer in the domain Electronics and Telecomunications, study
program Telecomunication Sistems and Technologies

Thesis Advisor
Conf. Dr. Ing. Mihnea UDREA

Student
Ilie GALAN

2013

Table of Contents

Introduction........................................................................................................................................15
Chapter 1: Theory...............................................................................................................................17
1.1

The Driver............................................................................................................................17

1.2

Break-in...............................................................................................................................18

1.3

Measuring the electrical resistance () .........................................................................18

1.4

Measuring the Resonance Frequency (fs) .....................................................................19

1.5

Measuring the Driver Impedance at the resonant frequency (Zmax) ............................20

1.6

Calculating the Driver Quality Factors ( Qts , Qms , Qes )...............................................20

1.7

Measuring the Effective Radiation Area of the Driver Cone (Sd ) ................................22

1.8

Determining the Driver Mechanical Compliance (Cms) ...............................................23

1.9

Calculating the volume of air equal to the driver compliance (Vas) .............................24

1.10

Determining the volume of the enclosure that will fit our driver (Vb) ..........................24

Chapter 2: KwikStick-K40.................................................................................................................27
2.1

CodeWarrior and Processor Expert......................................................................................28

2.2

Periodic Interrupt Timer (PIT).............................................................................................32

2.3

Liquid Chrystal Display (LCD)...........................................................................................36

2.4

12-bit Digital-to-Analog Converter (DAC).........................................................................38

2.5

16-bit Analog-to-Digital Converter (ADC).........................................................................49

2.6

Touch sense input (TSI).......................................................................................................54

Chapter 3: Implementation.................................................................................................................57
3.1

STAGE 0: Starting up and calibration.................................................................................57

3.2

STAGE 1: Measuring the DC Resistance of the driver ( ) ..........................................58

3.3

STAGE 2: AC Set-up and calibration..................................................................................59

3.4
STAGE 3: Measuring the Resonance Frequency fs and the value for the impedance of
the driver at resonant frequency Zmax ......................................................................................63
3.5
STAGE 4: Measuring the frequencies f 1f 2 at which the value of the impedance is
the geometric mean between and Zmax ..........................................................................64
3.6

STAGE 5: Measuring the Driver Mechanical Compliance Cms ...................................64

Conclusions........................................................................................................................................65
Bibliography.......................................................................................................................................67
Appendix 1: KwikStik_SegLCD.c.................................................................................................69
Appendix 2: Events.c......................................................................................................................73
Appendix 3: Board Schematics and Layout.......................................................................................85

List of Figures
Figure 1.1: Driver cross section.........................................................................................................17
Figure 1.2: Measuring the driver Electrical Resistance () ........................................................18
Figure 1.3: Measuring the Resonance Frequency..............................................................................19
Figure 1.4: The amplitude and phase variations of a driver simulated in LTSpice............................19
Figure 1.5: Driver frequency variation graph.....................................................................................21
Figure 1.6: Area of the cone...............................................................................................................22
Figure 2.1: Front Side of KwikStik Development Board [QSGKS]..................................................27
Figure 2.2: Back Side of KwikStik Development Board [QSGKS]..................................................28
Figure 2.3: File New Bareboard Project...................................................................................29
Figure 2.4: Selecting the board...........................................................................................................29
Figure 2.5: Selecting the connection type..........................................................................................29
Figure 2.6: Choosing Processor Expert as the Rapid Application Development tool........................30
Figure 2.7: Workspace configuration.................................................................................................30
Figure 2.8: MK40DX256ZVLQ10 Blocks and Pins Configuration..................................................31
Figure 2.9: PIT Block diagram [K40RM]..........................................................................................32
Figure 2.10: Initializing PIT...............................................................................................................33
Figure 2.11: PIT in

Components window......................................................................................33

Figure 2.12: PIT in Component Inspector window............................................................................33


Figure 2.13: Generate Processor Expert Code for PIT.......................................................................34
Figure 2.14: The generated files for PIT (TI1.c)................................................................................34
Figure 2.15: Square waveform at 5.971 kHz......................................................................................35
Figure 2.16: Square waveform at 5.974 Hz........................................................................................35
Figure 2.17: The LCD segment configuration...................................................................................36
Figure 2.18: Add LCD to project........................................................................................................36
Figure 2.19: LCD component functions.............................................................................................37
Figure 2.20: Display text "Hello".......................................................................................................37
Figure 2.21: Display number "22.9"..................................................................................................37
Figure 2.22: DAC Block Diagram [K40RM].....................................................................................38
Figure 2.23: Add DAC to project.......................................................................................................39
Figure 2.24: The output of the DAC (direct implementation)............................................................41

Figure 2.25: The FFT representation (direct implementation)...........................................................41


Figure 2.26: The difference when making a sine wave, using 10 samples with respect to using 100
samples...............................................................................................................................................42
Figure 2.27: Sinus function, plotted using the values generated by MatLab.....................................42
Figure 2.28: 10.01 Hz sinus function (for Freq1=100 or 10Hz)........................................................44
Figure 2.29: The FFT representation (for Freq1=100 or 10Hz).........................................................44
Figure 2.30: 100.2 Hz sinus function (for Freq2=1000 or 100Hz)....................................................44
Figure 2.31: The FFT representation (for Freq2=1000 or 100Hz).....................................................44
Figure 2.32: 79.43 Hz sinus function (for Freq3=795 or 79.5Hz).....................................................45
Figure 2.33: The FFT representation (for Freq3=795 or 79.5Hz)......................................................45
Figure 2.34: Linear interpolation error...............................................................................................46
Figure 2.35: 79.58 Hz sinus function (using linear interpolation).....................................................47
Figure 2.36: The FFT representation (using linear interpolation)......................................................47
Figure 2.37: Rising slope for the 10 Hz sinus wave...........................................................................48
Figure 2.38: Rising slope for the 100 Hz sinus wave.........................................................................48
Figure 2.39: ADC Block Diagram [K40RM].....................................................................................50
Figure 2.40: Add ADC to project.......................................................................................................51
Figure 2.41: Setting the conversion time for the ADC.......................................................................51
Figure 2.42: ADC measured value on Display...................................................................................52
Figure 2.43: ADC measurement.........................................................................................................52
Figure 2.44: Measured value if we set the DAC at 4095 (MAX)......................................................53
Figure 2.45: Measured value if we set the DAC at 3043...................................................................53
Figure 2.46: Copy the TSI component...............................................................................................55
Figure 2.47: TSI in Component Inspector window........................................................................55
Figure 2.48: TSI enabled....................................................................................................................56
Figure 2.49: TSI on hold....................................................................................................................56
Figure 3.1: Block diagram for measuring the DC connector cables resistance.................................57
Figure 3.2: Block diagram for measuring the electrical resistance of a driver..................................58
Figure 3.3: DAC generated sine wave with spikes............................................................................59
Figure 3.4: First order Butterworth low pass filter.............................................................................59
Figure 3.5: Sine wave after the LPF...................................................................................................60
Figure 3.6: First order Butterworth high pass filter............................................................................60
Figure 3.3.7: Sine wave after HPF.....................................................................................................61

Figure 3.3.8: The FFT representation.................................................................................................61


Figure 3.9: Voltage divider.................................................................................................................61
Figure 3.10: Sine wave after the voltage divider................................................................................61
Figure 3.11: Phase Variation...............................................................................................................62
Figure 3.12: Maximum amplitude at fs ........................................................................................62
Figure 3.13: Inverting summing amplifier.........................................................................................62
Figure 3.14: Difference between Vout

and Vin .......................................................................62

List of Acronyms
Name

Measuring
Units

Description

fs

hertz
[Hz]

Driver free air resonance

Re

ohms
[]

Electrical resistance of the coil measured at constant current

Z max

ohms
[]

Driver impedance measured in alternative current at resonant


frequency

Le

millihenry
[mH]

Q es

dimensionless

Driver electrical quality factor

Qms

dimensionless

Driver mechanical quality factor

Qts

dimensionless

Driver total quality factor

Cms

meters/Newton Driver mechanical compliance


[m/N]

Drivers coil impedance

V as

liters
[l]

It represents the equivalent air volume, that when pressed with a


surface equal to the surface of the driver membrane has the same
mechanical compliance (or elasticity) with the mobile system
composed of the drivers membrane, mobile coil and suspensions.

X max

millimeters
[mm]

The maximum possible movement of the driver membrane in one


direction (maintaining the same number of windings in the magnetic
field generated by the coil)

Vd

liters
[l]

The air volume dispatched by the driver membrane at

tesla
[B]

Magnetic field intensity in the coil

BL

tesla*meters
[B*m]

Driver motor strength. It is represented by the product between the


magnetic field intensity ( B ) and the length of the coil conductor (
L )

centimeters
[cm]

Driver membrane diameter

meters
[m]

Coil conductor length

M ms

grams
[g]

Total cone assembly mass including radiation mass

percentage
[%]

Pe

watts
[W]

X max

Energetic efficiency or driver yield


Electrical power that can be handled by the driver on long periods of
time without damaging itself

Vb
SPL

Sd

liters
[l]

The volume of the enclosure (box) that will fit our driver to form a
speaker

decibels
[dB]

Sound Pressure Level or acoustic pressure level. It is measured by an


SPL-meter placed one meter in front of a driver that radiates a power of
1W

centimeters
square
[cm^2]

Effective radiation area of the driver cone

OPAmp

Operational Amplifier

MCU

Multiport Control Unit

USB

Universal Serial Buss

PIT

Periodic Interrupt Timer

LCD

Liquid-Crystal Display

DAC

Digital-to-Analog Convertor

ADC

Analog-to-Digital Convertor

Fs
FFT

hertz
[Hz]

Sampling Frequency
Fast Fourier Transform

DC

Direct Current

LPF

Low Pass Filter

HPF

High Pass Filter

Introduction

Introduction
Measuring a driver is relatively simple and it only requires a scientific calculator, a
frequency generator, a couple of really accurate resistors (to form good voltage dividers), a good
AC voltmeter and a little patience. Most of the measurement can be made by anyone who knows
how to use the devices mentioned before but it can take some time and you have to always be
focused on your work; that is why I want to build a single device that performs the measurements
quickly and accurately.
We can say that drivers behave like a resonant circuit having therefore a resonant frequency
f s , and therefore a maximum impedance Z max . Knowing that resonant frequency and the
bandwidth at -3 dB we can compute the values of the quality factors Qms ,Qes , Qts . Also by
measuring the driver mechanical compliance Cms we can then calculate the volume of air
equal to the driver compliance V as . By knowing only these parameters and some geometrical
characteristics of the driver we can determine the optimum volume V b for an enclosure that
will fit our driver.
The reason why I chose to make a device that measures drivers is because not all of them
come with a specifications sheet made by the producer or maybe you have found a driver and you
wish to make an enclosure for it, therefore you will need to know some of their basic parameters.
There are a lot of books written about loudspeakers and drivers, so if you are an audio
enthusiast just like me, you will probably want to read a book by Ray Alden, Speaker Building
201 [SB201], which explains very well, at a beginners level, what is a driver, how does it work
and what do you have to keep in mind when you want do design a speaker system. If you want to
take it to the next level, then I recommend that you read Loudspeaker Design Cookbook by
Vance Dickason [LDC7], where you can find just about all there is to know about speakers and
drivers, presented in detail for those of us that already have a some broad knowledge on this
subject.
This paper, that you are going to read, presents at first a small introductive theory about
speakers; what they are, what parameters do they have and a glimpse on how to measure or
compute them. The theory is far from complete and it does not explain roughly why some formulas
where used the way they were but it provides sufficient information to understand what are the tasks
needed to be done and how I am going to implement the techniques necessary solve those tasks.
Although, if you are not interested in all of this audio stuff, but you like to program
microcontrollers, play with DACs, ADCs, LCDs or just build cool electronic circuits, you can
find out a lot of useful information further along in the book that will help you get started on a new
project or make you improve your current design. The microcontroller that I have used is called the
Kwikstick-K40 from Freescale Semiconductor, but the code and the methods that I have used can
work for anyone that is panning on using CodeWarrior and Processor Expert as the building
platforms for their projects.

15

Ilie-Iulian GALAN

I have developed this paper to act more as a guide to help anyone who wants to develop an
easy way of measuring drivers or act as a tutorial for those who those want to initialize a new
component into their CodeWarrior project.
Almost all of the procedures explained in Chapter 1: Theory will have their implementation
described in Chapter 3: Implementation, with references to the techniques presented in Chapter2:
Kwikstick-K40.
I consider my device to be necessary because it provides an easy and time efficient way of
measuring a driver, increasing the accuracy of the measurements by reducing human error factor
that comes along when we are trying to do the measurements by hand. A good and stable measuring
device can provide some accurate values for the Thiele/Small parameters of a driver, which will
lead to some very good enclosures designs, and a high sound quality for the speaker system.

16

Chapter1: Theory

Chapter 1: Theory
In this chapter I will try to explain the theoretical and practical principles behind measuring
the Thiele/Small parameters, principles upon which base my thesis and have constructed the
measuring device.
This whole chapter is based mainly on chapter eight of the book Loudspeaker Design
Cookbook, 7th edition by Vance Dickason. [LDC7]

1.1 The Driver


Drivers are basically a type of transformers that transform the electrical energy into
mechanical energy; therefore they can be decomposed into two parts: the electrical part which
includes the coil and the mechanical part which includes the cone and the suspension. An oscillating
signal applied at the input of the driver will make the coil move according to the magnitude and
polarity of the wave, therefore moving the cone and suspension along with it. In Figure 1.1 you can
see a cross section, for a more complete view of the driver and its components:

Figure 1.1: Driver cross section

The part of the driver that produces the sound is composed of the moving block which is
usually the cone and the dust cap or a one-piece dome and a suspension system which is made up of
the spider and the surround.
There is a whole chapter written in the book mentioned above about the driver components
and we will explain some of them further along in this paper referring back to this picture. The
purpose of my device is to generate a set of parameters that will model the behavior of these
mechanical and electrical components.
17

Ilie-Iulian GALAN

1.2 Break-in
Prior to testing, all cone speakers should be broken in 1. Because most of the drivers are
mass-produced, the materials that the suspension system is made from may be a little stiff and that
is why we must make sure that when we start measuring the driver, it is operating at its optimum
parameters.
To do this process you need to let the driver play for a long period (usually for 12 hours) at
low frequencies (best at 25 Hz) and with sufficient amplitude (it is recommended to use an
amplifier). This process loosens the suspension and it can reveal if the driver had any manufacturing
flaws like a broken suspension (poorly glued suspension or spider) or a misalignment (or rubbing)
of the voice coil. As you can see, this failure detection part that this system offers can be crucial
when choosing a driver to be measured, because if the driver presents some flaws then there is no
point in proceeding along with the actual measurements.

1.3 Measuring the electrical resistance (Re )


Usually most drivers are built with a fixed value for their electrical resistance, like 4, 8
or 16, to tell the audio amplifier manufacturers a specific value of the load that will be placed at
the output of their device, therefore allowing them to design a matched output stage that will ensure
a maximum power gain for the audio amplifier, but for most commercial drivers, the value of their
electrical resistance may slightly vary from the value specified by the manufacturer that is why it is
good to measure it because we will need to use it further along in some equations.
The simplest way to measure it is to connect a DC voltmeter directly to the terminals of the
driver and read the value from the measuring device.

Figure 1.2: Measuring the driver Electrical Resistance

(Re )

1 Break-in or breaking in, also known as run-in or running in, is the procedure of conditioning a
new piece of equipment by giving it an initial period of running, usually under light load, but
sometimes under heavy load or normal load. It is generally a process of moving parts wearing
against each other to produce the last small bit of size and shape adjustment that will settle them
into a stable relationship for the rest of their working life. [WIKI1]

Chapter1: Theory

1.4 Measuring the Resonance Frequency (f s)


I want to start by saying that the theory presented here fits the small-signal model for the
driver therefore when you will want to use small levels for the input voltage, less than 1V would be
preferable. Another known fact is that the driver can pick up some waves that are circulating in the
room (its acts like a microphone) that is why it is recommended that we do our measurements
outside, in free space, with the driver suspended 2-3 meters above the ground and away from any
other obstacle or wall, onto a rigid platform (preferably on a wood platform).
You will also need a reliable voltage oscillator, an accurate and known resistance of 1 k
and a really good AC voltmeter. You will generate sinusoidal wave with small amplitude and a
frequency varying between 10Hz and 100Hz using the signal generator, and with the AC voltmeter
you will measure the value for the amplitude across the voltage divider. For a clearer view see the
next picture.

Figure 1.3: Measuring the Resonance Frequency

Because the impedance of the driver varies with frequency, the output voltage will also vary.
The resonant frequency is the frequency at which the impedance of the driver is maximum;
therefore in order to find it we must vary the frequency on the sine wave oscillator until we find a
maximum for the output voltage.

Figure 1.4: The amplitude and phase variations of a driver simulated in LTSpice

19

Ilie-Iulian GALAN

In Figure 1.4 you can see the voltage and phase variations of the output voltage taken across
a driver at different frequency. You can clearly see that some ware between 10 Hz and 100 Hz there
is a peak in the voltage transfer function. We can say that that peak is formed at the resonant
frequency because we can observe that the phase deviation at that frequency is 0.

1.5 Measuring the Driver Impedance at the resonant frequency (Z max )


To measure the driver maximum impedance you must first find the resonant frequency and
keep feeding that frequency to the driver.
If you look back at Figure 1.3 you can see that the driver and the resistance R of 1k are
forming a voltage divider. By measuring the voltage across the driver, knowing the voltage
generated by the signal generator and knowing the other resistance you can then compute the value
for the impedance of the driver:
Zd=

RV out
V genV out

Although you have to keep in mind that you are also measuring the impedance of the wires
connecting the driver to the device, therefore you will have to measure them and subtract that value
from your result:
Z max =

RV out
Rc
V V out

1.6 Calculating the Driver Quality Factors ( Qts , Qms ,Q es )


By now we know the values for the electrical resistance of the driver ( Re ), the resonant
frequency ( f s ) and the value for the maximum impedance ( Z max ).
The driver can be considered a resonant circuit, and we know that the quality factor for a
resonant circuit is the ratio between the resonant frequency ( f s ) and the bandwidth at -3dB. In
this case, to make the measurements to be more exact, we will measure the bandwidth at -6dB. To
measure this bandwidth we must first fine the values for the frequencies at 6dB ( f 6 dB , f +6 dB
).
First we will calculate a reference impedance value:
Z ref =

Z max
Re

After which we can calculate


f 6 dB and f +6 dB :
Z x = Z ref R e

Z x , the impedance value of the driver at which we will find

Chapter1: Theory

Now by
frequency f s
the driver to be
frequency f s
impedance Z x

knowing Z x we just need to set the sine signal generator on the resonant
and then gradually lower the frequency until we get the value for the impedance of
Z x thus finding f 6 dB ; and then setting again the generator on the resonant
and this time rising the frequency until we get the same value for the driver
thus finding f +6 dB .

For a better understanding of the phenomena, I have plotted an approximation of a driver


response in frequency using MatLab and represented on that graph the values that were discussed
above:

Figure 1.5: Driver frequency variation graph

Finally we can now calculate the values for the quality factors using the following formulas:

Qms=

f s Z ref
mecahnical quality factor
f +6 dB f 6 dB

Qes=

Q ms
electrical quality factor
Z ref 1

Qts =

Q ms
total quality factor
Z ref
21

Ilie-Iulian GALAN

1.7 Measuring the Effective Radiation Area of the Driver Cone (S d )


The majority of the drivers have the shape of their cone built in the same manner, and they
all resemble the base of a cone. The surface of this king of a shape can be written as the sum
between the area of the bases and the lateral area:

S= A b+ A B + A lat =R12 + R 22 + ( R1+ R2 ) h 2+ ( R 2R1 )

For a better understanding please refer to the next figure:

Figure 1.6: Area of the cone

To determine the radiation area of the cone we must now subtract from the value obtained
above the area of the big base (it dose net really exists in the construction of a driver) and we must
add a third of the area of the suspension. To calculate the area of the suspension we will calculate
the whole area from which we will subtract the area of the big base:

S d =R12 + ( R 1+ R 2 ) h2 + ( R2 R 1 )2+

R32R22
3

or:
R 32R22
S d = R1 + ( R1 + R2 ) h + ( R 2R1 ) +
3

Chapter1: Theory

1.8 Determining the Driver Mechanical Compliance (C ms)


To determine the driver compliance we must first compute the value for the driver mass
M ms which can be computed by summing the values for the driver air-mass load M mr and
the assembly mass M md :
M ms=M md + M mr
The driver air-mass load M mr can be computed using the formula:
M mr=0.575Sd

1.5

To determine the assembly mass M md we will use the added mass method. For this
method we will need some extra weights to add on top of the driver cone. In my case I have used
some coins that I have pre-weighted and I have put them on top of the driver.
For this method to work we need to measure the resonant frequency of the driver and change
the mass of the cone (by adding weights) until the resonant frequency of the driver is lowered with
at least 25-30%.
When we are happy with the value that we have obtained for the frequency, we will note it
on a piece of paper as f s' and then calculate the value for the mass added and note it as
M a .
The value for the assembly mass M md can be then computed using the formula:

M mr=

Ma
fs
1
f s'

Combining all of the formulas above into one we can say that the driver mass M ms is:
M ms=0.575S d1.5 +

Ma
fs
1
f s'

By knowing the driver mass M ms we can now compute the driver suspension
compliance using the following formula:
Cms =[ ( 3.283f s )2M ms ]
where: - Cms
-

is given in meters/newton

M ms is given in kilograms
23

Ilie-Iulian GALAN

1.9 Calculating the volume of air equal to the driver compliance (V as)
To calculate the driver compliance V as we just need to input the data obtained above
into to following formula:
5

V as=1.4210 Sd C ms
There are some other methods for calculating V as but I have chosen the one above for
its simplicity.

1.10 Determining the volume of the enclosure that will fit our driver (V b )
The purpose of all these measurements is to find out the parameters of a driver and to design
the best enclosure that will fit that driver to form a speaker system.
There are whole books written about designing and building a speaker and I am not going to
go very hard into this matter. If you are really interested in designing your own speaker system you
can read the book Speaker Building 201 by Ray Alden [SB201].
There are mainly two types of enclosures; the closed enclosure and the vented enclosure. To
see what kind of box is recommended for your driver you can first calculate the efficiency
bandwidth product or EBP:

EBP=

fs
Q es

if EBP 50 then the driver is best suited in a closed box


if EBP > 50 then the driver is best suited in a vented box
This is not a rigid design rule but it only gives us a guideline for what to expect from our
driver.
If you decide on building a closed box then the volume of the enclosure should be:

V b=

V as
2

Qtc
1
Qts

( )

where: - Qtc is a value that describes the transient response of a box. You will want to take this
value to be 0.5 or 0.71 to avoid the ringing effect that a box can produce. For more information
please research the transient response curves of a speaker.
-

V b is given in liters

Chapter1: Theory

If you decided on building a vented box speaker then the volume of the box should be:

V b=15V as[ Qts2.87 ]

For the vented box you will need to find the tuning frequency f b

f b=

0.42f s

( Q ts)

0.9

And now to calculate the length of the port that will tune the box to this frequency:
7

lv=

1.46310 r
1.463r
2
f b V b

where: - l v

is the length of the port

- r

is the radius of the port

It is possible to use two or more ports, which combine will form the equivalent of a larger
diameter port:
D= D12+ D 22
It is good to know that there some rations for the dimensions of the box (l/ L/h) that are
widely spread among the speaker designers. The first one it scaled the acoustic ratio:
0.7937/1/1.2599

and the second one is called the golden ratio (found in mathematics and nature):
0.618/ 1/1.618

There are is a whole lot more to be discussed on this manner but I believe that with the
information presented above anyone can succeed at designing and building its very own speaker
system that will provide a good sound quality.

25

Ilie-Iulian GALAN

Chapter 2: KwikStick-K40

Chapter 2: KwikStick-K40
An ultra-low-cost, all-in-one development tool for evaluating, developing and debugging
Kinetis MCUs features the K40X256VLQ100 (144LQFP) MCU with USB, touch-sensing and
segment LCD functionality. It comes pre-flashed with demonstration software to exercise a small
portion of the capabilities of the Kinetis K40X256 device and the KwikStik hardware. The on-board
Segger J-Link debug interface allows the user to evaluate the features of the on-board K40
MCU or to develop, debug and program their own target hardware based on any Kinetis MCU.
The KWIKSTIK-K40 can be combined and used with several peripheral modules in the
Tower System; it can also be operated as a standalone debug tool.

Features:

K40X256 in 144 LQFP (256 KB flash, 256 KB FlexMemory, ultra-low-power, precision


mixed-signal)
LCD display with 306 segments
3.5 mm audio output jack and two micro USB connectors
Omni-directional microphone and a buzzer
On-board J-Link USB programmer
Infrared communications
Micro SD card slot
Capacitive touch-sensing interface
General purpose Tower plug-in (TWRPI) socket
Manganese lithium rechargeable battery [WWW2.1]

Figure 2.7: Front Side of KwikStik Development Board [QSGKS]

27

Figure 2.8: Back Side of KwikStik Development Board [QSGKS]

I have chosen this development board because it has a very good Digital-to-Analog
Convertor (DAC) to generate sinusoidal waveforms, a very good Analog-to-Digital Convertor
(ADC) to measure the output after connecting the driver, an Liquid-Crystal Display (LCD) to show
the results of the measurements, a really good operating frequency to conduct the calculus and it is
easy to learn more about by researching it the internet.
All of the following code examples apply to this development board.

2.1 CodeWarrior and Processor Expert


CodeWarrior is the name of the software provided by Freescale Semiconductor to be used to
build, debug and upload software applications for their line of microcontrollers. Or as stated on
their website: CodeWarrior Development Studio is a complete Integrated Development
Environment (IDE) that provides a highly visual and automated framework to accelerate
development of the most complex embedded applications [WWW2.1.1] .
Processor Expert technology is a development system to create, configure, optimize,
migrate, and deliver software components for Freescale silicon. This technology is integrated into
Freescales CodeWarrior products supporting S08/RS08, S12(X), Coldfire, Coldfire+, Kinetis, DSC
56800/E, and some Power Architecture processors.
Processor Expert technology makes it much easier for you to deal with the low level
intricacies of a hardware platform in an optimal manner. You do not have to accept generic onesize-fits-all drivers. You design custom peripheral drivers ideally suited to your needs, without
having to know everything about the hardware. Additional components are available online at the
Freescale Component Store.

Features:
28

Chapter 2: KwikStick-K40

Extensive and comprehensive knowledgebase of all supported silicon encapsulating all pins,
registers, etc.
System on a Chip (SoC) resource conflicts flagged at design time, allowing early correction
Simple creation of optimized peripheral drivers without reading massive documentation
Enables straightforward migration (wizard assisted) to new hardware
Enables creation of arbitrary custom components encapsulating any software
Can configure, package, and deliver components to your team
Fully integrated into CodeWarrior for Microcontrollers [WWW2.1.2]

For additional information on CodeWarrior and Processor Expert software please contact the
freescale website: www.freescale.com.
2.1.1 Setting up CodeWarrior and Processor Expert
First you need to start the CodeWarrior
software and create a new Bareboard project:
(File New Bareboard Project). This will
open a new window that will ask you to insert a
name for your project (any name will do but you
have to keep in mind that you cannot use the
same name twice).
Figure 2.9: File New Bareboard Project

After that you need to select your device


or board that you are going to use in your project
(in my case is the Kinetis PK40X256
microprocessor). You can either type some
characters from the name of your device in the
searching dialog box or you can search manually
for it by clicking the dropdown menu. Once you
have found your board, click next.

Figure 2.10: Selecting the board

Now you have to specify how your


computer
will
communicate
with
the
programmable board. For that you need to look
up your type of connector in your boards
reference guide under the section named
Programing the MC. In my case I have
selected the Segger J-Link and clicked next.
Figure 2.11: Selecting the connection type

Processor Expert as your Rapid Application


You can also click next to the following Development tool. Processor Expert can generate
panel and at the last panel you have to choose for you all the device initialization code and it
29

includes many low level drivers. Now you can


click finish and wait for the software to configure
itself.
Figure 2.12: Choosing Processor Expert as the
Rapid Application Development tool

Now you have finished configuring your software to work on your specific microprocessor.
You should have your workspace almost like in the following image, for which I will now start to
explain the use of every window.

Figure 2.13: Workspace configuration

On the upper left side of the screen we have the CodeWarrior Projects window, which
displays all the folders, subfolders and files that make your project. This window displays what is
actually written on our hard drive when we work on a project. When you chose to delete something
from this window it will be deleted from your hard drive as well so be careful. Now you should find
there a folder with the name of your project, click on it and it will expand and display some more
information. You should find a folder that says Sources, this is the folder that holds the main files
of your software design, where you will add your code, after which you compile, debug and write
them onto your microcontroller. The file that holds the main() function is called
ProcessorExpert.c, and its the file from where you should call most of your secondary functions.
There is also another file called Events.c that holds the declarations of all the interrupt functions
of your blocks that can be called. You can specify here specific tasks for every one of your interrupt
functions.
By clicking on one of these files you open up a new window in the middle of your
workspace that displays the actual code that is written on that file. From here you can modify the
code so it can serve your purpose, but as you can see, these files must obey a specific format that is
30

Chapter 2: KwikStick-K40

generated by ProcessorExpert. The format is explained clearly by the text that is generated in the
comment lines and it is very important to follow this format in order for the software to work.
In the right side of your workspace you should find the Components Library window
which holds all of the known features that your MCU can deliver. You can sort them alphabetically
or by category and you can add any one of them by right-clicking on it and selecting Add to
project. Also, if you need some information on the components you can right-click on it and select
Help in component which will open a new window with a lot of useful information on that item.
After you added your component, it will show up in the Components window which is
located in the lower left side of the workspace, under the components folder. In this window you
can tell the software if you want your code to be written in the FLASH or RAM section of the
microcontroller memory, and from here you can select one of you added components and modify its
parameters. We will talk about this when we will start adding components to our project.

Figure 2.14: MK40DX256ZVLQ10 Blocks and Pins Configuration

CodeWarrior has a lot of other windows and menus that we can use. One of those windows
is the Processor window (you can open it by clicking WindowShow viewProcessor)
presented in Figure 2.8, where we can see all of the blocks that we have on the microprocessor, its
31

pins and what block is connected to what pin. We can add a component using this menu as well by
right-clicking on the component and then Add to project.
2.1.2 Conclusions
CodeWarrior and Processor Expert are highly intuitive, easy to work with and you can find
a lot of examples on the internet that can help you understand them. Further on I will present some
of the components that I added and configured in order for my project to work.

2.2

Periodic Interrupt Timer (PIT)

The PIT timer module is an array of timers that can be used to raise interrupts and trigger
Direct Memory Access (DMA) channels.

Figure 2.15: PIT Block diagram [K40RM]

The main features of this component are that timers can generate DMA trigger pulses or
generate interrupts which are all maskable. We can also have independent timeout periods for
multiple timers.

32

Chapter 2: KwikStick-K40

2.2.1 Purpose
The current tendency in microcontroller programing is to work with interrupts. If we want to
write something on the DAC, read something from the ADC or call a function at a specific timing,
we need to use interrupts. Therefore we will use the PIT in order to generate periodic interrupts with
a frequency set by us.
2.2.2 Initialization and setting up
To initialize the PIT in your project you
have to search for that component in the
Component Library window, right-click on it
and select Add to project. Our PIT component
is named TimerInt_LLD.

Figure 2.16: Initializing PIT

You can now see that this new component is listed in the Components window (lower left
side of the workspace), and a new window has opened in the right side of the screen that says
Component Inspect. You can also see that this component has a red x sign on it which warns us
that this component needs to be set up (see Figure 2.10). You can set up the component by
configuring it as it suit you in the Component Inspect window (see figure 2.11).

Figure 2.17: PIT in


Components window

Figure 2.18: PIT in Component Inspector window

To get rid of that warning you just have to insert a value in the Interrupt period field. As
you can see in Figure 2.11, I have set my interrupts to be made with a frequency of 10 kHz, but you
can insert any value that you want and the software will automatically calculate de division factors
33

that it needs to apply to the primary clock period (PIT clock has a frequency of 50 MHz) in order to
get your desired frequency. You can change the name of your component to any value you want and
chose an interrupt source (you can have multiple interrupt that operate at different frequencies). I
also recommend that you set the Auto initialization field to yes, which will make the software
write the code to initialize the component an also set the OnInterupt field to Enabled, which
will let you use this timer as a trigger for an interrupt function TI1_OnInterupt. The interrupt
function will be prewritten in the Events.c file.
After you configured your component you have to press the Generate Processor Expert
Code (see Figure 2.12), which will write the files needed to operate this component and place them
in the Generated_Code folder located in your main project folder (see Figure 2.13).
You can also see in Figure 2.12 a list of all the functions that Processor Expert has generated
and can be used along with the timer component. If you hover over any of those components you
will see a description of that function appearing.

Figure 2.19: Generate Processor Expert Code for PIT

Figure 2.20: The generated files for PIT (TI1.c)

For the interrupt timer used in this example, Processor Expert has generated only the TI1.c
and TI1.h files and put them in the Generated_Code folder. But in Figure 2.13 you can see that
I have several other files generated, which will help me use the ADC, DAC, LCD and many other
components needed further in the project. You will have to make the same steps I have presented
above every time you want to add and use a new component. Now we are all set to start using this
timer and the interrupt function.
34

Chapter 2: KwikStick-K40

2.2.3 Generating a square waveform using interrupts


In order to test the interrupt component we will try to generate at the output a square
waveform that the values of 0V for the low part and 3V for the high part, and with a frequency
equal to half of the interrupt frequency.
We will keep the same configuration that we set when we initialized the PIT (see Figure
2.11), which means that we will have an interrupt frequency of 10 kHz.
If we open up the Events.c file located in the Sources folder and scroll down a little we
will find the declaration of the function TI1_OnInterrupt. This function was generated by
Processor Expert and its a function that calls itself with a frequency set by you when you modify
the Interrupt period field (in our case is 10 kHz).
To generate a square waveform you just have to write the following code:
unsigned int i=0; // a variable
void TI1_OnInterrupt(LDD_TUserData *UserDataPtr)
{

/* 2.2.3

Generating a square waveform using interrupts */

DA1_SetValue(DA1_DeviceData, i*4090);
i=(i+1)%2;

/* Set converter output */

// a flag which tells us if the function is low or high

The interrupt function first tells the DAC to write something at its output by calling the
function (this function will be explained in the next section where we present the
DAC) and then modifies the flag.
DA1_SetValue

By connecting the output of the DAC to an oscilloscope and by changing the interrupt
frequency from 10 kHz to 10 Hz we get the following images:

Figure 2.21: Square waveform at 5.971 kHz

Figure 2.22: Square waveform at 5.974 Hz

Because we chose to generate at the output two alternate values every time the interrupt
function is called, then we should get a square waveform with a frequency of half the frequency of
the interrupt function; therefore the first signal should have a frequency of 5 kHz and the second
one should have a frequency of 5 Hz.
35

From Figures 2.14 and 2.15 we notice that the microcontroller is generating the square
waveforms with a +19% deviation of the frequency from the desired value. The first thing that came
to my mind was to reduce the frequency with which the interrupt function is calling itself by 19%.
F 1=

10 kHz
10 Hz
=8.4 kHz , F 2=
=8.4 Hz
1.19
1.19

If we compensate for this error and insert the new values for the frequencies at the Interrupt
period field in the Component Inspector window, the results are perfect.
2.2.4 Conclusions
We can successfully use PIT as an accurate timer or as an interrupt for a specific function
within the microcontroller as long as we remember to scale its working frequency by -19%.

2.3 Liquid Chrystal Display (LCD)


Our KwikStik K40 has an 8x38 = 304 segment LCD integrated in the main development
board. Beside those 304 individually configurable segments the LCD has some other predefined
figures that can be displayed, as you can see in the following image:

Figure 2.23: The LCD segment configuration

2.3.1 Purpose
We will use the LCD to display some useful information during the test.
2.3.2 Initialization and setting up
To initialize the DAC we need to follow the same stapes as initializing the PIT.
In the Components Library window we
will search for the SegLCD_LLD, right-click
on it and Add to project. You will see that the
component is added to the project without any
errors and you can start using its functions.
36

Figure 2.24: Add LCD to project

Chapter 2: KwikStick-K40

2.3.3 Displaying a text


As you can see in the Components
window, this component has only two functions
to work with, one to initialize it and another to
select the segments, therefore if we want to write
something on the LCD we need to build our own
functions.

Figure 2.25: LCD component functions

2.3.4 To do this I have written a new file called KwikStik_SegLCD.c


where I have implemented some other functions like lcd_DisplayNumber,
lcd_DisplayChar or lcd_DisplayString, ho can display more facile the
information on the screen. The code for this function is kind of long but if you are
interested you can find it attached in Appendix 1 at the end of this paper.
Now if we want to display a text on the LCD, something like Hello, we just
have to call the following function:

lcd_DisplayString(SegLCD1_DeviceData, 0, "Hello");

where, the first argument specifies the component to be used, the second number specifies the
position where we want to start the display and the last argument is the text that we want to be
desplayed.
If we want to display a number, lets say 22.9, we will use this function:

lcd_DisplayNumber(SegLCD1_DeviceData, 0, 2, 1, 229);

whereas in the previous case, the first argument specifies the component to be used, the second
number specifies the position where we want to start the display, the third number specifies the
number of digits ahead of the comma, the forth number specifies the number of digits after the
comma and the last number is the number we want to display written with the comma removed
(microprocessors perform better if we only work with natural numbers).

Figure 2.27: Display number "22.9"

Figure 2.26: Display text "Hello"

37

2.4
in real life.

In the figures above you can see how the effect of the functions looks

2.4.1 Conclusions
It is very useful to have a display when you are working in a project and you
want to check and verify the results while you work. I will also use the LCD to make
my application more user-friendly with text and information that will guide the user
during the measurements.

2.5 12-bit Digital-to-Analog Converter (DAC)


The KwikStik K40 has a 12-bit low power, general purpose digital-to-analog
converter (DAC). The output of this DAC can be placed on an external pin or set as
one of the inputs to the analog comparator, OP-Amps, ADC, or other peripherals.

Figure 2.28: DAC Block Diagram [K40RM]

38

Chapter 2: KwikStick-K40

As you can see from its block diagram (Figure 2.17), this is a successive
approximation DAC which uses a 4096-level resistive network, selectable thru a
1/4096 multiplexer that is driven by a 12-bit register. We also have a buffer integrated
in the circuit to separate the DAC from the other devices that will be put at its output.
2.5.1 Purpose
We need this DAC in order to generate a sinusoidal waveform which can have any
frequency from 10Hz to 100Hz; that we need in order to test out the driver frequency response and
to find the resonant frequency.
2.5.2 Initialization and setting up
To initialize the DAC we need to follow the same stapes as initializing the PIT.

In the Components Library window we will search for


the DAC_LLD, right-click on it and Add to project. You
will see that the component is added to the project without any
errors this time and you can start using its functions.

Figure 2.29: Add DAC to project

Although there arent any errors, you can still change some of its proprieties in the
Component Inspector window (it should look almost like the one in Figure 2.11) as: the name of
the component, which of the two D/A convertor you want to use (DAC0 or DAC1), chose if you
want to use the predefined interrupt function that comes with it, which output pin you will use, the
resolution and many other. I also recommend setting Low power mode do Disabled if you
power your development board by an USB cable instead of its battery and set Auto initialization
to yes to make the software write the initialization code for you.
To write something on the DAC we just use the DA1_SetValue(DA1_DeviceData,
value); function where DA1_SetValue is the name of the function that sets an output value for
component DA1, DA1_DeviceData is the component declared by Processor Expert, and
value can be any number between 0 and 4095 ( 2121 which tells the DAC what value
between 0V and 3V to set at its output.
2.5.3 Generating a sinusoidal waveform: Direct implementation
The simplest way is to make the processor compute the values for the sinus waveform. The
sinus function can be written as:
f ( t )= Asin ( 2f t+ )=Asin ( t+)
where:

the amplitude - is the peak deviation of the function from zero

f the ordinary frequency - is the number of oscillations (cycles) that occur


each second of time
=2 f the angular frequency - is the rate of change of the function argument
in units of radians per second

39

the phase - specifies (in radians) where in its cycle the oscillation is as

t=0

To simplify the relation we will take that A=1 (the amplitude of the function will be
between -1 and 1), =0 (the waveform starts without any phase shift) and f =1 (the
waveform will have a frequency depending only on the number of computed samples and the
frequency of the microprocessor that computes the samples). Therefore we can implement the
following formula:
samples
Number
2i

f =sin

where: i a counter - is an integer number that goes from 0 to

samples
Number

samples
the number of points in which you want to compute the function - this
Number
number determines the fidelity of the resulted waveform
Now we have generated a number of values of the sinus function that have values in the
interval [-1, 1]. The problem is that our processor cannot work so well with negative values,
therefore we will add the value 1 to our result to eliminate this problem. This will make our results
be in the range [0, 2], but our 12-bit DAC works with values from 0 to 4095 (2121) therefore
we will multiply the result with 4095/2:
samples
Number
2i

( +1 ] 4095
2
sin
f =
In code we can write it like this:
/* The direct implementation */
#include <math.h>
#define SINUS_LENGTH 1000

// the number of samples

unsigned int i=0;

// a counter

float f;

// the value for the sin() function

void TI1_OnInterrupt(LDD_TUserData *UserDataPtr)


{
f=(sin(2*3.14*i/SINUS_LENGTH)+1)*2047;

40

//calculating the value

Chapter 2: KwikStick-K40
/* Set converter output */

DA1_SetValue(DA1_DeviceData, f);
i=(i+1)%SINUS_LENGTH;

//incrementing the counter

In the code above, I have declared a value for the number of samples that I want to compute
(1000 samples), two variables for counter and for the value of the sin() function and I have called an
interrupt function which computes the value for the amplitude, tells the DAC to write that value at
its output and increments the counter. This interrupt function is set to execute itself once every
100s (with a frequency of 10 kHz).
I have built and debugged the code, uploaded it the microcontroller and connected the output
of the DAC to the oscilloscope. The following picture resulted:

Figure 2.30: The output of the DAC


(direct implementation)

Figure 2.31: The FFT representation


(direct implementation)

From Figure DAC.1 we can see that the microprocessor is generating a periodic signal
which has the frequency of 6.743Hz and the peak-to-peak amplitude of 2.98V, but we can also
notice that the waveform is slightly bent to the left. To see how accurate our generated sinusoidal
wave is, I have set the oscilloscope to perform the Fast Fourier Transform (FFT) of that signal to
see if it has components on other frequencies as well. From Figure DAC.2 we can see that our
signal has a DC component and the main frequency set on 0 =6.75 Hz (which is good because
it is the same with what we have measured before), but we can also see that the signal has
components on other frequencies as well: 1 13.5 Hz , 2 20 Hz , 0 27 Hz , and so on, which
have significant amplitudes ( 1 is only 20dB below 0 and all the other frequencies are at
least 20dB above the amplitude of the noise).
Because of those extra spectral components we say that the microprocessor cannot compute
accurately the values for the sin() function and we must use a different method of generating the
sinusoidal waveform.
2.5.4 Generating a sinusoidal waveform: Table search method
To eliminate the error generated by the microprocessor when computing the sin() function,
we will calculate a number of values for the that function using a different software (in my case
MatLab) and store there values as an array in the memory of the microprocessor. After having that
data we can tell the microprocessor read the values from that array and set the output of the DAC
according to that value eliminating the need of excessive computations.
41

I will make MatLab generate 1000 values for the sinus function and have them stored in a
file. I chose to generate 1000 values because I plan to use a frequency of 10 kHz (8.4 kHz after we
scale it by -19%, as discussed in the interrupt section) as the sampling frequency of the
microprocessor and it will result that the microcontroller will be able to give at the output a stable
frequency of 10 Hz (F=Fs/N).
If we want to generate a higher frequency than 10 Hz, we can define a step that will be
incremented with itself and will tell us the position of the next sample, therefore skipping some
values (for example, to go from 10 Hz to 100Hz we will define =10 and therefore taking every
10th sample, jumping over the other 9).
Figure 2.21 shows us the problems that occur when we chose to jump over values when
creating a sinusoidal wave.

Figure 2.32: The difference when making a sine wave, using 10 samples with respect to using 100 samples
(taking every 10th sample)

To generate the values I have wrote the following MatLab code:


N=999;

% the number of samples-1

n=0:2*pi()/N:2*pi();

%a vector that goes from 0 to 2*PI with a step of 2*PI/N

f=(sin(n)+1)/2;

% computing the value of the sine, adding 1 to have only


positive values and dividing it by 2 to have the highest
value be 1

f=round(f*4095);

% multiply the result with 4095 (2^12) to increase the


range of values from [0,1] to [0,4095] and rounding them
to get only integer values that the DAC can work with

plot(f)

% plots the result

grid,title('Sinus Wave'),xlabel('Samples'),ylabel('Amplitude')
fid=fopen('sinus.txt','w','b');

% copy the result into a file for further use

fprintf(fid,'%d, ',f);
fclose(fid);

42

Chapter 2: KwikStick-K40
Sinus Wave

4500

Sinus.txt

4000

2048, 2060,
2138, 2150,
2228, 2240,
2317, 2330,
2406, 2419,
2495, 2507,
2582, 2595,
2669, 2681,
2754, 2766,
2838, 2850,
2920, 2932,
3001, 3012,
3080, 3091,
3156, 3167,
3231, 3242,
3303, 3314,
3373, 3383,
3441, 3450,
3506, 3515,
3567,
3576,
3618, .....

3500

Amplitude

3000
2500
2000
1500
1000
500
0

100

200

300

400

500
600
Samples

700

800

900

1000

Figure 2.33: Sinus function, plotted using the


values generated by MatLab

2073, 2086, 2099, 2112,


2163, 2176, 2189, 2202,
2253, 2266, 2279, 2292,
2343, 2355, 2368, 2381,
2432, 2444, 2457, 2469,
2520, 2532, 2545, 2557,
2607, 2619, 2632, 2644,
2693, 2705, 2717, 2730,
2778, 2790, 2802, 2814,
2861, 2873, 2885, 2897,
2943, 2955, 2966, 2978,
3023, 3035, 3046, 3057,
3102, 3113, 3124, 3135,
3178, 3189, 3199, 3210,
3252, 3262, 3273, 3283,
3324, 3334, 3344, 3354,
3393, 3403, 3412, 3422,
3460, 3469, 3478, 3487,
3523, 3532, 3541, 3550,
3585,
3593,
3601,

2125,
2215,
2304,
2394,
2482,
2570,
2656,
2742,
2826,
2908,
2989,
3068,
3146,
3221,
3293,
3364,
3431,
3496,
3559,
3610,

Figure 2.22 shows the sinus function which was generated using the values computed by
MatLab, and to the right you can see some samples of those values that were written in the
Sinus.txt file.
After we have generated the values we have to feed them to the microcontroller. To do that I
have defined a static variable that holds the number of values generated by MatLab and declared a
new array the size of that static variable which holds the generated values:
/* Table search method */
#define SINUS_LENGTH 1000

// the number of samples

static const LDD_DAC_TData SinusOutputData[SINUS_LENGTH] = {


2048,
2189, ...

2060,

};

2073,

2086,

2099,

2112,

2125,

2138,

2150,

2163,

2176,

// there are 1000 values declared here in the code

unsigned int i=0;

//a general purpose variable (in our case a counter)

int Freq=100;

//the output frequency (in Hz*10)

int Counter_Freq=0;

//a counter for the unitary and fractional part of the


frequency

void TI1_OnInterrupt(LDD_TUserData *UserDataPtr)


{
DA1_SetValue(DA1_DeviceData, SinusOutputData[i]);

/* Set converter output */

i=(i+Counter_Freq/100)%SINUS_LENGTH;

//calculates the position


of the next sample

Counter_Freq=Counter_Freq%100+Freq;

//calculates the new value


for the frequency counter

43

Next I have declared 3 new variables; first i which is a counter that indicates the position
of the next sample that needs to be sent at the output of the DAC, Freq which is a variable that
holds the value of the frequency that we want to obtain at the output and Counter_Freq which is a
variable that holds the value for the unitary and fractional part of the frequency and it is
incremented with itself. After the declarations I have called an interrupt function that is set to call
itself one every 100s. The function first tells the DAC to set at its output the value at the position
i from the array declared above, after that the function increments i with the decimal value of the
frequency pus 1 if the value of Counter_Freq is above 100 and finally increments Counter_Freq
with its initial value.
Running and uploading this code to de microprocessor for two different values of Freq
(Freq1=100 or 10Hz and Freq2=1000 or 100Hz) results in the following waveforms on the
oscilloscope:

Figure 2.34: 10.01 Hz sinus function


(for Freq1=100 or 10Hz)

Figure 2.35: The FFT representation


(for Freq1=100 or 10Hz)

Figure 2.36: 100.2 Hz sinus function


(for Freq2=1000 or 100Hz)

Figure 2.37: The FFT representation


(for Freq2=1000 or 100Hz)

We are happy to see that we are able to make the microprocessor produce such an accurate
frequency for the sinusoidal wave (with an error 0.5%) and without any other spectral
components beside their fundamental frequency.
44

Chapter 2: KwikStick-K40

The code above can tell the microcontroller to change the frequency with a sensitivity of 0.1
Hz therefore I have told the microcontroller to generate a sinusoidal function of 79.5 Hz
(Freq3=795) and I have attached the plots for the function and its FFT.

Figure 2.38: 79.43 Hz sinus function


(for Freq3=795 or 79.5Hz)

Figure 2.39: The FFT representation


(for Freq3=795 or 79.5Hz)

In conclusion, using this method offers good results when we want to generate sinusoidal
waveforms with frequencies within the range of 10 Hz up to 100 Hz with a step of only 0.1 Hz and
an accuracy of 0.5%.
2.5.5 Generating a sinusoidal waveform: Table search and linear
interpolation method
I will start by saying that we have obtained a pretty good sinusoidal wave with the previous
method but if you have a good microprocessor to make some extra computations and if you want to
generate an even better sinusoidal waveform you can apply linear interpolation to the previous
method.
Linear interpolation is effective if you want to generate a frequency that in not a multiple of
10Hz. Lets say for example that we want to generate a sinusoidal wave that has the frequency of
25Hz. With the previous method we would have taken the samples that are on the positions 0, 2, 5,
7, 10 and so on, therefore jumping at first over 1 value after which we jump over the next 2
values and then repeating the process. This means that we approximate the value that would have
been on position 2.5 with the value that is on position 2. To reduce this error (we dont eliminate it
completely) we will use linear interpolation between the two nearest points.
Linear interpolation method is based on the equation of a straight line:
y 2 y 1=m( x 2x 1)
We will always use two consecutive points therefore:
x i+1x i=1 m= y i +1 y i
Now if x is not a natural number, but instead it is a fractional number (in our case
=2.5), we can divide it by writing separately its natural part and fractional part: =i+ f (in our
case i=2 and f =0.5 ).
45

Now if we want to find the value, obtained by linear interpolation, which is placed at
position x we can apply the following formula:
y = y i+ f m

y = y i+ f ( y i +1 y i)

The following picture illustrates this procedure and shows how much we reduced the error.

Figure 2.40: Linear interpolation error

To implement this method I wrote the following code:


#define SINUS_LENGTH 1000

// the number of samples

static const LDD_DAC_TData SinusOutputData[SINUS_LENGTH] = {


2048,
2189, ...

2060,

};

2073,

2086,

2099,

2112,

2125,

2138,

2150,

2163,

2176,

// there are 1000 values declared here in the actual code

unsigned int i=0;

// a counter

int Freq=795;

//the output frequency (in Hz*10)

int Counter_Freq=0;

//a counter for the unitary and fractional part of the


frequency

int value=0;

// a variable used to store the value of the output - in


the 'Table search and linear interpolation method'

/* Table search and linear interpolation method */


DA1_SetValue(DA1_DeviceData,value);

/* Set converter output */

if(SinusOutputData[i+1]>SinusOutputData[i]){

//the microprocessor

value=SinusOutputData[i+1]-SinusOutputData[i]; dosen't operate well

46

Chapter 2: KwikStick-K40
with negative values

}else{

value= SinusOutputData[i]-SinusOutputData[i+1]; therefore we have to see


}

if the sinus wave is rising


or falling

i=(i+Counter_Freq/100)%(SINUS_LENGTH-1);

//calculates the position


of the next sample

Counter_Freq=Counter_Freq%100+Freq;
value=SinusOutputData[i]+value*(Counter_Freq%100)/100;//computes the value of
the sample that we want to
set at the output

The resulted signal which should have a frequency of 79.5 Hz looks like this:

Figure 2.41: 79.58 Hz sinus function


(using linear interpolation)

Figure 2.42: The FFT representation


(using linear interpolation)

The figures above look almost the same as Figure DAC.11 and Figure DAC.12 that
represent the same 79.5 Hz sinusoidal waveform that was obtained without using linear
interpolation.
My conclusion is that our microcontroller can generate a good sinusoidal waveform without
using linear interpolation. Adding more variables and extra equations will only add more
computations that the microprocessor has to evaluate which only adds to the load and complexity of
the software. Although, this method can be used if we want to improve the quality of our design.
2.5.6 Resolution
I want to find out what is the resolution with which our DAC operates. The resolution is the
minimum voltage change that the microcontroller can set at its output. Because the DAC works on
12 bits, it can deliver only 4096 ( 212 ) values for the output voltage, and it can operate only
within the voltage range of [0, 3] V. Therefore we can say that it has a resolution of:
R DAC =

3V
=0.73242 mV / sample
4096 samples
47

We have 4096 possible samples but we have declared an array of only 1000 elements,
therefore our sinus wave fill not use all of the 4096 samples, instead it will change with a higher
step.
I have written the following code in MatLab to see which is the maximum difference
between the 4096 possible output values.
max=0;

% a variable used to store the value for the maximum


diference of the numbers in the array

for i=2:N+1
x=f(i)-f(i-1);
if (x>max)
max=x;
end

% we can say that our sinus has the same rate of change
on the rising slope as on the falling slope

end
max

The code delivered the following result:


max =

13

This means that the Resolution of our sine wave which has a frequency of 10 Hz is 13 times
higher than the resolution of the DAC:
R10 Hz =13R DAC =9.52146 mV /sample
This only happens when the sinus wave has the biggest slope (it ascends or descends
rapidly). This effect is not really noticeable at 10 Hz but at 100 Hz you can see it clearly on the
oscilloscope. The reason is that at 100 Hz the microprocessor reads every 10th sample and this
leads to a higher difference between those 4096 possible outputs. I asked MatLab to compute this
difference and it gave the following result:
max =

129

which leads to the following resolution:


R100 Hz=129R DAC =94.48218 mV /sample

48

Chapter 2: KwikStick-K40

Figure 2.43: Rising slope for the


10 Hz sinus wave

Figure 2.44: Rising slope for the


100 Hz sinus wave

In the previous figures you can see clearly how the resolution with which we build the wave
affects the shape of the signal.
I think that most of the drivers that we are going to measure with this application dont have
their resonant frequency above 50 Hz, therefore we can say that the resolution problem wont affect
us that much, but to be safe I will implement a simple filter that will be placed at the output of the
DAC to eliminate those steps and to smoothen the waveform. This circuit will be discussed in
another chapter.
2.5.7 Conclusions
Our K40 ARM microprocessor cannot generate on its own a good sinusoidal waveform (I
have tried using the #include <math.h> library to make use of the sin() function but the output
resulted in a bent sinusoidal wave that had components on several frequencies). To eliminate this
error I have calculated the value for the sin() function on the interval [0, 2] in MatLab and
inputted those values in CodeWarrior as an array from which the software can read the values for
the sin() function at different time intervals and construct its own wave.
Using the table search method offers good results when we want to generate sinusoidal
waveforms with frequencies that range from 10Hz up to 100Hz with a step of only 0.1Hz and an
accuracy of 0.5%.
Although the 12-bit DAC can operate at a resolution of 0.73242 mV / sample our resulted
sinusoidal waves will have other resolutions that will range from 9.52146 mV / sample at 10 Hz
to 94.48218 mV /sample at 100 Hz. To reduce the effect that this resolution has on the shape of
the signal we will filter the output and smoothen the waveform.

2.6 16-bit Analog-to-Digital Converter (ADC)


The KwikStik K40 has a 16-bit analog-to-digital converter (ADC) which is a successive
approximation ADC designed for operation within an integrated microcontroller system-on-chip.
Features of the ADC module include:
Linear successive approximation algorithm with up to 16-bit resolution
Up to 4 pairs of differential and 24 single-ended external analog inputs
49

Output modes: differential 16-bit, 13-bit, 11-bit and 9-bit modes, or single-ended 16-bit, 12-bit,
10-bit and 8-bit modes
Output formatted in 2's complement 16-bit sign extended for differential modes
Output in right-justified unsigned format for single-ended
Single or continuous conversion (automatic return to idle after single conversion)
Configurable sample time and conversion speed/power
Conversion complete / hardware average complete flag and interrupt
Input clock selectable from up to four sources
Operation in low power modes for lower noise operation
Asynchronous clock source for lower noise operation with option to output the clock
Temperature sensor
Hardware average function
Selectable voltage reference: external or alternate
Self-calibration mode
Programmable Gain Amplifier (PGA) with up to x64 gain [K40RM]

50

Chapter 2: KwikStick-K40

Figure 2.45: ADC Block Diagram [K40RM]

For more information on the ADC please see the K40 Sub-Family Reference Manual
[K40RM], at page 767.
2.6.1 Purpose
We plan on using our driver together with a very accurate resistance in a voltage dividing
configuration. We will then use the ADC to measure the voltage drop across the driver and knowing
the voltage drop across the whole divider (voltage generated by the DAC) we can calculate the
value for the impedance of the driver at certain frequencies.
51

2.6.2 Initialization and set-up


To initialize the ADC we need to follow the same stapes as initializing the PIT.
In the Components Library window we will search for
the ADC_LLD, right-click on it and Add to project. You
will see that the component is added but an error is displayed.
To solve that error we need to go to the Component Inspector
window and select a Conversion time by double-clicking one
of the values predefined there.

Figure 2.46: Add ADC to project

For the Conversion time I have selected


the value of 9.615385 s (which is the highest
value that I could select). After I chose this
parameter the software automatically calculated
a set of 5 other values that you can see in Figure
2.36 including the ADC clock which is set to
Figure 2.47: Setting the conversion time for the ADC
2.621 MHz.
Therefore if I generate frequencies between 10 and 100 Hz using the DAC, and I sample
them with a frequency of 2.621 MHz then I will have between 262 and 26 thousand samples per
period for my reconstructed signal, which are more than enough.
You can make your own adjustments just like with the DAC, but I still recommend to enable
the Interrupt service/event and set Auto initialization to yes. Also I have selected the
PTE24/CAN1_TX/UART4_TX/EWM_OUT_b/ADC0_SE17 as my Input A/D channel (pin). I
have chosen this pin because it is conveniently connected to an external pin B42 located on the
Primary connector of the board (see Figure 2.1 and 2.2). For more information on how the pins
are connected please see the file KWIKSTIK-K40-SCH_V5.pdf, (KWIKSTIK-K40 Schematics),
[K40SCH].
2.6.3 Measuring the voltage drop
To measure the voltage drop and testing the accuracy of the ADC we will connect the output
of the DAC which will generate a constant voltage to the input of the ADC and read the result.
To generate a constant voltage using the DAC we only have to choose what value we want
to generate, scale this value to its respective 12-bit number and set this number to the DAC. Lets
say that we want to generate the value of 2.2V; we know that the DAC works linearly and 3V is the
maximum voltage that can be generated if we feed it with the value of: 2121=4095 , therefore
we just need to scale this value using the linear approximation method:
Value=

2.2
4095=3003
3

To implement this we wrote the following code in the main function:

52

Chapter 2: KwikStick-K40
int main(void) {
DA1_SetValue(DA1_DeviceData, 3003);

// Sets the DACs output

AD1_SelectSampleGroup(AD1_DeviceData,0);

// Selects the ADC

for(;;) {
AD1_StartSingleMeasurement(AD1_DeviceData);

//Starts the ADC measurement

}
}

The ADC has its own interrupt function in the Events.c file that calls itself every time the
measurement is complete. I have set this function to read the value of the measurement and display
the result on the LCD(see Figure 2.42) and connected the oscilloscope to get a read on the actual
value (see Figure 2.43):
void AD1_OnMeasurementComplete(LDD_TUserData *UserDataPtr)
{
AD1_GetMeasuredValues(AD1_DeviceData,&ADC_Value);
lcd_DisplayNumber(SegLCD1_DeviceData, 0, 5, 0, ADC_Value);
}

Figure 2.48: ADC measured value on Display


Figure 2.49: ADC measurement

From the LCD we notice that the last two digits arent readable because they change
rapidly but we can clearly read the first three digits. Just for the purpose of computations
lets make a rough average and say that the value displayed on the LCD is 47550. Knowing
that we are working with an 16-bit ACD, tells us that the ADC will yield the value 0 for a
2
voltage of 0V and the value 65535 ( 161) for 3V. Therefore, if we want to know the

53

voltage value measured by the ADC we just need to make again a linear approximation
between these last values.
Voltage=

47550
3=2.1768368 V
65535

We can now compute the measuring error:


e mas =1

2.1768368
=0.01V =10 mV
2.2

We have to keep in mind that this error is made mostly because of the noise that affects the
circuit and especially the wires.
2.6.4 Making an average between the measured values
We assume that the noise is a white noise which has the mean 0, that is why we will
implement a function that will make an average between all the measured values. To have a clearer
view of the result I have set the DAC to give at its output the maximum voltage.
AD1_GetMeasuredValues(AD1_DeviceData,&ADC_Value);
avg=ADC_Average(avg, ADC_Value, Nr_Measurements);
lcd_DisplayNumber(SegLCD1_DeviceData, 0, 5, 0, avg);

if (Nr_Measurements < 255){


Nr_Measurements++;
}

I chose that the number of measurement should not go above 255 because, in my opinion,
the values that we get in the last measurement should influence a lot more the result than the values
that were obtained in the first few measurements.
The result is encouragingly stable but discouragingly inaccurate:

Figure 2.50: Measured value if we set the DAC at


4095 (MAX)

Figure 2.51: Measured value if we set the DAC at


3043

In Figure 2.44 we see that if we set the DAC to give the maximum voltage (set the value of
4095) and connect the ADC directly to its output, we will read the value of 64664 which correspond
to a voltage value of:
54

Chapter 2: KwikStick-K40

Voltage=

64664
3=2.96 V
65535

which is 0.04 V

or 40 mV

lower that what we expected.

From now on we will take for granted that the DACs maximum output voltage is
and not 3 V .

2.96 V

To test the accuracy of our averaging function and of our previous approximation, we will
set again the DAC to give us a voltage of 2.2V , supplying a new input value:
Value=

2.2
4095=3043.581 3044
2.96

The result can be seen in Figure 2.45. The corresponding voltage value is now:
Voltage=

48042
2.96=2.199 V
64664

And the resulting error is:


e mas =1

2.199
4
=4.545410 V 0.5 mV
2.2

2.6.5 Conclusions
We can use this 16-bit Analog to Digital Convertor, that is integrated into our KwikStik K40
development board, to make some really accurate voltage measurements with a theoretical error of
only 0.5 mV .
Although we have to keep in mind that if we chose to use the internal DAC as our voltage
generator then our maximum voltage value will be 2.96 V and not 3 V which will correspond
to a 16-bit ADC value of 64664 and not 65535 ( 2161 ; which should have been its
maximum).

2.7 Touch sense input (TSI)


The touch sensing input (TSI) module provides capacitive touch sensing detection with
high sensitivity and enhanced robustness. Each TSI pin implements the capacitive measurement of
an electrode having individual programmable detection thresholds and result registers. The TSI
module can be functional in several low power modes with ultra-low current adder and waking up
the CPU in a touch event. It provides a solid capacitive measurement module for the
implementation of touch keypad, rotaries and sliders. [K40RM]

55

2.7.1 Purpose
We will enable the capacitive touch sense input in order to create a menu and, allow the user
to select the features that he wants to use from our project.
2.7.2 Initialization and set-up
Another method of initializing a component is to copy it from an existing project and insert
it into your current project.
To do this just open that past project, and
in the Components window right-click on the
desired component (in our case the
TSI1:TSI_LDD) and then click copy after
which open your current project and paste it in
the same folder.

Figure 2.52: Copy the TSI component

You should find that the component is


preconfigured and you can start using it, but you
can still make some changes if you want. In this
case it is useful to look in the Component
Inspector window and have a look at what is the
current configuration and especially you have to
know how many pins are allocated and where are
they placed. Figure 2.47 shows just an example
of my configurations. For more information on
the pin configuration please refer to the
Figure 2.53: TSI in Component Inspector window
development board schematics [K40SCH].
2.7.3 Test the component
To test the component I have implemented a simple algorithm that will increment a variable
and display it on the LCD as long as I keep my finger on that button.
First in the main() function I told the processor to verify the state of the buttons:
int main(void){
for(;;) {
TSI1_TriggerSoftwareScan(TSI1_DeviceData);
}
}

The TSI has its own interrupt function located in the Events.c file that calls itself every
time the value of the pin isnt between the threshold values:

56

Chapter 2: KwikStick-K40
int x;
void TSI1_OnOutOfRange(LDD_TUserData *UserDataPtr, TSI1_LDD_TSI_PinMask mask)

2.7.4 Conclusions
{

It is really easy and convenient to implement some buttons in your project that will allow us
x=(x+1)%10000;
to add a lot of configurability to our project.
lcd_DisplayNumber(SegLCD1_DeviceData, 0, 5, 0, x);

And you have to keep in mind that in the software related world there are a lot of free demos
and examples that can be redden, understood and then adapted to your current project, so you dont
have to invent the wheel all over again.
The result of this function can be seen in the following Figures:
}

57

Chapter 3: Implementation
In this chapter I will explain step by step the functionality of my device, the different
measuring block diagrams and the code that I used to program the KWIKSTIK-K40
microcontroller. If you skipped directly to this chapter and some ware along it you have some
questions on why I implemented a specific process, then please search for that process in Chapter 1:
Theory, or if the code doesnt make sense please look for the explications on that specific
component in Chapter 2: Kwikstick-K40.
I want to avoid inserting any code in this chapter because I want to retain my rights over
what I developed, although the code will be added at the end of the paper in the appendixes.

3.1 STAGE 0: Starting up and calibration


To start up the device you just need to plug it into the USB port of your personal computer,
to power up the microcontroller and to enable the serial communication between the device and the
computer, and plug in the measuring box to power up the analogic part of the system.
The system has several operating stages (each stage will be presented further along in this
chapter), and by default it will start in STAGE 0 in which a message will appear on the LCD that
says ~ DC CAL. This message informs the user that the device needs to be calibrated at first. In
order to make the measurements more accurate we must first know the resistance value of the
cables connecting the measuring device to the device being measured. To do this we must first
connect the cables to the device by inserting one end of the wires into output 1 (which is the DC
output) of the measuring device and then connecting the other loose ends together (in a short-circuit
configuration).

Figure 3.56: Block diagram for measuring the DC connector cables resistance

The method presented in Figure 1.3 for measuring the resistance is nothing more than just a
simple voltage divider made between a known resistance R and the resistance of the wires

Chapter 3: Implementation

Rc . If we supply a constant known voltage with the DAC (therefore knowing the input voltage
V ), if we measure the voltage across the wires using the ADC (therefore knowing the output
voltage V out ) and if we know the value of the fixed resistor R (which must be a very
accurate resistor, with low a tolerance and really small temperature variations), then we can easily
calculate the value of the resistance for the connecting cables Rc :
Rc =

RV out
V V out

After the user makes all of the connections he must then press the upper left button (which is
also marked by a sign on the LCD) that will tell the microcontroller to start calibration. Upon
pressing the button the software will start the measurement of the resistance, but it will also make
an average of the values measured and display the result on the LCD.
After a while, a clock figure will be displayed on the LCD which indicates that the
microcontroller has accumulated enough samples and it is waiting for the user to stop the
measurement and proceed to the next step. The user can leave the microprocessor measuring the
resistance for as long as he wants but after the clock is displayed the changes that can occur arent
noticeable.
Pressing again the same button that we pressed at first will stop the measurement and the
microcontroller will be now calibrated, knowing the resistance of the measurement wires.

3.2 STAGE 1: Measuring the DC Resistance of the driver (Re )


This stage is almost identical to the previous one, the only thing we need to do is to connect
the driver to the measuring cables

Figure 3.57: Block diagram for measuring the electrical resistance of a driver

Just as in the previous case, we have a voltage divider formed between the known resistance
R and the resistance of the wires Rc plus the resistance of the driver Re . Therefore,
we can use exactly the same formula as before to find out the value of Re , but we have to keep
in mind to subtract the resistance of the wires from the actual measurements:

Re =

RV out
R c
V V out

Now back to the microcontroller; a message should have appeared on the screen that tells
the user to connect the measuring wires to the driver ~CON DR.
After you have connected the wires you can press again the same button to tell the device to
start the measurement. The microprocessor already knows the value for the resistance of the wires
therefore it will automatically subtract it from the measured values and display on the LCD the
actual value for the driver resistance.
When you are happy with the value that you obtain you can tell the microprocessor to stop
(by clicking the same button) and go to the next measurement.

3.3 STAGE 2: AC Set-up and calibration


First of all, to measure the resonance frequency, and its respective impedance, you will need
a variable sine wave generator. To make this generator we can use the DAC integrated into the
microprocessor (for more information on how to make this oscillator please see chapter 2.4: 12-bit
Digital-to-Analog Converter).
The problem with generating an analogic signal using a digital source is that our signal is
bound to have spikes (see Figure 3.3). Spikes are some larger than average voltage outputs that the
DAC is generating. Also, at high frequencies, because we chose to generate a sine wave using the
table search method the difference in voltages is for each sample are high, therefore the steps tend
to form.
To get rid of those two effects we will use a first grade Butterworth low pass filter (see
Figure 3.4).

Figure 3.58: DAC generated sine wave with spikes

Figure 3.59: First order Butterworth low pass filter

Chapter 3: Implementation

The LPF is formed of the 1 k resistance and the 100 nF capacitor connected to the ground.
The value for the cutoff frequency is:
1
1
=
=1.591 kHz
2R1C 1 2103107

fc =
LPF

I have designed this LPF to have a cutoff frequency of at least ten times the frequency of the
highest frequency that we will be generating using the DAC, in order for our frequency to be
situated a decade below the cutoff frequency, therefore it wont be attenuated or affected by the
filter.
I have also added an operational amplifier to act as a buffer and repeat the filtered signal to
the next block separating therefore the LPF from the rest of the circuit.
The resulted signal can be seen next in Figure 3.5:

Figure 3.60: Sine wave after the LPF

Figure 3.61: First order Butterworth high pass filter

The resulted signal seen in Figure: 3.5 its a wonderful smooth sinusoidal signal but it still
has one problem. The DAC can generate voltages between 0V and 3V, but to measure the driver the
signal should have complementary values (the signal should be equally distributed on the positive
voltage values as on the negative voltage values). To achieve this we just need to remove the
constant current component that this signal has, which will bring our signal 1.5V down, oscillating
symmetrically along the 0 V line.
A first order Butterworth high pass filter is just what we need (Figure 3.6), which has the
cutoff frequency of:

fc =
HPF

1
1
=
=0.7957 Hz
2R2C 2 22106107

We have to design a filter that has a very low cutoff frequency to allow the minimum
frequency that will be generated by the DAC to pass without any attenuation. The actual cutoff
frequency of 0.8 Hz is more than a decade below our lowest generated frequency of 10 Hz,
therefore the filter will only stop the constant component and will let the pure signal pass.
I have also added an operational amplifier here to act as a buffer and repeat the signal to the
other blocks in the circuit therefore separating the HPF from the rest of the circuit.
The resulted signal can be seen in Figure 3.7; I have also added an FFT plot of the signal in
Figure 3.8 to show that the resulted signal has no other spectral components beside its actual
frequency.

Figure 3.3.62: Sine wave after HPF

Figure 3.3.63: The FFT representation

Only now we are ready to feed this signal with frequencies between 10 Hz and 100Hz into
the voltage divider and see the voltage drop across the driver:

Figure 3.64: Voltage divider

Figure 3.65: Sine wave after the voltage divider

Chapter 3: Implementation

We can clearly see how the amplitude of the resulting wave is changing when the frequency
generated by the DAC changes, and if you insert also the input wave into the oscilloscope you can
see the phase variations as well:

Figure 3.66: Phase Variation

Figure 3.67: Maximum amplitude at

fs

Now is a good time to start measuring the resonant frequency, but there still is one more
problem: the ADC that we are going to use does not operate with negative voltage values therefore
we must find a way to get rid of the negative part of the signal.
The first thing that comes to mind is to use a rectifying diode, but the diode needs at least
0.6V to open and we dont have that much voltage. Another method is to use an investor amplifier
and to add a known voltage to the one that we currently have, therefore rising its minimum value
above the 0V threshold.

Figure 3.68: Inverting summing amplifier

Figure 3.69: Difference between

V out and V

Only about now we can say that the signal across the driver is ready to be measured.

To make the measurements really accurate you will also have to measure with the ADC the
value across the whole voltage divider (V ) generated by the DAC (point 3 in Figure 3.6 and
Figure 3.9). I have observed that every time I change the load of the circuit the top voltage value
generated by the DAC changes as well, and furthermore, you cannot trust the DAC to be perfectly
linear, therefore it is a good practice to always measure the reference voltage when you want to use
it in an equation. I am also measuring the value of the constant voltage used to sum up the signal
(point 1V in Figure 3.13), therefore I dont really care about the actual values of the voltage divider;
they only need to generate a constant voltage some ware around 1V.
For the full view of the schematics please refer to Appendix 3: Board Schematics and
Layout at the end of this paper.
Therefore, to calibrate the device for the AC we will generate a range of frequencies from 10
Hz to 100 Hz and if we know the reference voltage and the summing voltage we can easily
calculate the value for the impedance of the connecting cables using the following formula:
V
V
( ADC 1V ADC 0)
R 3( ADC 0V ADC 2 )

Z c =

You can find references for the values written above in Appendix 3: Board Schematics.
And finally we are ready to implement all that we have said above in the microcontroller. By
now a message that says ~AC CAL should have appeared on the LCD; the microcontroller waits
for the user to connect the cables together and to start the measurement for the impedance of the
cables. The procedure is identical with the one described in the DC section, although, this time two
numbers will appear on the screen; the first one tell us how many measurements have been
concluded (the number of times that the microprocessor generated values form 10 Hz to 100 Hz)
and the second number tells the user the value for the impedance.
You can stop this measurement any time you want and finally get to the part where we will
measure the maximum impedance of the driver.

3.4 STAGE 3: Measuring the Resonance Frequency ( f s ) and the value for the
impedance of the driver at resonant frequency ( Zmax )
This stage is basically the same with the previous stage; the microcontroller first generates a
range of frequencies from 10 Hz to 100 Hz and measures the maximum value for the output
voltage, therefore finding the resonant frequency. I have also added an improvement to this stage
with respect to the previous one; in this stage the microprocessor first generates the frequencies
with a step of 1 Hz and finds a preliminary frequency, and then he makes the range of frequencies
narrower with a bandwidth of 5 Hz around that frequency, after which he begins to generate
frequencies in that range with a step of 0.1Hz, making therefore the measurement more accurate.

Chapter 3: Implementation

On the LCD screen you will see the values for the number of measurements concluded, on
the left, and the value of the resonant frequency, on the right.
Besides measuring the resonant frequency the microcontroller also calculates the value for
the maximum impedance of the driver using this simple formula:
V
V
( ADC 1V ADC0 )Z c
R 3( ADC 0V ADC 2 )

Z m ax =

3.5 STAGE 4: Measuring the frequencies ( f 1f 2) at which the value of the


impedance is the geometric mean between Re and Z max
First we have to calculate to calculate the value for
frequencies f 1 and f 2 .

Zx

at which we will find the

Z x = Z ref R e
Knowing this value I have programed the microprocessor to start generating the resonant
frequency and then gradually lower that frequency until it will find that the value of the impedance
of the driver is equal to the value calculated above, therefore finding f 1 , after which it will again
generate the resonant frequency and this time increase the frequency until it will find that the value
of the impedance of the driver is equal to the value calculated above, therefore finding f 2 .
Knowing these two frequencies allows us to calculate the bandwidth and with the bandwidth
we can compute the quality factors.

3.6 STAGE 5: Measuring the Driver Mechanical Compliance ( C ms )


To find the mechanical compliance we will use the added mass method which requires that
we lower the resonant frequency of the driver by increasing the mass of the cone.
This stage is almost identical to stage 3 where we measure the resonant frequency of the
driver but this time we have to keep in mind that we are not allowed to use any long term averaging
methods of eliminating the error because we are changing the resonant frequency on purpose.
Knowing the value for the new resonant frequency and the value for the mass added we can
therefore compute the value for the mechanical compliance, and with it we know all of the main
parameters needed to design a good enclosure for a driver making therefore a good overall speaker
system.

Conclusions
An automated system that measures drivers is definitely an improvement from the hands
on method that requires the user to perform the measurement manually. Every measurement is
predisposed to be affected by errors but we can implement using a microcontroller some algorithms
that can reduce the error by making at least an average of the values measured or by implementing a
couple of filters along the circuit that will improve the quality of the measurements.
Using the methods described before I have successfully measured and computed the
Thiele/Small parameters of one of my drivers and with that data I was able to design an enclosure
for that specific driver, to form a speaker. The consequences for this action are that now my
neighbor hates me because when I start playing music on my speaker her dishes tend to fall of from
the shelves (I currently live at the dorm so it is easy for that to happen; also you need to have a good
amplifier to supply the speaker with enough power).
There are also some improvements that I want to make by reducing the noise that appears in
the circuit and I also want to eliminate the effect that the impedance of the wires has upon the
measurement for the voltages by using a quadripolar configuration (it uses 4 wires instead of 2 to
measure the driver). Another thing that I wanted to do and I didnt have time was to make the
microcontroller transmit the values that he measured to the PC using a serial configuration and have
the computer calculate all of the data and produce some charts necessary for designing a speaker
system. I can also design a switching mechanism that will automatically transfer the microcontroller
from the DC mode to the AC mode, removing thus the need to switch manually the wires.
Designing a good speaker system is more of an art than science, although having accurate
data can be a big bonus when tackling the challenge of building a high fidelity speaker system.

Conclusions

Bibliography
[K40SCH]

KWIKSTIK-K40-SCH_V5, KWIKSTIK-K40 Schematics (version 5 boards), Rev.


5, Wednesday, April 27, 2011, Source: Freescale Semiconductor, Inc.

[K40RM]

K40P144M100SF2RM, K40 Sub-Family Reference Manual, Rev. 3, 4 Nov 2010,


Source: Freescale Semiconductor, Inc.

[QSGKS]

KWIKSTIKQSG, Quick Start Guide for KwikStik, Rev. 1, Source:


Semiconductor, Inc.

[LDC7]

Vance Dickason, Loudspeaker Design Cookbook, 7th edition, Published by Audio


Amateur Press, Peterborough, New Hampshire, 2006

[SB201]

Ray Alden, Speaker Building 201, First Edition, Audio Amateur Press,
Peterborough, New Hampshire, 2004

[WIKI1]

http://en.wikipedia.org/wiki/Break-in_(mechanical_run-in) visited on 21 June 2013

[WWW2.1]

https://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=KWIKSTIKK40 visited on 16 June 2013

[WWW2.1.1]

http://www.freescale.com/webapp/sps/site/homepage.jsp?code=CW_HOME visited
on 16 June 2013

[WWW2.1.2]

http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=PROCESSOREXPERT visited on 16 June 2013

Freescale

Bibliography

Appendix 1: KwikStik_SegLCD.c
#include "PE_LDD.h"
#include "KwikStik_SegLCD.h"
/* Character table */

Appendix 1: KwikStik_SegLCD.c
static const uint8_t CharTable[][5] =
{
{0x00,0x1C,0x1C,0x1C,0x00}, //0
{0x00,0x1C,0x1C,0x1C,0x00}, //1
{0x00,0x1C,0x1C,0x1C,0x00}, //2
{0x00,0x1C,0x1C,0x1C,0x00}, //3
{0x00,0x1C,0x1C,0x1C,0x00}, //4
{0x00,0x1C,0x1C,0x1C,0x00}, //5
{0x00,0x1C,0x1C,0x1C,0x00}, //6
{0x00,0x1C,0x1C,0x1C,0x00}, //7
{0x00,0x1C,0x1C,0x1C,0x00}, //8
{0x00,0x1C,0x1C,0x1C,0x00}, //9
{0x00,0x1C,0x1C,0x1C,0x00}, //10
{0x00,0x1C,0x1C,0x1C,0x00}, //11
{0x00,0x1C,0x1C,0x1C,0x00}, //12
{0x00,0x1C,0x1C,0x1C,0x00}, //13
{0x00,0x1C,0x1C,0x1C,0x00}, //14
{0x00,0x1C,0x1C,0x1C,0x00}, //15
{0x00,0x1C,0x1C,0x1C,0x00}, //16
{0x00,0x1C,0x1C,0x1C,0x00}, //17
{0x00,0x1C,0x1C,0x1C,0x00}, //18
{0x00,0x1C,0x1C,0x1C,0x00}, //19
{0x00,0x1C,0x1C,0x1C,0x00}, //20
{0x00,0x1C,0x1C,0x1C,0x00}, //21
{0x00,0x1C,0x1C,0x1C,0x00}, //22
{0x00,0x1C,0x1C,0x1C,0x00}, //23
{0x00,0x1C,0x1C,0x1C,0x00}, //24
{0x00,0x1C,0x1C,0x1C,0x00}, //25
{0x00,0x1C,0x1C,0x1C,0x00}, //26
{0x00,0x1C,0x1C,0x1C,0x00}, //27
{0x00,0x1C,0x1C,0x1C,0x00}, //28
{0x00,0x1C,0x1C,0x1C,0x00}, //29
{0x00,0x1C,0x1C,0x1C,0x00}, //30
{0x00,0x1C,0x1C,0x1C,0x00}, //31
{0x00,0x00,0x00,0x00,0x00}, // space
{0x5F,0x00,0x00,0x00,0x00}, //'!'
{0x00,0x06,0x00,0x06,0x00}, //'"'
{0x14,0x3E,0x14,0x3E,0x14}, //'#'
{0x24,0x2A,0x7F,0x2A,0x12}, //'$'
{0x26,0x16,0x08,0x34,0x32}, //'%'
{0x00,0x1C,0x1C,0x1C,0x00}, //38
{0x00,0x00,0x07,0x00,0x00}, //'''
{0x00,0x1C,0x22,0x41,0x00}, //'('
{0x00,0x41,0x22,0x1C,0x00}, //')'
{0x2A,0x1C,0x3E,0x1C,0x2A}, //'*'
{0x00,0x08,0x1C,0x08,0x00}, //'+'
{0x60,0x00,0x00,0x00,0x00}, //','
{0x00,0x08,0x08,0x08,0x00}, //'-'
{0x40,0x00,0x00,0x00,0x00}, //'.'
{0x20,0x10,0x08,0x04,0x02}, //'/'
{0xFF,0x81,0x81,0xFF,0x00}, //'0'
{0x00,0x00,0x00,0xFF,0x00}, //'1'
{0xF9,0x89,0x89,0x8F,0x00}, //'2'
{0x89,0x89,0x89,0xFF,0x00}, //'3'
{0x0F,0x08,0xFC,0x08,0x00}, //'4'
{0x8F,0x89,0x89,0xF9,0x00}, //'5'
{0xFF,0x89,0x89,0xF9,0x00}, //'6'
{0x01,0x01,0x01,0xFF,0x00}, //'7'
{0xFF,0x89,0x89,0xFF,0x00}, //'8'
{0x8F,0x89,0x89,0xFF,0x00}, //'9'
{0x6C,0x00,0x00,0x00,0x00}, //':'
{0x00,0x1C,0x1C,0x1C,0x00}, //59
{0x00,0x08,0x14,0x22,0x00}, //'<'
{0x00,0x14,0x14,0x14,0x00}, //'='

{0x00,0x22,0x14,0x08,0x00},
{0x02,0x01,0x51,0x09,0x06},
{0x3E,0x41,0x5D,0x5D,0x06},
{0x7E,0x11,0x11,0x11,0x7E},
{0x7F,0x49,0x49,0x49,0x36},
{0x3E,0x41,0x41,0x41,0x22},
{0x7F,0x41,0x41,0x41,0x3E},
{0x7F,0x49,0x49,0x49,0x41},
{0x7F,0x09,0x09,0x09,0x01},
{0x3E,0x41,0x49,0x49,0x3A},
{0x7F,0x08,0x08,0x08,0x7F},
{0x00,0x00,0x7F,0x00,0x00},
{0x21,0x41,0x41,0x41,0x3F},
{0x7F,0x08,0x14,0x22,0x41},
{0x7F,0x40,0x40,0x40,0x40},
{0x7F,0x02,0x04,0x02,0x7F},
{0x7F,0x04,0x08,0x10,0x7F},
{0x3E,0x41,0x41,0x41,0x3E},
{0x7F,0x09,0x09,0x09,0x06},
{0x3E,0x41,0x51,0x21,0x5E},
{0x7F,0x09,0x19,0x29,0x46},
{0x26,0x49,0x49,0x49,0x32},
{0x01,0x01,0x7F,0x01,0x01},
{0x3F,0x40,0x40,0x40,0x3F},
{0x1F,0x20,0x40,0x20,0x1F},
{0x3F,0x40,0x30,0x40,0x3F},
{0x63,0x14,0x08,0x14,0x63},
{0x03,0x0C,0x70,0x0C,0x03},
{0x61,0x51,0x49,0x45,0x43},
{0x00,0x7F,0x41,0x41,0x00},
{0x02,0x04,0x08,0x10,0x20},
{0x00,0x41,0x41,0x7F,0x00},
{0x00,0x04,0x02,0x04,0x00},
{0x80,0x80,0x80,0x80,0x80},
{0x00,0x00,0x02,0x04,0x00},
{0x7E,0x11,0x11,0x11,0x7E},
{0x7F,0x49,0x49,0x49,0x36},
{0x3E,0x41,0x41,0x41,0x22},
{0x7F,0x41,0x41,0x41,0x3E},
{0x7F,0x49,0x49,0x49,0x41},
{0x7F,0x09,0x09,0x09,0x01},
{0x3E,0x41,0x49,0x49,0x3A},
{0x7F,0x08,0x08,0x08,0x7F},
{0x00,0x00,0x7F,0x00,0x00},
{0x21,0x41,0x41,0x41,0x3F},
{0x7F,0x08,0x14,0x22,0x41},
{0x7F,0x40,0x40,0x40,0x40},
{0x7F,0x02,0x04,0x02,0x7F},
{0x7F,0x06,0x08,0x30,0x7F},
{0x3E,0x41,0x41,0x41,0x3E},
{0x7F,0x09,0x09,0x09,0x06},
{0x3E,0x41,0x51,0x21,0x5E},
{0x7F,0x09,0x19,0x29,0x46},
{0x26,0x49,0x49,0x49,0x32},
{0x01,0x01,0x7F,0x01,0x01},
{0x3F,0x40,0x40,0x40,0x3F},
{0x1F,0x20,0x40,0x20,0x1F},
{0x3F,0x40,0x30,0x40,0x3F},
{0x63,0x14,0x08,0x14,0x63},
{0x03,0x0C,0x70,0x0C,0x03},
{0x61,0x51,0x49,0x45,0x43},
{0x00,0x08,0x36,0x41,0x00},
{0x00,0x00,0x3E,0x00,0x00},
{0x00,0x41,0x36,0x08,0x00},

//'>'
//'?'
//'@'
//'A'
//'B'
//'C'
//'D'
//'E'
//'F'
//'G'
//'H'
//'I'
//'J'
//'K'
//'L'
//'M'
//'N'
//'O'
//'P'
//'Q'
//'R'
//'S'
//'T'
//'U'
//'V'
//'W'
//'X'
//'Y'
//'Z'
//'['
//'\'
//']'
//'^'
//'_'
//'`'
//'A'
//'B'
//'C'
//'D'
//'E'
//'F'
//'G'
//'H'
//'I'
//'J'
//'K'
//'L'
//'M'
//'N'
//'O'
//'P'
//'Q'
//'R'
//'S'
//'T'
//'U'
//'V'
//'W'
//'X'
//'Y'
//'Z'
//'{'
//'|'
//'}'

{0x03,0x03,0x03,0x03,0x00}, //'~'
{0x00,0x1C,0x1C,0x1C,0x00}, //127

};

/*
** ============================================================================
** Displays a int variable on the LCD at position specified by parameter posX
** ============================================================================
*/
void lcd_DisplayNumber(LDD_TDeviceData *lcdDevicePtr, uint8_t posX, uint8_t
nr_IntFigures, uint8_t nr_FracFigures, int number)
{
uint8_t i, j;
int16_t fPlaneIdx;
fPlaneIdx=posX+5*(nr_IntFigures+nr_FracFigures)+2;
for (j=0;j<+nr_IntFigures+nr_FracFigures;j++){
if (j==nr_FracFigures){
fPlaneIdx=fPlaneIdx-1;
SegLCD1_SetFrontplaneData(lcdDevicePtr,
(LDD_SegLCD_TPinIndex)fPlaneIdx, 0x40);
fPlaneIdx=fPlaneIdx-1;
}
for (i = 5; i >0; i--) {
if ((fPlaneIdx > 0) && (fPlaneIdx < 38)) {
SegLCD1_SetFrontplaneData(lcdDevicePtr,
(LDD_SegLCD_TPinIndex)fPlaneIdx, CharTable[48+number%10][i-1]);
}
fPlaneIdx=fPlaneIdx-1;
}
number=number/10;
}
}
/*
** ============================================================================
** Displays one character on the LCD at position specified by parameter posX
** ============================================================================
*/
void lcd_DisplayChar(LDD_TDeviceData *lcdDevicePtr, int16_t posX, char ch)
{
uint8_t i, chIdx;
int16_t fPlaneIdx;
chIdx = (uint8_t)ch;
/* Update appropriate LCD front planes */
for (i = 0; i < 6; i++) {
fPlaneIdx = posX + i + 1;
if ((fPlaneIdx > 0) && (fPlaneIdx < 38)) {
if (i < 5) {
SegLCD1_SetFrontplaneData(lcdDevicePtr,
(LDD_SegLCD_TPinIndex)fPlaneIdx, CharTable[chIdx][i]);
} else {
SegLCD1_SetFrontplaneData(lcdDevicePtr,
(LDD_SegLCD_TPinIndex)fPlaneIdx, 0);
}
}
}
}
/*
** ============================================================================

Appendix 1: KwikStik_SegLCD.c
** Displays string on segment LCD at position specified by parameter posX
** ============================================================================
*/
void lcd_DisplayString(LDD_TDeviceData *lcdDevicePtr, int16_t posX, char *str)
{
uint8_t i = 0;
int16_t charPosX = posX;

while (str[i] != '\0') {


lcd_DisplayChar(lcdDevicePtr, charPosX, str[i]);
if ((str[i] == ':') || (str[i] == '.')) {
charPosX += 2;
} else if ((str[i] >= '0') && (str[i] <= '9')) {
charPosX += 5;
} else {
charPosX += 6;
}
i++;
}

/*
** ============================================================================
** Enables/Disables the LCD graphical segment specified by parameter lcdSegment
** ============================================================================
*/
void lcd_SetGraphSegment(LDD_TDeviceData *lcdDevicePtr, TSegLcdGraphSegment
lcdSegment, bool state)
{
LDD_SegLCD_TFrontplaneData tmpFpData;
SegLCD1_GetFrontplaneData(lcdDevicePtr, 0, &tmpFpData);
if (state) {
SegLCD1_SetFrontplaneData(lcdDevicePtr, 0, tmpFpData |
(LDD_SegLCD_TFrontplaneData)lcdSegment);
} else {
SegLCD1_SetFrontplaneData(lcdDevicePtr, 0, tmpFpData &
~((LDD_SegLCD_TFrontplaneData)lcdSegment));
}
}

/*
** ============================================================================
** Gets status of the LCD graphical segment specified by parameter lcdSegment
** ============================================================================
*/
bool lcd_GetGraphSegment(LDD_TDeviceData *lcdDevicePtr, TSegLcdGraphSegment
lcdSegment)
{
LDD_SegLCD_TFrontplaneData tmpFpData;

SegLCD1_GetFrontplaneData(lcdDevicePtr, 0, &tmpFpData);
return (tmpFpData & (LDD_SegLCD_TFrontplaneData)lcdSegment);

/*
** ============================================================================
** Clears the segment LCD text area
** ============================================================================
*/

void lcd_ClearScreen(LDD_TDeviceData *lcdDevicePtr)


{
uint8_t i;

for (i = 1; i <= 38; i++) {


SegLCD1_SetFrontplaneData(lcdDevicePtr, i, 0);
}

Appendix 2: Events.c

Appendix 2: Events.c
/*
#########################################################
##########
**
Filename : Events.c
**
Project
: ProcessorExpert
**
Processor : MK40DX256ZVLQ10
**
Component : Events
**
Version
: Driver 01.00
**
Compiler : GNU C Compiler
**
Date/Time : 2013-06-09, 16:16, # CodeGen: 0
**
Abstract :
**
This is user's event module.
**
Put your event handler code here.
**
Settings :
**
Contents :
**
Cpu_OnNMIINT - void Cpu_OnNMIINT(void);
**
**
#########################################################
##########*/
/*!
** @file Events.c
** @version 01.00
** @brief
**
This is user's event module.
**
Put your event handler code here.
*/
/*!
** @addtogroup Events_module Events module documentation
** @{
*/
/* MODULE Events */
#include "Cpu.h"
#include "Events.h"
#include "KwikStik_SegLCD.h"
#ifdef __cplusplus
extern "C" {
#endif
/* User includes (#include below this line is not maintained by Processor Expert) */
#include <math.h>
int ADC_Average(int average, int value, uint8_t nr_measurements){
average=(average*nr_measurements+value)/(nr_measurements+1);
return average;
}
/*
**
=========================================================
==========

**
Event
: Cpu_OnNMIINT0 (module Events)
**
**
Component : Cpu [MK40N512LQ100]
**
Description :
**
This event is called when the Non maskable interrupt had
**
occurred. This event is automatically enabled when the <NMI
**
interrupt> property is set to 'Enabled'.
**
Parameters : None
**
Returns
: Nothing
**
=========================================================
==========
*/
void Cpu_OnNMIINT0(void)
{
/* Write your code here ... */
}
/*
**
=========================================================
==========
**
Event
: TI1_OnInterrupt (module Events)
**
**
Component : TI1 [TimerInt_LDD]
*/
/*!
**
@brief
**
Called if periodic event occur. Component and OnInterrupt
**
event must be enabled. See <SetEventMask> and <GetEventMask>
**
methods. This event is available only if a <Interrupt
**
service/event> is enabled.
**
@param
**
UserDataPtr
- Pointer to the user or
**
RTOS specific data. The pointer passed as
**
the parameter of Init method.
*/
/*
=========================================================
==========*/
#define SINUS_LENGTH 1000
// the number of samples
static const LDD_DAC_TData SinusOutputData[SINUS_LENGTH] = {
2048, 2060, 2073, 2086, 2099, 2112, 2125, 2138, 2150, 2163, 2176, 2189, 2202,
2215, 2228, 2240, 2253, 2266, 2279, 2292, 2304, 2317, 2330, 2343, 2355, 2368, 2381, 2394,
2406, 2419, 2432, 2444, 2457, 2469, 2482, 2495, 2507, 2520, 2532, 2545, 2557, 2570, 2582,
2595, 2607, 2619, 2632, 2644, 2656, 2669, 2681, 2693, 2705, 2717, 2730, 2742, 2754, 2766,
2778, 2790, 2802, 2814, 2826, 2838, 2850, 2861, 2873, 2885, 2897, 2908, 2920, 2932, 2943,
2955, 2966, 2978, 2989, 3001, 3012, 3023, 3035, 3046, 3057, 3068, 3080, 3091, 3102, 3113,
3124, 3135, 3146, 3156, 3167, 3178, 3189, 3199, 3210, 3221, 3231, 3242, 3252, 3262, 3273,
3283, 3293, 3303, 3314, 3324, 3334, 3344, 3354, 3364, 3373, 3383, 3393, 3403, 3412, 3422,
3431, 3441, 3450, 3460, 3469, 3478, 3487, 3496, 3506, 3515, 3523, 3532, 3541, 3550, 3559,
3567, 3576, 3585, 3593, 3601, 3610, 3618, 3626, 3634, 3643, 3651, 3659, 3666, 3674, 3682,
3690, 3698, 3705, 3713, 3720, 3727, 3735, 3742, 3749, 3756, 3763, 3770, 3777, 3784, 3791,
3798, 3804, 3811, 3817, 3824, 3830, 3837, 3843, 3849, 3855, 3861, 3867, 3873, 3879, 3884,
3890, 3896, 3901, 3907, 3912, 3917, 3922, 3928, 3933, 3938, 3943, 3947, 3952, 3957, 3961,
3966, 3970, 3975, 3979, 3983, 3988, 3992, 3996, 4000, 4003, 4007, 4011, 4014, 4018, 4021,
4025, 4028, 4031, 4034, 4038, 4041, 4043, 4046, 4049, 4052, 4054, 4057, 4059, 4062, 4064,
4066, 4068, 4070, 4072, 4074, 4076, 4078, 4079, 4081, 4082, 4084, 4085, 4086, 4087, 4088,
4089, 4090, 4091, 4092, 4093, 4093, 4094, 4094, 4094, 4095, 4095, 4095, 4095, 4095, 4095,
4095, 4094, 4094, 4093, 4093, 4092, 4092, 4091, 4090, 4089, 4088, 4087, 4086, 4084, 4083,
4082, 4080, 4078, 4077, 4075, 4073, 4071, 4069, 4067, 4065, 4063, 4060, 4058, 4056, 4053,
4050, 4048, 4045, 4042, 4039, 4036, 4033, 4030, 4026, 4023, 4020, 4016, 4013, 4009, 4005,

Appendix 2: Events.c
4001, 3998, 3994, 3990, 3985, 3981, 3977, 3973, 3968, 3964, 3959, 3954, 3950, 3945, 3940,
3935, 3930, 3925, 3920, 3915, 3909, 3904, 3898, 3893, 3887, 3882, 3876, 3870, 3864, 3858,
3852, 3846, 3840, 3833, 3827, 3821, 3814, 3808, 3801, 3794, 3788, 3781, 3774, 3767, 3760,
3753, 3746, 3738, 3731, 3724, 3716, 3709, 3701, 3694, 3686, 3678, 3670, 3663, 3655, 3647,
3639, 3630, 3622, 3614, 3606, 3597, 3589, 3580, 3572, 3563, 3554, 3546, 3537, 3528, 3519,
3510, 3501, 3492, 3483, 3474, 3464, 3455, 3446, 3436, 3427, 3417, 3407, 3398, 3388, 3378,
3369, 3359, 3349, 3339, 3329, 3319, 3309, 3298, 3288, 3278, 3268, 3257, 3247, 3236, 3226,
3215, 3205, 3194, 3183, 3173, 3162, 3151, 3140, 3129, 3118, 3107, 3096, 3085, 3074, 3063,
3052, 3040, 3029, 3018, 3006, 2995, 2984, 2972, 2961, 2949, 2938, 2926, 2914, 2903, 2891,
2879, 2867, 2856, 2844, 2832, 2820, 2808, 2796, 2784, 2772, 2760, 2748, 2736, 2724, 2711,
2699, 2687, 2675, 2662, 2650, 2638, 2625, 2613, 2601, 2588, 2576, 2563, 2551, 2538, 2526,
2513, 2501, 2488, 2476, 2463, 2450, 2438, 2425, 2413, 2400, 2387, 2374, 2362, 2349, 2336,
2324, 2311, 2298, 2285, 2272, 2260, 2247, 2234, 2221, 2208, 2195, 2183, 2170, 2157, 2144,
2131, 2118, 2105, 2093, 2080, 2067, 2054, 2041, 2028, 2015, 2002, 1990, 1977, 1964, 1951,
1938, 1925, 1912, 1900, 1887, 1874, 1861, 1848, 1835, 1823, 1810, 1797, 1784, 1771, 1759,
1746, 1733, 1721, 1708, 1695, 1682, 1670, 1657, 1645, 1632, 1619, 1607, 1594, 1582, 1569,
1557, 1544, 1532, 1519, 1507, 1494, 1482, 1470, 1457, 1445, 1433, 1420, 1408, 1396, 1384,
1371, 1359, 1347, 1335, 1323, 1311, 1299, 1287, 1275, 1263, 1251, 1239, 1228, 1216, 1204,
1192, 1181, 1169, 1157, 1146, 1134, 1123, 1111, 1100, 1089, 1077, 1066, 1055, 1043, 1032,
1021, 1010, 999, 988, 977, 966, 955, 944, 933, 922, 912, 901, 890, 880, 869, 859, 848, 838,
827, 817, 807, 797, 786, 776, 766, 756, 746, 736, 726, 717, 707, 697, 688, 678, 668, 659, 649,
640, 631, 621, 612, 603, 594, 585, 576, 567, 558, 549, 541, 532, 523, 515, 506, 498, 489, 481,
473, 465, 456, 448, 440, 432, 425, 417, 409, 401, 394, 386, 379, 371, 364, 357, 349, 342, 335,
328, 321, 314, 307, 301, 294, 287, 281, 274, 268, 262, 255, 249, 243, 237, 231, 225, 219, 213,
208, 202, 197, 191, 186, 180, 175, 170, 165, 160, 155, 150, 145, 141, 136, 131, 127, 122, 118,
114, 110, 105, 101, 97, 94, 90, 86, 82, 79, 75, 72, 69, 65, 62, 59, 56, 53, 50, 47, 45, 42, 39, 37,
35, 32, 30, 28, 26, 24, 22, 20, 18, 17, 15, 13, 12, 11, 9, 8, 7, 6, 5, 4, 3, 3, 2, 2, 1, 1, 0, 0, 0, 0, 0,
0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 16, 17, 19, 21, 23, 25, 27, 29, 31, 33, 36, 38,
41, 43, 46, 49, 52, 54, 57, 61, 64, 67, 70, 74, 77, 81, 84, 88, 92, 95, 99, 103, 107, 112, 116,
120, 125, 129, 134, 138, 143, 148, 152, 157, 162, 167, 173, 178, 183, 188, 194, 199, 205, 211,
216, 222, 228, 234, 240, 246, 252, 258, 265, 271, 278, 284, 291, 297, 304, 311, 318, 325, 332,
339, 346, 353, 360, 368, 375, 382, 390, 397, 405, 413, 421, 429, 436, 444, 452, 461, 469, 477,
485, 494, 502, 510, 519, 528, 536, 545, 554, 563, 572, 580, 589, 599, 608, 617, 626, 635, 645,
654, 664, 673, 683, 692, 702, 712, 722, 731, 741, 751, 761, 771, 781, 792, 802, 812, 822, 833,
843, 853, 864, 874, 885, 896, 906, 917, 928, 939, 949, 960, 971, 982, 993, 1004, 1015, 1027,
1038, 1049, 1060, 1072, 1083, 1094, 1106, 1117, 1129, 1140, 1152, 1163, 1175, 1187, 1198,
1210, 1222, 1234, 1245, 1257, 1269, 1281, 1293, 1305, 1317, 1329, 1341, 1353, 1365, 1378,
1390, 1402, 1414, 1426, 1439, 1451, 1463, 1476, 1488, 1500, 1513, 1525, 1538, 1550, 1563,
1575, 1588, 1600, 1613, 1626, 1638, 1651, 1663, 1676, 1689, 1701, 1714, 1727, 1740, 1752,
1765, 1778, 1791, 1803, 1816, 1829, 1842, 1855, 1867, 1880, 1893, 1906, 1919, 1932, 1945,
1957, 1970, 1983, 1996, 2009, 2022, 2035, 2047
};
unsigned int i=0;
// a counter
//DAC Variables
unsigned int j=0,k=0;;
int Freq=100;
//the output frequency (in
Hz*10)
int Freq_Max=1000;
//the maximum frequency (in Hz*10)
int Freq_Step=10;
//the step frequency (in Hz*10)
int Freq_Periods=3;
//the number of periods
int Freq_Resonant=0;
int Counter_Freq=0;
//a counter for the unitary and fractional part
of the frequency
//int value=0;
// a variable used to store the value of
the output - in the 'Table search and linear interpolation method'
//float f;
// the value for the sin() function
- used in 'The direct implementation'
unsigned int Nr_Measurements;
unsigned int MAX_Nr_Measurements=21745;

//ADC Variables
static uint16_t ADC_Value,MAX_ADC_Value=0, ADC_Freq_Buffer;
static uint16_t ADC_Buffer[10];
static uint32_t ADC_Sum;
static uint16_t ADC_AVGValue[3];
unsigned int ADC_i;
// DC and AC Resistance Variables
//static uint16_t INPUT_Voltage = 21250;
static uint32_t CONNECTORS_DC_Resistance;
// ohm*10
static uint16_t CONNECTORS_AC_Resistance = 100;
// ohm*10
static uint16_t DIVIDER_DC_Resistance= 10080; // ohm*100 (100.8ohm)
static uint16_t DIVIDER_AC_Resistance= 1008; // ohm*10 (100.8ohm)
static uint32_t DRIVER_Resistance;
// ohm*100
static uint32_t DRIVER_MAX_Impedance;
// ohm*10 Impedance at Resonant
Frequency Fs
static uint32_t DRIVER_Impedance_Buffer;
// ohm*10
static uint32_t DRIVER_X_Impedance;
// ohm*10
static uint32_t DRIVER_REF_Impedance;
// ohm*10 Reference Impedance
static int
DRIVER_REF_Sqrt_Impedance;
// ohm*10 Reference Impedance
// STATE 3 Variables
int Frequency_Lateral[2];
static uint8_t FLAG=0; // 0 - Goes to the right, 1 - Goes to the left
int Z_x=65;
//STATE of the machine
static int STATE=2; // 0 - Waits for the user to press a button
// 1 - Measures the driver electrical resistance (in DC)
// 2 - Generates sinusoidal signals between 10Hz and 100Hz
with a step of 1Hz and 3 periods per each frequency, measuring the frequency response and
finds an initial resonant frequency Fs
// after which - Generates sinusoidal signals between Fs-5Hz
and Fs+5Hz with a step of 0.1Hz and 10 periods per each frequency, measuring the frequency
response and finds the actual resonant frequency Fs
// 3 - Generates sinusoidal signals with frequencies less and
greater then Fs and fings F1 and F2
// MEASURE
static int MEASURE = 0;
// buttons TSI
int Button;
int Enable_Buttons=0;

// dose not measure


// measures

int ADC_Nr = 0;
void TI1_OnInterrupt(LDD_TUserData *UserDataPtr)
{
if (STATE==0){
if (MEASURE==0){
if (Enable_Buttons==0){
lcd_ClearScreen(SegLCD1_DeviceData);
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, "~");

Appendix 2: Events.c
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)5, "DC");
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)20, "CALIBRATION");
Enable_Buttons=1;
}else{
TSI1_TriggerSoftwareScan(TSI1_DeviceData); // enable the touch
buttons
}
}
if (MEASURE==1){
DA1_SetValue(DA1_DeviceData, 692);
AD1_SelectSampleGroup(AD1_DeviceData, ADC_Nr);
AD1_StartSingleMeasurement(AD1_DeviceData); //Starts the ADC
measurement
if(Nr_Measurements == MAX_Nr_Measurements){
SegLCD1_SetFrontplaneData(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, 0x08);
Enable_Buttons=1;
TSI1_TriggerSoftwareScan(TSI1_DeviceData); // enable the touch
buttons
}
}
}
if (STATE == 1){
if (MEASURE==0){
if (Nr_Measurements < MAX_Nr_Measurements){
Nr_Measurements = Nr_Measurements+1;
}else{
if (Enable_Buttons==0){
lcd_ClearScreen(SegLCD1_DeviceData);
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, "~");
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)5, "Con");
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)25, "Dr");
Enable_Buttons=1;
}else{
TSI1_TriggerSoftwareScan(TSI1_DeviceData); // enable the touch
buttons
}
}
}
if (MEASURE==1){
DA1_SetValue(DA1_DeviceData, 692);
AD1_SelectSampleGroup(AD1_DeviceData, ADC_Nr);
AD1_StartSingleMeasurement(AD1_DeviceData); //Starts the ADC
measurement
if(Nr_Measurements == MAX_Nr_Measurements){
SegLCD1_SetFrontplaneData(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, 0x08);
Enable_Buttons=1;
TSI1_TriggerSoftwareScan(TSI1_DeviceData); // enable the touch
buttons
}
}
}

if (STATE==2){

/* 2.2.3
Generating a square waveform using interrupts */
//
DA1_SetValue(DA1_DeviceData, i*4090);
/* Set converter output */
//
i=(i+1)%2;
// a flag which
tells us if the function is low or high
/* 2.3.3
Generating a sinusoidal waveform: Direct implementation */
//
f=(sin(2*3.14*i/SINUS_LENGTH)+1)*2047;
//calculating the value
//
DA1_SetValue(DA1_DeviceData, f);
/* Set converter output */
//
i=(i+1)%SINUS_LENGTH;
//incrementing the
counter
/* 2.3.4
Generating a sinusoidal waveform: Table search method */
if (MEASURE==0){
if (Nr_Measurements < MAX_Nr_Measurements){
Nr_Measurements = Nr_Measurements+1;
}else{
if (Enable_Buttons==0){
lcd_ClearScreen(SegLCD1_DeviceData);
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, "~");
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)5, "AC");
lcd_DisplayString(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)20, "CALIBRAIOn");
Enable_Buttons=1;
}else{
TSI1_TriggerSoftwareScan(TSI1_DeviceData); // enable the touch
buttons
}
}
}
if (MEASURE==1){
k=(i+Counter_Freq/100)/SINUS_LENGTH;
if (k==1){
if(j==0){
Freq=(Freq+Freq_Step)%Freq_Max;
j=j+1;}
else{ j=(j+1)%Freq_Periods;}}
if (Freq==0){
Freq_Resonant=(Freq_Resonant*Nr_Measurements+ADC_Freq_Buffer)/
(Nr_Measurements+1);
//
Freq_Resonant=ADC_Freq_Buffer;
Freq=Freq_Resonant-50;
Freq_Max=Freq_Resonant+50;
Freq_Periods=3;
//
Freq=300;
//
Freq_Max=310;
//
Freq_Periods=3;
Freq_Step=1;

Appendix 2: Events.c
DRIVER_MAX_Impedance=(DRIVER_MAX_Impedance*Nr_Measurements+DRIVER_Impedance_B
uffer)/(Nr_Measurements+1);
DRIVER_REF_Impedance=DRIVER_MAX_Impedance*10/DRIVER_Resistance;
// *10 to get the decimal value
DRIVER_REF_Sqrt_Impedance=sqrt(DRIVER_REF_Impedance*10);
DRIVER_X_Impedance=DRIVER_REF_Sqrt_Impedance*DRIVER_Resistance/10;
// /10 to scale down from 2 decimals
MAX_ADC_Value=0;
Nr_Measurements++;
}

DA1_SetValue(DA1_DeviceData, SinusOutputData[i]/6);
/*
Set converter output */
AD1_SelectSampleGroup(AD1_DeviceData, ADC_Nr);
AD1_StartSingleMeasurement(AD1_DeviceData);
//Starts the ADC measurement
i=(i+Counter_Freq/100)%SINUS_LENGTH;
//calculates the position of the next sample
Counter_Freq=Counter_Freq%100+Freq;
}
//
/* 2.3.5
Generating a sinusoidal waveform: Table search and linear interpolation
method */
//
DA1_SetValue(DA1_DeviceData,value);
/* Set
converter output */
//
//
if(SinusOutputData[i+1]>SinusOutputData[i]){
//the
microprocessor dosen't operate well with negative values therefore we have to see if the sinus
wave is rising or falling
//
value=SinusOutputData[i+1]-SinusOutputData[i];
//
}else{
//
value= SinusOutputData[i]-SinusOutputData[i+1];
//
}
//
//
i=(i+Counter_Freq/100)%(SINUS_LENGTH-1);
//calculates the position of the next sample
//
Counter_Freq=Counter_Freq%100+Freq;
//
value=SinusOutputData[i]+value*(Counter_Freq%100)/100;
//computes the
value of the sample that we want to set at the output
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//

if (STATE==3){
Freq_Step=1;
Freq_Max =1000;
Z_x=72;
k=(i+Counter_Freq/100)/SINUS_LENGTH;
if (k==1){
if(j==0){
if(DRIVER_Impedance_Buffer > Z_x){
if (FLAG==0){
Freq=(Freq-Freq_Step)%Freq_Max;
}else{
Freq=(Freq+Freq_Step)%Freq_Max;
}
}else{

//
Frequency_Lateral[FLAG]=Freq;
//
FLAG=(FLAG+1)%2;
//
Freq=312;
//
}
//
//
//
j=j+1;
//
MAX_ADC_Value=0;
//
}
//
else{ j=(j+1)%Freq_Periods;}}
//
//
//
/* 2.3.4
Generating a sinusoidal waveform: Table search method */
//
DA1_SetValue(DA1_DeviceData, SinusOutputData[i]);
/* Set converter
output */
//
i=(i+Counter_Freq/100)%SINUS_LENGTH;
//calculates the position of the next sample
//
Counter_Freq=Counter_Freq%100+Freq;
//
//
}
}
/*
**
=========================================================
==========
**
Event
: AD1_OnMeasurementComplete (module Events)
**
**
Component : AD1 [ADC_LDD]
*/
/*!
**
@brief
**
Called after measurement is done, <Interrupt service/event>
**
is enabled, OnMeasurementComplete event is enabled and ADC
**
device is enabled. See <SetEventMask()> method or <Event
**
mask> property group to enable this event and <Enable>
**
method or <Enabled in init. code> property to enable ADC
**
device.
**
@param
**
UserDataPtr
- Pointer to the user or
**
RTOS specific data. The pointer is passed
**
as the parameter of Init method.
*/
/*
=========================================================
==========*/
void AD1_OnMeasurementComplete(LDD_TUserData *UserDataPtr)
{
AD1_GetMeasuredValues(AD1_DeviceData,&ADC_Value);
if (STATE==1){
ADC_AVGValue[ADC_Nr]=ADC_Average(ADC_AVGValue[ADC_Nr], ADC_Value,
Nr_Measurements);
ADC_Nr=(ADC_Nr+1)%2;
if (Nr_Measurements < MAX_Nr_Measurements){
Nr_Measurements = Nr_Measurements+1;
}
}
if (STATE==2){
ADC_AVGValue[ADC_Nr]=ADC_Value;

Appendix 2: Events.c
if (ADC_Nr==2){
//if
it had measured all the pins
ADC_Sum=ADC_Sum+ADC_AVGValue[0]-ADC_Buffer[0];
for (ADC_i=0;ADC_i < 9; ADC_i++){
ADC_Buffer[ADC_i]=ADC_Buffer[ADC_i+1];
}
ADC_Buffer[9]=ADC_AVGValue[0];
if (ADC_Buffer[0]!=0 && ADC_Sum/10 > MAX_ADC_Value){
MAX_ADC_Value=ADC_Sum/10;
ADC_Freq_Buffer=Freq;
DRIVER_Impedance_Buffer=(MAX_ADC_Value33524)*DIVIDER_AC_Resistance/(64000-MAX_ADC_Value)-CONNECTORS_AC_Resistance; //
33424 is 1.53 V (the mean)
}
}
ADC_Nr=(ADC_Nr+1)%3;
}

}
/*
**
=========================================================
==========
**
Event
: SegLCD1_OnFrameFrequency (module Events)
**
**
Component : SegLCD1 [SegLCD_LDD]
*/
/*!
**
@brief
**
This event is called after the start of the LCD module frame.
**
This event is enabled only if interrupts/events are enabled.
**
@param
**
UserDataPtr
- Device data structure
**
pointer.
*/
/*
=========================================================
==========*/
void SegLCD1_OnFrameFrequency(LDD_TUserData *UserDataPtr)
{
if (STATE==0){
if (MEASURE==1){
CONNECTORS_DC_Resistance=ADC_AVGValue[1]*DIVIDER_DC_Resistance/
(ADC_AVGValue[0]-ADC_AVGValue[1]);
lcd_DisplayNumber(SegLCD1_DeviceData, 15, 2, 2,
CONNECTORS_DC_Resistance);
}
}
if (STATE==1){
if (MEASURE==1){
DRIVER_Resistance=ADC_AVGValue[1]*DIVIDER_DC_Resistance/
(ADC_AVGValue[0]-ADC_AVGValue[1])-CONNECTORS_DC_Resistance;
lcd_DisplayNumber(SegLCD1_DeviceData, 15, 2, 2, DRIVER_Resistance);

}
}
if (STATE==2){
if (MEASURE==1){
lcd_ClearScreen(SegLCD1_DeviceData);

lcd_DisplayNumber(SegLCD1_DeviceData, 10, 5, 0, Freq_Resonant);


lcd_DisplayNumber(SegLCD1_DeviceData, 0, 2, 0, Nr_Measurements);
}
}
}
/*
**
=========================================================
==========
**
Event
: TSI1_OnOutOfRange (module Events)
**
**
Component : TSI1 [TSI_LDD]
*/
/*!
**
@brief
**
This event is enabled only if Out of Range interrupt is
**
selected
**
@param
**
UserDataPtr
- Pointer to the user or
**
RTOS specific data. This pointer is passed
**
as the parameter of Init method.
**
@param
**
mask
- Pin indexes which were detected out of
**
range.
*/
/*
=========================================================
==========*/
void TSI1_OnOutOfRange(LDD_TUserData *UserDataPtr, TSI1_LDD_TSI_PinMask mask)
{
ADC_Nr=0;
Nr_Measurements=0;
if (Enable_Buttons==1){
Enable_Buttons=0;

if (STATE==2 && MEASURE==0){


MEASURE=1;
STATE=2;
Nr_Measurements=0;
SegLCD1_SetFrontplaneData(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, 0x00);
}
if (STATE==1 && MEASURE==1){
MEASURE=0;

Appendix 2: Events.c
STATE=2;
ADC_AVGValue[0]=0;
ADC_AVGValue[1]=0;
SegLCD1_SetFrontplaneData(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, 0x00);
}
if (STATE==1 && MEASURE==0){
lcd_ClearScreen(SegLCD1_DeviceData);
MEASURE=1;
lcd_DisplayString(SegLCD1_DeviceData, 0, "Re");
lcd_DisplayString(SegLCD1_DeviceData, 12, ":");
}
if (STATE==0 && MEASURE==1){
MEASURE=0;
STATE=1;
ADC_AVGValue[0]=0;
ADC_AVGValue[1]=0;
SegLCD1_SetFrontplaneData(SegLCD1_DeviceData,
(LDD_SegLCD_TPinIndex)0, 0x00);
}
if (STATE==0 && MEASURE==0){
lcd_ClearScreen(SegLCD1_DeviceData);
MEASURE=1;
lcd_DisplayString(SegLCD1_DeviceData, 0, "Rc");
lcd_DisplayString(SegLCD1_DeviceData, 12, ":");
}
}
}
/* END Events */
#ifdef __cplusplus
} /* extern "C" */
#endif
/*!
** @}
*/
/*
**
#########################################################
##########
**
**
This file was created by Processor Expert 10.2 [05.06]
**
for the Freescale Kinetis series of microcontrollers.
**
**
#########################################################
##########
*/

Appendix 3: Board Schematics and Layout

Appendix 3: Board Schematics and Layout

You might also like