:::info # PHYS 3360 Chapter 12 Experiments > [name=Nikolai Nekrutenko - Wed/Fri Lab Group] ::: Online version of this article (I highly recommend this one over the PDF) - https://hackmd.io/@nekrutnikolai/H1SFfqHvo ## Experiment 12.1 The purpose of this experiment was to examine the behavior of a crude 8-bit DAC driven by the Arduino and compare the voltage which it produces with the theoretically expected values. A schematic of the circuit is shown below: ![](https://i.imgur.com/7NywCro.png) Here is a program which iterates through the different pins connected to the DAC, and then turns them on all at once: ```C++ const int NP=8; const int PINS[NP]={25, 27, 29, 31, 33, 35, 37, 39}; void setup() { Serial.begin(2000000); for(int i=0; i<NP; i++){ pinMode(PINS[i], OUTPUT); } } void loop() { int NB = 10; // the number of bytes int i, j, dac[NB] = {0, 1, 2, 4, 8, 0x10, 0x20, 0x40, 0x80, 0xff}; Serial.println("start"); for(j=0; j<NB; j++){ while(i = Serial.read() < 0); Serial.print("Binary: "); Serial.print(dac[j], BIN); for(i=0; i<NP; i++){ digitalWrite(PINS[i], bitRead(dac[j], i)); } } } ``` The output voltage of the Arduino digital pins was measured to be 5.13V, which is used as $V_{ref}$ in the equation below. The expected output voltage is given by: $$V_{DAC} = \frac{V_{ref}}{3 \times 2^7}[2^7B_7+2^6B_6+ \dots +2^0B_0]$$ This table compares the output voltage of the Arduino DAC with that of the theoretically expected values. | Binary | Output | Expected | | -------- | ------- | -------- | | 00000000 | 0.0000V | 0.0000V | | 00000001 | 0.0144V | 0.0134V | | 00000010 | 0.0280V | 0.0267V | | 00000100 | 0.0550V | 0.0534V | | 00001000 | 0.1086V | 0.1069V | | 00010000 | 0.215V | 0.214V | | 00100000 | 0.429V | 0.428V | | 01000000 | 0.856V | 0.885V | | 10000000 | 1.705V | 1.71V | | 11111111 | 3.40V | 3.41V | ## Experiment 12.2 In this experiment, a voltage ramp was generated by first, the same schematic as in *Experiment 12.1*. Then, a circuit was built with a DAC-0808 IC to also produce a voltage ramp. Here is the program which generates the voltage ramp, it works by consecutively increasing the voltage by translating a value between 0 and 255 into binary, which is then used to trigger the output pins of the Arduino for the DAC. ```C++ const int NP=8; const int PINS[NP]={25, 27, 29, 31, 33, 35, 37, 39}; void setup() { Serial.begin(2000000); for(int i=0; i<NP; i++){ pinMode(PINS[i], OUTPUT); } } void loop() { int NB = 256; // the number of bytes int i, j; Serial.println("start"); for(j=0; j<NB; j++){ for(i=0; i<NP; i++){ digitalWrite(PINS[i], bitRead(j, i)); } delayMicroseconds(100); } } ``` This is an image of the waveform generated by using the DAC from *Experiment 12.1*: ![](https://i.imgur.com/kC4Jsyr.jpg) The wavefrom appears to be very linear, meaning the resistors had a high tolerance. The voltage ranges from 0.00V to 3.42V, with a period of 18.27ms. Zooming into this ramp, we can see the individual steps: ![](https://i.imgur.com/paABbUx.jpg) Next, a circuit was built using a DAC-0808 IC, two 3140 OpAmps, two 1N914A diodes, and a 74HC04 inverter IC, as shown in the schematic below: ![](https://i.imgur.com/KDTDSse.png) The output waveform of this circuit is very linear, but is almost double in amplitude of the previous DAC circuit. The waveform is shown below: ![](https://i.imgur.com/yjIgDLi.jpg) The voltage ranges from 0.00V to 7.44V, with a period of 18.28ms. Here is the waveform of L: ![](https://i.imgur.com/PBQe4FB.jpg) Varying Vx, which is the non-inverting input voltage of the second OpAmp, affected the proportion of HI to LOW signal. Or, in other words, the width of HI and LOW relative to the period of the pulse. ## Experiment 12.3 In this experiment, an Arduino-based voltmeter was constructed based on the DAC 0808 IC circuit as in *Experiment 12.2*. Initially, the code below was used to print out the corresponding decimal value in relation to the voltage when the signal from L turned high, which is when the DAC reaches the same voltage as $V_x$. In essence, the voltage is being incremented bit by bit until the threshold to turn L high is reached due to the comparator circuit. This decimal value was correlated with $V_x$, which was adjusted with the potentiometer, and was measured on a multimeter. A line of best fit was derived from the data, used to output the voltage in the final program. ```C++const int NP=8; const int PINS[NP]={25, 27, 29, 31, 33, 35, 37, 39}; const int INPIN=51; void setup() { Serial.begin(2000000); for(int i=0; i<NP; i++){ pinMode(PINS[i], OUTPUT); } pinMode(INPIN, INPUT); } void loop() { int NB = 256; // the number of bytes int i, j; for(j=0; j<NB; j++){ for(i=0; i<NP; i++){ digitalWrite(PINS[i], bitRead(j, i)); } delayMicroseconds(100); if(digitalRead(INPIN) == HIGH){ break; // exit the program } } if(j>0 && j<256){ Serial.println(j); } if(j==256 || j==0){ Serial.println("Vx outside of detectable range"); } delay(100); } ``` The following data was collected: | Decimal | Voltage | | ------- | ------- | | 255 | 1.278 | | 239 | 1.087 | | 223 | 0.904 | | 207 | 0.719 | | 191 | 0.533 | | 175 | 0.341 | | 159 | 0.157 | | 143 | -0.023 | | 127 | -0.206 | | 111 | -0.388 | | 95 | -0.577 | | 79 | -0.754 | | 63 | -0.938 | | 47 | -1.128 | | 31 | -1.309 | | 20 | -1.429 | Then, this data was plotted with Google Sheets, and line of best fit was derived: ![](https://i.imgur.com/ty0K6LI.png) The equation for the line of best fit is: $$V_x = 0.0115j-1.67$$ Where $V_x$ is the voltage to be measured and $j$ is the integer combination of Arduino I/O outputs to produce that voltage. It was also noted that this Arduino-based voltmeter can only measure values of $V_x$ from -1.67V to 1.26V. A modified version of the initial program prints out the voltage based on the line of best fit derived above: ```C++const int NP=8; const int PINS[NP]={25, 27, 29, 31, 33, 35, 37, 39}; const int INPIN=51; void setup() { Serial.begin(2000000); for(int i=0; i<NP; i++){ pinMode(PINS[i], OUTPUT); } pinMode(INPIN, INPUT); } void loop() { int NB = 256; // the number of bytes int i, j; for(j=0; j<NB; j++){ for(i=0; i<NP; i++){ digitalWrite(PINS[i], bitRead(j, i)); } delayMicroseconds(100); if(digitalRead(INPIN) == HIGH){ break; // exit the program } } if(j>0 && j<256){ Serial.print("Voltage: "); Serial.print((double(0.0115*j)-1.67), 3); Serial.println("V"); } if(j==256 || j==0){ Serial.println("Vx outside of detectable range"); } delay(100); } ``` Measuring a few different voltages: | Multimeter (V) | Arduino (V) | Error (mV) | | -------------- | ----------- | ---------- | | 1.193 | 1.182 | 11 | | 0.999 | 0.998 | 1 | | 0.903 | 0.895 | 8 | | 0.703 | 0.699 | 4 | | 0.505 | 0.504 | 1 | | 0.301 | 0.308 | 7 | | 0.097 | 0.101 | 4 | | -0.106 | -0.106 | 0 | | -0.305 | -0.301 | 4 | | -0.506 | -0.509 | 3 | | -0.703 | -0.704 | 1 | | -0.885 | -0.888 | 3 | | -1.100 | -1.106 | 6 | | -1.301 | -1.313 | 12 | The average sampling error of the data above is 4.64mV, with a maximum of 12mV, and a minimum of 0mV. The sampling error is given by the equation: $$\Delta V_{samp} = \frac{V_{max}-V_{min}}{2^n-1}$$ Evaluating this expression results in: $$\Delta V_{samp} = \frac{1.26V+1.67V}{2^8-1}=11.5mV$$ The average sampling error of the setup is less than the maximum expected error, 11.5mV given by the equation above. However, the maximum error, 12mV, is close to the maximum expected error of 11.5mV. It makes sense for the average of 4.64mV to be less than the maximum expected error of 11.5mV, as on average, for any value of $V_x$, is most probable to be within this window of error. As can be seen, the Arduino is relatively close to the multimeter's values. The times where is off is mostly due to the 8bit precision of the system not having enough resolution to exactly measure the voltages at the same precision as the mulimeter. This is further supported by the sampling error, calculated above. ## Experiment 12.4 The successive approximation method, a much more time-effecient method of getting the voltage as opposed to simply climbing up the voltage ramp in the finest resolution possible, was implemented following the same circuit setup as in *Experiment 12.2* and *Experiment 12.3*. Here is the code for this successive approximation method: ```C++ const int NP=8; const int PINS[NP]={25, 27, 29, 31, 33, 35, 37, 39}; const int INPIN=51; void setup() { Serial.begin(2000000); for(int i=0; i<NP; i++){ pinMode(PINS[i], OUTPUT); } pinMode(INPIN, INPUT); } void loop() { int NB = 7; // the number of bytes int i, j=0x80, val=0; for(i=0; i<NP; i++){ digitalWrite(PINS[i], LOW); // init to low } for(i=NB; i>=0; i--){ digitalWrite(PINS[i], HIGH); delayMicroseconds(50); if(LOW == digitalRead(INPIN)){ val = val + j; } else { digitalWrite(PINS[i], LOW); j = j/2; // next bit } } Serial.println(val, BIN); delay(100); } ``` Below are images of the program's output and the corresponding $V_{DAC}$ from the oscilloscope: ![](https://i.imgur.com/TgTYD31.jpg) ![](https://i.imgur.com/eaCKFAv.jpg) The waveform above makes sense as the program's output is oscillating between the highest output voltage, 100000000, and a relatively low voltage, 00010000. ![](https://i.imgur.com/13ctnow.jpg) ![](https://i.imgur.com/0mRDhT4.jpg) The waveform above is consistent with the results of the program output, as it is constantly at the highest voltage, 100000000. ![](https://i.imgur.com/sfpyZDy.jpg) ![](https://i.imgur.com/ntJw2FM.jpg) The waveform above is also consistent with the output of the program, as it constant at the low voltage, due to the binary 000000000. ## Experiment 12.5 In this experiment, a digital sampling scope was built with the Arduino's built in ADC and a digital synchronization signal from the CMOS port of the function generator that was passed through a protecting diode and inverter configuration as in the circuit below: ![](https://i.imgur.com/dLUewm7.png) Here is the program which makes the Arduino into a Digital Sampling Scope: ```C++ const int sensorPin = A0; const int N = 512; // # of data points const int PIN = 51; unsigned long data[N]; // to store scope trace void setup() { // put your setup code here, to run once: Serial.begin(2000000); for(int i=0; i<N; i++){ data[i] = 0; //init data to zero } pinMode(PIN, INPUT); } void loop() { // put your main code here, to run repeatedly: int i, j; // wait for synch to go to LOW, then HIGH while(HIGH == digitalRead(PIN)){}; while(LOW == digitalRead(PIN)){}; for(i=0; i<N; i++){ data[i] = analogRead(sensorPin); // read whole curve into data } for(i=0; i<N; i++){ Serial.println(data[i]); // output seperately } delay(1000); // pause } ``` The expected sampling error is given by the equation: $$\Delta V_{samp} = \frac{V_{max}-V_{min}}{2^n-1}$$ Knowing that the Arduino ADC can measure from 0 to 5V, and is 10 bit, evaluating this expression results in: $$\Delta V_{samp} = \frac{5V}{2^{10}-1}=4.89mV$$ This value can also be calculated from the data shown in the two graphs below: ![](https://i.imgur.com/RLhOVIe.jpg) ![](https://i.imgur.com/VDX6bsL.png) Knowing that the amplitude of the waveform generated by the function generator is 4.12V, and that the amplitude of the ADC is approximately 900 points, the sampling error is given by: $$\Delta V_{samp} = \frac{V_{max}-V_{min}}{n_{points}} = \frac{4.12V}{900} = 4.58mV$$ This is close to the expected value of 4.89mV. The images shown below are pairs of the function generator signal, which is the ADC input, and the corresponding Arduino plot: ![](https://i.imgur.com/vV4Ny0D.jpg) ![](https://i.imgur.com/LT4O6qs.png) ![](https://i.imgur.com/oP4YQgH.jpg) ![](https://i.imgur.com/9mrbMSp.png) ![](https://i.imgur.com/8W3ty82.jpg) ![](https://i.imgur.com/RbXDn8a.png) ![](https://i.imgur.com/7ucOkKQ.jpg) ![](https://i.imgur.com/S3rw7px.png) ![](https://i.imgur.com/0zCdee2.jpg) ![](https://i.imgur.com/ZJqF3NA.png) ![](https://i.imgur.com/vr7XAxC.jpg) ![](https://i.imgur.com/1E1wIlH.png) ![](https://i.imgur.com/Ty5oYBC.jpg) ![](https://i.imgur.com/khZhWHt.png) ![](https://i.imgur.com/xsLHRxb.jpg) ![](https://i.imgur.com/PLdT2v0.png) ![](https://i.imgur.com/ZGOxCbl.jpg) ![](https://i.imgur.com/9YdBCTg.png) ![](https://i.imgur.com/kidyjEr.jpg) ![](https://i.imgur.com/6KDcby5.png) It is interesting to observe, that for frequencies greater than or equal to half of the ADC's sampling frequency, ~10kHz, the Arduino-generated waveform picks up low-frequency components that are not the actual input of the ADC. This is called aliasing. ## Experiment 12.6 A random noise generating circuit was built out of two 74HC164 edge-triggered shift registers, some XOR gates of an 74HC86 IC, and a 3140 OpAmp. A fourrier transform was run on the circuit's output to verify that the generated noise is indeed random. In the schematic of the circuits used in the this experiment is shown below. The *CLOCK* was connected to the 100kHz clock on the Digi-Designer. The *Logic switch* was connected to one of the logic switches on the Digi-Designer. *VS* was the signal from the function generator, while *VN+VS* was probed with the oscilloscope. ![](https://i.imgur.com/7sck05H.jpg) ## Optional Experiment 12.6 Here is the signal *VN+VS*: ![](https://i.imgur.com/6n0axaz.jpg) Here is the result of running a fourrier transform on the signal *VN+VS*: ![](https://i.imgur.com/cPK5ABY.jpg) This does look like a random, white noise, signal as the distribution of the fourrier transform is evenly spread accross the whole x-axis, apart from one dominant frequency to the very left of the screen, which is likely the signal from the function generator. ## Experiment 12.7 In this experiment, the noise generator circuit from *Experiment 12.6* was put together. The Arduino oscilloscope was programmed to sum together successive measurements in an attempt to average out the noisy signal. The program from *Experiment 12.5* was modified to output the average of 10 readings for each data point: ```C++ const int sensorPin = A0; const int N = 400; // # of data points const int PIN = 51; unsigned long data[N]; // to store scope trace void setup() {Good Serial.begin(2000000); for(int i=0; i<N; i++){ data[i] = 0; //init data to zero } pinMode(PIN, INPUT); } void loop() { int i, j, sum, count = 10; // wait for TTL to go to LOW, then HIGH while(HIGH == digitalRead(PIN)){}; while(LOW == digitalRead(PIN)){}; for(i=0; i<N; i++){ sum = 0; for(int k=0; k<count; k++){ sum += analogRead(sensorPin); } data[i] = sum; // read whole curve into data } for(i=0; i<N; i++){ Serial.println(float(0.1*data[i])); } delay(1000); // pause } ``` The same signal, *VN+VS*, as for *Experiment 12.6* was passed into the Arduino's ADC: ![](https://i.imgur.com/6n0axaz.jpg) The averaging of the data did make the signal somewhat clearer, but no sinusoidal signal, from the function generator, can be discerned from this data: ![](https://i.imgur.com/YOz7jiT.png) Next, a signal where there was a high proportion of *VS* to *VN* was passed into the Arduino's ADC: ![](https://i.imgur.com/0f2dtsr.jpg) This higher proportion of *VS* to *VN* did make the signal from the Arduino clearer, as a rough sinusoidal wavefrom can be seen in the shape of the function generator input singal: ![](https://i.imgur.com/XLKuaQV.png) Noise elimination could be improved further by averaging more datapoints, but there would be a tradeoff with sampling frequency of the program. ## Optional Experiment 12.7 A radio was hooked up as the source of the noise, instead of the random noise generating circuit, as shown in the schematic below: ![](https://i.imgur.com/dlpdmdM.png) Here is a graph of the waveform of *VN+VS*. Perhaps there was not enough noise, as the signal is still somewhat clearly distinguishable: ![](https://i.imgur.com/W1zsRSE.jpg) ![](https://i.imgur.com/JumNCuX.png) This does resemble the general wavefrom of the input function, although it is not completely sinusoidal as more time is spent of the high part of the signal as opposed to the low part. Perhaps the radio was tuned to the sync signal, which was interfereing with the signal from the function generator.