circuitcellar.com
Magazine Support   Digital Library   Products & Services   Suppliers Directory 
 
 





 

December 2006, Issue 197

ARM Scope
Build an Analog Oscilloscope Companion


by Greg Cloutier


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.