Master Thesis Patrick His Ni Brat A As
Master Thesis Patrick His Ni Brat A As
Master Thesis Patrick His Ni Brat A As
Embedded
Microcontroller
System for
Bioimpedance
Measurements
Master thesis
Patrick Hisni
Brataas
June 2, 2014
Contents
Abstract vii
Acknowledgments ix
1 Introduction 1
1.1 Background and Motivation . . . . . . . . . . . . . . . . . . . 1
1.2 Goal of this Thesis . . . . . . . . . . . . . . . . . . . . . . . . 1
2 Bioimpedance Theory 3
2.1 Basics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.1 Resistor . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.2 Capacitor . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.3 Bode plot . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.4 Wessel plot . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Immittance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2.1 Impedance . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2.2 Admittance . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2.3 Resistivity, conductivity, impedivity, admittivity, im-
mittivity and permittivity . . . . . . . . . . . . . . . . 6
2.3 Bioimpedance . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3.1 Electrical properties of tissue . . . . . . . . . . . . . . 6
2.3.2 Polarization . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3.3 Dispersion . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3.4 Equivalent electrical circuit for biological tissue and
the Cole model . . . . . . . . . . . . . . . . . . . . . . 10
2.4 Electrodes and Measurement . . . . . . . . . . . . . . . . . . . 13
2.4.1 Monopolar measurements . . . . . . . . . . . . . . . . 13
2.4.2 Two electrode setup . . . . . . . . . . . . . . . . . . . . 14
2.4.3 Four electrode setup . . . . . . . . . . . . . . . . . . . 14
2.4.4 Transfer immittance . . . . . . . . . . . . . . . . . . . 17
2.4.5 Three electrode setup . . . . . . . . . . . . . . . . . . . 17
i
ii CONTENTS
3 Electronics Theory 23
3.1 Sine wave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.2 Direct Digital Synthesizer . . . . . . . . . . . . . . . . . . . . 23
3.3 Current Measurements . . . . . . . . . . . . . . . . . . . . . . 25
3.3.1 Shunt Ammeter . . . . . . . . . . . . . . . . . . . . . . 27
3.3.2 Transimpedance Amplifier . . . . . . . . . . . . . . . . 27
Feedback compensation . . . . . . . . . . . . . . . . . . 28
3.4 Virtual Ground . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.5 Decoupling capacitors . . . . . . . . . . . . . . . . . . . . . . . 30
3.6 Comparator . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.7 AC coupling . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.8 Analog-to-Digital Converter . . . . . . . . . . . . . . . . . . . 33
3.9 Operational Amplifier . . . . . . . . . . . . . . . . . . . . . . 33
3.9.1 Voltage Follower . . . . . . . . . . . . . . . . . . . . . 34
3.9.2 Negative Feedback Amplification . . . . . . . . . . . . 35
Non-inverting Amplifier . . . . . . . . . . . . . . . . . 35
Inverting Amplifier . . . . . . . . . . . . . . . . . . . . 36
Advantages and Disadvantages . . . . . . . . . . . . . 36
3.10 Single Supply . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.11 Noise in Analog circuits . . . . . . . . . . . . . . . . . . . . . 38
3.11.1 Thermal Noise . . . . . . . . . . . . . . . . . . . . . . 38
3.11.2 Shot Noise (Schottky Noise) . . . . . . . . . . . . . . . 38
3.11.3 Flicker Noise . . . . . . . . . . . . . . . . . . . . . . . 39
3.12 Transimpedance Amplifier Noise Analysis . . . . . . . . . . . . 40
4 Digital Theory 43
4.1 ARM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.1 What is ARM . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.2 What is ARM Coretex-M3 . . . . . . . . . . . . . . . . 43
4.1.3 Nested Vector Interrupt Controller (NVIC) . . . . . . . 43
4.1.4 Cortex Microcontroller Software Interface Standard (CM-
SIS) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.1.5 CMSIS Digital Signal Processing Library . . . . . . . . 44
4.2 STM32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.2.1 Clock tree . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.2.2 System clock . . . . . . . . . . . . . . . . . . . . . . . 46
4.2.3 Watchdog . . . . . . . . . . . . . . . . . . . . . . . . . 46
CONTENTS iii
References 121
E Schematics 331
Abstract
vii
viii ABSTRACT
Acknowledgments
This thesis is the fullfilling of the Master of Science in Electronics and Com-
puter Technology at the Department of Physics, University of Oslo. This
work was carried out part-time from August 2012 to June 2014.
I would like to thank my supervisors Professor Ørjan G. Martinsen and
Researcher Håvard Kalvøy for letting me undertake this project and for
all feedback and help during the work on this thesis. A thanks to Oslo
Bioimpedance Group for the trip to the International Conference on Elec-
trial Bio-Impedance in Germany and the two day seminar at Sundvollen.
Thanks to the guys at the Electronics lab for all help.
It has been great being able to bounce ideas and ask questions to you
Bent and thanks for the LaTeX template and valuable feedback.
At last I would like to thank my fantastic girlfriend Kristine for her infinite
patience and understanding and my friends and family for their support
during this time.
ix
x ACKNOWLEDGMENTS
Nomenclature
AF E Analog Front-End
EA Electrode Area
xi
xii ACKNOWLEDGMENTS
F IF O First-In First-Out
IC Integrated Circuit
IW DG Independent Watchdog
QM L Qt Meta/Modeling Language
RC Resistor Capacitor
SN R Signal-to-Noise Ratio
T IA Transimpedance Amplifier
xv
xvi LIST OF FIGURES
3.1 Sine wave with the most commen sine wave characteristics. . . 24
3.2 The building blocks of a DDS . . . . . . . . . . . . . . . . . . 26
3.3 Basic shunt ammeter . . . . . . . . . . . . . . . . . . . . . . . 27
3.4 Basic transimpedance amplifier. . . . . . . . . . . . . . . . . . 28
3.5 Transimpedance amplifier with compensation. . . . . . . . . . 29
3.6 A voltage divider. . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.7 A comparator. . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.8 A non-inverting AC coupling circuit. . . . . . . . . . . . . . . 33
3.9 An operational amplifier. . . . . . . . . . . . . . . . . . . . . . 34
3.10 A voltage follower. . . . . . . . . . . . . . . . . . . . . . . . . 34
3.11 A non-inverting operational amplifier. . . . . . . . . . . . . . . 35
3.12 An inverting operational amplifier. . . . . . . . . . . . . . . . 36
3.13 A biased AC coupled inverting operational amplifier. . . . . . 37
7.1 A screenshot from the logic analyzer software from Saleae. . . 109
7.2 Showing the ADC values before and after calibration. . . . . . 110
7.3 Showing the resistance offset before and after system voltage
calibration. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
7.4 Before and after the phase calibration on a resistor. . . . . . . 115
7.5 The calculated and measured phase as function of resistance
in a RC series circuit. . . . . . . . . . . . . . . . . . . . . . . . 116
7.6 The values measured on egg white and egg yolk. . . . . . . . . 118
xix
Chapter 1
Introduction
2. Develop software that wirelessly receive the impedance data and visu-
alizes it on a display.
1
2 CHAPTER 1. INTRODUCTION
Bioimpedance Theory
2.1 Basics
2.1.1 Resistor
A resistor is simply a passive electrical component that reduces current flow
and indirectly the voltage. This is because the current and voltage are di-
rectly proportional in a resistor. This leads to Ohm’s law:
V = RI (2.1)
where V is the voltage across the resistor, R is the resistance of the resistor
and I the current flowing through.
2.1.2 Capacitor
A capacitor is comprised of two conductors that is separated by air or some
kind of material. The material separating the two conductors is called a di-
electric. When a voltage is applied over a capacitor, it will charge and the
relationship between how much it charges and the voltage is called capaci-
tance. Its equation is usually written:
Q
C= (2.2)
V
where C is the capacitance, Q is the charge and V the voltage. The type
of dielectric and the geometry of the capacitor determines the capacitance.
3
4 CHAPTER 2. BIOIMPEDANCE THEORY
2.2 Immittance
Immittance is a more general term meaning impedance or admittance for
those cases where differentiating between the two has no value. Since Immit-
tance is just a term, it does not have its own unit.
2.2.1 Impedance
Before we can address bioimpedance, we must know what impedance is. In
this thesis it is implied that when using the word impedance it is referring to
electrical impedance. Impedance is measured in Ohms [Ω] and is a measure
of how well, for example, an electrical circuit oppose the resulting alternating
current when an alternating voltage is applied to it. This ratio can be written
as:
v
Z= (2.3)
i
where v is the applied voltage and i is the current. Written in the Carte-
sian form, impedance consists of two parts:
Z = R + jX (2.4)
where the real part R is the resistance and the imaginary part X is the
reactance. If written in complex exponential form:
Z = |Z|ejθ (2.5)
|Z| is the magnitude and θ the phase difference between the voltage and
current in the time domain. These two forms express the same and the
calculation from one form to the other is trivial:
2.2. IMMITTANCE 5
√
|Z| = R2 + X 2 (2.6)
X
θ = arctan( ) (2.7)
R
R = |Z|cos(θ) (2.8)
X = |Z|sin(θ) (2.9)
2.2.2 Admittance
Admittance is measured in Siemens [S] and is the inverse of impedance. This
is a measure of how well, for example, an electrical circuit conducts current.
The admittance can be measured when applying an alternating current and
measuring the resulting alternating voltage. This results in the ratio:
1 i
Y = = (2.10)
Z v
where i is the applied current and v the voltage. Written in Cartesian
form we get:
Y = G + jB (2.11)
where the real part G is the conductance and the imaginary part B is
the susceptance. In addition, like impedance, admittance can be written on
the complex exponential form. By using complex numbers theory it can be
shown that it is possible to convert to impedance to admittance and vice
versa. The resulting equations for the conversions are:
Impedance to admittance:
R
G= (2.12)
R2 + X2
X
B=− (2.13)
R2 + X2
Admittance to impedance:
G
R= (2.14)
G2 + B2
B
X=− (2.15)
G2 + B2
6 CHAPTER 2. BIOIMPEDANCE THEORY
R = ρG (2.16)
where R is the resistance, ρ the resistivity and G the geometry. In for
example a cylindrical resistor the resistance is given as:
l
R=ρ (2.17)
A
where l is the length of the cylinder and A is the cross-sectional area.
A similar dependency is found for a parallel plate capacitors capacitance
and permittivity:
A
C = G = (2.18)
d
where C is the capacitance, is the permittivity, A the plate area and d
is the distance between the plates.
This means the –ance suffix parameters are dependent on both the electri-
cal properties of the material and the geometry, while the –ivity suffix implies
parameters that only depend on the electrical properties of the material itself,
not the geometry. (Grimnes and Martinsen, 2008, p. 2)
2.3 Bioimpedance
Bioimpedance is the term used when referring to the impedance of a bio-
logical substance. Bioimpedance describes the passive electrical properties
of biological materials. Unlike metals where the charge carriers are the free
electrons, in biological materials the charge carriers are the ions in the intra-
cellular and extra-cellular liquids. The most important ions are K+ and Na+
as can be seen in table 2.1.
Since ions are the charge carriers, a current flow implies a movement of
substance, which in itself results in changes in the biological material. Hence,
a DC current would change the conductor itself, first at the electrodes, but
over time in the whole material. (Grimnes and Martinsen, 2008, p. 8))
the cells have capacitive properties. The conductivity of the membrane itself
is very low and the membrane is only about 7 nm thick. Because of this, the
µF
cell has a very high capacitance of about 1 cm 2 . (Grimnes and Martinsen,
2008, p. 100) Due to the diffusion of ions through the cell membrane, the
concentration of ions in the intracellular and extracellular liquids can change.
The migration of ions through the membrane is possible due to ion channels.
These channels are ion specific.
At DC and low frequencies the current flows around the cells in the extra-
cellular liquids, at higher frequencies the cell membrane capacitance allows
AC current to pass through. For frequencies in between there will be a mix
of how much goes around and through the cell. Approximate values for low,
medium and high frequencies in this context is less than 10 kHz, 100 kHz and
above 1 MHz respectively. This is shown in figure 2.2. Biological tissue also
have inductive properties, but these are insignificant at frequencies below 10
MHz (Riu, 2004) and not further discussed in this thesis.
In general, the impedance of tissue decreases with an increase in fre-
quency.
Biological tissue is a very heterogeneous material and different tissues like
muscles, fat, organs, body fluids etc. can have very different cell structures.
This includes cell size and shape, how they are joined together and how much
e.g. extracellular liquid that is present in a specific tissue. Adipose tissue has
little extracellular electrolytic liquid for conduction of lower frequencies while
skeletal muscles might have anisotropic (direction dependent) properties.
8 CHAPTER 2. BIOIMPEDANCE THEORY
Figure 2.2: At low frequencies (LF), the extracellular path dominates due
to the capacitive properties of the cell membrane. At high frequencies (HF)
the cells capacitive properties allows the AC current to pass through. Taken
from (Grimnes and Martinsen, 2008, p. 103)
2.3. BIOIMPEDANCE 9
2.3.2 Polarization
Polarization is the disturbance in the charge distribution in a region as a
result of an induced electric field. This means the charge does not change
while the distribution of charge does. Since transport of ionic current also
means that substance will be moved, there will be concentration changes in
the biological material near the electrodes. In the field of bioimpedance, the
measuring methods used are exogenic. This means energy is applied to a
system, which will polarize it. The energy applied can be both be stored
and dissipated in a dielectric medium. All materials are polarizable, altough
to different degrees. This includes conductors and insulators. An electric
dipole is a separation of positive and negative charges. An example of a
dipole is a water molecule which has a negative and positive pole. Dielectric
theories regarding biomaterials often consider them as either polar materials
or as inhomogeneous materials with interfacial polarization contributions.
(Grimnes and Martinsen, 2008, p. 58) The polarization is the sum of three
components:
2.3.3 Dispersion
Frequency dispersions can be found when measuring on biological materi-
als. Dielectric dispersion is the dependency between the permittivity and
frequency in a material when we polarize it. There is also a lag between the
polarization and the changes in the applied electrical field. Three dispersion
areas were described by Schwan in 1957 and named α, β and γ. Later a
fourth dispersion area between β and γ has been described and named δ.
The typical idealized dispersion regions for tissue can be shown in figure 2.3.
(Martinsen et al., 2002) Different tissue have different degrees of dispersion.
For example, muscle tissue exhibits a large α-dispersion while blood does not
have one.
Figure 2.3: Idealized dispersion regions for tissue. DC conductance have been
00
subtracted from the imaginary part of the complex permittivity r value.
Taken from (Martinsen et al., 2002)
Figure 2.4: The frequency regions and mechanisms that corresponds to the
different dispersions. Taken from (Grimnes and Martinsen, 2008, p. 90)
Figure 2.5: Electrical model of biological material. Taken from (Ivorra, 2003)
The circuit on the right in figure 2.5 is equivalent to the left after some
simplifications. (Ivorra, 2003) As shown, the current can flow through the
extracellular liquid (Re ), through the bilayer lipid membrane (Cm ) or across
the ionic channels (Rm ). Once the current has entered the cell it also travels
through the intracellular liquid (Ri ) and across the cell membrane (Rm ||Cm ).
Rm is often omitted as the cell membrane is a poor conductor. This model is
also often seen as an equivalent model in the literature. This model only has a
single dispersion, but tissue often have more than one dispersion overlapping
in some of the frequency area. This requires a more complex model where the
capacitor in the previous model is substituted by a Constant Phase Element
12 CHAPTER 2. BIOIMPEDANCE THEORY
(CPE). The CPE is not an electronic component that exists, but is described
as a frequency dependent capacitor and resistor. It can be modeled so that
the phase is frequency independent. (Grimnes and Martinsen, 2008, p. 299)
It is also called a fractional capacitor. CPE has the impedance:
1
ZCP E = (2.19)
(j2πf C)α
where j is the imaginary unit, f the frequency, C the capacitance and α
a parameter with a value usually between 0.5 and 1. When α =1 the CPE
behaves identical to an ideal capacitor and when α =0 it behaves like an
ideal resistor. When we substitute Rm and Cm with CPE in the circuit in
figure 2.5 we get the impedance expressed as:
R0 − R∞
Z = R∞ + (2.20)
1 + (j2πf τ )α
This equation is called the Cole equation. (Cole, 1940) R∞ = Ri is the
resistive part at infinite frequency, R0 is the impedance at 0 Hz, τ is the time
1
constant (∆RC) α (Elwakil and Maundy, 2010) where ∆R = Rm = R0 − R∞
and α the CPE parameter.
Different tissues can be characterized by finding these four parameters.
These four parameters can be found by measuring with an impedance ana-
lyzer and plotting the values in a Wessel plot, like in figure 2.6.
The real part in figure 2.6 is the resistance and the imaginary part the
0
reactance. R0 and R∞ can be found where the arc intersects the Z -axis.
The CPE angle can be found as the angle between the tangent along the arc
at R∞ and the R-axis. α can now be calculated as:
2ϕCP E
α= (2.21)
π
τ can be found by finding the frequency where |X| has its maximum
value. τ is then calculated as:
1
τ= (2.22)
2πf|X|max
When more than one dispersion occurs, the Cole equation can be ex-
panded:
(R0 − R∞ )1 (R0 − R∞ )2
Z = R∞ + + + ... (2.23)
1 + (j2πf τ1 ) 1 1 + (j2πf τ2 )α2
α
2.4. ELECTRODES AND MEASUREMENT 13
Figure 2.6: Wessel plot. Notice that the reactance-axis have been inversed.
This is often seen in Wessel plots. Redrawn from (Nordbotten, 2008, p. 11)
and modified.
shows two equal electrodes in a symmetrical bipolar system and figure 2.7b
shows a quasi-monopolar system. In figure 2.7b the sensitivity and therefore
the impedance contribution is closer to the small measuring electrode than
in figure 2.7a and the impedance contribution from the large electrode would
be insignificant.
Figure 2.7: The arrows shows the current density and the color the potential
distribution in this two-electrode configuration. a) Equal and symmetrical
electrodes. b) Quasi-monopolar setup with non-symmetrical electrode con-
figuration. Taken from (Kalvoy, 2010)
current flows through an electrode it will not be polarized. This means that
when using the four electrode setup the electrode polarization impedance
will not be affecting the measurements. This setup is popular, but also
has its pitfalls and are more vulnerable to errors than monopolar and bipolar
setup. (Grimnes and Martinsen, 2006) One of the pitfalls is zones of negative
sensitivity. This means that when measuring on heterogeneous materials it
is possible that the measurements can have large errors. If the electrodes
are placed as shown in figure 2.8 it is often believed that only the volume
between the pick-up electrodes are measured. This is not true as the area
between the pick-up electrode and the current-carrying electrode will have
negative sensitivity. If the impedivity is increased in this area the total
measured impedance will be lower and might not give the expected result.
The negative sensitivity area of a four-electrode setup can be seen in a finite
element method (FEM) simulation where the measured material is infinite
and homogenous. This is shown can be seen in figure 2.9.
Figure 2.9: Shows the sensitivity area for a four-electrode setup in an infinite
homogenous material. The dark blue indicate areas with negative sensitivity.
Taken from (Kalvoy, 2010)
2.4. ELECTRODES AND MEASUREMENT 17
The circuit in figure 2.10 does remove the 50/60 Hz mains noise that
the material measured on picks up, as the current carrying electrode C and
the operational amplifier it is connected to will compensate for it, without
interfering with the measuring electrode.
If swapping the wires connecting electrode R and C the measured immit-
tance should not change. This is guaranteed by the reciprocal theorem. If
it does change then one of the electrodes might be too small and be in the
non-linear region. This is an important test to ensure that the polarization
impedance of electrode C and impedance of R does not contribute to the
measurement.
If the metals of the R and M electrode is not the same, a DC potential
will be generated. This is rarely wanted and care should be taken ensuring
they are of the same material, especially when the same type of electrode
cannot be used at both sites. This problem also arises if the tissue connected
to generates an endogenic DC potential. The material measured on will then
be under constant polarization as the operational amplifier will compensate
for the potential difference trying to drive the inputs to zero volt difference.
Combining the three electrode setup with making the relative size of the
measurement electrode much smaller than the reference electrode increases
the effect of the monopolar measurement.
1. Less noise the larger the electrode area, because of the averaging effect.
2. The more the electrode is polarizable, the more noise it will generate.
2.4. ELECTRODES AND MEASUREMENT 19
Figure 2.11: Two common skin surface electrode designs. EA is the electrode
area and EEA is the effective electrode area. Taken from (Grimnes and
Martinsen, 2006)
20 CHAPTER 2. BIOIMPEDANCE THEORY
Electronics Theory
23
24 CHAPTER 3. ELECTRONICS THEORY
Figure 3.1: Sine wave with the most commen sine wave characteristics.
3.3. CURRENT MEASUREMENTS 25
crystal oscillator. This clock will be the sampling clock of the DDS. The
frequency output range of a DDS is normally from DC to half of the external
clock frequency.
A DDS consists of three building blocks. The accumulator, sine lookup
table (angle-to-amplitude converter) and a digital-to-analog converter. This
can be seen in figure 3.2.
The accumulator increments a number on each clock pulse resulting in a
ramp as shown in figure 3.2. The accumulator is often 12 to 24 bits. When
the accumulator overflows, one period of a sinusoidal signal is achieved and
the accumulator starts incrementing from the reminder value, if any. This
means that the larger the increment is, the faster it will overflow and higher
the output frequency. The size of the increment is one of the inputs to a
DDS and is often called the frequency (tuning) word.
The value output by the accumulator always corresponds to a phase be-
tween 0 and 360 degrees. The sine lookup table simply converts the phase
angle to a corresponding amplitude by looking up the phase angle in a read-
only memory (ROM) and output the amplitude. This creates a discrete
sinusoidal signal on the angle-to-amplitude converters output. This can be
seen in figure 3.2.
The digital-to-analog converter (DAC) converts the discrete digital sinu-
soidal signal to a continuous analog sinusoidal voltage or current signal as
seen in figure 3.2.
Even though usually not a part of the DDS, a low pass filtering circuit is
added after the DDS output to smooth the analog sinusoidal waveform and
remove unwanted harmonics/images.
The output frequency fout of the DDS is:
fclk Fw
fout = (3.4)
2N
where fclk is the external clock frequency, Fw the frequency word and N
the number of accumulator bits.
DDS
Digital-to-
Frequency Sine Lookup Low Pass
Accumulator Analog
Word Table Filter
Converter
External
Clock
Figure 3.2: The building blocks of a DDS
26
3.3. CURRENT MEASUREMENTS 27
I
Vout
Rs
Rf
I
Vout
In this circuit the input current I flows through the feedback resistor Rf .
If the op-amp has a relative low input offset current the output voltage Vout
is:
Feedback compensation
To increase the stability, bandwidth and reduce ringing on the output signal
of a transimpedance amplifier, feedback compensation is used. Feedback
compensation is done using a feedback capacitor Cf in parallel with the
feedback resistor Rf , as shown in figure 3.5.
To ensure stability, a capacitor value that provides a phase margin of
at least 45 degrees should be calculated. TI (2013). Phase margin is 180
degrees subtracted from the difference between the input and output phase
3.3. CURRENT MEASUREMENTS 29
Cf
Rf
I
Vout
R2
Vout = Vin (3.9)
R1 + R2
where Vin is the input voltage and R1 and R2 the resistor values. A
voltage follower can be cascaded after the voltage divider to assure lower
output impedance and more stable output reference voltage.
Vin
R1
Vout
R2
circuit elements ideally want a constant DC voltage level, but external noise
from the environment and other circuit elements are superimposed on the DC
voltage. The decoupling capacitor provides a low impedance path for these
transients and works as a local energy storage for the circuit element. When
there is a drop in voltage, the capacitor compensate by releasing energy and
when there is a spike it charges.
The value of the capacitance used depends on the frequencies one want to
suppress. A larger capacitance value is better at suppressing lower frequen-
cies and vice versa. Decoupling capacitors are widely used in most electronics
designs.
3.6 Comparator
A comparator is a specialized op-amp circuit that compares two voltage or
current signals and the binary output signal shows which is larger. A com-
parator is shown in figure 3.7.
If the positive input Vin+ is larger than the negative input Vin− then the
output Vout is usually close to the value of the comparators power supply.
If the negative input is larger than the positive input, then the output is
usually ground. Because the output only has two states, it can be suitable
to connect directly to a digital circuit.
The output of the ideal comparator changes its state when the difference
of the inputs are zero. Due to noise at the inputs, unwanted very fast changes
32 CHAPTER 3. ELECTRONICS THEORY
Vin+
Vout
Vin-
between the two output states can occur when the difference between the
inputs is close to zero. To avoid this behavior, internal hysteresis is often
integrated or must be provided externally by adding a resistor to the feedback
loop from the output to the positive input.
Speed and power consumption can be important parameters and in gen-
eral, the faster the comparator, the more power it consumes.
One of the many uses of a comparator is as a zero crossing detector (ZCD).
This is used to detect when the polarity of an AC signal changes. The output
of the comparator will be high when the polarity is positive and low when
the polarity is negative.
3.7 AC coupling
AC coupling is used to remove the DC signal. This is often done by placing
a capacitor in series with the signal one want to remove the DC signal from.
This means that only the AC signal may pass through to the next part of the
circuit. This is also called capacitive coupling and the capacitor is often called
a DC blocking capacitor or decoupling capacitor. The decoupling capacitor
in combination with the input impedance of the next stage in the circuit
forms a high pass filter. This high pass filter attenuate lower frequencies so
it is important to know the frequency characteristics, as the cut-off frequency.
It is possible to accurately control the cut-off frequency as shown in figure
3.8.
Here the shunt resistor and the DC blocking capacitor forms the high
pass filter. The cut-off frequency f−3dB is then:
1
f−3dB = (3.10)
2πRC
3.8. ANALOG-TO-DIGITAL CONVERTER 33
V
Resolution = LSB = (3.11)
2N −1
The sampling rate is the time resolution and must be set correctly for a
given setup to be able conduct a successful measurement.
V+
Vin-
Vout
Vin+
V-
Figure 3.9: An operational amplifier.
Vin
Vout
It simply ensures that the output voltage is the same as the input voltage:
Non-inverting Amplifier
The non-inverting amplifiers output voltage changes with the same phase as
the input voltage. A typical non-inverting amplifier is shown in figure 3.11.
Vin
Vout
R1 R2
The amplification is usually called gain (A) and if the open-loop gain
(AOL ) is very large, then the gain can approximately be calculated as:
Vout R2
A= =1+ (3.13)
Vin R1
36 CHAPTER 3. ELECTRONICS THEORY
Inverting Amplifier
The inverting amplifiers output voltage changes 180 degrees out of phase
with the input voltage. A typical inverting amplifier is shown in figure 3.12.
Rf
Vin
Vout
Rin
If the open-loop gain is very large the gain can approximately be calcu-
lated as:
Vout Rf
A= =− (3.14)
Vin Rin
The input and output voltage can normally have values within the power
supplies voltage range, both negative and positive.
Single supply systems are powered by a single supply and does not have
the same ground reference as a dual supply system has. When an input source
is connected to ground, it is not referenced to the single supply’s center,
but the lower power supply rail, which is ground. This is equivalent to the
negative supply in a dual supply system. This means a single supply op-amp
is not able to handle negative input signals for a non-inverting amplifier or
positive input signals for an inverting amplifier, as it would force the output
to go negative, which it cannot. It is therefore necessary to bias the single
supply op-amp to avoid this problem. An example of this can be seen in
figure 3.13.
Rf
V+
Vin
Vout
Rin
C
V+/2
Here the op-amp is biased with half of the op-amps power supply on the
positive input. The capacitor on the input assures that only AC voltage is
passed through from the input source and vice versa. This circuit superim-
poses the passing AC voltage on the bias voltage at the op-amps output,
even if the input source signal has positive polarity. The output can only
be positive so the input signal amplitude can maximally be half of the sin-
gle supply voltage if it is a sine wave centered around zero volt. This also
requires that the bias voltage is half the single supply voltage.
As demonstrated, it is possible to use single supply systems to perform
the same operations as dual supply systems with some modifications.
The motivation behind using single supply systems is the lower power
consumption demands of portable battery-powered devices. Also only one
battery is needed.
38 CHAPTER 3. ELECTRONICS THEORY
Cf + Csh + Ci
N2 = en ( ) (3.24)
Cf
where en is the voltage noise density, Cf the feedback capacitor, Csh the
shunt capacitance and Ci the op-amps input capacitance.
An estimation of the voltage noise is then:
r
π
Nvoltage = N2 · fp2 (3.25)
2
where fp2 is the second pole and defined as:
Cf
fp2 = fGBP · (3.26)
Cf + Csh + Ci
where fGBP is the gain bandwidth product.
The three noise sources are independent and Gaussian meaning that the
total noise is the root-sum-square (RSS):
3.12. TRANSIMPEDANCE AMPLIFIER NOISE ANALYSIS 41
q
2 2 2
Ntotal = NRf + Ncurrent + Nvoltage (3.27)
A low pass filter on the transimpedance output can greatly reduce the
total noise if fp2 is much higher than the signal bandwidth.
42 CHAPTER 3. ELECTRONICS THEORY
Chapter 4
Digital Theory
4.1 ARM
4.1.1 What is ARM
ARM is a family of microprocessors and microcontrollers based on the Re-
duced Instruction Set Computer (RISC) architecture. The RISC architecture
is often associated with low cost, less heat and low power computer proces-
sors. ARM Holdings, which own the ARM family, licenses its chip design
and instruction set to third-party companies. Third party companies then
add, e.g., memory, peripherals, wireless radios etc. This is what for example
ST Microelectronics and Texas Instruments do.
43
44 CHAPTER 4. DIGITAL THEORY
there are 16 levels. NVIC supports nesting of interrupts, which means inter-
rupts with higher priority can pre-empt lower priority interrupts when they
are being serviced. NVIC also supports tail-chaining interrupts. This means
that if there is a pending interrupt when already servicing another interrupt,
the microcontroller skips context switching and the next interrupt is serviced
only 6 cycle after the previous one ends. (ARM, 2006, p. 108)
4.2 STM32
STM32 is a microcontroller based on the ARM Cortex series. STM32F0xx
is based on Cortex-M0, STM32F1xx and STM32F2xx on Cortex-M3 and
STM32F3xx and STM32F4xx on Cortex-M4. There are also other STM32
microcontrollers based on ARM Cortex that specializes in low power, con-
nectivity or touch sensing. The microcontroller chosen in this thesis is the
STM32F103VC. This microcontroller is based on the ARM Cortex-M3 core
and has many peripherals added by STMicroelectronics.
4.2. STM32 45
Figure 4.1: STM32 clock tree. Taken from (STM32, 2011a, p. 90)
In this thesis, the external high speed (HSE) clock is used as the sys-
tem clock and provides most of the STM32 peripherals through the ARM
Advanced High Speed Bus (AHB) and the two ARM Advanced Peripheral
46 CHAPTER 4. DIGITAL THEORY
Busses (APB). The Real Time Clock (RTC) is connected to an external high
precision 32.768 kHz oscillator and the Independent Watchdog (IWDG) to
an internal inaccurate 40 kHz (±40%) RC oscillator.
4.2.3 Watchdog
The STM32 independent watchdog timer is a 12-bit count down timer that
will reset the microcontroller when it reaches zero. (STM32, 2011a, p. 475)
It is therefore necessary to update the watchdog before timeout unless a reset
is wanted. This means a system reset will happen if the software blocks or
delay too long before the watchdog timer is updated.
4.2.6 Flash
Flash is a non-volatile memory that can be electrically erased and pro-
grammed. The STM32 has embedded flash memory that is 16-bit aligned.
This means that the flash memory can only be programmed 16-bits at a time
and only at every second byte. The flash uses the HSI clock for write and
erase operations hence this must be enabled. The flash is divided into pages.
A page is a block of memory and is the smallest piece of flash memory that
can be erased. On the STM32F103VC used in this thesis, each page cor-
responds to 2 KB of memory and there are 128 pages in the main memory
block. Each page can endure minimum 10 000 erase cycles. (STM32, 2011b,
p. 63)
STM32 also support flash write and/or read protection with a special key
that has to be written to a specific register to unlock and be able to write
and/or read from flash.
Figure 4.2: STM32 memory map. Taken from (STM32, 2011b, p. 38)
4.3. MICROCONTROLLER 49
by using the arrow operator, which will dereference the pointer to the struc-
ture and access the value of the member. For example, setting the USART1
data register (DR) to 54 hexadecimal would look like this:
4.3 Microcontroller
4.3.1 Debugging
When debugging on STM32, it is important to properly set how I2C, watch-
dog, timers and more, should behave. If the watchdog is enabled when
debugging it will most likely time out and reset the system when hitting a
breakpoint. This is in most cases unwanted behavior and interrupts the de-
bugging process. The I2C bus can timeout when hitting a breakpoint and so
on. It is possible to set how some microcontroller peripherals should behave
when hitting a breakpoint. If using a watchdog or timer one can set it to
stop counting when at a breakpoint, if wanted. This will avoid the debug-
ging process to be the cause of the watchdog time out. More on this can be
found in the specific STM32 microcontroller reference manual under debug
support.
remove logical errors from the program. When debugging it is possible to halt
the processor at all executable lines of code, giving full control of execution.
Breakpoints are used to mark which line of code one wants to investigate
closer. It is possible to use one or more breakpoints. If a line with non-
executable code is selected, for instance a comment, the next executable line
is selected automatically instead. After inserting breakpoints, the debugger
can be run. There are usually five commands to control the flow of execution:
1. Step: This command steps to the next executable line of code. If the
next line is a function call, one will be taken to the first line in the
function.
2. Step Over: This command acts like a step as long as the next line is
not a function call. If the next line is a function call, it will run past
all the code in that function.
3. Step Out: This command executes the rest of the code in a func-
tion then continues debugging the next executable line in the function
caller’s code.
4. Run to Cursor Line / Continue: This command executes the code until
the next breakpoint is reached.
5. Stop: Stops the debugging.
4.3.2 Interrupts
An interrupt signals an internal or external event in a microcontroller that
requires handling. An interrupt will disrupt the flow of execution. Handling
is done in Interrupt Service Routines (ISR) and for USART1 an ISR can look
like in figure 4.3:
It is necessary to check the USART 1 interrupt status flags to determine
why the interrupt handler have been called, as all USART 1 interrupts will
call the same handler. This handler simply checks if a USART receive inter-
rupt have been received and if so, data is pushed to a queue and the USART
receive interrupt is cleared.
4.3.3 printf()
The printf() function provided by the C standard library, defined in the
stdio.h header file is the standard way of printing text when programming in
the C language. When printing on, e.g., the Windows operating system, the
standard output stream is set by the operating system.
4.3. MICROCONTROLLER 51
(void*)&pulStack[ST ACK_SIZE − 1]
to
fa = f − nfs (4.2)
4.4.2 Undersampling
Undersampling (also called sub-sampling) is sampling below the Nyquist fre-
quency. When this is done the absolute frequency information is lost as the
signal is aliased (or folded) on the first Nyquist zone from 0 to f2s .
In 4.4 two signals with the same amplitudes are shown. The blue and red
signal has a frequency of 1 Hz and 19 Hz respectively. The sampling rate is
20 Hz, which means the 19 Hz signal is folded on to the first Nyquist zone
as a 1 Hz signal. This can be seen as the samples of the signals is marked
with red and blue circles. The 20 Hz sampled 19 Hz signal seems to have the
same frequency as the 1 Hz signal, but out of phase.
In 4.5 the reconstructed signals can be seen and they both have the same
frequency. The 1 Hz signal is correctly reconstructed, while the 19 Hz signal
is reconstructed as a inverted 1 Hz signal. Since both signals has the same
frequency is it now impossible to distinguish the aliased from the non-aliased
signal.
This can be used to sample periodic signals that would require a sampling
rate much higher than the chosen ADC. In 4.6 10 periods of a 1 kHz signal
is shown in blue. Eleven samples are taken over 11 periods and is shown as
red circles. The first sample is taken in the first period when the ZCD is
triggered. The next sample is then taken the next time the ZCD triggers,
but with a delay of:
1 1
delay = · · (n − 1) (4.4)
11 f
where n is the period the sample is taken.
To properly see that the collected samples is similar to sampling the 1 kHz
signal at 11 kHz all the samples time axis values can be scaled by dividing
with periods + 1. This is shown as the green circles.
Instead of using a ZCD it is possible to calculate the sampling frequency
needed to sample N samples over M periods when a signal has the frequency
f:
N
=k (4.5)
M
where k is a coefficient that is used to find the sampling frequency:
fs = kf (4.6)
2. Multiply each sample with the filter coefficient at its position and ac-
cumulate the result, which is the output of the filter.
3. Shift all the samples in the filter by 1 and repeat these three steps.
Low Pass
Comparator Mixer Q
Filter
-90° Phase
Shift
If we have a signal S(t) and a square reference signal R(t) both with no
DC and the reference signal has no phase offset and an amplitude of 1:
As
cos [(ωs − ωr )t + θ] − cos [(ωs + ωr )t + θ]
2
Since the angular frequencies are equal, we get:
ω = ωs = ωr (4.12)
60 CHAPTER 4. DIGITAL THEORY
As
M (t) = cos (θ) − cos (2ωt + θ) (4.13)
2
After the mixing of the signal and reference signal, the result is a DC
component with half the amplitude of the signal amplitude that varies with
the cosine of the phase difference and a signal with twice the frequency of
the original signals.
If the component at twice the signal frequency is attenuated using a low
pass filter, M (t) is a DC signal whose amplitude only varies with the cosine
of the phase difference between the two signals.
The in-phase and quadrature-phase signal is then:
As
I(t) = cos (θ) (4.14)
2
As As
Q(t) = cos (θ − 90◦ ) = sin (θ) (4.15)
2 2
Here I(t) is the real part and Q(t) the imaginary part of the signal. The
amplitude and phase can be calculated:
p
|A(t)| = I(t)2 + Q(t)2 (4.16)
Q(t)
φ(t) = arctan ( ) (4.17)
I(t)
fs = 4f (4.18)
A signal is sent through a comparator and through the object to be mea-
sured. The output of the comparator is used as the reference signal. A zero
crossing detector (ZCD) ensures that the signal is sampled at the same point
as referenced by the reference signals rising or falling edge. The frequency
is known so a time delay corresponding to one fourth of the period can be
calculated and a timer is used as a trigger to the sampling ADC. This results
in a digital signal s[n] that is the sampled signal s(t) at:
nπ
t= where n = 0, 1, 2, 3... (4.19)
2
4.4. DIGITAL SIGNAL PROCESSING (DSP) 61
Sinusoidal Object To Be
Signal Measured
Comparator
s[0] - s[2] I
s[3] - s[1] Q
Figure 4.8: Block diagram of a digital lock-in amplifier using this technique.
σ
N=√ (4.22)
n
where σ the standard deviation of the noise and n the number of mea-
surements.
can add, modify or delete files and commit the changes to the repository.
The revision is incremented by one for each successful commit. It will then,
at any time, be possible to retrieve an earlier revision of the entire software
project. This task would be almost impossible elsewise. Another much-
valued functionality is the ability to compare specific files in the software
project to older revisions of the same file to see what changes was made.
During this thesis, the free open source application TortoiseSVN was used as
the software versioning and revision control system. TortoiseSVN provides
an easy to use graphical user interface and is available on Windows and Mac
OS X.
CooCox CoIDE
CooCox (Cooperate on Cortex) CoIDE is a relatively new and free software
development environment for ARM Cortex microcontrollers. All code can
be written, compiled, linked, downloaded to microcontroller and debugged
through this IDE.
4.6.5 Qt
The Qt project is an open source, C++ framework, IDE and more used to
develop GUI and applications.
Qt framework
The Qt framework is a powerful open source graphical user interface and
application framework and have a lot of functionality. Qt is a cross-platform
framework written in C++ and some of the supported platforms are Win-
dows, Linux, Mac OS X, Android, iOS, Blackberry, Windows Phone, WinRT
and a lot more. Qt also has QML which is a JavaScript superset declarative
language for designing GUI, mostly used for mobile development. In this
thesis, Qt have been used for all GUI development on both desktop and An-
droid. QML was not used due to QCustomPlot, which is the library used for
plotting, not supporting QML. It is possible to choose different compilers for
compiling code under Qt. MinGW GCC 4.8 was used for both desktop and
Android.
Qt Creator IDE
Qt Creator is a cross-platform IDE and is a part of the Qt project. It also
has the Qt Designer embedded which is a GUI layout and form builder. Also
debugging, examples and help documentation is integrated in the IDE along
with a lot of other functionality.
Qt and Android
From the release of Qt 5.2, the Android platform was officially supported.
This makes it possible to also deploy applications to Android and combine
both the Qt Framework and Android’s API written in the Java programming
language. This makes it possible to use not yet Qt-implemented Android
functionality by calling Android API/Java code. Before being able to compile
and deploy for Android, four third-party software’s are needed. These are:
1. Android Software Development Kit(SDK)
4.7. ANDROID / JAVA 65
4.6.6 Python
Python is an easy to use interpreted programming language with a large set
of libraries.
Software Version
Qt Framework 5.3.0
Qt Creator IDE 3.1.0
Tortoise SVN 1.8.4
Android SDK 20131030
Android NDK r9b
Apache ANT 1.9.2
JDK 1.7.0.45
CooCox IDE 1.7.6
ARM Toolchain 4.8.0
MathWorks MATLAB 2014a
WinMerge 2.14.0.0
Standard Peripheral Library 3.5.0
Python 3.3.0
MatPlotLib 1.2.0
CMSIS DSP Library 1.1.0
CMSIS 3.01
and the C++ programming language has been mostly used. The exception is
the Bluetooth functionality which was not supported by the Qt Framework
at the time of development and is therefore written in Java.
5.1 Overview
Figure 5.1 is an overview of the hardware platform and their interconnections.
The microcontroller communicates with a PC or Android system over the
Bluetooth module, controls the Direct Digital Synthesizer (DDS) and receives
measurement signals from the analog front-end (AFE).
Serial
Interface
DDS
Microcontroller
Biological
AC Coupling & Electrode R
UART ADC1 ADC2 ZCD ADC3 Material To
Attenuation and C
Be Measured
Bluetooth
Comparator
Analog Electrode M
Front-End
PC /
Android
Transimpedance
Gain Stage 2 Gain Stage 1 Amplifier
69
70 CHAPTER 5. SYSTEM DESIGN: HARDWARE
5.2 DDS
The AD9850 integrated circuit (IC) from Analog Devices was chosen as the
DDS to use in this prototype.
AD9850 satisfies the requirements as seen in table 5.2 and it was avail-
able as an evaluation board with 2.54 mm pitch making it easy to use in
prototyping. The evaluation board schematic is shown in the appendix and
a picture of the module is shown in figure 5.2.
An overview of the input and outputs used can be seen in figure 5.3.
Vcc and GN D are the power and ground line respectively, Iout the AD9850’s
internal DAC output, RESET the reset line and D7, W _CLK and F Q_U P
5.2. DDS 71
D7 VCC
W_CLK
AD9850 Iout
FQ_UP
RESET GND
This is shown in the data sheet and a modified figure is shown in figure
5.4.
5.2. DDS 73
fout
-
fclk fout
SIGNAL AMPLITUDE
fclk +fout -
2 fclk fout
The AD9850 has a 40-bit register that is set by the microcontroller. The
content of the register is shown in 5.5.
fout
register value = 232 − (5.4)
resolution
The output frequency is at its lowest when the register is 232 and at its
highest when its zero.
The two controls bits are not to be set by the programmer and are both
set zero.
74 CHAPTER 5. SYSTEM DESIGN: HARDWARE
The power-down bit is set high to power down the DDS and low to power
on.
The phase select bits can be used to shift the phase of the output signal,
but these have not been utilized in this project.
When writing to the register over the serial interface, the D7 input is
read at each rising edge of W _CLK. When all the 40 bits are shifted into
the register a pulse on the F Q_U D input is needed to update the AD9850.
V power supply.
V+
R10
1 kΩ U3 Virtual Ground
OPA350
R11 C4
1 kΩ 47 nF
R11 1000 Ω
Vout = · V+ = · 3.3 V = 1.65 V (5.5)
R10 + R11 1000 Ω + 1000 Ω
The input voltage comes from the DDS with a peak-to-peak amplitude
of about 1 V as calculated in equation 5.1.
The DDS output signal is first meet by a high pass filter that ensures DC
voltage is blocked. The high pass filters cut-off frequency fc is:
1 1
fc = = = 15.92 Hz (5.6)
2πRC 2π · 10000 · 10−6
78 CHAPTER 5. SYSTEM DESIGN: HARDWARE
R19 R20
220 Ω 10 kΩ
Vin
U1 Vout
R8 OPA350
C1 10 kΩ
1 µF
Virtual Ground
Figure 5.9: The AC coupling and attenuation stage used in the hardware
prototypes.
R19 + R20
Vout = · Vin(AC) (5.7)
R8
Equation 5.7 is only valid for frequencies much higher than the high pass
filters cut-off frequency from equation 5.6.
5.4.3 Comparator
The chosen comparator was MAX941CPA from Maxim Integrated. It is a
3 V compliant single supply comparator with 80 ns propagation delay and
internal hysteresis. It is manufactured in an 8-pin plastic dual inline package
(DIP-8) package among others. It also have a low operational current draw
of maximum 600 µA at 3 V and a shutdown current draw of a few tens of
microamperes.
5.4. ANALOG FRONT-END 79
The comparator has the output from the AC coupling and attenuation
stage on its non-inverting input and the virtual ground reference voltage on
its inverting input.
This means the comparators output is high when the input AC signal on
its non-inverting input is above the virtual ground reference voltage and visa
versa.
The comparator stage can be seen in figure 5.10.
Vin
Vout
MAX941
Virtual Ground
R electrode
U2 C electrode
Vin OPA350
Figure 5.11: The electrode interface stage used in the hardware prototypes.
C5
10 pF
R23 R13
680 Ω 10 kΩ
M Electrode / I in
U4 Vout
OPA350
Virtual Ground
Figure 5.12: The transimpedance amplifier stage used in the hardware pro-
totypes.
5.4. ANALOG FRONT-END 81
s r
Cs + Ci 0 + 6.5 pF
Cf lower = = = 6.3pF (5.8)
2πRf fGBP 2π · 680 Ω · 38 MHz
s r
Cs + Ci 0 + 6.5 pF
Cf upper = = = 1.6pF (5.9)
2πRf fGBP 2π · 10680 Ω · 38 MHz
s s
fGBP 38 MHz
f−3dB = = = 7.53MHz (5.10)
2πRf Cf 2π · 10680 Ω · 10pF
p
NRf = 4kT · ENBW · Rf
r (5.12)
J
= 4 · 1.38 · · 298 K · 11.83 MHz · 10680 Ω = 45.6 µVrms
K
82 CHAPTER 5. SYSTEM DESIGN: HARDWARE
√ fA √
ENBW = 4 √ · 10680 Ω · 11.83 MHz = 0.33 µVrms
Ncurrent = In Rf ·
Hz
(5.13)
The plateau/peak noise used to calculate the voltage noise is as given in
equation 3.24:
Cf + Csh + Ci nV 10 pF + 0 pF + 6.5 pF nV
N2 = en ( ) = 7√ · ( ) = 11.6 √
Cf Hz 10 pF Hz
(5.14)
and the second pole used to calculate the voltage noise as given in equation
3.26 is:
Cf 10 pF
fp2 = fGBP = 38 MHz ·( ) = 23.03 MHz
Cf + Csh + Ci 10 pF + 0 pF + 6.5 pF
(5.15)
The voltage noise is then defined by equation 3.25 as:
r r
π nV π
Nvoltage = N2 · fp2 = 11.6 √ · · 23.03 MHz = 69.8 µVrms (5.16)
2 Hz 2
The total noise when no or low shunt capacitance relative to the feedback
capacitance and the op-amps input capacitance is given by equation 3.27 as:
q
2 2 2
Ntotal = NRf + Ncurrent + Nvoltage
(5.17)
p
2 + 0.33µV 2 + 69.8µV 2
= 45.6µVrms rms rms = 83.4µVrms
The total noise is very low, but the noise is amplified in the following
gain stages with their gain and also their own inherent noise.
R22 R16
680 Ω 10 kΩ
R15
1 kΩ
U5 Vout
Virtual Ground OPA350
The output voltage for a given input voltage for the circuit in figure 5.13
is:
R16 + R22
Vout = · Vin (5.18)
R15
Stage Capacitance
Virtual Ground 47 nF
AC Coupling & Attenuation 47 nF
Comparator 47 nF
Transimpedance Amplifier 47 nF
Gain Stage 1 47 nF
Gain Stage 2 47 nF
5.4.8 Simulation
The analog front-end circuit shown in the appendix was simulated using
Circuit Lab (www.circuitlab.com). The OPA350’s parameters were added to
make the simulation more correct. The magnitude and phase plot is shown
in figure 5.14. The main area of interest is between 1 kHz and a few hundred
kilohertz. Figure 5.14 shows there is close to no change in magnitude from
1 kHz to 100 kHz and the phase is approximately 1◦ at 1 kHz, −9◦ at 100
kHz and −51◦ at 1 MHz. The phase decreases linearly with frequency and
this can be calibrated for. Even at 1 MHz, the magnitude has only decreased
with 2.2 dBV (factor of 1.3).
5.4.9 Prototypes
The first prototype was made on a breadboard using cheap op-amps like
the LM741 from Texas Instruments and MCP6002 from Microchip. It did
not have variable resistors or decoupling capacitors and was connected with
breadboard jumper wires. It was used to test low frequency operations as an
early test to be used with the microcontroller. The first prototype is pictured
in figure 5.15.
The second prototype was identical to the first except that the op-amps
was substituted with the OPA350 op-amp and MAX940CPA comparator.
This was used to test on higher frequencies and as a preparatory stage before
constructing the third prototype.
The third prototype was made on a prototype board with a 31 x 16 matrix
of plated vias. All components was soldered to the board, including the DDS
and Bluetooth module. It was now possible to fit the analog front-end board
directly on the microcontroller breakout board. The third prototype is shown
in figure 5.16.
5.5 Bluetooth
For the wireless transmission a Bluetooth 2.0 compatible module named
"JY_MCU BT_BOARD V1.4" has been selected. It supports 3.3-5 V logic
and a 3.6-6 V power supply. This modules supports the RFCOMM protocol
which emulates a serial link over Bluetooth. This means it can be directly
interfaced with UART on the microcontroller and the device connected to
this Bluetooth module will receive data as if it was a RS-232 interface. The
module can be configured using a set of AT commands. The baud-rate should
be 9600 kbps by default or can be set using the AT command "AT+BAUD4"
5.5. BLUETOOTH
6.1 Overview
6.1.1 Modular code
The microcontroller code have been developed with modularity in mind.
Modular programming is a software design technique that apply strict guide-
lines to how code is organized. Modular code is separated into many files
where each file is a part of the whole application with well-defined interfaces.
This often reduces the number of dependencies for each file, makes it easier to
navigate in the code, maintain the code and is a good foundation for further
development.
89
90 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
Main.c
Applications
Interface
Drivers
SPL
CMSIS
Hardware
Figure 6.1: Code abstraction levels.
6.1. OVERVIEW 91
They all use the basic functions in the standard peripheral library (SPL)
provided by STMicroelectronics.
Each driver filename is named after the peripheral/hardware they in-
teract with. Therefore, for the analog-to-digital converter the source and
header filename is ADC.c and ADC.h respectively. ADC.c/.h only contains
functions that are common for all the ADC peripherals. This means that
the ADC1.c/.h, ADC2.c/.h and ADC3.c/.h must have common code for it
to be moved to the ADC.c/.h. In general, it is desired to move code to the
highest layer reasonably possible. This is because changes to code at lower
levels larger effect on the rest of the code compared to changes done at higher
levels. Simply because code at a lower levels has more code depending on it.
The layer above is called the interface and this code uses the drivers and
are in general not allowed to call any code from the SPL or lower. The
interfaces implemented are shown in table 6.2.
Each interface file is a logical block of the program. For example, the
measurement.c/.h contains all functionality related to measurements, such
as calibration routines, calculation of measurement parameters, measurement
functions and immittance calculations. Another example is the Queues.c/.h
containing different queue functionality or Timer.c/.h that has higher-level
timing functionality.
The next layer is the application layer. This layer consists of applications
using the interface level to perform different tasks. In this thesis, only one
92 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
application is running on the system and must handle all states and changes
done to it at run-time. The application is called BMS.c/.h. Other applica-
tions can be added and an example would be a test application that tests
and verifies parts of the code and peripherals functionality.
The microcontroller point of entry is the main function in main.c file.
This function simply contains an infinite loop (from now on referred to as
the main loop) where applications can be run. The main loops responsibility
is running the applications in sequential order.
All the code explained above runs in a loop and the code flow is not
interrupted and is therefore running as if it was a thread. To be able to
asynchronous operations and be able to not use processing power to check
if some event has happened, interrupts are used. The interrupts are written
in an own file names ISR.c and they will interrupt the programs flow of
execution on some event, execute a short routine of code and resume.
The interrupts implemented are shown in table 6.3.
In addition, a header file with widely used constants exists and is named
defines.h. This file has all macro definitions and is used by all layers above
the SPL.
The direct memory access has been implemented for ADC1 and ADC3. Two
different implementations of the ADC1 exist. One that transfers the con-
verted ADC1 data to a buffer and one that transfers synchronously sampled
ADC1 and ADC2 data to a buffer. The DMA for ADC3 is used to trans-
fer the sampled ADC3 data. Two independent DMA controllers exist and
DMA2 is used for moving ADC3 data and DMA1 for the two others.
A structure containing the address of the sampling buffer, the number
of samples to move and a status flag indicating that the move is done has
been made. Get and set functions is available to set and get the values of
the structure. Each of the three DMA implementations has its own instance
of the structure and an initializer function.
94 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
make sure the system does not freeze. The watchdog timer uses the LSI as
reference and the calibration is necessary to ensure the timeout set is close to
the actual timeout. If the timeout is set with an expected LSI clock frequency
of 40 kHz when it is in fact 60 kHz, it will timeout two thirds faster than
expected and the system might continuously reset itself. An alternative is
to set a much higher timeout ensuring that the variations in the LSI clock
frequency has less or no effect.
The watchdog can be set up calling the initialize function and then has
200 milliseconds timeout by default. There is a function to set the timeout
and start the independent watchdog. The update function is used to keep
the system from resetting and a function can be called on system startup to
check if the system was reset due to the independent watchdog timeout.
A special function to halt the independent watchdog when debugging is
provided and explained why and how to use in chapter 4.3.1.
6.3.3 Initialize
This module contains a function that collects all initializations to be done
at system start-up. It also check whether the system was reset due to the
98 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
6.3.4 Measurements
The measurement module is one of the largest and most functionality related
to measurements, calibrations and immittance calculations are done here.
As an example the Measure_4fs_CSFM() function can be used after
some initialization to measure at an input frequency over a chosen number
of periods using the 4fs method. After this function has been invoked, all
the immittance parameters and IQ demodulation values can be retrieved by
calling the appropriate Immittance_Get() function.
System voltage calibration and phase calibration functionality is imple-
mented in this module.
6.3.6 Queues
Three equal FIFO queues has been implemented for 1, 2 and 4 bytes data
length. The implementation is speed optimized and functions for adding data
and retrieving next data in queue are the most important. In addition, it
is possible to check if the queue is empty, how many elements are currently
queued and look at a specified stored element in the queue.
6.3.7 Timer
This module has some high level timing functionality. The simplest one being
function for delaying of a number of millisecond. There is also a function for
starting a timer with a given timeout and a function that can be checked
regularly to see if this timeout has happened yet.
6.3.8 User
The user module has function to set parameters controlled by the applica-
tion’s user like frequency to measure at, chose the state of the system or do
6.4. MICROCONTROLLER : APPLICATION 99
phase calibration.
6.6.2 Communication
The C standard library function printf() is used to send data as text to the
Bluetooth module, which will transfer the data to the connected devices.
This is possible by retargeting the printf() function as explained in chapter
4.3.3. Binary data can also be sent byte-wise using the UART_SendData()
or UART_SendDataArray() function.
Data received by the Bluetooth module is sent to the microcontroller. The
microcontroller is interrupted and an ISR is set up to handle the incoming
data. The ISR adds the received byte to a received data queue for later
processing.
When enough bytes are received to construct a full message the message
is parsed and internal variables are set accordingly. The received messages
consists of a preamble, message identifier, payload and a postamble. The
preamble is a constant one byte value (0xAA hexadecimal) and signals the
start of a message. The message ID is used to tell the system what kind
of message it is and which parsing code to use. The payload can vary for
the different messages and contains the new information to the system. The
message used to control the measurement settings consists of four bytes of
binary data. The postamble is similar to the preamble except it is at the end
of the message and has a different value (0x0A hexadecimal). The message
format for the measurement settings message can be seen in figure 6.2.
reference signals falling edge to start the sampling timer TIM1. The sampling
timer controls the ADC1 that samples the signal from the AFE. The DMA1
automatically move the data from the ADC1 sample data register to an array.
When the preset number of samples has been collected and transferred by
the DMA1 the 4fs algorithm can use the data.
The 4fs algorithm is shown as a block diagram in figure 6.4. The sampled
data is first demodulated using the equations 4.20 and 4.21. Then it is
average for some set number of periods and converted to the corresponding
voltage values. By knowing the peak-to-peak values of the excitation signal
the measured current at the M electrode can be calculated from the voltage
value. The next step is calculating all the immittance parameters displayed
by the GUI.
Digital Lock-in
The digital lock-in synchronously sample the signal and reference on ADC1
and ADC2 using the timer TIM1. DMA1 transfers the data to an array for
later processing.
The square reference has a digital debouncing filter and is normalized and
has either the value 1 or -1. A 90 degrees out of phase square reference is
constructed and the signal is multiplied with both the in-phase and quadra-
ture reference. The result is a DC component and a component at twice the
102 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
Microcontroller
4fs Algorithm Lock-in Algorithm
Signal Excitation Excitation Signal &
Sample Sample Sample Reference
Data Data Data Sample Data
DMA1
Analog Front-End
Signal Signal
Figure 6.3: An overview of the used hardware and driver modules for the
two measurement techniques.
θ
Calculate
Excitation Average over
Peak-to-Peak
Sample Data M periods
Value
6.7.1 Overview
A GUI has been made to make it easy to control and visualize the measure-
ments by using graphs. The GUI is made with the Qt framework and have
been tested on Windows and Android, but might possibly be working on
many other platforms such as Linux and Mac.
This chapter will shortly explain the C++ classes of the application.
6.7.2 main.cpp
This file is the entry point and contains code that configures the GUI event
loop required by the Qt framework, instantiate the application and opens it
maximized.
6.7.3 Bluetooth
The Bluetooth class is split using two source files for one common header file.
One source file is for the Windows version and one for the Android version.
By using conditional compilation the appropriate source file is selected. This
was necessary as the Qt framework at the time did not support connecting
using Bluetooth on Android directly.
The singleton pattern has been used as explained in chapter 4.7.3.
The Windows and the Android version accomplish the same, but are
very different when it comes to implementation. The Windows version is
implemented using serial port communication as the BMS device will identify
as a virtual COM port. This is done in C++ using the QSerialPort class in
the Qt framework. On the Android version the Java Native Interface (JNI)
104 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
Figure 6.5: An image of the GUI.
6.7. GRAPHICAL USER INTERFACE 105
had to be utilized to invoke Java code and Android classes in the C++
application. More on how the JNI works is explained in chapter 4.7.2.
The most important implemented methods are the search for devices,
connect to device, read and write data methods.
6.7.7 Receive
Data received over Bluetooth is sent to this class for parsing. If a valid
message has been received the appropriate handler is called and the corre-
sponding data and settings updated.
106 CHAPTER 6. SYSTEM DESIGN: SOFTWARE
6.7.8 Send
This class readies data before transmission. A preamble, message ID and
postamble are added. The messages are also converted to binary data before
they are sent with Bluetooth.
Chapter 7
This chapter contains the system verification tests and calibration methods
used. All multimeter measurements were done with the Vichy VC99 multi-
meter.
Table 7.1: Table with DDS peak-to-peak amplitude output between 1 kHz
and 140 kHz.
107
108 CHAPTER 7. SYSTEM VERIFICATION AND CALIBRATION
Figure 7.1: A screenshot from the logic analyzer software from Saleae.
109
110 CHAPTER 7. SYSTEM VERIFICATION AND CALIBRATION
automatically subtracted from the converted sampling data. This has been
implemented and tested. Three hundred samples have been taken before and
after ADC calibration of a constant DC signal and is shown in figure 7.2.
1200,00
1000,00
Converted ADC value
800,00
Not Calibrated
600,00 Calibrated
400,00
200,00
0,00
0,00 50,00 100,00 150,00 200,00 250,00 300,00
Samples
Figure 7.2: Showing the ADC values before and after calibration.
σb = 22.10 (7.1)
σa = 6.37 (7.2)
This means that 99.73 % (3σ) of the values with an expected value of µ
are within:
The corresponding voltage value for ±66.30 and ±19.11 when the ADC
input range is 3.3 V is:
±3σb ±66.30
V3σb = · VADC = ( 12 ) · 3.3V = ±53.4 mV (7.5)
ADC resolution − 1 2 −1
±3σa ±19.11
V3σa = · VADC = ( 12 ) · 3.3V = ±15.7 mV (7.6)
ADC resolution − 1 2 −1
The 3σ limit is almost three and a half time closer to the expected value
µ after calibration.
900
800
Offset Resistance [Ω]
700
600
500
After Calibration
300
200
100
0
0 2000 4000 6000 8000 10000 12000
True Resistance [Ω]
Figure 7.3: Showing the resistance offset before and after system voltage
calibration.
7.6. PHASE CALIBRATION 113
1. Comparator delay.
The excitation signal is sent through the comparator and they split their
ways, which means the delay of the comparator will delay the reference com-
pared to the signal. The comparator MAX941CPA has a propagation delay
of 80 nanoseconds TexasInstruments (2005). By using equation 7.8 the phase
delay at the corresponding frequency f can be found.
delay
phase delay = 1 · 360◦ (7.8)
f
dθ ∆θ
= (7.10)
df 100 kHz − 10 kHz
We can now find the frequency where the phase intersects with the x-axis:
dθ
θ100kHz − (foffset · )=0 (7.11)
df
θ100kHz
=⇒ foffset = dθ
(7.12)
df
dθ
θoffset = (fzero − f ) · (7.14)
df
Figure 7.4 shows the phase before and after the phase calibration on a 678
Ω resistor as function of frequency. The phase calibration must be done on
a resistive object for the calibration to be successful, such as a resistor. The
implemented calibration on the bioimpedance measurement system averages
the phase of 1000 measurements on 10 kHz and 100 kHz.
-80
-70
-60
-50
Phase [deg]
-40
Calculated Phase
-30
Measured Phase
-20
-10
0 1000 2000 3000 4000 5000 6000 7000 8000 9000 10000
0
True Resistance [ Ω]
Figure 7.6: The values measured on egg white and egg yolk.
and the egg was sliced so that it could be more accurately verified that
the egg yolk was correctly discriminated, but most importantly the border
between the egg yolk and egg white. It was not possible by eye measure to
see any inaccurate discrimination of the egg white, egg yolk or the boundary
between.
This chapter summarizes the most important parts of the thesis, the results
and recommendations for future work.
• The system have been made from scratch, making it very flexible and
a good platform for further development.
• The analog front-end has been developed, tested and verified to work.
119
120 CHAPTER 8. SUMMARY, CONCLUSION AND FUTURE WORK
• The prototype were able to discriminate the egg white from the egg
yolk and accurately determine the boundary between.
• The AD9850 DDS chip is not the best DDS considering price and power
consumption. The AD9837 is recommended.
ARM (2012). Procedure Call Standard for the ARM Architecture. ARM.
121
122 BIBLIOGRAPHY
Martin, T. (2009). The Insider’s Guide To The STM32 ARM Based Micro-
controller. Hitex (UK) Ltd.
Microcontroller Code
A.1 Code: C
123
124 APPENDIX A. MICROCONTROLLER CODE
61 ADCx = ADC3 ;
62 break ;
63
64 default :
65 status = 1;
66 break ;
67 }
68
69 if ( status )
70 {
71 DEBUG ( " Invalid ADC enabled " ) ;
72 return ;
73 }
74
75 ADC_Cmd ( ADCx , ENABLE ) ;
76 }
77
78 // Disable ADC .
79 void ADC_Disable ( ADC_t ADC )
80 {
81 ADC_TypeDef * ADCx ;
82 uint8_t status ;
83
84 switch ( ADC )
85 {
86 case ADC_1 :
87 ADCx = ADC1 ;
88 break ;
89
90 case ADC_2 :
91 ADCx = ADC2 ;
92 break ;
93
94 case ADC_3 :
95 ADCx = ADC3 ;
96 break ;
97
98 default :
99 status = 1;
100 break ;
101 }
102
103 if ( status )
104 {
105 DEBUG ( " Invalid ADC disabled " ) ;
106 return ;
107 }
108
109 ADC_Cmd ( ADCx , DISABLE ) ;
110 }
111
112 // Calibrate ADC .
113 void ADC_Calibrate ( ADC_t ADC )
114 {
115 ADC_TypeDef * ADCx ;
116 uint8_t status ;
117
118 switch ( ADC )
119 {
120 case ADC_1 :
121 ADCx = ADC1 ;
122 break ;
126 APPENDIX A. MICROCONTROLLER CODE
123
124 case ADC_2 :
125 ADCx = ADC2 ;
126 break ;
127
128 case ADC_3 :
129 ADCx = ADC3 ;
130 break ;
131
132 default :
133 status = 1;
134 break ;
135 }
136
137 if ( status )
138 {
139 DEBUG ( " Invalid ADC selected for calibration " ) ;
140 return ;
141 }
142
143 // Reset ADCx calibration register .
144 A D C _ R e s e t C a l i b r a t i o n ( ADCx ) ;
145 // Wait until ADCx reset is done .
146 while ( A D C _ G e t R e s e t C a l i b r a t i o n S t a t u s ( ADCx ) == SET ) ;
147
148 // Start ADCx calibration .
149 A D C _ S t a r t C a l i b r a t i o n ( ADCx ) ;
150 // Wait until ADCx calibration is done .
151 while ( A D C _ G e t C a l i b r a t i o n S t a t u s ( ADCx ) == SET ) ;
152 }
153
154 // Enable external triggering of ADC .
155 void A D C _ E x t e r n a l T r i g g e r E n a b l e ( ADC_t ADC )
156 {
157 ADC_TypeDef * ADCx ;
158 uint8_t status ;
159
160 switch ( ADC )
161 {
162 case ADC_1 :
163 ADCx = ADC1 ;
164 break ;
165
166 case ADC_2 :
167 ADCx = ADC2 ;
168 break ;
169
170 case ADC_3 :
171 ADCx = ADC3 ;
172 break ;
173
174 default :
175 status = 1;
176 break ;
177 }
178
179 if ( status )
180 {
181 DEBUG ( " Invalid ADC selected for external trigger enable " ) ;
182 return ;
183 }
184
A.1. CODE: C 127
58 void A D C 1 _ N o r m a l _ I n i t i a l i z e ( void )
59 {
60 A DC _I n it T yp eD e f A D C _ I n i t S t r u c t ur e ;
61
62 // Configure DMA 1 channel 1 in NVIC .
63 N V I C _ D M A 1 _ C H 1 _ I n i t i a l i z e ( NVIC_PRIORITY_3 , N V I C _ S U B _ P R I O R I T Y _ 0 ) ;
64
65 // Configure ADC1 clocks .
66 R C C _ A D C 1 _ I n i t i a l i z e () ;
67
68 // Configure ADC1 GPIO .
69 G P I O _ A D C 1 _ I n i t i a l i z e () ;
70
71 // Configure ADC 1.
72 ADC_Cmd ( ADC1 , DISABLE ) ;
73 ADC_DeInit ( ADC1 ) ;
74 A D C _ I n i t S t r u c t u r e . ADC_Mode = A D C _ M o d e _ R e g S i m u l t ;
75 A D C _ I n i t S t r u c t u r e . A D C _ S c a n C o n v M o d e = DISABLE ;
76 A D C _ I n i t S t r u c t u r e . A D C _ C o n t i n u o u s C o n v M o d e = DISABLE ;
77 ADC_InitStructure . ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1 ;
78 A D C _ I n i t S t r u c t u r e . ADC_DataAlign = A D C _ D a t a A l i g n _ R i g h t ;
79 A D C _ I n i t S t r u c t u r e . A D C _ N b r O f C h a n n e l = 1;
80 ADC_Init ( ADC1 , & A D C _ I n i t S t r u c t u r e ) ;
81
82 // Register channel 15 on ADC1 .
83 A D C _ R e g u l a r C h a n n e l C o n f i g ( ADC1 , ADC_Channel_15 , 1 , A D C _ S a m p l e T i m e _ 7 C y c l e s 5
);
84
85 // Enable DMA on ADC 1.
86 ADC_DMACmd ( ADC1 , ENABLE ) ;
87
88 // Enable ADC 1 to trigger on external interrupts .
89 A D C _ E x t e r n a l T r i g C o n v C m d ( ADC1 , ENABLE ) ;
90
91 // Enable ADC 1.
92 ADC_Enable ( ADC_1 ) ;
93
94 // Calibrate ADC 1.
95 ADC_Calibrate ( ADC_1 ) ;
96
97 // Disable ADC 1.
98 ADC_Disable ( ADC_1 ) ;
99 }
100
101 // Initialize ADC1 to be used to measure on the internal voltage reference .
102 void A D C 1 _ I n t e r n a l R e f _ I n i t i a l i z e ( void )
103 {
104 A DC _I n it T yp eD e f A D C _ I n i t S t r u c t u r e ;
105
106 // Configure ADC1 clocks .
107 R C C _ S y s V o l t a g e _ I n i t i a l i z e () ;
108
109 // Configure ADC 1.
110 ADC_Cmd ( ADC1 , DISABLE ) ;
111 ADC_DeInit ( ADC1 ) ;
112 A D C _ I n i t S t r u c t u r e . ADC_Mode = A D C _ M o d e _ I n d e p e n d e n t ;
113 A D C _ I n i t S t r u c t u r e . A D C _ S c a n C o n v M o d e = DISABLE ;
114 A D C _ I n i t S t r u c t u r e . A D C _ C o n t i n u o u s C o n v M o d e = DISABLE ;
115 ADC_InitStructure . ADC_ExternalTrigConv = ADC_ExternalTrigConv_None ;
116 A D C _ I n i t S t r u c t u r e . ADC_DataAlign = A D C _ D a t a A l i g n _ R i g h t ;
117 A D C _ I n i t S t r u c t u r e . A D C _ N b r O f C h a n n e l = 1;
118 ADC_Init ( ADC1 , & A D C _ I n i t S t r u c t u r e ) ;
A.1. CODE: C 131
119
120 // Register channel 17 on ADC1 .
121 A D C _ R e g u l a r C h a n n e l C o n f i g ( ADC1 , ADC_Channel_17 , 1 ,
ADC_SampleTime_13Cycles5 );
122
123 // Enable ADC 1.
124 ADC_Enable ( ADC_1 ) ;
125
126 // Calibrate ADC 1.
127 ADC_Calibrate ( ADC_1 ) ;
128
129 // Disable ADC 1.
130 ADC_Disable ( ADC_1 ) ;
131
132 // Enable the temperature sensor and internal reference channels .
133 A D C _ T e m p S e n s o r V r e f i n t C m d ( ENABLE ) ;
134 }
132 APPENDIX A. MICROCONTROLLER CODE
58
59 freq = freq / D D S _ C L O C K _ R E S O L U T I O N ;
60 freq = 0 xFFFFFFFF - freq ;
61 d d s C l o c k R e s o l u t i o n M u l t i p l e = freq ;
62
63 DDS_Reset () ;
64
65 // Load frequency word .
66 for ( i = 0; i < 32; i ++)
67 {
68 if ( freq & (1 << i ) )
69 {
70 GPIO_SetBits ( DDS_PORT_D7 , DDS_D7 ) ;
71 }
72 else
73 {
74 GPI O_Rese tBits ( DDS_PORT_D7 , DDS_D7 ) ;
75 }
76
77 GPIO_SetBits ( DDS_PORT_WCLK , DDS_WCLK ) ;
78 GPIO _Reset Bits ( DDS_PORT_WCLK , DDS_WCLK ) ;
79 }
80
81 // Factory internal test bits (2 bits ) must be zero .
82 GPIO _Reset Bits ( DDS_PORT_D7 , DDS_D7 ) ;
83 GPIO_SetBits ( DDS_PORT_WCLK , DDS_WCLK ) ;
84 GPIO _Reset Bits ( DDS_PORT_WCLK , DDS_WCLK ) ;
85
86 GPIO _Reset Bits ( DDS_PORT_D7 , DDS_D7 ) ;
87 GPIO_SetBits ( DDS_PORT_WCLK , DDS_WCLK ) ;
88 GPIO _Reset Bits ( DDS_PORT_WCLK , DDS_WCLK ) ;
89
90 // Power down bit .
91 if ( powerDown & 0 x01 )
92 {
93 GPIO_SetBits ( DDS_PORT_D7 , DDS_D7 ) ;
94 }
95 else
96 {
97 GPIO _Reset Bits ( DDS_PORT_D7 , DDS_D7 ) ;
98 }
99
100 GPIO_SetBits ( DDS_PORT_WCLK , DDS_WCLK ) ;
101 GPIO _Reset Bits ( DDS_PORT_WCLK , DDS_WCLK ) ;
102
103 // Load phase bits .
104 for ( i = 0; i < 5; i ++)
105 {
106 if ( phase & (1 << i ) )
107 {
108 GPIO_SetBits ( DDS_PORT_D7 , DDS_D7 ) ;
109 }
110 else
111 {
112 GPI O_Rese tBits ( DDS_PORT_D7 , DDS_D7 ) ;
113 }
114
115 GPIO_SetBits ( DDS_PORT_WCLK , DDS_WCLK ) ;
116 GPIO _Reset Bits ( DDS_PORT_WCLK , DDS_WCLK ) ;
117 }
118
119 // Load 40 - bit serial word .
A.1. CODE: C 139
61 {
62 D MA _I n it T yp eD e f D M A _ I n i t S t r u c t ur e ;
63
64 // Configure DMA1 clocks .
65 R C C _ D M A 1 _ I n i t i a l i z e () ;
66
67 // Configure DMA1 channel 1.
68 DMA_Cmd ( DMA1_Channel1 , DISABLE ) ;
69 DMA_DeInit ( DMA1_Channel1 ) ;
70 D M A _ I n i t S t r u c t u r e . D M A _ P e r i p h e r a l B a s e A d d r = ( uint32_t )
ADC1_DATA_REGISTER_ADDRESS ;
71 D M A _ I n i t S t r u c t u r e . D M A _ M e m o r y B a s e A d d r = ( uint32_t ) dma4fsAdc1 .
samp lesAdd ress ;
72 D M A _ I n i t S t r u c t u r e . DMA_DIR = D M A _ D I R _ P e r i p h e r a l S R C ;
73 D M A _ I n i t S t r u c t u r e . DM A_Buff erSize = ( uint32_t ) dma4fsAdc1 . nu m be rO f Sa mp l es ;
74 DMA_InitStructure . DMA_PeripheralInc = DMA_PeripheralInc_Disable ;
75 D M A _ I n i t S t r u c t u r e . DMA_MemoryInc = D M A _ M e m o r y I n c _ E n a b l e ;
76 DMA_InitStructure . DMA_PeripheralDataSize =
DMA_PeripheralDataSize_HalfWord ;
77 DMA_InitStructure . DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;
78 D M A _ I n i t S t r u c t u r e . DMA_Mode = DM A _M od e _N or m al ;
79 D M A _ I n i t S t r u c t u r e . DMA_Priority = D M A _ P r i o r i t y _ H i g h ;
80 D M A _ I n i t S t r u c t u r e . DMA_M2M = D M A_ M2 M _D i sa bl e ;
81 DMA_Init ( DMA1_Channel1 , & D M A _ I n i t S t r u c t u r e ) ;
82
83 // Enable DMA 1 channel 1 Transfer Complete and Transfer Error interrupt .
84 DMA_ITConfig ( DMA1_Channel1 , ( DMA_IT_TC | DMA_IT_TE ) , ENABLE ) ;
85 }
86
87 void D M A _ A D C 1 _ N o r m a l _ I n i t i a l i z e ( void )
88 {
89 D MA _I n it T yp eD e f D M A _ I n i t S t r u c t ur e ;
90
91 // Configure DMA1 clocks .
92 R C C _ D M A 1 _ I n i t i a l i z e () ;
93
94 // Configure DMA1 channel 1.
95 DMA_Cmd ( DMA1_Channel1 , DISABLE ) ;
96 DMA_DeInit ( DMA1_Channel1 ) ;
97 D M A _ I n i t S t r u c t u r e . D M A _ P e r i p h e r a l B a s e A d d r = ( uint32_t )
ADC1_DATA_REGISTER_ADDRESS ;
98 D M A _ I n i t S t r u c t u r e . D M A _ M e m o r y B a s e A d d r = dmaNormalAdc1 . sa mplesA ddress ;
99 D M A _ I n i t S t r u c t u r e . DMA_DIR = D M A _ D I R _ P e r i p h e r a l S R C ;
100 D M A _ I n i t S t r u c t u r e . DM A_Buff erSize = dmaNormalAdc1 . nu mb e rO f Sa mp l es ;
101 DMA_InitStructure . DMA_PeripheralInc = DMA_PeripheralInc_Disable ;
102 D M A _ I n i t S t r u c t u r e . DMA_MemoryInc = D M A _ M e m o r y I n c _ E n a b l e ;
103 DMA_InitStructure . DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word ;
104 DMA_InitStructure . DMA_MemoryDataSize = DMA_MemoryDataSize_Word ;
105 D M A _ I n i t S t r u c t u r e . DMA_Mode = DM A _M od e _N or m al ;
106 D M A _ I n i t S t r u c t u r e . DMA_Priority = D M A _ P r i o r i t y _ H i g h ;
107 D M A _ I n i t S t r u c t u r e . DMA_M2M = D M A_ M2 M _D i sa bl e ;
108 DMA_Init ( DMA1_Channel1 , & D M A _ I n i t S t r u c t u r e ) ;
109
110 // Enable DMA 1 channel 1 Transfer Complete and Transfer Error interrupt .
111 DMA_ITConfig ( DMA1_Channel1 , ( DMA_IT_TC | DMA_IT_TE ) , ENABLE ) ;
112 }
113
114 void D MA _A D C1 _E n ab l e ( void )
115 {
116 // Enable DMA 1 channel 1.
117 DMA_Cmd ( DMA1_Channel1 , ENABLE ) ;
118 }
A.1. CODE: C 143
119
120 void D M A _ A D C 1 _ D i s a b l e ( void )
121 {
122 // Disable DMA 1 channel 1.
123 DMA_Cmd ( DMA1_Channel1 , DISABLE ) ;
124 }
125
126 /* DMA_ADC3 */
127 void D M A _ A D C 3 _ I n i t i a l i z e ( void )
128 {
129 D MA _I n it T yp eD e f D M A _ I n i t S t r u c t u r e ;
130
131 // Configure DMA2 clocks .
132 R C C _ D M A 2 _ I n i t i a l i z e () ;
133
134 // Configure DMA2 channel 5.
135 DMA_Cmd ( DMA2_Channel5 , DISABLE ) ;
136 DMA_DeInit ( DMA2_Channel5 ) ;
137 D M A _ I n i t S t r u c t u r e . D M A _ P e r i p h e r a l B a s e A d d r = ( uint32_t )
ADC3_DATA_REGISTER_ADDRESS ;
138 D M A _ I n i t S t r u c t u r e . D M A _ M e m o r y B a s e A d d r = dmaAdc3 . sampl esAddr ess ;
139 D M A _ I n i t S t r u c t u r e . DMA_DIR = D M A _ D I R _ P e r i p h e r a l S R C ;
140 D M A _ I n i t S t r u c t u r e . DM A_Buff erSize = dmaAdc3 . n um b er Of S am pl e s ;
141 DMA_InitStructure . DMA_PeripheralInc = DMA_PeripheralInc_Disable ;
142 D M A _ I n i t S t r u c t u r e . DMA_MemoryInc = D M A _ M e m o r y I n c _ E n a b l e ;
143 DMA_InitStructure . DMA_PeripheralDataSize =
DMA_PeripheralDataSize_HalfWord ;
144 DMA_InitStructure . DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;
145 D M A _ I n i t S t r u c t u r e . DMA_Mode = DM A _M od e _N or m al ;
146 D M A _ I n i t S t r u c t u r e . DMA_Priority = D M A _ P r i o r i t y _ H i g h ;
147 D M A _ I n i t S t r u c t u r e . DMA_M2M = D M A_ M2 M _D i sa bl e ;
148 DMA_Init ( DMA2_Channel5 , & D M A _ I n i t S t r u c t u r e ) ;
149
150 // Enable DMA 2 channel 5 Transfer Complete and Transfer Error interrupt .
151 DMA_ITConfig ( DMA2_Channel5 , ( DMA_IT_TC | DMA_IT_TE ) , ENABLE ) ;
152 }
153
154 void D MA _A D C3 _E n ab le ( void )
155 {
156 // Enable DMA 2 channel 5.
157 DMA_Cmd ( DMA2_Channel5 , ENABLE ) ;
158 }
159
160 void D M A _ A D C 3 _ D i s a b l e ( void )
161 {
162 // Disable DMA 2 channel 5.
163 DMA_Cmd ( DMA2_Channel5 , DISABLE ) ;
164 }
144 APPENDIX A. MICROCONTROLLER CODE
117
118 # ifdef DEBUG_MODE
119 // Check if address is aligned . Must be a multiple of 2.
120 if ( ( address % 2) )
121 {
122 DEBUG ( " Tried to write to unaligned memory " ) ;
123 return FALSE ;
124 }
125 # endif
126
127 flashStatus = F L A S H _ P r o g r a m H a l f W o r d ( address , data ) ;
128 if ( flashStatus != FLAS H_COMP LETE )
129 {
130 DEBUG ( " Failed to write to flash at address : % u " , ( unsigned int )
address ) ;
131 return FALSE ;
132 }
133 return TRUE ;
134 }
135
136 // Write 32 - bit to an address .
137 bool_t F L A S H _ W r i t e 3 2 A d d r e s s ( uint32_t address , uint32_t data )
138 {
139 FLASH_Status flashStatus ;
140
141 # ifdef DEBUG_MODE
142 // Check if address is aligned . Must be a multiple of 2.
143 if ( ( address % 2) )
144 {
145 DEBUG ( " Tried to write to unaligned memory " ) ;
146 return FALSE ;
147 }
148 # endif
149
150 flashStatus = F L A S H _ P r o g r a m W o r d ( address , data ) ;
151 if ( flashStatus != FLAS H_COMP LETE )
152 {
153 DEBUG ( " Failed to write to flash at address : % u " , ( unsigned int )
address ) ;
154 return FALSE ;
155 }
156 return TRUE ;
157 }
158
159 // Write a float to an address .
160 bool_t F L A S H _ W r i t e F l o a t A d d r e s s ( uint32_t address , float data )
161 {
162 FLASH_Status flashStatus ;
163
164 # ifdef DEBUG_MODE
165 // Check if address is aligned . Must be a multiple of 2.
166 if ( ( address % 2) )
167 {
168 DEBUG ( " Tried to write to unaligned memory " ) ;
169 return FALSE ;
170 }
171 # endif
172
173 uint32_t * tempPtr = ( uint32_t *) & data ;
174
175 flashStatus = F L A S H _ P r o g r a m W o r d ( address , * tempPtr ) ;
176 if ( flashStatus != FLAS H_COMP LETE )
150 APPENDIX A. MICROCONTROLLER CODE
177 {
178 DEBUG ( " Failed to write to flash at address : % u " , ( unsigned int )
address ) ;
179 return FALSE ;
180 }
181 return TRUE ;
182 }
183
184 // Write a double to an address .
185 // Due to memory alignment of 16 - bit , 2 bytes is the finest memory
granularity . This means when writing double to memory , address % 2 has
to be 0.
186 bool_t F L A S H _ W r i t e D o u b l e A d d r e s s ( uint32_t address , double data )
187 {
188 FLASH_Status flashStatus ;
189
190 # ifdef DEBUG_MODE
191 // Check if address is aligned . Must be a multiple of 2.
192 if ( ( address % 2) )
193 {
194 DEBUG ( " Tried to write to unaligned memory " ) ;
195 return FALSE ;
196 }
197 # endif
198
199 doubleUint32 temp ;
200 uint32_t MSB , LSB ;
201
202 temp . d = data ;
203
204 // Get LSB .
205 LSB = temp . uint [0];
206
207 // Write LSB to flash .
208 flashStatus = F L A S H _ P r o g r a m W o r d ( address , LSB ) ;
209 if ( flashStatus != FLAS H_COMP LETE )
210 {
211 DEBUG ( " Failed to write to flash at address : % u " , ( unsigned int )
address ) ;
212 return FALSE ;
213 }
214
215 // Increment address .
216 address += 4;
217
218 // Get MSB .
219 MSB = temp . uint [1];
220
221 // Write MSB to flash .
222 flashStatus = F L A S H _ P r o g r a m W o r d ( address , MSB ) ;
223 if ( flashStatus != FLAS H_COMP LETE )
224 {
225 DEBUG ( " Failed to write to flash at address : % u " , ( unsigned int )
address ) ;
226 return FALSE ;
227 }
228
229 return TRUE ;
230 }
231
232 // Reads 16 - bits from address .
233 uint16_t FLASH_Read16 ( uint32_t address )
A.1. CODE: C 151
234 {
235 # ifdef DEBUG_MODE
236 // Check if address is aligned . Must be a multiple of 2.
237 if ( ( address % 2) )
238 {
239 DEBUG ( " Tried to read from unaligned memory " ) ;
240 return 0 xFFFF ;
241 }
242 # endif
243
244 return (*(( uint16_t *) address ) ) ;
245 }
246
247 // Reads 32 - bits from address .
248 uint32_t FLASH_Read32 ( uint32_t address )
249 {
250 # ifdef DEBUG_MODE
251 // Check if address is aligned . Must be a multiple of 2.
252 if ( ( address % 2) )
253 {
254 DEBUG ( " Tried to read from unaligned memory " ) ;
255 return 0 xFFFFFFFF ;
256 }
257 # endif
258
259 return (*(( uint32_t *) address ) ) ;
260 }
261
262 // Reads float from address .
263 float FL AS H _R e ad Fl o at ( uint32_t address )
264 {
265 # ifdef DEBUG_MODE
266 // Check if address is aligned . Must be a multiple of 2.
267 if ( ( address % 2) )
268 {
269 DEBUG ( " Tried to read from unaligned memory " ) ;
270 return 0 xFFFFFFFF ;
271 }
272 # endif
273
274 return (*(( float *) address ) ) ;
275 }
276
277 // Reads double from address .
278 // Due to memory alignment of 16 - bit , 2 bytes is the finest memory
granularity . This means reading from memory , address % 2 has to be 0.
279 double F L A S H _ R e a d D o u b l e ( uint32_t address )
280 {
281 doubleUint32 temp ;
282
283 # ifdef DEBUG_MODE
284 // Check if address is aligned . Must be a multiple of 2.
285 if ( ( address % 2) )
286 {
287 DEBUG ( " Tried to read from unaligned memory " ) ;
288 return 0 x F F F F F F F F F F F F F F F F ;
289 }
290 # endif
291
292 temp . uint [0] = *(( uint32_t *) address ) ;
293 temp . uint [1] = *(( uint32_t *) ( address + 4) ) ;
294
152 APPENDIX A. MICROCONTROLLER CODE
123 G P I O _ E X T I L i n e C o n f i g ( GPIO_PortSourceGPIOE , G PI O _P in S ou r ce 2 ) ;
124 }
125
126 // Initialize PORTB pin GPIO .
127 void G P I O _ P I N _ P O R T B _ O U T _ I n i t i a l i z e ( uint16_t pins )
128 {
129 GPIO_InitTypeDef GPIO_InitStructure ;
130
131 R C C _ A P B 2 P e r i p h C l o c k C m d ( RCC_APB2Periph_GPIOB , ENABLE ) ;
132
133 // Configure GPIO .
134 G P I O _ I n i t S t r u c t u r e . GPIO_Pin = pins ;
135 G P I O _ I n i t S t r u c t u r e . GPIO_Speed = G P I O _ S p e e d _ 5 0 M H z ;
136 G P I O _ I n i t S t r u c t u r e . GPIO_Mode = G P I O _ M o d e _ O u t _ P P ;
137 GPIO_Init ( GPIOB , & G P I O _ I n i t S t r u c t u r e ) ;
138
139 // Clear pins .
140 G P I O _ P I N _ P O R T B _ C l e a r ( pins ) ;
141 }
142
143 // Set PORTB pins .
144 void G P I O _ P I N _ P O R T B _ S e t ( uint16_t pin )
145 {
146 GPIOB - > BSRR = pin ;
147 }
148
149 // Clear PORTB pins .
150 void G P I O _ P I N _ P O R T B _ C l e a r ( uint16_t pin )
151 {
152 GPIOB - > BRR = pin ;
153 }
154
155 // Toggle a single PORTB pin .
156 void G P I O _ P I N _ P O R T B _ T o g g l e ( uint16_t pin )
157 {
158 if (( GPIOB - > ODR & pin ) != ( uint32_t ) Bit_RESET )
159 {
160 // Bit set , clear it .
161 GPIOB - > BRR = pin ;
162 }
163 else
164 {
165 // Bit cleared , set it .
166 GPIOB - > BSRR = pin ;
167 }
168 }
A.1. CODE: C 157
61 }
62 }
63
64 // Set the independent watchdog timeout .
65 // Valid range for prescaler = 4 @ 40 kHz is about 1 to 400 milliseconds .
(0.1 msec resolution ) .
66 void I WD G_ S et Ti m eo ut ( uint16_t milliseconds )
67 {
68 uint16_t temp ;
69
70 // Check if milliseconds is within range . If not , set to default values .
71 if ( milliseconds < 1)
72 {
73 milliseconds = 1;
74 }
75 else if ( milliseconds > 400)
76 {
77 milliseconds = 400;
78 }
79
80 // Calculate watchdog reload value .
81 temp = ( uint16_t ) ((( L S I _ G e t F r e q u e n c y () /4) * milliseconds ) /1000) ;
82
83 // Enable write access to prescaler and reload registers .
84 IWDG_WriteAccessCmd ( IWDG_WriteAccess_Enable );
85
86 // Set reload value .
87 IWDG _SetRe load ( temp ) ;
88
89 // Disable write access to prescaler and reload registers .
90 IWDG_WriteAccessCmd ( IWDG_WriteAccess_Disable );
91 }
92
93 // Halt the independent watchdog when debugging .
94 void I W D G _ H a l t W h e n D e b u g g i n g ( void )
95 {
96 DBGMCU - > CR |= D B G M C U _ C R _ D B G _ I W D G _ S T O P ;
97 }
160 APPENDIX A. MICROCONTROLLER CODE
61 {
62 GPI O_Rese tBits ( GPIOB , GPIO_Pin_1 ) ;
63 }
64 }
65 }
A.1. CODE: C 163
57 }
58
59 // Initialize SysTick interrupt .
60 void N V I C _ S Y S T I C K _ I n i t i a l i z e ( void )
61 {
62 N V I C _ S e t P r i o r i t y ( SysTick_IRQn , 0 x00 ) ;
63 }
64
65 // Initialize TIM 1 channel 1 interrupt .
66 void N V I C _ T I M 1 _ C H 1 _ I n i t i a l i z e ( N VI C _P RI O RI T Y_ t priority , N V I C _ S U B _ P R I O R I T Y _ t
subPriority )
67 {
68 NVIC_InitTypeDef NVIC_InitStructure ;
69
70 // Register with Nested Vectored Interrupt Controller ( NVIC ) .
71 N V I C _ I n i t S t r u c t u r e . NV IC _ IR QC h an n el = TIM1_CC_IRQn ;
72 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l P r e e m p t i o n P r i o r i t y = priority ;
73 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l S u b P r i o r i t y = subPriority ;
74 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l C m d = ENABLE ;
75
76 // Add TIM1_CC_IRQn to NVIC .
77 NVIC_Init (& N V I C _ I n i t S t r u c t u r e ) ;
78 }
79
80 // Initialize TIM 2 channel 1 interrupt .
81 void N V I C _ T I M 2 _ C H 1 _ I n i t i a l i z e ( N VI C _P RI O RI T Y_ t priority , N V I C _ S U B _ P R I O R I T Y _ t
subPriority )
82 {
83 NVIC_InitTypeDef NVIC_InitStructure ;
84
85 // Register with Nested Vectored Interrupt Controller ( NVIC ) .
86 N V I C _ I n i t S t r u c t u r e . NV IC _ IR QC h an n el = TIM2_IRQn ;
87 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l P r e e m p t i o n P r i o r i t y = priority ;
88 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l S u b P r i o r i t y = subPriority ;
89 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l C m d = ENABLE ;
90
91 // Add TIM2_IRQn to NVIC .
92 NVIC_Init (& N V I C _ I n i t S t r u c t u r e ) ;
93 }
94
95 // Initialize TIM 8 channel 1 interrupt .
96 void N V I C _ T I M 8 _ C H 1 _ I n i t i a l i z e ( N VI C _P RI O RI T Y_ t priority , N V I C _ S U B _ P R I O R I T Y _ t
subPriority )
97 {
98 NVIC_InitTypeDef NVIC_InitStructure ;
99
100 // Register with Nested Vectored Interrupt Controller ( NVIC ) .
101 N V I C _ I n i t S t r u c t u r e . NV IC _ IR QC h an n el = TIM8_CC_IRQn ;
102 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l P r e e m p t i o n P r i o r i t y = priority ;
103 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l S u b P r i o r i t y = subPriority ;
104 N V I C _ I n i t S t r u c t u r e . N V I C _ I R Q C h a n n e l C m d = ENABLE ;
105
106 // Add TIM8_CC_IRQn to NVIC .
107 NVIC_Init (& N V I C _ I n i t S t r u c t u r e ) ;
108 }
109
110 // Initialize USART 1 interrupt .
111 void N V I C _ U S A R T 1 _ I n i t i a l i z e ( NV I C_ PR I OR IT Y _t priority , N V I C _ S U B _ P R I O R I T Y _ t
subPriority )
112 {
113 NVIC_InitTypeDef NVIC_InitStructure ;
114
A.1. CODE: C 169
120 }
121
122 // Initialize TIM8 clock .
123 void R C C _ T I M 8 _ I n i t i a l i z e ( void )
124 {
125 // Enable TIM8 clock .
126 R C C _ A P B 2 P e r i p h C l o c k C m d ( RCC_APB2Periph_TIM8 , ENABLE ) ;
127 }
128
129 // Initialize USART1 clock .
130 void R C C _ U S A R T 1 _ I n i t i a l i z e ( void )
131 {
132 // Enable GPIOA , USART1 and AFIO clock .
133 R C C _ A P B 2 P e r i p h C l o c k C m d (( R C C _ A P B 2 P e r i p h _ G P I O A | R C C _ A P B 2 P e r i p h _ U S A R T 1 |
R C C _ A P B 2 P e r i p h _ A F I O ) , ENABLE ) ;
134 }
135
136 // Initialize ZCD clock .
137 void R C C _ Z C D _ I n i t i a l i z e ( void )
138 {
139 // Enable AFIO and GPIOE clock .
140 R C C _ A P B 2 P e r i p h C l o c k C m d (( R C C _ A P B 2 P e r i p h _ G P I O E | R C C _ A P B 2 P e r i p h _ A F I O ) ,
ENABLE ) ;
141 }
174 APPENDIX A. MICROCONTROLLER CODE
61
62 case TIM_13 :
63 TIMx = TIM13 ;
64 break ;
65
66 case TIM_14 :
67 TIMx = TIM14 ;
68 break ;
69
70 default :
71 status = 1;
72 break ;
73 }
74
75 if ( status )
76 {
77 DEBUG ( " Invalid timer enabled " ) ;
78 return ;
79 }
80
81 // Clear timer
82 TIM_ SetCou nter ( TIMx , 0) ;
83
84 // Enable timer
85 TIM_Cmd ( TIMx , ENABLE ) ;
86 }
87
88 // Disable TIM .
89 void TIM_Disable ( TIM_t timer )
90 {
91 TIM_TypeDef * TIMx ;
92 uint8_t status = 0;
93
94 switch ( timer )
95 {
96 case TIM_1 :
97 TIMx = TIM1 ;
98 break ;
99
100 case TIM_2 :
101 TIMx = TIM2 ;
102 break ;
103
104 case TIM_3 :
105 TIMx = TIM3 ;
106 break ;
107
108 case TIM_4 :
109 TIMx = TIM4 ;
110 break ;
111
112 case TIM_5 :
113 TIMx = TIM5 ;
114 break ;
115
116 case TIM_6 :
117 TIMx = TIM6 ;
118 break ;
119
120 case TIM_7 :
121 TIMx = TIM7 ;
122 break ;
A.1. CODE: C 179
123
124 case TIM_8 :
125 TIMx = TIM8 ;
126 break ;
127
128 case TIM_9 :
129 TIMx = TIM9 ;
130 break ;
131
132 case TIM_10 :
133 TIMx = TIM10 ;
134 break ;
135
136 case TIM_11 :
137 TIMx = TIM11 ;
138 break ;
139
140 case TIM_12 :
141 TIMx = TIM12 ;
142 break ;
143
144 case TIM_13 :
145 TIMx = TIM13 ;
146 break ;
147
148 case TIM_14 :
149 TIMx = TIM14 ;
150 break ;
151
152 default :
153 status = 1;
154 break ;
155 }
156
157 if ( status )
158 {
159 DEBUG ( " Invalid timer disabled " ) ;
160 return ;
161 }
162
163 // Disable timer
164 TIM_Cmd ( TIMx , DISABLE ) ;
165 }
166
167 // Set the prescaler .
168 // This parameter can be a number between 0 x0000 and 0 xFFFF
169 void T I M _ S e t P r e s c a l e r ( TIMER_t * tim , uint16_t prescaler )
170 {
171 tim - > prescaler = prescaler ;
172 }
173
174 // Set counting mode up or down .
175 void T I M _ S e t C o u n t e r M o d e ( TIMER_t * tim , TI M_CNT_ MODE_t counterMode )
176 {
177 switch ( counterMode ) {
178 case T IM _C N T_ MO D E_ UP :
179 tim - > counterMode = T I M _ C o u n t e r M o d e _ U p ;
180 break ;
181
182 case T I M _ C N T _ M O D E _ D O W N :
183 tim - > counterMode = T I M _ C o u n t e r M o d e _ D o w n ;
184 break ;
180 APPENDIX A. MICROCONTROLLER CODE
185
186 default :
187 DEBUG ( " Invalid Timer Count Mode selected \ n " ) ;
188 tim - > counterMode = ( uint16_t ) T I M _ C o u n t e r M o d e _ U p ;
189 break ;
190 }
191 }
192
193 // Set the counting period .
194 // This parameter can be a number between 0 x0000 and 0 xFFFF
195 void TIM_SetPeriod ( TIMER_t * tim , uint16_t period )
196 {
197 tim - > period = period ;
198 }
199
200
201 // Set the clock divider .
202 void T I M _ S e t C l o c k D i v i d e r ( TIMER_t * tim , TIM_CLK_DIV_t clockDivider )
203 {
204 switch ( clockDivider ) {
205 case TIM_CLK_DIV_1 :
206 tim - > clockDivider = TIM_CKD_DIV1 ;
207 break ;
208
209 case TIM_CLK_DIV_2 :
210 tim - > clockDivider = TIM_CKD_DIV2 ;
211 break ;
212
213 case TIM_CLK_DIV_4 :
214 tim - > clockDivider = TIM_CKD_DIV4 ;
215 break ;
216
217 default :
218 DEBUG ( " Invalid Timer Clock Divider selected \ n " ) ;
219 tim - > clockDivider = TIM_CKD_DIV1 ;
220 break ;
221 }
222 }
A.1. CODE: C 181
114 {
115 uint32_t max = 0;
116 uint16_t i ;
117
118 for ( i = 0; i < length ; i ++)
119 {
120 // Check if new value if greater than max
121 if ( dataArray [ i ] > max )
122 {
123 max = dataArray [ i ];
124 }
125 }
126 return max ;
127 }
128
129 // Find minimum of an unsigned half - word .
130 uint16_t D S P _ F i n d M i n _ u i 1 6 ( uint16_t * dataArray , uint16_t length )
131 {
132 uint16_t min = 65535;
133 uint16_t i ;
134
135 for ( i = 0; i < length ; i ++)
136 {
137 // Check if new value if less than min
138 if ( dataArray [ i ] < min )
139 {
140 min = dataArray [ i ];
141 }
142 }
143 return min ;
144 }
145
146 // Find minimum of an unsigned word .
147 uint32_t D S P _ F i n d M i n _ u i 3 2 ( uint32_t * dataArray , uint16_t length )
148 {
149 uint32_t min = 65535;
150 uint16_t i ;
151
152 for ( i = 0; i < length ; i ++)
153 {
154 // Check if new value if less than min
155 if ( dataArray [ i ] < min )
156 {
157 min = dataArray [ i ];
158 }
159 }
160 return min ;
161 }
162
163 // Find minimum of a signed half - word .
164 int16_t DS P _F in d Mi n_ i 16 ( int16_t * dataArray , uint16_t length )
165 {
166 int16_t min = 32767;
167 uint16_t i ;
168
169 for ( i = 0; i < length ; i ++)
170 {
171 // Check if new value if less than min
172 if ( dataArray [ i ] < min )
173 {
174 min = dataArray [ i ];
175 }
A.1. CODE: C 199
176 }
177 return min ;
178 }
179
180 // Find maximum of a signed half - word .
181 int16_t DS P _F in d Ma x_ i 16 ( int16_t * dataArray , uint16_t length )
182 {
183 int16_t max = -32768;
184 uint16_t i ;
185
186 for ( i = 0; i < length ; i ++)
187 {
188 // Check if new value if greater than max
189 if ( dataArray [ i ] > max )
190 {
191 max = dataArray [ i ];
192 }
193 }
194 return max ;
195 }
196
197 // Check for maximum saturation in an array by determining a threshold and
how many values at or above this threshold is allowed .
198 uint16_t D S P _ C h e c k S a t u r a t i o n U p p e r A n d M a x ( int16_t * dataArray , uint16_t length
, int16_t maxValue , uint16_t nMaxValuesAllowed , int16_t * maxValueFound )
199 {
200 int16_t max = -32768;
201 uint16_t m a x V a l u e O c c u r e n c e C o u n t e r = 0 , i ;
202
203 // Loop through array
204 for ( i = 0; i < length ; i ++)
205 {
206 // Check if value is greater or equal to threshold value maxValue
207 if ( dataArray [ i ] >= maxValue )
208 {
209 m a x V a l u e O c c u r e n c e C o u n t e r ++;
210 if ( m a x V a l u e O c c u r e n c e C o u n t e r > n M a x V a l u e s A l l o w e d )
211 {
212 * maxValueFound = ( max > dataArray [ i ]) ? max : dataArray [ i ];
213 return 0 xFFFF ;
214 }
215
216 max = dataArray [ i ];
217 }
218 else if ( max < dataArray [ i ])
219 {
220 max = dataArray [ i ];
221 }
222 }
223
224 // Write maximum value
225 * maxValueFound = max ;
226
227 // Return number of values equal or greater than maxValue
228 return m a x V a l u e O c c u r e n c e C o u n t e r ;
229 }
230
231 // Check for minimum saturation in an array by determining a threshold and
how many values at or below this threshold is allowed .
232 uint16_t D S P _ C h e c k S a t u r a t i o n L o w e r A n d M i n ( int16_t * dataArray , uint16_t length
, int16_t minValue , uint16_t nMinValuesAllowed , int16_t * minValueFound )
233 {
200 APPENDIX A. MICROCONTROLLER CODE
294 {
295 sum += dataArray [ i ];
296 }
297
298 // Calculate average .
299 average = ( uint32_t ) ( sum / length ) ;
300
301 return average ;
302 }
303
304 // Find average of a signed half - word array , rounds down .
305 int16_t D S P _ A v e r a g e V a l u e O f A r r a y _ i 1 6 ( int16_t * dataArray , uint16_t length )
306 {
307 uint16_t i ;
308 int16_t average ;
309 int32_t sum = 0;
310
311 // Calculate average .
312 for ( i = 0; i < length ; i ++)
313 {
314 sum += dataArray [ i ];
315 }
316
317 // Calculate average .
318 average = sum / length ;
319
320 return average ;
321 }
322
323 // Find average of a signed word array , rounds down .
324 int D S P _ A v e r a g e V a l u e O f A r r a y _ i 3 2 ( int * dataArray , uint16_t length )
325 {
326 uint16_t i ;
327 int average ;
328 int64_t sum = 0;
329
330 // Calculate average .
331 for ( i = 0; i < length ; i ++)
332 {
333 sum += dataArray [ i ];
334 }
335
336 // Calculate average .
337 average = ( int ) ( sum / length ) ;
338
339 return average ;
340 }
341
342 // Find average of a float array .
343 float D S P _ A v e r a g e V a l u e O f A r r a y _ i 1 6 _ f ( int16_t * dataArray , uint16_t length )
344 {
345 uint16_t i ;
346 float average ;
347 int32_t sum = 0;
348
349 // Calculate average .
350 for ( i = 0; i < length ; i ++)
351 {
352 sum += dataArray [ i ];
353 }
354
355 // Calculate average .
202 APPENDIX A. MICROCONTROLLER CODE
418 {
419 // Vpeak = Vrms * sqrt (2)
420 return rmsSine * M_SQRT2 ;
421 }
422
423 // Convert from RMS to sinusoidal peak - to - peak .
424 float D S P _ R m s T o S i n e P e a k T o P e a k ( float rmsSine )
425 {
426 // Vpeak - peak = Vrms * sqrt (2) * 2
427 return rmsSine * M_SQRT2 * 2;
428 }
429
430 // Offset an array with offset .
431 void D SP _O f fs et A rr ay ( int16_t * inputDataArray , uint16_t length , int16_t
offset )
432 {
433 uint16_t i ;
434
435 for ( i = 0; i < length ; i ++)
436 {
437 inpu tDataA rray [ i ] -= offset ;
438 }
439 }
440
441 // Converts a signal to a 1 - bit signal with the threshold threshold .
442 void D S P _ A n a l o g T o D i g i t a l A r r a y ( int16_t * inputArray , uint16_t length ,
uint16_t threshold )
443 {
444 uint16_t i ;
445
446 for ( i = 0; i < length ; i ++)
447 {
448 // Give input array boolean values . This depends if the current value
is above or below given threshold
449 if ( (( i ) % ( S AM PL I NG _ FA CT O R /2) ) == 0 )
450 {
451 if ( i == 0)
452 {
453 continue ;
454 }
455 inputArray [ i ] = -1;
456 }
457 else if ( inputArray [ i ] >= threshold )
458 {
459 inputArray [ i ] = 1;
460 }
461 else
462 {
463 inputArray [ i ] = -1;
464 }
465 }
466 }
467
468 // Digital debouncing filter .
469 uint16_t D S P _ S q u a r e D e b o u n c e ( int16_t * ADCdataRef , uint16_t samples , uint8_t
debounceLevel )
470 {
471 uint8_t j ;
472 uint16_t i , d e bo un c eC ou n te r = 0;
473
474 // Loop through samples
475 for ( i = 1; i < samples ; i ++)
204 APPENDIX A. MICROCONTROLLER CODE
476 {
477 // Check for change in level
478 if ( ADCdataRef [ i ] != ADCdataRef [ i - 1] )
479 {
480 // Apply debounce filter
481 for ( j = 0; j < debounceLevel ; j ++)
482 {
483 if ( ADCdataRef [ i + j + 1] != ADCdataRef [ i ] )
484 {
485 ADCdataRef [ i ] = ADCdataRef [ i - 1];
486 d eb ou n ce C ou nt e r ++;
487 }
488 }
489 }
490 }
491 return d e bo un c eC ou n te r ;
492 }
493
494 // Initialize a FIR filter instance .
495 void F I R _ i n i t i a l i z e N e w I n s t a n c e A n d S t a t e A r r a y _ q 3 1 ( a r m _ f i r _ i n s t a n c e _ q 3 1 *
firInstance , q31_t * stateArray , q31_t * coefficients , uint16_t
numberOfTaps , uint32_t blockSize )
496 {
497 a r m _ f i r _ i n i t _ q 3 1 ( firInstance , numberOfTaps , coefficients , stateArray ,
blockSize ) ;
498 }
499
500 // FIR filter Q1 .31 fixed - point number values .
501 void FIR_filterQ31 ( a r m _ f i r _ i n s t a n c e _ q 3 1 * firInstance , q31_t * inputArray ,
q31_t * outputArray , uint16_t samples , uint32_t blockSize )
502 {
503 // Variables
504 uint16_t i ;
505
506 for ( i = 0; i < ( samples / blockSize ) ; i ++)
507 {
508 arm_fir_q31 ( firInstance , & inputArray [0] + ( i * blockSize ) , &
outputArray [0] + ( i * blockSize ) , blockSize ) ;
509 }
510 }
511
512 // Lock - in amplifier a signal and reference with a FIR low pass filter .
513 void DSP_LockIn ( a r m _ f i r _ i n s t a n c e _ q 3 1 * firInstance , int16_t * signal ,
int16_t * reference , uint16_t samples , q31_t * outputInPhase , q31_t *
outp utOutP hase )
514 {
515 uint16_t i ;
516
517 // Mix signal and ref . 0 and 90 degree ref .
518 for ( i = 0; i < samples - ( S AM P LI NG _ FA CT O R / 4) ; i ++)
519 {
520 tempInPhase [ i ] = ( q31_t ) ( signal [ i ] * reference [ i ]) ;
521 tempOutPhase [ i ] = ( q31_t ) ( signal [ i ] * reference [ i + ( SA M PL IN G _F AC T OR
/ 4) ]) ;
522 }
523
524 // FIR filter 2 f component in product , DC should be remaining
525 FIR_filterQ31 ( firInstance , & tempInPhase [0] , & outputInPhase [0] , samples -
( SA M PL IN G _F AC T OR / 4) , BLOCK_SIZE ) ;
526 FIR_filterQ31 ( firInstance , & tempOutPhase [0] , & out putOut Phase [0] , samples
- ( SA MP L IN G _F AC T OR / 4) , BLOCK_SIZE ) ;
527 }
A.1. CODE: C 205
528
529 // Prepare sampled data for lock - in , do lock - in with FIR filter and return
the IQ demodulated values .
530 void DSP_main ( int16_t * ADCdataSignal , int16_t * ADCdataRef , q31_t * I ,
q31_t * Q )
531 {
532 uint16_t de bo u nc eC o un te r = 0;
533 int16_t IMax , IMin , average ;
534 float averageFloat ;
535
536 // Check for upper and lower saturation on signal
537 if ( D S P _ C h e c k S a t u r a t i o n U p p e r A n d M a x (& ADCdataSignal [0] , NUMBER_OF_SAMPLES ,
4095 , NUMBER_OF_PERIODS , & IMax ) == 0 xFF )
538 {
539 DEBUG ( " ERROR : D S P _ C h e c k S a t u r a t i o n U p p e r : FAILED \ n \ n " ) ;
540 }
541 else
542 {
543 DEBUG ( " D S P _ C h e c k S a t u r a t i o n U p p e r : PASSED \ n \ n " ) ;
544 }
545
546 DEBUG ( " IMax : % i \ n \ n " , IMax ) ;
547
548 if ( D S P _ C h e c k S a t u r a t i o n L o w e r A n d M i n (& ADCdataSignal [0] , NUMBER_OF_SAMPLES ,
0 , NUMBER_OF_PERIODS , & IMin ) == 0 xFF )
549 {
550 DEBUG ( " ERROR : D S P _ C h e c k S a t u r a t i o n L o w e r : FAILED \ n \ n " ) ;
551 }
552 else
553 {
554 DEBUG ( " D S P _ C h e c k S a t u r a t i o n L o w e r : PASSED \ n \ n " ) ;
555 }
556
557 DEBUG ( " IMin : % i \ n \ n " , IMin ) ;
558
559 // Convert reference array to boolean values
560 D S P _ A n a l o g T o D i g i t a l A r r a y (& ADCdataRef [0] , NUMBER_OF_SAMPLES ,
REF_DATA_ANALOG_TO_DIGITAL_THRESHOLD );
561 DEBUG ( " D S P _ A n a l o g T o D i g i t a l A r r a y : DONE \ n \ n " ) ;
562
563 // Create a phase shift on sqaure reference by shifting samples
564 D S P _ S h i f t S q u a r e R e f e r e n c e (& ADCdataRef [0] , NUMBER_OF_SAMPLES ,
SHIFT_REFERENCE_BY_SAMPLES , S AM PL I NG _ FA CT O R ) ;
565
566 // Debounce the square reference signal .
567 d eb ou n ce C ou nt e r = D S P _ S q u a r e D e b o u n c e (& ADCdataRef [0] , NUMBER_OF_SAMPLES ,
1) ;
568 DEBUG ( " Debounced samples : % u \ n " , de bo u nc eC o un t er ) ;
569
570 // Get duty cycle of the square reference signal .
571 averageFloat = D S P _ R e f e r e n c e D u t y C y c l e (& ADCdataRef [0] , N U M B E R _ O F _ S A M P L E S ) ;
572
573 // Check if reference duty cycle is within some limits
574 if ( R E F _ D U T Y _ C Y C L E _ L O W E R _ T H R E S H O L D <= averageFloat && averageFloat <=
REF_DUTY_CYCLE_UPPER_THRESHOLD )
575 {
576 DEBUG ( " Reference duty cycle within limits %1.2 f <-> %1.2 f : %1.3 f \ n \ n " ,
R EF _D U TY _C Y CL E _L OW E R_ TH R ES HO L D , R EF _D U TY _ CY CL E _U PP E R_ TH R ES H OL D ,
averageFloat ) ;
577 }
578 else
579 {
206 APPENDIX A. MICROCONTROLLER CODE
580 DEBUG ( " ERROR : Reference duty cycle outside limits %1.2 f <-> %1.2 f :
%1.3 f \ n \ n " , RE F _D UT Y _C YC L E_ LO W ER _ TH RE S HO LD ,
R EF _D U TY _C Y CL E_ U PP E R_ TH R ES HO L D , averageFloat ) ;
581 }
582
583 // Get signal array average
584 average = D S P _ A v e r a g e V a l u e O f A r r a y _ i 1 6 (& ADCdataSignal [0] ,
NUMBER_OF_SAMPLES );
585
586 DEBUG ( " Offset / DC : % i \ n \ n " , average ) ;
587
588 // Remove offset ( minimum ) and get average to be y = 0
589 D SP _O f fs e tA rr a y (& ADCdataSignal [0] , NUMBER_OF_SAMPLES , average ) ;
590
591 // Line below only for debugging
592 average = D S P _ A v e r a g e V a l u e O f A r r a y _ i 1 6 (& ADCdataSignal [0] ,
NUMBER_OF_SAMPLES );
593
594 DEBUG ( " Signal offset after Offset / DC corrections : % i \ n \ n " , average ) ;
595
596 F I R _ i n i t i a l i z e N e w I n s t a n c e A n d S t a t e A r r a y _ q 3 1 (& FIR_instance , & te stStat eArray
[0] , & FIR_coef [0] , NUMBER_OF_TAPS , BLOCK_SIZE ) ;
597
598 // Get I and Q
599 DSP_LockIn (& FIR_instance , & ADCdataSignal [0] , & ADCdataRef [0] ,
NUMBER_OF_SAMPLES , & I [0] , & Q [0]) ;
600
601 return ;
602 }
603
604 // Convert an array of samples from 4 fs methods to I and Q values .
605 void D SP _S a mp le s To I Q ( int16_t * samples , int16_t * I , int16_t * Q , uint16_t
periods )
606 {
607 uint16_t i ;
608
609 for ( i = 0; i < periods ; i ++)
610 {
611 D S P _ F o u r S a m p l e s T o I Q (& samples [( i << 2) ] , & I [ i ] , & Q [ i ]) ;
612 }
613 }
614
615 // Convert 4 samples in a period using 4 fs method calculate the I and Q value
.
616 void D S P _ F o u r S a m p l e s T o I Q ( int16_t * samples , int16_t * I , int16_t * Q )
617 {
618 * I = samples [0] - samples [2];
619 * Q = samples [3] - samples [1];
620 }
621
622 // Calculate the amplitude from the I and Q value .
623 double D S P _ I Q T o A m p l i t u d e ( int16_t I , int16_t Q )
624 {
625 double amplitude ;
626
627 amplitude = sqrt ( ( double ) I *( double ) I + ( double ) Q *( double ) Q ) ;
628
629 return amplitude ;
630 }
631
632 // Calculate the phase from the I and Q value .
633 // Returns the phase with -90 degrees offset .
A.1. CODE: C 207
297 }
298 }
299 }
300
301 // Two point phase calibration .
302 void M e a s u r e _ C a l i b r a t e _ C a l c u l a t e P h a s e E r r o r S l o p e ( uint32_t frequency1 ,
uint32_t frequency2 )
303 {
304 int i ;
305 double phase1 ;
306 double phase2 ;
307 double deltaPhase ;
308 uint32_t fOffset ;
309
310 // Dummy sample .
311 M e a s u r e _ 4 f s _ C S F M ( frequency2 , 10) ;
312
313 phase2 = 0;
314
315 // Get phase at frequency 2.
316 for ( i = 0; i < 100; i ++)
317 {
318 M e a s u r e _ 4 f s _ C S F M ( frequency2 , 10) ;
319 phase2 += phase ;
320 }
321
322 phase1 = 0;
323
324 // Get phase at frequency 1.
325 for ( i = 0; i < 100; i ++)
326 {
327 M e a s u r e _ 4 f s _ C S F M ( frequency1 , 10) ;
328 phase1 += phase ;
329 }
330
331 // Average the collected phases .
332 phase2 /= 100;
333 phase1 /= 100;
334
335 // Calculate phase difference .
336 deltaPhase = phase2 - phase1 ;
337
338 // Calculate phase error slope .
339 c a l i b r a t i o n P h a s e S l o p e = deltaPhase / ( frequency2 - frequency1 ) ;
340
341 // Calculate the frequency where the slope intersects with phase = 0.
342 fOffset = ( uint32_t ) ( phase2 / c a l i b r a t i o n P h a s e S l o p e ) ;
343 c a l i b r a t i o n Z e r o P h a s e F r e q u e n c y = frequency2 - fOffset ;
344 }
345
346 // Corrects the measured phase if calibration have been done .
347 double M e a s u r e _ C a l i b r a t e _ C o r r e c t P h a s e ( uint32_t m e a s u r e m e n t F r e q u e n c y )
348 {
349 if ( c a l i b r a t i o n Z e r o P h a s e F r e q u e n c y == 0)
350 {
351 // No calibration has been done .
352 return 0;
353 }
354
355 // Phase offset as calculated by calibration .
356 return (( double ) ( (( double ) c a l i b r a t i o n Z e r o P h a s e F r e q u e n c y - ( double )
measurementFrequency ) * calibrationPhaseSlope ));
A.1. CODE: C 217
357 }
358
359 // Calibrate system voltage .
360 void M e a s u r e _ C a l i b r a t e _ S y s t e m V o l t a g e ( void )
361 {
362 uint8_t i ;
363 uint16_t adc Sample Value [50];
364
365 // Initialize ADC to read internal voltage .
366 A D C 1 _ I n t e r n a l R e f _ I n i t i a l i z e () ;
367
368 // Enable ADC1 .
369 ADC_Enable ( ADC_1 ) ;
370
371 size_t n u m b e r O f E l e m e n t s = N U M _ O F _ E L E M E N T S _ I N _ A R R A Y ( adc Sample Value ) ;
372
373 Timer_Delay (10) ;
374
375 for ( i = 0; i < n u m b e r O f E l e m e n t s ; i ++)
376 {
377 // Get single sample .
378 adcS ampleV alue [ i ] = A D C _ G e t S i n g l e C o n v e r s i o n ( ADC_1 ) ;
379
380 adcS ampleV alue [ i ] = ( uint16_t ) (((( double ) TWELVE_BIT / ( double )
adc Sample Value [ i ]) * ( double ) I NTERNA L_V_RE F ) * 1000) ;
381
382 // 1 ms delay between samples .
383 Timer_Delay (1) ;
384 }
385
386 // Calculate system voltage from the last 48 samples of the total 50 taken
. The first two samples are often way off .
387 Vcc_MilliVolt = D S P _ A v e r a g e V a l u e O f A r r a y _ u i 1 6 (& adc Sample Value [2] ,
n u m b e r O f E l e m e n t s - 2) ;
388
389 ADC_Disable ( ADC_1 ) ;
390 A D C _ D i s a b l e I n t e r n a l R e f () ;
391 }
392
393 // Get system voltage .
394 uint16_t M e a s u r e _ G e t S y s t e m V o l t a g e ( void )
395 {
396 return Vcc_MilliVolt ;
397 }
398
399 // Get excitation voltage DC offset .
400 int16_t M e a s u r e _ E x c i t a t i o n _ G e t D c O f f s e t ( void )
401 {
402 return exDcOffset ;
403 }
404
405 // Set excitation voltage peak - to - peak in millivolts .
406 void M e a s u r e _ S e t E x c i t a t i o n P e a k T o P e a k M i l l i v o l t s ( uint16_t exPtoPmV )
407 {
408 e x P e a k T o P e a k M i l l i v o l t s = exPtoPmV ;
409 }
410
411 // Get pointer to 4 fs data array .
412 int16_t * M e a s u r e _ 4 f s _ G e t D a t a A r r a y P o i n t e r ( void )
413 {
414 return samplesPtr ;
415 }
218 APPENDIX A. MICROCONTROLLER CODE
416
417 // Convenience function to set the temporary data array pointer .
418 int16_t * M e a s u r e _ 4 f s _ S e t T e m p D a t a A r r a y P o i n t e r ( int16_t * sample sPoint er )
419 {
420 samp lesTem pPtr = s amples Pointe r ;
421 return samp lesTem pPtr ;
422 }
423
424 // Get the temporary data array pointer .
425 int16_t * M e a s u r e _ 4 f s _ G e t T e m p D a t a A r r a y P o i n t e r ()
426 {
427 return samp lesTem pPtr ;
428 }
429
430 // Set the number of samples to sample .
431 void M e a s u r e _ 4 f s _ S e t N u m b e r O f S a m p l e s ( uint16_t samplesToSet )
432 {
433 n um be r Of S am pl e s = samplesToSet ;
434 }
435
436 // Get the number of samples set .
437 uint16_t M e a s u r e _ 4 f s _ G e t N u m b e r O f S a m p l e s ( void )
438 {
439 return n u mb er O fS am p le s ;
440 }
441
442 // Measure the excitation voltage .
443 void M e a s u r e _ E x c i t a t i o n _ U p d a t e P a r a m e t e r s ( uint32_t s i gn al F re qu e nc y )
444 {
445 // Change DDS frequency if needed .
446 if ( si gn a lF re q ue nc y != D D S _ G e t F r e q u e n c y () )
447 {
448 DDS_Config ( signalFrequency , 0 , FALSE ) ;
449 }
450
451 periods = M E A S U R E _ E X C I T A T I O N _ P E R I O D S ;
452
453 // Calculate sampling timer parameters .
454 M e a s u r e _ C a l c u l a t e _ S a m p l i n g P a r a m e t e r s (( uint32_t ) ( D D S _ G e t F r e q u e n c y F l o a t ()
+ ( float ) 0.5) , ( uint32_t ) ( ( D D S _ G e t F r e q u e n c y F l o a t () *
M E A S U R E _ E X C I T A T I O N _ S A M P L E S _ P E R _ P E R I O D ) + ( float ) 0.5) , FALSE ) ;
455
456 // Set sampling buffer .
457 D M A _ S e t S a m p l i n g B u f f e r ( D M A _ G e t A d c 3 S t r u c t () , & samples [0]) ;
458 D M A _ S e t T r a n s f e r D o n e ( D M A _ G e t A d c 3 S t r u c t () , FALSE ) ;
459
460 // Set number of samples to sample .
461 D M A _ S e t N u m b e r O f S a m p l e s ( D M A _ G e t A d c 3 S t r u c t () , M E A S U R E _ E X C I T A T I O N _ P E R I O D S *
MEASURE_EXCITATION_SAMPLES_PER_PERIOD );
462
463 D M A _ A D C 3 _ I n i t i a l i z e () ;
464
465 // Configure timer .
466 TIM_Disable ( TIM_8 ) ;
467 T I M _ S e t P r e s c a l e r ( T I M 8 _ C H 1 _ G e t S t r u c t () , time rPresc aler ) ;
468 TIM_SetPeriod ( T I M 8 _ C H 1 _ G e t S t r u c t () , timerPeriod ) ;
469 T I M _ S e t C l o c k D i v i d e r ( T I M 8 _ C H 1 _ G e t S t r u c t () , TIM_CLK_DIV_1 ) ;
470 T I M _ S e t C o u n t e r M o d e ( T I M 8 _ C H 1 _ G e t S t r u c t () , T IM _ CN T_ M OD E _U P ) ;
471 T I M 8 _ C H 1 _ I n i t i a l i z e () ;
472
473 // Enable DMA , ADC and Timer .
474 D MA _A D C3 _ En ab l e () ;
A.1. CODE: C 219
528
529 // When signal is very low close to noise floor , set I and Q average to
0.
530 if ( ( abs ( IAvg ) < 10) && ( abs ( QAvg ) < 10) )
531 {
532 IAvg = 0;
533 QAvg = 0;
534 }
535
536 // Calculate I and Q minimum and maximum values .
537 IMin = DS P_ F in dM i n_ i1 6 (& I [0] , periods ) ;
538 QMin = DS P_ F in dM i n_ i1 6 (& Q [0] , periods ) ;
539 IMax = DS P_ F in dM a x_ i1 6 (& I [0] , periods ) ;
540 QMax = DS P_ F in dM a x_ i1 6 (& Q [0] , periods ) ;
541
542 DEBUG ( " \ nImin : % i \ nImax : % i \ nQmin : % i \ nQmax : % i \ nIdelta : % i \ nQdelta : %
i " , IMin , IMax , QMin , QMax , IMax - IMin , QMax - QMin ) ;
543
544 // IQ Amplitude and Phase
545 IQPeakToPeak = D S P _ I Q T o A m p l i t u d e ( IAvg , QAvg ) ;
546 IQAmplitude = IQPeakToPeak / 2;
547 I Q A m p l i t u d e M i l l i V o l t s = C o m m o n _ F r o m X B i t s (( uint32_t ) IQAmplitude , 12 , (
uint32_t ) M e a s u r e _ G e t S y s t e m V o l t a g e () ) ;
548 I Q P e a k T o P e a k M i l l i V o l t s = I Q A m p l i t u d e M i l l i V o l t s * 2;
549
550 IQPhase = DSP_IQToPhase ( IAvg , QAvg ) ;
551 I QR ms M il l iV ol t s = D S P _ S i n e P e a k T o R m s (( double ) I Q A m p l i t u d e M i l l i V o l t s ) ;
552
553 DEBUG ( " \ nPeak to Peak ( mV ) : % u \ nAmplitude ( mV ) : % u \ nRMS ( mV ) : %4.4 f \
nPhase : %3.3 f \ nPhase Calibrated : %3.3 f \ nSignal Frequency : %7.3 f " ,
IQPeakToPeakMilliVolts , IQAmplitudeMilliVolts , IQRmsMilliVolts ,
IQPhase , IQPhaseCalibrated , D D S _ G e t F r e q u e n c y F l o a t () ) ;
554 }
555 else
556 {
557 // Calculate for normal
558 }
559 }
560
561 // Convenience function to calculate I to milliamperes .
562 double I m m i t t a n c e _ C a l c u l a t e _ I T o C u r r e n t M i l l i A m p e r e ( int16_t I )
563 {
564 iI = I m m i t t a n c e _ C a l c u l a t e _ I Q T o C u r r e n t M i l l i A m p e r e ( I ) ;
565 return iI ;
566 }
567
568 // Convenience function to calculate Q to milliamperes .
569 double I m m i t t a n c e _ C a l c u l a t e _ Q T o C u r r e n t M i l l i A m p e r e ( int16_t Q )
570 {
571 iQ = I m m i t t a n c e _ C a l c u l a t e _ I Q T o C u r r e n t M i l l i A m p e r e ( Q ) ;
572 return iQ ;
573 }
574
575 // Calculate I or Q to milliampere .
576 double I m m i t t a n c e _ C a l c u l a t e _ I Q T o C u r r e n t M i l l i A m p e r e ( int16_t IorQ )
577 {
578 double temp ;
579
580 // ADC values to mV
581 temp = ( double ) C o m m o n _ F r o m X B i t s S i g n e d ( IorQ , 12 , ( uint32_t )
M e a s u r e _ G e t S y s t e m V o l t a g e () ) ;
582
A.1. CODE: C 221
645
646 // Calculates Impedance , Admittance , Resistance , Conductance , Reactance and
Susceptance .
647 void I m m i t t a n c e _ C a l c u l a t e _ I m m i t t a n c e P a r a m e t e r s ( void )
648 {
649 double dividend ;
650 double phaseOffset ;
651
652 // G : Conductance : [ uS ] = iI [ nA ] / U [ mV ]
653 G = I m m i t t a n c e _ G e t C u r r e n t I N a n o A m p e r e () / ( double ) e x P e a k T o P e a k M i l l i v o l t s ;
654
655 // B : Susceptance : [ uS ] = iQ [ nA ] / U [ mV ]
656 B = I m m i t t a n c e _ G e t C u r r e n t Q N a n o A m p e r e () / ( double ) e x P e a k T o P e a k M i l l i v o l t s ;
657
658 // Calculate dividend once .
659 dividend = ( ( G * G ) + ( B * B ) ) ;
660
661 // R : Resistance : [ $ \ Omega$ ] = [ S ] / [ S ^2] = 1 / [ S ]
662 R = G / dividend ;
663
664 // X : Reactance : [ X ] = -[ S ] / [ S ^2] = 1 / [ S ]
665 X = -B / dividend ;
666
667 // Scale resistance and reactance from micro Ohm to Ohm .
668 R *= 1000000;
669 X *= 1000000;
670
671 // Z : Impedance : Z = R + jX
672 // | Z | : Modulus / Amplitude : | Z | = $sqrt ( R ^2 + X ^2 ) $ .
673 Z = sqrtf ( ( R * R ) + ( X * X ) ) ;
674
675 // Y : Admittance : Y = G + jB
676 // | Y | : Modulus / Amplitude : | Y | = $sqrt ( G ^2 + B ^2 ) $ .
677 Y = sqrtf ( ( G * G ) + ( B * B ) ) ;
678
679 // Calculate phase .
680 phase = DSP_RadToDeg ( atan2 (X , R ) ) ;
681
682 // Get phase to correct with .
683 phaseOffset = M e a s u r e _ C a l i b r a t e _ C o r r e c t P h a s e ( D D S _ G e t F r e q u e n c y () ) ;
684
685 // Correct for calibrated phase offset .
686 R = Z * cos ( DSP_DegToRad ( phase + phaseOffset ) ) ;
687 X = Z * sin ( DSP_DegToRad ( phase + phaseOffset ) ) ;
688 Z = sqrtf ( ( R * R ) + ( X * X ) ) ;
689
690 // Calculate dividend once .
691 dividend = ( ( R * R ) + ( X * X ) ) ;
692
693 // Admittance parameters in micro Siemens .
694 G = ( R * 1000) / ( dividend / 1000) ;
695 B = ( - X * 1000) / ( dividend / 1000) ;
696 Y = sqrtf ( ( G * G ) + ( B * B ) ) ;
697
698 // Update phase .
699 phase += phaseOffset ;
700 }
701
702 // Gets the last calculated phase .
703 double I m m i t t a n c e _ G e t P h a s e ( void )
704 {
705 return phase ;
A.1. CODE: C 223
706 }
707
708 // Gets the last calculated impedance modulus .
709 double I m m i t t a n c e _ G e t I m p e d a n c e M o d u l u s ( void )
710 {
711 return Z ;
712 }
713
714 // Gets the last calculated admittance modulus in microsiemens .
715 double I m m i t t a n c e _ G e t A d m i t t a n c e M o d u l u s I n M i c r o S i e m e n s ( void )
716 {
717 return Y ;
718 }
719 // Gets the last calculated resistance .
720 double I m m i t t a n c e _ G e t R e s i s t a n c e ( void )
721 {
722 return R ;
723 }
724
725 // Gets the last calculated reactance .
726 double I m m i t t a n c e _ G e t R e a c t a n c e ( void )
727 {
728 return X ;
729 }
730
731 // Gets the last calculated conductance in microsiemens .
732 double I m m i t t a n c e _ G e t C o n d u c t a n c e I n M i c r o S i e m e n s ( void )
733 {
734 return G ;
735 }
736
737 // Gets the last calculated susceptance in microsiemens .
738 double I m m i t t a n c e _ G e t S u s c e p t a n c e I n M i c r o S i e m e n s ( void )
739 {
740 return B ;
741 }
742
743 // Calculate all currents and immittance parameters from the current I and Q
average values .
744 void I m m i t t a n c e _ S t a r t ( void )
745 {
746 // Calculate current I component .
747 I m m i t t a n c e _ C a l c u l a t e _ I T o C u r r e n t M i l l i A m p e r e ( IAvg ) ;
748
749 // Calculate current Q component .
750 I m m i t t a n c e _ C a l c u l a t e _ Q T o C u r r e n t M i l l i A m p e r e ( QAvg ) ;
751
752 // Calculate current peak - to - peak .
753 I m m i t t a n c e _ C a l c u l a t e _ C u r r e n t P e a k T o P e a k M i l l i A m p e r e () ;
754
755 // Calculate immittance parameters .
756 I m m i t t a n c e _ C a l c u l a t e _ I m m i t t a n c e P a r a m e t e r s () ;
757 }
224 APPENDIX A. MICROCONTROLLER CODE
59 // Get message ID .
60 temp = Q8_FifoRead ( U S A R T 1 _ G e t R e c e i v e Q u e u e () ) ;
61
62 // Handle received message .
63 switch ( temp )
64 {
65 case M E A S U R E M E N T _ S E T T I N G S :
66 // Get message payload data .
67 for ( i = 0; i < 4; i ++)
68 {
69 payload [ i ] = Q8_FifoRead ( U S A R T 1 _ G e t R e c e i v e Q u e u e () ) ;
70 }
71
72 // Get end of message and check if valid .
73 temp = Q8_FifoRead ( U S A R T 1 _ G e t R e c e i v e Q u e u e () ) ;
74 if ( temp == MESSAGE_END )
75 {
76 // End of message found , parse message .
77 p a r s e M e s s a g e M e a s u r e m e n t S e t t i n g s (& payload [0]) ;
78 }
79 break ;
80
81 default :
82 // Do nothing , continue read from buffer and look for valid
messages .
83 break ;
84 }
85 }
86 }
87 }
A.1. CODE: C 227
61
62 // Check if queue is empty .
63 bool_t Q16_isEmpty ( Q16 * queue )
64 {
65 if ( queue - > indexRead == queue - > indexWrite )
66 {
67 return TRUE ;
68 }
69 else
70 {
71 return FALSE ;
72 }
73 }
74
75 // Find how many elements in queue .
76 uint16_t Q16_Count ( Q16 * queue )
77 {
78 return ( queue - > indexWrite - queue - > indexRead ) ;
79 }
80
81 // Write a word to queue .
82 void Q32_FifoWrite ( Q32 * queue , uint32_t value )
83 {
84 queue - > buffer [ ( queue - > indexWrite ++ & QU EU E _U I1 6 _M AS K ) ] = value ;
85 }
86
87 // Read / get next word from queue .
88 // Must always check if FIFO is empty before doing a read .
89 uint32_t Q32_FifoRead ( Q32 * queue )
90 {
91 return queue - > buffer [ ( queue - > indexRead ++ & Q UE UE _ UI 16 _ MA SK ) ];
92 }
93
94 // Read the element oldest element in queue .
95 uint32_t Q 3 2 _ F i f o R e a d E l e m e n t ( Q32 * queue , uint32_t element )
96 {
97 return queue - > buffer [ ( queue - > indexRead + element ) ];
98 }
99
100 // Check if queue is empty .
101 bool_t Q32_isEmpty ( Q32 * queue )
102 {
103 if ( queue - > indexRead == queue - > indexWrite )
104 {
105 return TRUE ;
106 }
107 else
108 {
109 return FALSE ;
110 }
111 }
112
113 // Find how many elements in queue .
114 uint16_t Q32_Count ( Q32 * queue )
115 {
116 return ( queue - > indexWrite - queue - > indexRead ) ;
117 }
230 APPENDIX A. MICROCONTROLLER CODE
51 // Do nothing .
52 break ;
53
54 case MEASURE :
55 B M S _ O n E n t e r _ M e a s u r e M o d e () ;
56 BMS_SetMode ( _MEASURE ) ;
57 break ;
58
59 case _MEASURE :
60 B MS _M o de Me a su re () ;
61 break ;
62
63 case EGG_MODE :
64 B M S _ O n E n t e r _ E g g M o d e () ;
65 BMS_SetMode ( _EGG_MODE ) ;
66 break ;
67
68 case _EGG_MODE :
69 BMS_ModeEgg () ;
70 break ;
71
72 default :
73 BMS_SetMode ( IDLE ) ;
74 break ;
75 }
76
77 // Do this every 50 th iteration .
78 if ( ( counter % 50) == 0)
79 {
80 // Send heart beat signal to GUI .
81 BMS_HeartBeat () ;
82
83 // Send calibration status .
84 B M S _ C a l i b r a t i o n S t a t u s () ;
85 }
86
87 // Get and parse incomming data .
88 g e t A n d P a r s e N e w M e s s a g e s () ;
89
90 // Update Watchdog .
91 IWDG_Update () ;
92
93 // Increment loop counter .
94 counter ++;
95 }
96
97 // Set BMS mode .
98 void BMS_SetMode ( BMS_MODE_t bmsMode )
99 {
100 previousBMSMode2 = previousBMSMode1 ;
101 p r e v i o u s B M S M o d e 1 = cur rentBM SMode ;
102 curr entBMS Mode = bmsMode ;
103 }
104
105 // Get current BMS mode .
106 BMS_MODE_t BMS_GetMode ()
107 {
108 if ( cur rentBM SMode >= 10000)
109 {
110 return ( cu rrentB MSMode - 10000) ;
111 }
112
A.1. CODE: C 239
175 xTemp += I m m i t t a n c e _ G e t R e a c t a n c e () ;
176 }
177
178 // Get average .
179 rTemp /= 10;
180 xTemp /= 10;
181
182 S E N D _ M S G _ R E S I S T A N C E _ R E A C T A N C E ( rTemp , xTemp ) ;
183 S E N D _ M S G _ C S F M _ F R E Q U E N C Y (( unsigned int ) D D S _ G e t F r e q u e n c y () ) ;
184 }
185 else if ( realtimePlot - > technique == T E C H N I Q U E _ N O R M A L )
186 {
187 // Normal mode .
188 }
189 }
190
191 // Update spectroscopy parameters .
192 if ( staticPlot - > s p e c t r o s c o p y P l o t E n a b l e )
193 {
194 if ( staticPlot - > technique == TECHNIQUE_4FS )
195 {
196 // Do frequency sweep .
197 M e a s u r e _ 4 f s _ S p e c t r o s c o p y (& s p e c t r o s c o p y F r e q u e n c i e s [0] ,
N U M _ O F _ E L E M E N T S _ I N _ A R R A Y ( s p e c t r o s c o p y F r e q u e n c i e s ) , 10) ;
198 }
199 else if ( staticPlot - > technique == T E C H N I Q U E _ N O R M A L )
200 {
201 // Do frequency sweep .
202
203 }
204 }
205
206 // If both plots are disabled , power down DDS .
207 if (! realtimePlot - > r e a l t i m e P l o t E n a b l e && ! staticPlot - >
spectroscopyPlotEnable )
208 {
209 // Power down DDS .
210 DDS_Config (0 ,0 , TRUE ) ;
211 }
212
213 // If calibration requested and calibration not done , do it .
214 if ( calibration - > ca librat ePhase && ! ca l ib ra t io nD o ne )
215 {
216 M e a s u r e _ C a l i b r a t e _ C a l c u l a t e P h a s e E r r o r S l o p e (10000 , 100000) ;
217 C a l i b r a t i o n _ D o n e P h a s e () ;
218 c al ib r at i on Do n e = TRUE ;
219 }
220 }
221
222 // Transition to egg demo mode .
223 void B M S _ O n E n t e r _ E g g M o d e ( void )
224 {
225 // Calibrate system voltage .
226 M e a s u r e _ C a l i b r a t e _ S y s t e m V o l t a g e () ;
227
228 // Initialize ADC1 .
229 A D C 1 _ 4 f s _ I n i t i a l i z e () ;
230
231 // Initialize Zero Crossing Detector .
232 ZCD_ Initia lize () ;
233
234 // Set excitation voltage .
A.1. CODE: C 241
235 M e a s u r e _ S e t E x c i t a t i o n P e a k T o P e a k M i l l i v o l t s (100) ;
236 }
237
238 // Egg demo mode .
239 void BMS_ModeEgg ( void )
240 {
241 unsigned int i = 0;
242 double phase3k ;
243 double phase30k ;
244 uint32_t mod3k ;
245 uint32_t mod30k ;
246 double deltaPhase ;
247
248 phase3k = 0;
249 mod3k = 0;
250 phase30k = 0;
251 mod30k = 0;
252
253 for ( i = 0; i < 10; i ++)
254 {
255 // Measure at 3 kHz .
256 M e a s u r e _ 4 f s _ C S F M (3000 ,10) ;
257
258 // Get impedance modulus and phase .
259 phase3k += I m m i t t a n c e _ G e t P h a s e () ;
260 mod3k += ( uint32_t ) I m m i t t a n c e _ G e t I m p e d a n c e M o d u l u s () ;
261 }
262
263 for ( i = 0; i < 10; i ++)
264 {
265 // Measure at 30 kHz .
266 M e a s u r e _ 4 f s _ C S F M (30000 ,10) ;
267
268 // Get impedance modulus and phase .
269 phase30k += I m m i t t a n c e _ G e t P h a s e () ;
270 mod30k += ( uint32_t ) I m m i t t a n c e _ G e t I m p e d a n c e M o d u l u s () ;
271 }
272
273 // Calculate average .
274 phase3k /= 10;
275 mod3k /= 10;
276 phase30k /= 10;
277 mod30k /= 10;
278
279 // Calculate phase difference .
280 deltaPhase = fabs ( phase3k - phase30k ) ;
281
282 // Check if needle is in egg yet .
283 if ( ( mod3k > 5000) && ( mod30k > 5000) )
284 {
285 S E N D _ M S G _ E G G _ S T A T E (0) ;
286 return ;
287 }
288 else if ( ( mod3k < 100 ) || ( mod30k < 100) )
289 {
290 S E N D _ M S G _ E G G _ S T A T E (0) ;
291 return ;
292 }
293
294 // Determine where in the egg the needle is at .
295 if ( deltaPhase > 32)
296 {
242 APPENDIX A. MICROCONTROLLER CODE
61 {
62 // Enable Timer 1
63 TIM1 - > CR1 |= TIM_CR1_CEN ;
64
65 // Disable zero crossing interrupt .
66 EXTI - > IMR &= Z E R O _ C R O S S I N G _ D E T E C T O R _ D I S A B L E _ I N T E R R U P T _ M A S K ;
67
68 // Clear interrupt .
69 EXTI - > PR = EXTI_Line2 ;
70 }
71 }
72
73 // IQ DMA ISR
74 void D M A 1 _ C h a n n e l 1 _ I R Q H a n d l e r ( void )
75 {
76 if (( DMA1 - > ISR & DMA_ISR_TCIF1 ) != RESET )
77 {
78 // Disable TIM1 .
79 TIM1 - > CR1 &= (~ TIM_CR1_CEN ) ;
80
81 // Disable ADC1 .
82 ADC1 - > CR2 &= (( uint32_t ) 0 xFFFFFFFE ) ;
83
84 // Set DMA flags .
85 D M A _ S e t T r a n s f e r D o n e ( D M A _ G e t 4 f s S t r u c t () , TRUE ) ;
86 D M A _ S e t T r a n s f e r D o n e ( D M A _ G e t N o r m a l S t r u c t () , TRUE ) ;
87
88 // Clear DMA transfer complete interrupt flag .
89 DMA1 - > IFCR = ~ D M A_ I FC R_ C TC IF1 ;
90 }
91
92 if (( DMA1 - > ISR & DMA_ISR_TEIF1 ) != RESET )
93 {
94 // Clear DMA transfer error interrupt flag .
95 DMA1 - > IFCR = ~ D M A_ I FC R_ C TE IF1 ;
96 }
97 }
98
99 // DDS DMA ISR
100 void D M A 2 _ C h a n n e l 4 _ 5 _ I R Q H a n d l e r ( void )
101 {
102 if ( DM A_ G et IT S ta tu s ( DMA2_IT_TC5 ) != RESET )
103 {
104 // Set DMA flag .
105 D M A _ S e t T r a n s f e r D o n e ( D M A _ G e t A d c 3 S t r u c t () , TRUE ) ;
106
107 // Clear DMA transfer complete interrupt flag
108 D M A _ C l e a r I T P e n d i n g B i t ( DMA2_IT_TC5 ) ;
109 }
110
111 if ( DM A_ G et IT S ta tu s ( DMA2_IT_TE5 ) != RESET )
112 {
113 // Clear DMA transfer error interrupt flag .
114 D M A _ C l e a r I T P e n d i n g B i t ( DMA2_IT_TE5 ) ;
115 }
116 }
117
118 // System tick ISR .
119 void S ys Ti c k_ Ha n dl e r ( void )
120 {
121 S Y S T I C K _ D e c r e m e n t C o u n t e r () ;
122 }
A.1. CODE: C 245
123
124 // Timer 1 CC interrupt .
125 // ADC1 sampling timer (4 fs or Normal ) .
126 void T I M 1 _ C C _ I R Q H a n d l e r ( void )
127 {
128 if ( ( TIM1 - > SR & TIM_IT_CC1 ) != RESET )
129 {
130 // Clear Timer 1 CC1 interrupt .
131 TIM1 - > SR = ~ TIM_IT_CC1 ;
132 }
133 }
134
135 // Millisecond precision hardware counter .
136 void T IM 2_ I RQ Ha n dl er ( void )
137 {
138 if ( TI M_ G et IT S ta tu s ( TIM2 , TIM_IT_CC1 ) != RESET )
139 {
140 // Increment millisecond counter .
141 timer2Counter ++;
142
143 // Clear Timer 2 CC1 interrupt .
144 T I M _ C l e a r I T P e n d i n g B i t ( TIM2 , TIM_IT_CC1 ) ;
145 }
146 }
147
148 // Calibrate LSI ISR .
149 void T IM 5_ I RQ Ha n dl er ( void )
150 {
151 static uint16_t timeStamp1 ;
152 uint16_t timeStamp2 ;
153 uint32_t timeDelta ;
154 uint32_t temp ;
155
156 if ( TI M_ G et IT S ta tu s ( TIM5 , TIM_IT_CC4 ) != RESET )
157 {
158 if ( L S I _ G e t C o u n t e r V a l u e () == 0)
159 {
160 // Get input capture value .
161 timeStamp1 = T I M_ Ge t Ca p tu re 4 ( TIM5 ) ;
162 }
163 else if ( L S I _ G e t C o u n t e r V a l u e () == 1)
164 {
165 // Get input capture value .
166 timeStamp2 = T I M_ Ge t Ca p tu re 4 ( TIM5 ) ;
167
168 // Get time delta and check for counter wrap - around .
169 if ( timeStamp2 > timeStamp1 )
170 {
171 timeDelta = ( timeStamp2 - timeStamp1 ) ;
172 }
173 else
174 {
175 timeDelta = ((0 xFFFF - timeStamp1 ) + timeStamp2 ) ;
176 }
177
178 // Compute LSI frequency .
179 temp = ( uint32_t ) ( S Y S T E M _ F R E Q U E N C Y / timeDelta ) ;
180
181 // Compensate for Timer 5 input compare prescaler .
182 temp *= 8;
183
184 // Set LSI frequency .
246 APPENDIX A. MICROCONTROLLER CODE
185 L S I _ S e t F r e q u e n c y ( temp ) ;
186 }
187
188 L S I _ S e t C o u n t e r V a l u e (1) ;
189
190 // Clear Timer 5 CC4 interrupt .
191 T I M _ C l e a r I T P e n d i n g B i t ( TIM5 , TIM_IT_CC4 ) ;
192 }
193 }
194
195 // Timer 8 CC interrupt .
196 // ADC3 sampling timer ( DDS ) .
197 void T I M 8 _ C C _ I R Q H a n d l e r ( void )
198 {
199 if ( TI M_ G et IT S ta tu s ( TIM8 , TIM_IT_CC1 ) != RESET )
200 {
201 // Clear Timer 8 CC1 interrupt .
202 T I M _ C l e a r I T P e n d i n g B i t ( TIM8 , TIM_IT_CC1 ) ;
203 }
204 }
205
206 // USART1 ISR .
207 void U S A R T 1 _ I R Q H a n d l e r ( void )
208 {
209 // Check if a receive interrupt was triggered .
210 if ( U S A R T _ G e t I T S t a t u s ( USART1 , USART_IT_RXNE ) != RESET )
211 {
212 // Get byte and write to queue .
213 Q8_FifoWrite ( U S A R T 1 _ G e t R e c e i v e Q u e u e () , U S A R T _ R e c e i v e D a t a ( USART1 ) ) ;
214
215 // Clear USART1 receive data interrupt flag .
216 U S A R T _ C l e a r I T P e n d i n g B i t ( USART1 , USART_IT_RXNE ) ;
217 }
218 }
A.1. CODE: C 247
59 return 0;
60 }
61 __attribute__ (( used ) )
62 int _read ( int file , char * ptr , int len )
63 {
64 return 0;
65 }
66 __attribute__ (( used ) )
67 int _write ( int file , char * ptr , int len )
68 {
69 int i ;
70
71 for ( i = 0; i < len ; i ++)
72 {
73 USAR T_Send Data ( USART1 , ptr [ i ]) ;
74
75 // Wait for message to be sent .
76 while ( U S A R T _ G e t F l a g S t a t u s ( USART1 , USART_FLAG_TC ) == RESET ) ;
77 }
78
79 return len ;
80 }
81
82 __attribute__ (( used ) )
83 void abort ( void )
84 {
85 /* Abort called */
86 while (1) ;
87 }
88
89 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - End Of File
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
254 APPENDIX A. MICROCONTROLLER CODE
Appendix B
GUI Code
255
256 APPENDIX B. GUI CODE
61
62 QAction * f re qu e nc yA c ti o n ;
63 QAction * c a l i b r a t i o n A c t i o n ;
64 QAction * aboutAction ;
65
66 QSpinBox * frequencyBox ;
67 QComboBox * systemModeBox ;
68
69 // Status bar items .
70 QLabel * systemStatus ;
71 QLabel * bl ue t oo th S ta t us ;
72 QLabel * c a l i b r a t i o n S t a t u s ;
73 QLabel * r e s i s t a n c e S t a t u s ;
74 QLabel * re ac t an ce S ta t us ;
75 QLabel * c o n d u c t a n c e S t a t u s ;
76 QLabel * s u s c e p t a n c e S t a t u s ;
77 QLabel * im pe d an ce S ta t us ;
78 QLabel * a d m i t t a n c e S t a t u s ;
79 QLabel * phaseStatus ;
80
81 // Other
82 QSt ackedW idget * stackedWidget ;
83 MeasurementPlots * measurementPlots ;
84 BluetoothDeviceList * bluetoothDeviceList ;
85 Bluetooth * bluetooth ;
86 Send * send ;
87 Receive * receive ;
88 QPushButton * confirmButton ;
89 QLabel * eggDemo ;
90 unsigned int counter ;
91 QTimer * heartbeat ;
92
93 private slots :
94 void onDiscover () ;
95 void o n T o g g l e P l o t V i e w () ;
96 void o nRealt imePlo t ( bool status ) ;
97 void o n R e s i s t a n c e R e a c t a n c e () ;
98 void o n C o n d u c t a n c e S u s c e p t a n c e () ;
99 void o nModul usPhas e () ;
100 void o n S p e c t r o s c o p y P l o t ( bool status ) ;
101 void o n F o u r F S M e a s u r e m e n t () ;
102 void o n N o r m a l M e a s u r e m e n t () ;
103 void o n F r e q u e n c y V a l u e C h a n g e d ( int value ) ;
104 void o n S y s t e m M o d e C h a n g e d ( QString text ) ;
105 void onCalibration () ;
106 void onAbout () ;
107
108 void s h o w S t a t u s B a r T e x t ( QString text , int timeout = 3000) ;
109
110 void o n B l u e t o o t h S e a r c h D o n e () ;
111 void o n D i s c o v e r y S t a r t e d () ;
112 void o n B l u e t o o t h C o n n e c t e d () ;
113 void o n B l u e t o o t h D i s c o n n e c t e d () ;
114 void o n B l u e t o o t h A d a p t e r N o t F o u n d () ;
115 void o n B l u e t o o t h C o n n e c t () ;
116 void o n H e a r t B e a t R e c e i v e d () ;
117 void o n H e a r t B e a t T i m e o u t () ;
118 void o n C a l i b r a t i o n S t a t u s R e c e i v e d ( QString state ) ;
119 void onConfirm () ;
120
121 void o n R e s i s t a n c e R e a c t a n c e R e c e i v e d ( QString resistance , QString reactance
);
260 APPENDIX B. GUI CODE
97
98 connect ( receive , SIGNAL ( eggState ( QString ) ) , this , SLOT ( onEggState (
QString ) ) ) ;
99
100 connect ( receive , SIGNAL ( heartbeat () ) , this , SLOT ( o n H e a r t B e a t R e c e i v e d () ) )
;
101
102 connect ( receive , SIGNAL ( c a l i b r a t i o n S t a t u s ( QString ) ) , this , SLOT (
o n C a l i b r a t i o n S t a t u s R e c e i v e d ( QString ) ) ) ;
103 }
104
105 MainWindow ::~ MainWindow ()
106 {
107 delete ui ;
108 }
109
110 // Sets up main windows menu and tool bars .
111 void MainWindow :: s e t u p M e n u A n d T o o l B a r ()
112 {
113 qDebug () << " Setting up menu , status and tool bar " ;
114
115 // Hide menu bar .
116 menuBar () -> setVisible ( false ) ;
117
118 // Set tool bar static embedded within application window .
119 ui - > mainToolBar - > setMovable ( false ) ;
120
121 // Make sure toolbar items fills the toolbar in horizontal direction .
122 ui - > mainToolBar - > setSizePolicy ( QSizePolicy :: Expanding , QSizePolicy ::
Preferred ) ;
123
124 // Create Discover menu .
125 discover = menuBar () -> addMenu ( tr ( " & Discover " ) ) ;
126
127 // Create Discover action . Set discover icon , priority and shortcut .
Connect signal to slot and add to menu and tool bar .
128 dis coverA ction = discover - > addAction ( tr ( " & Discover " ) ) ;
129 discoverAction - > setIcon ( QIcon ( " :/ icons / resources / icons / discover . png " ) ) ;
130 discoverAction - > setPriority ( QAction :: HighPriority ) ;
131 discoverAction - > setShortcut ( QKeySequence :: New ) ;
132 ui - > mainToolBar - > addAction ( disco verAct ion ) ;
133 discover - > addAction ( dis coverA ction ) ;
134 connect ( discoverAction , SIGNAL ( triggered () ) , this , SLOT ( onDiscover () ) ) ;
135
136 ui - > mainToolBar - > addSeparator () ;
137
138 // Create Toggle PlotVi ew menu .
139 tog glePlo tView = menuBar () -> addMenu ( tr ( " & Toggle Plot View " ) ) ;
140
141 // Create Toggle PlotVi ew action . Set discover icon , priority and shortcut
. Connect signal to slot and add to menu and tool bar .
142 t o g g l e P l o t V i e w A c t i o n = togglePlotView - > addAction ( tr ( " & Toggle Plot View " )
);
143 togglePlotViewAction - > setIcon ( QIcon ( " :/ icons / resources / icons / toggleChart
. png " ) ) ;
144 togglePlotViewAction - > setPriority ( QAction :: HighPriority ) ;
145 togglePlotViewAction - > setShortcut ( QKeySequence :: New ) ;
146 ui - > mainToolBar - > addAction ( t o g g l e P l o t V i e w A c t i o n ) ;
147 togglePlotView - > addAction ( t o g g l e P l o t V i e w A c t i o n ) ;
148 connect ( togglePlotViewAction , SIGNAL ( triggered () ) , this , SLOT (
o n T o g g l e P l o t V i e w () ) ) ;
149
264 APPENDIX B. GUI CODE
200
201 // Create C o n d u c t a n c e S u s c e p t a n c e menu .
202 c o n d u c t a n c e S u c e p t a n c e = menuBar () -> addMenu ( tr ( " & Conductance &&
Susceptance " ) ) ;
203
204 // Create C o n d u c t a n c e S u s c e p t a n c e action . Set discover icon , priority and
shortcut . Connect signal to slot and add to menu and tool bar .
205 c o n d u c t a n c e S u c e p t a n c e A c t i o n = conductanceSuceptance - > addAction ( tr ( " &
Conductance && Susceptance " ) ) ;
206 conductanceSuceptanceAction - > setIcon ( QIcon ( " :/ icons / resources / icons /
c o n d u c t a n c e S u s c e p t a n c e . png " ) ) ;
207 conductanceSuceptanceAction - > setPriority ( QAction :: HighPriority ) ;
208 conductanceSuceptanceAction - > setShortcut ( QKeySequence :: New ) ;
209 // ui - > mainToolBar - > addAction ( c o n d u c t a n c e S u c e p t a n c e A c t i o n ) ;
210 conductanceSuceptance - > addAction ( c o n d u c t a n c e S u c e p t a n c e A c t i o n ) ;
211 conductanceSuceptanceAction - > setCheckable ( true ) ;
212 connect ( conductanceSuceptanceAction , SIGNAL ( triggered () ) , this , SLOT (
o n C o n d u c t a n c e S u s c e p t a n c e () ) ) ;
213
214
215 // Create ModulusPhase menu .
216 modulusPhase = menuBar () -> addMenu ( tr ( " & Modulus && Phase " ) ) ;
217
218 // Create ModulusPhase action . Set discover icon , priority and shortcut .
Connect signal to slot and add to menu and tool bar .
219 m o d u l u s P h a s e A c t i o n = modulusPhase - > addAction ( tr ( " & Modulus && Phase " ) ) ;
220 modulusPhaseAction - > setIcon ( QIcon ( " :/ icons / resources / icons / modulusPhase .
png " ) ) ;
221 modulusPhaseAction - > setPriority ( QAction :: HighPriority ) ;
222 modulusPhaseAction - > setShortcut ( QKeySequence :: New ) ;
223 // ui - > mainToolBar - > addAction ( m o d u l u s P h a s e A c t i o n ) ;
224 modulusPhase - > addAction ( m o d u l u s P h a s e A c t i o n ) ;
225 modulusPhaseAction - > setCheckable ( true ) ;
226 connect ( modulusPhaseAction , SIGNAL ( triggered () ) , this , SLOT (
onMo dulusP hase () ) ) ;
227
228 m e a s u r e m e n t P a r a m e t e r = new QActionGroup ( this ) ;
229 measurementParameter - > addAction ( r e s i s t a n c e R e a c t a n c e A c t i o n ) ;
230 measurementParameter - > addAction ( c o n d u c t a n c e S u c e p t a n c e A c t i o n ) ;
231 measurementParameter - > addAction ( m o d u l u s P h a s e A c t i o n ) ;
232
233 modulusPhaseAction - > setChecked ( true ) ;
234
235 ui - > mainToolBar - > addSeparator () ;
236
237 // Create Frequency toolbar item .
238 frequencyBox = new QSpinBox () ;
239 frequencyBox - > setRange (100 , 140000) ;
240 frequencyBox - > setSizePolicy ( QSizePolicy :: Expanding , QSizePolicy ::
Expanding ) ;
241 QFont freNumfont ;
242 freNumfont . setPointSize (30) ;
243 frequencyBox - > setFont ( freNumfont ) ;
244 ui - > mainToolBar - > addWidget ( frequencyBox ) ;
245 connect ( frequencyBox , SIGNAL ( valueChanged ( int ) ) , this , SLOT (
o n F r e q u e n c y V a l u e C h a n g e d ( int ) ) ) ;
246
247 ui - > mainToolBar - > addSeparator () ;
248
249 // Create SystemMode toolbar item .
250 systemModeBox = new QComboBox () ;
251 systemModeBox - > addItem ( " Idle " ) ;
266 APPENDIX B. GUI CODE
302 calibrationAction - > setIcon ( QIcon ( " :/ icons / resources / icons / calibration .
png " ) ) ;
303 calibrationAction - > setPriority ( QAction :: HighPriority ) ;
304 calibrationAction - > setShortcut ( QKeySequence :: New ) ;
305 ui - > mainToolBar - > addAction ( c a l i b r a t i o n A c t i o n ) ;
306 calibration - > addAction ( c a l i b r a t i o n A c t i o n ) ;
307 calibrationAction - > setCheckable ( true ) ;
308 calibrationAction - > setChecked ( false ) ;
309 connect ( calibrationAction , SIGNAL ( triggered () ) , this , SLOT ( onCalibration
() ) ) ;
310
311 // Create About menu .
312 about = menuBar () -> addMenu ( tr ( " & About " ) ) ;
313
314 // Create About action . Set discover icon , priority and shortcut . Connect
signal to slot and add to menu and tool bar .
315 aboutAction = about - > addAction ( tr ( " & About " ) ) ;
316 aboutAction - > setIcon ( QIcon ( " :/ icons / resources / icons / about . png " ) ) ;
317 aboutAction - > setPriority ( QAction :: HighPriority ) ;
318 aboutAction - > setShortcut ( QKeySequence :: New ) ;
319 connect ( aboutAction , SIGNAL ( triggered () ) , this , SLOT ( onAbout () ) ) ;
320 ui - > mainToolBar - > addAction ( aboutAction ) ;
321 about - > addAction ( aboutAction ) ;
322
323 // Set Tool Bar maximum icon size .
324 ui - > mainToolBar - > setIconSize ( QSize (128 ,128) ) ;
325
326 // Configure Status Bar .
327
328 // Add confirm button .
329 confirmButton = new QPushButton () ;
330 confirmButton - > setText ( " Confirm " ) ;
331 confirmButton - > setSizePolicy ( QSizePolicy :: Preferred , QSizePolicy ::
Expanding ) ;
332 confirmButton - > setFixedWidth (250) ;
333 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( confirmButton ) ;
334 confirmButton - > setEnabled ( false ) ;
335 connect ( confirmButton , SIGNAL ( pressed () ) , this , SLOT ( onConfirm () ) ) ;
336
337 // Add system status .
338 systemStatus = new QLabel () ;
339 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( systemStatus ) ;
340 systemStatus - > setPixmap ( QPixmap ( " :/ icons / resources / icons /
s y s t e m S t a t u s E r r o r . png " ) ) ;
341
342 // Add bluetooth status .
343 b lu et o ot hS t at u s = new QLabel () ;
344 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( bl u et o ot hS t at us ) ;
345 bluetoothStatus - > setPixmap ( QPixmap ( " :/ icons / resources / icons /
b l u e t o o t h D i s c o n n e c t e d . png " ) ) ;
346
347 // Add calibration status .
348 c a l i b r a t i o n S t a t u s = new QLabel () ;
349 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( c a l i b r a t i o n S t a t u s ) ;
350 calibrationStatus - > setPixmap ( QPixmap ( " :/ icons / resources / icons /
c a l i b r a t i o n N o t D o n e . png " ) ) ;
351
352 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( " <b > R : </b > " ) ) ;
353 r e s i s t a n c e S t a t u s = new QLabel () ;
354 resistanceStatus - > setFixedWidth (75) ;
355 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( r e s i s t a n c e S t a t u s ) ;
356
268 APPENDIX B. GUI CODE
357 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( " <b > X : </b > " ) ) ;
358 r ea ct a nc eS t at u s = new QLabel () ;
359 reactanceStatus - > setFixedWidth (75) ;
360 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( r ea ct a nc eS t at u s ) ;
361
362 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( " <b > G [ uS ]: </b > " ) ) ;
363 c o n d u c t a n c e S t a t u s = new QLabel () ;
364 conductanceStatus - > setFixedWidth (75) ;
365 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( c o n d u c t a n c e S t a t u s ) ;
366
367 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( " <b > B [ uS ]: </b > " ) ) ;
368 s u s c e p t a n c e S t a t u s = new QLabel () ;
369 susceptanceStatus - > setFixedWidth (75) ;
370 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( s u s c e p t a n c e S t a t u s ) ;
371
372 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( " <b > Z : </b > " ) ) ;
373 i mp ed a nc eS t at u s = new QLabel () ;
374 impedanceStatus - > setFixedWidth (75) ;
375 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( i mp ed a nc eS t at u s ) ;
376
377 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( " <b > Y : [ uS ] </b > " ) ) ;
378 a d m i t t a n c e S t a t u s = new QLabel () ;
379 admittanceStatus - > setFixedWidth (75) ;
380 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( a d m i t t a n c e S t a t u s ) ;
381
382 QString temp ;
383 temp . append ( " <b > " ) ;
384 temp . append ( QChar (0 x03B8 ) ) ;
385 temp . append ( " : " ) ;
386 temp . append ( " </b > " ) ;
387 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( new QLabel ( temp ) ) ;
388 phaseStatus = new QLabel () ;
389 phaseStatus - > setFixedWidth (75) ;
390 ui - > statusBar - > a d d P e r m a n e n t W i d g e t ( phaseStatus ) ;
391
392 resistanceStatus - > setText ( " 0 " ) ;
393 reactanceStatus - > setText ( " 0 " ) ;
394 conductanceStatus - > setText ( " 0 " ) ;
395 susceptanceStatus - > setText ( " 0 " ) ;
396 impedanceStatus - > setText ( " 0 " ) ;
397 admittanceStatus - > setText ( " 0 " ) ;
398 phaseStatus - > setText ( " 0 " ) ;
399 }
400
401 void MainWindow :: onDiscover ()
402 {
403 qDebug () << " Discover " ;
404
405 stackedWidget - > s et C ur r en tI n de x (0) ;
406 bluetoothDeviceList - > clear () ;
407 bluetooth - > connect () ;
408 bluetooth - > discover () ;
409 }
410
411 void MainWindow :: o n T o g g l e P l o t V i e w ()
412 {
413 qDebug () << " Toggle Plot View ! " ;
414
415 if ( stackedWidget - > currentIndex () == 1)
416 {
417 measurementPlots - > o n T o g g l e P l o t V i e w () ;
418 }
B.2. CODE: C++ 269
419 else
420 {
421 stackedWidget - > s et C ur r en tI n de x (1) ;
422 }
423 }
424
425 void MainWindow :: o nRealt imePlo t ( bool status )
426 {
427 qDebug () << " Real Time Plot : " << status ;
428 s h o w S t a t u s B a r T e x t ( " Real Time Plot " + (( status ) ? QString ( " Enabled " ) :
QString ( " Disabled " ) ) ) ;
429 }
430
431 void MainWindow :: o n R e s i s t a n c e R e a c t a n c e ()
432 {
433 qDebug () << " Resistance & Reactance ! " ;
434 s h o w S t a t u s B a r T e x t ( " Resistance & Reactance Selected " ) ;
435 }
436
437 void MainWindow :: o n C o n d u c t a n c e S u s c e p t a n c e ()
438 {
439 qDebug () << " Conductance & Susceptance ! " ;
440 s h o w S t a t u s B a r T e x t ( " Conductance & Susceptance Selected " ) ;
441 }
442
443 void MainWindow :: o nModul usPhas e ()
444 {
445 qDebug () << " Modulus & Phase ! " ;
446 s h o w S t a t u s B a r T e x t ( " Modulus & Phase Selected " ) ;
447 }
448
449 void MainWindow :: o n S p e c t r o s c o p y P l o t ( bool status )
450 {
451 qDebug () << " Spectroscopy Plot : " << status ;
452 s h o w S t a t u s B a r T e x t ( " Spectroscopy Plot " + (( status ) ? QString ( " Enabled " )
: QString ( " Disabled " ) ) ) ;
453 }
454
455 void MainWindow :: o n F o u r F S M e a s u r e m e n t ()
456 {
457 qDebug () << " 4 fs Measurement ! " ;
458 s h o w S t a t u s B a r T e x t ( " 4 fs Measurement Selected " ) ;
459 }
460
461 void MainWindow :: o n N o r m a l M e a s u r e m e n t ()
462 {
463 qDebug () << " Normal Measurement ! " ;
464 s h o w S t a t u s B a r T e x t ( " Normal Measurement Selected " ) ;
465
466 }
467
468 void MainWindow :: o n F r e q u e n c y V a l u e C h a n g e d ( int value )
469 {
470 qDebug () << QString :: number ( value ) ;
471 }
472
473 void MainWindow :: o n S y s t e m M o d e C h a n g e d ( QString text )
474 {
475 qDebug () << " System mode : " << text ;
476
477 if ( text == " Measurement Mode " )
478 {
270 APPENDIX B. GUI CODE
61
62 };
63
64 # endif // BLUETOOTH_H
278 APPENDIX B. GUI CODE
57 // /
58 void Bluetooth :: de viceSe lected ()
59 {
60 // Parse item for MAC address .
61 QString macAddress = bluetoothSelectedDevice - > mid ( (
bluetoothSelectedDevice - > length () - 17) ) ;
62 qDebug () << " MAC address : " + macAddress ;
63
64 // Stop discovery if still discovering .
65 Q A n d r o i d J n i O b j e c t :: callStaticMethod < void >( " org / qtproject / qt5 / android /
bindings / MyActivity " , " B l u e t o o t h C a n c e l D i s c o v e r y " ) ;
66
67 // Convert QString to Java string .
68 Q A n d r o i d J n i O b j e c t javaString = Q A n d r o i d J n i O b j e c t :: fromString ( macAddress )
;
69
70 // Send MAC address to start establishing a connection .
71 Q A n d r o i d J n i O b j e c t :: callStaticMethod < void >( " org / qtproject / qt5 / android /
bindings / MyActivity " , " B l u e t o o t h C o n n e c t T o D e v i c e " , " ( Ljava / lang /
String ;) V " , javaString . object < jstring >() ) ;
72
73 // Emit signal that bluetooth is connected .
74 emit connected () ;
75 }
76
77 /* * * * * * * * * * * * * * * * * * */
78 /* * Java signals . * */
79 /* * * * * * * * * * * * * * * * * * */
80
81 // /
82 // / \ brief b l u e t o o t h D e v i c e F o u n d
83 // / \ param env
84 // / \ param device
85 // /
86 static void b l u e t o o t h D e v i c e F o u n d ( JNIEnv * env , jclass /* clazz */ , jstring
device )
87 {
88 QString temp = env - > G e t S t r i n g U T F C h a r s ( device , 0) ;
89 if ( temp . isNull () || temp . isEmpty () )
90 {
91 return ;
92 }
93 Bluetooth :: instance () -> b l u e t o o t h D e v i c e F o u n d ( temp ) ;
94 env - > R e l e a s e S t r i n g U T F C h a r s ( device , temp . toUtf8 () ) ;
95 }
96
97 // /
98 // / \ brief b l u e t o o t h D i s c o v e r y S t a r t e d
99 // /
100 static void b l u e t o o t h D i s c o v e r y S t a r t e d ( JNIEnv * /* env */ , jclass /* clazz */ )
101 {
102 Bluetooth :: instance () -> b l u e t o o t h D i s c o v e r y S t a r t e d () ;
103 }
104
105 // /
106 // / \ brief b l u e t o o t h S e a r c h D o n e
107 // /
108 static void b l u e t o o t h S e a r c h D o n e ( JNIEnv * /* env */ , jclass /* clazz */ )
109 {
110 Bluetooth :: instance () -> b l u e t o o t h S e a r c h D o n e () ;
111 }
112
B.2. CODE: C++ 281
113 // /
114 // / \ brief b l u e t o o t h A d a p t e r N o t F o u n d
115 // /
116 static void b l u e t o o t h A d a p t e r N o t F o u n d ( JNIEnv * /* env */ , jclass /* clazz */ )
117 {
118 Bluetooth :: instance () -> b l u e t o o t h A d a p t e r N o t F o u n d () ;
119 }
120
121 // /
122 // / \ brief b l u e t o o t h D a t a R e c e i v e d
123 // / \ param env
124 // / \ param data
125 // /
126 static void b l u e t o o t h D a t a R e c e i v e d ( JNIEnv * env , jclass /* clazz */ , jstring
data )
127 {
128 const char * temp = env - > G e t S t r i n g U T F C h a r s ( data , 0) ;
129 QString tempS ( temp ) ;
130 if ( tempS . isNull () || tempS . isEmpty () )
131 {
132 return ;
133 }
134 Bluetooth :: instance () -> b l u e t o o t h D a t a R e c e i v e d ( tempS ) ;
135 env - > R e l e a s e S t r i n g U T F C h a r s ( data , temp ) ;
136 }
137
138 // /
139 // / \ brief b l u e t o o t h E x c e p t i o n C r e a t e R F C O M M
140 // /
141 static void b l u e t o o t h E x c e p t i o n C r e a t e R F C O M M ( JNIEnv * /* env */ , jclass /* clazz
*/ )
142 {
143 Bluetooth :: instance () -> b l u e t o o t h E x c e p t i o n C r e a t e R F C O M M () ;
144 }
145
146 // /
147 // / \ brief b l u e t o o t h E x c e p t i o n J o i n T h r e a d
148 // /
149 static void b l u e t o o t h E x c e p t i o n J o i n T h r e a d ( JNIEnv * /* env */ , jclass /* clazz */ )
150 {
151 Bluetooth :: instance () -> b l u e t o o t h E x c e p t i o n J o i n T h r e a d () ;
152 }
153
154 // /
155 // / \ brief b l u e t o o t h E x c e p t i o n I n p u t O u t p u t S t r e a m
156 // /
157 static void b l u e t o o t h E x c e p t i o n I n p u t O u t p u t S t r e a m ( JNIEnv * /* env */ , jclass /*
clazz */ )
158 {
159 Bluetooth :: instance () -> b l u e t o o t h E x c e p t i o n I n p u t O u t p u t S t r e a m () ;
160 }
161
162 // /
163 // / \ brief b l u e t o o t h E x c e p t i o n R e a d I n p u t S t r e a m
164 // /
165 static void b l u e t o o t h E x c e p t i o n R e a d I n p u t S t r e a m ( JNIEnv * /* env */ , jclass /*
clazz */ )
166 {
167 Bluetooth :: instance () -> b l u e t o o t h E x c e p t i o n R e a d I n p u t S t r e a m () ;
168 }
169
170 // /
282 APPENDIX B. GUI CODE
171 // / \ brief b l u e t o o t h E x c e p t i o n W r i t e O u t p u t S t r e a m
172 // /
173 static void b l u e t o o t h E x c e p t i o n W r i t e O u t p u t S t r e a m ( JNIEnv * /* env */ , jclass /*
clazz */ )
174 {
175 Bluetooth :: instance () -> b l u e t o o t h E x c e p t i o n W r i t e O u t p u t S t r e a m () ;
176 }
177
178 static J N IN at i ve Me t ho d methods [] = {
179 { " b l u e t o o t h D e v i c e F o u n d " , " ( Ljava / lang / String ;) V " , ( void *)
bluetoothDeviceFound },
180 { " b l u e t o o t h D i s c o v e r y S t a r t e d " , " () V " , ( void *) b l u e t o o t h D i s c o v e r y S t a r t e d } ,
181 { " b l u e t o o t h S e a r c h D o n e " , " () V " , ( void *) b l u e t o o t h S e a r c h D o n e } ,
182 { " b l u e t o o t h A d a p t e r N o t F o u n d " , " () V " , ( void *) b l u e t o o t h A d a p t e r N o t F o u n d } ,
183 { " b l u e t o o t h D a t a R e c e i v e d " , " ( Ljava / lang / String ;) V " , ( void *)
bluetoothDataReceived },
184 { " b l u e t o o t h E x c e p t i o n C r e a t e R F C O M M " , " () V " , ( void *)
bluetoothExceptionCreateRFCOMM },
185 { " b l u e t o o t h E x c e p t i o n J o i n T h r e a d " , " () V " , ( void *)
bluetoothExceptionJoinThread },
186 { " b l u e t o o t h E x c e p t i o n I n p u t O u t p u t S t r e a m " , " () V " , ( void *)
bluetoothExceptionInputOutputStream },
187 { " b l u e t o o t h E x c e p t i o n R e a d I n p u t S t r e a m " , " () V " , ( void *)
bluetoothExceptionReadInputStream },
188 { " b l u e t o o t h E x c e p t i o n W r i t e O u t p u t S t r e a m " , " () V " , ( void *)
bluetoothExceptionWriteOutputStream }
189 };
190
191 // /
192 // / \ brief JNI_OnLoad
193 // / \ param vm
194 // /
195 jint JNICALL JNI_OnLoad ( JavaVM * vm , void *)
196 {
197 JNIEnv * env ;
198 if ( vm - > GetEnv ( reinterpret_cast < void ** >(& env ) , J NI _V E RS IO N _1 _4 ) !=
JNI_OK )
199 return JNI_FALSE ;
200
201 jclass clazz = env - > FindClass ( " org / qtproject / qt5 / android / bindings /
MyActivity " ) ;
202 if ( env - > R eg is t er N at iv e s ( clazz , methods , sizeof ( methods ) / sizeof (
methods [0]) ) < 0)
203 return JNI_FALSE ;
204
205 return J N I_ VE R SI ON _ 1_ 4 ;
206 }
207
208 // Dummy slots
209 void Bluetooth :: se rialDa taRead ()
210 {
211
212 }
213
214 void Bluetooth :: write ( QByteArray byteArray )
215 {
216 QString string ( byteArray ) ;
217
218 // Convert QString to Java string .
219 Q A n d r o i d J n i O b j e c t javaString = Q A n d r o i d J n i O b j e c t :: fromString ( string ) ;
220
221 // Send data
B.2. CODE: C++ 283
222 Q A n d r o i d J n i O b j e c t :: callStaticMethod < void >( " org / qtproject / qt5 / android /
bindings / MyActivity " , " Blueto othWri te " , " ( Ljava / lang / String ;) V " ,
javaString . object < jstring >() ) ;
223 }
224
225 void Bluetooth :: o n _ c o u l d N o t C o n n e c t ()
226 {
227
228 }
229
230 void Bluetooth :: on_connected ()
231 {
232
233 }
234
235 void Bluetooth :: o n _ p o r t S e t t i n g s F a i l e d ()
236 {
237
238 }
239
240 void Bluetooth :: on_openPort ()
241 {
242
243 }
284 APPENDIX B. GUI CODE
59
60 for ( int i = 0; i < ports - > length () ; i ++)
61 {
62 Q Se ri a lP or t In f o temp = ports - > at ( i ) ;
63 qDebug () << " Description : " + temp . description () ;
64 qDebug () << " Manufacturer : " + temp . manufacturer () ;
65 qDebug () << " Port name : " + temp . portName () ;
66 qDebug () << " System Location : " + temp . s ystemL ocatio n () + " \ n " ;
67 emit b l u e t o o t h D e v i c e F o u n d ( temp . portName () + " : " + temp . description
() ) ;
68 }
69 }
70
71 void Bluetooth :: write ( QString string )
72 {
73 QByteArray dataToWrite = string . toLatin1 () ;
74 serialPort - > write ( dataToWrite ) ;
75 }
76
77 void Bluetooth :: write ( QByteArray byteArray )
78 {
79 serialPort - > write ( byteArray ) ;
80 }
81
82 void Bluetooth :: de viceSe lected ()
83 {
84 QStringList comPort ;
85
86 comPort = bluetoothSelectedDevice - > split ( " : " ) ;
87
88 qDebug () << comPort . at (0) ;
89
90 // Create COM port object to be used with QSerialPort by providing COM
port in text .
91 Q Se ri a lP or t In f o temp ( comPort . at (0) ) ;
92
93 if ( temp . isBusy () )
94 {
95 qDebug () << " COM port is already open ! " ;
96 return ;
97 }
98
99 // Create serial port object .
100 serialPort = new QSerialPort ( temp ) ;
101
102 // Try to connect .
103 if (! serialPort - > open ( QIODevice :: ReadWrite ) )
104 {
105 qDebug () << " Failed to open port " ;
106 emit c ou ld N ot Co n ne ct () ;
107 return ;
108 }
109
110 // Set port settings .
111 if ( serialPort - > setBaudRate ( QSerialPort :: Baud115200 ) &&
112 serialPort - > setDataBits ( QSerialPort :: Data8 ) &&
113 serialPort - > setParity ( QSerialPort :: NoParity ) &&
114 serialPort - > setStopBits ( QSerialPort :: OneStop ) &&
115 serialPort - > setFlo wContr ol ( QSerialPort :: NoFlowControl ) != true )
116 {
117 qDebug () << " Failed to configure port " ;
118 emit c ou ld N ot Co n ne ct () ;
286 APPENDIX B. GUI CODE
119 // emit p o r t S e t t i n g s F a i l e d () ;
120 }
121
122 emit connected () ;
123
124 QObject :: connect ( serialPort , SIGNAL ( readyRead () ) , this , SLOT (
seri alData Read () ) ) ;
125 }
126
127 void Bluetooth :: o n _ c o u l d N o t C o n n e c t ()
128 {
129 emit c ou ld N ot Co n ne c t () ;
130 }
131
132 void Bluetooth :: on_connected ()
133 {
134 emit connected () ;
135 QObject :: connect ( serialPort , SIGNAL ( readyRead () ) , this , SLOT (
seri alData Read () ) ) ;
136 }
137
138 void Bluetooth :: o n _ p o r t S e t t i n g s F a i l e d ()
139 {
140 emit c ou ld N ot Co n ne c t () ;
141 }
142
143 void Bluetooth :: on_openPort ()
144 {
145 // Try to connect 5 times .
146 for ( int i = 0; i < 5; i ++)
147 {
148
149 if (! serialPort - > open ( QIODevice :: ReadWrite ) )
150 {
151 qDebug () << " Failed to open port " ;
152 if ( i == 4)
153 {
154 emit c ou ld N ot Co n nect () ;
155 return ;
156 }
157 }
158 else
159 {
160 break ;
161 }
162 }
163
164 // Set port settings .
165 if ( serialPort - > setBaudRate ( QSerialPort :: Baud115200 ) &&
166 serialPort - > setDataBits ( QSerialPort :: Data8 ) &&
167 serialPort - > setParity ( QSerialPort :: NoParity ) &&
168 serialPort - > setStopBits ( QSerialPort :: OneStop ) &&
169 serialPort - > setFlo wContr ol ( QSerialPort :: NoFlowControl ) != true )
170 {
171 qDebug () << " Failed to configure port " ;
172 // emit p o r t S e t t i n g s F a i l e d () ;
173 }
174
175 // emit connected () ;
176 }
B.2. CODE: C++ 287
115 axesRec - > axis ( QCPAxis :: atRight ) -> grid () -> setSubGridPen ( QPen ( QBrush (
QColor (0 ,0 ,255 ,20) ) ,1) ) ;
116 axesRec - > axis ( QCPAxis :: atRight ) -> grid () -> s e t S u b G r i d V i s i b l e ( true ) ;
117
118 QFont rightAxisFont = font () ;
119 rightAxisFont . setPointSize (10) ;
120 rightAxisFont . setBold ( true ) ;
121 axesRec - > axis ( QCPAxis :: atRight ) -> setLabelFont ( rightAxisFont ) ;
122 axesRec - > axis ( QCPAxis :: atRight ) -> setLabel ( " Phase Angle [ degrees ] " ) ;
123
124 // Create legend .
125 ui - > realtimePlot - > legend = new QCPLegend () ;
126
127 // Add legend to graph .
128 ui - > realtimePlot - > legend - > setVisible ( true ) ;
129 axesRec - > insetLayout () -> addElement ( ui - > realtimePlot - > legend , Qt ::
AlignTop | Qt :: AlignRight ) ;
130
131 // Create two graph objects . One for amplitude and one for phase .
132 r e a l t i m e P l o t A m p l i t u d e G r a p h = ui - > realtimePlot - > addGraph ( axesRec - > axis (
QCPAxis :: atBottom ) , axesRec - > axis ( QCPAxis :: atLeft ) ) ;
133 r e a l t i m e P l o t P h a s e G r a p h = ui - > realtimePlot - > addGraph ( axesRec - > axis (
QCPAxis :: atBottom ) , axesRec - > axis ( QCPAxis :: atRight ) ) ;
134
135 // Customize amplitude graph .
136 realtimePlotAmplitudeGraph - > setPen ( QPen ( Qt :: red ) ) ;
137 realtimePlotAmplitudeGraph - > setLineStyle ( QCPGraph :: lsLine ) ;
138 realtimePlotAmplitudeGraph - > setName ( " Amplitude " ) ;
139
140 // Customize phase graph .
141 realtimePlotPhaseGraph - > setPen ( QPen ( Qt :: blue ) ) ;
142 realtimePlotPhaseGraph - > setLineStyle ( QCPGraph :: lsLine ) ;
143 realtimePlotPhaseGraph - > setName ( " Phase " ) ;
144
145 // Customize legend .
146 QFont legendFont = font () ;
147 legendFont . setBold ( true ) ;
148 legendFont . setPointSize (10) ;
149 ui - > realtimePlot - > legend - > item (0) -> setFont ( legendFont ) ;
150 ui - > realtimePlot - > legend - > item (1) -> setFont ( legendFont ) ;
151 ui - > realtimePlot - > legend - > item (0) -> setTextColor ( QColor ( Qt :: black ) ) ;
152 ui - > realtimePlot - > legend - > item (1) -> setTextColor ( QColor ( Qt :: black ) ) ;
153 }
154
155 void M e a s u r e m e n t P l o t s :: r e a l t i m e P l o t D a t a S l o t ( QString amplitude , QString phase
)
156 {
157 // Previous time stamp .
158 static double previousTime = 0;
159
160 // Get current date / time in millisecond resolution .
161 double currentTime = QDateTime :: cu r re nt D at eT i me () . t o M S e c s S i n c e E p o c h ()
/1000.0;
162
163 // Only update every 50 ms at most .
164 if ( currentTime - previousTime > 0.05)
165 {
166 double amplitudeData = amplitude . toDouble () ;
167 double phaseData = phase . toDouble () ;
168
169 // Add new amplitude and phase data .
170 realtimePlotAmplitudeGraph - > addData ( currentTime , amplitudeData ) ;
B.2. CODE: C++ 293
229 axesRec - > axis ( QCPAxis :: atLeft ) -> setLabel ( " Impedance Modulus [ " + QString
( QChar (0 x03A9 ) ) + " ] " ) ;
230
231 // Configure right / phase axis .
232 // axesRec - > axis ( QCPAxis :: atRight ) -> setRange ( -180.1 , 180.1) ;
233 axesRec - > axis ( QCPAxis :: atRight ) -> setRange ( -90 , 90) ;
234 axesRec - > axis ( QCPAxis :: atRight ) -> s e t R a n g e R e v e r s e d ( true ) ;
235 axesRec - > axis ( QCPAxis :: atRight ) -> grid () -> setPen ( QPen ( QBrush ( QColor
(0 ,0 ,255 ,80) ) ,1) ) ;
236 axesRec - > axis ( QCPAxis :: atRight ) -> grid () -> setVisible ( true ) ;
237 axesRec - > axis ( QCPAxis :: atRight ) -> grid () -> setSubGridPen ( QPen ( QBrush (
QColor (0 ,0 ,255 ,20) ) ,1) ) ;
238 axesRec - > axis ( QCPAxis :: atRight ) -> grid () -> s e t S u b G r i d V i s i b l e ( true ) ;
239 QFont rightAxisFont = font () ;
240 rightAxisFont . setPointSize (10) ;
241 rightAxisFont . setBold ( true ) ;
242 axesRec - > axis ( QCPAxis :: atRight ) -> setLabelFont ( rightAxisFont ) ;
243 axesRec - > axis ( QCPAxis :: atRight ) -> setLabel ( " Phase Angle [ degrees ] " ) ;
244
245 // Create legend .
246 ui - > staticPlot - > legend = new QCPLegend () ;
247
248 // Add legend to graph .
249 ui - > staticPlot - > legend - > setVisible ( true ) ;
250 axesRec - > insetLayout () -> addElement ( ui - > staticPlot - > legend , Qt :: AlignTop
| Qt :: AlignRight ) ;
251
252 // Create two graph objects . One for amplitude and one for phase .
253 s t a t i c P l o t A m p l i t u d e G r a p h = ui - > staticPlot - > addGraph ( axesRec - > axis (
QCPAxis :: atBottom ) , axesRec - > axis ( QCPAxis :: atLeft ) ) ;
254 s t a t i c P l o t P h a s e G r a p h = ui - > staticPlot - > addGraph ( axesRec - > axis ( QCPAxis ::
atBottom ) , axesRec - > axis ( QCPAxis :: atRight ) ) ;
255
256 // Customize amplitude graph .
257 staticPlotAmplitudeGraph - > setPen ( QPen ( Qt :: red ) ) ;
258 staticPlotAmplitudeGraph - > setLineStyle ( QCPGraph :: lsLine ) ;
259 staticPlotAmplitudeGraph - > setName ( " Amplitude " ) ;
260
261 // Customize phase graph .
262 staticPlotPhaseGraph - > setPen ( QPen ( Qt :: blue ) ) ;
263 staticPlotPhaseGraph - > setLineStyle ( QCPGraph :: lsLine ) ;
264 staticPlotPhaseGraph - > setName ( " Phase " ) ;
265
266 // Customize legend .
267 QFont legendFont = font () ;
268 legendFont . setBold ( true ) ;
269 legendFont . setPointSize (10) ;
270 ui - > staticPlot - > legend - > item (0) -> setFont ( legendFont ) ;
271 ui - > staticPlot - > legend - > item (1) -> setFont ( legendFont ) ;
272 ui - > staticPlot - > legend - > item (0) -> setTextColor ( QColor ( Qt :: black ) ) ;
273 ui - > staticPlot - > legend - > item (1) -> setTextColor ( QColor ( Qt :: black ) ) ;
274 }
275
276 void M e a s u r e m e n t P l o t s :: o n T o g g l e P l o t V i e w ()
277 {
278 if ( ui - > staticPlot - > isVisible () && ui - > realtimePlot - > isVisible () )
279 {
280 ui - > staticPlot - > setVisible ( false ) ;
281 }
282 else if (! ui - > staticPlot - > isVisible () && ui - > realtimePlot - > isVisible () )
283 {
284 ui - > staticPlot - > setVisible ( true ) ;
B.2. CODE: C++ 295
61 };
62
63 enum M e s s a g e I D I n c o m i n g L e n g t h
64 {
65 DDS _MSG_L ENGTH = 2,
66 CONTINUOUS_SINGLE_FREQUENCY_4FS_MSG_LENGTH = 3,
67 FULL_FREQUENCY_SWEEP_4FS_MSG_LENGTH = 0,
68 QUICK_FREQUENCY_SWEEP_4FS_MSG_LENGTH = 0,
69 CONTINUOUS_SINGLE_FREQUENCY_NORMAL_MSG_LENGTH = 0,
70 FULL_FREQUENCY_SWEEP_NORMAL_MSG_LENGTH = 0,
71 QUICK_FREQUENCY_SWEEP_NORMAL_MSG_LENGTH = 0,
72 SYSTEM_STATUS_MSG_LENGTH = 2,
73 CURRENT_MSG_LENGTH = 2,
74 RESISTANCE_REACTANCE_MSG_LENGTH = 3,
75 CONDUCTANCE_SUSCEPTANCE_MSG_LENGTH = 3,
76 IMPEDANCE_ADMITTANCE_MSG_LENGTH = 3,
77 SPECTROSCOPY_DATA_MSG_LENGTH = 4,
78 CSFM_FREQUENCY_MSG_LENGTH = 2,
79 EGG_STATE_MSG_LENGTH = 2,
80
81 CALIBRATION_STATUS_MSG_LENGTH = 2,
82 HEARTBEAT_MSG_LENGTH = 2
83 };
84
85 # endif // MESSAGES_H
B.2. CODE: C++ 299
60 }
61 msgCounter - -;
62 return messages - > takeFirst () ;
63 }
64
65 QString Receive :: parseMessage ()
66 {
67 // Get message .
68 QString msg = getMsg () ;
69
70 // Check if message is empty .
71 if ( msg == NULL )
72 {
73 return " " ;
74 }
75
76 // Check if message is ok .
77 if (! is M sg Ch e ck su m OK ( msg ) )
78 {
79 return " " ;
80 }
81
82 QStringList msgFields = msg . split ( " : " ) ;
83
84 if ( msgFields . at (0) == " !!! " ) qDebug () << msgFields . at (0) + msgFields . at
(1) ;
85
86 switch ( msgFields . at (0) . toInt () ) {
87
88 case DDS :
89 if ( msgFields . length () != DDS_M SG_LEN GTH )
90 {
91 QString temp ;
92
93 temp += " Message corrupted . DDS : " ;
94
95 for ( int i = 0; i < msgFields . length () ; i ++)
96 {
97 if ( i ==0) temp += " Field 0 ( MSG type ) : " + msgFields . at (0) ;
98 if ( i ==1) temp += " Field 1 ( Amplitude ) : " + msgFields . at (1) ;
99 if (i >=2) temp += " Field " + QString :: number ( i ) + " ( unknown )
: " + msgFields . at ( i ) ;
100 }
101
102 qDebug () << temp ;
103 return " " ;
104 }
105
106 qDebug () << " DDS amplitude : " + msgFields . at (1) ;
107 break ;
108
109 case C O N T I N U O U S _ S I N G L E _ F R E Q U E N C Y _ 4 F S :
110 if ( msgFields . length () != C O N T I N U O U S _ S I N G L E _ F R E Q U E N C Y _ 4 F S _ M S G _ L E N G T H )
111 {
112 QString temp ;
113
114 temp += " Message corrupted . CSF4FS : " ;
115
116 for ( int i = 0; i < msgFields . length () ; i ++)
117 {
118 if ( i ==0) temp += " Field 0 ( MSG type ) : " + msgFields . at (0) ;
119 if ( i ==1) temp += " Field 1 ( Amplitude ) : " + msgFields . at (1) ;
302 APPENDIX B. GUI CODE
114 // / / / / / / / / / / / / / / / / / / /
115
116 // Native methods are used to signal the C ++ side of the application
and are used as Qt signals .
117 private static native void b l u e t o o t h D e v i c e F o u n d ( String device ) ;
118 private static native void b l u e t o o t h D i s c o v e r y S t a r t e d () ;
119 private static native void b l u e t o o t h S e a r c h D o n e () ;
120 private static native void b l u e t o o t h A d a p t e r N o t F o u n d () ;
121 private static native void b l u e t o o t h D a t a R e c e i v e d ( String data ) ;
122 private static native void b l u e t o o t h E x c e p t i o n C r e a t e R F C O M M () ;
123 private static native void b l u e t o o t h E x c e p t i o n J o i n T h r e a d () ;
124 private static native void b l u e t o o t h E x c e p t i o n I n p u t O u t p u t S t r e a m () ;
125 private static native void b l u e t o o t h E x c e p t i o n R e a d I n p u t S t r e a m () ;
126 private static native void b l u e t o o t h E x c e p t i o n W r i t e O u t p u t S t r e a m () ;
127
128 // ////////////
129 // Methods . //
130 // ////////////
131
132 // Get used UUID .
133 public static String B l u e t o o t h G e t U U I D () {
134 return uuid . toString () ;
135 }
136
137 // Check if bluetooth hardware is available on device and ask user to
enable bluetooth if not already enabled .
138 public static void B l u e t o o t h C o n n e c t ()
139 {
140 Log . d ( QtApplication . QtTAG , " JAVA : MyActivity . Bluetooth . connect ()
");
141 singletonInstance . bluetoothAdapter = BluetoothAdapter .
g e t D e f a u l t A d a p t e r () ;
142 if ( s i n g l e t o n I n s t a n c e . b l u e t o o t h A d a p t e r == null )
143 {
144 Log . d ( QtApplication . QtTAG , " JAVA : Bluetooth adapter is not
found " ) ;
145
146 // Alert user that bluetooth hardware not found on this
device .
147 b l u e t o o t h A d a p t e r N o t F o u n d () ;
148 return ;
149 }
150
151 if (! s i n g l e t o n I n s t a n c e . b l u e t o o t h A d a p t e r . isEnabled () )
152 {
153 Log . d ( QtApplication . QtTAG , " JAVA : Bluetooth is off " ) ;
154 Intent e n a b l e B l u e t o o t h I n t e n t = new Intent ( B l u e t o o t h A d a p t e r .
ACTION_REQUEST_ENABLE );
155 singletonInstance . startActivityForResult (
enableBluetoothIntent , E N A B L E _ B L U E T O O T H _ R E Q U E S T ) ;
156 }
157 else
158 {
159 Log . d ( QtApplication . QtTAG , " JAVA : Bluetooth is already on " ) ;
160 }
161 }
162
163 // Starts searching for nearby bluetooth devices .
164 public static void B l u e t o o t h S t a r t D i s c o v e r y ()
165 {
166 // Add " bluetooth device found " filter to bluetooth receiver .
167 IntentFilter filter = new IntentFilter ( Bl ue t oo th D ev i ce .
310 APPENDIX B. GUI CODE
ACTION_FOUND ) ;
168 singletonInstance . registerReceiver ( singletonInstance .
BluetoothReceiver , filter ) ;
169
170 // Add " bluetooth device discovery finished " filter to bluetooth
receiver .
171 filter = new IntentFilter ( B l u e t o o t h A d a p t e r .
ACTION_DISCOVERY_FINISHED );
172 singletonInstance . registerReceiver ( singletonInstance .
BluetoothReceiver , filter ) ;
173
174 // If already discovering , stop .
175 B l u e t o o t h C a n c e l D i s c o v e r y () ;
176
177 // Notify C ++ that discovery has started .
178 b l u e t o o t h D i s c o v e r y S t a r t e d () ;
179
180 // Start discovery
181 s i n g l e t o n I n s t a n c e . b l u e t o o t h A d a p t e r . s tartDi scover y () ;
182 }
183
184 public static void B l u e t o o t h C a n c e l D i s c o v e r y ()
185 {
186 // If already discovering , stop .
187 if ( s i n g l e t o n I n s t a n c e . b l u e t o o t h A d a p t e r . isDiscovering () ) {
188 s i n g l e t o n I n s t a n c e . b l u e t o o t h A d a p t e r . c an ce l Di sc o ve ry () ;
189 }
190 }
191
192 // Connect to device with macAddress .
193 public static void B l u e t o o t h C o n n e c t T o D e v i c e ( String macAddress )
194 {
195 Log . d ( QtApplication . QtTAG , " MAC address for received device : " +
macAddress ) ;
196
197 // Checks if MAC address is in a valid format
198 if (! B l u e t o o t h A d a p t e r . c h e c k B l u e t o o t h A d d r e s s ( macAddress ) ) {
199 s i n g l e t o n I n s t a n c e . finish () ;
200 }
201
202 // Get a device object from MAC address
203 s i n g l e t o n I n s t a n c e . device = s i n g l e t o n I n s t a n c e . b l u e t o o t h A d a p t e r .
g et Re m ot e De vi c e ( macAddress ) ;
204
205 B lu et o ot hS o ck e t tempSocket = null ;
206
207 // Try to connect a bluetooth socket with the device
208 try {
209 // SPP ( Serial Port Protocol ) UUID . Create RFCOMM bluetooth
socket .
210 tempSocket = s i n g l e t o n I n s t a n c e . device .
c r e a t e R f c o m m S o c k e t T o S e r v i c e R e c o r d ( uuid ) ;
211 } catch ( IOException e ) {
212 b l u e t o o t h E x c e p t i o n C r e a t e R F C O M M () ;
213 }
214
215 // Assign the successfully received tempSocket to bl ue t oo th S oc ke t
.
216 s i n g l e t o n I n s t a n c e . bl u et oo t hS oc k et = tempSocket ;
217
218 s i n g l e t o n I n s t a n c e . b l u e t o o t h C o n n e c t T h r e a d = new
B l u e t o o t h C o n n e c t T h r e a d ( s i n g l e t o n I n s t a n c e . b lu e to ot h So c ke t ) ;
B.3. CODE: JAVA 311
219 s i n g l e t o n I n s t a n c e . b l u e t o o t h C o n n e c t T h r e a d . start () ;
220
221 try {
222 // Wait for thread that tries to connect to b lu e to o th so c ke t .
223 s i n g l e t o n I n s t a n c e . b l u e t o o t h C o n n e c t T h r e a d . join () ;
224 } catch ( I n t e r r u p t e d E x c e p t i o n e ) {
225 b l u e t o o t h E x c e p t i o n J o i n T h r e a d () ;
226 }
227
228 try {
229 s i n g l e t o n I n s t a n c e . inputStream = s i n g l e t o n I n s t a n c e .
b lu et o ot h So ck e t . getInp utStre am () ;
230 s i n g l e t o n I n s t a n c e . outputStream = s i n g l e t o n I n s t a n c e .
b lu et o ot h So ck e t . g et Ou t pu t St re a m () ;
231 } catch ( IOException e ) {
232 b l u e t o o t h E x c e p t i o n I n p u t O u t p u t S t r e a m () ;
233 }
234
235 s i n g l e t o n I n s t a n c e . B l u e t o o t h S t a r t D a t a T r a n s f e r T h r e a d () ;
236 }
237
238 // Bluetooth receive data thread .
239 private void B l u e t o o t h S t a r t D a t a T r a n s f e r T h r e a d ()
240 {
241 Thread b l u e t o o t h D a t a T r a n s f e r T h r e a d = new Thread () {
242 public void run () {
243 byte [] buffer = new byte [ i n c o m i n g M s g Q u e u e S i z e ];
244 int bytesRead , i ;
245 String bufferString = " " ;
246 Bundle bundle = new Bundle () ;
247
248 // Listen for incoming data .
249 while ( true ) {
250 try {
251 // Read incoming data stream .
252 bytesRead = inputStream . read ( buffer , 0 ,
incomingMsgQueueSize );
253 } catch ( IOException e ) {
254 b l u e t o o t h E x c e p t i o n R e a d I n p u t S t r e a m () ;
255 break ;
256 }
257
258 if ( ( bytesRead > 0) && ( buffer . length != 0) ) {
259 // Get data from buffer to string .
260 bufferString = new String ( buffer , 0 , bytesRead ) ;
261
262 if ( bufferString == null ) {
263 continue ;
264 }
265
266 // Create Inter Thread Communication message .
267 Message msg = Message . obtain () ;
268
269 // Add data to message .
270 bundle . putString ( " R e c e i v e d B l u e t o o t h D a t a " ,
bufferString ) ;
271 msg . setData ( bundle ) ;
272
273 // Send message to handler .
274 B l u e t o o t h R e c e i v e M e s s a g e H a n d l e r . sendMessage ( msg ) ;
275 }
276 }
312 APPENDIX B. GUI CODE
277 }
278 };
279
280 // Start thread .
281 b l u e t o o t h D a t a T r a n s f e r T h r e a d . start () ;
282 }
283
284 // Received bluetooth data handler .
285 private Handler B l u e t o o t h R e c e i v e M e s s a g e H a n d l e r = new Handler () {
286 @Override
287 public void handleMessage ( Message msg ) {
288 // Get message data .
289 Bundle bundle = msg . getData () ;
290
291 // Send string to C ++.
292 String temp = bundle . getString ( " R e c e i v e d B l u e t o o t h D a t a " ) ;
293 if ( temp != null )
294 {
295 b l u e t o o t h D a t a R e c e i v e d ( temp ) ;
296 }
297 }
298 };
299
300 // Write data to connected bluetooth device .
301 public static void Blu etooth Write ( String string ) {
302 Log . d ( QtApplication . QtTAG , " JAVA : Text to send : " + string ) ;
303 s i n g l e t o n I n s t a n c e . bytesSend = string . getBytes () ;
304 try {
305 s i n g l e t o n I n s t a n c e . outputStream . write ( s i n g l e t o n I n s t a n c e .
bytesSend ) ;
306 } catch ( IOException e ) {
307 b l u e t o o t h E x c e p t i o n W r i t e O u t p u t S t r e a m () ;
308 }
309 }
310
311 // / / / / / / / / / / / / / / / / / / / / / / / /
312 // Broadcast Receivers . //
313 // / / / / / / / / / / / / / / / / / / / / / / / /
314
315 private final B r o a d c a s t R e c e i v e r B l u e t o o t h R e c e i v e r = new
B r o a d c a s t R e c e i v e r () {
316 public void onReceive ( Context context , Intent intent ) {
317 String action = intent . getAction () ;
318
319 if ( Bl u et oo t hD e vi ce . ACTION_FOUND . equals ( action ) ) {
320 // New device discovered .
321
322 // Get the bluetooth device object .
323 B lu et o ot hD e vi ce device = intent . g e t P a r c e l a b l e E x t r a (
B lu et o ot h De vi c e . EXTRA_DEVICE ) ;
324
325 // Send device information to C ++. 17 last characters is
MAC address .
326 b l u e t o o t h D e v i c e F o u n d ( device . getName () + device .
getAddress () ) ;
327 }
328 else if ( B l u e t o o t h A d a p t e r . A C T I O N _ D I S C O V E R Y _ F I N I S H E D . equals (
action ) ) {
329 // Notify C ++ that discovery is done .
330 b l u e t o o t h S e a r c h D o n e () ;
331 }
332 }
B.3. CODE: JAVA 313
333 };
334 }
335
336 class B l u e t o o t h C o n n e c t T h r e a d extends Thread {
337 private final B l ue to o th So c ke t b l ue to o th S oc ke t ;
338
339 public B l u e t o o t h C o n n e c t T h r e a d ( B lu e to ot h So ck e t b lu e to ot h So ck e t ) {
340 this . bl u et oo t hS o ck et = b l ue t oo th S oc ke t ;
341 }
342
343 public void run () {
344 try {
345 // Try to connect to b lu e to ot h So ck e t .
346 b lu et o ot hS o ck e t . connect () ;
347 } catch ( IOException c o n n e c t E x c e p t i o n ) {
348
349 // If unable to connect , try to close socket and return .
350 try {
351 b lu et o ot hS o ck e t . close () ;
352 } catch ( IOException closeE xcepti on ) {}
353
354 return ;
355 }
356 }
357 }
314 APPENDIX B. GUI CODE
Python Code
319
320 APPENDIX C. PYTHON CODE
39 dataArray = []
40 continue
41 # Show incoming data in graph
42 if plotEnable == True :
43 plotEnable = False
44
45 # Remove new line characters ’\ n ’
46 tempString = string [: -2]
47
48 # Split comma - separated values
49 tempList = tempString . split ( ’ , ’)
50
51 # Make numpy compatible array
52 yDataArray = np . array ( tempList [0])
53 for i in range (1 , len ( tempList ) - 1) :
54 yDataArray = np . append ( yDataArray , tempList [ i ])
55
56 # Make and show plot
57 plt . plot ( yDataArray )
58 plt . show ()
59
60
61 # Print values to console
62 print ( string )
63
64 # Write values to file
65 fileID = open ( outputFileName , fileP ermiss ion )
66 fileID . write ( string + " \ r \ n " )
67 fileID . close ()
68 string = " "
69 # string2 = ""
70 dataArray = []
71
72 # Main thread
73 if __name__ == " __main__ " :
74
75 # Key parameters
76 port = ’ COM8 ’
77 baudrate = 115200
78 time outSec onds = 0
79 encoding = ’UTF -8 ’
80 exitString = " _exit "
81 outp utFile Name = ’ output . txt ’
82 file Permis sion = ’w ’
83
84 # Set variables
85 writeString = " "
86
87 # Open serial port
88 port = serial . Serial ( port , baudrate , timeout = t imeout Second s )
89
90 # Clear file by opening in write mode
91 fileID = open ( outputFileName , file Permis sion )
92
93 # Change file permission to append
94 file Permis sion = ’a ’
95
96 # Spawn read thread
97 readThread = Thread ( target = readThreadMethod , args = () )
98 readThread . start ()
99
100 # Capture user input and send over established serial connection
321
Matlab Code
323
324 APPENDIX D. MATLAB CODE
Schematics
331
APPENDIX E. SCHEMATICS
R
R19 R20
219 Ω 706 Ω
IN CC
U2
U1 OPA350
R8 OPA350
C1 9.986 kΩ
DDS 1 µF
sine
100 kHz
VG
CMP
VG CMP1
V1 R
3.3 V
R10 VG M
999.6 Ω U3
OPA350
Biological Tissue
CC
R11 C4
998.3 Ω 47 nF
Figure E.1: Excitation part of the analog front-end.
332
C5
10 pF
R23 R13
679.3 Ω 10.11 kΩ R22 R16
M 680.1 Ω 1.049 kΩ R18 R24
TIA 676.5 Ω 103.2 Ω
U4 GS1
OPA350 U5
R15 AFEout
OPA350 U6
997.4 Ω R17 OPA350
1.000 kΩ
VG
VG
VG
U1 JP2 5V
JP1 5V PA0 23 81 PD0
PA0/WKUP/USART2_CTS/ADC123_IN0/TIM5_CH1/TIM2_CH1_ETR/TIM8_ETR PD0/FSMC_D2/CAN_RX FSMC_D2 1 2
PA1 24 82 PD1 PB12 PB13
1 2 PA1/USART2_RTS/ADC123_IN1/TIM5_CH2/TIM2_CH2 PD1/FSMC_D3/CAN_TX FSMC_D3 3 4
PE0 PE1 PA2 25 83 PD2 PB14 PB15
3 4 PA2/USART2_TX/TIM5_CH3/ADC123_IN2/TIM2_CH3 PD2/TIM3_ETR/USART5_RX/SDIO_CMD SDIO_CMD 5 6
PE2 PE3 PA3 26 84 PD3 PD8 PD9
5 6 PA3/USART2_RX/TIM5_CH4/ADC123_IN3/TIM2_CH4 PD3/FSMC_CLK/USART2_CTS SD_CD 7 8
PE4 PE5 PA4 29 85 PD4 PD10 PD11
7 8 TP_CS PA4/SPI1_NSS/DAC_OUT1/USART2_CK/ADC12_IN4 PD4/FSMC_NOE/USART2_RTS LCD_RD 9 10
PE6 VBAT PA5 30 86 PD5 PD12 PD13
9 10 SPI_SCK PA5/SPI1_SCK/DAC_OUT2/ADC12_IN5 PD5/FSMC_NWE/USART2_TX LCD_WR 11 12
D PC13 PC14 PA6 31 87 PD6 PD14 PD15 D
11 12 SPI_MISO PA6/SPI1_MISO/TIM8_BKIN/ADC12_IN6/TIM3_CH1/TIM1_BKIN PD6/FSMC_NWAIT/USART2_RX 13 14
PC15 PC0 PA7 32 88 PD7 PC6 PC7
13 14 SPI_MOSI PA7/SPI1_MOSI/TIM8_CH1N/ADC12_IN7/TIM3_CH2/TIM1_CHIN PD7/FSMC_NE1/FSMC_NCE2/USART2_CK LCD_CS 15 16
PC1 PC2 PC8 PC9
15 16 17 18
PC3 GND PA8 67 55 PD8 PA8 PA9
17 18 PA8/USART1_CK/TIM1_CH1/MCO PD8/FSMC_D13/USART3_TX FSMC_D13 19 20
GND VREF+ PA9 68 56 PD9 PA10 PA11
19 20 USART1_TX PA9/USART1_TX/TIM1_CH2 PD9/FSMC_D14/USART3_RX FSMC_D14 21 22
VDDA PA0 PA10 69 57 PD10 PA12 PA13
21 22 USART1_RX PA10/USART1_RX/TIM1_CH3 PD10/FSMC_D15/USART3_CK FSMC_D15 23 24
PA1 PA2 PA11 70 58 PD11 PA14 PA15
23 24 USB_DM PA11/USART1_CTS/CAN_RX/TIM1_CH4/USB_DM PD11/FSMC_A16/USART3_CTS LCD_RS 25 26
PA3 PA4 PA12 71 59 PD12 GND 3V3
25 26 USB_DP PA12/USART1_RTS/CAN_TX/TIM1_ETR/USB_DP PD12/FSMC_A17/TIM4_CH1/USART3_RTS 27 28
PA5 PA6 PA13 72 60 PD13 PC10 PC11
27 28 JTMS PA13/JTMS/SWDIO PD13/FSMC_A18/TIM4_CH2 29 30
PA7 PC4 PA14 76 61 PD14 PC12 PD0
29 30 JTCK PA14/JTCK/SWCLK PD14/FSMC_D0/TIM4_CH3 FSMC_D0 31 32
PC5 PB0 PA15 77 62 PD15 PD1 PD2
31 32 JTDI PA15/JTDI/SPI3_NSS/I2S3_WS PD15/FSMC_D1/TIM4_CH4 FSMC_D1 33 34
PB1 PB2 PD3 PD4
33 34 35 36
PE7 PE8 PB0 35 97 PE0 PD5 PD6
35 36 LED1 PB0/ADC12_IN8/TIM3_CH3/TIM8_CH2N/TIM1_CH2N PE0/TIM4_ETR/FSMC_NBL0 37 38
PE9 PE10 PB1 36 98 PE1 PD7 PB3
37 38 LED2 PB1/ADC12_IN9/TIM3_CH4/TIM8_CH3N/TIM1_CH3N PE1/FSMC_NBL1 39 40
PE11 PE12 PB2 37 1 PE2 PB4 PB5
39 40 USER_KEYB PB2/BOOT1 PE2/TRACECK/FSMC_A23 41 42
PE13 PE14 PB3 89 2 PE3 PB6 PB7
41 42 JTDO PB3/JTDO/TRACESWO/SPI3_SCK/I2S3_CK/TIM2_CH2/SPI1_SCK PE3/TRACED0/FSMC_A19 43 44
PE15 PB10 PB4 90 3 PE4 PB8 PB9
43 44 JTRST PB4/NJTRST/SPI3_MISO/TIM3_CH1/SPI1_MISO PE4/TRACED1/FSMC_A20 45 46
PB11 GND PB5 91 4 PE5
45 46 BL_CNT PB5/I2C1_SMBA/SPI3_MOSI/I2S3_SD/TIM3_CH2/SPI1_MOSI PE5/TRACED2/FSMC_A21 47 48 3V3
PB6 92 5 PE6
47 48 3V3 TP_IRQ PB6/I2C1_SCL/TIM4_CH1/USART1_TX PE6/TRACED3/FSMC_A22
PB7 93 38 PE7 HEADER 24X2
USB_EN PB7/I2C1_SDA/FSMC_NADV/TIM4_CH2/USART1_RX PE7/FSMC_D4/TIM1_ETR FSMC_D4
HEADER 24X2
PB8 95 39 PE8
PB8/TIM4_CH3/SDIO_D4/I2C1_SCL/CAN_RX PE8/FSMC_D5/TIM1_CH1N FSMC_D5
PB9 96 40 PE9
PB9/TIM4_CH4/SDIO_D5/I2C1_SDA/CAN_TX PE9/FSMC_D6/TIM1_CH1 FSMC_D6
PB10 47 41 PE10
C PB10/I2C2_SCL/USART3_TX/TIM2_CH3 PE10/FSMC_D7/TIM1_CH2N FSMC_D7 C
PB11 48 42 PE11
PB11/I2C2_SDA/USART3_RX/TIM2_CH4 PE11/FSMC_D8/TIM1_CH2 FSMC_D8
PB12 51 43 PE12
PB12/SPI2_NSS/I2S2_WS/I2C2_SMBA/USART3_CK/TIM1_BKIN PE12/FSMC_D9/TIM1_CH3N FSMC_D9
PB13 52 44 PE13
PB13/SPI2_SCK/I2S2_CK/USART3_CTS/TIM1_CH1N PE13/FSMC_D10/TIM1_CH3 FSMC_D10
PB14 53 45 PE14
PB14/SPI2_MISO/TIM1_CH2N/USART3_RTS PE14/FSMC_D11/TIM1_CH4 FSMC_D11
PB15 54 46 PE15
PB15/SPI2_MOSI/I2S2_SD/TIM1_CH3N PE15/FSMC_D12/TIM1_BKIN FSMC_D12
PC0 15 73
PC0/ADC123_IN10 NC
PC1 16 C1
PC1/ADC123_IN11
PC2 17 22P
PC2/ADC123_IN12
PC3 18 12
PC3/ADC123_IN13 OSC_IN Y1
PC4 33 S1
PC4/ADC12_IN14 8M
PC5 34 13 BOOT0
PC5/ADC12_IN15 OSC_OUT
PC6 63 C2
PC6/I2S2_MCK/TIM8_CH1/SDIO_D6/TIM3_CH1
PC7 64 22P
PC7/I2S3_MCK/TIM8_CH2/SDIO_D7/TIM3_CH2 3V3
94 BOOT0
BOOT0 R15
PC8 65
SDIO_D0 PC8/TIM8_CH3/SDIO_D0/TIM3_CH3 R10
PC9 66 510R 10K
SDIO_D1 PC9/TIM8_CH4/SDIO_D1/TIM3_CH4
PC10 78 14 RST
SDIO_D2 PC10/USART4_TX/SDIO_D2/USART3_TX NRST RST
PC11 79
SDIO_D3 PC11/USART4_RX/SDIO_D3/USART3_RX
PC12 80 VBAT BT1
SDIO_CK PC12/USART5_TX/SDIO_CK/USART3_CK
PC13 7 6
USER_KEYA PC13/TAMPER-RTC VBAT
PC14 8
PC14/OSC32_IN R16
PC15 9 3V S2
PC15/OSC32_OUT 10K
B RST B
R1 R2 50 49 C7
0R 0R VDD_1 VSS_1 104
75 74
VDD_2 VSS_2
1 4 100 99
3V3 VDD_3 VSS_3
28 27
VDD_4 VSS_4
2 3 11 10 3V3
VDD_5 VSS_5
22 19
C3 C4 VDDA VDDA VSSA
Y2 21 20
10P 10P VREF+ VREF-
32.768K
VREF+ STM32F103VCT6
A A
Title
3V3
3V3 5V
R24
R17 R19 R21 R4 10K
10K 10K 10K 0R CN2 R5
R36 USER_KEYA
CN1 1 0R
USB_DM VBUS
D 8 10 22R D- 2 D
SDIO_D1 D1 GND D-(DM)
7 9 D+ 3 S3
SDIO_D0 D0 CD R37 D+(DP)
6 4 KEYA
VSS USB_DP ID
5 22R 5
SDIO_CK CLK R40 GND Button A
4
3V3 VDD 1K5
3 3V3 0
SDIO_CMD CMD SHIELD
2 2 3
SDIO_D3 CD/D3 E C
1 Q1 MINI-USB
SDIO_D2 D2 R11 LD1
8550
B
510R
R23 LED
R18 R20 R22 TF-SD 10K
R3 0R R34 BUTTON
10K 10K 10K
1
SD_CD USB_EN
R26 10K 1K
3V3
3V3
R27
10K
R13
USB Device USER_KEYB
SD Card 510R
S4
KEYB
Button B
C 5V CN4 5V C
1 2
5V GND
3 4 3V3
FSMC_D0 D0 D1 FSMC_D1 R35
5 6
FSMC_D2 D2 D3 FSMC_D3 1K
7 8 5V 3V3
A
FSMC_D4 D4 D5 FSMC_D5
9 10
FSMC_D6 D6 D7 FSMC_D7
11 12 U2 LT1117-3.3
R25 FSMC_D8 D8 D9 FSMC_D9 C18 C19
13 14 5V 3V3
3V3 FSMC_D10 D10 D11 FSMC_D11 104 104 LD2
15 16 3 2 LD3
2 1
10K FSMC_D12 D12 D13 FSMC_D13 IN OUT
17 18 PWR
GND
FSMC_D14 D14 D15 FSMC_D15
LCD_CS 19 20 R12 A K
LCD_CS CS RS LCD_RS C27 C28 C20 LED1 1 2
21 22
K
R7 LCD_WR WR RD LCD_RD 510R
C22 22uF 104 LED1
1
0R RST 23 24
RST RST NC R8 104 22uF
25 26 0R
R6 5V BLVDD BLGND
0R 27 28 LD4
BL_CNT BL_CNT TP_IRQ TP_IRQ
29 30
TP_CS TP_CS TP_SCK SPI_SCK
31 32 R14 A K
SPI_MOSI TP_SI TP_SO SPI_MISO LED2 1 2
33 34 510R
3V3 3V3 GND LED2
R28 HY32D
10K
Power
LCD LED
B B
3V3
C5 Y3 C6
10P 10P
U3 CN5
PA10 1 28 12M R29 R30 R31 R32 1 2
USART1_RX TXD OSC2 10K 10K 10K 10K Vref Vsupply
2 27 3 4
DTR_N OSC1 JTRST nTRST GND1
C21 3 26 5 6
3V3 RTS_N PLL_TEST JTDI TDI GND2
104 4 25 5V 7 8
VDD_325 GND_A JTMS TMS GND3
PA9 5 24 9 10
USART1_TX RXD NC JTCK TCK GND4
6 23 11 12
RI_N GP1 R9 RTCK GND5
7 22 13 14
GND GP0 JTDO TDO GND6
21 0R CN3 RST 15 16
GND nSRST GND7
1 17 18
C24 R38 22R VBUS DBGRQ GND8
8 2 19 20
NC 104 D-(DM) R33 DBGACK GND9
20 3
VDD_5 5V D+(DP) 10K
9 19 4 JTAG
DSR_N NC R39 22R ID
10 18 5
DCD_N GND C23 GND
11
CTS_N C29
12 0
SHTD_N 104 10u SHIELD JTAG
13 17
EE_CLK VO_33
A 14 16 MINI-USB A
EE_DATA DM R41
15 1K5
DP
Title
PL2303HX
J5 J6 J7
D7 D6 VOUT-
7 7 7
GND D5 WAVE2
6 6 6
W_CLK D4
5 5 5
D FQ_UP D3 WAVE1 D
4 4 4
RESET D2
3 3 3
VCC D1 WAVE3
2 2 2
V-ADJ D0 WAVE4
1 1 1
CON7 CON7 CON7
VCC
C2
C1 105 C3 C4 C5 C6
104 R7 22P L1 22P L2 33P L3 22P R9
C7 C9 200 200
VCC 390NH 390NH 470NH
WAVE1
104 106 J1
C8 CON2 C10 C11 C12
104 4.7P 5.6P 1P
1
2
4
3
U1
D3 1 28 D4
D3 D4
VCC D2 2 27 D5
D2 D5
D1 3 26 D6 J4
VCC
OUT
D1 D6
10k D0 4 25 D7 WAVE5
R4 D0 D7 2
5 24
PGND DGND 1
10kVCC 6 23
C R3 R5 PVCC DVDD VCC C
W_CLK 7 22 RESET CON2
NC
GND
100 W_CLK RESET
FQ_UP 8 21
FQ_UD IOUT
9 20
RFCLOCK IOUTB
1
2
Y1 10 19
AGND AGND R10
30/125MHz 11 18 C13 100
VCC AVDD AVDD VCC
R-SET 12 17
RSET DACBP
WAVE3 13 16 WAVE2
VOUT- VINP
VCC WAVE4 14 15 104
R2 R6 VOUT+ VINN
WAVE1
R1 12K AD9850/1
1K R8 1K
392 V_REF
J2
J3 CON2 D1
CON2 R12
2
1
105K R13
1
2
R11 VCC
V-ADJ
472K
10K C15 LED
104
B B
A A
Title