|
@@ -11,23 +11,27 @@ I2C i2c(PA_10, PA_9);
|
|
|
// SWO_Channel swo("swo");
|
|
|
|
|
|
bool RtcTick, RefreshTick, RngTick;
|
|
|
-bool StartupFlag, RuntimeFlag;
|
|
|
|
|
|
typedef struct {
|
|
|
- char DigitA;
|
|
|
- ushort DigitAValue;
|
|
|
- char DigitB;
|
|
|
- ushort DigitBValue;
|
|
|
- bool DigitBActive;
|
|
|
+ char Value;
|
|
|
+ ushort Reg;
|
|
|
+ bool Active;
|
|
|
+ bool Increment;
|
|
|
+} DIGIT;
|
|
|
+
|
|
|
+typedef struct {
|
|
|
+ DIGIT Digits[MAX_FADE_DIGITS];
|
|
|
+ DIGIT *NextDigit;
|
|
|
+ DIGIT *PrevDigit;
|
|
|
bool Updated;
|
|
|
bool RefreshActive;
|
|
|
} TUBE;
|
|
|
|
|
|
-TUBE Display[NUM_TUBES] = {0}; // Active per-tube digit setting
|
|
|
+TUBE Tube[NUM_TUBES] = {0}; // Active per-tube digit setting
|
|
|
|
|
|
-int DotValue;
|
|
|
-bool DotUpdated = false;;
|
|
|
-bool DotIncrement = false;
|
|
|
+ushort DotValue = PCA9685_Min_Brightness;
|
|
|
+bool DotUpdated = false;
|
|
|
+bool DotIncrement = true;
|
|
|
|
|
|
Ticker StartupUpdateTicker, RuntimeUpdateTicker, RngUpdateTicker;
|
|
|
int StartupTickerIter, RuntimeTickerIter, RngTickerIter;
|
|
@@ -45,57 +49,35 @@ void RngTickCallback(void) {
|
|
|
RngTick = true;
|
|
|
}
|
|
|
|
|
|
-void StartupTickerUpdate(void) {
|
|
|
+void RuntimeTickerUpdate(void) {
|
|
|
|
|
|
for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- Display[i].DigitAValue = (i == 0 && Display[i].DigitA == 0) ? 0 : Display[i].DigitAValue + FADE_TICK_STEP;
|
|
|
- Display[i].Updated = true;
|
|
|
- }
|
|
|
|
|
|
- DotValue += FADE_TICK_STEP;
|
|
|
- DotUpdated = true;
|
|
|
+ if(!Tube[i].RefreshActive) {
|
|
|
|
|
|
- if (--StartupTickerIter == 0) {
|
|
|
- StartupUpdateTicker.detach();
|
|
|
- for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- Display[i].DigitBActive = !Display[i].DigitBActive;
|
|
|
- }
|
|
|
+ for (int j = 0; j < MAX_FADE_DIGITS; j++) {
|
|
|
|
|
|
- // Switch to runtime mode once startup sequence has completed
|
|
|
- RuntimeFlag = true;
|
|
|
- }
|
|
|
-}
|
|
|
+ DIGIT *digit = &Tube[i].Digits[j];
|
|
|
|
|
|
-void RuntimeTickerUpdate(void) {
|
|
|
+ if (digit->Active) {
|
|
|
|
|
|
- for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- if(!Display[i].RefreshActive) {
|
|
|
+ if (digit->Increment && digit->Reg < PCA9685_Max_Brightness) {
|
|
|
+ digit->Reg += FADE_TICK_STEP;
|
|
|
+ } else if (digit->Reg > PCA9685_Min_Brightness) {
|
|
|
+ digit->Reg -= FADE_TICK_STEP;
|
|
|
+ }
|
|
|
|
|
|
- if (Display[i].DigitA != Display[i].DigitB) {
|
|
|
-
|
|
|
- if (Display[i].DigitBActive) {
|
|
|
- Display[i].DigitAValue -= FADE_TICK_STEP;
|
|
|
- Display[i].DigitBValue = (i == 0 && Display[i].DigitB == 0) ? 0 : Display[i].DigitBValue + FADE_TICK_STEP;
|
|
|
- } else {
|
|
|
- Display[i].DigitBValue -= FADE_TICK_STEP;
|
|
|
- Display[i].DigitAValue = (i == 0 && Display[i].DigitA == 0) ? 0 : Display[i].DigitAValue + FADE_TICK_STEP;
|
|
|
+ Tube[i].Updated = true;
|
|
|
}
|
|
|
-
|
|
|
- Display[i].Updated = true;
|
|
|
- }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- DotValue = DotIncrement ? DotValue + FADE_TICK_STEP: DotValue - FADE_TICK_STEP;
|
|
|
+ DotValue = DotIncrement ? DotValue + FADE_TICK_STEP : DotValue - FADE_TICK_STEP;
|
|
|
DotUpdated = true;
|
|
|
|
|
|
if (--RuntimeTickerIter == 0) {
|
|
|
RuntimeUpdateTicker.detach();
|
|
|
- for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- if (Display[i].DigitA != Display[i].DigitB) {
|
|
|
- Display[i].DigitBActive = !Display[i].DigitBActive;
|
|
|
- }
|
|
|
- }
|
|
|
DotIncrement = !DotIncrement;
|
|
|
}
|
|
|
}
|
|
@@ -109,13 +91,20 @@ void RngTickerUpdate(void) {
|
|
|
}
|
|
|
|
|
|
int main() {
|
|
|
-
|
|
|
+
|
|
|
+ // Initialize pointers in global data structure
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ Tube[i].NextDigit = &Tube[i].Digits[0];
|
|
|
+ }
|
|
|
+
|
|
|
RtcTick = false;
|
|
|
RefreshTick = false;
|
|
|
RngTick = false;
|
|
|
- StartupFlag = true;
|
|
|
- RuntimeFlag = false;
|
|
|
|
|
|
+ int nextSecond, nextMinute, nextHour;
|
|
|
+ int prevMinute, prevHour;
|
|
|
+ bool startup = true;
|
|
|
+
|
|
|
// Start I2C at 400kHz for DS3231
|
|
|
i2c.frequency(400000);
|
|
|
|
|
@@ -125,7 +114,7 @@ int main() {
|
|
|
TUSB322_Init();
|
|
|
PCA9685_Init();
|
|
|
DS3231_Init(RtcTickCallback);
|
|
|
-
|
|
|
+
|
|
|
// Enable HV PSU
|
|
|
HV_EnableOutput(true);
|
|
|
|
|
@@ -135,7 +124,7 @@ int main() {
|
|
|
// swo.printf("CPU SystemCoreClock is %d Hz\r\n", SystemCoreClock);
|
|
|
|
|
|
// Bump I2C frequency to 1MHz
|
|
|
- i2c.frequency(1000000);
|
|
|
+ // i2c.frequency(1000000);
|
|
|
|
|
|
// Setup a ticker to refresh the display at 1kHz
|
|
|
Ticker refreshTicker;
|
|
@@ -164,12 +153,19 @@ int main() {
|
|
|
|
|
|
// On refresh, update the display with new values
|
|
|
|
|
|
- // If values has changed, update both prevous and next digits with new values
|
|
|
for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- if (Display[i].Updated) {
|
|
|
- PCA9685_SetDigit(i, Display[i].DigitA, Display[i].DigitAValue);
|
|
|
- PCA9685_SetDigit(i, Display[i].DigitB, Display[i].DigitBValue);
|
|
|
- Display[i].Updated = false;
|
|
|
+ if (Tube[i].Updated) {
|
|
|
+ Tube[i].Updated = false;
|
|
|
+
|
|
|
+ for (int j = 0; j < MAX_FADE_DIGITS; j++) {
|
|
|
+ DIGIT *digit = &Tube[i].Digits[j];
|
|
|
+ if (digit->Active) {
|
|
|
+ PCA9685_SetDigit(i, digit->Value, digit->Reg);
|
|
|
+ if (digit->Reg == 0 && !digit->Increment) {
|
|
|
+ digit->Active = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -182,54 +178,88 @@ int main() {
|
|
|
if (RtcTick) {
|
|
|
RtcTick = false;
|
|
|
|
|
|
- // On RTC 1Hz ticks, get the time and begin update
|
|
|
- int nextSecond, nextMinute, nextHour;
|
|
|
- DS3231_GetTime(&nextSecond, &nextMinute, &nextHour);
|
|
|
+ prevMinute = nextMinute;
|
|
|
+ prevHour = nextHour;
|
|
|
|
|
|
- if (Display[3].DigitBActive) {
|
|
|
- Display[3].DigitB = nextMinute % 10;
|
|
|
- } else {
|
|
|
- Display[3].DigitA = nextMinute % 10;
|
|
|
- }
|
|
|
+ DS3231_GetTime(&nextSecond, &nextMinute, &nextHour);
|
|
|
|
|
|
- if (Display[2].DigitBActive) {
|
|
|
- Display[2].DigitB = nextMinute / 10;
|
|
|
- } else {
|
|
|
- Display[2].DigitA = nextMinute / 10;
|
|
|
- }
|
|
|
+ if (startup) {
|
|
|
+ startup = false;
|
|
|
+
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+
|
|
|
+ bool Update = false;
|
|
|
+ if (i == 0 && nextHour / 10 != 0) {
|
|
|
+ Tube[i].NextDigit->Value = nextHour / 10;
|
|
|
+ Update = true;
|
|
|
+ } else if (i == 1) {
|
|
|
+ Tube[i].NextDigit->Value = nextHour % 10;
|
|
|
+ Update = true;
|
|
|
+ } else if (i == 2) {
|
|
|
+ Tube[i].NextDigit->Value = nextMinute / 10;
|
|
|
+ Update = true;
|
|
|
+ } else if (i == 3) {
|
|
|
+ Tube[i].NextDigit->Value = nextMinute % 10;
|
|
|
+ Update = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Update) {
|
|
|
+
|
|
|
+ Tube[i].NextDigit->Increment = true;
|
|
|
+ Tube[i].NextDigit->Active = true;
|
|
|
+ Tube[i].PrevDigit = Tube[i].NextDigit;
|
|
|
+ Tube[i].NextDigit = (Tube[i].NextDigit == &Tube[i].Digits[0]) ?
|
|
|
+ &Tube[i].Digits[1] : &Tube[i].Digits[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (Display[1].DigitBActive) {
|
|
|
- Display[1].DigitB = nextHour % 10;
|
|
|
} else {
|
|
|
- Display[1].DigitA = nextHour % 10;
|
|
|
- }
|
|
|
|
|
|
- if (Display[0].DigitBActive) {
|
|
|
- Display[0].DigitB = nextHour / 10;
|
|
|
- } else {
|
|
|
- Display[0].DigitA = nextHour / 10;
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+
|
|
|
+ bool Update = false;
|
|
|
+ if (i == 0 && ((prevHour / 10) != (nextHour / 10))) {
|
|
|
+ if (nextHour / 10 != 0) {
|
|
|
+ Tube[i].NextDigit->Value = nextHour / 10;
|
|
|
+ Tube[i].NextDigit->Increment = true;
|
|
|
+ Tube[i].NextDigit->Active = true;
|
|
|
+ }
|
|
|
+ Update = true;
|
|
|
+ } else if (i == 1 && ((prevHour % 10) != (nextHour % 10))) {
|
|
|
+ Tube[i].NextDigit->Value = nextHour % 10;
|
|
|
+ Tube[i].NextDigit->Increment = true;
|
|
|
+ Tube[i].NextDigit->Active = true;
|
|
|
+ Update = true;
|
|
|
+ } else if (i == 2 && ((prevMinute / 10) != (nextMinute / 10))) {
|
|
|
+ Tube[i].NextDigit->Value = nextMinute / 10;
|
|
|
+ Tube[i].NextDigit->Increment = true;
|
|
|
+ Tube[i].NextDigit->Active = true;
|
|
|
+ Update = true;
|
|
|
+ } else if (i == 3 && ((prevMinute % 10) != (nextMinute % 10))) {
|
|
|
+ Tube[i].NextDigit->Value = nextMinute % 10;
|
|
|
+ Tube[i].NextDigit->Increment = true;
|
|
|
+ Tube[i].NextDigit->Active = true;
|
|
|
+ Update = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Update) {
|
|
|
+ Tube[i].PrevDigit->Increment = false;
|
|
|
+ Tube[i].PrevDigit = Tube[i].NextDigit;
|
|
|
+ Tube[i].NextDigit = (Tube[i].NextDigit == &Tube[i].Digits[0]) ?
|
|
|
+ &Tube[i].Digits[1] : &Tube[i].Digits[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (StartupFlag) {
|
|
|
-
|
|
|
- // Fade in the current time
|
|
|
- StartupTickerIter = FADE_TICKS;
|
|
|
- StartupUpdateTicker.attach(StartupTickerUpdate, (float)(DIGIT_REFRESH_RATE_US / 1000) / StartupTickerIter);
|
|
|
- StartupFlag = false;
|
|
|
- }
|
|
|
-
|
|
|
- if (RuntimeFlag) {
|
|
|
-
|
|
|
- // Fade in the new time
|
|
|
- RuntimeTickerIter = FADE_TICKS;
|
|
|
- RuntimeUpdateTicker.attach(RuntimeTickerUpdate, (float)(DIGIT_REFRESH_RATE_US / 1000) / RuntimeTickerIter);
|
|
|
- }
|
|
|
+ // Fade in the new time
|
|
|
+ RuntimeTickerIter = FADE_TICKS;
|
|
|
+ RuntimeUpdateTicker.attach(RuntimeTickerUpdate, (float)(DIGIT_REFRESH_RATE_US / 1000) / RuntimeTickerIter);
|
|
|
}
|
|
|
|
|
|
if (RngTick) {
|
|
|
|
|
|
// Choose a random tube to refresh
|
|
|
- Display[rand() % 4].RefreshActive = true;
|
|
|
+ Tube[rand() % 4].RefreshActive = true;
|
|
|
|
|
|
RngTickerIter = DIGIT_RNG_REFRESH_ITER * FADE_TICKS;
|
|
|
RngUpdateTicker.attach(RngTickerUpdate, (DIGIT_REFRESH_RATE_US/1000)/FADE_TICKS);
|