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 TLC59116.h Source File

TLC59116.h

00001 // TLC59116 interface
00002 //
00003 // The TLC59116 is a 16-channel constant-current PWM controller chip with
00004 // an I2C interface.
00005 //
00006 // Up to 14 of these chips can be connected to a single bus.  Each chip needs
00007 // a unique address, configured via four pin inputs.  (The I2C address is 7
00008 // bits, but the high-order 3 bits are fixed in the hardware, leaving 4 bits
00009 // to configure per chip.  Two of the possible 16 addresses are reserved by
00010 // the chip hardware as broadcast addresses, leaving room for 14 unique chip
00011 // addresses per bus.)
00012 //
00013 // EXTERNAL PULL-UP RESISTORS ARE REQUIRED ON SDA AND SCL.  The internal 
00014 // pull-ups in the KL25Z GPIO ports will only work if the bus speed is 
00015 // limited to 100kHz.  Higher speeds require external pull-ups.  Because
00016 // of the relatively high data rate required, we use the maximum 1MHz bus 
00017 // speed, requiring external pull-ups.  These are typically 2.2K.
00018 //
00019 // This chip is similar to the TLC5940, but has a more modern design with 
00020 // several advantages, including a standardized and much more robust data 
00021 // interface (I2C) and glitch-free startup.  The only downside vs the TLC5940 
00022 // is that it's only available in an SMD package, whereas the TLC5940 is 
00023 // available in easy-to-solder DIP format.  The DIP 5940 is longer being 
00024 // manufactured, but it's still easy to find old stock; when those run out,
00025 // though, and the choice is between SMD 5940 and 59116, the 59116 will be
00026 // the clear winner.
00027 //
00028 
00029 #ifndef _TLC59116_H_
00030 #define _TLC59116_H_
00031 
00032 #include "mbed.h"
00033 #include "BitBangI2C.h"
00034 
00035 // Which I2C class are we using?  We use this to switch between
00036 // BitBangI2C and MbedI2C for testing and debugging.
00037 #define I2C_Type BitBangI2C
00038 
00039 // register constants
00040 struct TLC59116R
00041 {
00042     // control register bits
00043     static const uint8_t CTL_AIALL = 0x80;         // auto-increment mode, all registers
00044     static const uint8_t CTL_AIPWM = 0xA0;         // auto-increment mode, PWM registers only
00045     static const uint8_t CTL_AICTL = 0xC0;         // auto-increment mode, control registers only
00046     static const uint8_t CTL_AIPWMCTL = 0xE0;      // auto-increment mode, PWM + control registers only
00047 
00048     // register addresses
00049     static const uint8_t REG_MODE1 = 0x00;         // MODE1
00050     static const uint8_t REG_MODE2 = 0x01;         // MODE2
00051     static const uint8_t REG_PWM0 = 0x02;          // PWM 0
00052     static const uint8_t REG_PWM1 = 0x03;          // PWM 1
00053     static const uint8_t REG_PWM2 = 0x04;          // PWM 2
00054     static const uint8_t REG_PWM3 = 0x05;          // PWM 3
00055     static const uint8_t REG_PWM4 = 0x06;          // PWM 4
00056     static const uint8_t REG_PWM5 = 0x07;          // PWM 5
00057     static const uint8_t REG_PWM6 = 0x08;          // PWM 6
00058     static const uint8_t REG_PWM7 = 0x09;          // PWM 7
00059     static const uint8_t REG_PWM8 = 0x0A;          // PWM 8
00060     static const uint8_t REG_PWM9 = 0x0B;          // PWM 9
00061     static const uint8_t REG_PWM10 = 0x0C;         // PWM 10
00062     static const uint8_t REG_PWM11 = 0x0D;         // PWM 11
00063     static const uint8_t REG_PWM12 = 0x0E;         // PWM 12
00064     static const uint8_t REG_PWM13 = 0x0F;         // PWM 13
00065     static const uint8_t REG_PWM14 = 0x10;         // PWM 14
00066     static const uint8_t REG_PWM15 = 0x11;         // PWM 15
00067     static const uint8_t REG_GRPPWM = 0x12;        // Group PWM duty cycle
00068     static const uint8_t REG_GRPFREQ = 0x13;       // Group frequency register
00069     static const uint8_t REG_LEDOUT0 = 0x14;       // LED driver output status register 0
00070     static const uint8_t REG_LEDOUT1 = 0x15;       // LED driver output status register 1
00071     static const uint8_t REG_LEDOUT2 = 0x16;       // LED driver output status register 2
00072     static const uint8_t REG_LEDOUT3 = 0x17;       // LED driver output status register 3
00073     
00074     // MODE1 bits
00075     static const uint8_t MODE1_AI2 = 0x80;         // auto-increment mode enable
00076     static const uint8_t MODE1_AI1 = 0x40;         // auto-increment bit 1
00077     static const uint8_t MODE1_AI0 = 0x20;         // auto-increment bit 0
00078     static const uint8_t MODE1_OSCOFF = 0x10;      // oscillator off
00079     static const uint8_t MODE1_SUB1 = 0x08;        // subaddress 1 enable
00080     static const uint8_t MODE1_SUB2 = 0x04;        // subaddress 2 enable
00081     static const uint8_t MODE1_SUB3 = 0x02;        // subaddress 3 enable
00082     static const uint8_t MODE1_ALLCALL = 0x01;     // all-call enable
00083     
00084     // MODE2 bits
00085     static const uint8_t MODE2_EFCLR = 0x80;       // clear error status flag
00086     static const uint8_t MODE2_DMBLNK = 0x20;      // group blinking mode
00087     static const uint8_t MODE2_OCH = 0x08;         // outputs change on ACK (vs Stop command)
00088     
00089     // LEDOUTn states
00090     static const uint8_t LEDOUT_OFF = 0x00;        // driver is off
00091     static const uint8_t LEDOUT_ON = 0x01;         // fully on
00092     static const uint8_t LEDOUT_PWM = 0x02;        // individual PWM control via PWMn register
00093     static const uint8_t LEDOUT_GROUP = 0x03;      // PWM control + group dimming/blinking via PWMn + GRPPWM
00094 };
00095    
00096 
00097 // Individual unit object.  We create one of these for each unit we
00098 // find on the bus.  This keeps track of the state of each output on
00099 // a unit so that we can update outputs in batches, to reduce the 
00100 // amount of time we spend in I2C communications during rapid updates.
00101 struct TLC59116Unit
00102 {
00103     TLC59116Unit()
00104     {
00105         // start inactive, since we haven't been initialized yet
00106         active = false;
00107         
00108         // set all brightness levels to 0 intially
00109         memset(bri, 0, sizeof(bri));
00110         
00111         // mark all outputs as dirty to force an update after initializing
00112         dirty = 0xFFFF;
00113     }
00114     
00115     // initialize
00116     void init(int addr, I2C_Type &i2c)
00117     {        
00118         // set all output drivers to individual PWM control
00119         const uint8_t all_pwm = 
00120             TLC59116R::LEDOUT_PWM 
00121             | (TLC59116R::LEDOUT_PWM << 2)
00122             | (TLC59116R::LEDOUT_PWM << 4)
00123             | (TLC59116R::LEDOUT_PWM << 6);
00124         static const uint8_t buf[] = { 
00125             TLC59116R::REG_LEDOUT0 | TLC59116R::CTL_AIALL,
00126             all_pwm, 
00127             all_pwm, 
00128             all_pwm, 
00129             all_pwm 
00130         };
00131         int err = i2c.write(addr << 1, buf, sizeof(buf));
00132 
00133         // turn on the oscillator
00134         static const uint8_t buf2[] = { 
00135             TLC59116R::REG_MODE1, 
00136             TLC59116R::MODE1_AI2 | TLC59116R::MODE1_ALLCALL 
00137         };
00138         err |= i2c.write(addr << 1, buf2, sizeof(buf));
00139         
00140         // mark the unit as active if the writes succeeded
00141         active = !err;
00142     }
00143     
00144     // Set an output
00145     void set(int idx, int val)
00146     {
00147         // validate the index
00148         if (idx >= 0 && idx <= 15)
00149         {
00150             // record the new brightness
00151             bri[idx] = val;
00152             
00153             // set the dirty bit
00154             dirty |= 1 << idx;
00155         }
00156     }
00157     
00158     // Get an output's current value
00159     int get(int idx) const
00160     {
00161         return idx >= 0 && idx <= 15 ? bri[idx] : -1;
00162     }
00163     
00164     // Send I2C updates
00165     void send(int addr, I2C_Type &i2c)
00166     {
00167         // Scan all outputs.  I2C sends are fairly expensive, so we
00168         // minimize the send time by using the auto-increment mode.
00169         // Optimizing this is a bit tricky.  Suppose that the outputs
00170         // are in this state, where c represents a clean output and D
00171         // represents a dirty output:
00172         //
00173         //    cccDcDccc...
00174         //
00175         // Clearly we want to start sending at the first dirty output
00176         // so that we don't waste time sending the three clean bytes
00177         // ahead of it.  However, do we send output[3] as one chunk
00178         // and then send output[5] as a separate chunk, or do we send
00179         // outputs [3],[4],[5] as a single block to take advantage of
00180         // the auto-increment mode?  Based on I2C bus timing parameters,
00181         // the answer is that it's cheaper to send this as a single
00182         // contiguous block [3],[4],[5].  The reason is that the cost
00183         // of starting a new block is a Stop/Start sequence plus another
00184         // register address byte; the register address byte costs the
00185         // same as a data byte, so the extra Stop/Start of the separate
00186         // chunk approach makes the single continguous send cheaper. 
00187         // But how about this one?:
00188         //
00189         //   cccDccDccc...
00190         //
00191         // This one is cheaper to send as two separate blocks.  The
00192         // break costs us a Start/Stop plus a register address byte,
00193         // but the Start/Stop is only about 25% of the cost of a data
00194         // byte, so Start/Stop+Register Address is cheaper than sending
00195         // the two clean data bytes sandwiched between the dirty bytes.
00196         //
00197         // So: we want to look for sequences of contiguous dirty bytes
00198         // and send those as a chunk.  We furthermore will allow up to
00199         // one clean byte in the midst of the dirty bytes.
00200         uint8_t buf[17];
00201         int n = 0;
00202         for (int i = 0, bit = 1 ; i < 16 ; ++i, bit <<= 1)
00203         {
00204             // If this one is dirty, include it in the set of outputs to
00205             // send to the chip.  Also include this one if it's clean
00206             // and the outputs on both sides are dirty - see the notes
00207             // above about optimizing for the case where we have one clean
00208             // output surrounded by dirty outputs.
00209             if ((dirty & bit) != 0)
00210             {
00211                 // it's dirty - add it to the dirty set under construction
00212                 buf[++n] = bri[i];
00213             }
00214             else if (n != 0 && n < 15 && (dirty & (bit << 1)) != 0)
00215             {
00216                 // this one is clean, but the one before and the one after
00217                 // are both dirty, so keep it in the set anyway to take
00218                 // advantage of the auto-increment mode for faster sends
00219                 buf[++n] = bri[i];
00220             }
00221             else
00222             {
00223                 // This one is clean, and it's not surrounded by dirty
00224                 // outputs.  If the set of dirty outputs so far has any
00225                 // members, send them now.
00226                 if (n != 0)
00227                 {
00228                     // set the starting register address, including the
00229                     // auto-increment flag, and write the block
00230                     buf[0] = (TLC59116R::REG_PWM0 + i - n) | TLC59116R::CTL_AIALL;
00231                     i2c.write(addr << 1, buf, n + 1);
00232                     
00233                     // empty the set
00234                     n = 0;
00235                 }
00236             }
00237         }
00238         
00239         // if we finished the loop with dirty outputs to send, send them
00240         if (n != 0)
00241         {
00242             // fill in the starting register address, and write the block
00243             buf[0] = (TLC59116R::REG_PWM15 + 1 - n) | TLC59116R::CTL_AIALL;
00244             i2c.write(addr << 1, buf, n + 1);
00245         }
00246         
00247         // all outputs are now clean
00248         dirty = 0;
00249     }
00250     
00251     // Is the unit active?  If we have trouble writing a unit,
00252     // we can mark it inactive so that we know to stop wasting
00253     // time writing to it, and so that we can re-initialize it
00254     // if it comes back on later bus scans.
00255     bool active;
00256     
00257     // Output states.  This records the latest brightness level
00258     // for each output as set by the client.  We don't actually
00259     // send these values to the physical unit until the client 
00260     // tells us to do an I2C update.
00261     uint8_t bri[16];
00262     
00263     // Dirty output mask.  Whenever the client changes an output,
00264     // we record the new brightness in bri[] and set the 
00265     // corresponding bit here to 1.  We use these bits to determine
00266     // which outputs to send during each I2C update.
00267     uint16_t dirty;
00268 };
00269 
00270 // TLC59116 public interface.  This provides control over a collection
00271 // of units connected on a common I2C bus.
00272 class TLC59116
00273 {
00274 public:
00275     // Initialize.  The address given is the configurable part
00276     // of the address, 0x0000 to 0x000F.
00277     TLC59116(PinName sda, PinName scl, PinName reset)
00278         : i2c(sda, scl, true), reset(reset)
00279     {
00280         // Use the fastest I2C speed possible, since we want to be able
00281         // to rapidly update many outputs at once.  The TLC59116 can run 
00282         // I2C at up to 1MHz.
00283         i2c.frequency(1000000);
00284         
00285         // assert !RESET until we're ready to go
00286         this->reset.write(0);
00287         
00288         // there are no units yet
00289         memset(units, 0, sizeof(units));
00290         nextUpdate = 0;
00291     }
00292     
00293     void init()
00294     {
00295         // un-assert reset
00296         reset.write(1);
00297         wait_us(10000);
00298         
00299         // scan the bus for new units
00300         scanBus();
00301     }
00302     
00303     // scan the bus
00304     void scanBus()
00305     {
00306         // scan each possible address
00307         for (int i = 0 ; i < 16 ; ++i)
00308         {
00309             // Address 8 and 11 are reserved - skip them
00310             if (i == 8 || i == 11)
00311                 continue;
00312                 
00313             // Try reading register REG_MODE1
00314             int addr = I2C_BASE_ADDR | i;
00315             TLC59116Unit *u = units[i];
00316             if (readReg8(addr, TLC59116R::REG_MODE1) >= 0)
00317             {
00318                 // success - if the slot wasn't already populated, allocate
00319                 // a unit entry for it
00320                 if (u == 0)
00321                     units[i] = u = new TLC59116Unit();
00322                     
00323                 // if the unit isn't already marked active, initialize it
00324                 if (!u->active)
00325                     u->init(addr, i2c);
00326             }
00327             else
00328             {
00329                 // failed - if the unit was previously active, mark it
00330                 // as inactive now
00331                 if (u != 0)
00332                     u->active = false;
00333             }
00334         }
00335     }
00336     
00337     // set an output
00338     void set(int unit, int output, int val)
00339     {
00340         if (unit >= 0 && unit <= 15)
00341         {
00342             TLC59116Unit *u = units[unit];
00343             if (u != 0)
00344                 u->set(output, val);
00345         }
00346     }
00347     
00348     // get an output's current value
00349     int get(int unit, int output)
00350     {
00351         if (unit >= 0 && unit <= 15)
00352         {
00353             TLC59116Unit *u = units[unit];
00354             if (u != 0)
00355                 return u->get(output);
00356         }
00357         
00358         return -1;
00359     }
00360     
00361     // Send I2C updates to the next unit.  The client must call this 
00362     // periodically to send pending updates.  We only update one unit on 
00363     // each call to ensure that the time per cycle is relatively constant
00364     // (rather than scaling with the number of chips).
00365     void send()
00366     {
00367         // look for a dirty unit
00368         for (int i = 0, n = nextUpdate ; i < 16 ; ++i, ++n)
00369         {
00370             // wrap the unit number
00371             n &= 0x0F;
00372             
00373             // if this unit is populated and dirty, it's the one to update
00374             TLC59116Unit *u = units[n];
00375             if (u != 0 && u->dirty != 0)
00376             {
00377                 // it's dirty - update it 
00378                 u->send(I2C_BASE_ADDR | n, i2c);
00379                 
00380                 // We only update one on each call, so we're done.
00381                 // Remember where to pick up again on the next update() 
00382                 // call, and return.
00383                 nextUpdate = n + 1;
00384                 return;
00385             }
00386         }
00387     }
00388     
00389     // Enable/disable all outputs
00390     void enable(bool f)
00391     {
00392         // visit each populated unit
00393         for (int i = 0 ; i < 16 ; ++i)
00394         {
00395             // if this unit is populated, enable/disable it
00396             TLC59116Unit *u = units[i];
00397             if (u != 0)
00398             {
00399                 // read the current MODE1 register
00400                 int m = readReg8(I2C_BASE_ADDR | i, TLC59116R::REG_MODE1);
00401                 if (m >= 0)
00402                 {
00403                     // Turn the oscillator off to disable, on to enable. 
00404                     // Note that the bit is kind of backwards:  SETTING the 
00405                     // OSC bit turns the oscillator OFF.
00406                     if (f)
00407                         m &= ~TLC59116R::MODE1_OSCOFF; // enable - clear the OSC bit
00408                     else
00409                         m |= TLC59116R::MODE1_OSCOFF;  // disable - set the OSC bit
00410                         
00411                     // update MODE1
00412                     writeReg8(I2C_BASE_ADDR | i, TLC59116R::REG_MODE1, m);
00413                 }
00414             }
00415         }
00416     }
00417     
00418 protected:
00419     // TLC59116 base I2C address.  These chips use an address of
00420     // the form 110xxxx, where the the low four bits are set by
00421     // external pins on the chip.  The top three bits are always
00422     // the same, so we construct the full address by combining 
00423     // the upper three fixed bits with the four-bit unit number.
00424     //
00425     // Note that addresses 1101011 (0x6B) and 1101000 (0x68) are
00426     // reserved (for SWRSTT and ALLCALL, respectively), and can't
00427     // be used for configured device addresses.
00428     static const uint8_t I2C_BASE_ADDR = 0x60;
00429     
00430     // Units.  We populate this with active units we find in
00431     // bus scans.  Note that units 8 and 11 can't be used because
00432     // of the reserved ALLCALL and SWRST addresses, but we allocate
00433     // the slots anyway to keep indexing simple.
00434     TLC59116Unit *units[16];
00435     
00436     // next unit to update
00437     int nextUpdate;
00438 
00439     // read 8-bit register; returns the value read on success, -1 on failure
00440     int readReg8(int addr, uint16_t registerAddr)
00441     {
00442         // write the request - register address + auto-inc mode
00443         uint8_t data_write[1];
00444         data_write[0] = registerAddr | TLC59116R::CTL_AIALL;
00445         if (i2c.write(addr << 1, data_write, 1, true))
00446             return -1;
00447     
00448         // read the result
00449         uint8_t data_read[1];
00450         if (i2c.read(addr << 1, data_read, 1))
00451             return -1;
00452         
00453         // return the result
00454         return data_read[0];
00455     }
00456  
00457     // write 8-bit register; returns true on success, false on failure
00458     bool writeReg8(int addr, uint16_t registerAddr, uint8_t data)
00459     {
00460         uint8_t data_write[2];
00461         data_write[0] = registerAddr | TLC59116R::CTL_AIALL;
00462         data_write[1] = data;
00463         return !i2c.write(addr << 1, data_write, 2);
00464     }
00465  
00466     // I2C bus interface
00467     I2C_Type i2c;
00468     
00469     // reset pin (active low)
00470     DigitalOut reset;
00471 };
00472 
00473 #endif