2
edits
(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> |