Browse Source

Refactor code into separate files

Kevin Lee 5 years ago
parent
commit
c871dcb1ff

+ 0 - 16
Nixie_Firmware_Mbed/GettingStarted.html

@@ -1,16 +0,0 @@
-<!DOCTYPE HTML>
-<html lang="en-US">
-    <head>
-        <meta charset="UTF-8">
-        <meta http-equiv="refresh"
-              content="1;url="https://os.mbed.com/docs/latest/tools/exporting.html>
-        <script type="text/javascript">
-            window.location.href = "https://os.mbed.com/docs/latest/tools/exporting.html"
-        </script>
-        <title>Page Redirection</title>
-    </head>
-    <body>
-        If you are not redirected automatically, please follow the
-        <a href='https://os.mbed.com/docs/v5.6/tools/exporting.html/'>link to the online exporter documentation</a>
-    </body>
-</html>

+ 5 - 0
Nixie_Firmware_Mbed/Makefile

@@ -48,6 +48,11 @@ PROJECT := Nixie_FW
 
 OBJECTS += SWO/SWO.o
 OBJECTS += main.o
+OBJECTS += tusb322.o
+OBJECTS += ds3231.o
+OBJECTS += pca9685.o
+OBJECTS += ioc.o
+OBJECTS += animation.o
 OBJECTS += mbed-dev/cmsis/TARGET_CORTEX_M/mbed_tz_context.o
 OBJECTS += mbed-dev/drivers/AnalogIn.o
 OBJECTS += mbed-dev/drivers/BusIn.o

+ 266 - 0
Nixie_Firmware_Mbed/animation.cpp

@@ -0,0 +1,266 @@
+#include "animation.h"
+
+void Animate_Cycle_Basic(void) {
+
+    PCA9685_SetVoltage(1.0);
+
+    // Switch each digit from 0 to 100%
+    for (int i = 0; i < 10; i++) {
+        if (i % 2 == 0) {
+            PCA9685_SetDot(PCA9685_Min_Brightness);
+        } else {
+            PCA9685_SetDot(PCA9685_Max_Brightness);
+        }
+        
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i, PCA9685_Max_Brightness);
+        }      
+        
+        wait(0.5);
+        
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i, PCA9685_Min_Brightness);
+        }
+    }
+}
+
+void Animate_Cycle_Analog(void) {
+
+    for (int i = 0; i < 10; i++) {
+        
+        PCA9685_EnableOutput(false);
+        float minVoltage = 0.09;
+        
+        for (int j = 0; j < 4; j++) {    
+            PCA9685_SetDigit(j, i, PCA9685_Max_Brightness);
+            PCA9685_SetDot(PCA9685_Max_Brightness);
+        }
+        
+        // Slowly drop the voltage to the minimum level
+        for (double k = 1; k >= minVoltage; k -= 0.001) {
+            PCA9685_SetVoltage(k);
+            wait(0.001);
+        }
+        
+        PCA9685_EnableOutput(true);
+        
+        for (double k = minVoltage; k <= 1; k += 0.0001) {
+            PCA9685_SetVoltage(k);
+            wait(0.0002);
+        }
+        for (double k = 1; k >= minVoltage; k -= 0.0001) {
+            PCA9685_SetVoltage(k);
+            wait(0.0002);
+        }
+
+        PCA9685_EnableOutput(false);
+
+        // Slowly raise the voltage to the maximum level
+        for (double k = minVoltage; k <= 1; k += 0.01) {
+            PCA9685_SetVoltage(k);
+            wait(0.001);
+        }
+        
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i, PCA9685_Min_Brightness);
+            PCA9685_SetDot(PCA9685_Min_Brightness);
+        }
+    }
+        
+    PCA9685_EnableOutput(true);
+}
+
+void Animate_Cycle_Low_Pwm(void) {
+
+    PCA9685_SetVoltage(1.0);
+    PCA9685_SetDot(PCA9685_Min_Brightness);
+
+    int pwmMaxBrightness = 200;
+    for (int i = 0; i < 10; i++) {
+        for (int k = PCA9685_Min_Brightness; k <= pwmMaxBrightness; k++) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, k);
+                PCA9685_SetDot(k);
+            }
+            wait(0.01);
+        }      
+        for (int k = PCA9685_Min_Brightness; k <= pwmMaxBrightness; k++) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, pwmMaxBrightness-k);
+                PCA9685_SetDot(pwmMaxBrightness-k);;
+            }
+            wait(0.01);
+        }      
+        wait(0.2);
+    }
+}
+
+void Animate_Cycle_Pwm(void) {
+
+    PCA9685_SetVoltage(1.0);
+    PCA9685_SetDot(PCA9685_Min_Brightness);
+
+    for (int i = 0; i < 10; i++) {
+        for (int k = PCA9685_Min_Brightness; k <= PCA9685_Max_Brightness; k += 4) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, k);
+                PCA9685_SetDot(k);
+            }
+            wait(0.00001);
+        }      
+        for (int k = PCA9685_Min_Brightness; k <= PCA9685_Max_Brightness; k += 4) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, PCA9685_Max_Brightness-k);
+                PCA9685_SetDot(PCA9685_Max_Brightness-k);;
+            }
+            wait(0.00001);
+        }      
+        wait(0.2);
+    }
+}
+
+void Animate_Cycle_Fade(void) {
+
+    PCA9685_SetVoltage(1.0);
+    PCA9685_SetDot(PCA9685_Min_Brightness);
+    
+    for (int i = 0; i < 10; i++) {
+        int i_next = (i == 9) ? 0 : i+1;
+        for (int k = PCA9685_Min_Brightness; k <= PCA9685_Max_Brightness; k += 4) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, PCA9685_Max_Brightness-k);
+                PCA9685_SetDigit(j, i_next, k);
+            }
+            if (i % 2 == 0) {
+                PCA9685_SetDot(k);
+            } else {
+                PCA9685_SetDot(PCA9685_Max_Brightness-k);
+            }
+            wait(0.00005);
+        }        
+        wait(0.2);
+    }
+    
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 10; j++) {
+            PCA9685_SetDigit(i, j, PCA9685_Min_Brightness);
+        }
+    }
+}
+
+void Animate_Cycle_Fade_Random(void) {
+
+    int i_curr[4];
+    int i_next[4];
+
+    PCA9685_SetVoltage(1.0);
+    PCA9685_SetDot(PCA9685_Min_Brightness);
+    
+    for (int i = 0; i < 4; i++) {
+        i_curr[i] = rand() % 10;
+    }
+    
+    for (int k = PCA9685_Min_Brightness; k <= PCA9685_Max_Brightness; k += 4) {
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i_curr[j], k);
+        }
+        wait(0.00005);
+    }
+        
+    for (int iter = 0; iter < 10; iter++) {
+        
+        for (int i = 0; i < 4; i++) {
+            do {
+                i_next[i] = rand() % 10;
+            } while (i_next[i] == i_curr[i]);
+        }
+        
+        for (int k = PCA9685_Min_Brightness; k <= PCA9685_Max_Brightness; k += 4) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i_curr[j], PCA9685_Max_Brightness-k);
+                PCA9685_SetDigit(j, i_next[j], k);
+            }
+            if (iter % 2 == 0) {
+                PCA9685_SetDot(k);
+            } else {
+                PCA9685_SetDot(PCA9685_Max_Brightness-k);
+            }
+            wait(0.00005);
+        }
+        wait(0.2);
+        
+        for (int i = 0; i < 4; i++) {
+            i_curr[i] = i_next[i];
+        }
+    }
+    for (int k = PCA9685_Max_Brightness; k >= PCA9685_Min_Brightness; k -= 4) {
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i_curr[j], k);
+        }
+        wait(0.00005);
+    }
+}
+
+void Animate_Cycle_Fast(void) {
+
+    PCA9685_SetVoltage(1.0);
+    PCA9685_SetDot(PCA9685_Min_Brightness);
+
+    for (int k = 0; k < 10; k++) {
+        for (int i = 0; i < 10; i++) {
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, PCA9685_Max_Brightness);
+            }
+            if (i % 2 == 0) {
+                PCA9685_SetDot(PCA9685_Max_Brightness);
+            } else {
+                PCA9685_SetDot(PCA9685_Min_Brightness);
+            }
+            wait(0.1);
+            for (int j = 0; j < 4; j++) {
+                PCA9685_SetDigit(j, i, PCA9685_Min_Brightness);
+            }
+        }
+    }
+}
+
+void Animate_Cycle_Fast_Random(void) {
+
+    int i_curr[4];
+    int i_next[4];
+
+    PCA9685_SetVoltage(1.0);
+    PCA9685_SetDot(PCA9685_Min_Brightness);
+    
+    for (int i = 0; i < 4; i++) {
+        i_curr[i] = rand() % 10;
+    }
+
+    for (int k = 0; k < 100; k++) {
+        
+        for (int i = 0; i < 4; i++) {
+            do {
+                i_next[i] = rand() % 10;
+            } while (i_next[i] == i_curr[i]);
+        }
+        
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i_next[j], PCA9685_Max_Brightness);
+        }
+        
+        if (k % 2 == 0) {
+            PCA9685_SetDot(PCA9685_Max_Brightness);
+        } else {
+            PCA9685_SetDot(PCA9685_Min_Brightness);
+        }
+        
+        wait(0.1);
+        for (int j = 0; j < 4; j++) {
+            PCA9685_SetDigit(j, i_next[j], PCA9685_Min_Brightness);
+        }
+        
+        for (int i = 0; i < 4; i++) {
+            i_curr[i] = i_next[i];
+        }
+    }
+}

+ 17 - 0
Nixie_Firmware_Mbed/animation.h

@@ -0,0 +1,17 @@
+#ifndef _ANIMATION_H_
+#define _ANIMATION_H_
+
+#include "mbed.h"
+#include "main.h"
+#include "pca9685.h"
+
+void Animate_Cycle_Basic(void);
+void Animate_Cycle_Analog(void);
+void Animate_Cycle_Low_Pwm(void);
+void Animate_Cycle_Pwm(void);
+void Animate_Cycle_Fade(void);
+void Animate_Cycle_Fade_Random(void);
+void Animate_Cycle_Fast(void);
+void Animate_Cycle_Fast_Random(void);
+
+#endif

+ 7 - 0
Nixie_Firmware_Mbed/ds3231.cpp

@@ -0,0 +1,7 @@
+#include "ds3231.h"
+
+extern I2C i2c;
+
+void DS3231_Init(void) {
+
+}

+ 36 - 33
Nixie_Firmware_Mbed/ds3231.h

@@ -2,54 +2,55 @@
 #define _DS_3231_H_
 
 #include "mbed.h"
+#include "main.h"
 
 #define DS3231_ADDR 0x68
 
 typedef union {
     struct {
-        uint8_t Second : 7;
-        uint8_t        : 1;
+        uint8_t Value : 7;
+        uint8_t       : 1;
     };
     uint8_t AS_BYTE;
 } T_SECOND;
 
 typedef union {
     struct {
-        uint8_t Minute : 7;
-        uint8_t        : 1;
+        uint8_t Value : 7;
+        uint8_t       : 1;
     };
     uint8_t AS_BYTE;
 } T_MINUTE;
 
 typedef union {
     struct {
-        uint8_t Hour : 5;
-        uint8_t nAM  : 1;
-        uint8_t n24  : 1;
-        uint8_t      : 1;
+        uint8_t Value : 5;
+        uint8_t nAM   : 1;
+        uint8_t n24   : 1;
+        uint8_t       : 1;
     };
     uint8_t AS_BYTE;
 } T_HOUR;
 
 typedef union {
     struct {
-        uint8_t Day : 3;
-        uint8_t     : 5;
+        uint8_t Value : 3;
+        uint8_t       : 5;
     };
     uint8_t AS_BYTE;
 } T_DAY;
 
 typedef union {
     struct {
-        uint8_t Date : 6;
-        uint8_t      : 2;
+        uint8_t Value : 6;
+        uint8_t       : 2;
     };
     uint8_t AS_BYTE;
 } T_DATE;
 
 typedef union {
     struct {
-        uint8_t Month   : 5;
+        uint8_t Value   : 5;
         uint8_t         : 2;
         uint8_t Century : 1;
     };
@@ -58,33 +59,33 @@ typedef union {
 
 typedef union {
     struct {
-        uint8_t Year : 8;
+        uint8_t Value : 8;
     };
     uint8_t AS_BYTE;
 } T_YEAR;
 
 typedef union {
     struct {
-        uint8_t Second : 7;
-        uint8_t M1     : 1;
+        uint8_t Value : 7;
+        uint8_t M1    : 1;
     };
     uint8_t AS_BYTE;
 } A_SECOND;
 
 typedef union {
     struct {
-        uint8_t Minute : 7;
-        uint8_t M2     : 1;
+        uint8_t Value : 7;
+        uint8_t M2    : 1;
     };
     uint8_t AS_BYTE;
 } A_MINUTE;
 
 typedef union {
     struct {
-        uint8_t Hour : 5;
-        uint8_t nAM  : 1;
-        uint8_t n24  : 1;
-        uint8_t M3   : 1;
+        uint8_t Value : 5;
+        uint8_t nAM   : 1;
+        uint8_t n24   : 1;
+        uint8_t M3    : 1;
     };
     uint8_t AS_BYTE;
 } A_HOUR;
@@ -93,12 +94,12 @@ typedef union {
     struct {
         union {
             struct {
-                uint8_t Day  : 4;
-                uint8_t      : 2;
-            };
+                uint8_t Value : 4;
+                uint8_t       : 2;
+            } Day;
             struct {
-                uint8_t Date : 6;
-            };
+                uint8_t Value : 6;
+            } Date;
         };
         uint8_t nDT : 1;
         uint8_t M4  : 1;
@@ -134,24 +135,24 @@ typedef union {
 
 typedef union {
     struct {
-        uint8_t Data : 7;
-        uint8_t Sign : 1;
+        uint8_t Value : 7;
+        uint8_t Sign  : 1;
     };
     uint8_t AS_BYTE;
 } AGING_OFFSET;
 
 typedef union {
     struct {
-        uint8_t Data : 7;
-        uint8_t Sign : 1;
+        uint8_t Value : 7;
+        uint8_t Sign  : 1;
     };
     uint8_t AS_BYTE;
 } TEMP_MSB;
 
 typedef union {
     struct {
-        uint8_t      : 6;
-        uint8_t Data : 2;
+        uint8_t       : 6;
+        uint8_t Value : 2;
     };
     uint8_t AS_DATA;
 } TEMP_LSB;
@@ -180,4 +181,6 @@ typedef union {
     };
 } DS3231_REGS;
 
+void DS3231_Init(void);
+
 #endif

+ 24 - 0
Nixie_Firmware_Mbed/ioc.cpp

@@ -0,0 +1,24 @@
+#include "ioc.h"
+
+InterruptIn IO_nFault(PA_3);
+DigitalOut  IO_HvEn(PA_2);
+DigitalOut  IO_LED(PB_6);
+AnalogIn    IO_Imon(PA_0);
+AnalogIn    IO_Vout(PA_1);
+
+void InterruptFault(void) {
+    IO_LED.write(1);
+    while(1);
+}
+
+void IO_Init(void) {
+    IO_nFault.fall(&InterruptFault);
+}
+
+void HV_EnableOutput(bool Enable) {
+    IO_HvEn.write(Enable);
+}
+
+void LED_SetOutput(bool Value) {
+    IO_LED.write(Value);
+}

+ 10 - 0
Nixie_Firmware_Mbed/ioc.h

@@ -0,0 +1,10 @@
+#ifndef _IOC_H_
+#define _IOC_H_
+
+#include "mbed.h"
+#include "main.h"
+
+void IO_Init(void);
+void HV_EnableOutput(bool Enable);
+
+#endif

+ 29 - 383
Nixie_Firmware_Mbed/main.cpp

@@ -1,406 +1,52 @@
 #include "mbed.h"
+#include "main.h"
 #include "SWO.h"
 #include "pca9685.h"
 #include "tusb322.h"
 #include "ds3231.h"
+#include "ioc.h"
+#include "animation.h"
 
-DigitalIn nFault(PA_3);
-DigitalIn Usb(PB_4);
-DigitalOut HvEn(PA_2);
-DigitalOut nNixieEn(PA_7);
-DigitalOut AmpEn(PB_1);
-DigitalOut LED(PB_6);
-AnalogIn Imon(PA_0);
-AnalogIn Vout(PA_1);
-AnalogOut Aout(PA_5);
 I2C i2c(PA_10, PA_9);
-SWO_Channel swo("swo");
-
-PCM9685_REGS Regs[3] = {0};
-
-// Calculate the byte offset of a field in a structure of type type.
-#define FIELD_OFFSET(type, field) ((uint32_t)(uint32_t*)&(((type *)0)->field))
-
-void Clock_Update(int tube, int digit, int brightness) {
-    char buffer[sizeof(LED_CTRL)+1] = {0};
-    
-    if (!nFault.read()) { LED.write(1); while(1); }
-
-    buffer[0] = FIELD_OFFSET(PCM9685_REGS, LED0) + 
-        (Tube_Mapping[tube][digit][MAP_PIN] * sizeof(LED_CTRL));
-
-    LED_CTRL *reg = (LED_CTRL*)&buffer[1];
-    if (brightness >= 1000) {
-        reg->ON_FULL = 1;
-    } else if (brightness == 0) {
-        reg->OFF_FULL = 1;
-    } else {
-        reg->OFF = brightness * 4;
-    }
-
-    i2c.write(Tube_Mapping[tube][digit][MAP_ADDR] << 1, buffer, sizeof(buffer));
-}
-
-void Dot_Update(int brightness) {
-    char buffer[sizeof(LED_CTRL)+1] = {0};
-    
-    buffer[0] = FIELD_OFFSET(PCM9685_REGS, TUBE_DOT_PIN);
-    
-    LED_CTRL *reg = (LED_CTRL*)&buffer[1];
-
-    if (brightness == 0) {
-        reg->OFF_FULL = 1;
-    } else {
-        reg->OFF = brightness;
-    }
-
-    i2c.write(TUBE_DOT_ADDR << 1, buffer, sizeof(buffer));
-}
-
-void Clock_Init() {
-    char buffer[sizeof(REG_MODE1)+sizeof(REG_MODE2)+1] = {0};
-    buffer[0] = FIELD_OFFSET(PCM9685_REGS, MODE1);
-    
-    REG_MODE1 *reg1 = (REG_MODE1*)&buffer[1];
-    reg1->AI = 1;         // Turn on autoincrement
-    reg1->SLEEP = 1;      // Start disabled
-    reg1->ALLCALL = 1;    // Enable response to all call address
-    
-    REG_MODE2 *reg2 = (REG_MODE2*)&buffer[1+sizeof(REG_MODE1)];
-    reg2->OUTDRV = 1;     // Configure output for totem pole drive
-    
-    i2c.write(PCM9685_All_Call << 1, buffer, sizeof(buffer));
-    
-    char pre_scale[2] = {0};
-    pre_scale[0] = FIELD_OFFSET(PCM9685_REGS, PRE_SCALE);
-    pre_scale[1] = 0x03;  // Set PWM frequency to 1526 Hz
-    
-    i2c.write(PCM9685_All_Call << 1, pre_scale, sizeof(pre_scale));
-    
-    Dot_Update(0);
-    for (int i = 0; i < 4; i++) {
-        for (int j = 0; j < 10; j++) {
-            Clock_Update(i, j, 0);
-        }
-    }
-    
-    reg1->SLEEP = 0;
-    
-    i2c.write(PCM9685_All_Call << 1, buffer, sizeof(buffer));
-}
-
-void Set_Analog(float voltage) {
-    voltage = voltage * 0.6;
-    Aout.write(voltage);
-}
-
-void USB_Init() {
-    char buffer[sizeof(TUSB322_REGS)] = {0};
-    
-    // Disable UFP accessory support (otherwise IC stays in DRP mode)
-    buffer[0] = FIELD_OFFSET(TUSB322_REGS, Status2);
-    CONN_STATUS2 *stat = (CONN_STATUS2*)&buffer[1];
-    stat->UFP_ACCESSORY = 0x1;
-    i2c.write(TUSB322_ADDR << 1, buffer, 2);
-
-    // Disable CC termination to change to UFP mode
-    buffer[0] = FIELD_OFFSET(TUSB322_REGS, Control);
-    CTRL *ctrl = (CTRL*)&buffer[1];
-    ctrl->DISABLE_TERM = 0x1;
-    i2c.write(TUSB322_ADDR << 1, buffer, 2);
-
-    // For operation in UFP mode
-    ctrl->MODE_SELECT = 0x01;
-    i2c.write(TUSB322_ADDR << 1, buffer, 2);
-
-    // Reenable CC termination
-    ctrl->DISABLE_TERM = 0x0;
-    i2c.write(TUSB322_ADDR << 1, buffer, 2);
-}
-
-//#define CURRENT_TEST
-#define CYCLE_DISPLAY
-#define CYCLE_BASIC
-//#define CYCLE_ANALOG
-#define CYCLE_PWM
-#define CYCLE_FADE
-#define CYCLE_FADE_RANDOM
-#define CYCLE_FAST
-#define CYCLE_FAST_RANDOM
+// SWO_Channel swo("swo");
 
 int main() {
     
-    // Initialize pins
-    i2c.frequency(1000000);
-    
-    // Power up PCA9685 at 3.3V with outputs disabled
-    Aout.write(1);
-    AmpEn.write(1);
-    nNixieEn.write(1);
+    // Start I2C at 400kHz for DS3231 
+    i2c.frequency(400000);
     
     // Start with HV PSU disabled
-    HvEn.write(0);
-    
-    USB_Init();
+    HV_EnableOutput(false);
     
-    LED.write(1);
-    wait(1);
-    LED.write(0);
-    
-    Clock_Init();
-    nNixieEn.write(0);
+    TUSB322_Init();
+    PCA9685_Init();
     
     // Enable HV PSU
-    HvEn.write(1);
+    HV_EnableOutput(true);
     
-    // Reduce PCA9685 voltage
-    Set_Analog(1);
+    // Set PCA9685 input voltage to highest possible
+    PCA9685_SetVoltage(1.0);
     
-    swo.printf("CPU SystemCoreClock is %d Hz\r\n", SystemCoreClock);
+    // swo.printf("CPU SystemCoreClock is %d Hz\r\n", SystemCoreClock);
     
-#ifdef CURRENT_TEST
-    Dot_Update(100);  
-   Clock_Update(3, 1, 100);
-   Clock_Update(2, 3, 100);
-   Clock_Update(1, 5, 100);
-   Clock_Update(0, 7, 100);
-#endif
-
-    while(1) {
-            
-        int i_curr[4];
-        int i_next[4];
-            
-#ifdef CYCLE_DISPLAY
-        Set_Analog(1);
-            
-#ifdef CYCLE_BASIC
-        // Switch each digit from 0 to 100%
-        for (int i = 0; i < 10; i++) {
-            if (i % 2 == 0) {
-                Dot_Update(0);
-            } else {
-                Dot_Update(1000);
-            }
-            
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i, 1000);
-            }      
-            
-            wait(0.5);
-            
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i, 0);
-            }
-        }
-#endif
-      
-#ifdef CYCLE_ANALOG  
-        for (int i = 0; i < 10; i++) {
-            
-            nNixieEn.write(1);
-            
-            for (int j = 0; j < 4; j++) {    
-                Clock_Update(j, i, 1000);
-                Dot_Update(1000);
-            }
-            
-            for (double k = 1; k >= 0.1; k -= 0.001) {
-                Set_Analog(k);
-                wait(0.0001);
-            }
-            
-            nNixieEn.write(0);
-            
-            for (double k = 0.1; k <= 1; k += 0.001) {
-                Set_Analog(k);
-                wait(0.001);
-            }
-            wait(0.2);
-            for (double k = 1; k >= 0.1; k -= 0.001) {
-                Set_Analog(k);
-                wait(0.001);
-            }
-
-            nNixieEn.write(1);
-
-            for (double k = 0.1; k <= 1; k += 0.001) {
-                Set_Analog(k);
-                wait(0.0001);
-            }
-            
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i, 0);
-                Dot_Update(0);
-            }
-        }
-        
-        nNixieEn.write(0);
-        Set_Analog(1);
-#endif
-        
-#ifdef CYCLE_PWM
-        for (int i = 0; i < 10; i++) {
-            for (int k = 0; k <= 1000; k++) {
-                for (int j = 0; j < 4; j++) {
-                    Clock_Update(j, i, k);
-                    Dot_Update(k);
-                }
-                wait(0.0001);
-            }      
-            for (int k = 0; k <= 1000; k++) {
-                for (int j = 0; j < 4; j++) {
-                    Clock_Update(j, i, 1000-k);
-                    Dot_Update(1000-k);;
-                }
-                wait(0.0001);
-            }      
-            wait(0.2);
-        }
-#endif
-        
-#ifdef CYCLE_FADE
-        
-        Set_Analog(1);
-        Dot_Update(0);
-        
-        for (int i = 0; i < 10; i++) {
-            int i_next = (i == 9) ? 0 : i+1;
-            for (int k = 0; k <= 1000; k++) {
-                for (int j = 0; j < 4; j++) {
-                    Clock_Update(j, i, 1000-k);
-                    Clock_Update(j, i_next, k);
-                }
-                if (i % 2 == 0) {
-                    Dot_Update(k);
-                } else {
-                    Dot_Update(1000-k);
-                }
-                wait(0.00005);
-            }        
-            wait(0.2);
-        }
-        
-        for (int i = 0; i < 4; i++) {
-            for (int j = 0; j < 10; j++) {
-                Clock_Update(i, j, 0);
-            }
-        }
-#endif
-      
-#ifdef CYCLE_FADE_RANDOM  
-
-        Set_Analog(1);
-        Dot_Update(0);
-        
-        for (int i = 0; i < 4; i++) {
-            i_curr[i] = rand() % 10;
-        }
-        
-        for (int k = 0; k <= 1000; k++) {
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i_curr[j], k);
-            }
-            wait(0.00005);
-        }
-            
-        for (int iter = 0; iter < 10; iter++) {
-            
-            for (int i = 0; i < 4; i++) {
-                do {
-                    i_next[i] = rand() % 10;
-                } while (i_next[i] == i_curr[i]);
-            }
-            
-            for (int k = 0; k <= 1000; k++) {
-                for (int j = 0; j < 4; j++) {
-                    Clock_Update(j, i_curr[j], 1000-k);
-                    Clock_Update(j, i_next[j], k);
-                }
-                if (iter % 2 == 0) {
-                    Dot_Update(k);
-                } else {
-                    Dot_Update(1000-k);
-                }
-                wait(0.00005);
-            }
-            wait(0.2);
-            
-            for (int i = 0; i < 4; i++) {
-                i_curr[i] = i_next[i];
-            }
-        }
-        for (int k = 1000; k >= 0; k--) {
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i_curr[j], k);
-            }
-            wait(0.00005);
-        }
-#endif
-
-#ifdef CYCLE_FAST
-
-        Set_Analog(1);
-        Dot_Update(0);
-
-        for (int k = 0; k < 10; k++) {
-            for (int i = 0; i < 10; i++) {
-                for (int j = 0; j < 4; j++) {
-                    Clock_Update(j, i, 1000);
-                }
-                if (i % 2 == 0) {
-                    Dot_Update(1000);
-                } else {
-                    Dot_Update(0);
-                }
-                wait(0.1);
-                for (int j = 0; j < 4; j++) {
-                    Clock_Update(j, i, 0);
-                }
-            }
-        }
-#endif
-
-#ifdef CYCLE_FAST_RANDOM
+    // Bump I2C frequency to 1MHz for PCA9685
+    i2c.frequency(1000000);
 
-        Set_Analog(1);
-        Dot_Update(0);
-        
-        for (int i = 0; i < 4; i++) {
-            i_curr[i] = rand() % 10;
-        }
+    // PCA9685_SetDot(100);  
+    // PCA9685_SetDigit(3, 1, 100);
+    // PCA9685_SetDigit(2, 3, 100);
+    // PCA9685_SetDigit(1, 5, 100);
+    // PCA9685_SetDigit(0, 7, 100);
 
-        for (int k = 0; k < 100; k++) {
-            
-            for (int i = 0; i < 4; i++) {
-                do {
-                    i_next[i] = rand() % 10;
-                } while (i_next[i] == i_curr[i]);
-            }
-            
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i_next[j], 1000);
-            }
-            
-            if (k % 2 == 0) {
-                Dot_Update(1000);
-            } else {
-                Dot_Update(0);
-            }
-            
-            wait(0.1);
-            for (int j = 0; j < 4; j++) {
-                Clock_Update(j, i_next[j], 0);
-            }
+    while(1) {
             
-            for (int i = 0; i < 4; i++) {
-                i_curr[i] = i_next[i];
-            }
-        }
-
-#endif
-        
-#endif
-        
+        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();
     }
 }

+ 7 - 0
Nixie_Firmware_Mbed/main.h

@@ -0,0 +1,7 @@
+#ifndef _MAIN_H_
+#define _MAIN_H_
+
+// Calculate the byte offset of a field in a structure of type type.
+#define FIELD_OFFSET(type, field) ((uint32_t)(uint32_t*)&(((type *)0)->field))
+
+#endif

+ 95 - 0
Nixie_Firmware_Mbed/pca9685.cpp

@@ -0,0 +1,95 @@
+#include "pca9685.h"
+
+extern I2C i2c;
+DigitalOut PCA9685_PWM_nEN(PA_7);
+DigitalOut PCA9685_VIN_EN(PB_1);
+AnalogOut  PCA9685_VIN(PA_5);
+
+void PCA9685_Init(void) {
+    // Power up PCA9685 at full voltage with outputs disabled
+    PCA9685_VIN.write(1);
+    PCA9685_VIN_EN.write(1);
+    PCA9685_PWM_nEN.write(1);
+
+    char buffer[sizeof(REG_MODE1)+sizeof(REG_MODE2)+1] = {0};
+    buffer[0] = FIELD_OFFSET(PCM9685_REGS, MODE1);
+    
+    REG_MODE1 *reg1 = (REG_MODE1*)&buffer[1];
+    reg1->AI = 1;         // Turn on autoincrement
+    reg1->SLEEP = 1;      // Start disabled
+    reg1->ALLCALL = 1;    // Enable response to all call address
+    
+    REG_MODE2 *reg2 = (REG_MODE2*)&buffer[1+sizeof(REG_MODE1)];
+    reg2->OUTDRV = 1;     // Configure output for totem pole drive
+    
+    i2c.write(PCM9685_All_Call << 1, buffer, sizeof(buffer));
+    
+    char pre_scale[2] = {0};
+    pre_scale[0] = FIELD_OFFSET(PCM9685_REGS, PRE_SCALE);
+    pre_scale[1] = 0x03;  // Set PWM frequency to 1526 Hz
+    
+    i2c.write(PCM9685_All_Call << 1, pre_scale, sizeof(pre_scale));
+    
+    PCA9685_SetDot(0);
+    for (int i = 0; i < 4; i++) {
+        for (int j = 0; j < 10; j++) {
+            PCA9685_SetDigit(i, j, 0);
+        }
+    }
+    
+    reg1->SLEEP = 0;
+    
+    i2c.write(PCM9685_All_Call << 1, buffer, sizeof(buffer));
+
+    // Enable outputs after configuration has been completed
+    PCA9685_PWM_nEN.write(0);
+}
+
+void PCA9685_SetVoltage(float Percent) {
+    Percent = Percent * 0.6;
+    PCA9685_VIN.write(Percent);
+}
+
+void PCA9685_SetDigit(int Tube, int Digit, int Brightness) {
+    char buffer[sizeof(LED_CTRL)+1] = {0};
+
+    buffer[0] = FIELD_OFFSET(PCM9685_REGS, LED0) + 
+        (Tube_Mapping[Tube][Digit][MAP_PIN] * sizeof(LED_CTRL));
+
+    LED_CTRL *reg = (LED_CTRL*)&buffer[1];
+    if (Brightness >= PCA9685_Max_Brightness) {
+        reg->ON_FULL = 1;
+    } else if (Brightness == 0) {
+        reg->OFF_FULL = 1;
+    } else {
+        reg->OFF = Brightness;
+    }
+
+    i2c.write(Tube_Mapping[Tube][Digit][MAP_ADDR] << 1, buffer, sizeof(buffer));
+}
+
+void PCA9685_SetDot(int Brightness) {
+    char buffer[sizeof(LED_CTRL)+1] = {0};
+    
+    buffer[0] = FIELD_OFFSET(PCM9685_REGS, TUBE_DOT_PIN);
+    
+    LED_CTRL *reg = (LED_CTRL*)&buffer[1];
+
+    if (Brightness >= PCA9685_Max_Brightness) {
+        reg->ON_FULL = 1;
+    } else if (Brightness == 0) {
+        reg->OFF_FULL = 1;
+    } else {
+        reg->OFF = Brightness;
+    }
+
+    i2c.write(TUBE_DOT_ADDR << 1, buffer, sizeof(buffer));
+}
+
+void PCA9685_EnableOutput(bool Enabled) {
+    if (Enabled) {
+        PCA9685_PWM_nEN.write(0);
+    } else {
+        PCA9685_PWM_nEN.write(1);
+    }
+}

+ 14 - 4
Nixie_Firmware_Mbed/pca9685.h

@@ -2,6 +2,11 @@
 #define _PCA_9685_H_
 
 #include "mbed.h"
+#include "main.h"
+
+#define PCM9685_ADDR_1     0x41
+#define PCM9685_ADDR_2     0x42
+#define PCM9685_ADDR_3     0x43
 
 // Hardcoded addresses available on powerup
 #define PCM9685_All_Call   0x70     // Default enabled
@@ -10,6 +15,9 @@
 #define PCM9685_Sub_Call_3 0x73     // Default disabled
 #define PCM9685_Reset      0x03     // Default enabled?
 
+#define PCA9685_Max_Brightness 4096
+#define PCA9685_Min_Brightness 0
+
 typedef union {
     struct {
         uint8_t ALLCALL : 1;
@@ -88,10 +96,6 @@ typedef struct {
     uint8_t         TestMode;       // +0xFF
 } PCM9685_REGS;
 
-#define PCM9685_ADDR_1 0x41
-#define PCM9685_ADDR_2 0x42
-#define PCM9685_ADDR_3 0x43
-
 #define TUBE_DOT_ADDR PCM9685_ADDR_2
 #define TUBE_DOT_PIN LED15
 
@@ -143,4 +147,10 @@ const char Tube_Mapping[4][10][2] =
         { PCM9685_ADDR_3, 3  },     // Tube 3 Digit 8
         { PCM9685_ADDR_3, 5  } } }; // Tube 3 Digit 9
 
+void PCA9685_Init(void);
+void PCA9685_SetVoltage(float Percent);
+void PCA9685_SetDigit(int Tube, int Digit, int Brightness);
+void PCA9685_SetDot(int Brightness);
+void PCA9685_EnableOutput(bool Enabled);
+
 #endif

+ 34 - 0
Nixie_Firmware_Mbed/tusb322.cpp

@@ -0,0 +1,34 @@
+#include "tusb322.h"
+
+extern I2C i2c;
+InterruptIn IO_Usb(PB_4);
+
+void UsbInterrupt(void) {
+    // No real uses for this at the moment
+}
+
+void TUSB322_Init(void) {
+    char buffer[sizeof(TUSB322_REGS)] = {0};
+    
+    IO_Usb.fall(&UsbInterrupt);
+
+    // Disable UFP accessory support (otherwise IC stays in DRP mode)
+    buffer[0] = FIELD_OFFSET(TUSB322_REGS, Status2);
+    CONN_STATUS2 *stat = (CONN_STATUS2*)&buffer[1];
+    stat->UFP_ACCESSORY = 0x1;
+    i2c.write(TUSB322_ADDR << 1, buffer, 2);
+
+    // Disable CC termination to change to UFP mode
+    buffer[0] = FIELD_OFFSET(TUSB322_REGS, Control);
+    CTRL *ctrl = (CTRL*)&buffer[1];
+    ctrl->DISABLE_TERM = 0x1;
+    i2c.write(TUSB322_ADDR << 1, buffer, 2);
+
+    // For operation in UFP mode
+    ctrl->MODE_SELECT = 0x01;
+    i2c.write(TUSB322_ADDR << 1, buffer, 2);
+
+    // Reenable CC termination
+    ctrl->DISABLE_TERM = 0x0;
+    i2c.write(TUSB322_ADDR << 1, buffer, 2);
+}

+ 3 - 0
Nixie_Firmware_Mbed/tusb322.h

@@ -2,6 +2,7 @@
 #define _TUSB_322_H_
 
 #include "mbed.h"
+#include "main.h"
 
 #define TUSB322_ADDR 0x47 
 
@@ -48,4 +49,6 @@ typedef union {
     uint8_t AS_BYTE[11];
 } TUSB322_REGS;
 
+void TUSB322_Init(void);
+
 #endif