/* POV LED Display for Time www.catahoulatech.com Original Author: Jon B Stanley Version history: 1.0 - August 4, 2014 1.1 - September 7, 2014 Added pushbutton time update routine Leading 10Hr 0 will be hidden 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 1 // Select if bitmap should read legitibly at top or bottom #define msgDispMode 0 int displayIndex = 0; int col=0; int i; int j; int changeTime = 0; byte scrollIndex=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 falling calls "buttonPress" routine attachInterrupt(1,buttonPress,FALLING); // Clear the display bitmap for(j=0;j0 && checkColTime(displayIndex)) { if(displayIndex<=displayRes) { // Subtract 1 from displayIndex because after index sensor pulses // the "indexDisplay" ISR will set displayIndex to 1 and this value // is used as the bitmap array index which is 0 indexed. // displayIndex indicates which column from 1 to displayRes that // the LEDs are physically located at this given instant // scrollIndex artifically moves the column 1 location to create // a message scrolling effect when outputting column values to the LEDs 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); } } } } else { // Entire bitmap has been displayed, turn off LEDs and // wait until next index pulse. Normally this will happen // just right before the next index pulse arrives. displayIndex=0; outputLedColumn(0,0); } } // Check if 1 second has elapsed // or the time set pushbutton has been pressed // and then update the time if(checkOneSecond() | changeTime == 1) { if(changeTime == 0) { updateTime(0); } else { updateTime(1); changeTime = 0; } for(j=0;j<8;j++){ for(col=0;col<5;col++) { // Time is readable at bottom, append character columns to bitmap to the right if(msgDispMode==0){ // Do not display the 10th hour if 0 if(time[7] == 0 & j == 0) { bitmap[6*j+col] = 0; } else { bitmap[6*j+col] = digits[time[7-j]][col]; } } // Time is readable at top, append character columns to bitmap to the left else { // Do not display the 10th hour if 0 if(time[7] == 0 & j == 0) { bitmap[(displayRes-1)-(6*j+col)] = 0; } else { bitmap[(displayRes-1)-(6*j+col)] = 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() { // Note: The rate at which the minutes advance // depends on how often the POV board passes by the button IR sensor // That is, a slow refreshing display will advance minutes slower. changeTime = 1; } // 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; } } void updateTime(int updateMethod){ // Normal time update mode if updateMethod=0 // Zero seconds and advance minutes if updateMethod=1 if(time[0] == 9 | updateMethod == 1) { time[0] = 0; if(time[1] == 5 | updateMethod == 1) { 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]++; } }