Anonymous

LED tiles: Difference between revisions

From London Hackspace Wiki
10,024 bytes added ,  11 February 2016
Added test sketch
(Fix code block formatting)
(Added test sketch)
Line 158: Line 158:


The pixel writing could be made much faster using direct port operations, particularly with a port dedicated to the tile interface - one write with clock low to set the R G B values and the next holding the values with clock high - probably of the order of 50 times faster than the code above.
The pixel writing could be made much faster using direct port operations, particularly with a port dedicated to the tile interface - one write with clock low to set the R G B values and the next holding the values with clock high - probably of the order of 50 times faster than the code above.
== Test Sketch ==
The following sketch supports some test routines and primitive graphic output to the matrix.
Pulse durations need to be optimized, either by testing or using a logic analyzer on the actual driver boards in the space.
<pre>
/*
*  LED Matrix test sketch
*
*  Matrix driver code and test routines for 48 x 36 LED tile
*
*  2016-11-02  Michael Margolis
*/
#include "fastwrite.h"
// this code needs at least 2K of RAM so a Mega or Sanguino should be used
#if defined(__AVR_ATmega1280__) || defined (__AVR_ATmega2560__)
// port C
#define pinR1 37  // red
#define pinG1 36  // green
#define pinB1 35  // blue
#define RGB_PORT PORTC
// port A
#define pinA  24  // brown
#define pinB  25  // orange
#define pinC  26  // yellow
// port G
#define pinCK 39  // violet
#define pinLT 40  // white
#define pinEN 41  // grey
#else
#error "Arduino pin mapping not defined for this board"
#endif
// define for printf, only needed for debug info
static char _buf[64];
#define printf(...)  do { sprintf(_buf, __VA_ARGS__); Serial.write(_buf); } while (0)
// LED pixel display
const byte displayRows = 36;
const byte displayColumns = 48;
// internal layout of matrix buffer
const int _bufferRows = 6;
const int _bytesPerBufferRow = displayColumns * 6;
byte matrixBuffer[_bufferRows][_bytesPerBufferRow];
const int columnOffsets[] = {192, 200, 96, 104, 0, 8}; // translate to buffer offset
byte rowOffsets[] = {5,0,3,4,1,2}; // row order
int testDelay = 20; //delay in milliseonds used for testing
void setup() {
  Serial.begin(115200);
  Serial.println("LED Matrix test");
  // set pin modes
  pinMode(pinR1, OUTPUT);
  pinMode(pinG1, OUTPUT);
  pinMode(pinB1, OUTPUT);
  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);
  pinMode(pinC, OUTPUT);
  pinMode(pinCK, OUTPUT);
  pinMode(pinLT, OUTPUT);
  pinMode(pinEN, OUTPUT);
  // set initial pin states
  fastWrite(pinEN, LOW);      // this one is active low
  fastWrite(pinLT, LOW);
  fastWrite(pinCK, LOW);
  clearDisplay();
}
void loop()
{
  if (Serial.available() ) {
    // all commands terminated by a newline character
    int cmd = Serial.read();
    if (cmd == 'p') {
      // send "p=x,y,c" to set pixel at x,y to colour c
      byte x = Serial.parseInt();
      byte y = Serial.parseInt();
      byte colour = 1; //Serial.parseInt();
      setBufPixel(x, y, colour);
    }
    else if (cmd == 'c') {
      clearDisplay();
    }
    else if (cmd == 'C') {
      byte colour = Serial.parseInt();
      clearDisplay(colour);
    }
    else if (cmd == 'T') {
      testAll(); // scan all 36 rows
    }
    else if (cmd == 't') {
      byte y = Serial.parseInt();
      testRow(y); // scan the given row
    }
    else if (cmd == 's') {
      scanBuffers(); // code from JJ
    }
    else if (cmd == 'd') {
      // set test delay in  milliseconds
      testDelay = Serial.parseInt();
      printf("delay set to %d\n", testDelay);
    }
    else if (cmd == 'b') {
        //draw a box at x,y with given width, height and colour
        // "b=0,0,6,5,4" draws a blue box at upper left, witdh 6, height 5
        byte x = Serial.parseInt();
        byte y = Serial.parseInt();
        byte width = Serial.parseInt();
        byte height = Serial.parseInt();
        byte colour = Serial.parseInt();
        drawRectangle(x,y,width,height,colour);     
    }
  }
  refresh();
}
//================= Matrix Code ============================/
// Save the pixel colour into a buffer mapped to the phyical row layout
// This version has the x,y pixel origin at the upper left of the matrix
void setBufPixel(byte x, byte y, byte colour)
{
  int offset = columnOffsets[y / 6] + (x % 8) + (16 * (x / 8));
  byte row = y % 6;
  row = rowOffsets[row]; // adjust the row order
  matrixBuffer[row][offset] = colour;
  //printf("%d,%d -> row %d, offset %d colour= %d\n", x,y, row, offset, colour); 
}
void selectRow(byte row)  // set the address lines from the row group number
  fastWrite(pinA, (row & 1) );
  fastWrite(pinB, ((row >> 1) & 1) );
  fastWrite(pinC, ((row >> 2) & 1) );
  // TODO - is a small delay needed here?
}
// push colours directly to the port
void writePixel(byte colour)
{
  RGB_PORT = colour;
  fastWrite(pinCK, HIGH);  // clock it
  __asm__("nop\n\t");  __asm__("nop\n\t");  __asm__("nop\n\t");  __asm__("nop\n\t");
  // TODO - adjust the above delay?
  fastWrite(pinCK, LOW);
}
// sets all pixels to the given 3 bit colour or off
void clearDisplay(byte colour)
{
  //Serial.println("Clear");
  memset(matrixBuffer, colour, sizeof(matrixBuffer));
  RGB_PORT = colour; // colours vals 1-7, 0 is off
  for (byte row = 0; row < 6; row++) {
    selectRow(row);
    for ( int i = 0; i < 288; i++ ) {
      fastWrite(pinCK, HIGH);  // clock it
      fastWrite(pinCK, LOW);
    }
    fastWrite(pinLT, HIGH);  // toggle the latch
    fastWrite(pinLT, LOW);
  }
}
// convenience method to turn off all pixels
void clearDisplay()
{
  clearDisplay(0);
}
// write the buffer to the matrix
void refresh()
{
  for (int row = 0; row < _bufferRows; row++) {
    selectRow(row);
    for ( int i = 0; i < _bytesPerBufferRow; i++ ) {
      writePixel(matrixBuffer[row][i]);
    }
    fastWrite(pinLT, HIGH);  // toggle the latch
    //delay(1);
    fastWrite(pinLT, LOW);
  }
}
//================ Drawing Functions =====================
void drawHLine(byte x, byte y, byte width, byte color){ 
    for( byte i=0; i < width; i++)
      setBufPixel(x+i, y, color);
}
void drawVLine(byte x, byte y, byte height, byte color){
  for( byte i=0; i < height; i++)
      setBufPixel(x,y+i,color);
}
void drawRectangle(byte x, byte y, byte width, byte height, byte color){
      drawVLine(x,y,height,color);
      drawVLine(x+width,y, height,color);
      drawHLine(x,y,width,color);
      drawHLine(x,y+height,width,color);   
}
//========================== Test Code ===================/
// scans pixels from top to bottom
void testAll()
{
  for ( byte y = 0; y < displayRows; y++)  {
    testRow(y);
  }
  Serial.println("fin test");
}
// scan pixels displayed on given row y
void testRow(byte y)
{
  for (byte x = 0; x < displayColumns; x++) {
    setBufPixel(x, y, 4); // set a pixel
    refresh();
    delay(testDelay);
    clearDisplay();
  }
}
// code from JJ
void scanBuffers()
{
  for (int row = 0; row < 6; row++) { // loop for each row group
    selectRow(row);
    for (int i = 287; i >= 0; i--) { // working backwards (as data is 'pushed' into the shift register
      for (int j = 0; j < i; j++) { // first fill all ahead with black
        writePixel(0);
      }
      for (int j = 0; j < 8 && j < 288 - i; j++) { // set up to seven pixels to colours
        writePixel(j % 8);  // use colours in reverse order
      }
      for (int j = i + 7; j < 288; j++) { // then fill all behind with black
        writePixel(0);
      }
      fastWrite(pinLT, HIGH);  // toggle the latch
      fastWrite(pinLT, LOW);
      delay(testDelay);    // delay to make the pattern easier to follow
    }
  }
}
</pre>
The fastwrite.h header file is as follows
<pre>
/*
* arduino_io.h
* this file maps arduino pins to avr ports and pins
*
* The header file: pins_arduino.h is used if this exits
* otherwise the following controllers are defined in this file
* Arduino (ATmega8,168,328), Mega, Sanguino (ATmega644P)
*
*/
#include "pins_arduino.h"
#if !(defined(digitalPinToPortReg) && defined(digitalPinToBit))
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__)
  // Standard Arduino Pins
#define digitalPinToPortReg(P) \
    (((P) >= 0 && (P) <= 7) ? &PORTD : (((P) >= 8 && (P) <= 13) ? &PORTB : &PORTC))
#define digitalPinToBit(P) \
    (((P) >= 0 && (P) <= 7) ? (P) : (((P) >= 8 && (P) <= 13) ? (P) - 8 : (P) - 14))
#elif defined(__AVR_ATmega1280__) || defined (__AVR_ATmega2560__)
  // Arduino Mega Pins
#define digitalPinToPortReg(P) \
    (((P) >= 22 && (P) <= 29) ? &PORTA : \
  ((((P) >= 10 && (P) <= 13) || ((P) >= 50 && (P) <= 53)) ? &PORTB : \
    (((P) >= 30 && (P) <= 37) ? &PORTC : \
  ((((P) >= 18 && (P) <= 21) || (P) == 38) ? &PORTD : \
  ((((P) >= 0 && (P) <= 3) || (P) == 5) ? &PORTE : \
    (((P) >= 54 && (P) <= 61) ? &PORTF : \
  ((((P) >= 39 && (P) <= 41) || (P) == 4) ? &PORTG : \
  ((((P) >= 6 && (P) <= 9) || (P) == 16 || (P) == 17) ? &PORTH : \
  (((P) == 14 || (P) == 15) ? &PORTJ : \
    (((P) >= 62 && (P) <= 69) ? &PORTK : &PORTL))))))))))
#define digitalPinToBit(P) \
  (((P) >=  7 && (P) <=  9) ? (P) - 3 : \
  (((P) >= 10 && (P) <= 13) ? (P) - 6 : \
  (((P) >= 22 && (P) <= 29) ? (P) - 22 : \
  (((P) >= 30 && (P) <= 37) ? 37 - (P) : \
  (((P) >= 39 && (P) <= 41) ? 41 - (P) : \
  (((P) >= 42 && (P) <= 49) ? 49 - (P) : \
  (((P) >= 50 && (P) <= 53) ? 53 - (P) : \
  (((P) >= 54 && (P) <= 61) ? (P) - 54 : \
  (((P) >= 62 && (P) <= 69) ? (P) - 62 : \
  (((P) == 0 || (P) == 15 || (P) == 17 || (P) == 21) ? 0 : \
  (((P) == 1 || (P) == 14 || (P) == 16 || (P) == 20) ? 1 : \
  (((P) == 19) ? 2 : \
  (((P) == 5 || (P) == 6 || (P) == 18) ? 3 : \
  (((P) == 2) ? 4 : \
  (((P) == 3 || (P) == 4) ? 5 : 7)))))))))))))))
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) 
// Sanguino or other ATmega644 controller
#define digitalPinToPortReg(P) \
    (((P) >= 0 && (P) <= 7)  ? &PORTB : (((P) >= 8 && (P) <= 15) ? &PORTD : \
  (((P) >= 16 && (P) <= 23) ? &PORTC : &PORTA)))
#define digitalPinToBit(P) \
    (((P) >= 0 && (P) <= 23) ? (P) : (P) - 7 )
#error "ATmega644 has not been tested"
#elif defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) 
#define digitalPinToPortReg(P) \
    (((P) >= 0 && (P) <= 7) ? &PORTA : &PORTB )
#define digitalPinToBit(P) \
    (((P) >= 0 && (P) <= 7) ? (P) : \
  (((P) == 8) ? 2 : \
  (((P) == 9) ? 1 : 0 )))   
#else
#error "Arduino pin mapping not defined for this board"
#endif
#define fastWrite(P, V) \
  if (__builtin_constant_p(P) && __builtin_constant_p(V)) { \
    bitWrite(*digitalPinToPortReg(P), digitalPinToBit(P), (V)); \
  } else { \
    digitalWrite((P), (V)); \
  }
 
#endif
</pre>