De installatie van de hardware voor een PoV display is vrij eenvoudig, maar dit instructable bevat code waar de display kan worden gecontroleerd en geprogrammeerd gemakkelijk via de seriële verbinding, en weergave-instellingen kunnen worden opgeslagen, zodat ze automatisch worden geladen en uitgevoerd wanneer ingeschakeld van een accu.
Wil je een Arduino AVR microcontroller board, zoals een Uno, Nano of mini. Ongeveer 10 LED's, en 10 weerstanden van ongeveer 100 naar 220 Ohm.
Voor ons voorbeeld zullen we veronderstellen dat een 10 LED-display. Draad van de LED's in serie met een weerstand aan digitale I/O pinnen 3-10, en schik ze in een rechte rij.
Laden van de bijgevoegde schets.
Typ vanaf de seriële monitor (of terminal emulator), h om het helpmenu. Dit zal verscheidene opdrachten weergeven.
Mijn klas seriële lezer comprimeert witruimte, zodat u zult willen gebruiken. om te vertegenwoordigen "off" in de regel-instellingen. Zie de bijgevoegde Quelab.dat monster lijn instelling input file. De regels van dit bestand kunnen worden geknipt en geplakt in een terminal emulator om in te stellen van het PoV-bericht.
Zodra het gewenste beeldscherm is geladen, gebruiken de s) opdracht EEPROM te worden gebruikt bij volgende reset zijn instellingen opslaan.
int dummy=0; // this is to force sketch to put arduino include here #define MODE_UNKNOWN 0 #define MODE_PoV 1 #define MODE_RANDOM 2 #define MODE_CYLON 3 #define MAX_COLS 96 #define SERIAL_BUF_LEN (MAX_COLS+15) #include SerialLineBuffer LineBuf; struct { short nCols; // no. columns in buffer short spaceCols; // no. columns "space" time before repeat or reverse short mode; // MODE_ code from above short cylonCols; // no. cols of time for each cylon flash int colTime; // milliseconds/column int misc[3]; // reserved for future use short disp[MAX_COLS]; // display flags } State; // These are the DI/O pins used for the display #define NPINS 10 int ledPins[NPINS] = {12,11,10,9,8,7,6,5,4,3}; #include void loadState() { int n = sizeof(State); byte *bp = (byte *)(&State); for(int i=0; i < n; i++, bp++) *bp = EEPROM.read(i); if (!validState()) initState(); } void saveState() { int n = sizeof(State); byte *bp = (byte *)(&State); for(int i=0; i < n; i++, bp++) EEPROM.write(i,*bp); } // set state to a reasonable default void initState() { State.nCols = 2; State.spaceCols = 1; State.cylonCols = 10; State.mode = MODE_PoV; State.colTime = 10; // ms for (int i=0; i < MAX_COLS; i++) State.disp[i] = (i&1)?0x5555:0x2aaa; saveState(); } void setup() { int i; for (i=0;i { pinMode(ledPins[i],OUTPUT); } pinMode(13,OUTPUT); // use on-board LED // restore state from EEPROM loadState(); Serial.begin(9600); } void loop() { checkCommand(); int i,dt,k; dt = State.colTime; switch (State.mode) { case MODE_CYLON: dt *= State.cylonCols; for (i=0; i < NPINS; i++) { digitalWrite(ledPins[i],HIGH); delay(dt); digitalWrite(ledPins[(i+NPINS-1)%NPINS],LOW); delay(dt); } for (i=NPINS-2; i >= 0; i--) { digitalWrite(ledPins[i],HIGH); delay(dt); digitalWrite(ledPins[(i+1)%NPINS],LOW); delay(dt); } break; case MODE_PoV: for (i=0; i < State.nCols; i++) { short mask=1; for (k=0; k < NPINS; k++, mask <<= 1) digitalWrite(ledPins[k],(mask & State.disp[i])?HIGH:LOW); } for (k=0; k < NPINS; k++) digitalWrite(ledPins[k],LOW); delay(State.spaceCols*dt); break; default: // random default { dt *= 10; k = random(100); int lvl = (k<50)?LOW:HIGH; int j = random(NPINS); digitalWrite(ledPins[j],lvl); delay(dt); } } digitalWrite(13,digitalRead(13)?LOW:HIGH); // toggle heartbeat } // poll for commands from serial port void checkCommand() { short mask; if (!LineBuf.isComplete()) return; char key = lowCase(*(LineBuf.get())); switch(key) { //short mask; //int k; //char *b; case 'h' : Serial.println(" h) help (print this message)"); Serial.println(" s) save state"); Serial.println(" r) random lights mode"); Serial.println(" c) cylon mode"); Serial.println(" p) PoV sign mode"); Serial.println(" n) no. cols to display"); Serial.println(" t) col time, ms"); Serial.println(" b) blank cols between repeat"); Serial.println(" i) re-Initialize state"); Serial.print( " Lx) Set pattern for line x, 0<=x<="); Serial.println(NPINS); break; case 's' : saveState(); break; case 'r' : State.mode = MODE_RANDOM; break; case 'p' : State.mode = MODE_PoV; break; case 'c' : State.mode = MODE_CYLON; break; case 'i' : initState(); break; case 'n' : State.nCols =nextInt(LineBuf.get()+1); break; case 't' : State.colTime =nextInt(LineBuf.get()+1); break; case 'b' : State.spaceCols=nextInt(LineBuf.get()+1); break; case 'l' : { char *b = LineBuf.get()+1; int k = ((int)(*b)) - ((int)'0'); if ((k<0) || (k > 15)) break; b++; short mask = (short)(1< for (int i=0; i < State.nCols; i++, b++) { if (isOn(*b)) State.disp[i] |= mask; else State.disp[i] &= ~mask; } break; } default : Serial.print("Unrecognized Command : <"); Serial.print(LineBuf.get()); Serial.println(">"); Serial.println("Send command h for help."); } printState(); printMsg(); } void printState() { Serial.print(State.nCols); Serial.print(" Columns "); Serial.print(State.spaceCols); Serial.print(" "); Serial.print(State.colTime); Serial.println("ms/col"); Serial.flush(); } void printMsg() { int i,k; Serial.println(); for (i=0; i < State.nCols; i++) Serial.print("-"); short mask=1; for(k=0; k < NPINS; k++, mask <= 1) { for (i=0; i < State.nCols; i++) Serial.print(State.disp[k]&mask?"X":" "); Serial.println("|"); } Serial.println(); for (i=0; i < State.nCols; i++) Serial.print("-"); Serial.println(); Serial.flush(); } // parse next int from a string int nextInt(const char *s) { const char *c = s; int val = 0; for(;;) { int k = ((int)(*c)) - ((int)'0'); if ((k<0)||(k>9)) return val; val *= 10; val += k; c++; } } bool isOn(const char c) { if ((c=='0') || (c=='.') || (c==' ') || (c==0)) return false; //if ((c=='1')||(lowCase(c)=='x')) return true; return true; } bool validState() { // check for silly state, set to default if inconsistent if ((State.mode <= 0) || (State.mode > 3) || (State.nCols< 1) || (State.nCols>MAX_COLS)) return false; if ((State.spaceCols < 1) || (State.spaceCols > 10*MAX_COLS)) return false; if ((State.colTime < 1) || (State.colTime > 10000)) return false; if ((State.cylonCols < 1) || (State.cylonCols > 10000)) return false; return true; }
---ioUtil.h
class SerialLineBufferPrivates; class SerialLineBuffer { public: SerialLineBuffer(); //~SerialLineBuffer(); bool isComplete(); // reads from serial, return true if 0 or EOLN void clear(); void begin(); int length() const; int maxLength() const; char *get(); // retrieve current buffer and clear char buf[SERIAL_BUF_LEN+1]; protected: int _maxLength, _len; bool _complete; private: //class SerialLineBufferPrivates *Priv; }; char lowCase(const char a); int caseCmp(const char a0, const char b0); char *extractKey(char *cmdStr, char **val); bool keyMatch(const char *key, const char *key1);
---ioUtil.cpp
#include #define NULL 0 // don't want to depend on ctype.h, just for this! bool isBlank(int c) { if(c == 7) return(false); // bell return( (c <= ' ') || (c > '~') ); } #if defined(ARDUINO) && ARDUINO >= 100 #include #warning ARDUINO #else #error ARDUINO not >= 100 #include #endif void SerialLineBuffer::begin() { _maxLength = SERIAL_BUF_LEN; // AVR dynamic mem is tricky _len = 0; _complete = false; } SerialLineBuffer::SerialLineBuffer() { begin(); } bool isTerminator(int c) { if (c == 0) return(true); if (c == ';') return(true); // sending \n to serial is tricky. accept this too. //if ((c=='\n') || (c=='\r') || (c=='\m')) return(true); if ((c>=10) && (c<=13)) return(true); // \r, \n, form feed, vert tab return(false); } /// read from serial, return true if 0 or EOLN bool SerialLineBuffer::isComplete() { if (_complete) return(true); // don't read more until this line is consumed // add characters from serial while(Serial.available() > 0) { int nextByte = Serial.read(); //Serial.print("Got ");Serial.println(nextByte); if ((nextByte < 0) || (nextByte >= 256)) return(_complete); if (isTerminator(nextByte)) { //Serial.print("terminator ");Serial.println(nextByte); buf[_len] = 0; _complete = (_len > 0); return(_complete); } if (isBlank(nextByte)) { //Serial.print("blank ");Serial.println(nextByte); if (_len > 0) // ignore leading whitespace { if (buf[_len-1] != ' ') // compact space to 1 space { buf[_len++] = ' '; // convert all space to ' ' } } } else { buf[_len++] = (char)nextByte; } // don't allow overflow if (_len >= _maxLength) { Serial.println("\nOverflow. truncating command string"); _complete = true; } } return(_complete); } void SerialLineBuffer::clear() { _len = 0; _complete = false; } int SerialLineBuffer::length() const { return(_len); } int SerialLineBuffer::maxLength() const { return(_maxLength); } /// retrieve current buffer and clear char *SerialLineBuffer::get() { buf[_len]=0; clear(); return(buf); } //----------------------------------------------------------- /// split a keyword-value pair string into a key string and value string const char nullChar = 0; // static is scary on AVR char *extractKey(char *cmdStr, char **val) { *val = (char *)&nullChar; if (cmdStr == NULL) return(NULL); char *key = cmdStr; while (*key) // process comments { if (*key == '#') *key=0; // comment else key++; } key = cmdStr; while(*key && isBlank(*key)) key++; // trim leading space *val = key; while(**val && !isBlank(**val)) *val += 1; // skip key **val = 0; *val += 1; while(**val && isBlank(**val)) *val += 1; // skip whitespace return(key); } char lowCase(const char a) { if ((a >= 'A') && (a <= 'Z')) { int sft = ((int)'a') - ((int)'A'); int b = (int)a + sft; return((char)b); } return(a); } int caseCmp(const char a0, const char b0) { char a = lowCase(a0); char b = lowCase(b0); if (a < b) return(-1); return((a>b)?1:0); } bool keyMatch(const char *key0, const char *key1) { //Serial.print("keyMatch(");Serial.print(key0);Serial.print(",");Serial.print(key1);Serial.print(")="); while(*key0 || *key1) { if (caseCmp(*key0, *key1)) { //Serial.println("false"); return(false); } if (*key0) key0++; if (*key1) key1++; } //Serial.println("true"); return(true); }
---Invoergegevens voorbeeld
L0..Q.............l......b.... L1.QQQ............l......b.... L2Q...Q...........l......b.... L3Q...Q.u..u..ee..l..a.a.b.b.. L4Q...Q.u..u.e..e.l.a.aa.bb.b. L5Q...Q.u..u.e..e.l.a..a.b..b. L6Q.Q.Q.u..u.eeee.l.a..a.b..b. L7Q..Q..u..u.e....l.a..a.b..b. L8.Q.Q..u..u.e..e.l.a.aa.bb.b. L9.QQ.Q..uu...ee..l..a.a.b.b..