|
@@ -21,6 +21,8 @@ typedef enum {
|
|
|
typedef struct {
|
|
|
DigitState CurrentState;
|
|
|
int Value;
|
|
|
+ int PwmStart;
|
|
|
+ int PwmEnd;
|
|
|
bool Updated;
|
|
|
} Digit;
|
|
|
|
|
@@ -83,6 +85,79 @@ TUBE_CALLBACK(2)
|
|
|
Timeout Tube3UpdateTimeout;
|
|
|
TUBE_CALLBACK(3)
|
|
|
|
|
|
+// In the event that there are multiple PWM outputs at less than 100% duty cycle,
|
|
|
+// stagger the start time of each PWM to reduce the switch on surge current.
|
|
|
+void StaggerPwmOutput() {
|
|
|
+ int validOutputs = 0;
|
|
|
+ int totalOnTime = 0;
|
|
|
+ int lastPwmEnd = 0;
|
|
|
+
|
|
|
+ // Determine the number of active outputs as well as the total on-time across all outputs.
|
|
|
+ // Ignore outputs that are off (min) or fully on (max) as they have no surge impact.
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ for (int j = 0; j < NUM_DIGITS; j++) {
|
|
|
+ if (Tubes[i].Digits[j].Value != PCA9685_Min_Brightness &&
|
|
|
+ Tubes[i].Digits[j].Value != PCA9685_Max_Brightness) {
|
|
|
+
|
|
|
+ validOutputs++;
|
|
|
+ totalOnTime += Tubes[i].Digits[j].Value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (Dot.Value != PCA9685_Min_Brightness && Dot.Value != PCA9685_Max_Brightness) {
|
|
|
+ validOutputs++;
|
|
|
+ totalOnTime += Dot.Value;
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the total on-time across all outputs is less than one PWM period, stagger each
|
|
|
+ // output such that the rise of one pulse begins at the end of the previous pulse.
|
|
|
+ if (totalOnTime <= PCA9685_Max_Brightness) {
|
|
|
+
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ for (int j = 0; j < NUM_DIGITS; j++) {
|
|
|
+ if (Tubes[i].Digits[j].Value != PCA9685_Min_Brightness &&
|
|
|
+ Tubes[i].Digits[j].Value != PCA9685_Max_Brightness) {
|
|
|
+
|
|
|
+ Tubes[i].Digits[j].PwmStart = lastPwmEnd;
|
|
|
+ Tubes[i].Digits[j].PwmEnd = lastPwmEnd + Tubes[i].Digits[j].Value;
|
|
|
+ lastPwmEnd = Tubes[i].Digits[j].PwmEnd;
|
|
|
+ Tubes[i].Digits[j].Updated = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (Dot.Value != PCA9685_Min_Brightness && Dot.Value != PCA9685_Max_Brightness) {
|
|
|
+ Dot.PwmStart = lastPwmEnd;
|
|
|
+ Dot.PwmEnd = lastPwmEnd + Dot.Value;
|
|
|
+ lastPwmEnd = Dot.PwmEnd;
|
|
|
+ Dot.Updated = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ // Compute the amount of overlap between all outputs
|
|
|
+ int overlap = totalOnTime / (validOutputs - 1);
|
|
|
+
|
|
|
+ // Compute the staggered output period for each output
|
|
|
+ for (int i = 0; i < NUM_TUBES; i++) {
|
|
|
+ for (int j = 0; j < NUM_DIGITS; j++) {
|
|
|
+ if (Tubes[i].Digits[j].Value != PCA9685_Min_Brightness &&
|
|
|
+ Tubes[i].Digits[j].Value != PCA9685_Max_Brightness) {
|
|
|
+
|
|
|
+ Tubes[i].Digits[j].PwmStart = (lastPwmEnd == 0) ? 0 : lastPwmEnd - overlap;
|
|
|
+ Tubes[i].Digits[j].PwmEnd = Tubes[i].Digits[j].PwmStart + Tubes[i].Digits[j].Value;
|
|
|
+ lastPwmEnd = Tubes[i].Digits[j].PwmEnd;
|
|
|
+ Tubes[i].Digits[j].Updated = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (Dot.Value != PCA9685_Min_Brightness && Dot.Value != PCA9685_Max_Brightness) {
|
|
|
+ Dot.PwmStart = (lastPwmEnd == 0) ? 0 : lastPwmEnd - overlap;
|
|
|
+ Dot.PwmEnd = Dot.PwmStart + Dot.Value;
|
|
|
+ lastPwmEnd = Dot.PwmEnd;
|
|
|
+ Dot.Updated = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void FadeInOutDigit(int T, int D, int Duration, bool RngUpdate = false) {
|
|
|
|
|
|
// If the tube is in the middle of a refresh sequence and a call comes
|
|
@@ -203,6 +278,7 @@ int main() {
|
|
|
// Start with HV PSU disabled
|
|
|
HV_EnableOutput(false);
|
|
|
|
|
|
+ IO_Init();
|
|
|
TUSB322_Init();
|
|
|
PCA9685_Init();
|
|
|
DS3231_Init(RtcInterruptCallback);
|
|
@@ -229,8 +305,13 @@ int main() {
|
|
|
|
|
|
// wait(3);
|
|
|
|
|
|
- // DS3231_SetTime(00, 05, 00);
|
|
|
- // DS3231_SetDate(FRIDAY, 15, 3, 19, 0);
|
|
|
+ // Set RTC time through the following steps:
|
|
|
+ // 1. Build and flash firmware that sets time on boot
|
|
|
+ // 2. Reset board (non-DFU) at programmed time to set RTC time
|
|
|
+ // 3. Reset board in DFU mode
|
|
|
+ // 4. Build and flash firmware that does not set time on boot
|
|
|
+ // DS3231_SetTime(00, 13, 17);
|
|
|
+ // DS3231_SetDate(SUNDAY, 1, 11, 20, 0);
|
|
|
|
|
|
// Setup a ticker to refresh the display at 1kHz
|
|
|
RefreshTicker.attach_us(RefreshTickerCallback, REFRESH_RATE_US);
|
|
@@ -248,17 +329,28 @@ int main() {
|
|
|
if (RefreshTick) {
|
|
|
RefreshTick = false;
|
|
|
|
|
|
+ StaggerPwmOutput();
|
|
|
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);
|
|
|
+ if (Tubes[i].Digits[j].Value == PCA9685_Min_Brightness ||
|
|
|
+ Tubes[i].Digits[j].Value == PCA9685_Max_Brightness) {
|
|
|
+
|
|
|
+ PCA9685_SetDigit(i, j, Tubes[i].Digits[j].Value);
|
|
|
+ } else {
|
|
|
+ PCA9685_SetDigitPwm(i, j, Tubes[i].Digits[j].PwmStart, Tubes[i].Digits[j].PwmEnd);
|
|
|
+ }
|
|
|
Tubes[i].Digits[j].Updated = false;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (Dot.Updated) {
|
|
|
- PCA9685_SetDot(Dot.Value);
|
|
|
+ if (Dot.Value == PCA9685_Min_Brightness || Dot.Value == PCA9685_Max_Brightness) {
|
|
|
+ PCA9685_SetDot(Dot.Value);
|
|
|
+ } else {
|
|
|
+ PCA9685_SetDotPwm(Dot.PwmStart, Dot.PwmEnd);
|
|
|
+ }
|
|
|
Dot.Updated = false;
|
|
|
}
|
|
|
}
|