|
@@ -10,8 +10,6 @@
|
|
|
I2C i2c(PA_10, PA_9);
|
|
|
// SWO_Channel swo("swo");
|
|
|
|
|
|
-bool RtcTick, RefreshTick, RngTick;
|
|
|
-
|
|
|
typedef enum {
|
|
|
Decrementing = 0,
|
|
|
Incrementing = 1,
|
|
@@ -24,12 +22,14 @@ typedef struct {
|
|
|
} Digit;
|
|
|
|
|
|
typedef struct {
|
|
|
- Digit Digits[10];
|
|
|
+ Digit Digits[NUM_DIGITS];
|
|
|
+ int LastActiveDigit;
|
|
|
+ int RefreshLastDigit;
|
|
|
bool Refresh;
|
|
|
int FadeDuration;
|
|
|
} Tube;
|
|
|
|
|
|
-Tube Tubes[4]; // Active per-tube configuration
|
|
|
+Tube Tubes[NUM_TUBES]; // Active per-tube configuration
|
|
|
Digit Dot;
|
|
|
|
|
|
Timeout DotUpdateTimeout;
|
|
@@ -42,29 +42,30 @@ void DotUpdateCallback(void) {
|
|
|
Dot.Updated = true;
|
|
|
|
|
|
if (Dot.Value != DOT_MAX && Dot.Value != DOT_MIN) {
|
|
|
- DotUpdateTimeout.attach_us(DotUpdateCallback, DOT_FADE_DURATION_US / REFRESH_RATE_US);
|
|
|
+ DotUpdateTimeout.attach_us(DotUpdateCallback, REFRESH_RATE_US);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// Macro the tube callback function as each callback needs to be defined as
|
|
|
+// Macro the per-tube callback function as each callback needs to be defined as
|
|
|
// a unique function (callbacks cannot have arguments and are not reentrant)
|
|
|
#define TUBE_CALLBACK(x) \
|
|
|
void Tube##x##UpdateCallback(void) { \
|
|
|
- int step = (DOT_MAX + (Tubes[x].FadeDuration / REFRESH_RATE_US) - 1) / (Tubes[x].FadeDuration / REFRESH_RATE_US); \
|
|
|
+ int ticks = Tubes[x].FadeDuration / REFRESH_RATE_US; \
|
|
|
+ int step = (DOT_MAX + ticks - 1) / ticks; \
|
|
|
bool activeTube = false; \
|
|
|
- for (int i = 0; i < 10; i++) { \
|
|
|
+ for (int i = 0; i < NUM_DIGITS; i++) { \
|
|
|
Digit *digit = &Tubes[x].Digits[i]; \
|
|
|
- if (digit->CurrentState == Incrementing && digit->Value < DIGIT_MAX) { \
|
|
|
+ if (digit->CurrentState == Incrementing && digit->Value <= DIGIT_MAX) { \
|
|
|
digit->Value = (digit->Value + step >= DIGIT_MAX) ? DIGIT_MAX : digit->Value + step; \
|
|
|
digit->Updated = true; \
|
|
|
- } else if (digit->CurrentState == Decrementing && digit->Value > DOT_MIN) { \
|
|
|
+ } else if (digit->CurrentState == Decrementing && digit->Value >= DOT_MIN) { \
|
|
|
digit->Value = (digit->Value - step <= DIGIT_MIN) ? DIGIT_MIN : digit->Value - step; \
|
|
|
digit->Updated = true; \
|
|
|
} \
|
|
|
activeTube |= (digit->Value != DIGIT_MAX && digit->Value != DIGIT_MIN); \
|
|
|
} \
|
|
|
if (activeTube) { \
|
|
|
- Tube##x##UpdateTimeout.attach_us(Tube##x##UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US); \
|
|
|
+ Tube##x##UpdateTimeout.attach_us(Tube##x##UpdateCallback, REFRESH_RATE_US); \
|
|
|
} \
|
|
|
}
|
|
|
|
|
@@ -78,58 +79,77 @@ Timeout Tube3UpdateTimeout;
|
|
|
TUBE_CALLBACK(3)
|
|
|
|
|
|
void FadeInOutDigit(int T, int D, int Duration) {
|
|
|
- for (int i = 0; i < 10; i++) {
|
|
|
+ for (int i = 0; i < NUM_DIGITS; i++) {
|
|
|
Tubes[T].Digits[i].CurrentState = Decrementing;
|
|
|
}
|
|
|
if (D != -1) {
|
|
|
Tubes[T].Digits[D].CurrentState = Incrementing;
|
|
|
}
|
|
|
Tubes[T].FadeDuration = Duration;
|
|
|
+ Tubes[T].LastActiveDigit = D;
|
|
|
|
|
|
if (T == 0) {
|
|
|
- Tube0UpdateTimeout.attach_us(Tube0UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
|
|
|
+ Tube0UpdateTimeout.attach_us(Tube0UpdateCallback, REFRESH_RATE_US);
|
|
|
} else if (T == 1) {
|
|
|
- Tube1UpdateTimeout.attach_us(Tube1UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
|
|
|
+ Tube1UpdateTimeout.attach_us(Tube1UpdateCallback, REFRESH_RATE_US);
|
|
|
} else if (T == 2) {
|
|
|
- Tube2UpdateTimeout.attach_us(Tube2UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
|
|
|
+ Tube2UpdateTimeout.attach_us(Tube2UpdateCallback, REFRESH_RATE_US);
|
|
|
} else if (T == 3) {
|
|
|
- Tube3UpdateTimeout.attach_us(Tube3UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
|
|
|
+ Tube3UpdateTimeout.attach_us(Tube3UpdateCallback, REFRESH_RATE_US);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-Ticker RefreshTicker, RngUpdateTicker;
|
|
|
-int RngTickerIter;
|
|
|
-void RngTickerUpdate(void) {
|
|
|
-
|
|
|
-
|
|
|
- if (--RngTickerIter == 0) {
|
|
|
- RngUpdateTicker.detach();
|
|
|
- }
|
|
|
-}
|
|
|
+bool RtcTick, RefreshTick, RngTick;
|
|
|
+Ticker RefreshTicker, RngRefreshTicker;
|
|
|
|
|
|
// Callback from DS3231 interrupt (1Hz)
|
|
|
-void RtcTickCallback(void) {
|
|
|
+void RtcInterruptCallback(void) {
|
|
|
RtcTick = true;
|
|
|
}
|
|
|
|
|
|
// Callback from RefreshTicker (REFRESH_RATE_US)
|
|
|
-void RefreshTickCallback(void) {
|
|
|
+void RefreshTickerCallback(void) {
|
|
|
RefreshTick = true;
|
|
|
}
|
|
|
|
|
|
-void RngTickCallback(void) {
|
|
|
+void RngTickerCallback(void) {
|
|
|
RngTick = true;
|
|
|
}
|
|
|
|
|
|
+Timeout RngUpdateTimeout;
|
|
|
+int RngUpdateIteration;
|
|
|
+void RngUpdateCallback(void) {
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ if (Tubes[i].Refresh) {
|
|
|
+ if (RngUpdateIteration) {
|
|
|
+ int nextDigit;
|
|
|
+ do {
|
|
|
+ nextDigit = rand() % NUM_DIGITS;
|
|
|
+ } while (nextDigit == Tubes[i].LastActiveDigit ||
|
|
|
+ (RngUpdateIteration == 1 && nextDigit == Tubes[i].RefreshLastDigit));
|
|
|
+
|
|
|
+ FadeInOutDigit(i, nextDigit, DIGIT_RNG_FADE_DURATION_US);
|
|
|
+ } else {
|
|
|
+ FadeInOutDigit(i, Tubes[i].RefreshLastDigit, DIGIT_RNG_FADE_DURATION_US);
|
|
|
+ Tubes[i].Refresh = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (RngUpdateIteration-- != 0) {
|
|
|
+ RngUpdateTimeout.attach_us(RngUpdateCallback, DIGIT_RNG_FADE_DURATION_US);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int main() {
|
|
|
|
|
|
// Initialize pointers in global data structure
|
|
|
for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
- for (int j = 0; j < 10; j++) {
|
|
|
+ for (int j = 0; j < NUM_DIGITS; j++) {
|
|
|
Tubes[i].Digits[j].CurrentState = Decrementing;
|
|
|
Tubes[i].Digits[j].Value = 0;
|
|
|
Tubes[i].Digits[j].Updated = false;
|
|
|
}
|
|
|
+ Tubes[i].LastActiveDigit = -1;
|
|
|
Tubes[i].Refresh = false;
|
|
|
Tubes[i].FadeDuration = 0;
|
|
|
}
|
|
@@ -154,7 +174,7 @@ int main() {
|
|
|
|
|
|
TUSB322_Init();
|
|
|
PCA9685_Init();
|
|
|
- DS3231_Init(RtcTickCallback);
|
|
|
+ DS3231_Init(RtcInterruptCallback);
|
|
|
|
|
|
// Enable HV PSU
|
|
|
HV_EnableOutput(true);
|
|
@@ -168,11 +188,10 @@ int main() {
|
|
|
// i2c.frequency(1000000);
|
|
|
|
|
|
// Setup a ticker to refresh the display at 1kHz
|
|
|
- RefreshTicker.attach_us(RefreshTickCallback, REFRESH_RATE_US);
|
|
|
+ RefreshTicker.attach_us(RefreshTickerCallback, REFRESH_RATE_US);
|
|
|
|
|
|
// Kick off the RNG ticker at 0.1Hz
|
|
|
- // Ticker rngTicker;
|
|
|
- // rngTicker.attach(RngTickCallback, DIGIT_RNG_REFRESH_RATE_S);
|
|
|
+ RngRefreshTicker.attach(RngTickerCallback, DIGIT_RNG_REFRESH_RATE);
|
|
|
|
|
|
// DS3231_SetTime(0, 55, 6, true);
|
|
|
// DS3231_SetDate(0, 2, 12, 18, 0);
|
|
@@ -192,8 +211,8 @@ int main() {
|
|
|
if (RefreshTick) {
|
|
|
RefreshTick = false;
|
|
|
|
|
|
- for (int i = 0; i < 4; i++) {
|
|
|
- for (int j = 0; j < 10; j++) {
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ for (int j = 0; j < NUM_DIGITS; j++) {
|
|
|
if (Tubes[i].Digits[j].Updated) {
|
|
|
PCA9685_SetDigit(i, j, Tubes[i].Digits[j].Value);
|
|
|
Tubes[i].Digits[j].Updated = false;
|
|
@@ -239,13 +258,15 @@ int main() {
|
|
|
}
|
|
|
|
|
|
if (RngTick) {
|
|
|
+ RngTick = false;
|
|
|
+
|
|
|
+ int refreshTube = rand() % NUM_TUBES;
|
|
|
|
|
|
- // Choose a random tube to refresh
|
|
|
- // Tube[rand() % 4].RefreshActive = true;
|
|
|
+ Tubes[refreshTube].RefreshLastDigit = Tubes[refreshTube].LastActiveDigit;
|
|
|
+ Tubes[refreshTube].Refresh = true;
|
|
|
|
|
|
- // RngTickerIter = DIGIT_RNG_REFRESH_ITER * FADE_TICKS;
|
|
|
- // RngUpdateTicker.attach(RngTickerUpdate, (DIGIT_REFRESH_RATE_US/1000)/FADE_TICKS);
|
|
|
- // RngTick = false;
|
|
|
+ RngUpdateIteration = DIGIT_RNG_FADE_ITERATIONS;
|
|
|
+ RngUpdateCallback();
|
|
|
}
|
|
|
}
|
|
|
}
|