Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * This is a work-in-progress 7x7 version of Grumpy Mike's 4x4 driver modification
- *
- * The TLC5940 is a 16-channel, constant-current sink LED driver. Each channel
- * has an individually adjustable 4096-step grayscale PWM brightness control and
- * a 64-step, constant-current sink (no LED resistors needed!).
- *
- * This library is based off the datasheet at
- * http://www.ti.com/lit/gpn/TLC5940
- *
- * To use this library, put this folder in
- * <arduino folder>/hardware/libraries
- * Then open the Arduino program and look through the examples at
- * File -> Sketchbook -> Examples -> Library-TLC5940Multiplex
- *
- * Origional
- * Alex Leone - acleone ~AT~ u.washington.edu
- * 2008-09-03 - v005
- *
- * Hacked to drive a 4 row multiplexing display Mike Cook November 08
- * 4 rows by 16 LEDs per row. Can be physically arranged as an 8x8 matrix of single LEDs
- * or as a 4x4 matrix of RGB LEDs or any other combination.
- */
- #include <avr/interrupt.h>
- #include <avr/pgmspace.h>
- #include <stdlib.h>
- #include "wiring.h"
- #include "TLC5940Multiplex_defines.h"
- #include "TLC5940Multiplex_pins.h"
- #include "TLC5940Multiplex.h"
- static int8_t rowPin[7] = {2, 8, 7, 11, 5, 13, 18}; // pins to enable or turn on each row, low = on
- static int8_t buttonPressed[multiplexRows][multiplexColoums]; // array to hold push button states
- static int8_t switchInput[4] = {14, 15, 16, 17}; // pins connected to the coloums of the push butons
- static int8_t scan = 0; // row being used and enabled
- static int8_t scanindex = 0; // row being used and enabled
- //static uint8_t *mBuffer; // points to memory to feed into the TLC55940
- static uint8_t *pBuffer[7]; // array pointing to the memory that is the start of each row
- static bool _firstPWMupdate; // just loaded the dot correcion data
- TLC5940M::TLC5940M()
- {
- }
- // initializes the chip and starts the timers
- void TLC5940M::init()
- {
- int8_t i=0;
- // iniilise multiplex row selects and turn them all off (off = 1 as we have a P channel FET)
- for (i = 0; i<7; i++){
- digitalWrite(rowPin[i], HIGH); // do this first so as not to have a 0 breifly appere when set to outputs
- pinMode(rowPin[i], OUTPUT);
- pinMode(switchInput[i],INPUT); // Initilise switch inputs
- }
- pinMode(vprog, OUTPUT);
- digitalWrite(vprog, LOW);
- pinMode(SIN_PIN_ARDUINO, OUTPUT);
- pinMode(SCLK_PIN_ARDUINO, OUTPUT);
- pinMode(XLAT_PIN_ARDUINO, OUTPUT);
- pinMode(GSCLK_PIN_ARDUINO, OUTPUT);
- pinMode(BLANK_PIN_ARDUINO, OUTPUT);
- pinMode(vprog, OUTPUT);
- digitalWrite(vprog, LOW); // Grayscale Mode
- digitalWrite(SIN_PIN_ARDUINO, LOW);
- digitalWrite(SCLK_PIN_ARDUINO, LOW);
- digitalWrite(XLAT_PIN_ARDUINO, LOW);
- digitalWrite(GSCLK_PIN_ARDUINO, LOW);
- digitalWrite(BLANK_PIN_ARDUINO, HIGH); // turn off everything while initialising
- uint16_t dummyDataLength = ((uint16_t)192) + 1;
- for (uint16_t i = 0; i < dummyDataLength; i++) {
- pulse_pin(SCLK_PORT, SCLK_PIN);
- } // shift in 192 * chips + 1 bits of dummy data (actually all zeros) per the datasheet for initialization in grayscale mode
- pulse_pin(XLAT_PORT, XLAT_PIN);
- digitalWrite(BLANK_PIN_ARDUINO, LOW);
- _firstPWMupdate = false;
- }
- // resets the timers
- void TLC5940M::resetTimers()
- {
- stopTimers();
- // Timer 1 - BLANK
- TCCR1A = _BV(WGM11); // fast PWM, ICR1 top
- TCCR1B = _BV(WGM13) |
- _BV(WGM12);
- TCNT1 = 0; // clear timer/counter register
- ICR1 = 32767; // top (8 * 4096 -> 488.2Hz)
- OCR1A = 0; // duty factor on OC1A, XLAT is inside BLANK
- OCR1B = 1; // duty factor on BLANK (so it's larger than OCR1A (XLAT))
- // Timer 2 - GSCLK
- TCCR2A = _BV(COM2B1) | // set on BOTTOM, clear on OCR2A (non-inverting), output on OC2B (digital pin 3)
- _BV(WGM21) | // Fast pwm with OCR2A top
- _BV(WGM20); // Fast pwm with OCR2A top
- TCCR2B = _BV(WGM22); // Fast pwm with OCR2A top
- TCNT2 = 0; // clear timer/counter register
- OCR2A = 7; // top (8 steps - > 2MHz)
- OCR2B = 0; // duty factor (as short a pulse as possible)
- cli(); // disable interrupts so both timers start at about the same time
- TCCR1B |= _BV(CS10); // no prescale, (start pwm output)
- TCCR2B |= _BV(CS20); // no prescale, (start pwm output)
- sei(); // enable interrupts
- }
- void TLC5940M::stopTimers()
- {
- TCCR1B = TCCR2B = 0;
- }
- // start using the buffer passed to it
- void TLC5940M::startMultiplexing(uint8_t *mBufferP)
- {
- pBuffer[0]= mBufferP; // pre calculate pointer to each buffer segment to multiplex
- pBuffer[1]= mBufferP + 32; // to prevent calculating it in the intrrupt service routine
- pBuffer[2]= mBufferP + 64;
- pBuffer[3]= mBufferP + 96;
- pBuffer[4]= mBufferP + 128;
- pBuffer[5]= mBufferP + 160;
- pBuffer[6]= mBufferP + 192;
- set_XLAT_interrupt; // kick off the multiplexing
- }
- // interrupt called during BLANK to latch data (the interrupt enable is set in update())
- ISR(TIMER1_COMPA_vect)
- {
- clear_XLAT_interrupt;
- digitalWrite(BLANK_PIN_ARDUINO,HIGH);
- loadBuffer(); // load the TLC5940 and switch rows
- digitalWrite(BLANK_PIN_ARDUINO,LOW);
- set_XLAT_interrupt;
- }
- // inputs a single PWM value into the LED data array (channel starts with 1)
- void TLC5940M::set(uint8_t *dBuffer, int channelx, int channely, uint16_t value)
- {
- uint8_t index8 = (15 - channelx) + (channely * 16);
- uint8_t index12 = (uint8_t)((((uint16_t)index8) * 3) >> 1);
- if (index8 & 1) { //starts in the middle
- // first 4 bits intact 4 top bits of value
- dBuffer[index12] = (dBuffer[index12] & 0xF0) | (value >> 8);
- // 8 lower bits of value
- dBuffer[++index12] = value & 0xFF;
- } else { // starts clean
- // 8 upper bits of value
- dBuffer[index12++] = value >> 4;
- // 4 lower bits of value last 4 bits intact
- dBuffer[index12] = ((uint8_t)(value << 4)) | (dBuffer[index12] & 0xF);
- }
- }
- uint16_t TLC5940M::get(uint8_t *mBufferP, int channelx, int channely)
- {
- uint8_t index8 = (15 - channelx) + (channely * 16);
- uint8_t index12 = (uint8_t)((((uint16_t)index8) * 3) >> 1);
- return (index8 & 1)?
- //starts in the middle
- (((uint16_t)(_GSData[index12] & 15)) << 8) | // upper 4 bits
- _GSData[index12 + 1] // lower 8 bits
- : // starts clean
- (((uint16_t)_GSData[index12]) << 4) | // upper 8 bits
- ((_GSData[index12 + 1] & 0xF0) >> 4); // lower 4 bits
- // that's probably the ugliest ternary operator I've ever created
- }
- void TLC5940M::setDCs(uint8_t *DCs)
- {
- // we can only switch to DC mode if the VPRG pin is connected
- stopTimers();
- pin_low(GSCLK_PORT, GSCLK_PIN);
- pin_high(BLANK_PORT, BLANK_PIN); // turn off everything
- digitalWrite(12, HIGH); // Dot-correction data input Mode
- shiftData(DCs, chips * 12);
- pulse_pin(XLAT_PORT, XLAT_PIN);
- digitalWrite(12, LOW); // Grayscale (PWM) Mode
- pin_low(BLANK_PORT, BLANK_PIN);
- _firstPWMupdate = true; // so loadBuffer() doesn't add an extra SCLK pulse
- resetTimers();
- }
- int8_t TLC5940M::readPush(int8_t x, int8_t y)
- {
- x &= 0x03; // mask off anything higher than 3 change for an 8x8 array
- y = (y+1) & 0x03;
- // y ^= 0x03; // un comment to change direction of y so 0 = bottom left
- return (buttonPressed[x][y]); // return the state of the button asked for
- }
- // bit-bangs data out of SIN and SCLK
- static void shiftData(uint8_t *data, uint8_t length)
- {
- uint8_t *dpMax = data + length;
- for (uint8_t *dp = data; dp < dpMax; dp++) {
- shift8(*dp);
- }
- }
- static void shift8(uint8_t value)
- {
- // 128 = 1000 0000
- for (uint8_t bit = 128; bit; bit >>= 1) {
- if (value & bit) {
- pin_high(SIN_PORT, SIN_PIN);
- } else {
- pin_low(SIN_PORT, SIN_PIN);
- }
- pulse_pin(SCLK_PORT, SCLK_PIN);
- }
- }
- // Load the TLC5940 with the appropiate row from the display buffer and turn on that row
- void loadBuffer()
- {
- prog_uint8_t *bRow;
- if (_firstPWMupdate) { // adds an extra SCLK pulse unless we've just set dot-correction data
- _firstPWMupdate = false;
- } else {
- pulse_pin(SCLK_PORT, SCLK_PIN);
- }
- /*
- // read the state of the push buttons
- for(uint8_t i = 0 ; i<multiplexColoums; i++){
- buttonPressed[i][scan] = digitalRead(switchInput[i]);
- }
- */
- digitalWrite(rowPin[scan], HIGH); // Turn row off
- scan++;
- if (scan >=7) {
- scan=0;
- }
- //scan = (scanindex); // Increment row count
- //scan = (scan + 1) & 3; // original incrementor
- bRow = pBuffer[scan]; // point at next row in the bufer precalculated to save time
- // disable_XLAT_pulse;
- shiftData(bRow,32); // load the TLC594 // rearly multiply 24 by the number of chips
- enable_XLAT_pulse;
- digitalWrite(rowPin[scan], LOW); // Turn row on
- }
- // Preinstantiate
- TLC5940M Tlcm = TLC5940M();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement