main.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #include "mbed.h"
  2. #include "main.h"
  3. #include "SWO.h"
  4. #include "pca9685.h"
  5. #include "tusb322.h"
  6. #include "ds3231.h"
  7. #include "ioc.h"
  8. #include "animation.h"
  9. I2C i2c(PA_10, PA_9);
  10. // SWO_Channel swo("swo");
  11. bool RtcTick, RefreshTick, RngTick;
  12. typedef enum {
  13. Decrementing = 0,
  14. Incrementing = 1,
  15. } DigitState;
  16. typedef struct {
  17. DigitState CurrentState;
  18. int Value;
  19. bool Updated;
  20. } Digit;
  21. typedef struct {
  22. Digit Digits[10];
  23. bool Refresh;
  24. int FadeDuration;
  25. } Tube;
  26. Tube Tubes[4]; // Active per-tube configuration
  27. Digit Dot;
  28. Timeout DotUpdateTimeout;
  29. void DotUpdateCallback(void) {
  30. if (Dot.CurrentState == Incrementing && Dot.Value < DOT_MAX) {
  31. Dot.Value = (Dot.Value + DOT_FADE_STEP >= DOT_MAX) ? DOT_MAX : Dot.Value + DOT_FADE_STEP;
  32. } else if (Dot.CurrentState == Decrementing && Dot.Value > DOT_MIN) {
  33. Dot.Value = (Dot.Value - DOT_FADE_STEP <= DOT_MIN) ? DOT_MIN : Dot.Value - DOT_FADE_STEP;
  34. }
  35. Dot.Updated = true;
  36. if (Dot.Value != DOT_MAX && Dot.Value != DOT_MIN) {
  37. DotUpdateTimeout.attach_us(DotUpdateCallback, DOT_FADE_DURATION_US / REFRESH_RATE_US);
  38. }
  39. }
  40. // Macro the tube callback function as each callback needs to be defined as
  41. // a unique function (callbacks cannot have arguments and are not reentrant)
  42. #define TUBE_CALLBACK(x) \
  43. void Tube##x##UpdateCallback(void) { \
  44. int step = (DOT_MAX + (Tubes[x].FadeDuration / REFRESH_RATE_US) - 1) / (Tubes[x].FadeDuration / REFRESH_RATE_US); \
  45. bool activeTube = false; \
  46. for (int i = 0; i < 10; i++) { \
  47. Digit *digit = &Tubes[x].Digits[i]; \
  48. if (digit->CurrentState == Incrementing && digit->Value < DIGIT_MAX) { \
  49. digit->Value = (digit->Value + step >= DIGIT_MAX) ? DIGIT_MAX : digit->Value + step; \
  50. digit->Updated = true; \
  51. } else if (digit->CurrentState == Decrementing && digit->Value > DOT_MIN) { \
  52. digit->Value = (digit->Value - step <= DIGIT_MIN) ? DIGIT_MIN : digit->Value - step; \
  53. digit->Updated = true; \
  54. } \
  55. activeTube |= (digit->Value != DIGIT_MAX && digit->Value != DIGIT_MIN); \
  56. } \
  57. if (activeTube) { \
  58. Tube##x##UpdateTimeout.attach_us(Tube##x##UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US); \
  59. } \
  60. }
  61. Timeout Tube0UpdateTimeout;
  62. TUBE_CALLBACK(0)
  63. Timeout Tube1UpdateTimeout;
  64. TUBE_CALLBACK(1)
  65. Timeout Tube2UpdateTimeout;
  66. TUBE_CALLBACK(2)
  67. Timeout Tube3UpdateTimeout;
  68. TUBE_CALLBACK(3)
  69. void FadeInOutDigit(int T, int D, int Duration) {
  70. for (int i = 0; i < 10; i++) {
  71. Tubes[T].Digits[i].CurrentState = Decrementing;
  72. }
  73. if (D != -1) {
  74. Tubes[T].Digits[D].CurrentState = Incrementing;
  75. }
  76. Tubes[T].FadeDuration = Duration;
  77. if (T == 0) {
  78. Tube0UpdateTimeout.attach_us(Tube0UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
  79. } else if (T == 1) {
  80. Tube1UpdateTimeout.attach_us(Tube1UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
  81. } else if (T == 2) {
  82. Tube2UpdateTimeout.attach_us(Tube2UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
  83. } else if (T == 3) {
  84. Tube3UpdateTimeout.attach_us(Tube3UpdateCallback, DIGIT_FADE_DURATION_US / REFRESH_RATE_US);
  85. }
  86. }
  87. Ticker RefreshTicker, RngUpdateTicker;
  88. int RngTickerIter;
  89. void RngTickerUpdate(void) {
  90. if (--RngTickerIter == 0) {
  91. RngUpdateTicker.detach();
  92. }
  93. }
  94. // Callback from DS3231 interrupt (1Hz)
  95. void RtcTickCallback(void) {
  96. RtcTick = true;
  97. }
  98. // Callback from RefreshTicker (REFRESH_RATE_US)
  99. void RefreshTickCallback(void) {
  100. RefreshTick = true;
  101. }
  102. void RngTickCallback(void) {
  103. RngTick = true;
  104. }
  105. int main() {
  106. // Initialize pointers in global data structure
  107. for (int i = 0; i < NUM_TUBES; i++) {
  108. for (int j = 0; j < 10; j++) {
  109. Tubes[i].Digits[j].CurrentState = Decrementing;
  110. Tubes[i].Digits[j].Value = 0;
  111. Tubes[i].Digits[j].Updated = false;
  112. }
  113. Tubes[i].Refresh = false;
  114. Tubes[i].FadeDuration = 0;
  115. }
  116. Dot.CurrentState = Decrementing;
  117. Dot.Value = 0;
  118. Dot.Updated = false;
  119. RtcTick = false;
  120. RefreshTick = false;
  121. RngTick = false;
  122. int nextSecond, nextMinute, nextHour;
  123. int prevMinute, prevHour;
  124. bool startup = true;
  125. // Start I2C at 400kHz for DS3231
  126. i2c.frequency(400000);
  127. // Start with HV PSU disabled
  128. HV_EnableOutput(false);
  129. TUSB322_Init();
  130. PCA9685_Init();
  131. DS3231_Init(RtcTickCallback);
  132. // Enable HV PSU
  133. HV_EnableOutput(true);
  134. // Set PCA9685 input voltage to highest possible
  135. PCA9685_SetVoltage(1.0);
  136. // swo.printf("CPU SystemCoreClock is %d Hz\r\n", SystemCoreClock);
  137. // Bump I2C frequency to 1MHz
  138. // i2c.frequency(1000000);
  139. // Setup a ticker to refresh the display at 1kHz
  140. RefreshTicker.attach_us(RefreshTickCallback, REFRESH_RATE_US);
  141. // Kick off the RNG ticker at 0.1Hz
  142. // Ticker rngTicker;
  143. // rngTicker.attach(RngTickCallback, DIGIT_RNG_REFRESH_RATE_S);
  144. // DS3231_SetTime(0, 55, 6, true);
  145. // DS3231_SetDate(0, 2, 12, 18, 0);
  146. while(1) {
  147. // Animate_Cycle_Basic();
  148. // Animate_Cycle_Analog();
  149. // Animate_Cycle_Low_Pwm();
  150. // Animate_Cycle_Pwm();
  151. // Animate_Cycle_Fade();
  152. // Animate_Cycle_Fade_Random();
  153. // Animate_Cycle_Fast();
  154. // Animate_Cycle_Fast_Random();
  155. // On every refresh tick, update the display with new values
  156. if (RefreshTick) {
  157. RefreshTick = false;
  158. for (int i = 0; i < 4; i++) {
  159. for (int j = 0; j < 10; j++) {
  160. if (Tubes[i].Digits[j].Updated) {
  161. PCA9685_SetDigit(i, j, Tubes[i].Digits[j].Value);
  162. Tubes[i].Digits[j].Updated = false;
  163. }
  164. }
  165. }
  166. if (Dot.Updated) {
  167. PCA9685_SetDot(Dot.Value);
  168. Dot.Updated = false;
  169. }
  170. }
  171. // On every RTC tick, read and process the current time
  172. if (RtcTick) {
  173. RtcTick = false;
  174. // Save the previous time to check if digit(s) has changed
  175. prevMinute = nextMinute;
  176. prevHour = nextHour;
  177. DS3231_GetTime(&nextSecond, &nextMinute, &nextHour);
  178. // Update the display configuration based on the new/previous time
  179. if (startup || prevHour / 10 != nextHour / 10) {
  180. FadeInOutDigit(0, (nextHour / 10 != 0) ? nextHour / 10 : -1 , DIGIT_FADE_DURATION_US);
  181. }
  182. if (startup || prevHour % 10 != nextHour % 10) {
  183. FadeInOutDigit(1, nextHour % 10, DIGIT_FADE_DURATION_US);
  184. }
  185. if (startup || prevMinute / 10 != nextMinute / 10) {
  186. FadeInOutDigit(2, nextMinute / 10, DIGIT_FADE_DURATION_US);
  187. }
  188. if (startup || prevMinute % 10 != nextMinute % 10) {
  189. FadeInOutDigit(3, nextMinute % 10, DIGIT_FADE_DURATION_US);
  190. }
  191. Dot.CurrentState = (Dot.CurrentState == Decrementing) ? Incrementing : Decrementing;
  192. DotUpdateTimeout.attach_us(DotUpdateCallback, DOT_FADE_DURATION_US / REFRESH_RATE_US);
  193. // Clear the startup run-once flag after the first iteration
  194. startup = false;
  195. }
  196. if (RngTick) {
  197. // Choose a random tube to refresh
  198. // Tube[rand() % 4].RefreshActive = true;
  199. // RngTickerIter = DIGIT_RNG_REFRESH_ITER * FADE_TICKS;
  200. // RngUpdateTicker.attach(RngTickerUpdate, (DIGIT_REFRESH_RATE_US/1000)/FADE_TICKS);
  201. // RngTick = false;
  202. }
  203. }
  204. }
  205. void I2C_Write(int DeviceAddress, char RegAddress, char *Data, int Length) {
  206. char buffer[I2C_MAX_BUFFER+1] = {0};
  207. if (Length > I2C_MAX_BUFFER) LED_Fault(1);
  208. buffer[0] = RegAddress;
  209. memcpy(&buffer[1], Data, Length);
  210. i2c.write(DeviceAddress << 1, buffer, Length + 1);
  211. }
  212. void I2C_Read(int DeviceAddress, char RegAddress, char *Data, int Length) {
  213. i2c.write(DeviceAddress << 1, &RegAddress, 1);
  214. i2c.read(DeviceAddress << 1, Data, Length);
  215. }