
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
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
Generated on Sat Apr 18 2020 19:09:13 by
