Commits

Trammell Hudson committed dd1b4b6

Removed buffer; compute display from ASCII display[] on the fly

Comments (0)

Files changed (1)

alphaclock_18_Rev1_0/alphaclock_18_Rev1_0.pde

 #define bit_is_set(PORT, BIT) ((PORT) & (1 << (BIT)))
 
 
+/** This is the AlphaClockFive. */
+#define NUM_DIGITS 5
+
+
 /** Deslect all LED drivers to disable all digits */
 static inline void
 led_all_off(void)
 }
 
 
-/** Select a single LED digit driver */
+/** Select a single LED digit driver.
+ * The display buffer is stored from left to right, but the digits
+ * are indexed from right to left.
+ */
 static inline void
 led_digit_select(
 	const byte j
 )
 {
-	cbi(PORTA, j);
+	cbi(PORTA, NUM_DIGITS - j - 1);
 }
 
 
 byte HrAlrmOnes;
 byte HrAlrmTens;
 
-byte CharacterBuffer[10];  // What position in char array
 
-byte bufl1[10];  // First byte  (low power)
-byte bufh1[10];  // second byte (high power)
-byte bufh2[10];  // third byte  (high power)
+/** The characters to be displayed and the positions for the dots.
+ */
+static byte display[NUM_DIGITS];
+static byte display_dots[NUM_DIGITS];
 
-char stringTemp[5];
+#define DOT_TOP 'U'
+#define DOT_BOTTOM 'L'
+#define DOT_BOTH 'B'
+
 
 /*
  * The font for the LED digit display.
         1 << 0xA,
         
         // Chaser goes around the outside of the box
-        1 << 0,
-        1 << 1,
-        1 << 6,
-        1 << 7,
-        1 << 3,
-        1 << 2,
-        1 << 8,
-        1 << 9,
+        1 << 0x0,
+        1 << 0x1,
+        1 << 0x6,
+        1 << 0x7,
+        1 << 0x3,
+        1 << 0x2,
+        1 << 0x8,
+        1 << 0x9,
 };
 
 
-// Starting offset of our ASCII array:
-#define asciiOffset ' '
-
-// Starting offset for number zero
-#define numberOffset ('0' - asciiOffset)
-
-
 /**
  * Faster latch macro!
  * \todo What does this do?
 
 
 /*
- * Send the three byte command to the LED driver via SPI.
+ * Write a digit to the LED display.
+ *
+ * The top two bits are used to select the dots.
+ *
  * \note Blocks until it is done.
  */
 static void
-AlphaWrite(
-	const byte l1,
-	const byte h1,
-	const byte h2
+digit_write(
+	const byte c,
+	const byte dots
 )
 {
+	const uint16_t word
+		= pgm_read_word_near(AlphaArray + c - ' ');
+
 	led_all_off();
-	spi_write(l1);
-	spi_write(h1);
-	spi_write(h2);
+
+	// note that b2/b3 are shuffled relative to the original firmware
+	byte b1 = ((word >> 0) & 0x3F);
+	byte b2 = word >> 14;
+	byte b3 = word >> 6;
+
+	// Add the dots back into the first byte sent
+	if (dots == DOT_TOP)
+		b1 |= 128;
+	else
+	if (dots == DOT_BOTTOM)
+		b1 |= 64;
+	else
+	if (dots == DOT_BOTH)
+		b1 |= 64 | 128;
+
+	spi_write(b1);
+	spi_write(b2);
+	spi_write(b3);
 }
 
 
 byte HoldLEDTest;
 byte LEDTestStage;
 
-void LoadCharBuffer (char WordIn[])
-{ 
 
-  CharacterBuffer[0] = (WordIn[4] - asciiOffset);
-  CharacterBuffer[1] = (WordIn[3] - asciiOffset);
-  CharacterBuffer[2] = (WordIn[2] - asciiOffset);
-  CharacterBuffer[3] = (WordIn[1] - asciiOffset);
-  CharacterBuffer[4] = (WordIn[0] - asciiOffset);
+static inline void
+display_char(
+	const byte pos,
+	const byte c
+)
+{
+	display[pos] = c;
 }
 
-void DisplayWordSequence (char WordIn[], unsigned int durationMillis)
-{
-  WordStopTime = millis() + durationMillis; 
 
-  CharacterBuffer[0] = (WordIn[4] - asciiOffset);
-  CharacterBuffer[1] = (WordIn[3] - asciiOffset);
-  CharacterBuffer[2] = (WordIn[2] - asciiOffset);
-  CharacterBuffer[3] = (WordIn[1] - asciiOffset);
-  CharacterBuffer[4] = (WordIn[0] - asciiOffset);
-
-  LoadShiftRegBuffers();
-  DisplayWordMode = 1; 
-  OptionNameSequence++;
+static void
+display_string(
+	const char WordIn[]
+)
+{ 
+	for (byte i = 0 ; i < NUM_DIGITS ; i++)
+		display_char(i, WordIn[i]);
 }
 
 
+static void
+display_sequence(
+	const char WordIn[],
+	unsigned int durationMillis
+)
+{
+	WordStopTime = millis() + durationMillis; 
+	display_string(WordIn);
+	DisplayWordMode = 1; 
+	OptionNameSequence++;
+}
+
 
 #define EELength 8
 byte EEvalues[EELength];
      while((Serial.peek() != TIME_HEADER) &&  (Serial.peek() >= 0))  // Flush buffer up until next 0xFF.    
      Serial.read();
      
-     DisplayWordSequence("FLUSH",500);     //TODO: Remove this debug message
+     display_sequence("FLUSH",500);     //TODO: Remove this debug message
      }
      */
 
     if( Serial.read() == TIME_HEADER) { 
 
-      //   DisplayWordSequence("RECV ",100);  //TODO: Remove this debug message
+      //   display_sequence("RECV ",100);  //TODO: Remove this debug message
       //    Read command, next two bytes:
       charTemp = Serial.read();
       charTemp2 = Serial.read();
 
       if( charTemp == 'S' ){ 
 
-        // DisplayWordSequence("RECVS",100);  //TODO: Remove this debug message
+        // display_sequence("RECVS",100);  //TODO: Remove this debug message
         if( charTemp2 == 'T' ){ 
           // Time setting mode:
-          //DisplayWordSequence("RECVT",100);  //TODO: Remove this debug message
+          //display_sequence("RECVT",100);  //TODO: Remove this debug message
 
           time_t pctime = 0;
           for( i=0; i < 10; i++){   
 
       }
 
-      else if( charTemp == 'A' ){  
+      else if( charTemp == 'A' ){
         if( charTemp2 == '0' )  {
 
-          //  DisplayWordSequence("RECA0",500);  //TODO: Remove this debug message
-          // ASCII display mode, first 5 chars will be displayed.
+          //  display_sequence("RECA0",500);  //TODO: Remove this debug message
+		// ASCII display mode; read the first 5 characters normally
+		for( i=0; i < NUM_DIGITS; i++)
+			display[i] = Serial.read();
 
-          for( i=0; i < 10; i++){   
-            charTemp = Serial.read();          
+		// And then the next five for setting the dots.
+		for( i=0; i < NUM_DIGITS; i++)
+			display_dots[i] = Serial.read();
 
-            if (i < 5)
-            {
-              CharacterBuffer[4 - i] = charTemp - asciiOffset;
-            } 
-            else
-            {
-              CharacterBuffer[i] = charTemp;
-            }
-
-          }   
-
-          LoadShiftRegBuffers(); 
           DisplayWordMode = 1;  
-
-
-          for( i=5; i < 10; i++){  
-
-            if (CharacterBuffer[i] == 'L')
-              bufl1[9 - i] |= 64;  // Add lower DP 
-            if (CharacterBuffer[i] == 'U')
-              bufl1[9 - i] |= 128;  // Add upper DP 
-            if (CharacterBuffer[i] == 'B')
-              bufl1[9 - i] |= 192;  // Add both DPs 
-
-          }
           SerialDisplayMode  = 1;
           // Serial.println("Writing Text!");
         }
     }
     else
     {
-      DisplayWordSequence("ERROR",200);  //Display error!
+      display_sequence("ERROR",200);  //Display error!
     }
   }
   return false;  //if no message return false
 
 
 
-/**
- * Map the 5-character cotents of the ASCII Character Buffer into the
- * 15 SPI output bytes (three bytes per LED character) needed to draw
- * those characters.
- *
- * The font is packed so that the dots are not included in the bitmap and the
- * h1 only ever has two bits set, so it is stored at the top of the word.
- */
-void
-LoadShiftRegBuffers(void)
-{
-	for (byte j = 0 ; j < 5 ; j++)
-	{
-		const uint16_t word
-			= pgm_read_word_near(AlphaArray + CharacterBuffer[j]);
-
-		bufl1[j] = (word >> 0) & 0x3F; // mask out the dots
-		bufh2[j] = word >> 6;
-		bufh1[j] = word >> 14;
-	}
-}
-
-        
-/** Send a digit to one LED.
+/** Send a character c to LED digit.
  * Blocks until the digit is written and PWM'ed to create the effect.
  */
 static inline void
-AlphaWritePwm(
-	const byte j,
+digit_write_pwm(
+	const byte digit,
+	const byte c,
+	const byte dots,
 	const byte on_period,
 	const byte off_period
 )
 {  
-	AlphaWrite(bufl1[j],bufh1[j],bufh2[j]);      
+	digit_write(c, dots);
 	Latch();
-	led_digit_select(j);
+	led_digit_select(digit);
 
 	// For low brightness levels, we use a very low duty cycle PWM.
 	// That's normally fine, but the Arduino interrupts in the background
 
 
 static void
-refreshDisplay(void)
+display_refresh(void)
 {
 	byte bright = MainBright;
 
 	const byte off_period = pgm_read_byte_near(&pwm->off_period);
 
 	for (byte i = 0 ; i < loops ; i++)
-		for (byte j = 0 ; j < 5 ; j++)
-			AlphaWritePwm(j, on_period, off_period);
+	{
+		for (byte j = 0 ; j < NUM_DIGITS ; j++)
+		{
+			const byte c = display[j];
+			const byte dots = display_dots[j];
+			digit_write_pwm(j, c, dots, on_period, off_period);
+		}
+	}
 }
 
 
   SPCR = _BV(SPE) | _BV(MSTR);  // Initialize SPI, fast!
   SPSR |= 1;  // enable double-speed mode!	
 
-  AlphaWrite(0,0,0); 
+  digit_write(' ', '_');
   Latch();
 
   SecNow = 0; 
 
   PORTA |= _BV(6); // Blank LED driver
 
-  DisplayWordSequence("HELLO",1000); 
+#if 0
+  display_sequence("HELLO",1000); 
 
   while (  millis() < WordStopTime){    
-    refreshDisplay ();
+    display_refresh();
     DisplayWordMode = 0;
   }
 
   delay (10);
 
-  DisplayWordSequence("WORLD",2000);
+  display_sequence("WORLD",2000);
 
   while (  millis() < WordStopTime){    
-    refreshDisplay ();
+    display_refresh();
     DisplayWordMode = 0;
   }
 
   delay (250);
+#endif
 
 
   VCRmode = 1;  // Time is NOT yet set.
       {         
         snoozed = 1;
         TurnAlarmOff(); 	
-        DisplayWordSequence("SNOOZ",2500); 
+        display_sequence("SNOOZ",2500); 
 
         AlarmTimeSnoozeHr = HrNow;
         AlarmTimeSnoozeMin  = MinNow + 9;    // Nine minutes, from time *snooze button pressed*
         snoozed = 0;
 
         delay (100);
-        DisplayWordSequence("ALARM",1000);
+        display_sequence("ALARM",1000);
 
         while (  millis() < WordStopTime){    
-          refreshDisplay ();
+          display_refresh();
           DisplayWordMode = 0;
         }
 
         delay (100);
 
-        DisplayWordSequence(" OFF ",1000);
+        display_sequence(" OFF ",1000);
 
         while (  millis() < WordStopTime){    
-          refreshDisplay ();
+          display_refresh();
           DisplayWordMode = 0;
         }
 
         LEDTestMode = 0;
       else{
         LEDTestMode = 1;
-        DisplayWordSequence("VER10",2000);   // Display software version number, 1.0
-        bufl1[1] |= 64;  // Add lower DP for proper "1.0" !
+        display_sequence("VER10",2000);   // Display software version number, 1.0
+        display_dots[1] = DOT_BOTTOM;  // Add lower DP for proper "1.0" !
       }
     }
 
       // do not change the display.
       if (SecNow & 1) {
 
-        DisplayWordSequence("ALARM",100);
-        //LoadShiftRegBuffers();
+        display_sequence("ALARM",100);
         //DisplayWordMode = 1; 
       }
     }
   }
 
 
-
-
+#if 0
   if (LEDTestMode)
   {
 
 
     }
   }
+#endif
 
 
 
     if (OptionMode == 1) {  // AM-PM / 24 HR   
 
       if (HourMode24) 
-        LoadCharBuffer("24 HR"); 
+        display_string("24 HR"); 
       else
-        LoadCharBuffer("AM/PM");
-
-      LoadShiftRegBuffers();
+        display_string("AM/PM");
     }
     else if (OptionMode == 2)
     {
       if (OptionNameSequence == 0)
       {
-        DisplayWordSequence("NIGHT",700);      
+        display_sequence("NIGHT",700);      
       }
       else if (OptionNameSequence == 1)
-        DisplayWordSequence("     ",100);
+        display_sequence("     ",100);
       else if (OptionNameSequence == 2)
       {
-        DisplayWordSequence("LIGHT",700);
+        display_sequence("LIGHT",700);
       }
       else if (OptionNameSequence == 3)
-        DisplayWordSequence("     ",100);
+        display_sequence("     ",100);
       else {
         if(  NightLightType == 0)
         {
-          LoadCharBuffer(" NONE");
-          LoadShiftRegBuffers(); 
+          display_string(" NONE");
         }
         else if(  NightLightType == 1)
         {
-          LoadCharBuffer("LED_L");
-          LoadShiftRegBuffers(); 
+          display_string("LED_L");
         }
         else if(  NightLightType == 2)
         {
-          LoadCharBuffer("LED_H");
-          LoadShiftRegBuffers(); 
+          display_string("LED_H");
         }
 
       }
     {
       if (OptionNameSequence == 0)
       {
-        DisplayWordSequence("ALARM",700); 
+        display_sequence("ALARM",700); 
       }
       else if (OptionNameSequence == 1)
-        DisplayWordSequence("     ",100);
+        display_sequence("     ",100);
       else if (OptionNameSequence == 2)
       {
-        DisplayWordSequence("TONE ",700);
+        display_sequence("TONE ",700);
       }
       else if (OptionNameSequence == 3)
-        DisplayWordSequence("     ",100);
+        display_sequence("     ",100);
       else {
         if(  AlarmTone == 0)
         {
-          LoadCharBuffer(" HIGH");
-          LoadShiftRegBuffers(); 
+          display_string(" HIGH");
         }
         else if(  AlarmTone == 1)
         {
-          LoadCharBuffer(" MED ");
-          LoadShiftRegBuffers(); 
+          display_string(" MED ");
         }
         else if(  AlarmTone == 2)
         {
-          LoadCharBuffer(" LOW ");
-          LoadShiftRegBuffers(); 
+          display_string(" LOW ");
         }
         else if(  AlarmTone == 3)
         {
-          LoadCharBuffer("SIREN");
-          LoadShiftRegBuffers(); 
+          display_string("SIREN");
         }
 
       }
     {
       if (OptionNameSequence == 0)
       {
-        DisplayWordSequence("TEST ",700);
+        display_sequence("TEST ",700);
 
       }
       else if (OptionNameSequence == 1)
-        DisplayWordSequence("     ",100);
+        display_sequence("     ",100);
       else if (OptionNameSequence == 2)
       {
-        DisplayWordSequence("SOUND",700); 
+        display_sequence("SOUND",700); 
       }
       else if (OptionNameSequence == 3)
-        DisplayWordSequence("     ",100);
+        display_sequence("     ",100);
       else{
-        LoadCharBuffer("USE+-");
-        LoadShiftRegBuffers(); 
+        display_string("USE+-");
       }
     }
   }
 
     if ((PINBcopy & 1) == 0){
       // Display Alarm time whenever "Alarm/Snooze" button is pressed. 
-      CharacterBuffer[0] = (AMPM24HdisplayAlrm - asciiOffset);
-      CharacterBuffer[1] = (MinAlrmOnes + numberOffset);
-      CharacterBuffer[2] = (MinAlrmTens + numberOffset);
-      CharacterBuffer[3] = (HrAlrmOnes + numberOffset);
-      CharacterBuffer[4] = (HrAlrmTens + numberOffset);
+      display_char(4, AMPM24HdisplayAlrm);
+      display_char(3, MinAlrmOnes + '0');
+      display_char(2, MinAlrmTens + '0');
+      display_char(1, HrAlrmOnes + '0');
+      display_char(0, HrAlrmTens + '0');
 
       // Leading-Zero blanking for 12-hour mode:
 
       if (ZeroHourBlankAlrm)
-        CharacterBuffer[4] = (' ' - asciiOffset);
+        display_char(0,' ');
     }
     else {
       // "Normal" time display: 
-      //CharacterBuffer[0] = (AMPM24HdisplayNow - asciiOffset);
-      //CharacterBuffer[0] = (SecNow % 8) + 1 + '`' - asciiOffset; // spinner
-      CharacterBuffer[0] = (SecNow % 8) + 1 + 8  + '`' - asciiOffset; // chaser     
-      CharacterBuffer[1] = (MinNowOnes + numberOffset);
-      CharacterBuffer[2] = (MinNowTens + numberOffset);
-      CharacterBuffer[3] = (HrNowOnes + numberOffset);
-      CharacterBuffer[4] = (HrNowTens + numberOffset);
+      //display[0] = AMPM24HdisplayNow;
+      //display[0] = (SecNow % 8) + 1 + 8  + '`'; // chaser     
+      display_char(3, MinNowOnes + '0');
+      display_char(2, MinNowTens + '0');
+      display_char(1, HrNowOnes + '0');
+      display_char(0, HrNowTens + '0');
+      display_char(4, (SecNow % 8) + 1 + '`'); // Spinner
 
       // Leading-Zero blanking for 12-hour mode:
 
       if (ZeroHourBlankNow)
-        CharacterBuffer[4] = (' ' - asciiOffset);
+        display_char(0, ' ');
     }
 
-    LoadShiftRegBuffers();
-
     // Add time delimiter (colon) for time display, whether that's "real" time or the alarm.
-    bufl1[2] |= 128;
-    bufl1[3] |= 64;    
+    display_dots[1] = DOT_BOTTOM;
+    display_dots[2] = DOT_TOP;
 
     if (AlarmEnabled)
-      bufl1[4] |= 128;    // Upper left dot
+      display_dots[0] = DOT_TOP;
   }
 
   // Time (or word) to display is now computed.
   // Now is the place in the loop when we switch gears, and 
   // actually light up the LEDs. :)
 
-  refreshDisplay();
-
+  display_refresh();
 
   // Can this sync be tried only once per second?
   if( getPCtime()) {  // try to get time sync from pc
 
     if(DateTime.available()) { // update clocks if time has been synced
 
-      DisplayWordSequence("SYNC ",1000);
+      display_sequence("SYNC ",1000);
       // Set time to that given from PC.
       MinNow = DateTime.Minute;
       SecNow = DateTime.Second;
 /*
 Simple alternative loop , for testing brightness/refresh rates
  void loop()
- {   refreshDisplay();
+ {   display_refresh();
  }
  */