/* POV LED Display for Time www.catahoulatech.com Original Author: Jon B Stanley Version history: 1.0 - August 4, 2014 This Arduino sketch is released in the public domain. Please maintain this header and revision history. */ // Font for digit to 5x8 pixel bitmap int digits[][5] = { {510,513,513,513,510}, // 0 ASCII 48 {0,514,1023,512,0}, // 1 ASCII 49 {900,578,545,546,540}, // 2 ASCII 50 {388,514,545,546,476}, // 3 ASCII 51 {96,88,70,1023,64}, // 4 ASCII 52 {399,521,521,529,481}, // 5 ASCII 53 {508,546,529,529,482}, // 6 ASCII 54 {3,897,97,25,7}, // 7 ASCII 55 {492,530,529,273,238}, // 8 ASCII 56 {398,529,529,273,254}, // 9 ASCII 57 {0,204,204,0,0} // : ASCII 58 }; // Arduino pin locations of LEDs in column int leds[10] = {4,5,6,7,8,9,10,11,12,13}; // Pin 3 = ISR0 = Index sensor // Pin 4 = ISR1 = Pushbutton sensor // Display resolution (number of LED columns in 1 revolution) #define displayRes 200 // Offset for the display bitmap // Warning: this code does not check if the bitmap is offset too far! #define displayOffset 30 // 0 = Disable display scrolling, 1 = enable display scrolling #define scrollEnable 0 // Select if bitmap should read legitibly at top or bottom #define msgDispMode 1 int displayIndex = 0; int col=0; int i; int j; byte msgIndex=0; unsigned long lastTime = 0; unsigned long displayColTime = 0; unsigned long lastSecond = 0; // Initial power-up time is 12:00:00 // The array is in the order of [1s,10s,colon,1m,10m,colon,1h,10h] // The colon is the 10th font in the digits array above int time[8] = {0,0,10,0,0,10,2,1}; int bitmap[displayRes]; // initial routine that runs after power on reset void setup() { // initialize the LED column pins as outputs. for(int i=0; i<10; i++) { pinMode(leds[i], OUTPUT); } // Index sensor calls "indexDisplay" routine attachInterrupt(0,indexDisplay,FALLING); // Button sensor calls "buttonPress" routine attachInterrupt(1,buttonPress,FALLING); // Clear the display bitmap for(j=0;j0 && checkColTime(displayIndex)) { if((displayIndex-1+scrollIndex)>displayRes-1) { // Message is readable at bottom, scrolls clockwise if(msgDispMode==0) { outputLedColumn(bitmap[(displayIndex-1)+scrollIndex-displayRes],1); } // Message is readable at top, scrolls counterclockwise else { outputLedColumn(bitmap[(displayIndex-1)+scrollIndex-displayRes],0); } } else { if(msgDispMode == 0) { outputLedColumn(bitmap[(displayIndex-1)+scrollIndex],1); } else { outputLedColumn(bitmap[(displayIndex-1)+scrollIndex],0); } } // Increment the displayIndex so the next iteration of this loop // the checkColTime routine will ensure the LEDs have been lit for the // proper duration of the rotation time displayIndex++; // Display bitmap will shift by 1 column on every 120th column display // Select a value smaller than 120 for faster scrolling, larger than 120 for slower if(displayIndex%120==0 and scrollEnable==1) { // Message is readable at bottom, scrolls clockwise if(msgDispMode == 0) { if(scrollIndex0) { scrollIndex--; } else { scrollIndex=(displayRes-1); } } } // Check if 1 second has elapsed // and then update the time if(checkOneSecond()) { if(time[0] == 9) { time[0] = 0; if(time[1] == 5) { time[1] = 0; if(time[3] == 9) { time[3] = 0; if(time[4] == 5) { time[4] = 0; if(time[6] == 2 && time[7] == 1) { time[6] = 1; time[7] = 0; } else if(time[6] == 9) { time[6] = 0; time[7]++; } else { time[6]++; } } else { time[4]++; } } else { time[3]++; } } else { time[1]++; } } else { time[0]++; } // Update the display bitmap with the new time for(j=0;j<8;j++){ for(col=0;col<5;col++) { bitmap[6*j+col+displayOffset] = digits[time[7-j]][col]; } } } } // This ISR routine runs whenever the Index sensor pulses // When this happens, the time of the last revolution is // recorded and used for display timing in the next revolution // to make the display independent of motor speed void indexDisplay() { displayIndex = 1; displayColTime = (micros() - lastTime)/displayRes; lastTime = micros(); } void buttonPress() { // This function does nothing at the moment // Intended to be used to change the time or display modes // Since this interrupt does not necessarily happen once for a single // button press because the POV display board has to pass over the // IR LED multiple times, there may be a need to count pulses before // deciding the button has been pressed long enough to process. } // Function to output integer value to LED array void outputLedColumn(int colValue, int bitDir) { // Currently configured for 10 LEDs for(i=0;i<10;i++) { if(bitDir == 0) { // top to bottom LED bit order digitalWrite(leds[i],bitRead(colValue,9-i)); } else { // bottom to top LED bit order digitalWrite(leds[i],bitRead(colValue,i)); } } } boolean checkColTime(int colIndex){ if((micros()-lastTime)>=(displayColTime*colIndex)){ return true; } else { return false; } } // Function returns true if 1 second has elapsed // There is an error of a few microseconds due to using // the micros() function. A more direct and better timer interrupt // setup is probably required for long term accuracy. boolean checkOneSecond(){ if((micros()-lastSecond)>=1000000){ // Subtract 1 to compensate for the time it takes // to actually write the last second value // The vaue of 1 may need tweaking for long-term accuracy. lastSecond = micros()-1; return true; } else { return false; } }