2020-21 Sem 2 Lab 4: Building a capacitor

The objective of this experiment is to construct a parallel plate capacitor from materials readily available in your home or in a grocery shop. Suggested materials include aluminium foil (for the conducting plates) and cling film (for the dielectric that separates the plates). You’re free to improvise with other materials, but please observe the following rules:

  • Comply with all government guidelines related to Covid-19.
  • Use only dry materials in your capacitor. No liquids!
  • Carefully assess any potential risks associated with the materials you’re using and avoid doing anything that might cause injury (e.g. cutting up aluminium cans which produces dangerously sharp edges).

You’re encouraged to try to make the capacitance large. However, we’re also interested in how densely you can cram the capacitance into a compact space. We’re hoping that some of you will impress us with creative solutions.

To give you time to develop something impressive, we’ll spend two weeks on this experiment. At the end, you’ll submit a written lab report on your work. An example report (for a different, but related experiment) will be provided to guide you in writing your own report.

Theory

The following equation gives the expected capacitance for an ideal parallel plate capacitor.

C = \frac{\epsilon A}{d}

where

  • C is the capacitance in farads [F],
  • \epsilon is the absolute permittivity (often just referred to as the permittivity) of the dielectric (insulating material) that separates the conducting plates in farads per meter [Fm-1],
  • A is the area of each plate in square metres [m2], and
  • d is the distance between the plates in metres [m].

Note that the dielectric thickness, d, shown in the above diagram is greatly exaggerated. In a real capacitor, the dielectric would typically be extremely thin in order to get the plates as close to each other as possible.

Permittivity is a property of a material or medium in which an electric field is present. Tables of permittivity values are available for different materials (e.g. air, water, concrete, soil, glass, etc.). Permittivity has a very significant effect on how electromagnetic waves propagate through a medium so, among other things, it can tell us useful information about how mobile phone or wi-fi signals will penetrate the walls of a building. The medium with the lowest possible permittivity is a perfect vacuum. The permittivity of other materials are often provided as relative permittivity – the ratio between their absolute permittivity and the absolute permittivity of a vacuum.

\epsilon = \epsilon_r \epsilon_0

where

  • \epsilon is the absolute permittivity of the material in question in farads per metre [Fm-1],
  • \epsilon_r is the relative permittivity of the material, which is a dimensionless value (i.e. it has no units because it’s the ratio of two values that are in the same units), and
  • \epsilon_0 is the permittivity of a vacuum, 8.854188 ✕ 10-12 farads per metre [Fm-1].

The permittivity of air is very close to that of a vacuum, so it has a relative permittivity of approximately 1. Materials that are good insulators tend to have low relative permittivities. Have a look online for some of the published lists of material permittivities.

Part 1: Prepare the capacitance meter

You will use the capacitance meter you built last week to test your newly built capacitor. Please upload the code below to the Arduino. It includes some modifications that improve the accuracy of the meter when measuring large capacitances (up to 1000 μF). The capacitance you achieve in the capacitor you build is more likely to be in the nanofarad range, but there’s no harm updating the meter just in case you do achieve a larger capacitance than expected.

Once you’ve updated the code on the capacitance meter, check that it’s still functioning correctly by measuring the capacitance of one of the known capacitors in your kit (e.g. 100 μF).

//
// EEPP Arduino capacitance meter
// Written by Ted Burke - last updated 15-Feb-2021
//
// This system (hardware circuit and software) measures the time
// constant of an RC circuit in order to calculate the value of
// the capacitor. To obtain a time constant of appropriate length,
// the system has five resistor values (100, 1k, 10k, 100k, 1M).
// The 100 ohm resistance is only used for charging the capacitor.
// The other four resistances are tried one at a time until a time
// constant longer than 50ms is observed, at which point the
// capacitance is calculated and printed via the Serial Monitor.
//
 
void setup()
{
  pinMode(2, INPUT); // disable 100 ohm resistor
  pinMode(3, INPUT); // disable 1k resistor
  pinMode(4, INPUT); // disable 10k resistor
  pinMode(5, INPUT); // disable 100k resistor
  pinMode(6, INPUT); // disable 1M resistor
 
  Serial.begin(115200); // open serial connection
}
 
void loop()
{
  int n; // resistor/pin number, e.g. R3 is 10^3 ohms and on pin D3 
  float tau, R, C; // time constant, resistance, capacitance
   
  // Try the resistors in this order: 1k, 10k, 100k, 1M.
  // The resistance used is equal to 10^n, so the value of n is
  // incremented until a time constant over 50ms is observed.
  // That time constant is then used to calculate the capacitance.
  n = 3;
  while (1)
  {
    tau = measureTimeConstant(n);
    if (tau > 50e-3) break;
    if (n == 6) break;
    if (tau == 0) n = 3; // go back to lowest resistance (1k)
    else n++;            // move up to higher resistance
  }
 
  // Calculate the resistor and capacitor values
  R = pow(10,n);
  C = tau / R;
 
  // Display the time constant, resistance and capacitance
  Serial.print("tau="); Serial.print(1000*tau, 3); Serial.print(" ms, ");
  Serial.print("R="); Serial.print(R, 0); Serial.print(" ohm, ");
  Serial.print("C=");
  if      (C < 1e-9) {Serial.print(C*1e12, 3); Serial.print(" pF");}
  else if (C < 1e-6) {Serial.print(C*1e9 , 3); Serial.print(" nF");}
  else if (C < 1e-3) {Serial.print(C*1e6 , 3); Serial.print(" uF");}
  else               {Serial.print(C*1e3 , 3); Serial.print(" mF");}
  Serial.println();
}
 
// This function measures the time constant in seconds using resistor n.
float measureTimeConstant(int n)
{
  unsigned long t1, t2, timeout;
  float Vth;
   
  // Charge the capacitor up to its maximum through the 100 ohm resistor.
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  delay(1000);
  pinMode(2, INPUT);
 
  // Calculate the threshold voltage as 36.8% of the initial voltage.
  // The capacitor voltage drops to this value after one time constant.
  Vth = 0.36788 * analogRead(7);
   
  // Measure the time constant
  t1 = micros();              // record the start time in microseconds
  timeout = t1 + 1200000L;    // set timeout limit at 1.2 seconds
  pinMode(n, OUTPUT);         // enable the selected resistor
  digitalWrite(n, LOW);       // begin discharging the capacitor
  while(analogRead(7) > Vth)  // wait until voltage drops to Vth (or timeout)
    if (micros() > timeout) return 0;
  t2 = micros();              // record the end time in microseconds
  pinMode(n, INPUT);          // disable the selected resistor
  return (t2 - t1) * 1e-6;    // return the time constant (t2 - t1)
}

The circuit is exactly the same as it was in part 4 of last week’s lab.

This is the circuit on the breadboard. The capacitor near the top left corner of the breadboard is just there as an example. Remove that capacitor before plugging in the wires of your newly built capacitor to measure its capacitance.

Part 2: Construct a simple parallel plate capacitor

To begin with, construct a simple capacitor as follows:

  1. Cut out two square pieces of aluminium foil, approximately 20 cm x 20 cm, but leaving an extra strip of foil extending from one corner of each piece (for attaching wires).
  2. Cut two long wires – long enough to reach from the breadboard to opposite corners of your 20 cm x 20 cm square capacitor. Remove the insulation from the last 2 cm of each wire.
  3. Sellotape one wire onto the corner strip of each piece of foil.
  1. Lay one piece of foil down smooth flat surface.
  2. Lay a piece of A4 paper on top of the foil, taking care to cover every part of it except for the strip with the wire attached.
  3. Lay the second piece of foil down so that it is directly above the first piece (but separated from it by the paper). Orient it so that the corner with the wire attached is not at the same corner as the wire for the lower plate.
  4. Lay something flat and heavy (like a hardback book) on top to press the three layers tightly together.
  1. Plug the wires of your capacitor into your breadboard to measure its capacitance. (To give you a rough idea what to expect, when I tried building this capacitor I obtained a capacitance of approximately 15 nF.)
  2. Try pressing down hard on the stack to squeeze the plates ever closer together. Does it affect the measured capacitance?
  3. Using the equation from the theory section above, estimate the expected capacitance of this capacitor and compare it to the measured value. When applying the formula, note the following:
    • The area of the plates must be expressed in square metres.
    • The distance between the plates should be approximately equal to the thickness of the paper sheet. This is difficult to measure directly, but if you measure the height of a stack of sheets then divide by the number of sheets, a reasonably accurate estimate can be obtained. This distance must be expressed in metres.
    • Different types of paper will have different relative permittivities. The exact value for the paper you’re using won’t be possible to find, but you should be able to find a reasonable ballpark estimate online.

Retain all your calculations, measurements, and links to information sources so that you can include them all in your lab report next week.

Part 3: Design, build and test a larger capacitor

Now you’re ready to create your own capacitor design, hopefully with a capacitance much greater than that achieved using the simple design described above.

Strategies to consider:

  • Increase the area of the plates?
  • Try a thinner dielectric material?
  • Use a dielectric with a different permittivity?
  • Stack more plates together?
  • Roll your capacitor into tightly rolled cylinder?
  • Create more than one capacitor and combine them in parallel?

Research online to get some more ideas.

Part 4: Write a lab report about the capacitor you built

An example lab report is now available in the Content section of the EEPP2 Brightspace module. The example report is for a different, but closely related, experiment. When you’re ready to write your report, you can use the example report as a template for writing your own account on this experiment. In the meantime, please retain or collect all of the following items:

  • Photographs of everything you built, ideally including steps during the build process.
  • Screenshots of anything you measured using the capacitance meter (i.e. the Serial Monitor window displaying the measurement).
  • Any calculations you carried out. If the calculations were carried out on paper, then retain the written calculations. If the calculations were carried out in MATLAB / Octave, then grab a screenshot or save it to an M-file so that you can refer back to it.
  • The sources of any information you obtained online – e.g. material permittivity, thickness of paper / cling film, etc.

2020-21 Sem 2 Lab 3: Building a capacitance meter

Theory

In this lab, we use what we have learned about the time constant of an RC circuit to build a capacitance measurement system. The basic principle is to construct an RC circuit using a known resistance and an unknown capacitance and then measure its time constant. The time constant, \tau , of an RC circuit is given by the following equation.

\tau = RC

Hence,

C = \frac{\tau}{R}

In each part of this experiment, we measure the time constant by charging the capacitor to a maximum voltage and then allowing it to discharge through a resistor. The time it takes for the capacitor voltage to drop to 36.8% of its initial value is measured – this is the time constant.

For example, the diagram below illustrates what happens when a voltage source connected to an RC circuit suddenly drops from its initial value down to zero (at time t1). Assuming the voltage source had been at its initial voltage long enough for the system to settle, then the capacitor is fully charged until time t1 when it begins to discharge. The time constant provides a simple description of how fast the voltage drops towards its new resting level (0 V in this case).

In the first part of the lab, you will use the ASGO to capture a step response (of a negative-going step) and then analyse the data in Excel to find the time constant and calculate the capacitance. In parts 3 and 4, you will program the Arduino to measure the time constant automatically and print out the estimated capacitance.

In next week’s lab, you will construct your own capacitor from scratch using aluminium foil and other materials. The meter you build this week will allow you to measure the capacitance of your homemade capacitor next week.

Part 1: Measure capacitance by analysing a step response

In this part of the experiment, you will use the ASGO to capture the step response of an RC circuit and then analyse it in Excel to measure the time constant and hence obtain the capacitance. Construct the circuit shown below, using the ceramic (yellow) capacitor that is marked with the text “105M” (I won’t tell you the capacitance value, but the measurement you’re about to do should tell you what it is!).

Please note that I have rotated the MCP4911 DAC chip by 180 degrees since last week’s experiment – i.e. pin 1 of the DAC. is now in row 9 of the breadboard. You don’t need to rotate yours – I just thought it would be neater this way.

Upload the following code to the Arduino to generate a negative-going step voltage and record the response of the RC circuit.

//
// ASGO - Arduino Signal Generator and Oscilloscope
// Negative-going step response version
// Written by Ted Burke - last updated 9-Feb-2021
//
// Outputs a step waveform via a MCP4911 DAC IC, while
// simultaneously sampling the analog voltages on pins A0 and
// A1 and storing them to buffers. The input signal is generated
// for 1 second, of which the last 200 ms is recorded at a
// sampling frequency of 2 kHz. The step change occurs 20 ms
// after recording begins. 
//
// To generate a step and output a frame of sampled data to the
// Serial Monitor or Serial Plotter, set the baudrate to 115200
// and then send the text command "c" or "s" (for comma- or
// space-delimited data respectively).
//
  
#include <SPI.h>
#define SS_PIN 10
  
// Modify the following 2 lines to control the step input
const float v1 = 4.0;          // voltage before step [V]
const float v2 = 0.0;          // voltage after step [V]
 
// Convert initial and final step voltages to ints for writing to DAC
const int v1_int = 0.2 * 1023.0 * v1;
const int v2_int = 0.2 * 1023.0 * v2;
 
// Sampling frequency - do not change this!
const float fs = 2000;         // sampling frequency (input and output)
  
// Waveform buffers
int n = 0;                     // buffer index variable
const int N = 400;             // buffer length in samples
unsigned int buffer0[N] = {0}; // buffer for input A0
unsigned int buffer1[N] = {0}; // buffer for input A1
  
// Variables for interaction between main loop and Timer1 ISR
volatile int countdown1000ms = 0;
volatile int countdown820ms = 0;
  
void setup()
{
  pinMode(SS_PIN, OUTPUT);    // Ensure that SS is set to SPI master mode
  digitalWrite(SS_PIN, HIGH); // Unselect the device
  
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
    
  Serial.begin(115200);     // open serial connection at 115200 bits/s
  
  // Initialize Timer1 
  noInterrupts();           // disable interrupts while configuring Timer 1
  TCCR1A = 0;
  TCCR1B = (1 << WGM12) | (1 << CS10); // CTC mode and clock prescaler set to 1
  TCNT1  = 0;               // reset Timer1 counter
  OCR1A = 8000;             // interrupt frequency is 16e6 / 8000 = 2000 Hz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // re-enable interrupts
}
 
void loop()
{
  char c = 0;
  
  // Check for incoming text command character
  if (Serial.available() > 0) c = Serial.read();
  
  // Print either a space-delimited or comma-delimited data frame
  if (c == 's' || c == 'c')
  {
    // Set the initial voltage
    dac_output(v1_int);
     
    countdown820ms = 1640;  // count down to step time
    countdown1000ms = 2000; // count down one second of samples
    while(countdown1000ms); // let ISR handle the recording
  
    // Now stop recording and print buffers to PC
    for (int m=0 ; m<N ; ++m)
    {
      Serial.print(m / 2000.0, 4);
      Serial.print(c == 's' ? ' ' : ','); // either a comma or a space
      Serial.print((5.0 / 1023.0) * buffer0[(n+m)%N], 3);
      Serial.print(c == 's' ? ' ' : ',');
      Serial.println((5.0 / 1023.0) * buffer1[(n+m)%N], 3);
    }
    Serial.flush();
  }
}
  
//
// ** Timer1 interrupt service routine (ISR) **
// This runs once every sample period, updating the
// DAC output voltage and sampling the input voltage
// on pins A0 and A1 and storing them to buffers.
//
ISR(TIMER1_COMPA_vect)
{
  if (countdown1000ms)
  {
    // update DAC voltage to generate sinusoid
    dac_output(countdown820ms ? v1_int : v2_int);
  
    // store voltages from A0 and A1 to sample buffers
    n = (n+1)%N; // increment sample buffer index    
    buffer0[n] = analogRead(0);
    buffer1[n] = analogRead(1);
     
    if (countdown820ms) --countdown820ms; // count down to step
    --countdown1000ms; // count down to end of recording
  }
}
  
// Write a 10-bit value to the DAC to update its output voltage
void dac_output(unsigned short data)
{
  // Construct 16-bit value (command and data bits)
  data &= 0x3ff;          // truncate data value to 10 bits
  boolean channel = 0;    // always zero for MCP4911
  boolean bufferVref = 0; // Vref input buffering disabled
  boolean gain2x = 0;     // 2x gain disabled
  boolean shdn = 0;       // output shutdown disabled
  uint16_t out = (channel << 15) | (bufferVref << 14) |
                  ((!gain2x) << 13) | (!shdn << 12) | (data << 2);
  
  // Transmit the 16-bit value
  digitalWrite(SS_PIN, LOW);         // select DAC chip
  SPI.transfer((out & 0xff00) >> 8); // high byte
  SPI.transfer(out & 0xff);          // low byte
  digitalWrite(SS_PIN, HIGH);        // unselect DAC chip
}

To check that the program and circuit are working correctly, open the Serial Plotter. Make sure the baudrate is set to “115200 baud”. Send the text command “c” from the send box and you should see signals like the ones below appearing in the Serial Plotter.

Close the Serial Plotter and open the Serial Monitor instead. Send the text command “c” again to capture a frame of data in CSV format. You should see data similar to the following appearing.

Copy and paste all of the data from the Serial Monitor into an Excel document, as shown below. You’ll need to scan down through the rows of data to identify the values of t1 and t2. The second Excel screenshot below shows exactly how to find t1 and t2. Once you have found both values, calculate \tau and C and show them both clearly on your spreadsheet. Create a neat graph with clear labels like that shown below.

How to find t1 and t2:

SUBMISSION ITEM 1: Submit (to Brightspace) a screenshot of your Excel spreadsheet showing your graph and calculated capacitance value. Also submit the Excel document itself.

Part 2: Response of RC circuit to square wave input voltage

Using the same circuit as in part 1, upload the following code to the Arduino. This version of the ASGO code outputs a square wave.

//
// ASGO - Arduino Signal Generator and Oscilloscope
// Square wave version
// Written by Ted Burke - last updated 9-Feb-2021
//
// Outputs a square wave via a MCP4911 DAC IC,
// while simultaneously sampling the analog voltages on
// pins A0 and A1 and storing them to buffers.
//
// To output a frame of sampled data to the Serial Monitor or
// Serial Plotter, set the baudrate to 115200 and then send the
// text command "c" or "s" (for comma- or space-delimited data
// respectively).
//
   
#include <SPI.h>
#define SS_PIN 10
   
// Modify the following 3 lines to control the output waveform
const float dc = 2.5;          // d.c. voltage offset [V]
const float aw = 1.0;          // waveform amplitude [V]
const float fw = 10;           // waveform frequency [Hz]
   
// Set sampling frequency - do not change this!
const float fs = 2000;         // sampling frequency (input and output)
const float inc = fw * 2.0 * M_PI / fs;  // angle increment per sample
   
// Waveform buffers
int n = 0;                     // buffer index variable
const int N = 400;             // buffer length in samples
unsigned int buffer0[N] = {0}; // buffer for input A0
unsigned int buffer1[N] = {0}; // buffer for input A1
   
// Variables for interaction between main loop and Timer1 ISR
volatile int recording = 1;
volatile int countdown = 0;
   
void setup()
{
  pinMode(SS_PIN, OUTPUT);    // Ensure that SS is set to SPI master mode
  digitalWrite(SS_PIN, HIGH); // Unselect the device
   
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
     
  Serial.begin(115200);     // open serial connection at 115200 bits/s
   
  // Initialize Timer1 
  noInterrupts();           // disable interrupts while configuring Timer 1
  TCCR1A = 0;
  TCCR1B = (1 << WGM12) | (1 << CS10); // CTC mode and clock prescaler set to 1
  TCNT1  = 0;               // reset Timer1 counter
  OCR1A = 8000;             // interrupt frequency is 16e6 / 8000 = 2000 Hz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // re-enable interrupts
}
   
void loop()
{
  char c = 0;
   
  // Check for incoming text command
  if (Serial.available() > 0) c = Serial.read();
   
  // Print either a space-delimited or comma-delimited data frame
  if (c == 's' || c == 'c')
  {
    // Count down one second of samples
    countdown = fs;
    while(countdown);
   
    // Now stop recording and print buffers to PC
    recording = 0;
    for (int m=0 ; m<N ; ++m)
    {
      Serial.print(m / 2000.0, 4);
      Serial.print(c == 's' ? ' ' : ',');
      Serial.print((5.0 / 1023.0) * buffer0[(n+m)%N], 3);
      Serial.print(c == 's' ? ' ' : ',');
      Serial.println((5.0 / 1023.0) * buffer1[(n+m)%N], 3);
    }
    Serial.flush();
    recording = 1;
  }
}
   
//
// ** Timer1 interrupt service routine (ISR) **
// This runs once every sample period, updating the
// DAC output voltage and sampling the input voltage
// on pins A0 and A1 and storing them to buffers.
//
ISR(TIMER1_COMPA_vect)
{
  static float angle = 0.0;
  float v;
  int v_int;
     
  if (recording)
  {
    // update DAC voltage to generate sinusoid
    angle = angle + inc; // increment phase angle
    v_int = cos(angle) > 0 ? 4 : 1;
    v_int = 0.2 * 1023.0 * v_int;
    dac_output(v_int);
   
    // store voltages from A0 and A1 to sample buffers
    n = (n+1)%N; // increment sample buffer index    
    buffer0[n] = analogRead(0);
    buffer1[n] = analogRead(1);
  }
   
  if (countdown) --countdown; // decrement counter if counting down
}
   
// Write a 10-bit value to the DAC to update its output voltage
void dac_output(unsigned short data)
{
  // Construct 16-bit value (command and data bits)
  data &= 0x3ff;          // truncate data value to 10 bits
  boolean channel = 0;    // always zero for MCP4911
  boolean bufferVref = 0; // Vref input buffering disabled
  boolean gain2x = 0;     // 2x gain disabled
  boolean shdn = 0;       // output shutdown disabled
  uint16_t out = (channel << 15) | (bufferVref << 14) |
                  ((!gain2x) << 13) | (!shdn << 12) | (data << 2);
   
  // Transmit the 16-bit value
  digitalWrite(SS_PIN, LOW);         // select DAC chip
  SPI.transfer((out & 0xff00) >> 8); // high byte
  SPI.transfer(out & 0xff);          // low byte
  digitalWrite(SS_PIN, HIGH);        // unselect DAC chip
}

Open the Serial Plotter and send the text command “c” to display the input and output voltage waveforms. You should see a plot similar to that shown below.

SUBMISSION ITEM 2: Upload a screenshot of the Serial Plotter window showing the square-wave input voltage and corresponding output voltage exhibiting exponential decay in response to each step.

Part 3: Capacitance meter – basic version

In this part of the experiment, you will reprogram the Arduino to automatically measure the time constant and calculate the capacitance value used in the RC circuit. We begin with a basic version of the system, using the following circuit:

To begin with, you can choose any capacitor from your kit. The 100 μF electrolytic capacitor might be a good one to start with.

Note that the breadboard circuit shown below includes the MCP4911 DAC chip. However, this chip is not used in this part of the experiment. I just left mine on the breadboard for later use.

//
// EEPP Arduino capacitance meter - basic version
// Written by Ted Burke - 9-Feb-2021
//
// This system (hardware circuit and software) measures the time
// constant of an RC circuit in order to calculate the value of
// the capacitor. The unknown capacitor is placed in series with
// a 1k resistor and the time constant, tau, is measured. The
// capacitance is calculated and printed via the Serial Monitor.
//

void setup()
{
  pinMode(3, OUTPUT); // pin D3 supplies the step to the RC circuit
  Serial.begin(115200); // open serial connection
}

void loop()
{
  float tau, R, C; // time constant, resistance, capacitance
  unsigned long t1, t2;
  float Vth;
  
  // Charge the capacitor up to its maximum through the resistor.
  digitalWrite(3, HIGH);
  delay(2000);

  // Calculate the threshold voltage as 36.8% of the initial voltage.
  // The capacitor voltage drops to this value after one time constant.
  Vth = 0.36788 * analogRead(7);
  
  // Measure the time constant
  t1 = micros();              // record the start time in microseconds
  digitalWrite(3, LOW);       // begin discharging the capacitor
  while(analogRead(7) > Vth); // wait until voltage drops to Vth
  t2 = micros();              // record the end time in microseconds
  digitalWrite(3, HIGH);      // begin charging the capacitor again
  tau = (t2 - t1) * 1e-6;     // calculate the time constant (t2 - t1)

  // Calculate the resistor and capacitor values
  R = 1000.0;
  C = tau / R;

  // Display the time constant, resistance and capacitance
  Serial.print("tau=");
  Serial.print(1000*tau, 3); // milliseconds with 3 decimal places
  Serial.print(" ms, R=");
  Serial.print(R, 0);
  Serial.print(" ohm, C=");
  Serial.print(C*1e9 , 3); // nanofarads with 3 decimal places
  Serial.println(" nF");
}

To view the measured values (time constant \tau , resistance R, capacitance C), simply open the Serial Monitor. Test the output of the system with different capacitors from your kit. Does it seem to be measuring correctly?

This basic capacitance meter works well some of the time, but has some significant flaws. For large capacitance values, such as the 1000 μF capacitor used in the screenshot below, the measured values seem reasonably accurate, but each measurement takes more than a second and displaying the value in nanofarads results in very large numbers that are difficult to read.

For smaller capacitance values, such as the 100 nF capacitor used in the screenshot below, this basic meter gives very inaccurate results. The time constant of the RC circuit is too short for the Arduino to measure accurately. One option would be to replace the 1 kΩ resistor with a much larger value and modify the program accordingly. However, this would result in extremely long measurement times for larger capacitors, which would be inconvenient. A better solution is presented in the next section.

SUBMISSION ITEM 3: Upload a photo of your breadboard circuit and the output of the system displayed in the Serial Monitor.

Part 4: Capacitance meter – full version

This circuit operates on the same principle as the basic version in the previous section, but five different resistor values are included, ranging from 100 Ω to 1 MΩ. When the capacitor to be measured is placed in the circuit, the system tries each resistor value in turn (starting with the smallest) until a sufficiently long time constant is obtained. The capacitance is then measured from the time constant. The result is a capacitance meter that works quite well over the range of capacitor values found in your kit.

Build the circuit shown below.

Upload the following program to your Arduino.

//
// EEPP Arduino capacitance meter - full version
// Written by Ted Burke  - 9-Feb-2021
//
// This system (hardware circuit and software) measures the time
// constant of an RC circuit in order to calculate the value of
// the capacitor. To obtain a time constant of appropriate length,
// the system has five resistor values (100, 1k, 10k, 100k, 1M),
// which are tried one at a time until a time constant longer
// than 50ms is observed, at which point the capacitance is
// calculated and printed via the Serial Monitor.
//

void setup()
{
  pinMode(2, INPUT); // disable 100 ohm resistor
  pinMode(3, INPUT); // disable 1k resistor
  pinMode(4, INPUT); // disable 10k resistor
  pinMode(5, INPUT); // disable 100k resistor
  pinMode(6, INPUT); // disable 1M resistor

  Serial.begin(115200); // open serial connection
}

void loop()
{
  int n; // resistor/pin number, e.g. R3 is 10^3 ohms and on pin D3 
  float tau, R, C; // time constant, resistance, capacitance
  
  // Try the resistors in this order: 100, 1k, 10k, 100k, 1M.
  // The resistance used is equal to 10^n, so the value of n is
  // incremented until a time constant over 50ms is observed.
  // That time constant is then used to calculate the capacitance.
  n = 2;
  while (1)
  {
    tau = measureTimeConstant(n);
    if (tau == 0) n = 1;
    if (tau > 50e-3) break;
    if (n == 6) break;
    n++;
  }

  // Calculate the resistor and capacitor values
  R = pow(10,n);
  C = tau / R;

  // Display the time constant, resistance and capacitance
  Serial.print("tau="); Serial.print(1000*tau, 3); Serial.print(" ms, ");
  Serial.print("R="); Serial.print(R, 0); Serial.print(" ohm, ");
  Serial.print("C=");
  if      (C < 1e-9) {Serial.print(C*1e12, 3); Serial.print(" pF");}
  else if (C < 1e-6) {Serial.print(C*1e9 , 3); Serial.print(" nF");}
  else if (C < 1e-3) {Serial.print(C*1e6 , 3); Serial.print(" uF");}
  else               {Serial.print(C*1e3 , 3); Serial.print(" mF");}
  Serial.println();
}

// This function measures the time constant in seconds using resistor n.
float measureTimeConstant(int n)
{
  unsigned long t1, t2, timeout;
  float Vth;
  
  // Charge the capacitor up to its maximum through the 100 ohm resistor.
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  delay(500);
  pinMode(2, INPUT);

  // Calculate the threshold voltage as 36.8% of the initial voltage.
  // The capacitor voltage drops to this value after one time constant.
  Vth = 0.36788 * analogRead(7);
  
  // Measure the time constant
  t1 = micros();              // record the start time in microseconds
  timeout = t1 + 500000L;     // set timeout limit
  pinMode(n, OUTPUT);         // enable the selected resistor
  digitalWrite(n, LOW);       // begin discharging the capacitor
  while(analogRead(7) > Vth)  // wait until voltage drops to Vth (or timeout)
    if (micros() > timeout) return 0;
  t2 = micros();              // record the end time in microseconds
  pinMode(n, INPUT);          // disable the selected resistor
  return (t2 - t1) * 1e-6;    // return the time constant (t2 - t1)
}

Open the Serial Monitor and observe how the reading change as you swap different capacitor values in and out of the circuit.

SUBMISSION ITEM 4: Upload (to Brightspace) a very clear photo of your final breadboard circuit and the output of the system in the Serial Monitor as you changed between several capacitor values.

Part 5: Planning for next week

In next week’s lab you will build your own capacitor from scratch using (probably) aluminium foil, cling film and other materials. It will basically be a parallel plate capacitor with a thin dielectric (an electrically insulating material) in the middle. Do some research online to find out how the capacitance of a parallel plate capacitor depends on its physical dimensions and the electrical properties of the materials used to construct it. You will be using the capacitance meter from part 4 of today’s experiment to measure the capacitance of your homemade capacitor.

2020-21 Sem 2 Lab 2: Step response and frequency response of an RC circuit

In today’s lab, we investigate the so-called RC circuit, which is simply a capacitor in series with a resistor. We will use the ASGO (Arduino signal generator and oscilloscope) to apply a series of voltage signals to the RC circuit to see how it behaves.

Background theory

At this point, you’re familiar with the concept of resistance and you’ve seen how Ohm’s law describes the relationship between the voltage across a resistor and the current flowing through it.

v = iR

where v is the instantaneous voltage (in volts) between the terminals of the resistor, i is the instantaneous current (in amperes) flowing through it, and R is the resistance (in ohms). This means that at every moment in time, the current flowing through a resistor is proportional to the voltage across it at that instant. This is still true when an AC voltage is applied to a resistor. When you apply a sinusoidal voltage, you get a sinusoidal current with a magnitude that depends on the resistance. The voltage and current waveforms are exactly in phase – i.e. when the voltage is at its peak, the current is also at its peak.

The relationship between voltage and current in a capacitor is more complicated.

i=C\frac{dv}{dt}

where v is the instantaneous voltage (in volts) between the terminals of the capacitor, i is the instantaneous current (in amperes) flowing through it, and C is the capacitance (in farads). What this means is that the current is proportional to the rate of change of voltage. So when an AC voltage is applied to a capacitor, the magnitude of the current depends on how fast the voltage is changing.

  • If the frequency of the AC voltage is high enough (meaning that the voltage is constantly changing very rapidly), then the current will be large because the capacitor doesn’t impede it very much.
  • Conversely, if the frequency of the AC voltage is very low (meaning that the voltage is only changing relatively slowly), then the capacitor impedes the current a lot.

This leads us to the concept of impedance which tells us how much an element (resistor, capacitor or inductor) impedes the flow of electric current. Conventionally, impedance values are represented using an upper case letter Z. In general, impedance is a complex number. However, the impedance of a resistor is purely real (i.e. it has zero imaginary part). The impedance of a resistor is simply equal to its resistance:

Z_R = R

where R is the resistance (in ohms) and Z_R is the impedance of the resistor (in ohms).

The impedance of a capacitor is purely imaginary (i.e. its real part is zero). It depends on two things: the capacitance and the frequency of the current.

Z_C =  \frac{1}{j\omega C} = \frac{-1}{\omega C}j

where Z_C is the imedance of the capacitor (in ohms), C is the capacitance (in farads), j is the imaginary unit (i.e. j=\sqrt{1}), and \omega is the angular frequency (in rad/s). Angular frequency represents the same property of a signal as frequency, but in different units. Whereas frequency is measured in hertz (cycles per second), angular frequency is measured in rad/s (radians per second). When working with impedances, it can be more convenient to use angular frequencies because the arithmetic becomes simpler. Frequency, f, in hertz and angular frequency, \omega, in rad/s are related as follows.

w=2\pi f

Last semester, we learned various approaches for analyzing circuits made up of resistors and DC voltage and current sources. In the coming weeks, we will learn how to apply the same techniques to circuits made up of resistors, inductors, capacitors, and sinusoidal voltage and current sources. The key to doing this will be representing the sinusoidal voltages and currents using complex number values known as phasors, and by converting all of the resistors, capacitors and inductors into impedances.

If you think of impedance as an extension of the concept of resistance, then a capacitor is “like a resistor” that changes its resistance depending on the frequency of the current flowing through it. High frequencies pass through easily. Low frequencies find it much harder to pass through. Because impedance is a complex number (having a magnitude and an angle) it also introduces a phase shift between voltage and current waveforms. In a capacitor, the current waveform leads the voltage waveform by exactly 90^{\circ} (equivalent to \frac{\pi}{2} radians) – i.e. one quarter cycle.

Part 0: Construct the RC circuit on the ASGO breadboard

This is the complete circuit you will use for today’s experiment:

As before, the ASGO consists of the Arduino Nano and the DAC chip (MCP4911). The only new part here is the RC circuit on the right-hand side of the circuit diagram. This simple circuit is the focus of today’s experiment. You will apply voltage signals of different frequencies to it and observe how the voltage across the capacitor responds.

To give myself plenty of space on the breadboard, I moved the DAC chip right up against the end of the Arduino. However, if yours is a bit further down the breadboard, there’s no need to move it – you should still have plenty of room for the RC circuit.

One very important task is to correctly identify the 1 μF capacitor in your kit. It’s a yellow ceramic one and should have “105M” written on in. Here’s a photo of the 1 μF capacitor in your kit:

In case you’re wondering, when capacitor values are written as a three digit code, the value is the first two digits, followed by whatever number of zeros are specified by the third digit, in units of pF (picofarads). Note that 1 pF = 1 ✕ 10-12 F. Hence, “105” denotes a “10” followed by “00000” (5 zeros) which is equal to 1000000 pF = 1 μF.

SUBMISSION ITEM 1: Upload a clear photograph of your completed breadboard circuit to Brightspace.

Part 1: Step response of the RC circuit

In this part of the experiment, you will apply an input voltage signal to the RC circuit that goes through a step change (increasing abruptly from 0 V to 4 V) and observe how the capacitor voltage changes in response. Although the input signal changes value abruptly, the capacitor voltage cannot change so suddenly. It takes time for current to flow to charge the capacitor up to its new resting voltage. Furthermore, as the capacitor voltage gets closer and closer to its final value, the rate of charging reduces. It approaches the final value asymptotically – theoretically getting closer and closer forever but never quite arriving. An RC circuit is an example of a first order system (because the relationship between voltage and current in a capacitor is a first order differential equation). This type of asymptotic waveform, which is often referred to as an exponential decay, is characteristic of first order systems.

In the screenshot below from the Arduino Serial Plotter, you can see the input voltage (red line) increase abruptly, but the capacitor voltage (green line) rises more slowly and approaches the input voltage asymptotically. This green curve is the step response of the RC circuit. Useful information about a system can be gleaned from its step response. For example, we could measure the time constant of this system from the step response. However, for today we’re just using the step response to verify that the RC circuit and ASGO are wired correctly on the breadboard.

  • Upload the code shown below to your Arduino.
  • Open the Serial Plotter and set the baud rate to “115200 baud”.
  • Type “c” into the text box and press the “Send” button.
  • Assuming you see a plot similar to that shown above, take a screenshot.
  • If you don’t see a plot similar to the one above then there may be a problem with your circuit.

SUBMISSION ITEM 2: Upload your screenshot of the Serial Plotter displaying the step response to Brightspace. It should resemble the one shown above.

//
// ASGO - Arduino Signal Generator and Oscilloscope
// Step response version - written by Ted Burke - 1 Feb 2021
//
// Outputs a step waveform via a MCP4911 DAC IC, while
// simultaneously sampling the analog voltages on pins A0 and
// A1 and storing them to buffers. The input signal is generated
// for 1 second, of which the last 200 ms is recorded at a
// sampling frequency of 2 kHz. The step change occurs 20 ms
// after recording begins. 
//
// To generate a step and output a frame of sampled data to the
// Serial Monitor or Serial Plotter, set the baudrate to 115200
// and then send the text command "c" or "s" (for comma- or
// space-delimited data respectively).
//
 
#include <SPI.h>
#define SS_PIN 10
 
// Modify the following 2 lines to control the step input
const float v1 = 0.0;          // voltage before step [V]
const float v2 = 4.0;          // voltage after step [V]

// Convert initial and final step voltages to ints for writing to DAC
const int v1_int = 0.2 * 1023.0 * v1;
const int v2_int = 0.2 * 1023.0 * v2;

// Sampling frequency - do not change this!
const float fs = 2000;         // sampling frequency (input and output)
 
// Waveform buffers
int n = 0;                     // buffer index variable
const int N = 400;             // buffer length in samples
unsigned int buffer0[N] = {0}; // buffer for input A0
unsigned int buffer1[N] = {0}; // buffer for input A1
 
// Variables for interaction between main loop and Timer1 ISR
volatile int countdown1000ms = 0;
volatile int countdown820ms = 0;
 
void setup()
{
  pinMode(SS_PIN, OUTPUT);    // Ensure that SS is set to SPI master mode
  digitalWrite(SS_PIN, HIGH); // Unselect the device
 
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
   
  Serial.begin(115200);     // open serial connection at 115200 bits/s
 
  // Initialize Timer1 
  noInterrupts();           // disable interrupts while configuring Timer 1
  TCCR1A = 0;
  TCCR1B = (1 << WGM12) | (1 << CS10); // CTC mode and clock prescaler set to 1
  TCNT1  = 0;               // reset Timer1 counter
  OCR1A = 8000;             // interrupt frequency is 16e6 / 8000 = 2000 Hz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // re-enable interrupts
}

void loop()
{
  char c = 0;
 
  // Check for incoming text command character
  if (Serial.available() > 0) c = Serial.read();
 
  // Print either a space-delimited or comma-delimited data frame
  if (c == 's' || c == 'c')
  {
    // Set the initial voltage
    dac_output(v1_int);
    
    countdown820ms = 1640;  // count down to step time
    countdown1000ms = 2000; // count down one second of samples
    while(countdown1000ms); // let ISR handle the recording
 
    // Now stop recording and print buffers to PC
    for (int m=0 ; m<N ; ++m)
    {
      Serial.print(m / 2000.0, 4);
      Serial.print(c == 's' ? ' ' : ','); // either a comma or a space
      Serial.print((5.0 / 1023.0) * buffer0[(n+m)%N], 3);
      Serial.print(c == 's' ? ' ' : ',');
      Serial.println((5.0 / 1023.0) * buffer1[(n+m)%N], 3);
    }
    Serial.flush();
  }
}
 
//
// ** Timer1 interrupt service routine (ISR) **
// This runs once every sample period, updating the
// DAC output voltage and sampling the input voltage
// on pins A0 and A1 and storing them to buffers.
//
ISR(TIMER1_COMPA_vect)
{
  if (countdown1000ms)
  {
    // update DAC voltage to generate sinusoid
    dac_output(countdown820ms ? v1_int : v2_int);
 
    // store voltages from A0 and A1 to sample buffers
    n = (n+1)%N; // increment sample buffer index    
    buffer0[n] = analogRead(0);
    buffer1[n] = analogRead(1);
    
    if (countdown820ms) --countdown820ms; // count down to step
    --countdown1000ms; // count down to end of recording
  }
}
 
// Write a 10-bit value to the DAC to update its output voltage
void dac_output(unsigned short data)
{
  // Construct 16-bit value (command and data bits)
  data &= 0x3ff;          // truncate data value to 10 bits
  boolean channel = 0;    // always zero for MCP4911
  boolean bufferVref = 0; // Vref input buffering disabled
  boolean gain2x = 0;     // 2x gain disabled
  boolean shdn = 0;       // output shutdown disabled
  uint16_t out = (channel << 15) | (bufferVref << 14) |
                  ((!gain2x) << 13) | (!shdn << 12) | (data << 2);
 
  // Transmit the 16-bit value
  digitalWrite(SS_PIN, LOW);         // select DAC chip
  SPI.transfer((out & 0xff00) >> 8); // high byte
  SPI.transfer(out & 0xff);          // low byte
  digitalWrite(SS_PIN, HIGH);        // unselect DAC chip
}

Part 2: Frequency response of the RC circuit

In this part of the experiment, you will apply voltage signals of different frequencies to the RC circuit and observe how the magnitude and phase of the capacitor voltage vary (relative to the input voltage).

The code shown below generates a sinusoidal input voltage at a frequency of 10 Hz. To check that the code is functioning correctly, upload it to the Arduino then open the Serial Plotter. Sending the command “c” to the Arduino should produce the following plot:

To plot the magnitude and phase response of the RC circuit, you will take measurements at a number of different frequencies. To accurately measure the peak-to-peak amplitude of the input and output waveforms at each frequency and the phase shift between them, you will use the Serial Monitor to capture the data and Microsoft Excel to analyse it.

You will measure at ten different frequencies: 10 Hz, 20 Hz, 30 Hz, 40 Hz, 60 Hz, 80 Hz, 100 Hz, 140 Hz, 180 Hz, 220 Hz.

The measurement and analysis process is explained in the following video:

The process for each frequency is as follows:

  • Modify line 21 of the code shown below to set the frequency to the desired value.
  • Upload the code to the Arduino.
  • Open the Serial Monitor and set the baud rate to “115200 baud”.
  • If any text is currently displayed in the Serial Monitor, then press the “Clear output” button.
  • In the Serial Monitor, type “c” into the text box and press “Send”.
  • Click in the text area and press Ctrl+A followed by Ctrl+c to copy all of the data to the clipboard.
  • Paste the data into the “waveforms” sheet of your Excel document to find the amplitude of the capacitor voltage.
  • Copy and paste the peak amplitude of the capacitor voltage into the “frequency response” sheet of the Excel document.
  • Examine the input and output columns of data on the “waveforms” sheet to estimate how many samples the output lags behind the input. Type this value into the “frequency response” sheet, as shown in the video.

Once you have filled in the values for all frequencies in the “waveform” sheet, you’re ready to plot the frequency response of the RC circuit. You should plot the magnitude response in one chart and the phase response in a second chart.

SUBMISSION ITEM 3: Take a screenshot of the two final charts (magnitude response and phase response) and upload it to Brightspace.

SUBMISSION ITEM 4: Upload your Excel document to Brightspace.

//
// ASGO - Arduino Signal Generator and Oscilloscope
// Version 0.1 - written by Ted Burke - 25 Jan 2021
//
// Outputs a sinusoidal waveform via a MCP4911 DAC IC,
// while simultaneously sampling the analog voltages on
// pins A0 and A1 and storing them to buffers.
//
// To output a frame of sampled data to the Serial Monitor or
// Serial Plotter, set the baudrate to 115200 and then send the
// text command "c" or "s" (for comma- or space-delimited data
// respectively).
//
  
#include <SPI.h>
#define SS_PIN 10
  
// Modify the following 3 lines to control the output waveform
const float dc = 2.5;          // d.c. voltage offset [V]
const float aw = 1.0;          // waveform amplitude [V]
const float fw = 10;           // waveform frequency [Hz]
  
// Set sampling frequency - do not change this!
const float fs = 2000;         // sampling frequency (input and output)
const float inc = fw * 2.0 * M_PI / fs;  // angle increment per sample
  
// Waveform buffers
int n = 0;                     // buffer index variable
const int N = 400;             // buffer length in samples
unsigned int buffer0[N] = {0}; // buffer for input A0
unsigned int buffer1[N] = {0}; // buffer for input A1
  
// Variables for interaction between main loop and Timer1 ISR
volatile int recording = 1;
volatile int countdown = 0;
  
void setup()
{
  pinMode(SS_PIN, OUTPUT);    // Ensure that SS is set to SPI master mode
  digitalWrite(SS_PIN, HIGH); // Unselect the device
  
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
    
  Serial.begin(115200);     // open serial connection at 115200 bits/s
  
  // Initialize Timer1 
  noInterrupts();           // disable interrupts while configuring Timer 1
  TCCR1A = 0;
  TCCR1B = (1 << WGM12) | (1 << CS10); // CTC mode and clock prescaler set to 1
  TCNT1  = 0;               // reset Timer1 counter
  OCR1A = 8000;             // interrupt frequency is 16e6 / 8000 = 2000 Hz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // re-enable interrupts
}
  
void loop()
{
  char c = 0;
  
  // Check for incoming text command
  if (Serial.available() > 0) c = Serial.read();
  
  // Print either a space-delimited or comma-delimited data frame
  if (c == 's' || c == 'c')
  {
    // Count down one second of samples
    countdown = fs;
    while(countdown);
  
    // Now stop recording and print buffers to PC
    recording = 0;
    for (int m=0 ; m<N ; ++m)
    {
      Serial.print(m / 2000.0, 4);
      Serial.print(c == 's' ? ' ' : ',');
      Serial.print((5.0 / 1023.0) * buffer0[(n+m)%N], 3);
      Serial.print(c == 's' ? ' ' : ',');
      Serial.println((5.0 / 1023.0) * buffer1[(n+m)%N], 3);
    }
    Serial.flush();
    recording = 1;
  }
}
  
//
// ** Timer1 interrupt service routine (ISR) **
// This runs once every sample period, updating the
// DAC output voltage and sampling the input voltage
// on pins A0 and A1 and storing them to buffers.
//
ISR(TIMER1_COMPA_vect)
{
  static float angle = 0.0;
  float v;
  int v_int;
    
  if (recording)
  {
    // update DAC voltage to generate sinusoid
    angle = angle + inc; // increment phase angle
    v_int = (0.2 * 1023.0) * (dc + aw * cos(angle));
    dac_output(v_int);
  
    // store voltages from A0 and A1 to sample buffers
    n = (n+1)%N; // increment sample buffer index    
    buffer0[n] = analogRead(0);
    buffer1[n] = analogRead(1);
  }
  
  if (countdown) --countdown; // decrement counter if counting down
}
  
// Write a 10-bit value to the DAC to update its output voltage
void dac_output(unsigned short data)
{
  // Construct 16-bit value (command and data bits)
  data &= 0x3ff;          // truncate data value to 10 bits
  boolean channel = 0;    // always zero for MCP4911
  boolean bufferVref = 0; // Vref input buffering disabled
  boolean gain2x = 0;     // 2x gain disabled
  boolean shdn = 0;       // output shutdown disabled
  uint16_t out = (channel << 15) | (bufferVref << 14) |
                  ((!gain2x) << 13) | (!shdn << 12) | (data << 2);
  
  // Transmit the 16-bit value
  digitalWrite(SS_PIN, LOW);         // select DAC chip
  SPI.transfer((out & 0xff00) >> 8); // high byte
  SPI.transfer(out & 0xff);          // low byte
  digitalWrite(SS_PIN, HIGH);        // unselect DAC chip
}