#include "mbed.h" #include "main.h" #include "SWO.h" #include "pca9685.h" #include "tusb322.h" #include "ds3231.h" #include "ioc.h" #include "animation.h" 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; bool Updated; bool RefreshActive; } TUBE; TUBE Display[NUM_TUBES] = {0}; // Active per-tube digit setting int DotValue; bool DotUpdated = false;; bool DotIncrement = false; Ticker StartupUpdateTicker, RuntimeUpdateTicker, RngUpdateTicker; int StartupTickerIter, RuntimeTickerIter, RngTickerIter; void RtcTickCallback(void) { RtcTick = true; } void RefreshTickCallback(void) { RefreshTick = true; } void RngTickCallback(void) { RngTick = true; } void StartupTickerUpdate(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 (--StartupTickerIter == 0) { StartupUpdateTicker.detach(); for (int i = 0; i < NUM_TUBES; i++) { Display[i].DigitBActive = !Display[i].DigitBActive; } // Switch to runtime mode once startup sequence has completed RuntimeFlag = true; } } void RuntimeTickerUpdate(void) { for (int i = 0; i < NUM_TUBES; i++) { if(!Display[i].RefreshActive) { 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; } Display[i].Updated = true; } } } 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; } } void RngTickerUpdate(void) { if (--RngTickerIter == 0) { RngUpdateTicker.detach(); } } int main() { RtcTick = false; RefreshTick = false; RngTick = false; StartupFlag = true; RuntimeFlag = false; // Start I2C at 400kHz for DS3231 i2c.frequency(400000); // Start with HV PSU disabled HV_EnableOutput(false); TUSB322_Init(); PCA9685_Init(); DS3231_Init(RtcTickCallback); // Enable HV PSU HV_EnableOutput(true); // Set PCA9685 input voltage to highest possible PCA9685_SetVoltage(1.0); // swo.printf("CPU SystemCoreClock is %d Hz\r\n", SystemCoreClock); // Bump I2C frequency to 1MHz i2c.frequency(1000000); // Setup a ticker to refresh the display at 1kHz Ticker refreshTicker; refreshTicker.attach_us(RefreshTickCallback, DIGIT_REFRESH_RATE_US); // Kick off the RNG ticker at 0.1Hz // Ticker rngTicker; // rngTicker.attach(RngTickCallback, DIGIT_RNG_REFRESH_RATE_S); // DS3231_SetTime(0, 55, 6, true); // DS3231_SetDate(0, 2, 12, 18, 0); while(1) { // Animate_Cycle_Basic(); // Animate_Cycle_Analog(); // Animate_Cycle_Low_Pwm(); // Animate_Cycle_Pwm(); // Animate_Cycle_Fade(); // Animate_Cycle_Fade_Random(); // Animate_Cycle_Fast(); // Animate_Cycle_Fast_Random(); if (RefreshTick) { RefreshTick = false; // 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 (DotUpdated) { PCA9685_SetDot(DotValue); DotUpdated = false; } } if (RtcTick) { RtcTick = false; // On RTC 1Hz ticks, get the time and begin update int nextSecond, nextMinute, nextHour; DS3231_GetTime(&nextSecond, &nextMinute, &nextHour); if (Display[3].DigitBActive) { Display[3].DigitB = nextMinute % 10; } else { Display[3].DigitA = nextMinute % 10; } if (Display[2].DigitBActive) { Display[2].DigitB = nextMinute / 10; } else { Display[2].DigitA = nextMinute / 10; } 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; } 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); } } if (RngTick) { // Choose a random tube to refresh Display[rand() % 4].RefreshActive = true; RngTickerIter = DIGIT_RNG_REFRESH_ITER * FADE_TICKS; RngUpdateTicker.attach(RngTickerUpdate, (DIGIT_REFRESH_RATE_US/1000)/FADE_TICKS); RngTick = false; } } } void I2C_Write(int DeviceAddress, char RegAddress, char *Data, int Length) { char buffer[I2C_MAX_BUFFER+1] = {0}; if (Length > I2C_MAX_BUFFER) LED_Fault(1); buffer[0] = RegAddress; memcpy(&buffer[1], Data, Length); i2c.write(DeviceAddress << 1, buffer, Length + 1); } void I2C_Read(int DeviceAddress, char RegAddress, char *Data, int Length) { i2c.write(DeviceAddress << 1, &RegAddress, 1); i2c.read(DeviceAddress << 1, Data, Length); }