Time Domain Reflectometer System
Time Domain Reflectometer System
Time Domain Reflectometer System
Table of Contents
1. Abstract .
.
.
.
.
.
2. Introduction
.
.
.
.
.
3. Background
.
.
.
.
.
4. Design Requirements
.
.
.
.
5. Preferred Implementation
.
.
.
5.1 Operation
.
.
.
.
.
5.2 The TDR Box .
.
.
.
.
5.2.1 The Microcontroller
5.2.2 The Delay Generator
5.2.3 The Fast Sampler and Impulse Generator
5.3 The TDR Application Software
.
.
6. Schedule and Budget
.
.
.
.
7. Results
.
.
.
.
.
.
7.1 Project Implementation (Overview) .
.
7.2 TDR Board Schematic .
.
.
.
7.2.1 Power
7.2.2 Communication
7.2.3 LEDs
7.2.4 Delay Generator
7.2.5 Step Generator
7.2.6 Impulse Generator
7.2.7 Sampler
7.2.8 Sample Conditioning
7.2.9 ADC Buffers
7.3 Circuit Board Layout .
.
.
.
7.4 Microcontroller Programming .
.
.
7.5 Software Programming .
.
.
.
7.6 Test and Verification .
.
.
.
8. Discussion and Conclusion
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
3
4
5
6
7
8
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
16
18
19
19
21
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
24
24
24
24
34
Appendix A. Schematic
.
.
.
Appendix B. Board Layout .
.
.
Appendix C. Application Source Code
.
Appendix D. Microcontroller Source Code
Appendix E. References
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
35
43
44
54
70
.
.
.
.
.
1. Abstract
For our project, our group designed a low cost portable Time Domain
Reflectometer (TDR) system for use in measuring the impedances of printed
circuit board traces in a test coupon format. This system consists of a Personal
Computer (PC) running a host application and a remote box, connected via a USB
cable or an RS-232 cable. The remote box contains the electronic circuits required
for generating and measuring the TDR signals. The host application software
receives data from the remote TDR box, and then processes and displays the TDR
image with calculated impedance measurements.
2. Introduction
As technology has progressed, signal speeds have also increased. Printed Circuit
Board (PCB) designs have had to change in order to deal with these increasing
signal speeds. In many cases, PCB traces have effectively become transmission
lines, requiring controlled impedances to prevent signal loss and distortion. The
burden placed on both PCB manufactures and design engineers is to measure and
verify the impedance of these traces.
Time Domain Reflectometry, or TDR, is the method used to measure the
impedance of a transmission line.TDR is the process of injecting a fast step pulse
into a transmission line and then measuring the reflections that result from the
signal as it travels through the transmission line. Because the PCB traces are
much shorter in length compared to normal cables (millimeters compared to
meters), TDR systems used to measure the impedance of long cables cannot be
used effectively for these traces.
A very fast TDR system, which is usually part of a Vector Network Analyzer or a
Digital Sampling Oscilloscope, must be used in order to perform impedance
measurements of short transmission lines like PCB traces. These types of TDR
systems are very expensive, making them difficult to obtain for the average
design engineer or engineering department, and large and fragile, making them
difficult to transport. Our proposed project is to design a low cost, portable TDR
system for measuring the trace impedance of Printed Circuit Boards. The
proposed system should have specifications that are comparable to the more
expensive systems and sufficient for making the required measurements.
Printed circuit boards are typically manufactured on large panels (18 x 24
typical). Each panel can contain several boards (depending on the board size)
made up of various layers of laminates including copper, epoxy, fiberglass, and
other materials. These materials are heat-pressed together into a tight sandwichlike structure. After further processing, the individual boards are then cut out of
the panel using a router and then inspected, tested, and finally sent to the client.
Impedance controlled boards are boards that contain traces that must meet a
certain impedance specification. Their specifications will be provided by the
client. The board manufacturer will then adjust the process in order to meet these
specifications. In order to verify that the specifications are met, the board
manufacturer will add another small board to the panel of boards which will
contain traces of the same line width and copper weight on the same layer as the
controlled impedances on the customers board. These small boards are known as
Test Coupons. We designed our TDR system to measure these coupons.
3. Background
The state of the art is shown in Table 1. The two high-end competitors are
Tektronix and Agilent. Their systems are used for measuring the impedance of
PCB traces, but they are primarily designed for measuring the impedance of small
microwave components as part of their s-parameter specifications, etc. We
intended to compete with them only on price, targeting firms with smaller
engineering department budgets.
Product
Sampling Oscilloscope
Agilent
CSA8200+80E10 TDR
plug-in or P8018 probe
and 80A02 plug-in.
Sampling Oscilloscope
Polar Instruments
86100+N1020A probe
kit.
CITS900 system
Specifications
12ps risetime
incident
Price
$44k+
pulse
15ps risetime
incident pulse
$38k+
200ps risetime
incident pulse
$12k+
The system from Polar Instruments is designed specifically for measuring the
impedance of PCB test coupons. We expected to meet and possibly exceed the
electrical specifications of their system. With a proposed customer price of $2500
(not including a required PC), our target is small engineering firms, engineering
departments, students, hobbyists, and anyone designing high speed controlled
impedance PCBs.
Performance Requirement
= 75ps (11.242 mm or 0.443
in)
Supplemental Information
Vp 10 to 90% into precision
short.
Reflected Pulse
(i.e. Incident pulse risetime).
Output Impedance
50(ohm) 1%
Pulse Amplitude
Pulse Width
Pulse Repetition Time
Vertical Resolution
Horizontal Resolution
1.0ps
0.7s
Performance Requirement
60m(rho)/div =400 values
Vertical Position
Supplemental Information
Based on 400 vertical pixels
in display window.
5. Preferred Implementation
The proposed TDR system consists of a application software on a PC and an
external TDR box containing the circuitry needed to produce the TDR incident
pulse and measure the reflections returned. The TDR box is connected to the host
computer via USB.
Each of the major parts of the circuit required additional research to determine
how that part can be implemented (e.g. components, circuits and other various
issues).
5.1 Operation
We intended to design the system so that the PC will do the majority of the work,
allowing a much simpler design for the microcontroller code, whose primary
purpose will be to pass the raw sample or dot data to the PC where it will be
processed.
The system operates by the following steps:
1. The PC sends a request for a TDR image (data).
2. The ramp generator starts producing the TDR step pulse and sample
impulse delays.
3. The TDR step is sent out of the coax connector to the device under
test. The impulse delay is used to generate the required pulse to snap
the SRD (step-recovery diode) and produce the complementary
impulses.
4. The impulses travel to the diode sampler where the pulses forward bias
the two diodes. This momentarily allows the input signal to charge the
two capacitors. This amounts to a very fast sample-and-hold circuit.
5. The voltage from the sample capacitors is conditioned, amplified and
then digitized by the ADC, producing a sample point or dot voltage
value that is stored in the microcontroller memory array.
6. The value is sent to the PC, which receives the data and stores it in an
internal memory array or a file.
7. Steps 2 through 6 are repeated until the array is full or the image is
completed, whichever occurs first.
8. The data is then processed and filtered to remove as much noise as
needed or as possible. This is done with an FIR filter in software. The
FIR filter coefficients are adjusted via inverse convolution algorithm
using an ideal input step as a reference.
9. The post-processed data is normalized and impedance measurements
are made. These measurements are based on a precision impedance
reference that was measured and previously stored in memory during
the initial system calibration. Normally this reference is an air-core
coaxial cable or a precision terminator.
The circuit in Figure 3 is a possible constant current source. The Vref is the set
voltage derived from a stable voltage reference (zener or band gap reference). The
current is set via R1.
10
.
For example:
If a 16-bit DAC is used to set the Sample delay Vdac voltage, very small delay
increments are possible. If the voltage reference input of the DAC is set to the
(delta)V of the ramp (3V in this case), the DAC has 0 to 65535 voltage
increments of
per LSB. (Referencing the ramp in Figure 4, there would be 65536 possible t
sample dots at a t of 366.2fs). Our required specification is 1ps DAC resolution
so this is a good margin.
The comparators must be very fast. A CML comparator from Analog Devices
ADCMP57x looks to be an ideal part for the TDR pulse comparator/step
generator. This comparator is designed to directly drive 400mV into a 50 ohm
transmission line. Below is a simplified schematic Diagram of its output.
11
This comparator has an output rise/fall time of 35ps. Our specification is 75ps.
The TDR step pulse rise time directly affects the resolution of the TDR. If the
TDR rise time is too slow, discontinuities can be missed if their reflection rise
time is faster than the incident step pulse. Two neighboring discontinuities may be
indistinguishable if the distance between them amounts to less half the TDR rise
time. This equation summarizes this concept:
.
In 35ps, light travels approximately 9.6mm. According to the above equation, our
resolution would be approximately 4.8mm.
12
13
The number of samples and the repetition rate of the system determine the time
required to reconstruct the waveform in memory. After the waveform has been
acquired, the data array is sent to the host computer where it can be
used/displayed.
sampled by the two diode sampler. The diodes D1 and D2 are low capacitance
Schottky diodes biased off by the two bias voltages Vbias+/-. When the
diodes are momentarily driven into forward conduction by the symmetric strobes
Impulse+/-, the input signal partially charges the hold capacitors C1 and C2. If
the repetition frequency of the input signal is a multiple of the strobe frequency
(and it will be in our design), at each successive strobe interval the sampling
diode will further charge the holding capacitors and cause the output voltage
sampled to asymptotically approach that of the input voltage. These two capacitor
voltages are summed, conditioned and finally digitized by the ADC.
15
16
17
Finalize Schematic.
3/29/2011 (week):
4/8/2011:
Assemble board.
4/9-15/2011:
4/18/2011:
4/25-29/2011:
Debug software.
5/2-6/2011:
18
7. Results
19
The board design took a longer time to design than we expected, mainly because
of the complex geometries that were used in the high-speed step and impulse
generator circuits used to produce the TDR step and the impulse voltage for the
diode sampler. The traces for these circuits are impedance-controlled and two
different techniques were used to produce these traces (Microstrip and Coplanar
Wave Guide with Ground (CPWGG)). Each technique presented its own set of
challenges and every time a change was made to the design such as moving a
component, trace calculations were necessary and the traces were carefully
redrawn. The delays in board layout and design review meetings put the project
behind schedule by a little over one month.
Once the board design was complete, the design files were sent to a quick-turn
board manufacturing house, and we had our boards in four days. Since the boards
were designed with mostly surface-mount components, we were able to use solder
paste to solder our components. A solder paste mask was manufactured at a
different location while the boards were being manufactured and we received both
on the same day. It then took one afternoon to populate the components, solder,
clean, and inspect the boards. This was completed on Friday, April 15, 2011.
Figure 11 is an image of our populated printed circuit board.
20
The microcontrollers signals CPUTX and CPURX exit P0[0] and P0[1],
respectively (sheet 2), and are sent to both the USB connector and RS-232
connector (sheet 3) for serial communications with the personal computer.
7.2.3 LEDs
The microcontroller sends commands to two LEDs (sheet 2). LEDSAMP2,
LEDCOMM, and LEDERR are currently not being used. LEDSAMP1 inverts and
blinks D4 while sampling. The unnamed signal from port zero, pin 23 P0[23]
briefly lights D11 (HEART BEAT) every second when the TDR is powered on.
7.2.4 Delay Generator
Time zero for a single sample begins when the microcontroller sends
commands DDG_D and DDGCLK (sheet 2, left side) to set the vernier flip-flop
U10 (sheet 5). Q bar goes from high to low, turning off Q2. With the current
provided by the current source (U5, Q1, et al.), VERNIER becomes a ramp signal.
CLAMP is 3V, and with 0.3V across D3, the ramp clamps at 3.3V. VERNIER is
also buffered by U11 into the signal BVERNIER.
7.2.5 Step Generator
When the TDR powers on it sends commands STDAC_CS and STDAC_LO
(sheet 2) to the Step DAC U20 (sheet 7). This outputs an analog voltage signal
STEPDAC1 to fast comparator U12. The ramp signal BVERNIER on sheet five
inputs to the fast comparator, and when its voltage matches STEPDAC1, a step
pulse is sent down a 50 Ohm trace and out J3 through the device under test.
22
7.2.7 Sampler
When the impulses reach C59 and C60 (sheet 11) the capacitors act like shorts,
and for a brief duration of time, Schottky diodes D8 and D9 conduct, charging
C24 and C35 with the voltage from the 50-Ohm trace coming from the fast
comparator. These are summed at a node into the signal VSAMP1, which is sent
for conditioning.
7.2.8 Sample Conditioning
Rather than use an open loop sampler, we closed the loop. VSAMP1 is sent to the
charge amplifier U8 (sheet 8), where it is inverted and conditioned before being
sent to an integrator, U3 (MEMORY), but it does not happen just yet. The
charge amps output will be a pulse, and the integrator needs to sum the energy of
only that pulse, or it may add (or subtract) unwanted data.
This is controlled by a switch, U4, which passes the sample voltage to the
integrator only during that pulse. The step pulse generated by the sampling
comparator (sheet 5) is sent to two one-shots that delay the signal GATE and
control its duration. GATE is sent to U4 on sheet 8, to pass the signal to the
integrator. The charge amps U6-A and U6-B receive this data and pass it back to
the loop as VM1 and VP1, into C15 and C34 (sheet 7). Approximately five time
constants of the tracking amps will have passed before the next sample.
7.2.9 ADC Buffers
The output of U3, VOUT1 (sheet 8) is conditioned by an ADC buffer, U1 (sheet
4), where its amplitude and offset are at voltages that can be read by the
microcontrollers ADC. The microcontroller sends the samples index and data to
the personal computer, and then prepares for the next sample.
23
temporary fix until a larger fuse can be added. The power LED did not light. It
was discovered that this and all the other LEDs had an error in their PCB
footprint, reversing their anode and cathode. To fix this problem, it was necessary
to rotate the LEDs 90 degrees and tack a small piece of wire across the LEDs to
complete the circuit.
From the +15V external supply, six other internal voltage levels are produced:
+12V, +5V, +3.3V, +3.3V (analog), -7.5V (switching power supply), -5V. The
power supply voltages were measured and all were within specification except for
the +12V supply, which had to be adjusted by changing a resistor value.
The next test was to verify that various other voltages in different circuits were
correct. While measuring voltages in the constant current source (used in the
vernier delay circuit), it was discovered that the voltages were incorrect and that
some of the components were getting very hot. An attempt was made to debug
and fix this circuit but the problem is being caused by something not yet
discovered.
Due to the time constraints of our project, this circuit has temporarily been
disconnected and replaced with a simple resistor to +12V. Clamping at +3.3V
(less than half a time constant), it closely resembles a linear ramp, and it has
allowed us to continue testing and debugging. The constant current source will
still need to be corrected for better performance of our circuit (see section 8).
After checking and verifying the various circuit voltages, the next test was to
make the microcontroller blink an LED. We had chosen a microprocessor based
on a 100MHz ARM Cortex M3 core (the LPC1768) for our project. Initially a
demo program was used that blinks an LED on a LPC1768 development board.
We had populated an LED on the same I/O port for the purpose of debug and to
later act as a heart-beat indicator to let us know our program was alive. It was
discovered that this LED also had a pin-out problem but a jumper formed by a
small piece of wire solved it, and we had our blinking LED. This accomplishment
proved that we had established communication with the microcontroller via JTAG
port, that the microcontroller pin-out was correct, that the controller was
functioning, and that we were able to program its flash-based ROM.
The next series of test involved us writing our own custom embedded source
code. We had to test and verify that the RS-232 and USB communications worked
and that the onboard analog-to-digital converter of the microcontroller worked.
We also checked it for accuracy. We completed these tasks over the next few
days. The source code of the microcontroller continues to evolve, as does the host
program that runs on a PC and communicates with the TDR board via RS-232.
25
Once the source code had evolved to the point where we could start/stop the
vernier delay and digitize the sample voltage, we needed to move on with testing
and measuring the initial performance of the system. In order to establish the
efficiency of the sampler section of the TDR, we needed to observe and measure
the impulses that turn on the sample diodes.
The way this circuit works is that the diode are normally biased to their off state
by applying a 1V back-bias voltage. When the fast impulses arrive at the diodes, it
momentarily biases the diodes into their on state. At this point, the diodes act as
a switch (or sampling gate), allowing some charge to be transferred from the
voltage being measured to a storage capacitor. From there the charge is amplified,
integrated and sampled by the microcontroller analog to digital converter (Fig.
13).
26
If we know the time that the diode is in its conducting region or on state (tg), we
can calculate the overall bandwidth of our sampler:
BW (GHz )
350
.
t g ps
When we designed the board, it was inconvenient to add test points for measuring
the impulses being generated. To measure the impulses using the highest
bandwidth sampling oscilloscope that we have available, we soldered a piece of
hard-line coaxial cable directly to the board (Fig. 14).
Fig 14. Step Generator and Diode Sampler with 50-Ohm hard line
After soldering the hard-line coax into the impulse circuit, a Tektronix 11801x
50GHz sampling scope was used to measure the impulse amplitude and gate time
(tg). Images of our impulses are displayed in Fig. 15 and 16. In Fig. 15, the
negative impulse has a tg of 295.5 ps and in Fig 16, the tg is 181.0 ps This give a
bandwidth of:
27
BW (GHz )
350
1.184 GHz
295.5 ps
BW (GHz )
350
1.933 GHz
181.0 ps
The bandwidth that we would like to have in order to meet our initial
specifications is somewhere on the order of 4 to 5GHz. This falls short of our
desired specification, but we are able to adjust these pulse widths by adding solder
to the gaps on both sides of each shorted transmission line used to set these pulse
widths. These can be seen in Fig. 14 as the traces labeled 25 ohm The pulse
widths, , are set by:
2L
Vp
60% of C
These adjustments can be done at a later time to increase the performance of the
system.
The next measurement that we made was the rise time of our incident step pulse.
This rise time is important because it determines the linear resolution of our TDR
system:
28
TResolution
2 TRisetime
An image or our incident step pulse can be seen below in Fig. 17. The rise time is
a very disappointing 311.52 ps. The high-speed comparator being used to produce
the step pulse can produce edges of 35 to 38 picoseconds. We are still
investigating the reason for our circuits poor performance. According to the
above formula, our resolution would be approximately 48mm, or about 2 inches.
This falls very short of our desired specification of 11.2mm (.440 inches) or
better.
Notably, the jitter of our system was very high (+/-180ps) at the time these
measurements were made, and averaging was used to capture waveforms. This
implies that our edges are much faster than the displays indicate, but at the time of
this report we were unable to verify the actual speeds. The source of the jitter is
still being investigated. The method of operation of the Tektronix 11801x
sampling scope requires 100ns pre-trigger in order to display a waveform. This
meant we had to use our own board as a trigger source while making these
measurements. We did this by using a large RC time constant as our vernier delay
(220ns). We then used the Q output of the vernier flip flop (Appendix A, sheet 5)
used to start the ramp as a trigger source for the sampling scope. We positioned
the step pulse at 100ns by adjusting the comparator DAC voltage to about half of
the vernier ramp amplitude. The problem with this method is that the vernier ramp
is caused by an RC circuit instead of our preferred constant current source as
mentioned above, so the ramp is not entirely linear. Also, any noise on the resistor
power supply translates directly into the ramp voltage, which causes jitter. Until
we correct our fault with the constant current source, we will not be able to
exclude the vernier ramp from the/a source of our jitter, nor will we be able to
make accurate measurements.
29
Vreflected
Vincident
Z L Z0 *
30
1
1
Fig 18. TDR Pulse from a Tektronix 11801 sampling oscilloscope using and SD-26 head.
Fig 19. TDR Pulse from our TDR system (open 50 ohm cable, 200ns window)
Using our host software, the image shown in Fig. 19 was produced. This image
shows the TDR of a 50 ohm coaxial cable 6 feet long open at the end. The
incident step and the reflected steps show this to be the case. (Ignore the Z and
31
rho cursor calculations, as well as the OPEN and SHORT lines in these
figures. They were based on samples that must remain arbitrary until we have
decided on our exact window.) In Fig. 20 the TDR pulse shows the same 50-ohm
coaxial cable with a 50-ohm termination resistor at the end. The amplitude stays
constant, since Z0 is the same as ZL.
Fig 20. TDR of 50 ohm cable terminated into 50 ohm load (200ns window)
In Fig. 21, a TDR pulse is shown of the same 50-ohm coaxial cable shorted to
ground at the end. The little bump is where a pulse should be. It should have the
same amplitude as the incident pulse shown in Fig. 20. We are still investigating
the reason that it does not. We believe it is related to timing and/or offset voltage
adjustments.
32
We are continuing to adjust the circuits in the TDR system in order to improve the
performance.
33
34
APPENDIX A
Schematic
Sheet 2 (Sheet 1 is the schematics title page):
35
Sheet 3:
36
Sheet 4:
37
Sheet 5:
38
Sheet 6:
39
Sheet 7:
40
Sheet 8:
41
Sheet 9:
42
APPENDIX B
Board Layout
43
APPENDIX C
Source code for application software (in BASIC)
#COMPILE EXE
#DIM ALL
#RESOURCE "ICONANDVERSION.PBR"
' A resource file to make our icon
'******************************************************************************
'*
*
'*
LOW COST PORTABLE TIME DOMAIN REFLECTOMETER
*
'*
*
'* By:
SP, PB
*
'* Version:
Don't know
*
'* Date:
5/1/2011
*
'* For:
*
'*
*
'* https://sites.google.com/site/lcptdrsystem/
*
'*
*
'******************************************************************************
%TRUE
%FALSE
%SAMPCNT
= -1
= 0
= 1024
'
'
'
'
'
'
'
TYPE SampleData
ypos
AS LONG
n
AS LONG
tdr
AS LONG
rho
AS DOUBLE
z
AS DOUBLE
END TYPE
'
'
'
'
'
44
FUNCTION PBMAIN ()
'==============================================================================
'
Variable declarations
'-----------------------------------------------------------------------------LOCAL hComm
AS LONG
'COMM setting identifier
LOCAL comstring()
AS STRING
'COMM port identifier, ports 1 through 9
LOCAL rawData()
AS STRING
'ASCII characters sent from the TDR
LOCAL rDataPtr
AS BYTE PTR 'Start of rawData string
LOCAL numData()
LOCAL tdrData()
LOCAL fiftymean
AS LONG
AS LONG
AS DOUBLE
LOCAL tdrfile
AS LONG
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
LOCAL
vpdiv
voffset
tpdiv
toffset
fiftyPos
xPos
yPos
hWin
ylabelPtr
openline
shortline
AS
AS
AS
AS
AS
AS
AS
AS
AS
AS
AS
DOUBLE
DOUBLE
DOUBLE
DOUBLE
LONG
LONG
LONG
LONG
BYTE PTR
LONG
LONG
LOCAL
LOCAL
LOCAL
LOCAL
myInput
y
x
a
AS
AS
AS
AS
STRING
LONG
LONG
LONG
LOCAL tryAgain
DIM
DIM
DIM
DIM
DIM
DIM
comstring$(
rawData$ (
tdr
(
curs
numData
(
tdrData
(
AS LONG
8 )
%SAMPCNT )
0 TO %SAMPCNT-1 ) AS SampleData
AS SampleData
4 )
4 )
'==============================================================================
'
The "main program"
'-----------------------------------------------------------------------------WHILE 1
IF tryAgain = 0 THEN GOSUB welcomeScreen
tryAgain = 0
GOSUB openComms
GOSUB getRawData
GOSUB processRawData
GOSUB writeFile
GOSUB graphData
WEND
'Program continues in a continuous loop.
45
'==============================================================================
'
The "modules"
'==============================================================================
' Module that presents a screen to start the program.
'-----------------------------------------------------------------------------welcomeScreen:
COLOR 10,0
' Because it's trendy to go green nowadays
CLS : PRINT
PRINT " ************************************************************
PRINT " *
*
PRINT " *
T
D
R
*
PRINT " *
*
PRINT " *
*
PRINT " * Shannon Petty's and Paul Bailey's
*
PRINT " * enigmatic Senior Design Project
*
PRINT " *
*
PRINT " * (sites.google.com/site/lcptdrsystem)
*
PRINT " ************************************************************
PRINT : SLEEP 500
RETURN
"
"
"
"
"
"
"
"
"
"
'==============================================================================
' Module that opens COM3 for communications with the TDR
'-----------------------------------------------------------------------------openComms:
FOR x = 1 TO 9
comstring$(x) = "COM" + CHR$(x+48)
NEXT x
PRINT "Opening serial comms . . . " : SLEEP 300
hComm = FREEFILE
x = 1
WHILE %TRUE
COMM OPEN comstring(x) AS #hComm
IF ERRCLEAR THEN
x = x+1
IF x = 10 THEN
PRINT "
ERROR! COM1 through COM9 are not available, and I can't"
PRINT "
open COM10 without doing some crazy stuff! Check your"
PRINT "
port settings, then press any key to continue."
INPUT FLUSH : WAITSTAT
x = 1
END IF
ITERATE LOOP
ELSE
EXIT LOOP
END IF
WEND
COMM
COMM
COMM
COMM
COMM
COMM
SET
SET
SET
SET
SET
SET
#hComm,
#hComm,
#hComm,
#hComm,
#hComm,
#hComm,
BAUD
BYTE
PARITY
STOP
TXBUFFER
RXBUFFER
=
=
=
=
=
=
38400
8
%FALSE
0
1024
4194304
'
'
'
'
'
'
9'k6 baud
8 bits
No Parity
1 stop bit
1 Kb transmit buffer
4 Mb receive buffer
PRINT
PRINT , comstring(x)
PRINT , "opened successfully."
PRINT
PRINT "
WARNING! Continuing will overwrite previous tdrdata.txt"
PRINT "
file. If you do not want to overwrite tdrdata.txt, move"
PRINT "
it to a different directory." : PRINT
PRINT "Press any key to continue."
: PRINT
INPUT FLUSH : WAITSTAT
RETURN
46
'==============================================================================
' Module that gets "raw data," strings of twelve characters,
' form the TDR and puts them into an array
'-----------------------------------------------------------------------------getRawData:
PRINT
PRINT "
Collecting Data."
PRINT
PRINT "
This takes a wicked long time."
PRINT
PRINT "
Smoke if you got em."
PRINT
y = TIMER
FOR x = 0 TO %SAMPCNT-1
COMM SEND #hComm, "?"
WHILE COMM(#hComm, RXQUE) < 12
IF TIMER-y > 20 THEN
'Timeout error
PRINT "ERROR!!! Not all values were read!"
rawData$(x) = "0000000000"
ITERATE FOR
END IF
WEND
COMM LINE #hComm, rawData$(x)
IF x = INT(%SAMPCNT*1/10) THEN PRINT "
10% complete"
IF x = INT(%SAMPCNT*2/10) THEN PRINT "
20% complete"
IF x = INT(%SAMPCNT*3/10) THEN PRINT "
30% complete"
IF x = INT(%SAMPCNT*4/10) THEN PRINT "
40% complete"
IF x = INT(%SAMPCNT*5/10) THEN PRINT "
50% complete"
IF x = INT(%SAMPCNT*6/10) THEN PRINT "
60% complete"
IF x = INT(%SAMPCNT*7/10) THEN PRINT "
70% complete!"
IF x = INT(%SAMPCNT*8/10) THEN PRINT "
80% complete!!"
IF x = INT(%SAMPCNT*9/10) THEN PRINT "
90% complete!!!!!"
NEXT x
COMM CLOSE #hComm
RETURN
47
'==============================================================================
' Module to sort out the TDR's data (sample number and ADC value)
' and put them in their own arrays. Also calculates rho, time, and
' impedance for each sample.
'-----------------------------------------------------------------------------processRawData:
CLS : SLEEP 500 : PRINT : PRINT
PRINT "Processing data."
FOR x = 0 TO %SAMPCNT-1
rDataPtr = STRPTR(rawdata$(x))
' decode the
numData(0) =
numData(1) =
numData(2) =
numData(3) =
tdr(x).n
=
48
'==============================================================================
' Module to put processed data into a readable text file
'-----------------------------------------------------------------------------writeFile:
tdrfile& = FREEFILE
OPEN "tdrdata.txt" FOR OUTPUT AS #tdrfile&
PRINT #tdrfile&, ,,,"TDR DATA"
PRINT #tdrfile&,
PRINT #tdrfile&, "File Created on: "; DATE$; " at "; TIME$
PRINT #tdrfile&,
PRINT #tdrfile&, "Sample no.",,"ADC Value (V)",,"Rho",,"Z"
PRINT #tdrfile&,
FOR x = 0 TO %SAMPCNT-1
PRINT #tdrfile&, tdr(x).n,, FORMAT$(tdr(x).tdr*3/4096, "0.000"),
PRINT #tdrfile&, , FORMAT$(tdr(x).rho, "0.0000"), ,
IF tdr(x).z > 1000 THEN
PRINT #tdrfile&, FORMAT$(tdr(x).z, "0.3E+###")
ELSE
PRINT #tdrfile&, FORMAT$(tdr(x).z, "##.000")
END IF
NEXT x
CLOSE #tdrfile&
RETURN
'==============================================================================
' A big, long module that plots the data and calculates
' V, rho, and z, based on cursor position.
'-----------------------------------------------------------------------------graphData:
GRAPHIC WINDOW "TDR!!!", 20, 20, %XLEN+100, %YLEN+100 TO hWin
GRAPHIC ATTACH hWin, 0
'the default values
vpdiv = 1
' volts per division
tpdiv = 200
' time (samples) per division
voffset = 0
' voltage offset due to scrolling
toffset = 0
' not yet used
fiftyPos = %YSTART _
' Graph position of fifty-ohm cali- ( %YLEN*fiftymean/4096 _
' bration
+ voffset*%DIVISION
)/vpdiv
curs.ypos = fiftyPos-(fiftyPos MOD 10) ' Cursor starts near 50-Ohms
theMainGraph:
' Updated position values based on scolling, cursor movement, etc.
fiftyPos = %YSTART - (%YLEN*fiftymean/4096 + voffset*%DIVISION)/vpdiv
openline = %YSTART - (%YLEN*%OPEN/4096 + voffset*%DIVISION)/vpdiv
shortline = %YSTART - (%YLEN*%SHORT/4096 + voffset*%DIVISION)/vpdiv
curs.tdr = ((%YSTART-curs.ypos)*vpdiv - voffset*%DIVISION)*4096/%YLEN
' Some linear interpolation to calibrate if 50-Ohms does not
' exactly corespond to the mean(open, short) :
IF curs.tdr > fiftymean THEN
curs.rho = (curs.tdr-fiftymean)/(%OPEN-fiftymean)
ELSE
curs.rho = (curs.tdr-%SHORT)/(fiftymean-%SHORT) - 1
END IF
curs.z = (1+curs.rho)/(1-curs.rho)*50
49
CLS
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
PRINT
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
GRAPHIC
GRAPHIC
GRAPHIC
GRAPHIC
=====================================================
| TDR data printed to:
tdrdata.txt
|
| in this program's directory.
|
+---------------------------------------------------+
|
Here are the options:
|
| (You need to be in this window to perform these.) |
+----------------------------+----------------------+
|
W:
|
|
|
Scroll up
|
|
|
A:
S:
|
B: Begin again
|
| Increase
Decrease |
|
|
V/div
Z:
V/div |
ESC: Exit program |
|
Scroll
|
|
|
down
|
|
+----------------------------+----------------------+
|
U: Cursor up
|
|
|
|
N: Cursor down
|
|
|
| (Set your offset and V/div before moving cursor.) |
=====================================================
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
CLEAR
BOX (0,0) - (%XLEN+100, %YLEN+100), 0, %BLACK, %BLUE
BOX (7,7) - (%XLEN+93, %YLEN+93 ), 0, %BLACK, RGB(255,255,224)
COLOR %BLACK, RGB(255,255,224)
'axes
GRAPHIC LINE (%XSTART, %YEND) - (%XSTART, %YSTART)
GRAPHIC LINE STEP - (%XEND,%YSTART)
'title
GRAPHIC SET POS (240, 10)
GRAPHIC PRINT "TDR RESULTS"
'v/div and cursor values:
GRAPHIC SET POS (75, 25)
GRAPHIC PRINT "V/div = "
GRAPHIC SET POS (125, 25)
GRAPHIC PRINT STR$(vpdiv,5)
GRAPHIC SET POS (175, 25)
GRAPHIC PRINT "rho = "
GRAPHIC SET POS (225, 25)
GRAPHIC PRINT FORMAT$(curs.rho, "0.000")
GRAPHIC SET POS (295, 25)
GRAPHIC PRINT "Z = "
GRAPHIC SET POS (325, 25)
IF curs.ypos < openline THEN
GRAPHIC PRINT "Inf"
ELSEIF curs.ypos > shortline THEN
GRAPHIC PRINT "0.000"
ELSE
GRAPHIC PRINT STR$( curs.z, 4 )
END IF
GRAPHIC SET POS (395, 25)
GRAPHIC PRINT "V = "
GRAPHIC SET POS (425, 25)
GRAPHIC PRINT STR$( curs.tdr*%YLEN/4096/%DIVISION, 4 )
'x-axis title
GRAPHIC SET POS (100,%YSTART+10)
GRAPHIC PRINT "SAMPLE NUMBER (THIS WILL LATER BE 'TIME')"
'x-axis ticks
FOR x = 1 TO 5
GRAPHIC LINE ( %XSTART+x*%DIVISION, %YSTART )_
- ( %XSTART+x*%DIVISION, %YSTART-10 )
NEXT x
50
'y-axis
GRAPHIC
GRAPHIC
GRAPHIC
ticks
LINE ( %XSTART, %YSTART-100 ) - ( %XSTART+10, %YSTART-100 )
LINE ( %XSTART, %YSTART-200 ) - ( %XSTART+10, %YSTART-200 )
LINE ( %XSTART, %YEND
) - ( %XSTART+10, %YEND
)
'x grid
y = %YSTART - 100
WHILE y >= %YEND
x = %XSTART
WHILE x =< %XEND-10
GRAPHIC LINE (x,y)-(x+10,y), %GRAY
x = x + 20
WEND
y = y - 100
WEND
'y grid
x = %XSTART + 100
WHILE x =< %XEND
y = %YSTART
WHILE y >= %YEND+10
GRAPHIC LINE (x,y)-(x,y-10), %GRAY
y = y - 20
WEND
x = x + 100
WEND
'Cursor gridline
x = %XSTART
WHILE x =< %XEND-10
GRAPHIC LINE (x,curs.ypos)-(x+5,curs.ypos), RGB(0,139,139)
x = x + 10
WEND
'OPEN and SHORT markers
GRAPHIC COLOR RGB(255,160,122), RGB(255,255,224)
GRAPHIC LINE (%XSTART, openline)-(%XEND, openline), RGB(255,160,122)
GRAPHIC SET POS (%XEND-50,openline-15)
GRAPHIC PRINT "OPEN"
GRAPHIC LINE (%XSTART, shortline)-(%XEND, shortline), RGB(255,160,122)
GRAPHIC SET POS (%XEND-50,shortline+5)
GRAPHIC PRINT "SHORT"
GRAPHIC COLOR %BLACK, RGB(255,255,224)
'x-axis markers
FOR x = 1 TO 5
GRAPHIC SET POS( %XSTART+5+x*%DIVISION, %YSTART-15 )
GRAPHIC PRINT STR$( tpdiv*(x + toffset) )
NEXT x
'y-axis markers
FOR x = 0 TO 3
GRAPHIC SET POS( %XSTART-25, %YSTART-x/3*%YLEN )
GRAPHIC PRINT FORMAT$( vpdiv*x - voffset, "#.##" )
NEXT x
'y-axis label
myInput
= "ADC VOLTAGE V"
ylabelPtr = STRPTR(myInput)
FOR y = 0 TO LEN(myInput)-1
GRAPHIC SET POS (15,110+15*y)
GRAPHIC PRINT CHR$(@ylabelPtr[y])
NEXT y
' The data points
FOR x = 0 TO %SAMPCNT-1
yPos = %YSTART - (%YLEN*tdr(x).tdr/4096 + voffset*%DIVISION)/vpdiv
xPos = %XSTART + tdr(x).n/(tpdiv/%DIVISION)
GRAPHIC SET PIXEL (xPos, yPos), %RED
NEXT x
GRAPHIC REDRAW
51
mainGraphWait:
' Portion that determines user's next step: end program,
INPUT FLUSH
' resend data and plot again, move cursor, scroll, or
WAITSTAT
' change V/div
myInput = INKEY$
SELECT CASE myInput
CASE $ESC : GOTO terminate
CASE "a"
IF vpdiv = 1 THEN
PRINT " Volts per division is at its max."
GOTO mainGraphWait
ELSEIF vpdiv = 0.5 THEN
vpdiv = 1 : GOTO theMainGraph
ELSEIF vpdiv = 0.2 THEN
vpdiv = 0.5 : GOTO theMainGraph
ELSEIF vpdiv = 0.1 THEN
vpdiv = 0.2 : GOTO theMainGraph
ELSEIF vpdiv = 0.05 THEN
vpdiv = 0.1 : GOTO theMainGraph
ELSEIF vpdiv = 0.02 THEN
vpdiv = 0.05 : GOTO theMainGraph
ELSE
' if vpdiv = 0.01
vpdiv = 0.02 : GOTO theMainGraph
END IF
CASE "s"
IF vpdiv = 1 THEN
vpdiv = 0.5 : GOTO theMainGraph
ELSEIF vpdiv = 0.5 THEN
vpdiv = 0.2 : GOTO theMainGraph
ELSEIF vpdiv = 0.2 THEN
vpdiv = 0.1 :GOTO theMainGraph
ELSEIF vpdiv = 0.1 THEN
vpdiv = 0.05 : GOTO theMainGraph
ELSEIF vpdiv = 0.05 THEN
vpdiv = 0.02 : GOTO theMainGraph
ELSEIF vpdiv = 0.02 THEN
vpdiv = 0.01 : GOTO theMainGraph
ELSE
' if vpdiv = 0.01
PRINT " Volts per division is at its min."
GOTO mainGraphWait
END IF
CASE "w"
voffset = voffset - vpdiv
GOTO theMainGraph
CASE "z"
voffset = voffset + vpdiv
GOTO theMainGraph
CASE "b"
CLS : GRAPHIC WINDOW END
tryAgain = 1
RETURN
CASE "u"
IF ABS(curs.ypos-%YEND) < 10 THEN
PRINT " The cursor is at its max."
GOTO mainGraphWait
ELSE
curs.ypos = curs.ypos - 10
GOTO theMainGraph
END IF
CASE "n"
IF ABS(curs.ypos-%YSTART) < 10 THEN
PRINT " The cursor is at its min."
GOTO mainGraphWait
ELSE
curs.ypos = curs.ypos + 10
GOTO theMainGraph
END IF
52
CASE ELSE
PRINT
PRINT "
Invalid key pressed "
PRINT
PRINT "
Options are W, A, S, B, U, N, Z, and ESC "
PRINT "
(Hint: don't use caps.)"
PRINT
GOTO mainGraphWait
END SELECT
RETURN
terminate:
END FUNCTION
53
APPENDIX D
Source code for LPC1768 microcontroller on TDR board (in C)
(See comments in code regarding register definitions not found in common libraries.)
/*
===============================================================================
File Name
: main.c
Author
: SP and PB
Version
: I'm not a version!
Copyright
: Copyright (C) 04/2011
Description : main definition for the TDR program. See comments at front of
"tdr.c" for explanation of orange- and red-highlighted registers
defined in "LPC17xx.h" and UM10360.
===============================================================================
*/
#include "LPC17xx_hti.h"
#include <NXP/crp.h>
#include "tdr.h"
#define SAMPCNT
#define STEPDAC_DATA
1024
850
//
//
//
//
//
//
54
//
//
//
//
//
Enter an eternal
cation will send
will then have a
samples. Then it
signal.
loop, waiting for the user. The computer applia signal through the serial interface; UART3Count
nonzero value, triggering the TDR to take its
reset UART3Count to zero and wait for the next
55
/******************************************************************************
* tdr.h
*
These are the definitions for functions used in tdr.c and main.c for the
*
TDR project. Special thanks to the good people at NXP for giving new mean*
ing to the word "abstruse."
*
*
We used the same names for registers (U3IER, etc.) that can be found in
*
the user's manual for the LPC1768, UM10360; these registers are defined
*
in NXP's header file "LPC17xx.h"
******************************************************************************/
#ifndef TDR_H_
#define TDR_H_
#include <NXP/LPC17xx/LPC17xx.h>
//==============================================================================
//
#defines here
//-----------------------------------------------------------------------------// Set ADC clock to 1MHz
#define ADC_CLK
1000000
//
UART3 #defines
// We don't want to brute force our comms or guess timing. UART3's IRQ handler
// was provided by NXP's LPC software, and we gladly used it. Here are #defines
// used in their IRQ handler function.
// Bits to set or clear in UART3 interrupt enable register (U3IER). These en// able or disable interrupts for specific registers
#define IER_RBR
0x01
// for the receiver buffer register
#define IER_THRE
0x02
// for the transmit holding register
#define IER_RLS
0x04
// for the Rx line status
// Bits to set or clear in UART3 interrupt identification register (U3IIR).
// These are used for changing certain volatile variables in the IRQ handler,
// and will be necessary for determining if the PC has sent a command to the
// LPC for TDR sampling.
#define IIR_RLS
0x03
// check for Rx line status
#define IIR_RDA
0x02
// check if Rx data available
#define IIR_CTI
0x06
// check chrctr. time-out ind. (not used by us)
#define IIR_THRE
0x01
// check if transmit holding register empty
// Bits to set or clear in UART3 Line Status Register. These contain flags for
// transmit and receive status.
#define LSR_RDR
0x01
// receiver data ready
#define LSR_OE
0x02
// overrun error
#define LSR_PE
0x04
// prity error (we aren't checking parity)
#define LSR_FE
0x08
// framing error
#define LSR_BI
0x10
// break interrupt
#define LSR_THRE
0x20
// transmit holding register ready
#define LSR_RXFE
0x80
// error in Rx FIFO
// UART buffer can only hold so much:
#define BUFSIZE
0x40
// Useful for defining clock frequencies
#define MHZ
1000000
// An important register, not included in NXP's header
#define SPTSR
(*(volatile unsigned long *)(0x40020014UL))
// Clock functions:
void Initialize_Clocks(void);
// SPI, DAC, and vernier functions:
void spiInit(void);
void send14bits(uint16_t data);
void send16bits(uint32_t data);
void vernierInit(void);
void startVernier(void);
void stopVernier(void);
// ADC functions:
uint32_t ADCInit( void );
uint32_t ADC0Read( void );
56
// UART functions:
void UART3_IRQHandler (void);
uint32_t UARTInit( void );
void processSend( uint32_t n, uint32_t adcVal );
// LED functions:
void led_init (void);
void heartbeat_on (void);
void ledSamp1_on (void);
void ledErr_on (void);
void heartbeat_off (void);
void ledSamp1_off (void);
void ledSamp1_invert (void);
// Systick and delay functions:
void SysTick_Handler(void);
void mydelay(uint32_t waits);
uint32_t check_heartbeat(uint32_t curTicks);
#endif
/* End of tdr.h */
57
/*=============================================================================
Name
: tdr.c
Author
: SP and PB
Version
: lost count
Description : Functions called by main.c for TDR's micro-controller. See comments at front of "tdr.h" for embellishment, wit, and erudition.
FIOnDIR, FIOnSET, PINSELn, et al. registers are as named in the
LPC1768's users manual, UM10360, and as defined in NXP's header
file, "LPC17xx.h" In this report these registers are in bold
orange for easy recognition. Macros that define certain bits in
these registers are in red (not bold).
==============================================================================*/
#include "LPC17xx_hti.h"
// Thanks to Rob Gaddi at Highland Technology.
#include <cr_section_macros.h>
// Some changes made to LCP v. 3.6.
#include <NXP/crp.h>
// Code read protection macros.
#include "type.h"
// Type definition for NXP LPC17xx family uPCs.
#include <NXP/LPC17xx/LPC17xx.h>
// Defines registers used in this function.
#include "tdr.h"
// Our header
#define BAUDRATE
38400
//=============================================================================
//
Function
void Initialize_Clocks(void)
//
//
See LPC17xx User's Manual UM10360, section 4.5.13 on PLL0 setup (page
//
46) and read it carefully because you could really hose this up.
//=============================================================================
void Initialize_Clocks(void)
{
const PLL_SETTINGS pll =
{
.src
= PLL_SRC_INTERNAL,
// 4 MHz
.f_xtal
= 0,
.pll_m
= 75,
// * 2 * 75
.pll_n
= 2,
// / 2
.cclk_div
= 3
// / 3
};
// = 100 MHz
disable_pll();
// Configure the peripheral clocks. The master bootloader
// doesn't actually use any peripherals. Per the errata
// notes, this has to be done while PLL0 is disabled.
PCLKSEL0 = 0;
PCLKSEL1 = 0;
// This only fails if our PLL settings are wrong.
uint32_t clock_freq = configure_pll(&pll);
assert(clock_freq == 100*MHZ);
}
58
/******************************************************************************
*
*
SPI Functions here
*
*
void spiInit(void)
*
*
void send14bits(uint32_t data)
*
*
void send16bits(uint32_t data
*
******************************************************************************/
//=============================================================================
//
Function
void spiInit(void)
//
//
Sets up SPI with the peripherals on the TDR.
//=============================================================================
void spiInit(void)
{
// Peripheral power control
PCONP &= ~(1 << 8);
// make sure in default mode (redundant)
PCONP |= (1 << 8);
// power on spi hardware
// Peripheral clock setup
PCLKSEL0 |= (3 << 16); // CCLK = 4 MHz; PCCLK = CCLK/8;
S0SPCCR = 0x08;
// Another divider = PCCLK/8, = ca. 62.5 kHz
// Pin setup
PINSEL0 &=~(3 << 30);
// Reset to defaults
PINSEL1 &= ~((3 << 0)|(3<<2)|(3<<4));
// Setup the SPI pins now
PINSEL0 |= (3 << 30);
// Set P0[15] to SCK
(not yet used)
PINSEL1 |= ((3 << 30)|
// Set P0[16] to SSEL,
(3 << 2) |
//
P0[17] to MISO, (not used)
(3 << 4));
//
P0[18] to MOSI
// Interrupts? We don't need no stinkin' interrupts!
// Control signals
//
For the TDR, these are the DAC control signals:
//
P1[0] = ~STDAC_CS
//
P1[1] = ~STDAC_LD
//
P1[4] = SAMP_~CS/LD
//
P1[8] = SAMP_~CLR
PINSEL2 &= ~(0x0003030F);
// Initialize P1[0], P1[1], P1[4], and P1[8]
PINMODE2 &= ~(0x0003030F);
FIO1DIR |= ( (1 << 0) | (1 << 1) | (1 << 4) | (1 << 8) );
FIO1SET = ( (1 << 0) | (1 << 1) | (1 << 4) | (1 << 8) );
return;
}
59
//=============================================================================
//
Function
void send14bits(uint32_t data)
//
//
Sends data to the step DAC to set the fixed threshold for the fast com//
parators -- that is, to set the time at which the TDR's step pulse will
//
be generated.
//=============================================================================
void send14bits(uint16_t data)
{
FIO1SET = (1 << 1);
FIO1CLR = (1 << 0);
S0SPCR = 0x00000E3C; // Sets for 14 bits, & does other cool stuff.
// Send the data packing.
S0SPDR = data; // Put data in SPI data register.
while( (S0SPSR & 0x80) != 0x80 )
{
// Wait for transfer complete flag
}
//Toggle the control signals
FIO1SET = (1 << 0);
// Set the chip select high to stop selecting.
mydelay(1);
// wait for a bit
FIO1CLR = (1 << 1);
// Latch the data internally in the DAC.
mydelay(1);
// wait for a bit
FIO1SET = (1 << 1);
// Set the load back to a high value.
// Before we return, we want to clear and verify clear the SPIF bit
// This is done by reading the dummy byte (8-bits) returned during xmit.
volatile uint8_t dummy1;
while((S0SPSR & 0x80) == 0x80)
{
// After transfer complete set, clear it by reading data reg
dummy1 = S0SPDR;
// Read dummy data
}
// Trap here until the bit is cleared
return;
}
//=============================================================================
//
Function
void send16bits(uint32_t data)
//
//
Sends data to the samp DAC to determine at which time to sample the
//
TDR pulse.
//=============================================================================
void send16bits(uint32_t data)
{
FIO1CLR = (1 << 4);
// Set CS/LD low.
FIO1SET = (1 << 8);
// latch the new data in the DAC register.
S0SPCR = 0x0000003C; // Same as above function, but set to 16 bits.
// Ship off the data.
S0SPDR = data;
// Put data in SPI data register.
while( (S0SPSR & 0x80) != 0x80 )
{
// Wait for transfer complete flag in SPTSR bit 7
}
// Toggle the control signals
FIO1SET = (1 << 4);
// Set chip loading ye internal DAC register.
FIO1SET = (1 << 8);
// Always keep on high.
// Clear SPIF bit and make sure it's clear
volatile uint8_t dummy1;
// dummy var for reading dummy data
while ((S0SPSR & 0x80) == 0x80)
{
// After xfer complete flag set, clear it by reading data reg
dummy1 = S0SPDR;
// Read the dummy data
}
// Trap here until the bit is cleared
return;
}
60
/******************************************************************************
*
*
Vernier functions here
*
*
*
void vernierInit(void)
*
*
void startVernier(void)
*
*
void stopVernier(void)
*
******************************************************************************/
//=============================================================================
//
Function
void vernierInit(void)
//
//
Initialized the GPIO to the vernier FF and gives Q_not an initial high
//
value.
//=============================================================================
void vernierInit(void)
{
// Set P2[0] (DDG_D) and P2[1] (DDGCLK) to 00 - GPIO
PINSEL4 &= (~((3 << 0) | (3 << 2)));
// Set GPIO - P2[0] and P2[1] - to be output
FIO2DIR |= (1 << 0)|(1 << 1) ;
// Signals to set Qn on vernier FF to high, initially
FIO2CLR = (1 << 1);
// Ck low
FIO2CLR = (1 << 0);
// D low
FIO2SET = (1 << 1);
// Ck high (Qn goes high)
}
//=============================================================================
//
Function
void startVernier(void)
//
//
Starts the Vernier ramp by setting Q_not on Vernier FF to high.
//=============================================================================
void startVernier(void)
{
//toggles vernier FF
FIO2CLR = (1 << 1);
// Ck low
FIO2SET = (1 << 0);
// D high
FIO2SET = (1 << 1);
// Ck high (Qn goes low, starts ramp)
FIO2CLR = (1 << 1);
// Ck low
}
//=============================================================================
//
Function
void stopVernier(void)
//
//
Ends the Vernier ramp by setting Q_not on Vernier FF to low.
//=============================================================================
void stopVernier(void)
{
//toggles vernier FF
FIO2CLR = (1 << 0);
// D low
FIO2SET = (1 << 1);
// Ck high (Q high, ends ramp)
FIO2CLR = (1 << 1);
// Ck low
}
61
/******************************************************************************
*
*
ADC functions here
*
*
* uint32_t ADCInit( void )
*
* uint32_t ADC0Read( void )
*
******************************************************************************/
//=============================================================================
//
Function
uint32_t ADCInit( void )
//
//
Sets up to read the ADC's samples on ye TDR
//=============================================================================
uint32_t ADCInit( void )
{
uint32_t pclkdiv, pclk;
// Enable CLOCK into ADC controller
PCONP |= (1 << 12);
// all the
PINSEL0 |=
PINSEL1 &=
PINSEL1 |=
PINSEL3 |=
0x01 <<
( pclk
0 << 16
0 << 17
1 << 21
0 << 24
0 << 27
0 ) |
/ ADC_CLK - 1 ) << 8 ) |
) |
) |
) |
) |
);
//
//
//
//
//
//
//
//
return (TRUE);
}
62
//=============================================================================
//
Function
uint32_t ADC0Read( void )
//
//
Reads the ADC. What else?
//=============================================================================
uint32_t ADC0Read( void )
{
uint32_t regVal, ADC_Data;
AD0CR &= 0xFFFFFF00;
AD0CR |= (1 << 24) | (1 << 0);
while(1)
{
regVal = AD0DR0;
if ( regVal & ADC_DONE )
{
break;
}
}
63
/******************************************************************************
*
*
UART Functions here
*
*
*
void UART3_IRQHandler( void )
*
* uint32_t UARTInit( void )
*
* void processSend( uint32_t n, uint32_t adcVal )
*
******************************************************************************/
volatile
volatile
volatile
volatile
uint32_t
uint8_t
uint8_t
uint32_t
UART3Status;
UART3TxEmpty=1;
UART3Buffer[BUFSIZE];
UART3Count = 0;
//=============================================================================
//
Function
void UART3_IRQHandler (void)
//
//
Regularly updates the above volatile variables, depending on their
//
status. This function has priority as per "cr_startup_lpc176x.c," which
//
contains the forward declarations for the LPC17xx IRQ handlers.
//=============================================================================
void UART3_IRQHandler (void)
{
uint8_t IIRValue, LSRValue;
uint8_t Dummy = Dummy;
IIRValue = U3IIR;
IIRValue >>= 1;
IIRValue &= 0x07;
if ( IIRValue == IIR_RLS )
// (3 << 0), Receive Line Status
{
LSRValue = U3LSR;
if ( LSRValue & (LSR_OE|LSR_PE|LSR_FE|LSR_RXFE|LSR_BI) )
{
// There are errors or break interrupt
// Read LSR will clear the interrupt
UART3Status = LSRValue;
Dummy = U3RBR;
// Dummy read on RX to clear
return;
// interrupt, then bail out
}
if ( LSRValue & LSR_RDR )
// Receive Data Ready
{
// If no error on RLS, normal ready, save into the data buffer.
// Note: read RBR will clear the interrupt
UART3Buffer[UART3Count] = U3RBR;
UART3Count++;
if ( UART3Count == BUFSIZE )
{
UART3Count = 0;
// buffer overflow
}
}
}
else if ( IIRValue == IIR_RDA )
// (1 << 1) Receive Data Available
{
// Receive Data Available
UART3Buffer[UART3Count] = U3RBR;
UART3Count++;
if ( UART3Count == BUFSIZE )
{
UART3Count = 0;
// buffer overflow
}
}
else if ( IIRValue == IIR_CTI )
// (3 << 2) Character timeout indicator
{
UART3Status |= 0x100;
// Bit 9 as the CTI error
}
64
}
//=============================================================================
//
Function UARTInit( void )
//
//
Initializes the UART for serial comms with the PC
//=============================================================================
uint32_t UARTInit( void )
{
uint32_t Fdiv;
uint32_t pclkdiv, pclk;
// pins specific to port3
PINSEL0 &= ~0x0000000F;
PINSEL0 |= 0x0000000A;
PCONP |= ( 1<<4 ) | ( 1<<25 );
// The following is a fancy way of setting the clock dividers based on the
// baud rate. See UM10360, page 312 for a figure of the algorithm, and
// section 14.4.12.1 for baud rate calculation.
//
pclkdiv finds the peripheral clock selection (for UART3 here) and in
// the lookup table will find that it's 0x00, so that pclk will be
// SystemCoreClock/4. The rest is to calculate for different baud rates.
pclkdiv = (PCLKSEL1 >> 18) & 0x03;
switch ( pclkdiv )
{
case 0x00:
default:
pclk = SystemCoreClock/4;
// <-- This one, 100 MHz/4 = 25 MHz
break;
//
(Ignore the others.)
case 0x01:
pclk = SystemCoreClock;
break;
case 0x02:
pclk = SystemCoreClock/2;
break;
case 0x03:
pclk = SystemCoreClock/8;
break;
}
U3LCR = (1 << 7) |
// Enable access to dividers (look up)
(0 << 6) |
// No break transmission
(0 << 3) |
// No parity
(0 << 2) |
// 1 stop bit
(3 << 0);
// 8 bits
Fdiv = ( pclk / 16 ) / BAUDRATE ;
// Fdiv = (int)40.69 = 40
U3DLM = Fdiv / 256;
// Divisor latch MSB is 1
U3DLL = Fdiv % 256;
// Divisor latch LSB is 40, or 0x28
U3LCR = 0x03;
// DLAB = 0
U3FCR = 0x07;
// Enable and reset TX and RX FIFO.
NVIC_EnableIRQ(UART3_IRQn);
U3IER = (1 << 0) |
(1 << 1) |
(1 << 2);
return (TRUE);
// UART3_IRQn = 8, a LCPxx-specific
// interrupt number for UART3
// Enable UART3 receiver buffer register interrupt
// Enable UART3 transmit holding register interrupt
// Enable UART3 RX line status interrupt
65
//=============================================================================
//
Function
void processSend( uint32_t n, uint32_t adcVal )
//
//
My favorite. This takes the value read from the ADC, assigns it an
//
index number n, puts them in a twelve-byte array, and sends it out
//
the UART.
//
//
Parameters:
n
Index number
//
adcVal
guess
//=============================================================================
void processSend( uint32_t n, uint32_t adcVal )
{
uint8_t *uartPtr;
char sendVal[12];
int i;
uartPtr = (uint8_t *)&sendVal[0];
// "N"
sendVal[0] = (char)
// Sample Number
sendVal[1] = (char)
sendVal[2] = (char)
sendVal[3] = (char)
sendVal[4] = (char)
// ","
sendVal[5] = (char)
// ADC value
sendVal[6] = (char)
sendVal[7] = (char)
sendVal[8] = (char)
sendVal[9] = (char)
// "CR" and "LF"
sendVal[10] = (char)
sendVal[11] = (char)
0x4E
(
(
(
(
0x30
0x30
0x30
0x30
0x2C
(
(
(
(
0x30
0x30
0x30
0x30
(
(
0x0D
0x0A
);
|
|
|
|
((n
((n
((n
((n
&
&
&
&
|
|
|
|
((adcVal
((adcVal
((adcVal
((adcVal
&
&
&
&
66
/******************************************************************************
*
*
LED functions here
*
*
*
void led_init(void)
*
*
void heartbeat_on(void)
*
void ledSamp1_on(void)
*
void ledErr_on
*
*
void heartbeat_off(void)
*
void ledSamp1_off (void)
*
There isn't a need for ledErr_off. It will go off when resetting
*
(unplugging) the TDR.
*
*
void ledSamp1_invert (void)
*
There isn't a use for inverting the other leds, until we make use of
*
the second channel on the TDR
*
******************************************************************************/
void led_init (void)
{
// HEARTBEAT:
PINSEL1 &= (~(3 << 12));
FIO0DIR |= (1 << 22);
FIO0CLR = (1 << 22);
// LEDSAMP1:
PINSEL3 &= (~(3 << 16));
FIO1DIR |= (1 << 25);
FIO1SET = (1 << 25);
// LEDERR:
PINSEL3 &= (~(3 << 22));
FIO1DIR |= (1 << 27);
FIO1SET = (1 << 27);
}
void heartbeat_on (void)
{
FIO0SET = (1 << 22);
}
void ledSamp1_on (void)
{
FIO1CLR = (1 << 25);
FIO2SET = (1 << 11);
}
void ledErr_on (void)
{
FIO1SET = (1 << 27);
}
void heartbeat_off (void)
{
FIO0CLR = (1 << 22);
}
void ledSamp1_off (void)
{
FIO1SET = (1 << 25);
FIO2CLR = (1 << 11);
}
67
//=============================================================================
//
Function
mydelay(waits*90ns + 70ns)
//
//
With the PLL boosting our 4MHs up to 100MHz, each assembly instruction
//
(if 1 clock/instruction) takes 10ns.
//
//
This is a brute force delay the min. (waits = 0) delay is ca. 160ns.
//
Each nummber of waits adds ca. 90ns.
//=============================================================================
void mydelay(uint32_t waits)
{
if (waits < 1)
{
// gotta be at least one man.
waits = 1;
}
while(waits != 0)
{
// decrement waits
waits--;
}
return;
}
68
//=============================================================================
//
Function
uint32_t check_heartbeat(uint32_t curTicks)
//
//
"Beats" the HEARTBEAT led by briefly turning it on every second, give
//
or take.
//
//
Parameter:
curTicks
Value to compare with system's msTicks.
//
Function will return as is or reset to zero,
//
depending whether or not it's time for a
//
"beat."
//=============================================================================
uint32_t check_heartbeat(uint32_t curTicks)
{
if((msTicks - curTicks) >= 960)
{
heartbeat_on();
}
if((msTicks - curTicks) >= 1000)
{
heartbeat_off();
curTicks = msTicks;
}
return curTicks;
}
/* End of tdr.c */
69
APPENDIX E
References
Hewlett Packard Application Note 918: Pulse and Wavefore Generation with Step
Recovery Diodes. Palo Alto: Hewlett Packard, 1984.
LCP17xx Users Manual UM10360. NXP Semiconductors, 2010.
Mulvey, John. Sampling Oscilloscope Circuits. Beaverton: Tektronix, 1970.
70