|
@@ -12,26 +12,28 @@ I2C i2c(PA_10, PA_9);
|
|
|
|
|
|
bool RtcTick, RefreshTick, RngTick;
|
|
|
|
|
|
-typedef struct {
|
|
|
- char Value;
|
|
|
- ushort Reg;
|
|
|
- bool Active;
|
|
|
- bool Increment;
|
|
|
-} DIGIT;
|
|
|
+typedef enum {
|
|
|
+ Idle = 0,
|
|
|
+ Incrementing = 1,
|
|
|
+ Decrementing = 2,
|
|
|
+} DigitState;
|
|
|
|
|
|
typedef struct {
|
|
|
- DIGIT Digits[MAX_FADE_DIGITS];
|
|
|
- DIGIT *NextDigit;
|
|
|
- DIGIT *PrevDigit;
|
|
|
+ DigitState CurrentState;
|
|
|
+ int Value;
|
|
|
+ int Brightness;
|
|
|
+ int Step;
|
|
|
bool Updated;
|
|
|
- bool RefreshActive;
|
|
|
-} TUBE;
|
|
|
+} Digit;
|
|
|
|
|
|
-TUBE Tube[NUM_TUBES] = {0}; // Active per-tube configuration
|
|
|
+typedef struct {
|
|
|
+ Digit Digits[10];
|
|
|
+ bool Refresh;
|
|
|
+ int UpdateRate;
|
|
|
+} Tube;
|
|
|
|
|
|
-ushort DotValue = PCA9685_Min_Brightness;
|
|
|
-bool DotUpdated = false;
|
|
|
-bool DotIncrement = true;
|
|
|
+Tube Tubes[4]; // Active per-tube configuration
|
|
|
+Digit Dot;
|
|
|
|
|
|
Ticker RuntimeUpdateTicker, RngUpdateTicker;
|
|
|
int RuntimeTickerIter, RngTickerIter;
|
|
@@ -48,33 +50,55 @@ void RngTickCallback(void) {
|
|
|
RngTick = true;
|
|
|
}
|
|
|
|
|
|
-void RuntimeTickerUpdate(void) {
|
|
|
-
|
|
|
- for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- if(!Tube[i].RefreshActive) {
|
|
|
- for (int j = 0; j < MAX_FADE_DIGITS; j++) {
|
|
|
- DIGIT *digit = &Tube[i].Digits[j];
|
|
|
- if (digit->Active) {
|
|
|
- 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;
|
|
|
- }
|
|
|
- Tube[i].Updated = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+Timeout DotUpdateTick;
|
|
|
+void DotUpdateCallback(void) {
|
|
|
+ if (Dot.CurrentState == Incrementing && Dot.Brightness < PCA9685_Max_Brightness) {
|
|
|
+ Dot.Brightness += Dot.Step;
|
|
|
+ } else if (Dot.CurrentState == Decrementing && Dot.Brightness > PCA9685_Min_Brightness) {
|
|
|
+ Dot.Brightness -= Dot.Step;
|
|
|
}
|
|
|
+ Dot.Updated = true;
|
|
|
+
|
|
|
+ if (Dot.Brightness == PCA9685_Max_Brightness ||
|
|
|
+ Dot.Brightness == PCA9685_Min_Brightness) {
|
|
|
+ DotUpdateTick.detach();
|
|
|
+ } else {
|
|
|
+ // DotUpdateTick.attach(DotUpdateCallback, (float)1/DOT_FADE_TICKS);
|
|
|
+ DotUpdateTick.attach_us(DotUpdateCallback, 700);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- DotValue = DotIncrement ? DotValue + FADE_TICK_STEP : DotValue - FADE_TICK_STEP;
|
|
|
- DotUpdated = true;
|
|
|
+void Tube0UpdateCallback(void) {
|
|
|
|
|
|
- if (--RuntimeTickerIter == 0) {
|
|
|
- RuntimeUpdateTicker.detach();
|
|
|
- DotIncrement = !DotIncrement;
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
+// void RuntimeTickerUpdate(void) {
|
|
|
+
|
|
|
+// for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+// if(!Tube[i].RefreshActive) {
|
|
|
+// for (int j = 0; j < MAX_FADE_DIGITS; j++) {
|
|
|
+// DIGIT *digit = &Tube[i].Digits[j];
|
|
|
+// if (digit->Active) {
|
|
|
+// 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;
|
|
|
+// }
|
|
|
+// Tube[i].Updated = true;
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
+// DotValue = DotIncrement ? DotValue + FADE_TICK_STEP : DotValue - FADE_TICK_STEP;
|
|
|
+// DotUpdated = true;
|
|
|
+
|
|
|
+// if (--RuntimeTickerIter == 0) {
|
|
|
+// RuntimeUpdateTicker.detach();
|
|
|
+// DotIncrement = !DotIncrement;
|
|
|
+// }
|
|
|
+// }
|
|
|
+
|
|
|
void RngTickerUpdate(void) {
|
|
|
|
|
|
|
|
@@ -86,9 +110,9 @@ 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];
|
|
|
- }
|
|
|
+ // for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ // Tube[i].NextDigit = &Tube[i].Digits[0];
|
|
|
+ // }
|
|
|
|
|
|
RtcTick = false;
|
|
|
RefreshTick = false;
|
|
@@ -130,6 +154,10 @@ int main() {
|
|
|
// DS3231_SetTime(0, 55, 6, true);
|
|
|
// DS3231_SetDate(0, 2, 12, 18, 0);
|
|
|
|
|
|
+ Dot.CurrentState = Decrementing;
|
|
|
+ Dot.Step = DOT_TICK_STEP;
|
|
|
+ Dot.Brightness = 0;
|
|
|
+
|
|
|
while(1) {
|
|
|
|
|
|
// Animate_Cycle_Basic();
|
|
@@ -145,25 +173,25 @@ int main() {
|
|
|
if (RefreshTick) {
|
|
|
RefreshTick = false;
|
|
|
|
|
|
- for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (DotUpdated) {
|
|
|
- PCA9685_SetDot(DotValue);
|
|
|
- DotUpdated = false;
|
|
|
+ // for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ // 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;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ if (Dot.Updated) {
|
|
|
+ PCA9685_SetDot(Dot.Brightness);
|
|
|
+ Dot.Updated = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -178,60 +206,64 @@ int main() {
|
|
|
DS3231_GetTime(&nextSecond, &nextMinute, &nextHour);
|
|
|
|
|
|
// Update the display configuration based on the new/previous time
|
|
|
- for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
-
|
|
|
- bool Update = false;
|
|
|
- if (i == 0 && (startup || 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 && (startup || 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 && (startup || 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 && (startup || prevMinute % 10 != nextMinute % 10)) {
|
|
|
- Tube[i].NextDigit->Value = nextMinute % 10;
|
|
|
- Tube[i].NextDigit->Increment = true;
|
|
|
- Tube[i].NextDigit->Active = true;
|
|
|
- Update = true;
|
|
|
- }
|
|
|
+ // for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+
|
|
|
+ // bool Update = false;
|
|
|
+ // if (i == 0 && (startup || 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 && (startup || 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 && (startup || 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 && (startup || prevMinute % 10 != nextMinute % 10)) {
|
|
|
+ // Tube[i].NextDigit->Value = nextMinute % 10;
|
|
|
+ // Tube[i].NextDigit->Increment = true;
|
|
|
+ // Tube[i].NextDigit->Active = true;
|
|
|
+ // Update = true;
|
|
|
+ // }
|
|
|
|
|
|
// Handle display configuration changes for the given tube
|
|
|
- if (Update) {
|
|
|
- if (!startup) {
|
|
|
- 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 (Update) {
|
|
|
+ // if (!startup) {
|
|
|
+ // 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];
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+
|
|
|
+ Dot.CurrentState = (Dot.CurrentState == Decrementing) ? Incrementing : Decrementing;
|
|
|
+ // DotUpdateTick.attach(DotUpdateCallback, (float)1/DOT_FADE_TICKS);
|
|
|
+ DotUpdateTick.attach_us(DotUpdateCallback, 700);
|
|
|
|
|
|
// Clear the startup run-once flag after the first iteration
|
|
|
- startup = false;
|
|
|
+ // startup = false;
|
|
|
|
|
|
// Fade in the new time
|
|
|
- RuntimeTickerIter = FADE_TICKS;
|
|
|
- RuntimeUpdateTicker.attach(RuntimeTickerUpdate, (float)(DIGIT_REFRESH_RATE_US / 1000) / RuntimeTickerIter);
|
|
|
+ // RuntimeTickerIter = FADE_TICKS;
|
|
|
+ // RuntimeUpdateTicker.attach(RuntimeTickerUpdate, (float)(DIGIT_REFRESH_RATE_US / 1000) / RuntimeTickerIter);
|
|
|
}
|
|
|
|
|
|
if (RngTick) {
|
|
|
|
|
|
// Choose a random tube to refresh
|
|
|
- Tube[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);
|
|
|
- RngTick = false;
|
|
|
+ // RngTickerIter = DIGIT_RNG_REFRESH_ITER * FADE_TICKS;
|
|
|
+ // RngUpdateTicker.attach(RngTickerUpdate, (DIGIT_REFRESH_RATE_US/1000)/FADE_TICKS);
|
|
|
+ // RngTick = false;
|
|
|
}
|
|
|
}
|
|
|
}
|