Start
Hardware
Overview
Embedded Code
User Interface
Functional Code
System Development
Sources and PDF
HARDWARE
OVERVIEW
I
have played around with many different microcontrollers
and development environments. Until I started
this project, all my experience with microcontrollers
had been with either 8- or 16-bit varieties, and
none of the microcontrollers I had used could
run fast enough for my desired PWM rate. Most
of them didn’t have a DAC or enough RAM. I hadn’t
ever used an ARM processor before, but I tried
one in this design.
The
companion features a Keil MCB2130 evaluation board,
which made it easy to experiment with and explore
the capabilities of the NXP (founded by Philips)
LPC2138, which is a superset of the LPC213x Tiny
32-bit ARM7TDMI-S processor family. This series
of processors appears to be extremely popular.
Without providing hard data, the family’s cost
is surprisingly competitive with most 8- or 16-bit
offerings. The processor’s peripherals matched
what I needed for my oscilloscope companion.
The
MCB2130 evaluation board enabled easy access to
route signals in and out. I didn’t need to create
a custom circuit board to accomplish my goals.
Many of the evaluation board’s features (e.g.,
push buttons, LEDs, a potentiometer, and a speaker)
came in handy while I was learning to program
the ARM processor. It also includes a JTAG port,
which is useful if you happen to have a JTAG debugger.
(I don’t have one, so I didn’t use the port.)
Power is supplied through a USB plug so that your
PC sources 5 V to the board. Best of all, there
are two fully functional RS-232 ports complete
with level converters. One of the RS-232 ports
is used to download your program into the ARM
processor’s flash memory. Both can be used for
communicating with a PC. After learning about
all of these features, I knew the project would
become the ARM Scope (see Figure 1).
|

(Click
here to enlarge)
|
Figure
1—Take a look at how the equipment is linked
together. The Keil evaluation board is at
the heart of the system. All of the data and
signals flow through it. |
Being
a superset for the series, 512-KB of flash memory
was nice to see, but well beyond what I needed
for this application. The included 32 KB of RAM
was also more than I required. That being said,
have you ever heard of anyone complaining about
having too much RAM? Most of the microcontrollers
I’ve used had less total flash memory than this
ARM contains for RAM. The evaluation board is
clocked by a 12-MHz crystal, which the processor
then multiplies up to 60 MHz via its PLL function.
This is how the project achieved the speed I desired.
I
used the PWM peripheral to output the timebase
calibration source, which was my first functional
requirement. The PWM is its own peripheral and
does not link to (but is based on) the system
counter/timers. A programmable 32-bit prescaler
can slow down the PWM, which contains its own
32-bit timer/counter. The PWM can drive up to
six pins for various uses. I decided that two
PWM signals would be a nice fit. One pin is configured
to output a pulse that is one division wide on
the oscilloscope display. The other pin is configured
to output a pulse that is eight divisions wide.
This provided enough flexibility to verify my
oscilloscope’s calibration all the way from 2
s per division to 0.05 µs per division. The period
and duty cycle are set on the fly by command.
Refer to Table 1 to see how
I associated the PWM timing with the oscilloscope
timebases.
The
Timer0 peripheral is set up to pace the A/D input
and the D/A output. As with the PWM, this also
contains a 32-bit timer/counter and can be slowed
down by a programmable 32-bit prescaler. In this
case, however, it’s used to time or count events.
The events that it counts in my application are
clock cycles. I use it to create time delays.
Given that a clock rate of 60 MHz will count from
zero to 60 million in 1 s, you can calculate what
you need to count to for a desired delay. When
it matches a value that you provide, an interrupt
is triggered. This interrupt is used to pace the
system. The Timer0 interrupt can reset the counter
to zero and start counting all over again. This
way repetitive tasks (e.g., collecting data at
a certain rate) occur on their own. Even running
at 60 MHz with no prescaler, a 32-bit counter
can range from a count of one (about 17 ns) to
a count of 4,294,967,296 (about 71.5 s). The time
delay is to be set on the fly by command.
The
ADC peripheral is used to capture analog voltage
signals and convert them into a binary number.
All but the lowest-end LPC213x ARM processors
contain two 10-bit ADCs. The lower end devices
contain just one. This isn’t as limiting as it
sounds. Each ADC is multiplexed among up to eight
input pins. The ADC conversion takes place in
2.44 µs. This means you could gather full 10-bit
readings from a pin at a rate of just higher than
400,000 samples per second. You could even go
a bit faster if you are willing to accept a lower
resolution. This 10-bit result represents a range
of 0 to 3.3 V. A voltage of zero will be given
a value of zero. A voltage of 3.3 will be given
a value of 1,023. Every thing in between is scaled
accordingly.
Timer0
is used to pace the system, so each ADC conversion
happens within the timer interrupt. I chose to
use only the top 8 bits of the result (256 possible
values rather than 1,024). I didn’t do this because
I wanted to increase my collection rate. What
I really wanted was to increase the rate at which
I could transfer data to my PC. To send an 8-bit
result, I can do it in 1 byte. Sending a 10-bit
result would require 2 bytes that would slow down
the display rate by a factor of two. An 8-bit
result looks just fine on my PC’s display, especially
because I’m most interested in the shape or appearance
of the signal.
The
DAC peripheral is used to output an analog voltage
by converting a binary number to the desired voltage.
Unlike the ADC, the DAC is not multiplexed, and
it outputs only a voltage through one pin. All
but the lowest-end LPC213x ARM processors contain
a single 10-bit DAC. The lower-end device doesn’t
contain a DAC. Given the opposite functionality
of the ADC, you supply a value and are rewarded
with an analog voltage in 1 µs. You could update
this voltage up to 1,000,000 times per second.
To output 0 V, you would provide a value of zero.
To output 3.3 V, you would provide a value of
1,023.
I
am not passing this data continuously across from
the PC, so the resolution will remain at its full
10 bits. The analog output signal is contained
in a 1,000-byte array (500 data points). Within
the Timer0 interrupt, I increment the data array
index and send the value to the DAC. Because both
the ADC and DAC’s functionality are paced by the
same timer interrupt, these signals are synchronized.
The data buffer, which is filled by command, can
contain any arbitrary signal or pattern.
I
used the two UART peripherals. UART0 was used
to program the on-board flash memory with my program.
The LPC213x ARM processor series contains an on-board
bootloader. At reset, the bootloader looks to
UART0 to see if a new program needs to be loaded.
This is great because you don’t need to buy or
build a special device programmer.
On
the PC side, once you have created a program,
you can use the NXP application dedicated to programming
the ARM. The LPC2000 Flash Utility communicates
with the bootloader to get things done with no
intervention from the developer. The other UART
is used for communicating with the PC over an
RS-232 interface. This port is used for sending
and receiving the analog data. It is also used
for receiving and responding to commands from
the user interface. The port’s speed is configured
to send or receive data at 57,600 bps. This data
rate is the true limiting factor in my design.
Because I send the analog data as I capture it
(in the Timer0 interrupt), I don’t capture data
any faster than it can be sent to the PC. Of course,
this is fast enough to meet my functional goals.
The UART’s input and output both contain a 16-byte
hardware buffer. When a byte arrives at the UART,
a polling loop calls a function to handle the
data.