An I/O controller for virtual pinball machines: accelerometer nudge sensing, analog plunger input, button input encoding, LedWiz compatible output controls, and more.

Dependencies:   mbed FastIO FastPWM USBDevice

Fork of Pinscape_Controller by Mike R

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers 74HC595.h Source File

74HC595.h

00001 /* Copyright 2014 M J Roberts, MIT License
00002 *
00003 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00004 * and associated documentation files (the "Software"), to deal in the Software without
00005 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
00006 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
00007 * Software is furnished to do so, subject to the following conditions:
00008 *
00009 * The above copyright notice and this permission notice shall be included in all copies or
00010 * substantial portions of the Software.
00011 *
00012 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00013 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00014 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00015 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00016 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00017 */
00018 
00019 #ifndef HC595_INCLUDED
00020 #define HC595_INCLUDED
00021 
00022 #include "mbed.h"
00023 
00024 // 74HC595 Interface
00025 //
00026 // We require four GPIO pins: 
00027 //
00028 //    sin - serial data
00029 //    sclk - serial clock
00030 //    latch - the LATCH signal, which transfers the internal shift register
00031 //            bits to the physical output pin states
00032 //    ena - the Enable signal
00033 //
00034 // Note that the physical !OE (output enable) pin on the 74HC595 is active-low.
00035 // To allow for orderly startup that guarantees that outputs won't be pulsed
00036 // (even briefly) during power-on, we require the !OE pin to be wired with a
00037 // pull-up resistor to Vcc, and connected to our ENA GPIO pin via an inverter.
00038 //
00039 // Recommended wiring: connect the GPIO pin to the base of an NPN transistor
00040 // through a 2.2K resistor, connect the collector the !OE pin on the 74HC595, 
00041 // and connect the emitter to ground.  This will pull !OE to ground when we
00042 // write a digital 1 to the ENA GPIO, enabling the outputs.
00043 //
00044 // We use simple bit-banging through plain DigitalOut pins to send serial
00045 // data to the chips.  This is fast enough for our purposes, since we send
00046 // only 8 bits per chip on each update (about 4us per chip per update), and
00047 // we only update when we get a command from the PC host that changes an
00048 // output state.  These updates are at USB speed, so the update interval is
00049 // extremely long compared to the bit-banging time.  If we wanted to use
00050 // these chips to implement PWM controlled by the microcontroller, or we
00051 // simply wanted to use a very long daisy-chain, we'd probably have to use 
00052 // a faster transfer mechanism, such as the SPIO controller.
00053 
00054 class HC595
00055 {
00056 public:
00057     HC595(int nchips, PinName sin, PinName sclk, PinName latch, PinName ena) :
00058         nchips(nchips), sin(sin), sclk(sclk), latch(latch), ena(ena)
00059     {
00060         // turn off all pins initially
00061         this->sin = 0;
00062         this->sclk = 0;
00063         this->latch = 0;
00064         this->ena = 0;
00065         
00066         // allocate the state array
00067         state = new char[nchips*8];
00068         memset(state, 0, nchips*8);
00069         dirty = false;
00070     }
00071     
00072     // Initialize.  This must be called once at startup to clear the chips' 
00073     // shift registers.  We clock a 0 bit (OFF state) to each shift register 
00074     // position and latch the OFF states on the outputs.  Note that this
00075     // doesn't enable the chips - that must be done with a separate call
00076     // to enable(true).
00077     void init()
00078     {
00079         // set the internal state of all inputs
00080         memset(state, 0, nchips*8);
00081         dirty = false;
00082         
00083         // clock a 0 to each shift register bit (8 per chip)
00084         sin = 0;
00085         for (int i = 0 ; i < nchips*8 ; ++i)
00086         {
00087             sclk = 1;
00088             sclk = 0;
00089         }
00090         
00091         // latch the output data (this transfers the serial data register
00092         // bit for each pin to the actual output pin)
00093         latch = 1;
00094         latch = 0;
00095     }
00096     
00097     // Set an output state.  This only sets the state internally; call
00098     // update() to apply changes to the physical outputs.
00099     void set(int idx, int val)
00100     {
00101         if (state[idx] != val)
00102         {
00103             state[idx] = val;
00104             dirty = true;
00105         }
00106     }
00107     
00108     // Global enable/disable the outputs.  We use this for cleaner startup,
00109     // by disabling all outputs after power-on and when coming out of sleep
00110     // mode until we've had a chance to initialize the chip registers.  The
00111     // chips have random values in their shift registers when first powered
00112     // on, so we have to send an initial update after power-on.  The snag
00113     // is that the chips might have a separate power supply from the KL25Z,
00114     // so we can't assume that the chips are powered just because the program
00115     // is running.  Instead, we can use the USB connection status as a proxy
00116     // for chip power, on the assumption that (a) the chips are running off
00117     // of the PC power supply, and (b) the USB connection can only be running
00118     // when the PC is running (hence the PC power supply is on).  
00119     void enable(bool f)
00120     {
00121         // set the new enable state
00122         ena = (f ? 1 : 0);
00123     }
00124     
00125     // Apply updates.  This sends the current state of each pin to the
00126     // chips and latches the new settings.  If 'force' is true, we flush
00127     // our internal state to the chips even if we haven't made any changes
00128     // since the last update.
00129     void update(bool force = false)
00130     {
00131         // if we have changes to apply, or the caller wants the update to
00132         // happen regardless of pending changes, refresh the chips
00133         if (dirty || force)
00134         {
00135             // Clock out the new states.  Since the outputs are arranged
00136             // as shift registers, we have to clock out the bits in reverse
00137             // order of port numbers - the first bit we output will end up
00138             // in the last register after we clock out all of the other bits.
00139             // So clock out the last bit first and the first bit last.
00140             for (int i = nchips*8-1 ; i >= 0 ; --i)
00141             {
00142                 sclk = 0;
00143                 sin = state[i];
00144                 sclk = 1;
00145             }
00146             
00147             // latch the new states
00148             latch = 1;
00149             sclk = 0;
00150             latch = 0;
00151             
00152             // outputs now reflect internal state
00153             dirty = false;
00154         }
00155     }
00156     
00157     
00158 private:
00159     int nchips;         // number of chips in daisy chain
00160     bool dirty;         // do we have changes to send to the chips?
00161     DigitalOut sin;     // serial data pin
00162     DigitalOut sclk;    // serial clock pin
00163     DigitalOut latch;   // latch pin
00164     DigitalOut ena;     // enable pin
00165     char *state;        // array of current output states (0=off, 1=on)
00166 };
00167         
00168 #endif // HC595_INCLUDED