SHARE
TWEET

TLC5940Multiplex.cpp 7x7

a guest Jun 19th, 2011 176 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * This is a work-in-progress 7x7 version of Grumpy Mike's 4x4 driver modification
  3.  *
  4.  * The TLC5940 is a 16-channel, constant-current sink LED driver.  Each channel
  5.  * has an individually adjustable 4096-step grayscale PWM brightness control and
  6.  * a 64-step, constant-current sink (no LED resistors needed!).
  7.  *
  8.  * This library is based off the datasheet at
  9.  * http://www.ti.com/lit/gpn/TLC5940
  10.  *
  11.  * To use this library, put this folder in
  12.  *  <arduino folder>/hardware/libraries
  13.  * Then open the Arduino program and look through the examples at
  14.  *  File -> Sketchbook -> Examples -> Library-TLC5940Multiplex
  15.  *
  16.  * Origional
  17.  * Alex Leone - acleone ~AT~ u.washington.edu
  18.  * 2008-09-03 - v005
  19.  *
  20.  * Hacked to drive a 4 row multiplexing display Mike Cook November 08
  21.  * 4 rows by 16 LEDs per row. Can be physically arranged as an 8x8 matrix of single LEDs
  22.  * or as a 4x4 matrix of RGB LEDs or any other combination.
  23.  */
  24.  
  25. #include <avr/interrupt.h>
  26. #include <avr/pgmspace.h>
  27. #include <stdlib.h>
  28. #include "wiring.h"
  29.  
  30. #include "TLC5940Multiplex_defines.h"
  31. #include "TLC5940Multiplex_pins.h"
  32. #include "TLC5940Multiplex.h"
  33.  
  34.  
  35.  
  36. static int8_t rowPin[7] = {2, 8, 7, 11, 5, 13, 18};   // pins to enable or turn on each row, low = on
  37. static int8_t buttonPressed[multiplexRows][multiplexColoums]; // array to hold push button states
  38. static int8_t switchInput[4] = {14, 15, 16, 17};  // pins connected to the coloums of the push butons
  39. static int8_t scan = 0;  // row being used and enabled
  40. static int8_t scanindex = 0;  // row being used and enabled
  41. //static uint8_t *mBuffer; // points to memory to feed into the TLC55940
  42. static uint8_t *pBuffer[7];  // array pointing to the memory that is the start of each row
  43. static bool _firstPWMupdate; // just loaded the dot correcion data
  44.  
  45. TLC5940M::TLC5940M()
  46. {
  47.  
  48. }
  49.  
  50. // initializes the chip and starts the timers
  51. void TLC5940M::init()
  52. {
  53.   int8_t i=0;
  54.    // iniilise multiplex row selects and turn them all off (off = 1 as we have a P channel FET)
  55.     for (i = 0; i<7; i++){
  56.     digitalWrite(rowPin[i], HIGH);  // do this first so as not to have a 0 breifly appere when set to outputs
  57.     pinMode(rowPin[i], OUTPUT);
  58.     pinMode(switchInput[i],INPUT); // Initilise switch inputs
  59.     }
  60.    pinMode(vprog, OUTPUT);
  61.     digitalWrite(vprog, LOW);
  62.        
  63.         pinMode(SIN_PIN_ARDUINO, OUTPUT);
  64.         pinMode(SCLK_PIN_ARDUINO, OUTPUT);
  65.         pinMode(XLAT_PIN_ARDUINO, OUTPUT);
  66.         pinMode(GSCLK_PIN_ARDUINO, OUTPUT);
  67.         pinMode(BLANK_PIN_ARDUINO, OUTPUT);
  68.  
  69.                 pinMode(vprog, OUTPUT);
  70.                 digitalWrite(vprog, LOW); // Grayscale Mode
  71.  
  72.         digitalWrite(SIN_PIN_ARDUINO, LOW);
  73.         digitalWrite(SCLK_PIN_ARDUINO, LOW);
  74.         digitalWrite(XLAT_PIN_ARDUINO, LOW);
  75.         digitalWrite(GSCLK_PIN_ARDUINO, LOW);
  76.  
  77.         digitalWrite(BLANK_PIN_ARDUINO, HIGH); // turn off everything while initialising
  78.  
  79.         uint16_t dummyDataLength = ((uint16_t)192) + 1;
  80.         for (uint16_t i = 0; i < dummyDataLength; i++) {
  81.                 pulse_pin(SCLK_PORT, SCLK_PIN);
  82.         } // shift in 192 * chips + 1 bits of dummy data (actually all zeros) per the datasheet for initialization in grayscale mode
  83.         pulse_pin(XLAT_PORT, XLAT_PIN);
  84.  
  85.         digitalWrite(BLANK_PIN_ARDUINO, LOW);
  86.  
  87.         _firstPWMupdate = false;
  88. }
  89.  
  90.  
  91. // resets the timers
  92. void TLC5940M::resetTimers()
  93. {      
  94.         stopTimers();
  95.        
  96.         // Timer 1 - BLANK
  97.         TCCR1A = _BV(WGM11); // fast PWM, ICR1 top
  98.         TCCR1B = _BV(WGM13) |
  99.                          _BV(WGM12);
  100.         TCNT1 = 0; // clear timer/counter register
  101.         ICR1 = 32767; // top (8 * 4096 -> 488.2Hz)
  102.         OCR1A = 0; // duty factor on OC1A, XLAT is inside BLANK
  103.         OCR1B = 1; // duty factor on BLANK (so it's larger than OCR1A (XLAT))
  104.  
  105.         // Timer 2 - GSCLK
  106.         TCCR2A = _BV(COM2B1) | // set on BOTTOM, clear on OCR2A (non-inverting), output on OC2B (digital pin 3)
  107.                          _BV(WGM21) | // Fast pwm with OCR2A top
  108.                          _BV(WGM20);  // Fast pwm with OCR2A top
  109.         TCCR2B = _BV(WGM22); // Fast pwm with OCR2A top
  110.         TCNT2 = 0; // clear timer/counter register
  111.         OCR2A = 7; // top (8 steps - > 2MHz)
  112.         OCR2B = 0; // duty factor (as short a pulse as possible)
  113.  
  114.         cli(); // disable interrupts so both timers start at about the same time
  115.  
  116.         TCCR1B |= _BV(CS10); // no prescale, (start pwm output)
  117.         TCCR2B |= _BV(CS20); // no prescale, (start pwm output)
  118.  
  119.         sei(); // enable interrupts
  120. }
  121.  
  122. void TLC5940M::stopTimers()
  123. {
  124.         TCCR1B = TCCR2B = 0;
  125. }
  126.  
  127. // start using the buffer passed to it
  128. void TLC5940M::startMultiplexing(uint8_t *mBufferP)  
  129. {
  130.         pBuffer[0]= mBufferP;      // pre calculate pointer to each buffer segment to multiplex
  131.         pBuffer[1]= mBufferP + 32; // to prevent calculating it in the intrrupt service routine
  132.         pBuffer[2]= mBufferP + 64;
  133.         pBuffer[3]= mBufferP + 96;
  134.    pBuffer[4]= mBufferP + 128;
  135.    pBuffer[5]= mBufferP + 160;
  136.    pBuffer[6]= mBufferP + 192;
  137.         set_XLAT_interrupt;        // kick off the multiplexing
  138. }
  139.  
  140. // interrupt called during BLANK to latch data (the interrupt enable is set in update())
  141. ISR(TIMER1_COMPA_vect)
  142. {
  143.         clear_XLAT_interrupt;
  144.         digitalWrite(BLANK_PIN_ARDUINO,HIGH);  
  145.         loadBuffer();      // load the TLC5940 and switch rows 
  146.         digitalWrite(BLANK_PIN_ARDUINO,LOW);
  147.         set_XLAT_interrupt;
  148. }
  149.  
  150.  
  151. // inputs a single PWM value into the LED data array (channel starts with 1)
  152. void TLC5940M::set(uint8_t *dBuffer, int channelx, int channely, uint16_t value)
  153. {
  154.         uint8_t index8 = (15 - channelx) + (channely * 16);
  155.         uint8_t index12 = (uint8_t)((((uint16_t)index8) * 3) >> 1);
  156.  
  157.         if (index8 & 1) { //starts in the middle
  158.                                                                         // first 4 bits intact      4 top bits of value
  159.                 dBuffer[index12] = (dBuffer[index12] & 0xF0) | (value >> 8);
  160.                                                         // 8 lower bits of value
  161.                 dBuffer[++index12] = value & 0xFF;
  162.         } else { // starts clean
  163.                                                                 // 8 upper bits of value
  164.                 dBuffer[index12++] = value >> 4;
  165.                                                                 // 4 lower bits of value         last 4 bits intact
  166.                 dBuffer[index12] = ((uint8_t)(value << 4)) | (dBuffer[index12] & 0xF);
  167.         }
  168.  
  169. }
  170.  
  171. uint16_t TLC5940M::get(uint8_t *mBufferP, int channelx, int channely)
  172. {
  173.         uint8_t index8 = (15 - channelx) + (channely * 16);
  174.         uint8_t index12 = (uint8_t)((((uint16_t)index8) * 3) >> 1);
  175.         return (index8 & 1)?
  176.                   //starts in the middle
  177.                         (((uint16_t)(_GSData[index12] & 15)) << 8) | // upper 4 bits
  178.                         _GSData[index12 + 1] // lower 8 bits
  179.                 : // starts clean
  180.                         (((uint16_t)_GSData[index12]) << 4) | // upper 8 bits
  181.                         ((_GSData[index12 + 1] & 0xF0) >> 4); // lower 4 bits
  182.         // that's probably the ugliest ternary operator I've ever created
  183. }
  184.  
  185. void TLC5940M::setDCs(uint8_t *DCs)
  186. {
  187.         // we can only switch to DC mode if the VPRG pin is connected
  188.        
  189.                 stopTimers();
  190.  
  191.                 pin_low(GSCLK_PORT, GSCLK_PIN);
  192.                 pin_high(BLANK_PORT, BLANK_PIN);  // turn off everything
  193.                 digitalWrite(12, HIGH); // Dot-correction data input Mode
  194.  
  195.                 shiftData(DCs, chips * 12);
  196.                 pulse_pin(XLAT_PORT, XLAT_PIN);
  197.  
  198.                 digitalWrite(12, LOW); // Grayscale (PWM) Mode
  199.                 pin_low(BLANK_PORT, BLANK_PIN);
  200.                
  201.                 _firstPWMupdate = true; // so loadBuffer() doesn't add an extra SCLK pulse
  202.                 resetTimers();
  203.        
  204. }
  205.  
  206.  
  207. int8_t TLC5940M::readPush(int8_t x, int8_t y)
  208. {
  209.         x &= 0x03;      // mask off anything higher than 3 change for an 8x8 array
  210.         y = (y+1) & 0x03;
  211. //      y ^= 0x03;      // un comment to change direction of y so 0 = bottom left
  212.         return (buttonPressed[x][y]);  // return the state of the button asked for
  213. }
  214.  
  215. // bit-bangs data out of SIN and SCLK
  216. static void shiftData(uint8_t *data, uint8_t length)
  217. {
  218.         uint8_t *dpMax = data + length;
  219.         for (uint8_t *dp = data; dp < dpMax; dp++) {
  220.                 shift8(*dp);
  221.         }
  222. }
  223.  
  224. static void shift8(uint8_t value)
  225. {
  226.         //                              128 = 1000 0000
  227.         for (uint8_t bit = 128; bit; bit >>= 1) {
  228.                 if (value & bit) {
  229.                         pin_high(SIN_PORT, SIN_PIN);
  230.                 } else {
  231.                         pin_low(SIN_PORT, SIN_PIN);
  232.                 }
  233.                 pulse_pin(SCLK_PORT, SCLK_PIN);
  234.         }
  235. }
  236.  
  237. // Load the TLC5940 with the appropiate row from the display buffer and turn on that row
  238. void loadBuffer()
  239. {  
  240.    
  241.     prog_uint8_t *bRow;
  242.     if (_firstPWMupdate) { // adds an extra SCLK pulse unless we've just set dot-correction data
  243.         _firstPWMupdate = false;
  244.     } else {
  245.         pulse_pin(SCLK_PORT, SCLK_PIN);
  246.     }
  247.    
  248.     /*
  249.     // read the state of the push buttons
  250.     for(uint8_t i = 0 ; i<multiplexColoums; i++){
  251.         buttonPressed[i][scan] = digitalRead(switchInput[i]);  
  252.     }
  253. */
  254.     digitalWrite(rowPin[scan], HIGH);  // Turn row off
  255.    
  256.     scan++;
  257.     if (scan >=7) {
  258.         scan=0;
  259.     }
  260.    
  261.     //scan = (scanindex); // Increment row count
  262.     //scan = (scan + 1) & 3; // original incrementor
  263.     bRow = pBuffer[scan];   // point at next row in the bufer precalculated to save time
  264.  
  265.     //  disable_XLAT_pulse;
  266.     shiftData(bRow,32);  // load the TLC594 // rearly multiply 24 by the number of chips       
  267.     enable_XLAT_pulse;
  268.     digitalWrite(rowPin[scan], LOW);  // Turn row on
  269. }
  270.  
  271. // Preinstantiate
  272. TLC5940M Tlcm = TLC5940M();
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top