<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Chris Greenley</title>
    <subtitle>Blog and projects</subtitle>
    <link rel="self" type="application/atom+xml" href="https://chrisgreenley.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://chrisgreenley.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2020-08-02T00:00:00+00:00</updated>
    <id>https://chrisgreenley.com/atom.xml</id>
    <entry xml:lang="en">
        <title>Mojo Toolchain</title>
        <published>2020-08-02T00:00:00+00:00</published>
        <updated>2020-08-02T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/blog/mojo-toolchain/"/>
        <id>https://chrisgreenley.com/blog/mojo-toolchain/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/blog/mojo-toolchain/">&lt;p&gt;This is the setup I used for going through the book “Programming FPGAs: Getting Started with Verilog.”&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Download Xilinx ISE (&lt;a href=&quot;https:&#x2F;&#x2F;www.xilinx.com&#x2F;support&#x2F;download&#x2F;index.html&#x2F;content&#x2F;xilinx&#x2F;en&#x2F;downloadNav&#x2F;vivado-design-tools&#x2F;archive-ise.html&quot;&gt;link&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;ISE-download-page.png&quot; alt=&quot;ISE download page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Unzip downloaded file and locate .ova virtual machine file.
&lt;ul&gt;
&lt;li&gt;Xilinx_ISE_14.7_Win10_14.7_VM_0213_1\ova\14.7_VM.ova&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Download and install VirtualBox&lt;&#x2F;li&gt;
&lt;li&gt;Load the 14.7_VM.ova into VirtualBox&lt;&#x2F;li&gt;
&lt;li&gt;Connect the Mojo to the computer via USB and notice which COM port shows up (COM4 in my case)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;com-port-device-manager.png&quot; alt=&quot;COM port in device manager&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Modify VirtualBox settings as shown below to gain network access&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;VM-network-settings.png&quot; alt=&quot;VM network settings&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Pipe the COM port through to the VM. The dropdown menu (set to COM1 in the image below) is the internal port which will map to the external host port entered into “Path&#x2F;Address” field. (&lt;a href=&quot;https:&#x2F;&#x2F;techtooltip.wordpress.com&#x2F;2008&#x2F;09&#x2F;12&#x2F;using-host-serial-port-from-guest-in-virtual-box&#x2F;&quot;&gt;More details&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;VM-serial-port-settings.png&quot; alt=&quot;VM serial port settings&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Start VM&lt;&#x2F;li&gt;
&lt;li&gt;Start ISE&lt;&#x2F;li&gt;
&lt;li&gt;Help -&amp;gt; Manage License…&lt;&#x2F;li&gt;
&lt;li&gt;Acquire License (see screenshots below)
&lt;ul&gt;
&lt;li&gt;Free Vivado&#x2F;ISE Webpack License&lt;&#x2F;li&gt;
&lt;li&gt;Download license file “Xilinx.lic”&lt;&#x2F;li&gt;
&lt;li&gt;From the “Manage Licenses” tab click “Load License…”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;ISE-manage-license-acquire.png&quot; alt=&quot;ISE manage license acquire&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;ISE-manage-license-connect-now.png&quot; alt=&quot;ISE manage license connect now&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Download Mojo loader program (&lt;a href=&quot;https:&#x2F;&#x2F;alchitry.com&#x2F;pages&#x2F;mojo-loader&quot;&gt;link&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;mojo-loader-download-page.png&quot; alt=&quot;Mojo loader download page&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Create example program in the ISE&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;ISE-counter-program.png&quot; alt=&quot;ISE counter program&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Generate .bit file&lt;&#x2F;li&gt;
&lt;li&gt;Start Mojo loader (see screenshot below)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;mojo-loader-start.png&quot; alt=&quot;Mojo loader start&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Load the .bit file (note you will need to change the viewable file type to “*”)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;blog&#x2F;mojo-toolchain&#x2F;opening-counter-bit-file.png&quot; alt=&quot;Opening counter bit file&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Flash the FPGA by clicking “Load”!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Characterizing a DC motor</title>
        <published>2018-11-20T00:00:00+00:00</published>
        <updated>2018-11-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/dc-motor-pwm/"/>
        <id>https://chrisgreenley.com/projects/dc-motor-pwm/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/dc-motor-pwm/">&lt;p&gt;In the process of going through the Robotics course on &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;www.robogrok.com&quot;&gt;robogrok.com&lt;&#x2F;a&gt; I realized I wasn’t sure what PWM frequency I should choose for controlling a DC motor. After some googling left my state of uncertainty unchanged, clearly the only road forward was to record much data and make many graphs.&lt;&#x2F;p&gt;
&lt;p&gt;These are the motor and motor driver I’m using.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.robotshop.com&#x2F;en&#x2F;6v-301-530rpm-micro-metal-gearmotor-encoder.html&quot;&gt;6V 30:1 530RPM Micro Metal Gearmotor w&#x2F; Encoder&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.pololu.com&#x2F;product&#x2F;2130&quot;&gt;DRV8833 Dual Motor Driver&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The setup shown below is for measuring torque. I’m using a PSoC 5LP dev board to control the motor driver. The power for the motor is provided by a linear bench PSU.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;full-setup.jpg&quot; alt=&quot;Full setup&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The motor is attached to a pulley which pulls up on a 3 lb lead weight. The scale is zeroed with the string slack so that the force exerted by the string pulling up is shown as a negative value on the scale.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;pulley-closeup.jpg&quot; alt=&quot;Pulley closeup&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The first tests I performed were measurements of the no-load speed. The setup simply used the motor with the pulley attached but nothing connected to the pulley. This motor has a quadrature encoder and I used the 5LP to count the pulse edges coming from the encoder over a span of a 10 seconds 5 times in a row, averaged the values, and divided by 10 to get the counts per second. The number of counts per revolution I calculated by manually rotating the motor 20 times and dividing the resulting number by 20. (The LCD came in handy for displaying these values.) The counts per revolution came out to 824. (Note: &lt;a href=&quot;https:&#x2F;&#x2F;www.precisionmicrodrives.com&#x2F;content&#x2F;encoder-resolution-ppr-and-cpr&#x2F;&quot;&gt;CPR vs PPR&lt;&#x2F;a&gt;) The RPM value is then (average counts in 1 second)&#x2F;(counts per revolution)*(60 seconds per minute). Of course this is the speed at which the pulley is rotating, the motor speed is 30 times faster due to the gear ratio.&lt;&#x2F;p&gt;
&lt;p&gt;I ran tests at PWM duty cycles which correspond to 2, 3, 4 and 5 volts equivalent. Respectively 1&#x2F;3 (33%), 1&#x2F;2 (50%), 2&#x2F;3 (66%) and 5&#x2F;6 (83%) of the 6 volts provided by the power supply. (Frequency and duty cycle were confirmed using an oscilloscope.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;speed-vs-pwm-freq.png&quot; alt=&quot;Speed vs PWM frequency&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see that the speed stays mostly constant until around 500 Hz. By 1 kHz, and definitely 2 kHz, the speed has dropped off significantly, especially at the lower duty cycles. (Note the logarithmic x-axis.)&lt;&#x2F;p&gt;
&lt;p&gt;The plot below shows vertical slices through the prior plot. The speed isn’t quite linearly related to the duty cycle.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;speed-vs-duty-cycle.png&quot; alt=&quot;Speed vs duty cycle&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;From the above results it seems like choosing a PWM frequency under 1 kHz is going to be a good choice.&lt;&#x2F;p&gt;
&lt;p&gt;The plot below shows the “lift mass” (reading on the scale) vs PWM frequency for various duty cycles. Since 100% duty cycle is just a constant 6 volts, this torque should be independent of PWM frequency and it does in fact stay within a fairly stable range, providing a sanity check.&lt;&#x2F;p&gt;
&lt;p&gt;Torque in Nm can be calculated using the equation below (where 0.011 is the length of the moment arm in meters and the lift mass is in kg).&lt;&#x2F;p&gt;
&lt;p&gt;\( \tau = (\mathrm{lift~mass}) \cdot 9.8 \cdot 0.011 \)&lt;&#x2F;p&gt;
&lt;p&gt;The measurement process:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Rotate pulley fully clockwise (pause a few seconds to give the motor time to cool between tests)&lt;&#x2F;li&gt;
&lt;li&gt;Rotate pulley fully counter-clockwise at some combo of freq and duty cycle&lt;&#x2F;li&gt;
&lt;li&gt;Wait 10 seconds and record value shown on scale&lt;&#x2F;li&gt;
&lt;li&gt;Repeat 5 times for each duty cycle&lt;&#x2F;li&gt;
&lt;li&gt;Average measurements and calculate standard deviation&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;torque-vs-pwm-freq.png&quot; alt=&quot;Torque vs PWM frequency&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly this plot shows a peak in torque for three of the datasets at 800 Hz and at 400 Hz for the lowest duty cycle. This combined with the previous plots indicates that 800 Hz would be a good choice of PWM frequency.&lt;&#x2F;p&gt;
&lt;p&gt;The next plot compares the torque for a PWM signal driving the motor at 800 Hz to using a constant voltage. A PWM duty cycle of 33.3% with a 6 V square wave should be equivalent to 6*(2&#x2F;3) = 2 volts. The other equivalent voltages are calculated in the same way.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;pwm-vs-constant-voltage.png&quot; alt=&quot;PWM vs constant voltage&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly the PWM signal consistently beats out the constant equivalent voltage, though what should really be compared is the current. (The 6 volt data points should be identical since the PWM signal is at 100% duty cycle, I suspect the difference between them may be due to temperature differences in the motor windings).&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of the windings, let’s examine them a bit closer. Measuring the inductance with my LC meter gave a value of about 2.15 mH (at a measurement frequency of about 71 kHz).&lt;&#x2F;p&gt;
&lt;p&gt;Measuring the winding resistance with both my LC meter and with a multimeter yielded resistances of about 10 Ohms. For a more accurate measurement I measured the current through the stalled motor for a range of voltages, shown in the plot below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;stall-current.png&quot; alt=&quot;Stall current&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Resistance is the inverse of the slope which yields a value of about 12.4 Ohms (for a fairly cold coil).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dc-motor-pwm&#x2F;datasheet-comparison.png&quot; alt=&quot;Datasheet comparison&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dumping an external EEPROM</title>
        <published>2018-10-17T00:00:00+00:00</published>
        <updated>2018-10-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/eeprom-dumping/"/>
        <id>https://chrisgreenley.com/projects/eeprom-dumping/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/eeprom-dumping/">&lt;h4 id=&quot;part-i-i2c-twi-eeprom&quot;&gt;Part I: I2C&#x2F;TWI EEPROM&lt;&#x2F;h4&gt;
&lt;p&gt;I’m going to use the Arduino Wire library to  communicate with the EEPROM over I2C. On the UNO, SDA is A4 and SCL is A5. You can see pinouts for other boards on the &lt;a href=&quot;https:&#x2F;&#x2F;www.arduino.cc&#x2F;en&#x2F;Reference&#x2F;Wire&quot;&gt;Wire Library reference page&lt;&#x2F;a&gt;. This &lt;a href=&quot;https:&#x2F;&#x2F;playground.arduino.cc&#x2F;Main&#x2F;WireLibraryDetailedReference&quot;&gt;page&lt;&#x2F;a&gt; shows more details about how the Wire library works.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately this chip has legs that are spaced out enough for my cheap logic probes. I setup my scope to eavesdrop on the SDA and SCL lines so that I could verify the code was operating as desired. This also allowed me to determine that the clock frequency being used in the original circuit was about 30 kHz.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;setup.jpg&quot; alt=&quot;I2C setup&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;eeprom-datasheet.pdf&quot;&gt;Datasheet&lt;&#x2F;a&gt; for the EEPROM in the above picture.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;Dump_I2C_EEPROM.ino&quot;&gt;Code&lt;&#x2F;a&gt; used on the UNO (also shown below)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#include &amp;lt;Wire.h&amp;gt;
#include &amp;lt;stdint.h&amp;gt;

#define AT24C02_ADDR 0x50

void setup() {
  uint8_t dataAddr;
  Serial.begin(9600);
  Wire.begin();
  Wire.setClock(31000L); &amp;#x2F;&amp;#x2F;31 kHz
  Wire.beginTransmission(AT24C02_ADDR);
  Wire.write(0x00); &amp;#x2F;&amp;#x2F;Sets the start address for use in the upcoming reads
  Wire.endTransmission();
  for(uint8_t i=0;i&amp;lt;8;++i){ &amp;#x2F;&amp;#x2F;cycle through enough times to capture entire EEPROM
    Wire.requestFrom(AT24C02_ADDR,32,1); &amp;#x2F;&amp;#x2F;read 32 bytes at a time
    while (Wire.available()){
      uint8_t c = Wire.read();
      Serial.write(c); &amp;#x2F;&amp;#x2F;Send raw data over serial to computer
    }
  }

}

void loop() {
  
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I recommend viewing the output in &lt;a href=&quot;https:&#x2F;&#x2F;realterm.sourceforge.io&#x2F;&quot;&gt;RealTerm&lt;&#x2F;a&gt;. Shown below are the settings I used to connect, your port will probably be different.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;realterm-port.png&quot; alt=&quot;RealTerm port settings&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once connected, I reset the UNO and told realterm to display the data coming over the serial connection as hex values.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;realterm-display.png&quot; alt=&quot;RealTerm display&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once I could tell it was working, I had realterm capture the output to a file.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;realterm-capture.png&quot; alt=&quot;RealTerm capture&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;ReSource_EEPROM.bin&quot;&gt;Binary file&lt;&#x2F;a&gt; of EEPROM contents. This EEPROM is from a Genesis Resource dive computer (&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;&quot;&gt;link to teardown post&lt;&#x2F;a&gt;). Now that you have the file you can use a variety of utilities to examine the data.&lt;&#x2F;p&gt;
&lt;p&gt;For example, hexdump:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;I2C&#x2F;hexdump-outputs.png&quot; alt=&quot;Hexdump outputs&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;See &lt;a href=&quot;https:&#x2F;&#x2F;www.suse.com&#x2F;c&#x2F;making-sense-hexdump&#x2F;&quot;&gt;this page&lt;&#x2F;a&gt; for helpful info on using hexdump.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;part-ii-spi-eeprom&quot;&gt;Part II: SPI EEPROM&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;AT25256_SPI_EEPROM_datasheet.pdf&quot;&gt;Datasheet for the AT25256 EEPROM&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;Dump_SPI_EEPROM.ino&quot;&gt;Code&lt;&#x2F;a&gt; used on the UNO (also shown below)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#include &amp;lt;stdint.h&amp;gt;
#include &amp;lt;SPI.h&amp;gt;

const uint8_t chipSelectPin = 10;
const uint8_t RDSR = 0x05; &amp;#x2F;&amp;#x2F;Read Status Register
const uint8_t READ = 0x03; &amp;#x2F;&amp;#x2F;Read command, to be followed by 16 bit address
uint16_t readAddr = 0x0000;&amp;#x2F;&amp;#x2F; Address to start reading at
uint8_t outputByte = 0;

void setup() {
  Serial.begin(115200);
  SPI.begin(); 
  pinMode(chipSelectPin, OUTPUT);
  
  &amp;#x2F;&amp;#x2F;Sets up the parameters for SPI communication
  &amp;#x2F;&amp;#x2F;800 kHz clock, big-endian (most significant bit first),
  &amp;#x2F;&amp;#x2F;mode 3 (clock default is high, data read on rising edge)
  SPI.beginTransaction(SPISettings(800000, MSBFIRST, SPI_MODE3));
  delay(10); &amp;#x2F;&amp;#x2F;probably not really needed

  digitalWrite(chipSelectPin,LOW); &amp;#x2F;&amp;#x2F;Activate EEPROM
  SPI.transfer(READ);
  SPI.transfer16(readAddr);
  for(uint16_t i=0;i&amp;lt;32000;++i){&amp;#x2F;&amp;#x2F;This EEPROM has 256kbits &amp;#x2F; 8 = 32kBytes
    outputByte = SPI.transfer(0x00);
    Serial.write(outputByte); &amp;#x2F;&amp;#x2F;Sent raw binary data over serial
  }
  
  digitalWrite(chipSelectPin, HIGH);
  SPI.endTransaction();

  
}

void loop() {
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since my logic probes aren’t small enough to attach to the legs of the surface mount components on this PCB, I rigged up a different solution using coolant hoses, alligator clips and pogo pins.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;setup-1.jpg&quot; alt=&quot;SPI setup 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It actually worked quite well.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;setup-2.jpg&quot; alt=&quot;SPI setup 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Before connecting the UNO I sniffed the boot up communications between the onboard MSP430 and the EEPROM. The first communication on each power on is shown below. Looking at the data sheet (page 6) we can see that is the WRDI (Reset Write Enable Latch) command, also known as the Write Disable command. The datasheet says it boots with write disabled so I guess this is just to be extra cautious about preventing accidental writes?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;scope-WRDI.png&quot; alt=&quot;Scope WRDI&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The READ command format is 0000.X011 (where the X means that could be either 0 or 1 and has no effect). In HEX this is 0x03 (assuming a 0 for the don’t care bit). To read back the memory contents starting at a specific address, the master must send this command followed by the 16bit address. Once those three bytes have been sent to the slave, the slave will respond with the byte at that memory address the next time the master sends a byte. The slave will then automatically increment its internal address counter so that to keep receiving data all the master has to do is keep sending dummy bytes.&lt;&#x2F;p&gt;
&lt;p&gt;Below you can see a READ starting at address 0x0010. This is the memory location accessed by the onboard MSP430 shortly after boot-up. Sniffing this before manually accessing the EEPROM meant I could compare against it later to be sure my dump was correct.&lt;&#x2F;p&gt;
&lt;p&gt;At the bottom of the screenshot you can see that the scope is automatically decoding the bits. The MOSI (Master Out Slave In) corresponds to the purple (IN) trace. The first byte is the READ command followed by the address 0x0010. Then jumping up tot he MISO (Master In Slave Out) line you can see the data being returned.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;scope-readmemory.png&quot; alt=&quot;Scope read memory&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Before hooking up the UNO, I used a fifth pogo pin to connect to the reset pin on the MSP430. Holding this pin low keeps the microcontroller in reset which means the pins connected to the EEPROM will be tri-stated (high impedance). This is important so I don’t have the MSP430 trying to hold a pin low while the UNO tries to hold it high (or visa versa) causing a short circuit. I checked that the pins were actually tri-stated by hooking up a 10k pull-up to each pin being pulled low. Since the 10k pulled the voltage immediately up to Vcc, the pin had to be high impedance.&lt;&#x2F;p&gt;
&lt;p&gt;The Chip Select line I connected to digital pin 10 on the UNO. The clock line is connected to pin 13, MISO to pin 12 and MOSI to pin 11. Since this EEPROM runs at 3.3 volts and the UNO uses 5 volts, I connected everything through a logic level shifter and used the 3.3 volt rail from the UNO to power the EEPROM.&lt;&#x2F;p&gt;
&lt;p&gt;The actual dumping process is nearly identical to that shown above (use RealTerm except set the baud rate to 115200).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;Versa_EEPROM.bin&quot;&gt;Binary file&lt;&#x2F;a&gt; of EEPROM contents. This EEPROM is from an Oceanic Versa Pro dive computer (&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;&quot;&gt;link to the teardown post&lt;&#x2F;a&gt;). Shown below is the first section, formatted using hexdump.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;eeprom-dumping&#x2F;SPI&#x2F;hexdump.png&quot; alt=&quot;SPI hexdump&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dive Computer Teardown - Versa Pro</title>
        <published>2018-10-12T00:00:00+00:00</published>
        <updated>2018-10-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/dive-comp-teardown-versa-pro/"/>
        <id>https://chrisgreenley.com/projects/dive-comp-teardown-versa-pro/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/dive-comp-teardown-versa-pro/">&lt;p&gt;A few days ago I obtained a malfunctioning dive computer along with some other SCUBA gear and decided to do another teardown. This one proved a bit more interesting since none of the chips were blobbed over. (See my &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;&quot;&gt;previous teardown&lt;&#x2F;a&gt; of a Genesis ReSource dive computer.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;versa_pro_manual.pdf&quot;&gt;Versa Pro Manual&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;teardown&quot;&gt;Teardown&lt;&#x2F;h4&gt;
&lt;p&gt;First step, remove the battery. It turns out the hard plastic cover with the copper layers both provides the surface for the o-ring to fit around, and is a piezo buzzer&#x2F;speaker (more about that later).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;teardown-1.jpg&quot; alt=&quot;Teardown 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The outer metal covering pops off easily with a small flathead screwdriver to pry the clips apart. The grey plastic ring underneath just slides off.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;teardown-2.jpg&quot; alt=&quot;Teardown 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With that, the plastic covering the LCD screen comes right out, along with the o-ring.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;teardown-3.jpg&quot; alt=&quot;Teardown 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And now the screen&#x2F;circuit board module lifts out. You can see all the little springs which connect the circuit up to the external contacts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;teardown-4.jpg&quot; alt=&quot;Teardown 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The screen unclips from the circuit board with three little tabs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;teardown-5.jpg&quot; alt=&quot;Teardown 5&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The LCD itself is in the lower left. The polywELD object in the upper left is an electro-luminescent (EL) light which forms the backlight. In the upper right corner you can see two pieces of &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Elastomeric_connector&quot;&gt;zebra connector&lt;&#x2F;a&gt; which connect the LCD screen (the long piece) and the EL light (the short piece) to the circuit board.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;teardown-6.jpg&quot; alt=&quot;Teardown 6&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-circuit&quot;&gt;The Circuit&lt;&#x2F;h4&gt;
&lt;p&gt;Below you can see labels for all the main components of the circuit. Most of these will be discussed in more detail further down this post.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.ti.com&#x2F;lit&#x2F;ds&#x2F;slas272h&#x2F;slas272h.pdf&quot;&gt;MSP430 Microcontroller Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;AT25256_SPI_EEPROM_datasheet.pdf&quot;&gt;EEPROM Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;LC75824_datasheet.pdf&quot;&gt;LCD Driver Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;topside-labeled.png&quot; alt=&quot;Topside labeled&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A closeup to show the text on the EEPROM chip.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;eeprom.jpg&quot; alt=&quot;EEPROM&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The reverse side of the PCB. Note the pads which connect to the springs I pointed out before.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;bottomside-labeled.png&quot; alt=&quot;Bottomside labeled&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;pressure-sensor&quot;&gt;Pressure Sensor&lt;&#x2F;h5&gt;
&lt;p&gt;The pressure sensor was giving bad readings according to the error the computer showed. Since the computer is from circa 2003, the manufacturer doesn’t service this model anymore, so no point in being non-destructive.&lt;&#x2F;p&gt;
&lt;p&gt;This is tiny module underneath the black protective goop inside the pressure sensor. It measures 1.5mm x 1.5mm x 0.9mm.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;sensor-0.jpg&quot; alt=&quot;Sensor 0&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And this is the module under a microscope. You can see the locations the bond wires connected before I broke them by pulling it out of the housing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;sensor-1.jpg&quot; alt=&quot;Sensor 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;sensor-2.jpg&quot; alt=&quot;Sensor 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is definitely some 3-dimensionality to this module. From doing some reading I believe it is a Wheatstone bridge composed of piezo-resistive elements. There is probably a sealed chamber inside for use as a reference pressure. The output signal is a differential analog voltage which goes off to an  opamp circuit to prepare a signal which can be measured by the ADC in the microcontroller.&lt;&#x2F;p&gt;
&lt;p&gt;Update: I emailed SMI and asked if they could identify the sensor, and they responded a few hours later that it was the SM5106. In the snippets from the datasheet shown below, you can see that it is indeed a Wheatstone bridge composed of four piezoresistive elements.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;SM5106_datasheet.pdf&quot;&gt;SM5106 Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;SM5106_die.pdf&quot;&gt;SM5106 Datasheet 2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;sensor-3.png&quot; alt=&quot;Sensor 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Below is a nearly identical die showing the 4 squiggly bits which form the actual sensing elements.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;sensor-4.png&quot; alt=&quot;Sensor 4&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The circuit below conditions the signal coming from the sensor, preparing it to be read by the ADC. It is composed of four opamps and various passives.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;MAX4294_quad_opamp_datasheet.pdf&quot;&gt;Opamp Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;opamp-circuit.jpg&quot; alt=&quot;Opamp circuit&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As shown in the schematic below, opamp C creates a constant current source pushing 330 uA through the Wheatstone bridge. Opamp D is just a buffer. Opamps A and B form something similar to an instrumentation amplifier, but not quite the standard configuration. The net result is that if the voltage at the non-inverting inputs of opamps A and B is identical, then the output of B will be 30 mV. If the voltage at the non-inverting input of A is higher than at the non-inverting input of B, then the voltage on the output of B will go up. In the reverse situation the voltage will go down, though of course it bottoms out pretty quickly at zero. I suspect the sensor is setup so this situation should never happen.&lt;&#x2F;p&gt;
&lt;p&gt;The output voltage which would be read by the microcontroller can be found using the equation show below. \(V_{A-} \) and \(V_{B-}\) are the voltages at the inverting inputs of opamps A and B. Since they are setup with negative feedback, these will equal the voltages at their non-inverting inputs AKA the voltages from the sense terminals on the Wheatstone bridge.&lt;&#x2F;p&gt;
&lt;p&gt;\( V_{out} = (3\frac{96e3}{26e3}+1)(\Delta V) + 0.03 \) where \( \Delta V = V_{A-} - V_{B-} \)&lt;&#x2F;p&gt;
&lt;p&gt;or approximately&lt;&#x2F;p&gt;
&lt;p&gt;\( V_{out} = (12)(\Delta V) + 0.03 \)&lt;&#x2F;p&gt;
&lt;p&gt;You can see a simulation of the circuit &lt;a href=&quot;http:&#x2F;&#x2F;everycircuit.com&#x2F;circuit&#x2F;4762821295603712&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;opamp-schematic.jpg&quot; alt=&quot;Opamp schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;backlight&quot;&gt;Backlight&lt;&#x2F;h5&gt;
&lt;p&gt;The backlight consists of an electro-luminescent (EL) panel shaped to exactly match the LCD. EL panels are a capacitive load (this one measures about 6 nF) which need to be driven by a high voltage AC signal, typically 150 Vpp or more. The IC shown in the circuit diagram below is specifically designed to drive EL lights. It uses the inductor to generate very large voltage spikes at a frequency controlled by a clock signal from the microcontroller.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;SP4412_EL_driver_datasheet.pdf&quot;&gt;EL Lamp Driver Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;backlight-schematic.jpg&quot; alt=&quot;Backlight schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Below you can see the panel glowing. I have it connected to a stand-alone EL driver I have for use with EL wire.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;backlight.jpg&quot; alt=&quot;Backlight&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;speaker-buzzer&quot;&gt;Speaker&#x2F;Buzzer&lt;&#x2F;h5&gt;
&lt;p&gt;I found this circuit especially interesting to understand, since I didn’t realize at first that the back disk behaved as a buzzer. For a while I was thinking that it might be a way to detect the intrusion of water to the battery compartment.&lt;&#x2F;p&gt;
&lt;p&gt;The buzzer consists of two copper plates separated by a small distance. The smaller plate is connected directly to the positive terminal of the battery. Why the positive terminal and not the negative “ground” terminal? I’m pretty sure just because it was more physically convenient, and whether that plate is held at “zero” or at three volts is irrelevant compared to the large voltages on the other plate.&lt;&#x2F;p&gt;
&lt;p&gt;The IC marked ACCM in the circuit below is a simple capacitive charge pump style voltage doubler (See my &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;&quot;&gt;post&lt;&#x2F;a&gt; about building an inverting charge pump for more info on how these work). When transistor Q2 is turned on (by pulling the GPIO to ground) Node 2 gets connected to Vcc. This turns on the LED and provides the input voltage for the voltage doubler. Pin 2 is then at 6 volts (Vcc * 2).&lt;&#x2F;p&gt;
&lt;p&gt;The fun comes when Q1 is alternately turned on and off by a PWM signal from the microcontroller. When Node 1 is pulled to ground through the transistor, the inductor behaves like a simple wire (in the steady state). But, when the transistor is turned off, current through the inductor attempts to stop instantly and the collapsing magnetic field creates an induced current in the same direction but now this current has no where to go. The result is a large voltage spike at Node 1 which means a large potential difference across the capacitive load (about 12.3 nF) formed by the buzzer. This built up charge then goes back through the inductor resulting in an oscillation which is stopped when the transistor turns back on, pulling Node 1 back to ground.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;pdfs&#x2F;MAX1683_voltage_doubler_datasheet.pdf&quot;&gt;Voltage Doubler Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;speaker-schematic.jpg&quot; alt=&quot;Speaker schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The lower (yellow) trace is the approx 40% +duty cycle PWM signal from the microcontroller. The upper (blue) trace is the voltage at Node 1. The approx 40 volt peak-to-peak oscillation results in the buzzer vibrating at 2.5 kHz (plus harmonics).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;speaker-scope.jpg&quot; alt=&quot;Speaker scope&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;lcd-voltage-generation&quot;&gt;LCD Voltage Generation&lt;&#x2F;h5&gt;
&lt;p&gt;The LCD requires a few different voltages to operate. I couldn’t find a datasheet for the IC marked “MCAB P358” but the purpose is to generate the one third and two thirds Vcc voltages for the LCD driver IC. (See my &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;&quot;&gt;post&lt;&#x2F;a&gt; reverse engineering an LCD screen for more info on why these voltages are needed.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;P358-schematic.jpg&quot; alt=&quot;P358 schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;light-sensor&quot;&gt;Light Sensor&lt;&#x2F;h5&gt;
&lt;p&gt;The component beside the LED is (as far as I can tell) a photo-transistor which is used to sense the ambient light levels. The computer manual says it uses this information to only turn on the backlight above water if the computer is in a low light situation. This way battery isn’t wasted lighting up the screen when it isn’t needed. In low light the sense GPIO will remain high, pulled to Vcc by the resistor. In bright light the transistor will turn on, pulling the sense GPIO pin to ground. I suspect that other than for the moment when the microcontroller is checking the light level, both GPIO pins are tri-stated to save power.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown-versa-pro&#x2F;phototransistor-schematic.jpg&quot; alt=&quot;Phototransistor schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading! Shoot me an email if you caught anything I’m wrong about!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dive Computer Teardown - Genesis ReSource</title>
        <published>2018-09-29T00:00:00+00:00</published>
        <updated>2018-09-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/dive-comp-teardown/"/>
        <id>https://chrisgreenley.com/projects/dive-comp-teardown/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/dive-comp-teardown/">&lt;p&gt;I recently took out my old Genesis ReSource dive computer to see if it still worked. While a fresh set of batteries did induce it to turn on, it insisted on shutting down right after a brief LCD test cycle. I looked online to see if anyone had info on the hardware in the computer and I discovered a rather appalling lack of dive computer teardowns. Obviously the only course of action was to take mine apart and see the insides for myself.&lt;&#x2F;p&gt;
&lt;p&gt;The outer ring slid off easily enough.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;outer-ring.jpg&quot; alt=&quot;Outer ring&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Removing the battery cover turned out not to be necessary but I didn’t know that at the time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;back.jpg&quot; alt=&quot;Back&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;battery-compartment.jpg&quot; alt=&quot;Battery compartment&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This was the hard part. The clear shell has internal threads that a ring which fits over the sandwich of the screen, circuitboard and back section screws into. I had to pry the edge up all the way around to break the glue, and then I managed to stab a flathead screwdriver rather deeply into my finger while trying to exert enough torque to unscrew things. What ended up working was banging on the back of the screwdriver with a hammer as I held it angled down into one of the 4 small indents and at a tangent to the circle of rotation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;outer-shell-a.jpg&quot; alt=&quot;Outer shell a&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;outer-shell-b.jpg&quot; alt=&quot;Outer shell b&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here you can see the zebra strip along the bottom of the screen and the corresponding contact points on the circuit board. I was sad to see that two of the ICs were blobbed over.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-and-screen.jpg&quot; alt=&quot;Circuit and screen&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The two large contact areas on the back of the circuit board get pressed against the battery contacts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-and-back.jpg&quot; alt=&quot;Circuit and back&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you look closely, you can see a bodge resistor on the far right edge.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-front-full.jpg&quot; alt=&quot;Circuit front full&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-front-full-labeled.png&quot; alt=&quot;Circuit front full labeled&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-front-close-a.jpg&quot; alt=&quot;Circuit front close a&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-front-close-b.jpg&quot; alt=&quot;Circuit front close b&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-front-close-c.jpg&quot; alt=&quot;Circuit front close c&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The metal can thing at the top is the pressure sensor which is exposed to the water via the three small holes visible in the pictures of the back of the computer. The large component in the upper right is a 470 uH inductor. Also visible are a few capacitors and what I’m guessing is a crystal oscillator.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-back-full.jpg&quot; alt=&quot;Circuit back full&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The inductor is connected to pin 4 of the D355 Electroluminescent Lamp driver IC. (&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;D355_datasheet.pdf&quot;&gt;Datasheet&lt;&#x2F;a&gt; for a very similar IC.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;dive-comp-teardown&#x2F;circuit-back-close.jpg&quot; alt=&quot;Circuit back close&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I believe this computer was originally purchased at some point in the 90’s and I don’t know how similar modern dive computers are, but they still need to do all the same things so probably not that different.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>DIY Suunto Vyper Data Cable</title>
        <published>2018-09-23T00:00:00+00:00</published>
        <updated>2018-09-23T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/vyper-cable/"/>
        <id>https://chrisgreenley.com/projects/vyper-cable/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/vyper-cable/">&lt;p&gt;To buy the cable from Suunto for connecting my Vyper dive computer to my desktop computer is absurdly expensive. &lt;a href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Suunto-Download-Cobra-Mosquito-computers&#x2F;dp&#x2F;B000JL2C06&quot;&gt;On Amazon it goes for $85.&lt;&#x2F;a&gt; Fortunately I was able to find info online about building an adapter from components I already had.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;homepage.hispeed.ch&#x2F;scubadiver&#x2F;download&#x2F;usb.pdf&quot;&gt;PDF&lt;&#x2F;a&gt; from which I mainly adapted this circuit&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;homepage.hispeed.ch&#x2F;scubadiver&#x2F;english.html&quot;&gt;Image gallery&lt;&#x2F;a&gt; with ideas of how to made the physical connection (click “Picture Gallery” on the sidebar)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The main downside of the info already available was that much of it was designed for hooking up to a serial port (which computers don’t really have any more), or was simply more complicated than strictly needed. Below is what I believe to be the simplest and probably cheapest way to build a vyper interface cable.&lt;&#x2F;p&gt;
&lt;p&gt;The Suunto software doesn’t seem to work with this, but from reading around I’m not sure if my adapter is to blame or it’s just that the software is crap. I’ve been using the fantastic &lt;a href=&quot;https:&#x2F;&#x2F;subsurface-divelog.org&#x2F;&quot;&gt;Subsurface&lt;&#x2F;a&gt; software on Windows 10. This should work fine on Linux as well.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Obviously you should know what you are doing and I take no responsibility if you kill any of your computers, or really for anything else in your life.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;parts-list&quot;&gt;Parts List&lt;&#x2F;h5&gt;
&lt;ul&gt;
&lt;li&gt;3.3 V capable Serial to USB adapter (&lt;a href=&quot;https:&#x2F;&#x2F;www.amazon.com&#x2F;Micro-Basic-Breakout-Module-Arduino&#x2F;dp&#x2F;B00N4MCS1A&quot;&gt;Link&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Old floppy disk drive power connector&lt;&#x2F;li&gt;
&lt;li&gt;NPN transistor (such as 2N3904)&lt;&#x2F;li&gt;
&lt;li&gt;10 kOhm resistors (x3)&lt;&#x2F;li&gt;
&lt;li&gt;100 kOhm resistor (x1)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The finished product&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;vyper-cable&#x2F;cable-and-computer.jpg&quot; alt=&quot;Cable and computer&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So fancy&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;vyper-cable&#x2F;cable.jpg&quot; alt=&quot;Cable&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Oh yeah that fits nice&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;vyper-cable&#x2F;connection.jpg&quot; alt=&quot;Connection&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Grind down the connector a bit in two places so the wire can be more flush.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;vyper-cable&#x2F;connector.jpg&quot; alt=&quot;Connector&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Apparently the computer uses negative voltage for logic high (at least according to this one place on the internet, I’m not actually sure they are correct, but this works so I’m just going with it for now). With the computer face down and the two pin data port facing you, common is on the right and the I&#x2F;O pin is on the left.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;vyper-cable&#x2F;circuit.jpg&quot; alt=&quot;Circuit&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is an initial burst of data being sent to the vyper and then the rest is data being sent from the vyper.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;vyper-cable&#x2F;scope-output.jpg&quot; alt=&quot;Scope output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Tube Based Night Light</title>
        <published>2018-03-27T00:00:00+00:00</published>
        <updated>2018-03-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/tube-night-light/"/>
        <id>https://chrisgreenley.com/projects/tube-night-light/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/tube-night-light/">&lt;p&gt;A photoresistor, a couple standard resistors, an LED and a single transistor make a great transistor-as-a-switch demo. The photoresistor’s resistance increases in the dark and it can be used to build a voltage divider which has an increased output voltage inversely proportional to the light level. The output of the voltage divider is connected to the base of a transistor which turns it on or off.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;npn-night-light.svg&quot; alt=&quot;Transistor Night Light&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Since I had a box of vacuum tubes hanging around, I was curious to see if I could create the same circuit using a vacuum tube instead of a transistor. The first step was to learn how tubes work in a practical sense. I knew the basic theory but I’d never actually used a tube.&lt;&#x2F;p&gt;
&lt;p&gt;I found the following videos quite helpful:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=04sCi50B5CY&quot;&gt;US Army Training Film (1943)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=ZZpcRsKtfig&quot;&gt;Comparing Transistors and Tubes - AllAmericanFiveRadio&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=oHjZs0bNwEs&quot;&gt;How a Tube Works - Mr. Carlson’s Lab&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;After a few iterations I designed a circuit which works quite nicely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;tube-night-light.svg&quot; alt=&quot;Tube night light schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;setting-the-quiescent-values&quot;&gt;Setting the Quiescent Values&lt;&#x2F;h5&gt;
&lt;p&gt;When the photoresistors (LDRs) are exposed to light, the LEDs should be off. For the tube to be “off” the grid needs to be lower than the cathode. Since I didn’t want to deal with generate a negative voltage rail, I set the cathode voltage to be higher than ground using the voltage divider composed of R2+LDR2 and the pot. The grid voltage is set via R1 and LDR1.&lt;&#x2F;p&gt;
&lt;p&gt;The pot should be adjusted until the LEDs are just barely off when the photoresistors are exposed to the desired light level. Around 800 Ohms worked well for the 12AU7, but it depends on the tube you are using. In this state I measured the grid to be about 2.65 V below the cathode.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;operation&quot;&gt;Operation&lt;&#x2F;h5&gt;
&lt;p&gt;When LDR1 is covered its resistance increases and the this forces the grid voltage higher. when just LDR1 covered I measured the grid to be 0.95 volts below the cathode.&lt;&#x2F;p&gt;
&lt;p&gt;When LDR2 is covered the cathode voltage is forced down due to the change in resistance. This makes up for the extra current going across the pot which is trying to force the cathode voltage up. With just LDR2 covered I measured the grid to be 0.77 volts below the cathode.&lt;&#x2F;p&gt;
&lt;p&gt;With both LDR1 and LDR2 covered I measured the grid to be 0.57 volts ABOVE the cathode. This results in significantly more current through the tube and brighter LEDs.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;types-of-tubes&quot;&gt;Types of Tubes&lt;&#x2F;h5&gt;
&lt;p&gt;I tried two dual triode tubes, an Amperex 12AU7&#x2F;ECC82 and an RCA 12AX7.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;frank.pocnet.net&#x2F;sheets&#x2F;184&#x2F;1&#x2F;12AU7.pdf&quot;&gt;12AU7 Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.mif.pg.gda.pl&#x2F;homepages&#x2F;frank&#x2F;sheets&#x2F;137&#x2F;1&#x2F;12AX7.pdf&quot;&gt;12AX7 Datasheet&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;From the graphs we can get the anode current for a gate voltage of zero volts and an anode voltage of 50 volts.&lt;&#x2F;p&gt;
&lt;p&gt;12AU7: 5.4 mA
12AX7: 1.2 mA&lt;&#x2F;p&gt;
&lt;p&gt;Either current will easily light up an LED, but the larger current should be noticeably brighter. Both tubes have identical pinouts and since each is a dual triode, I can connect the two tridodes in parallel to double the current. This is accomplished by simply connecting anode 1 to anode 2, cathode 1 to cathode 2, and grid 1 to grid 2.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;tubes_x2.jpg&quot; alt=&quot;12AX7 and 12AU7 Vacuum Tubes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;tube-connections&quot;&gt;Tube Connections&lt;&#x2F;h5&gt;
&lt;p&gt;The picture below shows a BOTTOM VIEW pinout.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;tube-pinout.png&quot; alt=&quot;Tube Pinout&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;P - Plate&#x2F;Anode
G - Grid
K - Cathode&lt;&#x2F;p&gt;
&lt;p&gt;Pins 1-4 are associated with one triode and pins 5-8 are the other. Pins 4 and 5 are the two ends of the heater filament and pin 9 is the common center of the filament. Each of the two “sides” of the filament take 150 mA to heat up to the proper temperature. This can be achieved by either not using pin 9 and running 12.6 volts across the entire filament or, as I did in my circuit, connecting pins 4 and 5 together and running 6.3 volts across the combination.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;powering-the-circuit&quot;&gt;Powering the Circuit&lt;&#x2F;h5&gt;
&lt;p&gt;I wanted this to be a nice compact demo unit and so I used a 9 volt battery connected to a boost converter to generate 30 volts. After some testing I found it actually works pretty similarly from 25 up to 40 volts.&lt;&#x2F;p&gt;
&lt;p&gt;For the 6.3 volt rail I simply used the LM317 (variable linear regulator) along with two resistors to set the output close to 6.3 volts. The input comes from the 9 volt battery. (9-6.3)*0.3A = 0.81 watts dissipated in the LM317 which it can easily handle.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;making-it-pretty&quot;&gt;Making it Pretty&lt;&#x2F;h5&gt;
&lt;p&gt;I soldered all the components to proto board then designed and 3D printed a base for everything to attach to. I also added a switch to disconnect the battery from the rest of the circuit since even with the LEDs off, the heater filament is still using nearly 2 W on a constant basis which will drain a 9 V battery in just a few hours.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;tube-demo-1.jpg&quot; alt=&quot;Tube Demo 1&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;tube-demo-2.jpg&quot; alt=&quot;Tube Demo 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;tube-night-light&#x2F;tube-demo-3.jpg&quot; alt=&quot;Tube Demo 3&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Of course this is an incredibly inefficient circuit, but it makes a fun demo to show how a tube can be used as an electronic switch and to show why transistors are so much better in so many ways.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>C Structs</title>
        <published>2018-02-18T00:00:00+00:00</published>
        <updated>2018-02-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/blog/c-structs/"/>
        <id>https://chrisgreenley.com/blog/c-structs/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/blog/c-structs/">&lt;p&gt;A collection of examples for quick reference. For more info I recommend the following resources:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.tutorialspoint.com&#x2F;cprogramming&#x2F;c_structures.htm&quot;&gt;TutorialsPoint - C Structures&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Section 6.7.2.1 “Structure and union specifiers” in the &lt;a href=&quot;http:&#x2F;&#x2F;www.open-std.org&#x2F;JTC1&#x2F;SC22&#x2F;WG14&#x2F;www&#x2F;docs&#x2F;n1570.pdf&quot;&gt;current C11 standard&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.drdobbs.com&#x2F;the-new-c-compound-literals&#x2F;184401404&quot;&gt;The New C: Compound Literals&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;nickdesaulniers.github.io&#x2F;blog&#x2F;2013&#x2F;07&#x2F;25&#x2F;designated-initialization-with-pointers-in-c&#x2F;&quot;&gt;Designated Initialization With Compound Literals in C&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The following code can be run and forked at &lt;a href=&quot;https:&#x2F;&#x2F;onlinegdb.com&#x2F;SyhklQzAf&quot;&gt;onlinegdb.com&#x2F;SyhklQzAf&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#include &amp;lt;stdio.h&amp;gt;

typedef struct _vector {
    int x;
    int y;
}  vector_type;

void printPoint(vector_type point);

int main()
{
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F; Declaring a struct without typedef &amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F; Method 1 &amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;* The form of the stuct is defined and given the identifier _vectorA *&amp;#x2F;
    &amp;#x2F;* The vectorA variable is then declared to be a struct of form _vectorA *&amp;#x2F;
    &amp;#x2F;* (The underscore has no special meaning here.) *&amp;#x2F;
    struct _vectorA {
        int x;
        int y;
    };
    struct _vectorA vectorA;
    
    &amp;#x2F;* Members x and y are accessed using the &amp;#x27;.&amp;#x27; operator *&amp;#x2F;
    vectorA.x = 10;
    vectorA.y = 20;
    printf(&amp;quot;vectorA: (%d,%d)\n&amp;quot;,vectorA.x,vectorA.y);
    
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F; Method 2 &amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;* The form of the struct is included in the declaration statement *&amp;#x2F;
    &amp;#x2F;* for vectorB. The form identifier can be reused later to declare *&amp;#x2F;
    &amp;#x2F;* additional structs with the same form, such as vectorB2 *&amp;#x2F;
    
    struct _vectorB {
        int x;
        int y;
    } vectorB;
    
    struct _vectorB vectorB2;
    
    vectorB.x = 30;
    vectorB.y = 33;
    vectorB2.x = 36;
    vectorB2.y = 37;
    printf(&amp;quot;vectorB: (%d,%d)\n&amp;quot;,vectorB.x,vectorB.y);
    printf(&amp;quot;vectorB2: (%d,%d)\n&amp;quot;,vectorB2.x,vectorB2.y);
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F; Method 3 - variation &amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;* If the struct form is combined with the declaration, the form *&amp;#x2F;
    &amp;#x2F;* identifier is optional. Of course without it, no additional stucts *&amp;#x2F;
    &amp;#x2F;* of the same form can be declared later on. *&amp;#x2F;
    
    struct {
        int x;
        int y;
    } vectorC;
    
    vectorC.x = 40;
    vectorC.y = 44;
    printf(&amp;quot;vectorC: (%d,%d)\n&amp;quot;,vectorC.x,vectorC.y);
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F;Method 4&amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;* In addition to declaring a variable as a struct, a variable may be *&amp;#x2F;
    &amp;#x2F;* declared as a pointer to a struct. Multiple struct variables and *&amp;#x2F;
    &amp;#x2F;* pointers to structs may be declared at the same time *&amp;#x2F;
    &amp;#x2F;* (Once again, the struct identifier _vectorD is optional) *&amp;#x2F;
    struct _vectorD {
        int x;
        int y;
    } vectorD, *vectorD_ptr=NULL;
    vectorD_ptr = &amp;amp;vectorD;
    
    vectorD.x = 50;
    vectorD.y = 55;
    printf(&amp;quot;vectorD: (%d,%d)\n&amp;quot;,vectorD.x,vectorD.y);
    
    &amp;#x2F;* When accessing a struct member via a pointer, one must use &amp;#x27;-&amp;gt;&amp;#x27; *&amp;#x2F;
    vectorD_ptr-&amp;gt;x = 56;
    vectorD_ptr-&amp;gt;y = 59;
    printf(&amp;quot;vectorD: (%d,%d) &amp;quot;,vectorD_ptr-&amp;gt;x,vectorD_ptr-&amp;gt;y);
    printf(&amp;quot;&amp;#x2F;&amp;#x2F;Set and accessed via pointer\n&amp;quot;);
    
    
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F; Declaring a struct using typedef &amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;* A new variable type may be defined using typedef, which can then be *&amp;#x2F;
    &amp;#x2F;* used to declare variables and pointers to variables, etc... just like *&amp;#x2F;
    &amp;#x2F;* any other data type.  *&amp;#x2F;
    typedef struct _vectorE { &amp;#x2F;&amp;#x2F;Or just: typedef struct {
        int x;
        int y;
    } vectorE_type;
    
    &amp;#x2F;* Decare vectorE and vectorE_ptr which will hold the address of vectorE *&amp;#x2F;
    vectorE_type vectorE, *vectorE_ptr=NULL;
    vectorE_ptr = &amp;amp;vectorE;
    
    vectorE.x = 60;
    vectorE.y = 66;
    printf(&amp;quot;vectorE: (%d,%d)\n&amp;quot;,vectorE.x,vectorE.y);
    
    vectorE_ptr-&amp;gt;x = 63;
    vectorE_ptr-&amp;gt;y = 65;
    printf(&amp;quot;vectorE: (%d,%d) &amp;quot;,vectorE_ptr-&amp;gt;x,vectorE_ptr-&amp;gt;y);
    printf(&amp;quot;&amp;#x2F;&amp;#x2F;Set and accessed via pointer\n&amp;quot;);
    
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F; Initalizing a struct using designated initializers &amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    typedef struct {
        int x;
        int y;
    } vectorF_type;
    
    &amp;#x2F;* Initializing a struct without specifying the value&amp;#x2F;field assocation has *&amp;#x2F;
    &amp;#x2F;* various downsides. One must refer back to the definition to know the 
    &amp;#x2F;* field order and the order might change, resulting in broken code. *&amp;#x2F;
    vectorF_type vectorF1 = {70,77}; &amp;#x2F;&amp;#x2F;Without designating the fields
    printf(&amp;quot;vectorF1: (%d,%d)\n&amp;quot;,vectorF1.x,vectorF1.y);
    
    &amp;#x2F;* Using designated initializers fixes these issues *&amp;#x2F;
    vectorF_type vectorF2 = {.x=80, .y=88};
    printf(&amp;quot;vectorF2: (%d,%d)\n&amp;quot;,vectorF2.x,vectorF2.y);
    
    
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F; Initalizing a struct using a compound literal      &amp;#x2F;&amp;#x2F;
    &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
    
    &amp;#x2F;* If you want to declare and initalize a struct seperatly, or if you *&amp;#x2F;
    &amp;#x2F;* want to assign new values using the initialization syntax then you *&amp;#x2F;
    &amp;#x2F;* must use a compound literal, which behaves pretty much like a cast. *&amp;#x2F;
    &amp;#x2F;* The &amp;quot;cast&amp;quot; tells the compiler how to handle the values in the braces. *&amp;#x2F; 
    vectorF_type vectorF3;
    vectorF3 = (vectorF_type){90,99}; &amp;#x2F;&amp;#x2F;Would fail without &amp;quot;cast&amp;quot; 
    printf(&amp;quot;vectorF3: (%d,%d)\n&amp;quot;,vectorF3.x,vectorF3.y);
    
    &amp;#x2F;&amp;#x2F;Can also combine with designated initializers
    vectorF3 = (vectorF_type){.y=92,.x=91}; &amp;#x2F;&amp;#x2F;Can designate the values in any order
    printf(&amp;quot;vectorF3: (%d,%d)\n&amp;quot;,vectorF3.x,vectorF3.y);
    
    &amp;#x2F;&amp;#x2F;Can be used to pass anonymous structures to functions
    &amp;#x2F;&amp;#x2F;printPoint({.x=100,.y=101}); &amp;lt;-- Will fail to compile
    printPoint((vector_type){.x=100,.y=101});
    

    return 0;
}

void printPoint(vector_type point) {
    printf(&amp;quot;vector: (%d,%d)\n&amp;quot;,point.x,point.y);
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Console output:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;vectorA: (10,20)
vectorB: (30,33)
vectorB2: (36,37)
vectorC: (40,44)
vectorD: (50,55)
vectorD: (56,59) &amp;#x2F;&amp;#x2F;Set and accessed via pointer
vectorE: (60,66)
vectorE: (63,65) &amp;#x2F;&amp;#x2F;Set and accessed via pointer
vectorF1: (70,77)
vectorF2: (80,88)
vectorF3: (90,99)
vectorF3: (91,92)
vector: (100,101)
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Enabling PWM on the TM4C123 microcontroller</title>
        <published>2018-01-22T00:00:00+00:00</published>
        <updated>2018-01-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/blog/tm4c-pwm/"/>
        <id>https://chrisgreenley.com/blog/tm4c-pwm/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/blog/tm4c-pwm/">&lt;p&gt;This example shows how to enable PWM output on pin PB6 for the Cortex M4 core TM4C123GH6PM microcontroller. The steps outlined in the &lt;a href=&quot;http:&#x2F;&#x2F;www.ti.com&#x2F;lit&#x2F;ds&#x2F;symlink&#x2F;tm4c123gh6pm.pdf&quot;&gt;datasheet&lt;&#x2F;a&gt; in section 20.4 are as follows:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Enable PWM clock (RCGC0)&lt;&#x2F;li&gt;
&lt;li&gt;Enable GPIO port clock (RCGC2)&lt;&#x2F;li&gt;
&lt;li&gt;Enable alternate function on pins to be used for PWM (GPIOAFSEL)&lt;&#x2F;li&gt;
&lt;li&gt;Assign PWM signals to the appropriate pins (GPIOPCTL)&lt;&#x2F;li&gt;
&lt;li&gt;Select PWM clock source and potentially set clock divider (RCC)&lt;&#x2F;li&gt;
&lt;li&gt;Configure counting mode and generator A and B behavior (PWMxCTL, PWMxGENA, PWMxGENB)&lt;&#x2F;li&gt;
&lt;li&gt;Set the reload counter value (PWMxLOAD)&lt;&#x2F;li&gt;
&lt;li&gt;Set comparator A trigger value (PWMxCMPA)&lt;&#x2F;li&gt;
&lt;li&gt;Set comparator B trigger value (PWMxCMPB)&lt;&#x2F;li&gt;
&lt;li&gt;Start the PWM timers (PWMxCTL)&lt;&#x2F;li&gt;
&lt;li&gt;Enable PWM outputs (PWMENABLE)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The problem with these instructions is that they are incomplete. Looking at table 10-3 (in the GPIO configuration and initialization section) on the row “Digital Output (PWM)” shows that the appropriate bit needs set in both the alternative function select register (AFSEL) AND in the digital enable (DEN) register. The PWM initialization instructions mention the former but not the later, and at no point mention table 10-3.&lt;&#x2F;p&gt;
&lt;p&gt;The code below includes a line for setting bit 6 in the PORTB DEN register to enable pin PB6.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;void PWM_Init(void){
 int volatile delay;
 
 SYSCTL_RCC_R  &amp;amp;= ~(SYSCTL_RCC_USEPWMDIV); &amp;#x2F;&amp;#x2F;Use undivided system clock as PWM clock
 
 SYSCTL_RCGC0_R     |= (1&amp;lt;&amp;lt;20);     &amp;#x2F;&amp;#x2F;activate PWM module 0 clock
 SYSCTL_RCGC2_R     |= (1&amp;lt;&amp;lt;1);      &amp;#x2F;&amp;#x2F;activate port B clock
 delay = SYSCTL_RCGC2_R;            &amp;#x2F;&amp;#x2F;Allow time for clock to stabalize
 GPIO_PORTB_DR4R_R  |= (1&amp;lt;&amp;lt;6);
 GPIO_PORTB_DEN_R   |= (1&amp;lt;&amp;lt;6);      &amp;#x2F;&amp;#x2F;Enable digital output on PB6
 GPIO_PORTB_AFSEL_R |= (1&amp;lt;&amp;lt;6);      &amp;#x2F;&amp;#x2F;Activate  Alternate function on PB6
 GPIO_PORTB_PCTL_R  |= (0x04000000);&amp;#x2F;&amp;#x2F;Set alternate peripheral to PWM
 
 PWM0_0_CTL_R   &amp;amp;= ~(1&amp;lt;&amp;lt;0);         &amp;#x2F;&amp;#x2F;Disable pwm block until ready to use
 PWM0_0_CTL_R   &amp;amp;= ~(1&amp;lt;&amp;lt;1);         &amp;#x2F;&amp;#x2F;Set count-down mode
 PWM0_0_GENA_R  |=  (0xC2);         &amp;#x2F;&amp;#x2F;Drive pwm low on CmpA and high on zero
 PWM0_0_LOAD_R   =  (1000-1)        &amp;#x2F;&amp;#x2F;80e6&amp;#x2F;80e3=1000 (80khz pwm)
 PWM0_0_CMPA_R   =  (500-1);        &amp;#x2F;&amp;#x2F;Set couter value at which CmpA triggers
 PWM0_0_CTL_R   |=  (0x01);         &amp;#x2F;&amp;#x2F;Enable pwm block
 PWM0_ENABLE_R  |=  (PWM_ENABLE_PWM0EN);&amp;#x2F;&amp;#x2F;Enable pwm output to M0PWM0
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Building an inverting charge pump</title>
        <published>2018-01-20T00:00:00+00:00</published>
        <updated>2018-01-20T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/charge-pump/"/>
        <id>https://chrisgreenley.com/projects/charge-pump/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/charge-pump/">&lt;p&gt;I’ve been using a bench power supply to provide the required negative voltage supply for the LCD screen I reverse engineered. I want to make this project portable, so everything needs to run off a USB battery pack. These tend to be lacking in “non-5V” power rails, so I built an inverting voltage charge pump.&lt;&#x2F;p&gt;
&lt;p&gt;The LCD display is composed of many cells forming pixels and each cell behaves very much like a capacitor. This means that there is very little net flow of current. Hooking up a multimeter showed that I really only need to provide a voltage rail at -13.5  volts, or lower, capable of approximately 3 mA. A charge pump is a cheap easy solution when dealing with currents lower than a few tens of mA. The main benefit of a charge pump is that it only uses diodes and capacitors combined with a square wave to generate the output voltage. No comparatively expensive or bulky inductors are required.&lt;&#x2F;p&gt;
&lt;p&gt;Dave Jones has done a few great videos on the EEVBlog explaining both inverting and non-inverting charge pumps, so for an explanation of how they work check out the following links.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=ep3D_LC2UzU&quot;&gt;EEVblog #469 - Cockcroft-Walton Multiplier&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=I4ED_8cuVTU&quot;&gt;EEVBlog #473 - Microcontroller Voltage Doubler&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=LtoPHevexTM&quot;&gt;EEVblog #483 - Microcontroller Voltage Inverter Tutorial&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The image below shows a five stage inverting voltage multiplier. The ideal voltage outputs are labeled at the bottom of the diagram. The last stage gives -5*Vin = -25 volts. Of course in the real world there are diode drops and output impedance to deal with. In practice I was able to achieve a no-load output voltage of around -21 volts.&lt;&#x2F;p&gt;
&lt;p&gt;Starting on the left side of the circuit, there is a logic level shifter followed by a push pull amplifier and then the five “pump” stages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;pwm_charge_pump.svg&quot; alt=&quot;Charge Pump Circuit Diagram&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Let’s go over the circuit in a bit more detail.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Capacitor C11 provides smoothing for the input voltage and helps prevent noise from getting back to the microcontroller.&lt;&#x2F;li&gt;
&lt;li&gt;Transistor Q2 combined with resistors R1, R4, and diode D11 shift the 3.3 volt square wave coming from the micro to a 5 volt square wave needed to drive the push-pull amplifier stage. See the end of this post for more details on this stage (such as what the diode is doing).&lt;&#x2F;li&gt;
&lt;li&gt;Transistors Q1 and Q3 with resistors R1 and R2 form the push-pull amplifier. This takes the fairly high output impedance 5 volt square wave coming from the logic level shifting stage and creates a very low output impedance square wave. Q1 slams the voltage down to ground and Q3 slams the voltage up to 5 volts. Pulling more current won’t significantly impact the peak to peak voltage of the square wave which means the pump stages will get a nice constant input.&lt;&#x2F;li&gt;
&lt;li&gt;Each charge pump stage consists of two capacitors and two diodes. Ideally these would be Schottky diodes to reduce the losses due to the forward voltage drop, but I only had one Schottky diode and a whole bag of 1N4148 diodes. I tried both 1uF and 10uF ceramic capacitors and found the 10uF caps worked better.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I constructed the circuit on a proto board laid out nearly exactly as shown in the diagram. On the left, the lowest pin is GND, the next is the PWM input and the top two pins are both Vin = 5 V. On the right, both pins are the negative voltage rail output.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;ChargePump_Circuit.jpg&quot; alt=&quot;Charge Pump Circuit&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The screen capture below shows the unloaded output voltage and the PWM signal. The output signal is AC coupled so that the noise can be observed. When unloaded there is no noticeable ripple, but there is the switching noise associated with the rising and falling edges of the square wave.&lt;&#x2F;p&gt;
&lt;p&gt;A multimeter reading shows -21 volts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;ChargePumpOutput_NoLoad.png&quot; alt=&quot;No Load Output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The next screen capture shows the waveform when the output is connected to a resistor drawing 3.3 mA. Once again the switching noise is evident, but now the ripple dominates. Peak to peak ripple (not accounting for switching noise) is ~100 mV.&lt;&#x2F;p&gt;
&lt;p&gt;A multimeter reading shows -14 volts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;ChargePumpOutput_Loaded.png&quot; alt=&quot;Loaded Output&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;appendix&quot;&gt;Appendix&lt;&#x2F;h4&gt;
&lt;p&gt;When testing the voltage level shifter portion of the circuit I encountered an interesting issue. At low frequencies the turn on and turn off times of the transistor can be neglected, but at high frequencies the turn off time becomes a significant percentage of the PWM period.&lt;&#x2F;p&gt;
&lt;p&gt;In the screen capture below (and all the following captures) the yellow signal is the PWM output measured directly from the uC pin, the light blue signal is the voltage on the base of the transistor, and the dark blue is the collector voltage. The shifter both shifts the high voltage from 3.3 to 5 volts and inverts the output (in a logical “NOT” sense). The transistor turns off when the PWM signal goes low, at which point the 2 kOhm resistor pulls the collector voltage high. The transistor turns on when the PWM signal goes low, pulling the collector voltage to ground.&lt;&#x2F;p&gt;
&lt;p&gt;The transistor turns on very quickly (less than 100ns), but it takes around 1.5 us for it to turn off after the PWM signal goes low. At a PWM frequency of 80 kHz, this is about 12% of the period. What this means is that if I set the PWM frequency to a +duty cycle of 50%, the +duty cycle of the level shifter will be about 40%. I could fix this in software by setting the +duty cycle to 60%, but I’d prefer to fix it in hardware.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;LevelShifter_JustResistor.png&quot; alt=&quot;Level Shifter - Just Resistor&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The root cause is that when the transistor is in saturation (fully on, collector voltage low), it takes a while to remove enough charge carriers to turn it off.&lt;&#x2F;p&gt;
&lt;p&gt;Below I show two possible solutions. Both involve adding a component in parallel with the base resistor which allows charge carries to be removed more quickly.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;solution-one&quot;&gt;Solution One&lt;&#x2F;h5&gt;
&lt;p&gt;The first solution is to place a capacitor in parallel with the base resistor. The rising and falling edges of the square wave are composed of very high frequency components to which the capacitor appears as a very low impedance path. This does make the turn on and turn off times of the transistor extremely fast, but it makes the base voltage go down to -3.3 volts for half of each cycle. From what I read, this will eventually make the transistor degrade.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;LevelShifter_1uFCap.png&quot; alt=&quot;Level Shifter - 1uF Cap&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;solution-two&quot;&gt;Solution Two&lt;&#x2F;h5&gt;
&lt;p&gt;The other solution is to place a diode in parallel with the base transistor (with the low side away from the transistor). When the PWM signal is high, the diode is reversed biased thus effectively not a part of the circuit. But when the PWM signal is low, the diode is forward biased and allows the charge carriers to leave the transistor much faster.&lt;&#x2F;p&gt;
&lt;p&gt;The following plots show the results for a variety of diode types.&lt;&#x2F;p&gt;
&lt;p&gt;The first diode is the 1N4148 which does improve the turn off time, but not as much as I would like. This is due to the forward voltage of the 1N4148 being basically the same as the transistor PN junction forward voltage.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;LevelShifter_1N4148.png&quot; alt=&quot;Level Shifter - 1N4148&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The next is a 1N4001, which although the diode takes a longer time to fully turn on (as it’s not a high speed switching diode like the 1N4148), it turns on more in the initial few ns. With this diode, the turn off time is quite good though there is a decent amount of ringing when it turns back on.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;LevelShifter_1N4001.png&quot; alt=&quot;Level Shifter - 1N4001&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The generally recommended diode is a Schottky diode due to its low forward voltage. The next screen capture shows a 1N5818 Schottky diode which results in an extremely short turn off time, but a great deal of ringing. Measuring the capacitance of this diode we can see that it is around 150 pF.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;LevelShifter_1N5818.png&quot; alt=&quot;Level Shifter - 1N5818&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The diode I went with is the 1N60 germanium diode. This diode has a very low forward voltage drop but the capacitance is only around 15 pF. The result, shown below, is a very fast turn off time but no ringing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;LevelShifter_1N60.png&quot; alt=&quot;Level Shifter - 1N60&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Writing drivers for a Dot-Matrix LCD Display</title>
        <published>2018-01-14T00:00:00+00:00</published>
        <updated>2018-01-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/lcd-driver/"/>
        <id>https://chrisgreenley.com/projects/lcd-driver/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/lcd-driver/">&lt;ul&gt;
&lt;li&gt;Read &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;&quot;&gt;Part One&lt;&#x2F;a&gt; about reverse engineering the LCD.&lt;&#x2F;li&gt;
&lt;li&gt;Read about the &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;charge-pump&#x2F;&quot;&gt;inverting voltage multiplier&lt;&#x2F;a&gt; I’m using to power the LCD.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The previous post dealt with the LCD hardware and the specifics of the ICs. This post will focus on the driver code used to create the sequence of signals previously discussed. I’m using a TI dev board with the Cortex M4 core TM4C123GH6PM microcontroller.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-driver&#x2F;hardware.jpg&quot; alt=&quot;Hardware&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;architecture&quot;&gt;Architecture&lt;&#x2F;h4&gt;
&lt;p&gt;Before diving into the details of getting pixels to actually show up in the proper locations on the screen, let’s walk though a high level overview of how the driver is going to be set up.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There will be two chunks of memory allocated as frame buffers. The two buffers will switch between being written to and being read from. This will be accomplished by using two pointers, &lt;code&gt;readBuffer&lt;&#x2F;code&gt; and &lt;code&gt;writeBuffer&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Sprites will be stored in row-column order, each row right padded to the nearest byte. Each bit will represent a single pixel. Zero meaning deactive (clear), and One meaning active (dark).&lt;&#x2F;li&gt;
&lt;li&gt;Sprites will be loaded into the frame buffer currently pointed to by &lt;code&gt;writeBuffer&lt;&#x2F;code&gt;. Upon loading the last sprite, the higher level code will let the driver know that the frame is complete.&lt;&#x2F;li&gt;
&lt;li&gt;A timer will trigger an interrupt and at each interrupt a single row of the &lt;code&gt;readBuffer&lt;&#x2F;code&gt; will be loaded into the LCD controller ICs and latched out to display on the screen.&lt;&#x2F;li&gt;
&lt;li&gt;After loading the last row in a frame, a check is performed to determine if the pointers to the read and write buffers should be switched.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;system-overview&quot;&gt;System Overview&lt;&#x2F;h4&gt;
&lt;p&gt;The following digram shows a brief system overview limited to the portions of the system directly involved in using the LCD screen. In this post I’m going to focus on how data gets from memory (the buffer) to being displayed on the LCD. Future posts will delve into detail on  how the sprites are stored and how the buffers get updated.&lt;&#x2F;p&gt;
&lt;p&gt;The modules involved are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;main.c&lt;&#x2F;li&gt;
&lt;li&gt;IO.c (IO.h)&lt;&#x2F;li&gt;
&lt;li&gt;screen.c (screen.h)&lt;&#x2F;li&gt;
&lt;li&gt;DisplayTests.c (DisplayTests.h)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The file main.c calls the appropriate initialization functions and then enters the infinite while loop. In the loop a check is performed to see if image data should be updated, that is, if a buffer is available to write to. If true, sprite states are updated and loaded into the current writeBuffer.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-driver&#x2F;System_Overview.svg&quot; alt=&quot;System Overview&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;main&quot;&gt;Main&lt;&#x2F;h5&gt;
&lt;p&gt;The most relevant parts of main.c are shown below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;int main(void){
  
  DisableInterrupts();
  PLL_Init();
  IO_Init();
  EnableInterrupts();
  
  &amp;#x2F;&amp;#x2F;Down the rabbit hole we go
  while(1){
    
    if(IO_Ready())
    {
      DisplayTests_DrawDiag();
      DisplayTests_DrawBorder();

      IO_UpdatesCompleted(); 
    }
    
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;input-output-io-module&quot;&gt;Input&#x2F;Output (IO) module&lt;&#x2F;h5&gt;
&lt;p&gt;The IO module has public functions declared in IO.h. The relevant public functions are shown below. The first three are called by main.c. The last, LoadSprite(), is called by DisplayTests.c.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;void IO_Init(void);
bool IO_Ready(void);
void IO_UpdatesCompleted(void);
void IO_LoadSprite( const uint16_t xpos, 
                    const uint16_t ypos, 
                    const Sprite_t sprite );
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;screen-module&quot;&gt;Screen module&lt;&#x2F;h5&gt;
&lt;p&gt;The screen module has public functions which are only called by IO.c. No other module is aware of its existence. The header file, screen.h, contains the declarations of these public functions as well as the screen width and screen height in pixels.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#define  SCREEN_W  480
#define  SCREEN_H  64

void Screen_Init(void);
void Screen_SetBufferIsFull(void);
bool Screen_IsBufferAvailable(void);
void Screen_ClearBuffer(void);
uint8_t * Screen_GetBuffer(void);
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;the-screen-driver&quot;&gt;The Screen Driver&lt;&#x2F;h4&gt;
&lt;p&gt;Alright, now that the overview is out of the way, let’s get into the meat of turning a bunch of ones and zeros into pixels on a screen.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;initialization&quot;&gt;Initialization&lt;&#x2F;h5&gt;
&lt;p&gt;A few screen related constants, two arrays large enough to hold an entire screen of data, and two pointers to those arrays are initialized at the top of screen.c.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#define FRAME_REFRESH_HZ  11
#define LCD_REFRESH_HZ    44
#define BUFFER_SIZE          (SCREEN_W*SCREEN_H&amp;#x2F;8)

uint8_t ScreenBufferA[BUFFER_SIZE] = {0};
uint8_t ScreenBufferB[BUFFER_SIZE] = {0}; 
uint8_t *writeBuffer = NULL;
uint8_t *readBuffer = NULL;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are five data signals which need to be handled.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Data Clock (CP)&lt;&#x2F;li&gt;
&lt;li&gt;Latch Clock (Load)&lt;&#x2F;li&gt;
&lt;li&gt;Frame Clock (Row Data, DIO1)&lt;&#x2F;li&gt;
&lt;li&gt;Column Data 1 (SDI)&lt;&#x2F;li&gt;
&lt;li&gt;Column Data 2 (SDI)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These will use pins 4-0 on port F. The TM4C line of uCs has a feature which allows bits [9-2] of the address used to access a GPIO register to act as a mask, thus enabling atomic read and write operations on any combination of pins. This is known as “bit banding” or “bit-specific addressing.” See the following sections of the &lt;a href=&quot;http:&#x2F;&#x2F;www.ti.com&#x2F;lit&#x2F;ds&#x2F;symlink&#x2F;tm4c123gh6pm.pdf&quot;&gt;datasheet&lt;&#x2F;a&gt; for more info:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;10.2.1.2 Data Register Operation (p. 654)&lt;&#x2F;li&gt;
&lt;li&gt;2.4.5 Bit-Banding (p. 97)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;and &lt;a href=&quot;http:&#x2F;&#x2F;users.ece.utexas.edu&#x2F;~valvano&#x2F;Volume1&#x2F;E-Book&#x2F;C6_MicrocontrollerPorts.htm&quot;&gt;this webpage&lt;&#x2F;a&gt; (ctrl-f for ‘bit-specific addressing’).&lt;&#x2F;p&gt;
&lt;p&gt;The first three signals will use PF1, PF2 and PF3 (respectively).&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#define DataClk_PF1 (*((volatile unsigned long *)0x40025008))
#define LatchClk_PF2 (*((volatile unsigned long *)0x40025010))
#define FrameClk_PF3 (*((volatile unsigned long *)0x40025020))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The two serial data signals will always be written together, so a single mask will suffice.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;#define SerialData_PF04 (*((volatile unsigned long *)0x40025044))
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As an example, the address for accessing PF0 and PF4 simultaneously is calculated via:&lt;&#x2F;p&gt;
&lt;p&gt;Address \( =\textrm{Base_Address} + ((2^4 + 2^0) \cdot 4)  \)&lt;&#x2F;p&gt;
&lt;p&gt;where the Base-Adress for the port F data register on the APB (Advanced Peripheral Bus) is 0x4002.5000. (Multiplying by 4 is equivilant to bit shifting left by 2.)&lt;&#x2F;p&gt;
&lt;p&gt;Now writing is as simple as:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;DataClk_PF1 = (1&amp;lt;&amp;lt;1); &amp;#x2F;&amp;#x2F;Make high
DataClk_PF1 = 0; &amp;#x2F;&amp;#x2F;Make low;
SerialData_PF04 = (1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;0); &amp;#x2F;&amp;#x2F;Make both high
SerialData_PF04 = 0; &amp;#x2F;&amp;#x2F;Make both low
SerialData_PF04 = (0&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;0); &amp;#x2F;&amp;#x2F;Make PF4 low and PF0 high
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are great because they can’t effect any other pins than those intended, are way faster than the standard read-modify-write sequence, and because the operations are atomic (can’t be interrupted.)&lt;&#x2F;p&gt;
&lt;h6 id=&quot;the-init-function&quot;&gt;The Init function&lt;&#x2F;h6&gt;
&lt;p&gt;The Screen_Init() function has three jobs.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Setup the GPIO pins needed for the LCD&lt;&#x2F;li&gt;
&lt;li&gt;Setup the SysTick timer to trigger interrupts for sending data to the LCD&lt;&#x2F;li&gt;
&lt;li&gt;Initialize the writeBuffer and readBuffer pointers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;void Screen_Init(void){
  &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
  &amp;#x2F;&amp;#x2F;Enable GPIOs for connecting to LCD            &amp;#x2F;&amp;#x2F;
  &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
  SYSCTL_RCGC2_R |= (1&amp;lt;&amp;lt;5);                  &amp;#x2F;&amp;#x2F; activate port F clock
  while((SYSCTL_PRGPIO_R &amp;amp; (1&amp;lt;&amp;lt;5)) == 0){;}  &amp;#x2F;&amp;#x2F; Wait for clock to stabalize 
  GPIO_PORTF_LOCK_R = 0x4C4F434B;            &amp;#x2F;&amp;#x2F; unlock PortF PF0  
  GPIO_PORTF_CR_R = 0x1F;                    &amp;#x2F;&amp;#x2F; allow changes to PF4-0
  GPIO_PORTF_DIR_R    |=   ((1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;3)|(1&amp;lt;&amp;lt;2)|(1&amp;lt;&amp;lt;1)|(1&amp;lt;&amp;lt;0)); &amp;#x2F;&amp;#x2F; make PF4-0 out (1)
  GPIO_PORTF_AFSEL_R  &amp;amp;=  ~((1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;3)|(1&amp;lt;&amp;lt;2)|(1&amp;lt;&amp;lt;1)|(1&amp;lt;&amp;lt;0)); &amp;#x2F;&amp;#x2F; disable alt funct 
  GPIO_PORTF_DR8R_R   |=   ((1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;3)|(1&amp;lt;&amp;lt;2)|(1&amp;lt;&amp;lt;1)|(1&amp;lt;&amp;lt;0)); &amp;#x2F;&amp;#x2F; can drive up to 8mA out
  GPIO_PORTF_DEN_R    |=   ((1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;3)|(1&amp;lt;&amp;lt;2)|(1&amp;lt;&amp;lt;1)|(1&amp;lt;&amp;lt;0)); &amp;#x2F;&amp;#x2F; enable digital I&amp;#x2F;O 
  GPIO_PORTF_AMSEL_R  &amp;amp;=  ~((1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;3)|(1&amp;lt;&amp;lt;2)|(1&amp;lt;&amp;lt;1)|(1&amp;lt;&amp;lt;0)); &amp;#x2F;&amp;#x2F; no analog 
  GPIO_PORTF_DATA_R   &amp;amp;=  ~((1&amp;lt;&amp;lt;4)|(1&amp;lt;&amp;lt;3)|(1&amp;lt;&amp;lt;2)|(1&amp;lt;&amp;lt;1)|(1&amp;lt;&amp;lt;0)); &amp;#x2F;&amp;#x2F;set to zero
  
  
  &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
  &amp;#x2F;&amp;#x2F;Enable systick interrupts for writing to LCD  &amp;#x2F;&amp;#x2F;
  &amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;&amp;#x2F;
  uint32_t sysTickReloadCount = (80000000&amp;#x2F;LCD_REFRESH_HZ&amp;#x2F;SCREEN_H);  
  NVIC_ST_CTRL_R = 0;           &amp;#x2F;&amp;#x2F; disable SysTick during setup
  NVIC_ST_RELOAD_R = sysTickReloadCount-1;     &amp;#x2F;&amp;#x2F; reload value 
  NVIC_ST_CURRENT_R = 0;        &amp;#x2F;&amp;#x2F; any write to current clears it
  NVIC_SYS_PRI3_R = (NVIC_SYS_PRI3_R&amp;amp;0x00FFFFFF)|0x20000000; &amp;#x2F;&amp;#x2F; priority 1     
  NVIC_ST_CTRL_R = 0x07;  &amp;#x2F;&amp;#x2F; enable with core clock and interrupts  
  

  writeBuffer = ScreenBufferA;
  readBuffer = ScreenBufferB;
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;public-interface&quot;&gt;Public Interface&lt;&#x2F;h4&gt;
&lt;p&gt;The following functions make up the public interface which is accessed by the IO.c module. The names should be fairly self explanatory.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;uint8_t * Screen_GetBuffer(void)
{
  return writeBuffer;
}

void Screen_SetBufferIsFull(void)
{
  writeBufferIsFull = true;
  writeBufferAvailable = false;
}


bool Screen_IsBufferAvailable(void)
{
  return writeBufferAvailable;
}

void Screen_ClearBuffer(void)
{ 
  uint16_t i;
  for(i=0;i&amp;lt;BUFFER_SIZE;i++){
    writeBuffer[i]=0x00; &amp;#x2F;&amp;#x2F;zeros correspond to the default clear pixel state
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;interrupt-handling&quot;&gt;Interrupt Handling&lt;&#x2F;h4&gt;
&lt;p&gt;Each time the interrupt service routine is called, the row corresponding to the current value of rowIndex is loaded into the LCD. The value of rowIndex loops from zero up to SCREEN_H-1. A row stays active on the display until the next time the interrupt runs. The second part of the ISR only runs if the last row of a frame was just displayed, if the current frame has been displayed enough times, and if the new frame is ready. If all these conditions are met, then the read and write buffers are switched.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;void SysTick_Handler(void){
  static const uint8_t minLcdUpdatesPerFrame = LCD_REFRESH_HZ&amp;#x2F;FRAME_REFRESH_HZ;
  static uint8_t rowIndex = 0;
  static uint8_t lcdUpdates = 0;
  
  PrintNextRow(rowIndex);
  rowIndex = (rowIndex+1)%SCREEN_H;
  
  if(rowIndex==0 and ++lcdUpdates&amp;gt;=minLcdUpdatesPerFrame and writeBufferIsFull)
  {
      SwitchBuffer();
      writeBufferAvailable = true;
      lcdUpdates = 0;
      writeBufferIsFull = false;
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;static void SwitchBuffer(void)
{
  if(writeBuffer == ScreenBufferA)
  {
    writeBuffer = ScreenBufferB;
    readBuffer = ScreenBufferA;
  }
  else
  {
    writeBuffer = ScreenBufferA;
    readBuffer = ScreenBufferB;
  } 
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;printing-a-row&quot;&gt;Printing a row&lt;&#x2F;h4&gt;
&lt;h6 id=&quot;a-few-requirements&quot;&gt;A few requirements&lt;&#x2F;h6&gt;
&lt;ul&gt;
&lt;li&gt;The interrupt rate will be such that the entire LCD is refreshed at a rate of at least 40 Hz, that is (40Hz)*(number of rows). I found that below this rate there would visible flickering.&lt;&#x2F;li&gt;
&lt;li&gt;The data clock must not exceed 3.3 MHz (as per the LCD driver IC datasheets)&lt;&#x2F;li&gt;
&lt;li&gt;The width of a clock pulse must not be less than approx 80 ns (The datasheet says 100 ns, but in testing I found down to about 80 ns to be fine.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h5 id=&quot;logical-flow&quot;&gt;Logical flow&lt;&#x2F;h5&gt;
&lt;p&gt;The flowchart below shows the function PrintNextRow() and the functions it calls. The LCD is split into a left and right half so the first bit of the left half and the first bit of the right half get sent on the same falling edge of the data clock, etc… The falling edge of the latch clock tells the LCD ICs to take their data buffers and output them to their physical pins as high or low voltages. This signal also tells the IC controlling the rows to shift to the next row. The frame clock shifts in a new high value to the row IC which then gets shifted through each row by the latch clock signal until shifting off the last pin on the IC. This way the rows can be activated one at a time.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-driver&#x2F;LCD_Driver.svg&quot; alt=&quot;LCD Driver flowchart&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The following code is logically equivalent but has been modified from the simplest implementation to achieve the 3.3 MHz data rate.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The “for each bit in a byte” loop has been unrolled&lt;&#x2F;li&gt;
&lt;li&gt;The SendData() function has been integrated into the unrolled loop&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These two changes drastically improved the data rate, actually getting over 5 Mhz. The delayns() is used to tune the data rate to close to 3.3 MHz as measured on a scope.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;static void PrintNextRow(uint8_t rowIndex){

  uint8_t bytesPerRow,colIndex;
  uint8_t bufferByteLeft, bufferByteRight,bitValLeft,bitValRight;
  bytesPerRow = SCREEN_W&amp;gt;&amp;gt;3;&amp;#x2F;&amp;#x2F; divided by 8 (bytes)
  
    for(colIndex=0;colIndex&amp;lt;(bytesPerRow&amp;gt;&amp;gt;1);++colIndex){
      bufferByteLeft = readBuffer[rowIndex*bytesPerRow+colIndex];
      bufferByteRight = readBuffer[rowIndex*bytesPerRow+colIndex+(bytesPerRow&amp;gt;&amp;gt;1)];

      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;7))&amp;gt;&amp;gt;7;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;7))&amp;gt;&amp;gt;7;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;6))&amp;gt;&amp;gt;6;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;6))&amp;gt;&amp;gt;6;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;5))&amp;gt;&amp;gt;5;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;5))&amp;gt;&amp;gt;5;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;4))&amp;gt;&amp;gt;4;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;4))&amp;gt;&amp;gt;4;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;3))&amp;gt;&amp;gt;3;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;3))&amp;gt;&amp;gt;3;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;2))&amp;gt;&amp;gt;2;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;2))&amp;gt;&amp;gt;2;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;1))&amp;gt;&amp;gt;1;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;1))&amp;gt;&amp;gt;1;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
      delayns();
      
      DataClk_PF1 = (1&amp;lt;&amp;lt;1);
      bitValLeft = (bufferByteLeft&amp;amp;(1&amp;lt;&amp;lt;0))&amp;gt;&amp;gt;0;
      bitValRight = (bufferByteRight&amp;amp;(1&amp;lt;&amp;lt;0))&amp;gt;&amp;gt;0;
      SerialData_PF04 = ((bitValRight)&amp;lt;&amp;lt;0)|((bitValLeft)&amp;lt;&amp;lt;4);
      DataClk_PF1 = 0x00; &amp;#x2F;&amp;#x2F;Clocks in data on falling clock edge
    }
    if(rowIndex==0)
      FrameClock();
    else
      LatchClock();
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;c&quot; class=&quot;language-c &quot;&gt;&lt;code class=&quot;language-c&quot; data-lang=&quot;c&quot;&gt;&amp;#x2F;&amp;#x2F;Latch Clock: LatchClk_PF2
static void LatchClock(void){
  unsigned long i=17; &amp;#x2F;&amp;#x2F;specific number doesn&amp;#x27;t matter
  LatchClk_PF2 = (1&amp;lt;&amp;lt;2);
  i=i&amp;#x2F;i; &amp;#x2F;&amp;#x2F;Delay enough to keep peak at least ~80ns wide
  LatchClk_PF2 =  0x00;
}

&amp;#x2F;&amp;#x2F;DIO1: FrameClk_PF3
static void FrameClock(void){
  FrameClk_PF3 =   (1&amp;lt;&amp;lt;3);
  LatchClock();
  FrameClk_PF3 =  0x00;
}

static void delayns(void){
  unsigned int i;
  i=i&amp;#x2F;i&amp;#x2F;i;
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;ideas-for-the-future&quot;&gt;Ideas for the Future&lt;&#x2F;h4&gt;
&lt;p&gt;The code I have now is working quite well, but I think I could utilize a dual SPI setup to do the actual sending of each row. I believe there are some  microcontrollers which can send multiple streams of data on a single SPI peripheral, but I don’t believe the one I’m using can. But I might be able to use two SPI peripherals and have them share a single clock.&lt;&#x2F;p&gt;
&lt;p&gt;If I get SPI working I’d also like to utilize DMA for transferring data to the SPI peripheral.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Reverse Engineering an LCD dot-matrix display</title>
        <published>2018-01-12T00:00:00+00:00</published>
        <updated>2018-01-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/lcd-screen/"/>
        <id>https://chrisgreenley.com/projects/lcd-screen/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/lcd-screen/">&lt;p&gt;A few months ago I popped into Goodwill to see if they had any interesting electronics to take apart. I was rewarded with a fully functioning 1993 Smith Corona electric word processor for all of $5. After playing around with it for a few days and learning how it worked, I took it apart and turned it into a box of interesting electrical and mechanical doodads.&lt;&#x2F;p&gt;
&lt;p&gt;One of the components was a 480x64 pixel dot-matrix style LCD display (LMG6721UHGR) which I decided to use for a project. I already had a few of the 16x2 character LCDs and a Nokia cell phone screen breakout board I could have used, but those are pretty small and I was intrigued by the idea of reusing this old display.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;Screen_front.jpg&quot; alt=&quot;Screen front&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-hardware&quot;&gt;The Hardware&lt;&#x2F;h4&gt;
&lt;p&gt;The circuit board the screen is attached to has a ten pin row of standard 0.1“ headers which provided the connections back to the main control board when it was in the typewriter. The circuit board has seven large ICs and two smaller ICs. After googling for the datasheets it turns out that one of the smaller ICs is a quad opamp (LA6324N) which provides the required voltages for driving the LCD cells (more on that later). The other small IC (53003HEB-S) is some sort of counter (I haven’t been able to find a datasheet for it). Six of the large ICs (three LC7940 and three LC7941) control the 480 columns (80 columns for each chip). The seventh (LC7942) controls the 64 rows.&lt;&#x2F;p&gt;
&lt;p&gt;Datasheets&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.pacificdisplay.com&#x2F;ics_app%20notes&#x2F;sanyo&#x2F;LC7940-41YC.pdf&quot;&gt;LC7940&#x2F;LC7941 Segment Driver&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.pacificdisplay.com&#x2F;ics_app%20notes&#x2F;sanyo&#x2F;LC7942.pdf&quot;&gt;LC7942 Common Driver&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.onsemi.com&#x2F;pub&#x2F;Collateral&#x2F;LA6324N-D.PDF&quot;&gt;LA6324 Quad OpAmp&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;Screen_back.jpg&quot; alt=&quot;Screen back&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;Screen_back_sides.png&quot; alt=&quot;Screen back sides&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My next task was to determine which signals are broken out to the header pins and how the LC794x ICs work. After a few hours of referring to data sheets, learning how LCDs work, and many continuity measurements I determined the pinout for the headers and what pins on the driver ICs are connected to what.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;headers.svg&quot; alt=&quot;Headers&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The headers are shown as viewed in the picture of the back of the screen (above). The labels are referring to the pins on the driver ICs.&lt;&#x2F;p&gt;
&lt;p&gt;Each column of pixels is controlled by the six 7940 and 7941 ICs, the pinout for which is shown below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;pinout_annotated.png&quot; alt=&quot;Column Driver IC pinout&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The 7940 and 7941 are the same IC, just with their pins flipped to help with dense circuit designs. The figure above matches the lower three ICs in orientation, but since the pins are flipped on the upper three ICs the order of the pins ends up being the same. The numbers refer to the pin number in the headers pins. (I’ve noted where physical pins exist but aren’t shown in the pinout diagram because they don’t connect to anything internally.)&lt;&#x2F;p&gt;
&lt;p&gt;The P&#x2F;S pin is pulled low which sets the IC to serial data input mode. The pins which would be used for parallel input (DI1,2,3) are also pulled low. The screen is split into a left half and a right half. The three ICs controlling the left half get data from header pin 1 and the three ICs controlling the right half get data from header pin 2. Strangely the DISPOFF pin doesn’t seemed to be pulled up or down.&lt;&#x2F;p&gt;
&lt;p&gt;CDI on the first IC of each set of three is pulled low. The CDO pin of the first IC is connected to the CDI pin of the second, and the CDO of the second connected to the CDI of the third. The CDO of the third IC in each set isn’t connected to anything.&lt;&#x2F;p&gt;
&lt;p&gt;The rows are controlled from the LC7940 driver IC which is shown below rotated by 180 degrees to match the orientation in the picture of the back of the screen (above). See the &lt;a href=&quot;http:&#x2F;&#x2F;www.pacificdisplay.com&#x2F;ics_app%20notes&#x2F;sanyo&#x2F;LC7942.pdf&quot;&gt;datasheet&lt;&#x2F;a&gt; for the regular orientation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;pinout_7942.png&quot; alt=&quot;Row Driver IC pinout&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The RS&#x2F;LS pin is pulled to logic low which sets the shift direction to “right shift”, meaning DIO1 is the input, DIO64 is the output, and the data shifts from O1 to O64. This chip behaves exactly like a shift register, so data shifted into DIOI on the falling edge of the first clock pulse is shifted out of DIO64 at the 64th falling edge. This allows chaining to control displays with more than 64 columns. In this display O1 is connected to the top row and O64 is connected to the bottom row.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;driving-an-lcd&quot;&gt;Driving an LCD&lt;&#x2F;h5&gt;
&lt;p&gt;The driver ICs utilize seven different voltage levels. Three are from header pins 7, 8 and, via the transistor, pin 9. The remaining 4 are created by the quad opamp IC.&lt;&#x2F;p&gt;
&lt;p&gt;In order from highest to lowest:&lt;&#x2F;p&gt;
&lt;p&gt;VDD = V1 (logic high), V2, V3, V4, V5, VEE (LCD Gnd)&lt;&#x2F;p&gt;
&lt;p&gt;Shown below are how V2 through V5 relate to VDD and VEE along with example numbers assuming VDD=3.3V and VEE=-10V.&lt;&#x2F;p&gt;
&lt;p&gt;Let  |dV| = |VDD - VEE|.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;V2 = |dV|*(6&#x2F;7) - |VEE| = 1.4V&lt;&#x2F;li&gt;
&lt;li&gt;V3 = |dV|*(5&#x2F;7) - |VEE| = -0.52V&lt;&#x2F;li&gt;
&lt;li&gt;V4 = |dV|*(2&#x2F;7) - |VEE| = -6.3V&lt;&#x2F;li&gt;
&lt;li&gt;V5 = |dV|*(1&#x2F;7) - |VEE| = -8.2V&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Logic low (Vss) will be close to V3, but could be above or below it depending on the what the pot is set to (since the pot controls VEE).&lt;&#x2F;p&gt;
&lt;p&gt;The image below shows how the voltage generation circuitry using 3.3 volts as logic high and with -15 volts connected to pin 9. The voltages are all measured relative to logic low. The potentiometer adjusts the voltage difference between the collector and the emitter which changes VEE. The end effect of this is to change the screen contrast.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;opamp.png&quot; alt=&quot;Voltage circuitry&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To understand why all the voltages are required (and what the yet to be mentioned “M signal” does), we have to delve into the workings of Liquid Crystal Display technology.&lt;&#x2F;p&gt;
&lt;p&gt;A common LCD panel configuration consists of two polarizers at right angles to each other sandwiching the cell structure containing the “liquid crystal” as well as sheets of transparent electrodes on each side of the liquid crystal sheet. Light passing through one polarizer is then rotated 90 degrees by the liquid crystal such that it lines up with the second polarizer. Thus the default state is a “clear” pixel. When a voltage is applied to the electrodes, the crystal structure is untwisted meaning the polarization angle of the light is no longer being rotated and so will be perpendicular to the second polarizer causing the light to be blocked and the pixel to turn black.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, things get a bit more complicated. If you apply a DC voltage to a cell it will work but not for long. The crystal structure will degrade and the electrodes will cease to be transparent. The solution is to apply an AC voltage centered on zero. The voltage across the cell can’t simply go from say 10 volts to 0 and back to 10, it must flip polarity each cycle. So if one moment electrode A is 10 volts higher than electrode B, the next moment electrode A must be 10 volts lower than electrode B. Not only does this switching need to occur when the cell is activated, but also when in it’s inactive state.&lt;&#x2F;p&gt;
&lt;p&gt;See these wikipedia pages for more info: &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Liquid_crystal#Effect_of_chirality&quot;&gt;Effect of Chirality&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Twisted_nematic_field_effect&quot;&gt;Twisted nematic field effect&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The total voltage difference across a cell is determined by the difference between the voltages output by the column drivers and the row driver. For the column drivers, selected (dark pixel) voltages are V1(VDD) and VEE and not-selected (clear pixel) voltages are V3 and V4. For the row driver the selected voltages are the same, but the not-selected voltages and V2 and V5.&lt;&#x2F;p&gt;
&lt;p&gt;Which voltage is output at any given moment is a function of the M level and the Q level (corresponding to the electrode being ON or OFF).&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Q State&lt;&#x2F;th&gt;&lt;th&gt;M&lt;&#x2F;th&gt;&lt;th&gt;Row&lt;&#x2F;th&gt;&lt;th&gt;Col&lt;&#x2F;th&gt;&lt;th&gt;Row-Col&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;ON&lt;&#x2F;td&gt;&lt;td&gt;Low&lt;&#x2F;td&gt;&lt;td&gt;VDD&lt;&#x2F;td&gt;&lt;td&gt;VEE&lt;&#x2F;td&gt;&lt;td&gt;+13.3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ON&lt;&#x2F;td&gt;&lt;td&gt;High&lt;&#x2F;td&gt;&lt;td&gt;VEE&lt;&#x2F;td&gt;&lt;td&gt;VDD&lt;&#x2F;td&gt;&lt;td&gt;-13.3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;OFF&lt;&#x2F;td&gt;&lt;td&gt;Low&lt;&#x2F;td&gt;&lt;td&gt;V2&lt;&#x2F;td&gt;&lt;td&gt;V3&lt;&#x2F;td&gt;&lt;td&gt;+1.9&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;OFF&lt;&#x2F;td&gt;&lt;td&gt;High&lt;&#x2F;td&gt;&lt;td&gt;V5&lt;&#x2F;td&gt;&lt;td&gt;V4&lt;&#x2F;td&gt;&lt;td&gt;-1.9&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;For a pixel to be OFF (clear) the voltage difference between the electrodes is 1.9 volts and for a pixel to be ON (dark) the voltage difference is 13.3 volts.
The scope output below shows the voltages on the electrodes for row 1 and column 1, as well as the latch&#x2F;load clock and the M signal for one complete frame. The M signal alternates every 5 falling edges of the load&#x2F;latch clock (look for the very thin yellow peaks) and the electrode voltages swap each time the M signal changes. This output shows the somewhat unrealistic, but helpfully illustrative, case where every row and every column are ON at all times. Normally only one output of the row driver IC would be activated at any given moment.&lt;&#x2F;p&gt;
&lt;p&gt;The MATH output shows the column voltage minus the row voltage. This alternation results in a AC signal with a peak-to-peak amplitude of 2*|VDD-VEE|.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;scope_M_math_AllRows.png&quot; alt=&quot;Scope M math all rows&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The following scope output shows exactly the same signals, just zoomed in on the time axis.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;scope_M_math_AllRows_zoomed.png&quot; alt=&quot;Scope M math all rows zoomed&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The next output still shows the same signals, but with the more realistic scenario where the rows are being activated one at a time while  every pixel in every column is ON (dark). Each row is being turned dark one at a time as the row IC driver shifts through it’s 64 outputs before starting again.&lt;&#x2F;p&gt;
&lt;p&gt;Looking at the results of the difference between the row and column voltages, it is clear that the difference in voltage is large enough to darken the pixels in row 1 only for the time between the first and second latch clock pulses. Since the M signal changes every 5 latch clocks and that doesn’t divide evenly into 64, the voltage applied to the cells in this row will keep flipping as the row driver IC loops back around.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;scope_M_math_OneRow.png&quot; alt=&quot;Scope M math one row&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;loading-data&quot;&gt;Loading Data&lt;&#x2F;h5&gt;
&lt;p&gt;In brief:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The output buffer of each column driver is serially filled with 80 bits of data&lt;&#x2F;li&gt;
&lt;li&gt;Each bit gets read in on the falling edge of the data clock&lt;&#x2F;li&gt;
&lt;li&gt;Once the buffer is full the first time, the latch clock latches the buffer out to the drive circuitry&lt;&#x2F;li&gt;
&lt;li&gt;At effectively the same moment the frame clock shifts a 1 into the first bit of the row driver&lt;&#x2F;li&gt;
&lt;li&gt;The column buffers get refilled (240 data clocks), the active output of the row driver shifts to the next row and the column data is latched to the pins.&lt;&#x2F;li&gt;
&lt;li&gt;This continues over and over with a 1 getting shifted into the row driver every 64 latch clocks&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h6 id=&quot;filling-the-column-buffers&quot;&gt;Filling the Column Buffers&lt;&#x2F;h6&gt;
&lt;p&gt;The screen is split into two sections, each 240 pixels wide. The two halves are loaded in parallel and all the logic is the same for each half. Until I get into the software we can ignore the right half of the screen and pretend there are only three column driver ICs.&lt;&#x2F;p&gt;
&lt;p&gt;All three ICs have their serial inputs connected together and brought out to header pin 1. This means each IC sees all 240 bits of data but only accepts 80. This is accomplished though the CDI and CDO pins. When CDI (Chip Disable) is low the chip is enabled and will read in data from the serial input. When the CDI pin is high it won’t. The following steps go through the process of filling all three buffers.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Start with all buffers empty (due to the latch clock)&lt;&#x2F;li&gt;
&lt;li&gt;CDI1 is held low, CDO1&#x2F;CDI2 and CDO2&#x2F;CDI3 begin high&lt;&#x2F;li&gt;
&lt;li&gt;After the first 80 bits have been read into IC1, CDO1 (and thus CDI2) goes low&lt;&#x2F;li&gt;
&lt;li&gt;IC1 is full and so will ignore the remaining 160 bits&lt;&#x2F;li&gt;
&lt;li&gt;The middle set of 80 bits are read into IC2, CDO2&#x2F;CDI3 goes low&lt;&#x2F;li&gt;
&lt;li&gt;IC1 and IC2 are both full and thus ignore the remaining 80 bits&lt;&#x2F;li&gt;
&lt;li&gt;The last 80 bits are read into IC3&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The scope output below illustrates these steps. CDI1 is not shown since that pin is always held low. CDO1&#x2F;CDI2 is high for the first third of the data and CDO2&#x2F;CDI3 is high for the first two thirds.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;scope_CDI_CDO.png&quot; alt=&quot;Scope CDI CDO&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h6 id=&quot;multiplexing&quot;&gt;Multiplexing&lt;&#x2F;h6&gt;
&lt;p&gt;There are three “clock” signals which control the ability to multiplex the data, thus controlling many pixels with only a few pins.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The Data Clock (CP) controls the loading of data into the buffers in the column driver ICs, the state of the SDI pin is read in on each falling edge of the data clock.&lt;&#x2F;li&gt;
&lt;li&gt;The Latch Clock (load) has several roles
&lt;ul&gt;
&lt;li&gt;It controls when the column buffers are latched to the output pins&lt;&#x2F;li&gt;
&lt;li&gt;It provides the clock signal to the counter IC which produces the alternating M signal every 5 latch clocks&lt;&#x2F;li&gt;
&lt;li&gt;It provides the clock signal to the column driver, shifting the active row each time.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;The Frame Clock (DIO1) is really the input data signal for the row driver IC, the state of which is clocked in by the latch clock. However, it’s useful to think of it as the “clock signal” which begins a single frame.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The following scope output shows the relationship between the data clock, latch clock and frame clock. There are 30 groups of 8 data clock pulses between each latch clock due to the data being read in one bit at a time from 8 bit chunks.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;scope_frameclk_latchclk_dataclk.png&quot; alt=&quot;Scope frame latch data clocks&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;real-example&quot;&gt;Real Example&lt;&#x2F;h5&gt;
&lt;p&gt;The scope output below shows the beginning of a row where the data 01110 is being loaded, corresponding to three dark pixels with a clear pixel on either side.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-screen&#x2F;scope_Three_pixels.png&quot; alt=&quot;Scope three pixels&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Read &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;lcd-driver&#x2F;&quot;&gt;Part Two&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Electronic Load</title>
        <published>2017-05-19T00:00:00+00:00</published>
        <updated>2017-05-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/electronic-load/"/>
        <id>https://chrisgreenley.com/projects/electronic-load/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/electronic-load/">&lt;p&gt;The simplest load is a plain old resistor, and for many testing applications that will be perfectly adequate. However, when testing power supplies and batteries one often needs to set the exact current draw and have it remain constant across a range of voltages. &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=8xX2SVcItOA&quot;&gt;This video&lt;&#x2F;a&gt; by Dave Jones showcases a simple design which uses an opamp in a negative feedback configuration controlling the base of a MOSFET to maintain the voltage across a sense resistor.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-math&quot;&gt;The Math&lt;&#x2F;h4&gt;
&lt;p&gt;Mostly I want a load that can handle 1 or 2 amps for long periods, but I’d like it to be able to go up to 5 amps for shorter periods. A 1 ohm sense resistor must then be rated for at least 25 Watts (\( P=I^2R \)). I’d also like to be able to handle up to 30 volts at 5 amps which means the MOSFET must be able to dissipate \((5A)(30V)-25W = 125W\). The figure below shows the power dissipated assuming an ideal sense resistor and MOSFET as a function of current for various supply voltages. Fortunately I had on hand a few high power MOSFETs from an old receiver which meet the criteria quite nicely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;power_plot_theoretical.svg&quot; alt=&quot;Theoretical Power Dissipation&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The IRFP450A MOSFET has a max drain-source voltage of 500 volts, a max power dissipation of 190 Watts, can handle up to 8.7 Amps at a case temperture of 100 degrees Celsius, and has a nice low drain-source on resistance of 0.4 Ohms. From figure 1 in the datasheet we can see that to obtain 5 Amps the minimum required gate-source voltage is 6 volts (and drain-source is 1.5 volts). Since at 5 amps the voltage drop across the sense resistor will be 5 volts, the opamp has to be able to output at least 11 volts and supply under test must be above 6.5 volts. Testing the opamp I’m using revealed that it can output up to Vcc-1.5V. Thus a Vcc of 12.5 volts is required.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-parts&quot;&gt;The Parts&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;LM358P Dual Opamp&lt;&#x2F;li&gt;
&lt;li&gt;Potentiometer&lt;&#x2F;li&gt;
&lt;li&gt;IRFP450A Power MOSFET (&lt;a href=&quot;http:&#x2F;&#x2F;www.vishay.com&#x2F;docs&#x2F;91230&#x2F;91230.pdf&quot;&gt;Datasheet&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;1 Ohm 25 Watt Power Resistor&lt;&#x2F;li&gt;
&lt;li&gt;Large heatsink with fan&lt;&#x2F;li&gt;
&lt;li&gt;Thermal Paste&lt;&#x2F;li&gt;
&lt;li&gt;2 standard resistors for voltage divider&lt;&#x2F;li&gt;
&lt;li&gt;Screw terminal&lt;&#x2F;li&gt;
&lt;li&gt;9 volt battery and holder&lt;&#x2F;li&gt;
&lt;li&gt;Boost Converter&lt;&#x2F;li&gt;
&lt;li&gt;Banana jacks&lt;&#x2F;li&gt;
&lt;li&gt;Toggle switch&lt;&#x2F;li&gt;
&lt;li&gt;Panel Voltmeter&lt;&#x2F;li&gt;
&lt;li&gt;Copper clad board&lt;&#x2F;li&gt;
&lt;li&gt;Feric Chloride&lt;&#x2F;li&gt;
&lt;li&gt;Label backing paper&lt;&#x2F;li&gt;
&lt;li&gt;Laser Printer&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;the-circuit&quot;&gt;The Circuit&lt;&#x2F;h4&gt;
&lt;p&gt;The circuit shown below is designed to connect directly to a 9V battery, but after I built it I added in a voltage boost converter in series with the battery to let me jump the voltage up to 12.5V. The voltage divider formed by R1 and R2 drop 9V down to 5V and the sense resistor of 1 Ohm gives a direct conversion to current. These can be changed such that the max output voltage from the first opamp is dropped down to a voltage equal in number to the max current you want the load to ever “ask for.”&lt;&#x2F;p&gt;
&lt;p&gt;A convenient result of using a 1 Ohm sense resistor is that I can use a simple panel voltmeter to measure the current to an accuracy decent enough for most of my applications. (Avoid the 2 wire meters which measure and power themselves from the same rail.) You might notice there are no dedicated connections for the meter on the circuit. I realized this too late in the process and so I just tacked the wires down at the appropriate points on the board.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;ElectronicLoadSchematic.svg&quot; alt=&quot;Schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;pcb-design&quot;&gt;PCB Design&lt;&#x2F;h5&gt;
&lt;p&gt;The image below shows a PCB design for this circuit created in KiCad. The main current loop is shown by the yellow highlighter. Parts can be directly soldered to the board but items such as the sense resistor, MOSFET, pot, and the terminals for connecting power supplies under test I connected via jumper wires so they could be mounted elsewhere. The low power traces and other tolerances are wider than one would usually make them since I wanted to experiment with etching my own board.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The two thick traces on the right side should be covered in solder to increase their current carrying capacity.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;ElectronicLoadPCB.png&quot; alt=&quot;PCB&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h5 id=&quot;pcb-etching&quot;&gt;PCB Etching&lt;&#x2F;h5&gt;
&lt;p&gt;After a variety of failures and running to the store for tiny carbide drill bits I managed to create a workable board with only one bodge wire (this has been corrected in the schematic above).&lt;&#x2F;p&gt;
&lt;p&gt;My etching process&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Export just the copper layer from KiCad (not mirrored)&lt;&#x2F;li&gt;
&lt;li&gt;Cut copper clad board to match size&lt;&#x2F;li&gt;
&lt;li&gt;Peel labels off of backing material and cut out a piece of backing material large enough to print the circuit on&lt;&#x2F;li&gt;
&lt;li&gt;Rub the shiny side down with Acetone&lt;&#x2F;li&gt;
&lt;li&gt;Tape to a sheet of printer paper using masking tape (print to paper first to be sure the location is correct)&lt;&#x2F;li&gt;
&lt;li&gt;Print with laser printer&lt;&#x2F;li&gt;
&lt;li&gt;Place toner side down onto copper clad board, cover with tin foil, and press down with a clothes iron for several minutes&lt;&#x2F;li&gt;
&lt;li&gt;Carefully remove paper (may help to soak in a bowl of water)&lt;&#x2F;li&gt;
&lt;li&gt;Place copper clad board in a Ferric Chloride bath and agitate until all visible copper is removed&lt;&#x2F;li&gt;
&lt;li&gt;Remove toner with acetone&lt;&#x2F;li&gt;
&lt;li&gt;Drill holes (the huge pain-in-the-ass part no one ever mentions)&lt;&#x2F;li&gt;
&lt;li&gt;Place components on copper free side so that the legs stick through to the copper side where soldering will happen&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;ElectronicLoadPrototypes.jpg&quot; alt=&quot;Prototypes&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;the-enclosure&quot;&gt;The Enclosure&lt;&#x2F;h4&gt;
&lt;p&gt;I used a steel box I had from taking apart some old broken equipment which I decided to use for the project box. The idea is that the whole container will form part of the heatsink. To avoid drilling into steel as much as possible, I designed an adapter plate to be 3D printed which would house the controls and fit into the odd oval-ish hole in the box.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;ElectronicLoadAdapter.png&quot; alt=&quot;Adapter Plate&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;putting-it-together&quot;&gt;Putting it together&lt;&#x2F;h4&gt;
&lt;p&gt;I drilled holes in the housing and used M2 and M3 bolts to firmly attach the adapter plate, sense resistor, MOSFET and heatsink. Before mounting, thermal paste was applied as needed, and the MOSFET was electrically isolated using a mica sheet. I glued a section of foam to the case for the circuit board to sit on top of to prevent shorting the copper traces on the metal of the chassis.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;ElectronicLoadInner.jpg&quot; alt=&quot;Inside the enclosure&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After some testing, I found that the max continuous dissipation in this configuration was about 30 watts and added that warning to the case.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;ElectronicLoadOuter.jpg&quot; alt=&quot;Outside view&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;testing&quot;&gt;Testing&lt;&#x2F;h4&gt;
&lt;p&gt;The first plot shows what what the maximum current draw is as a function of the device under test voltage. In theory this would be that voltage divided by 1.4 Ohms (Sense resistor + MOSFET on resistance).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;MaxCurrent.svg&quot; alt=&quot;Max Current&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The second plot shows the maximum total power dissipated by the load at the same voltages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;electronic-load&#x2F;MaxPower.svg&quot; alt=&quot;Max Power&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;future-modifications&quot;&gt;Future Modifications&lt;&#x2F;h4&gt;
&lt;p&gt;I went the battery route for extra portability, but it goes through batteries pretty fast when the fan is running so for long tests I’m going to add in a power jack.&lt;&#x2F;p&gt;
&lt;p&gt;I’m going to work on changing the heat dissipation setup so I can get at least 50 watts.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>PHYS3098: Circuits</title>
        <published>2017-04-04T00:00:00+00:00</published>
        <updated>2017-04-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/circuits-lab/"/>
        <id>https://chrisgreenley.com/projects/circuits-lab/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/circuits-lab/">&lt;p&gt;I taught the circuits lab for physics majors three times, the first time as a grad student and the second and third times as an instructor. I really enjoyed teaching this lab, partly because I learned so much myself.&lt;&#x2F;p&gt;
&lt;p&gt;The second year, I did a complete rewrite of all the labs. Below are links to the full labs and summaries of what was covered in each lab.&lt;&#x2F;p&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-1-instrumentation-and-measurements-pdf&quot;&gt;Lab 1 - Instrumentation and Measurements &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_01_Instrumentation.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab1_scope.png&quot; alt=&quot;Lab 1 scope&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Using a multimeter without letting the magic smoke out of the fuse, operating a variable DC power supply and a function generator, building circuits on breadboards, voltage divider circuits, and proper oscilloscope usage. Ends with a discussion of uncertainty and propagation thereof.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-2-input-and-output-impedance-pdf&quot;&gt;Lab 2 - Input and Output Impedance &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_02_Impedance.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab2_thevenin.svg&quot; alt=&quot;Lab 2 thevenin&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Measure the input impedance of a voltmeter and an oscilloscope, and observe the effects of measurement equipment loading a circuit. Analyze resistive and capacitive voltage dividers. Measure the output impedance of a function generator.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-3-rc-circuits-pdf&quot;&gt;Lab 3 - RC Circuits &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_03_RC_Circuits.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab3_impedance.svg&quot; alt=&quot;Lab 3 impedance&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Measure the time constant of charging and discharging RC circuits. Derive RC circuit voltages using complex analysis and use this to build integrator and differentiator circuits with outputs accurate to within 1%. I created a &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=nAsnk1Yj4u8&quot;&gt;YouTube video&lt;&#x2F;a&gt; showing an example derivation.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-4-thevenin-equivalence-and-filters-pdf&quot;&gt;Lab 4 - Thevenin Equivalence and Filters &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_04_Equiv_Circuits_and_Filters.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab4_notch.svg&quot; alt=&quot;Lab 4 notch&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Analyze a resistor circuit using Thevenin’s Theorem, construct low and high pass RC filters, create Bode plots from amplitude data, build LC bandpass and notch filters and use an oscilloscope as a simple spectrum analyzer.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-5-wave-behavior-and-filter-design-pdf&quot;&gt;Lab 5 - Wave Behavior and Filter Design &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_05_Wave_Behavior_and_Filter_Design.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab5_fft.svg&quot; alt=&quot;Lab 5 fft&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Use a bandpass filter to pull out the Fourier components of a square wave. Design and construct a two stage RC and a third order Butterworth low pass filter.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-6-filters-diodes-and-power-supplies-pdf&quot;&gt;Lab 6 - Filters, Diodes, and Power Supplies &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_06_Filters_Diodes_and_PSUs.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab6_bridge.svg&quot; alt=&quot;Lab 6 bridge&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Combine three sine waves of different frequencies and design three filters to pull each one back out. Compare small signal, Schottky and Zener in a half wave rectifier. Build a variable regulated DC power supply.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-7-ltspice-simulations-and-iv-curves-pdf&quot;&gt;Lab 7 - LTSpice Simulations and IV Curves &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_07_Simulations_and_IV_Curves.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab7_IV.svg&quot; alt=&quot;Lab 7 IV&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Use LTSpice to simulate a voltage divider, a high pass filter in the time domain, create a Bode plot, and the IV curve for a diode. Measure the real life current and voltage values for a diode and create an IV plot.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-8-transistors-i-pdf&quot;&gt;Lab 8 - Transistors I &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_08_Transistors_I.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab8_transistor.svg&quot; alt=&quot;Lab 8 transistor&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Measure collector and base current and base-emitter voltage for NPN and PNP transistors. Create plots of collector current as a function of base-emitter voltage, and collector current as a function of base current. Design and construct a “night light” circuit which turns on an LED when the ambient lights gets dim.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-9-transistors-ii-pdf&quot;&gt;Lab 9 - Transistors II &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_09_Transistors_II.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab9_follower.svg&quot; alt=&quot;Lab 9 follower&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Construct an unbiased emitter follower. Design and construct a biased emitter follower. Construct an LC oscillator and connect to the biased emitter follow to demonstrate its use as a buffer amplifier and impedance transformer. Use the oscilloscope to build a curve tracer and examine the curves for diodes, resistors, capacitors, inductors and transistors.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-10-operational-amplifiers-pdf&quot;&gt;Lab 10 - Operational Amplifiers &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_10_OpAmps.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab10_opamp.svg&quot; alt=&quot;Lab 10 opamp&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Construct an inverting amplifier and a summing amplifier. Build a closed loop PID constant illumination controller using eight opamps and a phototransistor.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-11-digital-logic-and-audio-synths-pdf&quot;&gt;Lab 11 - Digital Logic and Audio Synths &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_11_Digital_Logic_and_Synths.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab11_or.svg&quot; alt=&quot;Lab 11 or&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Construct NAND, NOT, AND, OR and NOR gates using NAND logic with LEDs showing the state of each gate. Using 555 timer ICs construct astable and monostable multivibrators. Link these together to form a simple audio synthesizer, often called the Atari Punk Console.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-12-microcontrollers-i-pdf&quot;&gt;Lab 12 - Microcontrollers I &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_12_Microcontrollers_I.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab12_unoboard.png&quot; alt=&quot;Lab 12 unoboard&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Set up the Arduino IDE and upload code to a SparkFun Redboard. Use the microcontroller to control an RGB LED. Read in values from a flex sensor and set the position of a servo accordingly. Control a relay via a transistor and use a scope to show why a flyback diode is needed. Display text on an LCD.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;lab-entry&quot;&gt;
&lt;h4 id=&quot;lab-13-microcontrollers-ii-pdf-code&quot;&gt;Lab 13 - Microcontrollers II &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;PHYS_3098_Lab_13_Microcontrollers_II.pdf&quot;&gt;(pdf)&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab13code.zip&quot;&gt;(code)&lt;&#x2F;a&gt;&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;circuits-lab&#x2F;lab13_dac.svg&quot; alt=&quot;Lab 13 dac&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Decode the signal from an IR remote. Build a 5-bit R-2R digital to analog converter (DAC). Hook up the DAC to a common collector amplifier and a speaker. Synthesize sine waves corresponding to musical notes using direct digital synthesis (DDS). Use the remote control to select various frequencies, creating a remote controlled piano.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Teaching an old scope new tricks.</title>
        <published>2016-11-19T00:00:00+00:00</published>
        <updated>2016-11-19T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/scope-mod/"/>
        <id>https://chrisgreenley.com/projects/scope-mod/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/scope-mod/">&lt;p&gt;I have an old analog Tektronix oscilloscope I use for my home projects which has served me quite well over the last couple years. One gripe I do have is that when I switch my probe to x10 attenuation mode, the backlight on the voltage dial shows the wrong number. If I was using the proper Tek probes there would be a little pin which would connect to the metal ring around the BNC connector on the scope. This would tell the scope when I switched in to x10 mode and switch the backlight position on the voltage dial. This would be especially useful for when I take pictures of the scope display for future reference and I need the numbers in the picture to be correct, not off by a factor of 10.&lt;&#x2F;p&gt;
&lt;p&gt;As a poor grad student, I’m not going to go spend money on new probes just for this minor feature. Instead I decided to install a switch for each channel which would allow me to manually change the voltage backlight as needed.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;scope_front.jpg&quot; alt=&quot;Scope front view&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Of course the first thing to do is dive into the data sheet and figure out how the lights are connected. Fortunately the data sheets are all available online. Since this scope is modular, there is a data sheet for each of the three modules and a overall data sheet for the scope body. I have two different amplifier modules, but the lights work the same in each. &lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;TEK_5A15N.pdf&quot;&gt;Here is the data sheet for the simpler one, the 5A15N.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I found a promising looking section of the schematic (shown below, red additions mine) which noted “with probe”. When I looked up DS191 and DS192 I found that they are indeed neon indicator lamps.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;tek_schematic_mod.png&quot; alt=&quot;Schematic mod&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;tek_lamps.png&quot; alt=&quot;Parts list&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The base of transistor Q191 is normally pulled high by R190. This both connects the x1 light to GND (completing its circuit) and keeps the base of the transistor Q192 low and thus the x10 light off. So I just need to make a switch to pull the base of Q191 low which will result in the base of Q192 being pulled high (by the 1 MOhm resistor) thus completing the circuit for the x10 light. From some other reading I did it seems like the Tek probes connected the base of Q182 to ground through a ~10k resistor, it would probably be fine either way, but I’ll go ahead and use the resistor. With the 150k and the 10k resistors forming a voltage divider when the switch is closed, the base of the transistor would be at (5*10)&#x2F;(150+10) = 0.3 volts which is easily low enough to turn off Q191.&lt;&#x2F;p&gt;
&lt;p&gt;The modifications are shown on the schematic in red.&lt;&#x2F;p&gt;
&lt;p&gt;Once I settled on using a latching pushbutton style switch scavenged from an old receiver, I set about performing the requisite physical modifications. Namely, mounting the switch and drilling a hole in the front of the case. As is shown in the left image below, I went for the very high tech piece-of-wood-glued-in-place option.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;mount_and_hole.jpg&quot; alt=&quot;Switch mount and hole&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In the wiring shown below, the black wire connects to ground and the resistor lead (covered in red heat shrink tubing) connects to the wire going out to the x10 sense ring. When the switch is clicked out the two legs are internally disconnected and when it is clicked in the legs are internally connected.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;wiring.jpg&quot; alt=&quot;Wiring&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And now I have a nice easy way to switch the scope between x1 and x10 mode.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;x1_x10.jpg&quot; alt=&quot;x1 and x10 result&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Since I have a 3D printer I can’t help but model up some switch covers in Fusion 360 and give the whole project just a little extra finesse.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;scope-mod&#x2F;covers.jpg&quot; alt=&quot;Covers&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’m quite pleased with the final result and it will make using the scope rather more convenient.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Voltaic Pile</title>
        <published>2016-10-15T00:00:00+00:00</published>
        <updated>2016-10-15T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/voltaic-pile/"/>
        <id>https://chrisgreenley.com/projects/voltaic-pile/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/voltaic-pile/">&lt;p&gt;I found the available information to be sadly lacking in data and graphs and so proceeded to do my part to rectify the situation. The details are in &lt;a href=&quot;https:&#x2F;&#x2F;www.instructables.com&#x2F;id&#x2F;Building-and-Testing-a-Penny-Battery&#x2F;&quot;&gt;the Instructable I wrote&lt;&#x2F;a&gt;, but here is a brief summary.&lt;&#x2F;p&gt;
&lt;p&gt;For a voltaic battery constructed out of copper and zinc cells the voltage per cell is between 0.9 and 1.0 volts. The internal resistance (with a salt and vinegar electrolyte solution) is about 640 Ohms per cell.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;voltaic-pile&#x2F;graph.jpg&quot; alt=&quot;Internal Resistance Graph&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The graph shows how I determined the internal resistance for various situations. Knowing the voltage and the internal resistance, I calculated that it would take 710 batteries each containing 10 cells, where each cell consists of two pennies, to build a battery capable of putting out 5 volts at 0.5 amps, that is, a mediocre phone charger.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;That comes out to $142 worth of pennies.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Lists of Top 100 Books</title>
        <published>2016-09-24T00:00:00+00:00</published>
        <updated>2016-09-24T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/booklists/"/>
        <id>https://chrisgreenley.com/projects/booklists/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/booklists/">&lt;p&gt;I love reading and I’ve often thought of working my way through reading every book on a “top books” list. But which list to choose? What books show up consistently? What authors are always represented but with different works? How representative are book lists with regards to historical works?&lt;&#x2F;p&gt;
&lt;p&gt;I was thinking about these questions at the same time as I was interested in learning more about bash programming and AWK. This project is the result.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;which-book-lists-to-choose&quot;&gt;Which Book Lists to Choose?&lt;&#x2F;h4&gt;
&lt;p&gt;I decided to stick to top 100 lists and to avoid specific lists like “Top 100 scifi novels.” Some of the following are chosen by voting readers and some by literary experts.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.bbc.co.uk&#x2F;arts&#x2F;bigread&#x2F;top100.shtml&quot;&gt;BBC’s Big Read Top 100&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;thegreatestbooks.org&#x2F;lists&#x2F;107&quot;&gt;The 100 Favorite Novels of Librarians&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.goodreads.com&#x2F;list&#x2F;show&#x2F;13086.Goodreads_Top_100_Literary_Novels_of_All_Time&quot;&gt;Goodreads Top 100 Literary Novels of All Time&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.harvard.com&#x2F;shelves&#x2F;top100&#x2F;&quot;&gt;Harvard Book Store Top 100 Books&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.modernlibrary.com&#x2F;top-100&#x2F;100-best-novels&#x2F;&quot;&gt;Modern Library 100 Best Novels (Board’s list and Reader’s list)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.npr.org&#x2F;templates&#x2F;story&#x2F;story.php?storyId=106983620&quot;&gt;NPR 100 Best Beach Books&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;data-wrangling&quot;&gt;Data Wrangling&lt;&#x2F;h4&gt;
&lt;p&gt;I downloaded the raw HTML for each page and wrote AWK scripts to pull out the title and author.&lt;&#x2F;p&gt;
&lt;p&gt;An example of the HTML, in this case from GoodReads.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;  &amp;lt;tr itemscope itemtype=&amp;quot;http:&amp;#x2F;&amp;#x2F;schema.org&amp;#x2F;Book&amp;quot;&amp;gt;
      &amp;lt;td valign=&amp;quot;top&amp;quot; class=&amp;quot;number&amp;quot;&amp;gt;1&amp;lt;&amp;#x2F;td&amp;gt;
    &amp;lt;td width=&amp;quot;5%&amp;quot; valign=&amp;quot;top&amp;quot;&amp;gt;
      &amp;lt;a name=&amp;quot;1885&amp;quot;&amp;gt;&amp;lt;&amp;#x2F;a&amp;gt;
      &amp;lt;a href=&amp;quot;&amp;#x2F;book&amp;#x2F;show&amp;#x2F;1885.Pride_and_Prejudice&amp;quot; title=&amp;quot;Pride and Prejudice&amp;quot;&amp;gt;
          &amp;lt;img alt=&amp;quot;Pride and Prejudice&amp;quot; class=&amp;quot;bookSmallImg&amp;quot; src=&amp;quot;http:&amp;#x2F;&amp;#x2F;d202m5krfqbpi5.cloudfront.net&amp;#x2F;books&amp;#x2F;1320399351s&amp;#x2F;1885.jpg&amp;quot; &amp;#x2F;&amp;gt;
&amp;lt;&amp;#x2F;a&amp;gt;    &amp;lt;&amp;#x2F;td&amp;gt;
    &amp;lt;td width=&amp;quot;100%&amp;quot; valign=&amp;quot;top&amp;quot;&amp;gt;
      &amp;lt;a href=&amp;quot;&amp;#x2F;book&amp;#x2F;show&amp;#x2F;1885.Pride_and_Prejudice&amp;quot; class=&amp;quot;bookTitle&amp;quot; itemprop=&amp;quot;url&amp;quot;&amp;gt;
        &amp;lt;span itemprop=&amp;#x27;name&amp;#x27;&amp;gt;Pride and Prejudice&amp;lt;&amp;#x2F;span&amp;gt;
&amp;lt;&amp;#x2F;a&amp;gt;      &amp;lt;br&amp;#x2F;&amp;gt;
        &amp;lt;span class=&amp;#x27;by smallText&amp;#x27;&amp;gt;by&amp;lt;&amp;#x2F;span&amp;gt;
&amp;lt;span itemprop=&amp;#x27;author&amp;#x27; itemscope=&amp;#x27;&amp;#x27; itemtype=&amp;#x27;http:&amp;#x2F;&amp;#x2F;schema.org&amp;#x2F;Person&amp;#x27;&amp;gt;
&amp;lt;a href=&amp;quot;http:&amp;#x2F;&amp;#x2F;www.goodreads.com&amp;#x2F;author&amp;#x2F;show&amp;#x2F;1265.Jane_Austen&amp;quot; class=&amp;quot;authorName&amp;quot; itemprop=&amp;quot;url&amp;quot;&amp;gt;&amp;lt;span itemprop=&amp;quot;name&amp;quot;&amp;gt;Jane Austen&amp;lt;&amp;#x2F;span&amp;gt;&amp;lt;&amp;#x2F;a&amp;gt;
&amp;lt;&amp;#x2F;span&amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The following AWK script was used to pull out the title and author and write that information to a tab delimited file.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;awk&quot; class=&quot;language-awk &quot;&gt;&lt;code class=&quot;language-awk&quot; data-lang=&quot;awk&quot;&gt;#!&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;awk
#awk -f extract&amp;lt;name&amp;gt;.awk -f functionLibrary.awk &amp;lt;name&amp;gt;.html
#Source: http:&amp;#x2F;&amp;#x2F;www.goodreads.com&amp;#x2F;list&amp;#x2F;show&amp;#x2F;13086.Goodreads_Top_100_Literary_Novels
BEGIN{
FS=&amp;quot;&amp;lt;[^&amp;gt;]+&amp;gt;&amp;quot;;
OFS=&amp;quot;\t&amp;quot;;
lineCount=100;
rankIterator=0;
}

FNR==1{
split(FILENAME,fileNameArray,&amp;quot;.&amp;quot;);
outputFile = fileNameArray[1]&amp;quot;.table&amp;quot;; 
}

&amp;#x2F;itemprop=&amp;#x27;name&amp;#x27;&amp;#x2F;{
title=$2
#print FNR,title
}

&amp;#x2F;class=&amp;quot;authorName&amp;quot;&amp;#x2F;{
author=$3
#print FNR,author
currentLine=FNR
}
FNR==currentLine{
numNames=split(author,authorNameArray,&amp;quot; &amp;quot;);
firstNames=join(authorNameArray,1,numNames-1);
#rank=weightBook(lineCount,rankIterator++);
print title,authorNameArray[numNames],firstNames &amp;gt; outputFile;
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The result was a file for each list with the book title and the authors name on each line (family name followed by given name).&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Pride and Prejudice Austen  Jane
1984  Orwell  George
The Great Gatsby  Fitzgerald  F. Scott
Jane Eyre Brontë  Charlotte
Crime and Punishment  Dostoyevsky Fyodor
Lolita  Nabokov Vladimir
The Adventures of Huckleberry Finn  Twain Mark
Of Mice and Men Steinbeck John
Wuthering Heights Brontë  Emily
Brave New World Huxley  Aldous
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To merge the lists I used another AWK script, this time called from a bash script. This script also counted up the number of times the book shows up in a list and which lists it showed up in.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;#!&amp;#x2F;bin&amp;#x2F;bash
#usage: .&amp;#x2F;tabulateBooks.sh

awk -f tabulateBooks.awk \
..&amp;#x2F;rawData&amp;#x2F;bookman_librarians.table \
..&amp;#x2F;rawData&amp;#x2F;bbc_the_big_read.table \
..&amp;#x2F;rawData&amp;#x2F;modern_library_readers.table \
..&amp;#x2F;rawData&amp;#x2F;modern_library_board.table \
..&amp;#x2F;rawData&amp;#x2F;npr_beach_books.table \
..&amp;#x2F;rawData&amp;#x2F;good_reads.table \
..&amp;#x2F;rawData&amp;#x2F;harvard_bookstore.table \
| sort -t&amp;quot;,&amp;quot; -k1.2,1gr -k2.2,2gr &amp;gt; tabulatedBooks.csv
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;awk&quot; class=&quot;language-awk &quot;&gt;&lt;code class=&quot;language-awk&quot; data-lang=&quot;awk&quot;&gt;BEGIN{
  FS=&amp;quot;\t&amp;quot;; #Input delimiter
  OFS=&amp;quot;,&amp;quot;; #Output delimiter
  lineCount=100 #Number of items in the longest list
}

{ #for every line in each file
title=toupper($1)
authorLast=$2
authorFirst=$3
rank=lineCount-(FNR-1)
#bookArray[title]+=rank
NumOfListsArray[title]+=1
FirstNameArray[title]=authorFirst
LastNameArray[title]=authorLast
#Pull out filename sans extension 
len = split(FILENAME,N1,&amp;quot;&amp;#x2F;&amp;quot;)
split(N1[len],N2,&amp;quot;.&amp;quot;)
listName = N2[1]
#Append which lists the book appears in and the rank it has in that list
FileNameArray[title]=(FileNameArray[title] &amp;quot;\&amp;quot;,\&amp;quot;&amp;quot; listName &amp;quot;\&amp;quot;,\&amp;quot;&amp;quot; rank)
totalRankArray[title]=(totalRankArray[title] + rank)
}



END{
#print &amp;quot;-------------------&amp;quot;
  for(title in FirstNameArray) {
      gsub(&amp;#x2F;^\&amp;quot;,\&amp;quot;&amp;#x2F;,&amp;quot;&amp;quot;,FileNameArray[title]) #Remove extra delimter 
        # Number of lists book is in,total rank, book title, author first name, last name, lists in which book appears and rank in that list
          print &amp;quot;\&amp;quot;&amp;quot;NumOfListsArray[title]&amp;quot;\&amp;quot;&amp;quot;,&amp;quot;\&amp;quot;&amp;quot;totalRankArray[title]&amp;quot;\&amp;quot;&amp;quot;,&amp;quot;\&amp;quot;&amp;quot;title&amp;quot;\&amp;quot;&amp;quot;,&amp;quot;\&amp;quot;&amp;quot;FirstNameArray[title]&amp;quot;\&amp;quot;&amp;quot;,&amp;quot;\&amp;quot;&amp;quot;LastNameArray[title]&amp;quot;\&amp;quot;&amp;quot;,&amp;quot;\&amp;quot;&amp;quot;FileNameArray[title]&amp;quot;\&amp;quot;&amp;quot;
            }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Of course some duplicates slipped through since some lists had typos, title variations, etc… Here I gave in and used python to search through the merged list for likely duplicates by calculating the &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Jaccard_index&quot;&gt;Jaccard Index&lt;&#x2F;a&gt; for every combination of books in the list. Once I found the duplicates I corrected the errant names in the relevant source files and recreated the merged list.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;python&quot; class=&quot;language-python &quot;&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;#!&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;python

import sys

#####################################

def jaccard_index(set_1,set_2):
        n = len(set_1.intersection(set_2))
        return n &amp;#x2F; float(len(set_1) + len(set_2) - n)

#####################################

fileName=str(sys.argv[1])

shingleLen=4

with open(fileName,&amp;#x27;r&amp;#x27;) as f:

  data = f.readlines()

  lineNumA=0
  for line in data:
    line=line.rstrip(&amp;#x27;\n&amp;#x27;)
    primaryShingle=[line[i:i + shingleLen] for i in range(len(line) - shingleLen + 1)]
    primarySet=set(primaryShingle)

    lineNumB=0
    for ll in data:
      if lineNumA &amp;lt; lineNumB:
        ll=ll.rstrip(&amp;#x27;\n&amp;#x27;)
        secondaryShingle=[ll[k:k + shingleLen] for k in range(len(ll) - shingleLen + 1)]
        secondarySet=set(secondaryShingle)

        jac = jaccard_index(primarySet,secondarySet)
        if jac &amp;gt; .35:
          print &amp;quot;%.3f\n%s\n%s&amp;quot;%(jac,line,ll)
          print &amp;quot;-----------&amp;quot;
      #print lineNumB
      lineNumB += 1
    #print lineNumA
    lineNumA += 1
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This left me with a list of 443 unique books. The next step was to determine the year each book was published. Using curl I ran the title and author as search terms through Google and WolframAlpha and was able to pull out the publishing date for nearly every book. The last few I filled in by hand.&lt;&#x2F;p&gt;
&lt;p&gt;This script looks to see if the date is already known (since I ran this quite a few times) and if it isn’t, tries to determine the date using Google and then WolframAlpha.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;#!&amp;#x2F;bin&amp;#x2F;bash
#Usage: .&amp;#x2F;tabulateBooks.sh tabulatedBooks.csv
: &amp;gt; tempdatesfile #Create empty file
cat $1 |  
while read line; do #for each line in the file do the following
  #The FPAT variable keeps commas inside quotes from being field seperators
  TA=$(echo &amp;quot;$line&amp;quot; | awk -vFPAT=&amp;#x27;([^,]*)|(&amp;quot;[^&amp;quot;]+&amp;quot;)&amp;#x27; -vOFS=+ \
    &amp;#x27;{print $3,$5}&amp;#x27;) #Title and author&amp;#x27;s last name
  Stitle=$(echo &amp;quot;$TA&amp;quot; | cut -d&amp;quot;+&amp;quot; -f1) #cut out just the title
  #Now remake $title and $TA with the &amp;#x27;+&amp;#x27; delimiter replacing spaces
  TA=$(echo $TA | awk -F&amp;#x27;[ ]&amp;#x27; &amp;#x27;BEGIN{OFS=&amp;quot;+&amp;quot;;} {$1=$1;print;}&amp;#x27;)
  title=$(echo $Stitle | awk -F&amp;#x27;[ ]&amp;#x27; &amp;#x27;BEGIN{OFS=&amp;quot;+&amp;quot;;} {$1=$1;print;}&amp;#x27;)


  sanitizedTitle=$( echo ${Stitle&amp;#x2F;&amp;#x2F;\(&amp;#x2F;\\\(} ) #Replaces ( with \(
  sanitizedTitle=$( echo ${sanitizedTitle&amp;#x2F;&amp;#x2F;\)&amp;#x2F;\\)} ) #Replaces ) with \)

  #Look for known dates in file (use perl regex because it works)
  year=$(cat tabulatedBooks_withDates.csv | grep -iP \
    &amp;quot;$sanitizedTitle\,\&amp;quot;([0-9]|\-[0-9])&amp;quot; \
    | awk -vFPAT=&amp;#x27;([^,]*)|(&amp;quot;[^&amp;quot;]+&amp;quot;)&amp;#x27; &amp;#x27;{gsub(&amp;quot;\&amp;quot;&amp;quot;,&amp;quot;&amp;quot;,$4); print $4}&amp;#x27; )

  #Check if year is in the range 1000 to 2999 CE
  if (echo $year | grep -Eq &amp;#x27;^[1-2][0-9][0-9][0-9]$&amp;#x27;)
  then
    printf &amp;#x27;Known year:%s\n&amp;#x27; &amp;quot;$year&amp;quot;
    printf &amp;#x27;\&amp;quot;%s\&amp;quot;,%s\n&amp;#x27; &amp;quot;$year&amp;quot; &amp;quot;$line&amp;quot; &amp;gt;&amp;gt; tempdatesfile
    continue
  fi

  #Check if year is in the range 0 to 999 CE
  if (echo $year | grep -Eq &amp;#x27;^[0-9][0-9][0-9]$&amp;#x27;)
  then
    printf &amp;#x27;Known year:%s\n&amp;#x27; &amp;quot;$year&amp;quot;
    printf &amp;#x27;\&amp;quot;%s\&amp;quot;,%s\n&amp;#x27; &amp;quot;$year&amp;quot; &amp;quot;$line&amp;quot; &amp;gt;&amp;gt; tempdatesfile
    continue
  fi

  #Check if year is between 0 and 999 BCE
  if (echo $year | grep -Eq &amp;#x27;^\-[0-9][0-9][0-9]$&amp;#x27;)
  then
    printf &amp;#x27;Known year:%s\n&amp;#x27; &amp;quot;$year&amp;quot;
    printf &amp;#x27;\&amp;quot;%s\&amp;quot;,%s\n&amp;#x27; &amp;quot;$year&amp;quot; &amp;quot;$line&amp;quot; &amp;gt;&amp;gt; tempdatesfile
    continue 
  fi

  if !(echo $year | grep -Eq &amp;#x27;^[0-9][0-9][0-9][0-9]$&amp;#x27;)
  then
    echo &amp;quot;Searching for year&amp;quot;
    searchString=$(printf &amp;#x27;%s%s&amp;#x27; &amp;quot;$TA&amp;quot; &amp;quot;+published&amp;quot;)
    year=$(curl --silent --user-agent &amp;quot;Mozilla&amp;#x2F;5.0 (X11; Linux x86_64) AppleWebKit&amp;#x2F;537.36 (KHTML, like Gecko) Chrome&amp;#x2F;27.0.1453.93 Safari&amp;#x2F;537.36&amp;quot; \
    https:&amp;#x2F;&amp;#x2F;www.google.com&amp;#x2F;search?q=$searchString | \
    awk -f scrapeGoogle.awk)
  fi
  
  if !(echo $year | grep -Eq &amp;#x27;^[0-9][0-9][0-9][0-9]$&amp;#x27;)
  then
    echo &amp;quot;Scraping google failed, trying wolframalpha...&amp;quot;
    searchString=$(printf &amp;#x27;%s%s&amp;#x27; &amp;quot;\&amp;quot;$title\&amp;quot;&amp;quot; &amp;quot;+first+publication+date&amp;quot;)
    year=$(curl --silent --user-agent &amp;quot;Mozilla&amp;#x2F;5.0 (X11; Linux x86_64) AppleWebKit&amp;#x2F;537.36 (KHTML, like Gecko) Chrome&amp;#x2F;27.0.1453.93 Safari&amp;#x2F;537.36&amp;quot; \
    http:&amp;#x2F;&amp;#x2F;www.wolframalpha.com&amp;#x2F;input&amp;#x2F;?i=$searchString | \
    awk -f scrapeWolfram.awk)
  fi

  if !(echo $year | grep -Eq &amp;#x27;^[0-9][0-9][0-9][0-9]$&amp;#x27;)
  then
    #make sure $year is empty if it isn&amp;#x27;t a 4 digit number  
    year=&amp;quot;&amp;quot;
    echo &amp;quot;FAILED: $Stitle&amp;quot;
  fi

  printf &amp;#x27;\&amp;quot;%s\&amp;quot;,%s\n&amp;#x27; &amp;quot;$year&amp;quot; &amp;quot;$line&amp;quot; &amp;gt;&amp;gt; tempdatesfile
  
done
: &amp;gt; tempdatesfile2
awk -vOFS=&amp;quot;,&amp;quot; -vFPAT=&amp;#x27;([^,]*)|(&amp;quot;[^&amp;quot;]+&amp;quot;)&amp;#x27; \
  &amp;#x27;{t=$1; $1=$2; $2=$3; $3=$4; $4=t; gsub(&amp;quot;,,&amp;quot;,&amp;quot;,&amp;quot;); \
  print &amp;gt;&amp;gt; &amp;quot;tempdatesfile2&amp;quot;}&amp;#x27; tempdatesfile
rm tempdatesfile
mv tabulatedBooks_withDates.csv tabulatedBooks_withDates.csv.backup 
mv tempdatesfile2 tabulatedBooks_withDates.csv
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;These are the helper AWK scripts to interpret the downloaded HTML.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;awk&quot; class=&quot;language-awk &quot;&gt;&lt;code class=&quot;language-awk&quot; data-lang=&quot;awk&quot;&gt;#For google results
#!&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;awk
BEGIN{
RS=&amp;quot;&amp;lt;[^&amp;gt;]+&amp;gt;&amp;quot;; #Make each HTML tag a row divider
OFS=&amp;quot;+&amp;quot;;
ORS=&amp;quot;&amp;quot;;
}

&amp;#x2F;Originally published&amp;#x2F;{
if ($0==&amp;quot;Originally published&amp;quot;) #Did it work?
  pubNR=NR #Remember row number
else
  next #No luck, proceed to next book
}


NR==pubNR+3{ #Date will be 3 rows after the &amp;#x27;originally published&amp;#x27; text
dateArrayLength=split($0,dateArray,&amp;quot; &amp;quot;)
year=dateArray[dateArrayLength] #Pull out just the year
print year
}

&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre data-lang=&quot;awk&quot; class=&quot;language-awk &quot;&gt;&lt;code class=&quot;language-awk&quot; data-lang=&quot;awk&quot;&gt;#For wolframalpha results
#!&amp;#x2F;usr&amp;#x2F;bin&amp;#x2F;awk
BEGIN{
FS=&amp;quot;\&amp;quot;&amp;quot;;
OFS=&amp;quot;&amp;quot;;
}

&amp;#x2F;stringified&amp;#x2F;{
c++;
if(c==2){
  dateArrayLength=split($4,date,&amp;quot; &amp;quot;)
  year=date[dateArrayLength]
  print year
  }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point I have a delimited file containing the book title, author name, publishing date, number of lists the book appears in, and which lists those are and the rank in each list..&lt;&#x2F;p&gt;
&lt;h4 id=&quot;graphs-n-things&quot;&gt;Graphs ’n Things&lt;&#x2F;h4&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Joule Thief</title>
        <published>2016-07-25T00:00:00+00:00</published>
        <updated>2016-07-25T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/projects/joule-thief/"/>
        <id>https://chrisgreenley.com/projects/joule-thief/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/projects/joule-thief/">&lt;p&gt;The Joule Thief is a fantastic intro to analog electronic circuits. The idea of a self-oscillating voltage booster is very old, but the current internet popularity (and name) is due to &lt;a href=&quot;http:&#x2F;&#x2F;www.bigclive.com&#x2F;joule.htm&quot;&gt;Clive Mitchel&lt;&#x2F;a&gt;. I taught an intro to circuits workshop at my local makerspace using a slightly modified version of his circuit which used a capacitor to smooth the output voltage. The makes a great project for people to take home at the end of a workshop and covers soldering, resistors, diodes, LEDs, inductors, induced current, back emf, capacitors, and persistence of vision.&lt;&#x2F;p&gt;
&lt;p&gt;Below is my prototype for the class, the only differences are that I added a battery holder and used a slightly different switch.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;joule-thief&#x2F;JT_photo.jpg&quot; alt=&quot;Joule Thief photo&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to be able to introduce all the basic circuit components and the standard circuit doesn’t have any capacitors. Adding in a small capacitor (and diode to force it to discharge through the LED) had the added benefit of keeping the LED on constantly instead of flashing at a few tens of kilohertz with an approximately 50% duty cycle. While the increase in brightness wasn’t huge, it was certainly noticeable.&lt;&#x2F;p&gt;
&lt;p&gt;Note: The two inductors in the schematic are a single double wound toroid. I suggest doubling up and pulling through two wires at once. Normal wire works fine, but magnet wire is nice since the insulation is so thin. The easiest mistake is to solder together the two wires at either the beginning or end of the coil. What should happen is that one wire from the beginning of the coil and the OTHER wire from the end of the coil should be connected together and then to the positive battery terminal. Those are the two I have going through the board in the center of the toroid and then connecting to the switch, which then connects to the positive terminal.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;joule-thief&#x2F;JT_Schem.svg&quot; alt=&quot;Schematic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I also wanted the circuit to build into a useful form factor. Both so that members could practice soldering and so they would have more than a breadboard circuit to take home at the end. I thought about getting a PCB made, but I wanted it to feel less like a kit and more like a DIY project.&lt;&#x2F;p&gt;
&lt;p&gt;I sketched up an outline in Inkscape and cut some thin plywood to the proper size. Before the class I drilled all the holes in the wood and printed out a few pages of the outline. In class, once each person had their circuit tested on a breadboard, they glued the outline to the wood and placed all the parts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;joule-thief&#x2F;frame_outline.pdf&quot;&gt;Download PDF to print outlines&lt;&#x2F;a&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;joule-thief&#x2F;frame_outline.svg&quot;&gt;Download SVG to edit outlines&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;joule-thief&#x2F;DiagramAndWood.jpg&quot; alt=&quot;Diagram and wood&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If wires are left long enough, most of the connections can be made without and extra jumper wire.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;chrisgreenley.com&#x2F;projects&#x2F;joule-thief&#x2F;BackSoldering.jpg&quot; alt=&quot;Back soldering&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The circuit will run for a few days off of a “dead” AA battery and a few weeks off a new one.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using GPG</title>
        <published>2015-05-05T00:00:00+00:00</published>
        <updated>2015-05-05T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://chrisgreenley.com/blog/gpg/"/>
        <id>https://chrisgreenley.com/blog/gpg/</id>
        
        <content type="html" xml:base="https://chrisgreenley.com/blog/gpg/">&lt;p&gt;There is a great deal of info out there about PGP and GPG but I could never find what I wanted summarized nicely in one place. This is my attempt to do that.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;process-outline&quot;&gt;PROCESS OUTLINE&lt;&#x2F;h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create PGP public&#x2F;private key pair.&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Backup public key, private key, and emergency private key revocation certificate in a SECURE location.&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Upload public key to keyserver&lt;&#x2F;strong&gt; (e.g. pgp.mit.edu)&lt;br &#x2F;&gt;
(Keyservers share keys between each other so it
is only necessary to upload to one server.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Download the public keys you wish to sign.&lt;&#x2F;strong&gt;
Signing is a confirmation that a public key (user ID&#x2F;email) belongs to the person it claims to belong to and that you believe that the private key associated with that public key is safely kept by that person. THIS IS NOT SOMETHING TO BE DONE LIGHTLY. THIS IS HOW A WEB OF TRUST IS BUILT.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;CONFIRM THE KEY FINGERPRINT IN A SECURE WAY&lt;&#x2F;strong&gt; (e.g. in person)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Sign the user IDs that you trust, you can sign any subset of a user’s IDs.&lt;&#x2F;strong&gt; (Many IDs (emails, images) can be associated with one public key.)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Upload the keys you’ve signed back to the keyserver.&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Optional: You can also ‘trust’ a key&#x2F;ID. Signing a key is a assertion of the validity of the key, that is, the person  with the associated private key is who they claim to be. Trusting a key is local only (trust values don’t get uploaded to a keyserver) and is an assertion of how much you believe you can trust other keys signed by that person. GPG will by default trust a key if it is signed by a key you ‘fully’ trust or by three keys you ‘partially’ trust. Only ‘ultimately’ trust yourself.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Test sending and receiving messages&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note these commands work on Mac and Linux with both gpg and gpg2 (as far as I am aware).&lt;&#x2F;p&gt;
&lt;h4 id=&quot;creating-a-pgp-key&quot;&gt;CREATING A PGP KEY&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;gpg2 --gen-key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use 4096 RSA (1)&lt;&#x2F;li&gt;
&lt;li&gt;Expiration date (less than 2 years is recommended, this date can be extended later and acts as a dead man switch)&lt;&#x2F;li&gt;
&lt;li&gt;Enter (real) full name&lt;&#x2F;li&gt;
&lt;li&gt;Enter real email address (advisable to use dedicated)&lt;&#x2F;li&gt;
&lt;li&gt;Enter optional comment (shown along with key name to others)&lt;&#x2F;li&gt;
&lt;li&gt;Enter a passphrase (length at least 12, memorable)(Can be changed)
&lt;ul&gt;
&lt;li&gt;A long passphrase of over 6 words from more than one language will be very resistant to brute force and dictionary attacks but can be made easy to type and remember.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;backup-your-private-and-public-key&quot;&gt;BACKUP YOUR PRIVATE AND PUBLIC KEY&lt;&#x2F;h4&gt;
&lt;p&gt;Export public key&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --export -a &quot;User Name&quot; &amp;gt; user.pub.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Export private key&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --export-secret-key -a &quot;User Name&quot; &amp;gt; user.priv.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Create emergency revocation certificate. This is the ONLY way to revoke your public key if you lose access to your private key.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --output user.rev.asc --gen-revoke key_user_name&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;BACK THESE FILES UP IN A SECURE LOCATION (SUCH AS CD&#x2F;PAPER IN A BANK VAULT, ENCRYPTED DRIVE, ETC…)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;manage-and-edit-keys&quot;&gt;MANAGE AND EDIT KEYS&lt;&#x2F;h4&gt;
&lt;p&gt;List available public keys&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --list-keys&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;List available private keys&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --list-secret-keys&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Display fingerprint of all public keys (Can be combined with &lt;code&gt;--list-secret-keys&lt;&#x2F;code&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --fingerprint&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Edit a key&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --edit-key key_user_name&#x2F;email&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Type “help” to see list of commands in edit mode&lt;&#x2F;p&gt;
&lt;h4 id=&quot;exchanging-signing-and-trusting-keys&quot;&gt;EXCHANGING, SIGNING AND TRUSTING KEYS&lt;&#x2F;h4&gt;
&lt;h6 id=&quot;upload-public-key-to-keyserver&quot;&gt;Upload public key to keyserver&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;em&gt;This can NEVER be deleted, only revoked, so don’t clutter the server.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --keyserver pgp.mit.edu --send-keys KEY_ID&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h6 id=&quot;download-public-key-from-keyserver&quot;&gt;Download public key from keyserver&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;code&gt;gpg2 --keyserver pgp.mit.edu --recv-keys KEY_ID&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h6 id=&quot;sign-validate-user-id-aka-public-key-signing&quot;&gt;Sign&#x2F;validate user ID (AKA public key signing)&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;code&gt;gpg2 --sign-key key_user_name&#x2F;email&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Will ask to confirm which user IDs to sign. You can sign specific or all user IDs.&lt;&#x2F;p&gt;
&lt;h6 id=&quot;trusting-keys&quot;&gt;Trusting keys&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;code&gt;gpg2 --edit-key key_user_name&#x2F;email&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2&amp;gt; trust&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Default is “I don’t know.”
Level of trust is how much you trust both their understanding of PGP (so they won’t make a mistake) and how much you trust their commitment to properly validating keys. “Ultimate” is a special level reserved for your own key.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;sending-and-receiving&quot;&gt;SENDING AND RECEIVING&lt;&#x2F;h4&gt;
&lt;h6 id=&quot;decrypt-file&quot;&gt;Decrypt file&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;code&gt;gpg2 -d &amp;lt;file_name&amp;gt;&lt;&#x2F;code&gt; (Spits out text to the terminal.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 -d &amp;lt;file_name&amp;gt; &amp;gt; file.txt&lt;&#x2F;code&gt; (Saves text to file.)&lt;&#x2F;p&gt;
&lt;h6 id=&quot;encrypt-file-minimal-example&quot;&gt;Encrypt file (minimal example)&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;code&gt;gpg2 --encrypt -r &quot;recipient_name&quot; &amp;lt;file_name&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h6 id=&quot;encrypt-sign-specify-output-name&quot;&gt;Encrypt, sign, specify output name&lt;&#x2F;h6&gt;
&lt;p&gt;Use -a to export an ASCII file (for copying into a text field and such)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 -es -o &amp;lt;output_filename&amp;gt; -r &quot;Recipient&quot; &amp;lt;file_name&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h6 id=&quot;clearsign&quot;&gt;Clearsign&lt;&#x2F;h6&gt;
&lt;p&gt;Encrypts with your private key so only your public key can be used
to decrypt it (proves it comes from you and that the data hasn’t been tampered with).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --clearsign &amp;lt;file_name&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Use -u &amp;lt;user_name&amp;gt; to specify a non-default private key&lt;&#x2F;p&gt;
&lt;p&gt;Note that the keys&#x2F;IDs of the sender and recipient can be seen by a third party even if they can’t decrypt the message. The flag below can hid the recipient. See the examples section for more discussion.&lt;&#x2F;p&gt;
&lt;h6 id=&quot;to-hide-the-intended-recipient-from-a-third-party&quot;&gt;To hide the intended recipient from a third-party&lt;&#x2F;h6&gt;
&lt;p&gt;&lt;code&gt;gpg2 --hidden-recipient -e -r &quot;recipient_name&quot; &amp;lt;file_name&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;example&quot;&gt;EXAMPLE&lt;&#x2F;h4&gt;
&lt;p&gt;Recently I was playing on online game of Diplomacy (a strategy game like Risk) with friends and we decided to add a crypto element. Below I’ll go through how we communicated using pgp. Of course you don’t have to use gpg or the terminal to do this, but it does give you a better sense of what is going on behind the scenes. We started off by each creating a private&#x2F;public key pair for the country we were playing as. We didn’t want to clutter key servers so all keys were shared via text on a simple message board my friend programmed on his server.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;sharing-public-key&quot;&gt;Sharing public key&lt;&#x2F;h5&gt;
&lt;p&gt;Export public key&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --export -a &quot;User Name&quot; &amp;gt; user.pub.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Make a plain text file with your intro message and sign that file (only need -u option if you have a different default key)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 -u &quot;yourCountry&quot; --clearsign message.txt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This will output a file &lt;code&gt;message.txt.asc&lt;&#x2F;code&gt; which will have the message in clear text and a crypto signature proving the key which was used to sign it&lt;&#x2F;p&gt;
&lt;p&gt;Copy the contents of the &lt;code&gt;message.txt.asc&lt;&#x2F;code&gt; file into a message on the board, and then also copy the contents of the &lt;code&gt;user.pub.key&lt;&#x2F;code&gt; and append to the same message.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;proving-your-identity&quot;&gt;Proving your identity&lt;&#x2F;h5&gt;
&lt;p&gt;The easiest way is to generate a key fingerprint and put that in a message on the vDiplomacy site so we know for sure which key belongs to which country.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --fingerprint&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Outputs something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;pub   1024R&amp;#x2F;DB9C59CF 2015-01-13
      Key fingerprint = E03D 7518 B11E BE5C 82BE 2143 FB43 9F02 DB9C 59CF
uid       [ultimate] Austria 
sub   1024R&amp;#x2F;5CD5DF64 2015-01-13
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Copy that key fingerprint into the vDip message board.&lt;&#x2F;p&gt;
&lt;h5 id=&quot;checking-someone-s-identity&quot;&gt;Checking someone’s identity&lt;&#x2F;h5&gt;
&lt;p&gt;First import their public key. Make a file named country_name.pub.key and copy the key into it (Including the “BEGIN” and “END” lines)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --import country_name.pub.key&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now check the fingerprint&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 --fingerprint&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Lists all keys on your keyring, look for the one you just imported and check the fingerprint against that on the vDip message board.&lt;&#x2F;p&gt;
&lt;p&gt;Check the signature of the message&lt;&#x2F;p&gt;
&lt;p&gt;Copy from the “BEGIN SIGNED MESSAGE” line to the “END SIGNATURE” line into a plain text file: &lt;code&gt;file_name.txt.asc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 -d file_name.txt.asc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You should see an output like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;gpg: Signature made Tue 13 Jan 2015 12:45:02 PM CST using RSA key ID xxxxxxxx                                                         
gpg: Good signature from &amp;quot;user_name&amp;quot; 
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h5 id=&quot;communicating&quot;&gt;Communicating&lt;&#x2F;h5&gt;
&lt;p&gt;To send a signed, encrypted message to a single person, create a plain text file as before with the contents of your message save as something like &lt;code&gt;otherCountry_msg1.txt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 -eas -u &quot;yourCountry&quot; -o otherCountry_msg1.asc -r &quot;otherCountry&quot; otherCountry_msg1.txt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Copy the contents of the newly created file &lt;code&gt;otherCountry_msg1.asc&lt;&#x2F;code&gt; into a message on the board. You can make a note at the top who it is for, or just let everyone try to decrypt it.&lt;&#x2F;p&gt;
&lt;p&gt;To decrypt a message, copy the contents from the BEGIN to END lines into a text file named something like &lt;code&gt;otherCountry_msg2.asc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;gpg2 -d otherCountry_msg2.asc &amp;gt; otherCountry_msg2.txt&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note that if the command above is used, a third party can see who is talking, even if they can’t read the message. For example, a message between Russia and England when tried to be decrypted by Austria gave the following:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;gpg2 -d temp.enc
gpg: encrypted with 1024-bit RSA key, ID D6762868, created 2015-01-15
      &amp;quot;Russia&amp;quot;
gpg: encrypted with 4096-bit RSA key, ID 7D5EF8AB, created 2015-01-12
      &amp;quot;England Anonymous&amp;quot;
gpg: decryption failed: No secret key
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This can be partially remedied by the use of the &lt;code&gt;--hidden-recipient&lt;&#x2F;code&gt; flag, but there doesn’t seem to be a way to hide the sender. Of course the third party would have to have the sender’s and recipient’s public keys for this to work. It also seems to depend some on the software being used, I haven’t quite figured out exactly how.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
