Walter Dso Project
Walter Dso Project
Walter Dso Project
Stephan Walter
<stephan@walter.name>
Semester Project
Contents
1 2 3 4 5 6 7 Introduction Previous work Specications Hardware design Software Results Summary and outlook 3 5 10 11 26 32 37 39 40 53 54
Chapter 1
Introduction
The Masters project done by Fabrizio Lo Conte [10] presents a universal interface for the USB 2.0 bus. Various different modules can be connected to the interface card. The data received over the serial USB bus are converted to a parallel, 16-bit wide bus. The modules are addressed by a 16-bit parallel address bus. Data can be read from or written to modules in chunks of up to 1024 words. This project builds upon this interface card to make a software-controlled DSO (digital sampling oscilloscope). It consists of the following elements: an analog circuit for amplifying the signal that is to be measured an analog-to-digital converter a digital circuit to temporarily store the digital data interface logic to communicate with the parallel data and address bus software running on a standard desktop PC that will collect and display the data in realtime
Chapter 2
Previous work
2.1 Projects by others
The following list shows some existing work on designing a digital oscilloscope.
2.1.1
Bitscope
This oscilloscope is sold commercially in different variants for USD 550. to 1600.. The ADC is a TLC5540 by Texas Instruments, the sample buffer is a Micron MT 5C2568. These are controlled by a PIC microcontroller by Microchip. Interfaces to a PC by Ethernet or USB.
2.1.2
PC-based DSO
Author: area26 [2] Performance: 4 channels 100 MS/s This project consists of a main board with an Altera FPGA and up to four ADC boards with circuits of type MAX1449 by Maxim. The oscilloscope interfaces to a PC over Ethernet.
2.1.3
DSOA Mk3
Author: D. Jones [7] Performance: 2 channels 20 MS/s The ADC is a Philips TDA8703, the sample buffer is an IDT 7202. There is a parallel (printer) port connection to a PC.
2.1.4
Oscilloscopio digitale
Author: L. Lutti [11] The ADC is a Texas Instruments ADS830, the sample buffer is a CY7C199. These are controlled by a Xilinx CPLD.
2.1.5
DSO
Author: J. Glaser [5] Performance: 4 channels 80 MS/s The ADC is a MAX1448 by Maxim, controlled by a Xilinx FPGA. Interfaces to a PC via USB. The analog input stage (see gure 2.2) is a good starting point for a new design and I have taken some inspiration from it.
47nF
100220
FETInputs, 1x 10x
U ref 350
+5V
5x
2x
22pF MAX4547CEE AD8065AR OPA643NB 450 250 +5V MAX4547CEE 400 MAX4547CEE AD8180AR
MAX4105ESA
103GM1A5/2D
0.5V
700 300
MAX4104ESA
MAX1448
MMBD1703
50pF
150pF
90k
5V
U = 2.048V
10k
Ref MAX5842LEUB
Vdd
100nF COM
3.3
300
+5V
50 IN 22pF 300
Figure 2.2: The analog stage of a digital oscilloscope designed by J. Glaser [5]
2.1.6
LSDO
Author: T. Grocutt [6] Performance: 2 channels 50 MS/s The ADC is an Intersil HI5667, which is controlled by an Altera FPGA.
2.1.7
eOscope
Author: eosystems.ro [4] Performance: 1 channel 40 MS/s The ADC is a Texas Instruments ADS830, the sample buffer is an IDT 7201. An Atmel AVR microcontroller and a Xilinx CPLD are used for control. The device has an LCD screen and does not interface to a computer.
Chapter 3
Specications
3.1 Hardware specications
After studying the performances of the designs shown in the last chapter, the following specications have been established: Sampling frequency Resolution Analog bandwidth (3dB) Input voltage range Input resistance Input capacitance Coupling Attenuation / amplication Supply voltage Power consumption
= = selectable selectable =
From these basic requirements, some others can be derived: for example, any operational amplier acting on the signal must have a high enough slew rate so that it can follow the signal.
3.2
Software specications
The software has two major functions: a) control the settings of the acquisition card such as frequency and attenuation and b) visualization of the samples in a diagram of time/voltage and possibly other information. The detailed requirements will be dened later in section 5.1, as they depend on the hardware design.
10
Chapter 4
Hardware design
4.1 Analog section
unity gain
lowpass filter
ADC
x5 gain
x20 gain
Figure 4.1: Overview of the analog section An oscilloscope probe is connected to a BNC connector. From there, the signal to be measured goes through several different stages as shown on gure 4.1.
4.1.1
AC/DC coupling
The rst stage of the oscilloscope is the AC or DC coupling. AC coupling is done by a capacitor C101 of 47nF. This capacitor can be by-passed by closing a relay (see gure 4.2). Although such a mechanical device can be bulky and consume a lot of current, it seems a better choice than a FET switch which would have to be driven at 30V. For the relay, the model G6K by Omron was chosen for its small size, 3V operation and low coil current. This current of 30mA makes it possible to drive the relay directly from 74 logic ICs, provided the logic family has sufcient drive current. When the relay is closed, the capacitor is discharged through the relay. In order to respect the relays current limit, a resistor is placed in series with the coupling capacitor. The coil of the relay (R = 91) is in series with a 20 resistor. This leads to a coil current of 30mA in the closed state. The Schottky diode is a protection against voltage spikes that can occur when removing the coil supply voltage.
11
AC/DC
R109 20
D105
8
NC
1 4 3 2
OMRON G6K
K101 CONN101
1 2
C101
47n
R101 30
4.1.2
Now the signal is attenuated by a factor of 10, selected by a relay of the same type as above. The attenuation itself is achieved using a combined resistive and capacitive voltage divider for low and high frequencies, respectively. Figure 4.3 shows the attenuation circuit.
:10 R119 20 D102 R105 62
+3.3V
3V
D106
OMRON G6K
8 5 1 6
1M8 R102
1M8 R103
C103
K102
R104 100k
3V
3.3V
12
Amps/package 1 1 1 1, 2, 4
The values of C102,103 and R102...104 are chosen so that they result in a total input impedance of 1M and about 25pF, values that are common in commercially available oscilloscopes. The following stages must be protected against voltages exceeding 3V. Sometimes, this is done by clamping the signal over two diodes to Vsupply . This means that the power supply must be able to compensate any over-voltage. In the present case, this would not be a good idea, as the power is supplied by a PC over the USB bus. A voltage surge might damage the components of the computer. The solution adopted here is the use of Zener diodes. They are reverse biased with a constant small biasing current from the power supplies. The signal is then clamped by conventional diodes to the Zeners.
4.1.3
Input buffer
Now the signal is put through an op-amp of unity gain. The requirements for this amplier are as follows: Compatibility with the 3.3V power supply. Input and output voltage range of 3V. 3dB bandwidth of at least 100MHz at unity gain, in order to satisfy the requirements given in section 3.1. Slew rate > 60V/s. (see below) Low input biasing current Iib . Besides the bandwidth, the slew rate is an important AC parameter for an amplier. The signal must be allowed to swing along the whole voltage range (from 3 to +3V) during one sample period (100ns at 10Msps). Input biasing current is important as this is the rst amplier, which is directly connected to the input. Any biasing current inuences the circuit we want to measure. Operational ampliers often have Iib in the range of several microamperes. Table 4.1 lists some candidates. For the rst design of the analog stage, the AD8065 by Analog Devices was chosen. This chip turned out to be difcult to obtain. Also, it tends to amplify high frequencies to much, as will be shown in section 6.1.
13
R123 330
3.3V
LM4040
AD8065
+3.3V 3 4 5 V+ V 2 3.3V 3.3V
AD8062 U101
1 +3.3V
R107 3k9
3 2
8 V+ V 4
U102
1
R121 20
R114 1k R115 1k
4.1.4
DC offset
The second op-amp stage will add a selectable offset voltage to the signal, as well as limiting its swing for the ADC or the following gain stages. ADCs with a supply voltage in the order of 3V often have an input voltage swing of 2V centered around a reference voltage. This is also true for the ADC chosen here, which is presented in section 4.2.1. The offset voltage is generated by a DAC with an output range of 0 to 2.5V. This would make it impossible to have a negative offset, or shift down the signal. The solution is to use the amplier in a summing conguration: the weighted addition of the signal, the DAC voltage and a negative reference voltage gives a great exibility. The voltages are summed as follows: Vout = 0.33Vin + 1.28Vo f f set + 0.39(2.5V) The important requirements for the amplier are: a gain bandwidth > 200MHz and a slew rate > 20V/s. These parameters are obtained by the same reasoning shown for the rst amplier. Some candidates are listed in table 4.2
14
Bandwidth [MHz]
Amps/package 1 2
Comments not rail-to-rail input not rail-to-rail input not rail-to-rail rail-to-rail low noise, not rail-to-rail
gain=20
gain=5
AD8062
+3.3V
LT6200 U102
7 +3.3V 1 8 2 6
NC
LT6200
+3.3V
IN
R121 20
9 10 16
NC
5 6
8 V+ V 4
R122 20
3 2
IN
7 U103 V+ V 4 3.3V
5
NO COM
3 2
7 U105 V+ V 4 3.3V 6
13
NO COM
ADCIN
MAX4547
MAX4547
U104
U104
3.3V
R111 R112 3k
12k
4.1.5
Gain
The signal can be amplied using a multiplier of 5 or 20 or a combined multiplier of 100. The gain of 5 is achieved with a single operational amplier with a gain bandwidth > 500MHz. A gain of 20 would be difcult for a single amplier, so two operational ampliers are used. As can be seen on gure 4.5, the rst amplier has a gain of 1 + 2400 = 3 and the second 1 + 1200 6800 = 6.67. For the lower gain, the type of the amplier is the same as for the offset circuit. 1200 For the two gain stages of 6.67 and 5, the LT6200-5 was used. It has a minimum stable gain of 5 (see table 4.3). Small resistors are placed in series with the op-amp outputs to isolate the outputs from the parasitic input capacitances of the next stage.
15
Amps/package 3 1, 2 1, 2 1 1
Comments single supply, not rail-to-rail unity-gain stable unity-gain stable G5 limited voltage
16
4.2
Sampling
+3.3V Vcc
C202 2u2
C201 100n
C210 2u2
C209 100n
Vcc
10
21
32
C207
100n
32 1
VDD
VDD U201
OVDD D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
16 17 18 19 20 24 25 26 27 28
VCC SA9 SA8 SA7 SA6 SA5 SA4 SA3 SA2 SA1 NC CLK TP
22
C206
100n C208
29 31
MAX1444
7 6 5 4 31 30 29 28 3
D0 D1 D2 D3 D4 D5 D6 D7 D8
U202 IDT72V05
Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8
10 11 13 14 19 20 21 22 15
IN+
2 18
COM IN
W R
EF FF XO/HF
24 9 23
FEMPTY FFULL NC
NC
WRITE
Vcc
26
CLK FIFORS
25 8
FL/RT RS XI GND
16
C204 100n
C205
13
PD OE GND
4
22p
15
GND
5
GND
8
GND
11
GND
14
GND
30
OGND
23
4.2.1
ADC
Several companies offer analog to digital converters with the desired sampling rate. Important characteristics are the resolution and the maximum and minimum sampling rate (some converters have a minimum sampling rate of about 1MHz, which would be wasteful for acquiring signals of low frequency). Supply voltage is also an important concern as some circuits require different voltages for the analog and digital parts. The converter chosen for this project is the Maxim MAX1444. It has a resolution of 10 bit and a sampling rate of up to 40MHz. Pin-compatible variants offer up to 80MHz.
4.2.2
Sample storage
The sample data is stored in a FIFO memory of type IDT72V06. This chip has a supply voltage of 3V, a capacity of 16k 9 bit and an access time of 15ns. The high capacity allows for some leeway in the communication over USB. That is, a total of 16384 samples can be memorized before the FIFO memory is full and has to be read out. The ADC will output a 10-bit value at each rising clock pulse plus t DO = 8ns max, as shown in gure 4.7. Figure 4.8 show the timing diagram of the FIFO memory (t DH = 0). If the same clock signal is applied to the ADC and to the W input of the FIFO, the output of the ADC changes between the periods DATA IN VALID of the FIFO. On the FIFO, the write cycle starts on the falling edge of W if the full ag is not set. Data is not overwritten if the FIFO is full.
17
18
4.3
Clock
Both the ADC and the FIFO operate on the same clock. The clock source must be programmable by software in a range of up to 10MHz. Important parameters the frequency accuracy and jitter. The importance of the jitter of the ADC can be illustrated with the following example: The relationship between signal-to-noise ratio and jitter delay t j is given by the following equation (see [8]): 1 SNR j = 20 log10 2 f t j The effective number of bits (ENOB) of an ADC is typically dened as: ENOB = SNR 1.76 6.02
My design uses 9 bits of the ADC (ENOB ADC = 9). Jitter noise should not make things worse. Thus ENOBj (the equivalent limitation on the number of bits due to jitter) must be bigger than 9. Re-arranging the equation gives the following condition for the product of input frequency and jitter time: f t j < 2.5 104 Suppose we are measuring a sine wave signal of 100kHz at full swing. The jitter must be smaller than 2.5ns, which corresponds to 2.5% at a sampling frequency of 10MHz. The chip chosen was an LTC6903 by Linear Technologies. It provides a clock signal between 1kHz and 68MHz and can be programmed via SPI (serial peripheral interface). This IC has a jitter of less that 0.4% for frequencies below 10MHz, as can be seen on gure 4.10.
Figure 4.10: Jitter vs frequency for LTC6903. Excerpt from datasheet [9] The clock signal is buffered by a 7404 inverter (in a single-gate variant). Such a buffer is suggested in the datasheet if more than two inputs are driven, or if the line is longer than 5cm. Both
19
Vcc
C501
10n
U502 CLK
6 5 2 4
C502
1u
7
V+ OE U501
CLK
CLK
NC
74LVC1G04
2 3 4
LTC6903
GND
1
Figure 4.11: Clock generation circuit are the case here. Also, the clock signal needs to stay high during a reset of the FIFO. Thanks to the inverting buffer, we can simply turn off the clock chip with the correct SPI command, and the clock line will stay high.
4.4
4.4.1
Bus interface
Bus timing requirements
Figure 4.12 shows the state of the bus when sending data from the PC to the oscilloscope module. The address on the bus must be compared to the modules address. If they match, the data is simply read at every rising edge of the EnableOut signal.
E vvvvvrrvvrrvvrrvvErrvvrrvvrrvvvv vvvvvvvvvvvvvvvvvvvvvvvvvvvvvEvvvvvvvvvvvvvvvvvvvvvvvvvv
Address
E
Data0 Data1 DataN
Figure 4.12: Timing diagram PC oscilloscope The bus timing for retrieving the data from the module is shown in gure 4.13. The Cypress chip will read the data at the middle of every pulse of EnableOut.
20
Figure 4.13: Timing diagram oscilloscope PC After a rst design of the analog stage had been established, it became clear that multiple channels could be easily offered. This is done by making an interface card where multiple ADC cards could be plugged in. The overhead for this turned out to be minimal: only a multiplexer was needed to send the WRITE or READ signal to the correct ADC card.
4.4.2
Communication protocol
The communication protocol for this project has been designed to be simple. It can be implemented using standard logic circuits. For reading the samples, the 9-bit values are read directly from the FIFO. The data format is as follows:
15 .. 10 9 8 / FF / EF D8 | | set if FIFO not full - -+ | set if FIFO not empty - - - - -+ 14 .. 13 .. 12 .. 11 .. 7 D7 6 D6 5 D5 4 D4 3 D3 2 D2 1 D1 0 D0
The 9 lowest bits are the data bits. The ags indicating whether the FIFO is empty (EF) or full (FF) are also sent. The computer simply reads an amount of data and discards the words that have EF set to 0. The data lines of the FIFO are connected directly to the bus. For the two ags, a buffer of type 74125 is used.
21
Changing the acquisition parameters is just as simple: one word is sent over the bus that contains all the control bits. The 8 lowest bits are kept separately for each channel in a ip-op of type 74574. The following gure lists each control bit:
15 .. 8 7 6 5 4 3 2 1 0 / CS AC :10 x20 x5 SDI SCK / DS / RS | | | | | | | | | SPI : clock chip select - - - - - - - -+ | | | | | | | | AC or DC coupling - - - - - - - - - - - - - - - - - - - -+ | | | | | | | :10 divider - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ | | | | | | x20 multiplier - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ | | | | | x5 multiplier - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ | | | | SPI : data in - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ | | | SPI : clock - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ | | SPI : DAC chip select - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ | reset FIFO - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+ 14 .. 13 .. 12 .. 11 .. 10 .. 9 ..
4.4.3
Logic circuitry
The digital logic is constructed using logic ICs from the 7400 series. The choice of the family is crucial: only a few both support low supply voltages and have low propagation delays. Additionally, for driving the relays, high output drive current is necessary. Name HC AHC LVT LVC Supply [V] 26 26 2.73.6 1.23.6 Delay [ns] 9 5 2 4 Drive [mA] 8 8 64 24 Comments standard
Table 4.4: Logic family parameters For driving the relays, the LVT family will be used. The other chips will be selected by their availability and price.
4.4.4
Logic functions
The module must be able to detect its address on the bus. A 8-bit wide comparator (74521) is used. The address bus being 16 bit wide, in fact I use up 256 addresses of the 65536 possible ones. Next we need to know whether we are supposed to read from or write to the bus. This is done by looking at the R/W line of the bus. These signals are now combined using NAND and inverter gates. After the rst prototype was built, it became apparent that some samples were lost. The problem was that after all valid values had been read from the FIFO, a pulse of the sampling clock could occur. This meant that one value was read from the ADC into the FIFO. As the FIFO was no longer empty now, another read operation would take place on the next bus clock pulse. Because
22
74521
1 EN
U401
A=B 19 B0 3 B1 5 B2 7 B3 9 B4 12 B5 14 B6 16 B7 18
ADDR
Vdd
2 A0 4 A1 6 A2 8 A3
D401 U403
12
U411
13 Q
74123
A 1 B 2 Vdd CLR 3
D09
R402
470
Vcc
47p
NC C402
4 Q 14 Ce 15 Re/Ce
ENOUT
3
U403
6 9
U402
8
R401
4 5 7410
7404
7404
Figure 4.15: Bus read/write detection circuit the ADC clock and the bus clock are not synchronized at all, this could lead to an incomplete write operation. The solution was to inhibit further read attempts after the FIFO was empty. A 74123 monostable circuit is triggered by the empty ag. It is re-triggered at each pulse of the bus clock and resets itself after some time.
4.5
The analog part of the circuit needs +3.3V and 3.3V supplies. For the digital part, only a +3.3V supply is needed but it should be separated from the analog supply to avoid voltage spikes inuencing the analog signals. Table 4.5 gives an estimation of power consumption when using one channel. An attempt was made to design a power supply. However, the ripple voltage from the DC
23
# 2 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1
Component Relay Op-amp Op-amp Op-amp Analog switch ADC FIFO DAC Flip-op Bus buffer Comparator Flip-op Clock AND Inverter Monostable
Type Omron G6K AD8065 AD8062 LT6200 MAX4547 MAX1444 IDT72V06 LTC2630 74574 74125 74688 74574 LTC6903 7408 7404 74123
Vs [V] 3.3 6.6 6.6 6.6 6.6 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3 3.3
DC converter turned out to be problematic. A commercially available dual power supply is currently used.
24
4.6
Board construction
Two acquisition boards and one bus interface board have been made and tested. Figure 4.16 shows the three circuit boards connected to the existing USB interface by Fabrizio Lo Conte (in gray).
25
Chapter 5
Software
5.1 Requirements
The program must rst establish a connection to the Cypress card via USB. Then the acquisition parameters such as the gain must be sent to the card. The clock is halted so as not to have any difference in the sample memory of the different channels. After the FIFO memory of each channel has been reset, the clock can be programmed and started. Now the program must periodically read the values from the FIFO chips. Apart from reading and storing the 9 data bits, the ags FEMPTY and FFULL must be checked for each word. If the full ag is set, sample data has been lost. In that case we have no choice but to reset the FIFO and start the sampling anew. If the empty ag is set, the corresponding words are not valid samples. We simply ignore them and wait for data where the empty ag is not set. The sample date is displayed as a diagram of voltage vs time. A trigger condition will dene the rst sample to show on the left edge of the screen. The display can be triggered by a rising or a falling edge of a channel and at a voltage both specied by the user.
5.2
The Python programming language [13] has been chosen because of the following advantages over C++ (the traditional choice): it is an interpreted language, programs can be run on different platforms (Windows, Linux) without compiling memory is managed automatically, which reduces the work necessary as well as the potential for errors For the communication over USB, the libusb library is used. This makes the code dealing with USB platform-independent. Graphical user interfaces (GUI) for Python programs are traditionally implemented with the Tk toolkit. This toolkit is however rather old (the rst version dates back to 1991) and was
26
main.py
drawwindow.py
thread
dso.py
controlsframe.py
bufferedwindow.py
spi.py
iousb.py
trigger.py
sampleops.c sampleops.py
Figure 5.1: Simplied view of code dependencies. White boxes: existing libraries developed for the X Window System used in Unix operating systems. WxWidgets [14] is a more modern toolkit running on all modern operating systems. It is used for this project via the WxPython interface [12].
5.3
Structure of implementation
5.3.1 main.py
This module creates the frames for displaying the data and the control elements. It then starts the data acquisition in dso.py as a seperate thread. A timer is set to re-draw the data 50 times per second.
27
5.3.3 controlsframe.py
The control elements (buttons, sliders) are dened here. A screen-shot of the program is shown in gure 5.2. Note that the control elements are only shown for two channels. This is simply done to prevent the window from taking up a lot of screen space. All four channels can be activated by changing a single line in a conguration le.
5.3.4 dso.py
Most of the actual work is done by this module. The method start() rst establishes a connection with the USB interface. Then, the parameters such as gain, offset and sampling frequency are set. The FIFO memory chips are reset and the data acquisition begins. In an innite loop, we rst check if an operation by the user requires a reset of the FIFOs, for example if a channel has just been activated. Then the request for the sample data are sent to the USB interface. The samples received are then sent to the triggering module for storage and analysis (see next section). In the rest of the loop, we check if other parameters such as offset and gain have been modied. These do not require a reset, the new value is simply sent over the USB.
28
5.3.5 trigger.py
Sample data is received by this module in chunks of several hundred samples. First, it is put into a buffer according to the channel number.
The channel that the user selected for triggering is then analyzed, sample for sample, to nd the positions of the trigger event (indicated in the following gure as red dots).
For a periodic signal, there are usually several trigger points. We have to chose one that is followed by enough data to ll the screen.
The old data, that is samples which were received before the trigger point, can now be deleted. This has to be done for all channels in the same way in order to guarantee the synchronicity.
The sample values which are inside the display window are then sent to the drawwindow.py module, where all the channels are drawn in one frame.
29
5.3.6 sampleops.py
Some functions that deal with sample data in a time-consuming way have been separated into this le. This Python module can then be replaced by a module written in C, as shown later in the section on optimization.
5.4
Optimization
The choice of the Python programming language made a rapid development possible. It is however not without disadvantages: Operations on byte strings are not as fast as they would be in C or C++. Code execution time has been measured for an acquisition time of one minute. Table 5.1 shows the times for the function that use the most time in total. The cumulative time is the time of the function itself and all other functions that are called from it. le sampleops.py trigger.py iousb.py sampleops.py iousb.py sampleops.py iousb.py iousb.py dso.py function usb bulk read1 callback write checksamples sortInData nd trig events sendData addDataToReadBuffer cb time/call [s] 614.4 72.7 62.8 31.3 43.5 23.3 22.2 5.6 4.5 cumulative time/call [s] 97.9 312.5 1011.0 134.5
The functions highlighted with bold text have been chosen for optimization. These functions were re-written in C. The result is shown in Table 5.2. Measuring the time of the C function is not possible with the method used. But by comparing the cumulative time of some Python function, one can see that there it is indeed faster: The function sendData for example, which calls write and sortInData, uses 35% less time.
30
31
Chapter 6
Results
6.1 Performance of analog stage
The transfer function of the input stage from the BNC connector to the input of the ADC has been measured. This measurement was done with DC coupling and has been repeated for all gain settings. A network analyzer of type Agilent 4395A with an active FET probe (750MHz) was used. It should be noted that the values of the gain refer to the range of the ADC input. A gain of one means that the maximum input signal of 3V is amplied to the maximum input of the ADC, which is +0.5V . . . + 2.5V. On the Bode diagrams, the marker denoted 0 indicates the 3dB cut-off frequency. Input power, scale and reference position have been adapted to best show each curve.
32
33
Figure 6.9: Bode plots for G = 1 at different nodes The bandwidth obtained here is around 8MHz for some gain congurations and around 20MHz for others. This does not satisfy the specications. An obvious problem is the spike at around 30MHz. If we measure the frequency response at different nodes in the circuit (gure 6.9), it is clear that this spike is caused by the unity-gain buffer op-amp. In the bode diagram, the difference between the signals labeled before buffer and after buffer is caused by the AD8065. The datasheet of this chip shows that it is indeed a shortcoming. In gure 6.11 it can be seen that the magnitude of this error depends on CL , the load capacitance from output to ground.
34
Figure 6.10: AD8065 small signal frequency response. Excerpt from datasheet [1]
Figure 6.11: AD8065 frequency response for various CL . Excerpt from datasheet [1]
35
6.2
Software performance
The software runs on Linux, and with slightly worse performance on Windows. It can acquire the samples and display the traces graphically. All parameters (AC/DC coupling, attenuation, gain, offset) can be modied. The channels are shown in the same frame using different colors. The sampling frequency is theoretically limited to about 3Msps. This is due to the USB interface which has a throughput of about 43Mbit/s (reading speed, see [10]), and the fact that each sample uses 16 bits. Improvements should be possible here, as the gross throughput for USB is 480Mbit/s. Currently, the sampling frequency is limited by the latency of the operating system on the PC. At 2MHz for example, the FIFO memory would ll up completely in 8ms. Desktop operating systems such as Windows and Linux have a granularity of the task scheduler that is in the range of 1 to 10ms. The problem is apparent on gure 6.12, which shows the communication on the parallel bus when connected to a Linux PC. Each spike represents one packet of 1022 words. Interruptions of up to 10ms duration are occurring. On a reasonably fast PC running Linux, the highest sampling rate achieved using one channel was 1.5Msps. One possible solution is to use a real-time operating system, such as RTLinux or RTAI. The drawback of such a system would be the loss of compatibility with any desktop PC.
36
Chapter 7
The project, in its current state, has the basic functionalities of a digital oscilloscope. It is capable of measuring voltage signals smaller than 30V. The signal can be attenuated or amplied at various factors. The digital logic that processes the sample data has been designed to be simple. On the hardware side, no programming was involved. PCB layouts for the circuits have been made and the oscilloscope was assembled with two channels. I characterized the frequency response of the analog stage. The software allows the user to view the data in the same way as with any oscilloscope, and to modify the conguration of the hardware (gain, sampling rate, etc.). Written in Python with some functions optimized in C, the software runs on both the MS Windows and GNU/Linux operating systems. The specications are however not completely satised: real-time sampling faster than 1.5Msps is not possible. This is due to the latency of the operating system and is unlikely to be improved unless signicant changes are made to the hardware or the software. Also, the desired analog bandwidth could not be reached.
7.2
Future prospects
The following improvements to this project are possible: resolve problems in the high frequency range by choosing a different operational amplier (see section 6.1) and modifying the PCB layout to reduce parasitic capacitances. investigate the use of programmable logic (CPLD or FPGA) instead of standard 74 logic chips. This would likely result in higher exibility and lower component count but also higher price. design, test and integrate a power supply that satises the requirements laid out in section 4.5. The 5V supply of the USB interface could be used for this, as power consumption is quite low (< 1W for one channel).
37
do trigger detection in hardware. This would allow for higher sampling rates if the data is not transmitted in real-time, but only after a trigger condition has occurred. extend the software to do various data visualization methods such as frequency analysis, XY curves or arithmetic operations on multiple channels.
***
38
Bibliography
[1] Analog Devices Inc.: AD8065/AD8066 datasheet. [2] area26: PC-based DSO. http://beta.area26.no-ip.org/projects/dso. [3] BitScope Designs: The Bitscope hardware design. hardware/. http://www.bitscope.com/design/
[4] eosystems.ro: eOscope. http://eosystems.ro/eoscope/eoscope_en.htm. [5] Glaser, J.: Digital sampling oscilloscope. http://johann-glaser.at/projects/DSO/. [6] Grocutt, T.: Large storage depth oscilloscope, 2000. http://dsoworld.co.uk/. [7] Jones, D.L.: Digital storage oscilloscope adapter Mk3, 1998. http://alternatezone.com/ electronics/dsoamk3.htm. [8] Kester, W.: Data Conversion Handbook. Elsevier/Newnes, 2005, ISBN 0750678410. http: //www.analog.com/library/analogDialogue/archives/39-06/data_conversion_ handbook.html. [9] Linear Technology Corporation: LTC6903/LTC6904 datasheet. [10] Lo Conte, F.: Interface I/O Universelle pour port USB, 2006. [11] Lutti, L.: Autocostruzione di un oscilloscopio digitale, 2003. http://www.enetsystems.com/ ~lorenzo/scope/. [12] Rappin, N. and R. Dunn: wxPython in Action. Manning Publications Co., Greenwich, CT, USA, 2006, ISBN 1932394621. [13] Rossum, G. van: Python reference manual, 2006. http://docs.python.org/ref/ref.html. [14] Smart, J., R. Roebling, V. Zeitlin, R. Dunn, et al.: wxWidgets 2.8.6: A portable C++ and Python GUI toolkit, 2007. http://www.wxwidgets.org/manuals/stable/wx_contents.html.
39
Appendix A
Code
main.py
25
#!/usr/bin/env python
40
30
import controlsframe, drawwindow, dso, settings, trigger from commondefs import from cong import
10
class TracesFrame(wx.Frame): def init (self): wx.Frame. init (self, parent=None, title=DSO, size=(1000,512)) self.window = drawwindow.DrawWindow(self, settings)
35
15
40
def OnExit(self, event=None): settings.halt.acquire() logging.info(Waiting for acquisition thread to reset device...) time.sleep(0.5) logging.info(Goodbye!) sys.exit()
20
class App(wx.App):
trigger = trigger.Trigger(settings)
30
for x in range(1,T DIVS) ] + \ [(0, yself.h/V DIVS, self.w, yself.h/V DIVS) for y in range(1,V DIVS) ]
50
def main(): thread.start new thread(dso.DSO().start, (trigger.callback, settings)) App().MainLoop() def draw(self, dc): def scalepoint(s): return (512 s) self.h / 512
55
main()
40
drawwindow.py
T DIVS = 10 V DIVS = 8
class DrawWindow(bufferedwindow.Window):
50
41
55 60 65 70
10
15
def init (self, parent, settings, id=1): self.settings = settings bufferedwindow.Window. init (self, parent)
# show sampling frequency and time per grid division dc.SetTextForeground(#FFFFFF) f khz = self.settings.frequency / 1000.0 tdiv = self.settings.time width / f khz / oat(T DIVS) if tdiv > 10.0: freq text = %.3fkS/s %.1fus/DIV % (f khz,tdiv) elif tdiv > 1.0: freq text = %.2fkS/s %.2fus/DIV % (f khz,tdiv) elif tdiv > 0.1: freq text = %.1fkS/s %.3fus/DIV % (f khz,tdiv) else: freq text = %.2fMS/s %.0fns/DIV % (f khz/1000.0,tdiv1000.0) dc.DrawText(freq text, 0, self.h20) for chan in self.settings.active chans: ch data = self.settings.channels[chan] dc.SetPen(wx.Pen(self.channelColors[chan], 1, wx.SOLID)) # calculate y pixel coordinates from sample values ypoints = map(scalepoint, ch data.samples) dc.DrawLines(zip(self.xpoints, ypoints)) text = CH%d %s %.3fV/DIV % (chan+1,
20
25
# precalculate list of xcoordinates of samples self.xpoints = [ iself.w/self.settings.time width for i in xrange(self.settings.time width) ]
coupling names[ch data.coupling], (5.75 / gain factors[ch data.gain] / V DIVS)) dc.SetTextForeground(self.channelColors[chan]) dc.DrawText(text, chanself.w/4, 0) import wx, dso from commondefs import from cong import
5
controlsframe.py
75
if chan == self.settings.trig channel: tlevel = scalepoint(self.settings.trig level) dc.DrawLine(0, tlevel, self.w/20, tlevel) class Frame(wx.Frame):
bufferedwindow.py
10
class Window(wx.Window):
Doublebuffered window
def init (self, parent, pos = wx.DefaultPosition, size = wx.DefaultSize): wx.Window. init (self, parent, pos=pos, size=size)
42
20 25 30 35
10
# create frame and divide space wx.Frame. init (self, parent=None, id=1, title=controls, style=wx.MINIMIZE BOX|wx.SYSTEM MENU|wx.CAPTION|wx.CLOSE BOX) self.panel = wx.Panel(self) sizer = wx.BoxSizer(wx.VERTICAL) trigsizer = wx.BoxSizer(wx.HORIZONTAL)
self.onSize(None)
15
def make radio box(label, choices, selection, callback): box = wx.RadioBox(self.panel, label=label, style=wx.RA VERTICAL, choices=choices) box.SetSelection(selection) self.Bind(wx.EVT RADIOBOX, callback, box) trigsizer.Add(box, 0, wx.ALL, border=5) # create trig mode radiobox make radio box(Trig mode, trig mode names, self.settings.trig mode, self.trig mode)
20
def onSize(self,event): Make a new offscreen bitmap buffer. self.buffer = wx.EmptyBitmap(self.GetClientSizeTuple()) self.updateDrawing()
25
# create trig channel radiobox make radio box(Channel, [CH+str(i+1) for i in range(NUM CHANNELS)], self.settings.trig channel, self.trig channel)
30
def updateDrawing(self): Create buffer for new drawing dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) self.draw(dc)
# create trig condition radiobox make radio box(Condition, trig condition names,
40
sizer.Add(trigsizer, 0, wx.EXPAND)
85
45
90
50
freq label = wx.StaticText(self.panel, label=u/\/ Sampling frequency /\ /, style=wx.ALIGN CENTRE) freq slider = wx.Slider(self.panel, value=self.settings.freq setting/FREQ DIV, minValue=0, maxValue=MAX FREQUENCY/FREQ DIV) self.Bind(wx.EVT SLIDER, self.frequency, freq slider) sizer.Add(freq label, 0, wx.EXPAND) sizer.Add(freq slider, 0, wx.EXPAND)
95
chansizer = wx.BoxSizer(wx.HORIZONTAL)
55
# offset slider self.offset label[c] = wx.StaticText(self.panel, label=, style=wx.ALIGN CENTRE) self.set offset text(c) csizer.Add(self.offset label[c], 0, wx.ALL|wx.ALIGN CENTER, border=2) coffset = wx.Slider(self.panel, id=c, value=MAX OFFSET self.settings.channels[c].offset, minValue=0, maxValue=MAX OFFSET, size=(5,300), style=wx.SL VERTICAL) self.Bind(wx.EVT SLIDER, self.offset, coffset) csizer.Add(coffset, 1, wx.ALL|wx.EXPAND|wx.ALIGN CENTER, border=2)
100
43
105 110 115 120
60
# gain increase/decrease buttons self.gain label[c] = wx.StaticText(self.panel, label=Gain: x1) csizer.Add(self.gain label[c], 0, wx.ALL|wx.ALIGN CENTER, border=2)
65
# trig level tsizer = wx.BoxSizer(wx.VERTICAL) tlevel label = wx.StaticText(self.panel, label=Trig\nlevel, style=wx.ALIGN CENTRE) tsizer.Add(tlevel label, 0, wx.ALL|wx.ALIGN CENTER, 2) tlevel = wx.Slider(self.panel, value=MAX SAMPLE VALUEself.settings.trig level, minValue=0, maxValue=MAX SAMPLE VALUE, size=(5,400), style=wx.SL VERTICAL) self.Bind(wx.EVT SLIDER, self.trig level, tlevel) tsizer.Add(tlevel, 1, wx.ALL|wx.EXPAND|wx.ALIGN CENTER, 2) chansizer.Add(tsizer, 1, wx.ALL, border=5)
gainsizer = wx.BoxSizer(wx.HORIZONTAL) minusbutton = wx.Button(self.panel, id=c, label=, size=(26,26)) self.Bind(wx.EVT BUTTON, self.decrease gain, minusbutton) plusbutton = wx.Button(self.panel, id=c+10, label=+, size=(26,26)) self.Bind(wx.EVT BUTTON, self.increase gain, plusbutton) gainsizer.Add(minusbutton, 0, wx.ALL, border=0) gainsizer.Add(plusbutton, 0, wx.ALL, border=0) csizer.Add(gainsizer, 0, wx.ALL|wx.ALIGN CENTER, border=2) chansizer.Add(csizer, 1, wx.ALL, border=5)
70
75
for c in range(NUM CHANNELS): # channel toggle button csizer = wx.BoxSizer(wx.VERTICAL) cbutton = wx.ToggleButton(self.panel, id=c, label=channel names[c], size=(50,26)) if c in self.settings.active chans: cbutton.SetValue(True) self.Bind(wx.EVT TOGGLEBUTTON, self.toggle channel, cbutton) csizer.Add(cbutton, 0, wx.ALL|wx.ALIGN CENTER, border=0)
sizer.Add(chansizer,1, wx.EXPAND) self.panel.SetSizer(sizer) sizer.Fit(self) def trig mode(self, event): self.settings.trig mode = event.Selection def trig channel(self, event):
80
125
dso.py
import sys, logging, time from optparse import OptionParser
def trig level(self, event): self.settings.trig level = MAX SAMPLE VALUE event.Int
130 5
import iousb, settings, spi from commondefs import from cong import from sampleops import checksamples
135 10
def toggle channel(self, event): # rst remove channel from list of active channels self.settings.active chans = [ c for c in self.settings.active chans if c != event.Id ] # then add it again if it is active if event.Selection: self.settings.active chans.append(event.Id)
140 15
44
20 25 30 35
def set offset text(self, c): self.offset label[c].SetLabel(Offset:\n%.3fV % ( self.settings.channels[c].offset \ MAX OFFSET VOLTAGE / (MAX OFFSET+1) ))
145
class DSO(object): def init (self): logging.info(opening USB device...) try: self.card = iousb.IODevice() except iousb.NoInterfaceError: logging.error(No Cypress USB interface found) sys.exit(1) except iousb.InterfacePermissionsError: logging.error(Claiming USB interface not permitted. + Change permissions or run program as root user.) sys.exit(1) spi.init(self.card) def reset fos(self): Stop oscillator, reset FIFO of each channel, restart oscillator. spi.sendOscillator(0,0,3) for channel in self.settings.active chans: self.card.clearBit(BASE ADDRESS + channel, FIFO RS) self.card.setBit(BASE ADDRESS + channel, FIFO RS)
def offset(self, event): o = MAX OFFSET event.Int self.settings.channels[event.Id].offset = o self.set offset text(event.Id)
150
155
def decrease gain(self, event): g = self.settings.channels[event.Id].gain 1 if g<0: g = 0 self.settings.channels[event.Id].gain = g self.gain label[event.Id].SetLabel(Gain: +gain names[g])
160
#TODO halt and check fo state spi.sendOscillator((self.settings.freq setting >> 10), (self.settings.freq setting & 0x3ff)) self.card.sendData()
try:
80
40
def set coupling(self, channel, coupling): if coupling: self.card.setBit(BASE ADDRESS + channel, ACDC) else: self.card.clearBit(BASE ADDRESS + channel, ACDC) result = checksamples(data) callback(channel, result) except RuntimeError: logging.warning(FIFO FULL channel:%d, channel) callback(1, None) self. reset fos()
45
def set gain(self, ch, gain): Combine divider and multiplier circuits to obtain correct gain.
90
50
if gain in [G 0 1, G 0 5, G 2, G 10]: # enable /10 divider self.card.clearBit(BASE ADDRESS + ch, ATTEN10) else: self.card.setBit(BASE ADDRESS + ch, ATTEN10)
95
logging.info(set coupling, attenuation and gain...) for ch in range(NUM CHANNELS): self.card.addDataToWriteBuffer(BASE ADDRESS + ch, [0xffff]) self.card.clearBit(BASE ADDRESS + ch, GAIN5) self.card.clearBit(BASE ADDRESS + ch, GAIN20) self. set coupling(ch, 0) self. set offset(ch, settings.channels[ch].offset) self.card.sendData()
55
45
100 105 110 115
if gain in [G 0 5, G 5, G 10, G 100]: # enable x5 multiplier self.card.setBit(BASE ADDRESS + ch, GAIN5) else: self.card.clearBit(BASE ADDRESS + ch, GAIN5)
while not settings.halt.locked(): if settings.active chans modied or settings.frequency modied: callback(1, None) self. reset fos() settings.active chans modied = False settings.frequency modied = False
60
65
if gain in [G 2, G 10, G 20, G 100]: # enable x20 multiplier self.card.setBit(BASE ADDRESS + ch, GAIN20) else: self.card.clearBit(BASE ADDRESS + ch, GAIN20)
READ SAMPLES = 1020 / len(settings.active chans) for ch in settings.active chans: self.card.addDataToReadBuffer(READ SAMPLES, BASE ADDRESS+ch, cb) self.card.sendData()
def start(self, callback, settings, format=dec, channels=0): Send conguration to module and start receiving samples.
70
for ch in settings.active chans: chan data = settings.channels[ch] if chan data.coupling modied: self. set coupling(ch, chan data.coupling) chan data.coupling modied = False resend = True
75
if chan data.offset modied: self. set offset(ch, chan data.offset) chan data.offset modied = False resend = True
120
if chan data.gain modied: self. set gain(ch, chan data.gain) chan data.gain modied = False resend = True trig ch = self.settings.trig channel old trig len = len(self.samples[trig ch]) self.samples[channel].extend(data)
30
125
if channel == trig ch and self.settings.trig mode in (T RUN, T SINGLE): # if we have new data for the triggered channel, analyze it
130
self.trig events.extend( nd trig events( data, old trig len, self.settings.trig level, self.settings.trig condition)) trig sample = 0 min len = min([ len(self.samples[c]) for c in self.settings.active chans ])
trigger.py
46
45 50 55 60
import thread from commondefs import from sampleops import nd trig events
class Trigger(object):
10
for i, e in enumerate(self.trig events): if (min len e) < self.settings.time width: for ee in range(i, len(self.trig events)): self.trig events[ee] = trig sample del self.trig events[0:i] break trig sample = e
15
if len(self.samples[trig ch]) > 10self.settings.time width: # no trig event found, delete old samples print del old samples trig sample=len(self.samples[trig ch])self.settings.time width self.trig events = [] for c in self.settings.active chans: del self.samples[c][:trig sample] elif self.settings.trig mode == T NONE: for ch in data.keys():
20
def callback(self, channel, data): if channel == 1: # reset sample buffer for i in self.samples.keys(): self.samples[i] = [] self.trig events = [] return
65
def get samples(self): for c in self.settings.active chans: self. get channel samples(c)
25
shiftBits(0, oct, 4) shiftBits(0, dac, 10) shiftBits(0, cnf, 2) card.setBit(BASE ADDRESS, SPI OSCI CS) def sendDAC(channel, data, command = 3): Send data to DAC.
30
70
def get channel samples(self, ch): # trigger type ttype = self.settings.trig mode swidth = self.settings.time width
75
Default command = 3 is to write voltage data and power up DAC. See LTC2630 datasheet. Does not call sendData(). card.clearBit(BASE ADDRESS+channel, SPI CLK) card.clearBit(BASE ADDRESS+channel, SPI DAC CS)
80
if ttype == T NONE: self.settings.channels[ch].samples = self.samples[ch] else: if ttype == T RUN: self.settings.channels[ch].samples = self.samples[ch][:swidth] elif ttype == T STOP: self.settings.channels[ch].samples = self.samples[ch][swidth:] del self.samples[ch][:]
35
47
45 50
spi.py
card = None
def shiftBits(channel, value, bits): for i in range(bits): if value&(2(bits1)): card.setBit(BASE ADDRESS+channel, SPI DIN) else: card.clearBit(BASE ADDRESS+channel, SPI DIN) clkToggle(channel) value <<= 1 def clkToggle(channel): card.clearBit(BASE ADDRESS + channel, SPI CLK) card.setBit(BASE ADDRESS + channel, SPI CLK)
10
15
See LTC6903 datasheet for detailed meaning of parameters. Does not call sendData().
iousb.py
import array, time, usb, logging from cong import from sampleops import usb bulk read1
45
class IODevice(object):
10
The names of the methods correspond mostly to those in Fabrizios CUSBControl.cpp. Buffer lengths are not given in arguments as the length of the list is dened implicitly in Python.
55
15
Return interface handle of USB card for bus in usb.busses(): for dev in bus.devices: if dev.idVendor==VEND ID and dev.idProduct==PROD ID: logging.info(IO card found, trying to congure...) self.intf = dev.congurations[0].interfaces[0][0] logging.debug( Interface nr:%s class: %s subclass:%s protocol:%s, self.intf.interfaceNumber, self.intf.interfaceClass, self.intf.interfaceSubClass, self.intf.interfaceProtocol) return dev
20
def addDataToWriteBuffer(self, address, data): Queue data for writing self.writeBuffer.append(len(data)) self.writeBuffer.append(address) self.writeBuffer.extend(data) self.last written[address] = data[1] def addDataToReadBuffer(self, count, address, callback): Queue read requests
48
70 75 80 85
25
30
35
40
def del (self): try: logging.info(closing USB interface...) self.handle.releaseInterface(self.intf) del self.handle except: pass
def ndDevice(self):
tosend.append(0xFFFF) tosend.extend(self.readBuffer)
130
Resend the last value. self.addDataToWriteBuffer(address, [self.last written[address]]) class Request(object): pass class NoInterfaceError(Exception): pass class InterfacePermissionsError(Exception): pass
tosend.extend(self.writeBuffer)
90
self. write(tosend)
95
if read len > 0: self.inData = usb bulk read1(self.handle, 2self.currentGPIFCount) self. sortInData()
main.py
#!/usr/bin/env python import logging, thread, time, sys, wx
5
100
import controlsframe, drawwindow, dso, settings, trigger from commondefs import from cong import
105 10
49
15 20 25 30
def sortInData(self): Call callback function for every received data block for req in self.readRequests: # rst two words are not used, ignore them req.callback(req.address, self.inData[2:req.count+2]) del self.inData[:req.count+2] self.readRequests = []
class TracesFrame(wx.Frame): def init (self): wx.Frame. init (self, parent=None, title=DSO, size=(1000,512)) self.window = drawwindow.DrawWindow(self, settings) self.timer = wx.Timer(self, id=1) wx.EVT TIMER(self, 1, self.newDrawing) self.timer.Start(50) # 20 images / second
110
def write(self, buffer, timeout = 200): # buffer is a 16bit array but we need to write 8bit # therefore we convert it to a string b = array.array(H, buffer).tostring() self.handle.bulkWrite(self.bulkOut, b, timeout)
115
def setBit(self, address, bit): Set a specic bit. word = self.last written[address] | (1<<bit) self.addDataToWriteBuffer(address, [word])
120
class App(wx.App): def OnInit(self): frameTraces = TracesFrame() frameTraces.Show() frameControls = controlsframe.Frame(settings) frameControls.Show() frameTraces.Bind(wx.EVT CLOSE, self.OnExit) frameControls.Bind(wx.EVT CLOSE, self.OnExit)
def clearBit(self, address, bit): Clear a specic bit. word = self.last written[address] & (1<<bit) self.addDataToWriteBuffer(address, [word])
125
15
return True
35
20
static PyObject usb bulk read1(PyObject self, PyObject args) { int size = 0; Py usb DeviceHandle handle;
40
def OnExit(self, event=None): settings.halt.acquire() logging.info(Waiting for acquisition thread to reset device...) time.sleep(0.5) logging.info(Goodbye!) sys.exit()
25
45
settings = settings.Settings() settings.channels[0].active = True settings.channels[1].active = False settings.trig channel = 0 char buffer = malloc(size); if (!buffer) return NULL;
30
trigger = trigger.Trigger(settings)
logging.basicCong(level=getattr(logging,LOGLEVEL))
50
Py BEGIN ALLOW THREADS size = usb bulk read(handle>deviceHandle, BULK IN EP, buffer, size, USB READ TIMEOUT); Py END ALLOW THREADS
50
35 40 45
55
main()
sampleops.c
PyObject ret = NULL; if (size < 0) { PyErr SetString(PyExc RuntimeError, USB read error); } else { int i; u int16 t bufptr = (u int16 t ) buffer; ret = PyList New(size/2); for (i=0; i<size/2; i++) PyList SET ITEM(ret, i, PyInt FromLong((int)bufptr[i])); } free(buffer); return ret; } PyObject result;
50
10
typedef struct Py usb DeviceHandle { PyObject HEAD usb dev handle deviceHandle; int interfaceClaimed; } Py usb DeviceHandle;
#dene FIFO EMPTY MASK (1<<9) #dene FIFO FULL MASK (1<<10) #dene USB READ TIMEOUT 100
55
static PyObject checksamples(PyObject self, PyObject args) { PyObject data; if(!PyArg ParseTuple(args, O, &data)) return NULL;
} return result; }
100
60
105
65
static PyMethodDef sampleops methods[] = { {checksamples, checksamples, METH VARARGS, NULL}, {nd trig events, nd trig events, METH VARARGS, NULL}, {usb bulk read1, usb bulk read1, METH VARARGS, NULL}, {NULL, NULL} / sentinel / };
110
PyMODINIT FUNC initsampleops(void) { fputs(Using C sample analysis functions\n, stderr); result = PyList New(1020); Py InitModule3(sampleops, sampleops methods, NULL);
70
for(i=0; i<len; i++) { s = PyInt AS LONG(PyList GET ITEM(data, i)); if (!(s & FIFO FULL MASK)) { PyErr SetString(PyExc RuntimeError, FIFO full); return NULL; } PyList SetItem(result, i, PyInt FromLong(s & 0x1ff)); if(!(s & FIFO EMPTY MASK)) { i++; break; } } Py DECREF(data); return PyList GetSlice(result, 0, i);
115
75
51
static PyObject nd trig events(PyObject self, PyObject args) { static int prev = 10000;
cong.py
# USB device vendor ID VEND ID = 0x04b4
80
PyObject data; int old trig len, level, tcond; PyArg ParseTuple(args, Oiii, &data, &old trig len, &level, &tcond);
5
# USB device product ID PROD ID = 0x1004 # USB device bulk in endpoint BULK IN EP = 0x86
85
# USB device bulk out endpoint BULK OUT EP = 0x02 USB READ TIMEOUT = 100
15
90
if(tcond ? ((prev>level) && (s<=level)) : //falling edge? ((prev<level) && (s>=level)) ) //rising edge? PyList Append(result, PyInt FromLong(i+old trig len));
95
prev = s;
BASE ADDRESS = 0xA100 READ BUFFER SIZE = 4096 WRITE BUFFER SIZE = 4096 MAX GPIF COUNTER = 65535
20 35
SPI DIN=3 SPI OSCI CS=8 SPI DAC CS=1 TIME WIDTH = 500
25 40
30 45
# I/O pins FIFO RS = 0 GAIN5 = 4 GAIN20 = 5 ATTEN10 = 6 ATTEN10 MASK = 2ATTEN10 ACDC = 7 FIFO EMPTY = 9 FIFO EMPTY MASK = 2FIFO EMPTY FIFO FULL = 10 FIFO FULL MASK = 2FIFO FULL MAX OFFSET = 4095 MAX OFFSET VOLTAGE = 2.5 MAX SAMPLE VALUE = 511 MAX FREQUENCY = 11000 FREQ DIV = 16 # log level LOGLEVEL = DEBUG
52
Appendix B
The following programs and libraries are required to run the program: Python 2.4 or higher (http://python.org) wxPython 2.6.* or 2.8.* (http://wxpython.org) wxWidgets (http://wxwidgets.org) pyusb (http://pyusb.berlios.de) libusb (http://libusb.sf.net) On a Debian or Ubuntu system, these can be installed by issuing the following command: sudo apt-get install python-pyusb python-wxgtk2.8 By default, a Linux system does not permit the direct usage of a USB device for any user. The program will abort with an error message. There are two solutions: log in as the root user, or allow USB access for normal users. The latter can be done by copying the le software/linux/ 90-cypressio.rules from the CD to the directory /etc/udev/rules.d.
B.2
Microsoft Windows
The use of the software requires a driver which is incompatible with the driver normally used with Cypress USB devices. The Cypress driver must be completely removed before installing the new driver, which is located on the CD in the software/windows/ directory.
53
Appendix C
Schemata
54
:10
DCOFFSET
R108
R109 20 GAIN20
R119
20
GAIN5
gain=5
D105
OMRON G6K
NC D106 D103
OMRON G6K +3.3V +3.3V 3 5 V+ 1 8 V+ 1 8 V+ V 4 3.3V 5 V 4 3
8 1 5 6 7
AD8065
C101
R101
LT6200
+3.3V 1
LT6200
+3.3V 9
30 3k9
2
U102
1M8
3.3V 3.3V
1M8
R122
3 7 V+
U103 20
2 V 4 3.3V 6
8 2
NC
IN
10 5
NO COM
NC
IN
13 3 MAX4547 16 6
NO COM
R103
7 U105 V+ 2 V 4
ADCIN
MAX4547
55
C102 D101 R106 R115 3V
3.3V
U104
U104
3.3V
100k
2k4
6k8
R111 R112 3k
12k
for AD8065
+3.3V +3.3V
for AD8062
+3.3V
for LT6200
+3.3V
C104
C105
100n
100n
3.3V
100n
100n
3.3V
100n
100n
3.3V
100n
100n
3.3V
+3.3V
Vcc
C201
C210
C209
3 21
10
32
VCC
10
U202 IDT72V05
11 13
C206
29 31
100n 10k
R203
C208
14 19 20 21 22 15
100n
6
D0 D1 D2 D3 D4 D5 D6 D7 D8
Q0 Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8
D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 CLK WRITE
Vcc
ADCIN
2
56
R202
7
51
C203
W R
EF FF XO/HF
26
24 9 23
22p 51
12
CLK CLK
RS XI GND
16
C204
13
C205 PD OE GND
4 5 8 11 14
100n
15
OGND
23
U302
2
D00
4
74574 D00
1D 2D 3D 4D 4Q 16 3Q 17 2Q 18 1Q 2 19
D02
6 8 10 12 14
D01
D06
D10 WRITE
11
READ
16 18 20
13
CLK
15
17
19
READ
11
3.3V
57
+3.3V
C301 U304
Y 3
100n
FEMPTY
EN
VCC
3 2
VOUT
DCOFFSET
74125
FFULL
CSDAC
CS
LTC2630 GND
5
EN
Vcc
C501
10n
U502 CLK
6 5 2 4
C502
7
1u OE U501 CLK NC
2
V+
CLK
74LVC1G04
LTC6903
SCLK
CSCLK
READ1
A14
A13 A13
A2 A3 A4 B4 B3 9 B2 6
A12 A01
A10 BUSWRITE
G2 2Y0 15 12
10
A08
NC A10
A5 13 A6 A7 B7 18 B6 16 B5 13 14
A07 = 0xA1__
11
12
A06
NC A09 A08
17 15
7404
NC
A05
13
14
A04
NC
NC
A03
15
16
A02
NC
A01
17
18
A00
D07
19
20
D06
21
22
D04 U411
Q Q Ce Re/Ce
23
24
74123
A B CLR 1 2 Vdd 3
D09
D01
25
26
D00
NC R402 470
Vcc
27
28
D14
NC R401
D13
29
30
D12
NC
U406
1Q 2Q 19 18
NC ENOUT U402
7410 6 7404 5 4
D11
31
32
D10
33
34
D08
59
R/W
7404 5
ENOUT
35
36
R/W
CSCLK NC NC NC NC
8 7D 7Q 13
37
38
NC
39
40
NC NC
9 1 8D OUT 8Q 12
NC
11
BUSREAD
CLK
U408 D01 D03 D05 D07 D09 D00 D02 D04 D06 D08 D10 WRITE2 READ3 CLK
+3.3V 1 3 5 7 9 11 13 15 17 19
U409
2 4 6 8 10 12 14 16 18 20 +3.3V Vdd
U410 D01 D03 D05 D07 D09 D00 D02 D04 D06 D08 D10 WRITE3 READ4 CLK
1 3 5 7 9 11 13 15 17 19 2 4 6 8 10 12 14 16 18 20 +3.3V Vdd
D00
D01
D02
D03
D04
D05
D06
D07
D08
10
D09
11
12
READ1
13
14
WRITE1
WRITE4
CLK
15
16
17
18
+3.3V
19
20
3.3V
3.3V
3.3V
3.3V