Advertisement
Guest User

TLC5940Multiplex.cpp 7x7

a guest
Jun 19th, 2011
236
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.64 KB | None | 0 0
  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();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement