Browse Source

Verified communication with external chips

Kevin Lee 2 years ago
parent
commit
a11b00b7e6

+ 4 - 3
Nixie_Firmware_Rust/Cargo.toml

@@ -12,6 +12,7 @@ cortex-m-semihosting = "0.3.3"
 # panic-halt = "0.2.0"
 tock-registers = "0.7.0"
 field-offset = "0.3.4"
+stm32l4xx-hal = { version = "0.6.0", features = ["stm32l4x2", "rt"] }
 
 # Uncomment for the panic example.
 # panic-itm = "0.4.1"
@@ -26,9 +27,9 @@ field-offset = "0.3.4"
 # features = ["stm32f303", "rt"]
 # version = "0.7.1"
 
-[dependencies.stm32l4xx-hal]
-version = "0.6.0"
-features = ["stm32l4x2", "rt"]
+# [dependencies.stm32l4xx-hal]
+# version = "0.6.0"
+# features = ["stm32l4x2", "rt"]
 
 # this lets you use `cargo fix`!
 # [[bin]]

+ 103 - 90
Nixie_Firmware_Rust/src/main.rs

@@ -1,7 +1,6 @@
 #![cfg_attr(test, allow(unused_imports))]
 #![cfg_attr(not(test), no_std)]
 #![cfg_attr(not(test), no_main)]
-// #![feature(generic_const_exprs)]
 #![feature(half_open_range_patterns)]
 #![feature(exclusive_range_pattern)]
 #![allow(dead_code)]
@@ -11,14 +10,15 @@
 use core::panic::PanicInfo;
 
 use core::{cell::RefCell, ops::DerefMut};
-// use cortex_m::asm;
-use cortex_m::{interrupt::free, interrupt::Mutex, peripheral::NVIC};
+use cortex_m::{asm, interrupt::free, interrupt::Mutex, peripheral::NVIC};
 use cortex_m_rt::entry;
 // use cortex_m_semihosting::hprintln;
 use stm32l4xx_hal::{
     delay::Delay,
     device::I2C1,
-    gpio::{Alternate, Edge, Input, OpenDrain, Output, PullUp, PushPull, AF4, PA3, PC15},
+    gpio::{
+        Alternate, Edge, Floating, Input, OpenDrain, Output, PullUp, PushPull, AF4, PA3, PB5, PC15,
+    },
     gpio::{State, PA10, PA9},
     i2c::I2c,
     interrupt, pac,
@@ -28,9 +28,13 @@ use stm32l4xx_hal::{
 };
 
 mod ds3231;
-mod tusb322;
+mod nixie;
 mod pca9685;
+mod tusb322;
+
+use nixie::*;
 
+static RTC_INT: Mutex<RefCell<Option<PB5<Input<Floating>>>>> = Mutex::new(RefCell::new(None));
 static FAULT_INT: Mutex<RefCell<Option<PA3<Input<PullUp>>>>> = Mutex::new(RefCell::new(None));
 static FAULT_LED: Mutex<RefCell<Option<PC15<Output<PushPull>>>>> = Mutex::new(RefCell::new(None));
 static I2C: Mutex<
@@ -47,68 +51,6 @@ static I2C: Mutex<
     >,
 > = Mutex::new(RefCell::new(None));
 
-const DS3231_ADDR: u8 = 0x68;
-const TUSB322_ADDR: u8 = 0x47;
-const PCA9685_ADDR_1: u8 = 0x41;
-const PCA9685_ADDR_2: u8 = 0x42;
-const PCA9685_ADDR_3: u8 = 0x43;
-
-
-const MAP_ADDR: usize = 0;
-const MAP_PIN: usize = 1;
-
-static PCA9685_MAPPING: [[[u8; 2]; 10]; 4] = [
-    [
-        [PCA9685_ADDR_1, 8],  // Tube 0 Digit 0
-        [PCA9685_ADDR_1, 9],  // Tube 0 Digit 1
-        [PCA9685_ADDR_1, 10], // Tube 0 Digit 2
-        [PCA9685_ADDR_1, 12], // Tube 0 Digit 3
-        [PCA9685_ADDR_1, 15], // Tube 0 Digit 4
-        [PCA9685_ADDR_1, 14], // Tube 0 Digit 5
-        [PCA9685_ADDR_1, 11], // Tube 0 Digit 6
-        [PCA9685_ADDR_1, 0],  // Tube 0 Digit 7
-        [PCA9685_ADDR_1, 1],  // Tube 0 Digit 8
-        [PCA9685_ADDR_1, 13], // Tube 0 Digit 9
-    ],
-    [
-        [PCA9685_ADDR_1, 5], // Tube 1 Digit 0
-        [PCA9685_ADDR_1, 6], // Tube 1 Digit 1
-        [PCA9685_ADDR_1, 7], // Tube 1 Digit 2
-        [PCA9685_ADDR_1, 2], // Tube 1 Digit 3
-        [PCA9685_ADDR_2, 4], // Tube 1 Digit 4
-        [PCA9685_ADDR_2, 1], // Tube 1 Digit 5
-        [PCA9685_ADDR_1, 4], // Tube 1 Digit 6
-        [PCA9685_ADDR_2, 2], // Tube 1 Digit 7
-        [PCA9685_ADDR_2, 3], // Tube 1 Digit 8
-        [PCA9685_ADDR_1, 3], // Tube 1 Digit 9
-    ],
-    [
-        [PCA9685_ADDR_3, 8],  // Tube 2 Digit 0
-        [PCA9685_ADDR_3, 9],  // Tube 2 Digit 1
-        [PCA9685_ADDR_3, 10], // Tube 2 Digit 2
-        [PCA9685_ADDR_3, 12], // Tube 2 Digit 3
-        [PCA9685_ADDR_2, 12], // Tube 2 Digit 4
-        [PCA9685_ADDR_2, 13], // Tube 2 Digit 5
-        [PCA9685_ADDR_3, 11], // Tube 2 Digit 6
-        [PCA9685_ADDR_2, 14], // Tube 2 Digit 7
-        [PCA9685_ADDR_2, 11], // Tube 2 Digit 8
-        [PCA9685_ADDR_3, 13], // Tube 2 Digit 9
-    ],
-    [
-        [PCA9685_ADDR_3, 5],  // Tube 3 Digit 0
-        [PCA9685_ADDR_3, 6],  // Tube 3 Digit 1
-        [PCA9685_ADDR_3, 7],  // Tube 3 Digit 2
-        [PCA9685_ADDR_3, 2],  // Tube 3 Digit 3
-        [PCA9685_ADDR_3, 14], // Tube 3 Digit 4
-        [PCA9685_ADDR_3, 15], // Tube 3 Digit 5
-        [PCA9685_ADDR_3, 4],  // Tube 3 Digit 6
-        [PCA9685_ADDR_3, 1],  // Tube 3 Digit 7
-        [PCA9685_ADDR_3, 0],  // Tube 3 Digit 8
-        [PCA9685_ADDR_3, 3],  // Tube 3 Digit 9
-    ],
-];
-
-// NewType wrapper for the
 // unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
 //     core::slice::from_raw_parts(
 //         (p as *const T) as *const u8,
@@ -144,17 +86,26 @@ fn main() -> ! {
     let clocks = rcc
         .cfgr
         .pll_source(rcc::PllSource::HSI16)
-        .sysclk(80.mhz())
-        .hclk(80.mhz())
-        .pclk1(80.mhz())
-        .pclk2(80.mhz())
+        .sysclk(64.mhz())
+        .hclk(64.mhz())
+        .pclk1(64.mhz())
+        .pclk2(64.mhz())
         .freeze(&mut flash.acr, &mut pwr);
 
+    // Configure abstract timer that operates off systick timer
+    let mut timer = Delay::new(cp.SYST, clocks);
+
     // Split GPIO peripheral into independent pins and registers
-    // let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
     let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
+    let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
     let mut gpioc = dp.GPIOC.split(&mut rcc.ahb2);
 
+    // Configure high voltage PSU enable pin on PA2
+    let mut hv_enable =
+        gpioa
+            .pa2
+            .into_push_pull_output_with_state(&mut gpioa.moder, &mut gpioa.otyper, State::Low);
+
     // Configure fault LED output on PC15
     let fault_led = gpioc.pc15.into_push_pull_output_with_state(
         &mut gpioc.moder,
@@ -162,7 +113,7 @@ fn main() -> ! {
         State::Low,
     );
 
-    // Store fault LED in global static variable as it is accessed in interrupts
+    // Store fault LED in static singleton so that interrupt has access to it
     free(|cs| {
         FAULT_LED.borrow(cs).replace(Some(fault_led));
     });
@@ -182,7 +133,7 @@ fn main() -> ! {
             NVIC::unmask(Interrupt::EXTI3);
         }
 
-        // Store fault input in global static variable as it is accessed in interrupt
+        // Store fault interrupt in static singleton so that interrupt has access to it
         free(|cs| {
             FAULT_INT.borrow(cs).replace(Some(fault_int));
         });
@@ -190,12 +141,6 @@ fn main() -> ! {
         panic!();
     }
 
-    // Start with HV PSU disabled (enable pin on PA2)
-    let mut _hv_en =
-        gpioa
-            .pa2
-            .into_push_pull_output_with_state(&mut gpioa.moder, &mut gpioa.otyper, State::Low);
-
     // Configure I2C SCL
     let scl = gpioa
         .pa9
@@ -208,26 +153,79 @@ fn main() -> ! {
         .into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper);
     let sda = sda.into_af4(&mut gpioa.moder, &mut gpioa.afrh);
 
-    // Initialize I2C
-    let mut i2c = I2c::i2c1(dp.I2C1, (scl, sda), 100.khz(), clocks, &mut rcc.apb1r1);
+    // Initialize I2C (configured for 1Mhz, but actually runs at 600kHz)
+    let mut i2c = I2c::i2c1(dp.I2C1, (scl, sda), 1.mhz(), clocks, &mut rcc.apb1r1);
 
+    // Initialize TUSB322 (USB Type-C configuration chip)
     tusb322::init(TUSB322_ADDR, &mut i2c);
 
+    // Initialize DS3231 (RTC)
     ds3231::init(DS3231_ADDR, &mut i2c);
 
+    // Configure input interrupt pin from DS3231 on PB5
+    // Interrupt is pulled high, with open drain on DS3231 to pull low
+    let mut rtc_int = gpiob
+        .pb5
+        .into_floating_input(&mut gpiob.moder, &mut gpiob.pupdr);
+    rtc_int.make_interrupt_source(&mut dp.SYSCFG, &mut rcc.apb2);
+    rtc_int.enable_interrupt(&mut dp.EXTI);
+    rtc_int.trigger_on_edge(&mut dp.EXTI, Edge::FALLING);
+
+    // Configure NVIC mask to enable interrupt source
+    unsafe {
+        NVIC::unmask(Interrupt::EXTI9_5);
+    }
+
+    // Store RTC interrupt in static singleton so that interrupt has access to it
+    free(|cs| {
+        RTC_INT.borrow(cs).replace(Some(rtc_int));
+    });
+
+    // Configure DAC AMP enable pin for AD8591 on PB1
+    let mut _dac_enable = gpiob.pb1.into_push_pull_output_with_state(
+        &mut gpiob.moder,
+        &mut gpiob.otyper,
+        State::High,
+    );
+
+    // Configure DAC VIN for AD8591 on PA5
+    // Note that this pin should actually be configured as analog output (for DAC)
+    // but stm32l4xx_hal doesn't have support for the DAC as of now. We also currently
+    // set the output to only the highest possible voltage, so the same functionality
+    // can be achieved by configuring the pin as a digital output set to high.
+    let mut _dac_output = gpioa.pa5.into_push_pull_output_with_state(
+        &mut gpioa.moder,
+        &mut gpioa.otyper,
+        State::High,
+    );
+
+    // Configure PWM enable pin (active low) for PCA9685 on PA7
+    let mut pwm_enable = gpioa.pa7.into_push_pull_output_with_state(
+        &mut gpioa.moder,
+        &mut gpioa.otyper,
+        State::High,
+    );
+
+    // Small delay to ensure that PCA9685 is fully powered on before writing to it
+    timer.delay_us(10_u32);
+
+    // Initialize PCA9685 (PWM driver)
+    pca9685::init(PCA9685_ALL_CALL, &mut i2c);
+
+    // Enable PWM output after PCA9685 has been initialized
+    pwm_enable.set_low().unwrap();
+
+    pca9685::set_digit(PCA9685_ADDR_1, &mut i2c, 0, 0, 4096);
+
+    // Store I2C peripheral in global static variable as it is used in interrupt
     free(|cs| {
         I2C.borrow(cs).replace(Some(i2c));
     });
 
-    // Configure abstract timer that operates off systick timer
-    let mut timer = Delay::new(cp.SYST, clocks);
+    // Enable the high voltage power supply last
+    hv_enable.set_high().unwrap();
 
-    loop {
-        timer.delay_ms(1000_u32);
-        set_fault_led(State::High);
-        timer.delay_ms(1000_u32);
-        set_fault_led(State::Low);
-    }
+    loop {}
 }
 
 fn set_fault_led(state: State) {
@@ -242,6 +240,21 @@ fn set_fault_led(state: State) {
     });
 }
 
+#[interrupt]
+fn EXTI9_5() {
+    free(|cs| {
+        let mut rtc_int_ref = RTC_INT.borrow(cs).borrow_mut();
+        if let Some(ref mut rtc_int) = rtc_int_ref.deref_mut() {
+            if rtc_int.check_interrupt() {
+                rtc_int.clear_interrupt_pending_bit();
+                set_fault_led(State::High);
+                asm::delay(8_000_000);
+                set_fault_led(State::Low);
+            }
+        }
+    });
+}
+
 #[interrupt]
 fn EXTI3() {
     free(|cs| {

+ 241 - 0
Nixie_Firmware_Rust/src/nixie.rs

@@ -0,0 +1,241 @@
+pub const DS3231_ADDR: u8 = 0x68;
+pub const TUSB322_ADDR: u8 = 0x47;
+pub const PCA9685_ADDR_1: u8 = 0x41;
+pub const PCA9685_ADDR_2: u8 = 0x42;
+pub const PCA9685_ADDR_3: u8 = 0x43;
+
+pub const PCA9685_ALL_CALL: u8 = 0x70; // Default enabled
+pub const PCA9685_SUB_CALL_1: u8 = 0x71; // Default disabled
+pub const PCA9685_SUB_CALL_2: u8 = 0x72; // Default disabled
+pub const PCA9685_SUB_CALL_3: u8 = 0x73; // Default disabled
+
+const MAP_DOT_ADDR: u8 = PCA9685_ADDR_2;
+const MAP_DOT_PIN: u8 = 15;
+
+const MAP_ADDR: usize = 0;
+const MAP_PIN: usize = 1;
+
+struct DigitToPin {
+    address: u8,
+    pin: usize,
+}
+
+struct PwmDriver {
+    digit: [DigitToPin; 10],
+}
+
+struct PwmOutputMap {
+    driver: [PwmDriver; 4],
+    dot_address: u8,
+    dot_pin: usize,
+}
+
+static TUBE_MAPPING: PwmOutputMap = {
+    PwmOutputMap {
+        driver: [
+            PwmDriver {
+                digit: [
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 8,
+                    }, // Tube 0 Digit 0
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 9,
+                    }, // Tube 0 Digit 1
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 10,
+                    }, // Tube 0 Digit 2
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 12,
+                    }, // Tube 0 Digit 3
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 15,
+                    }, // Tube 0 Digit 4
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 14,
+                    }, // Tube 0 Digit 5
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 11,
+                    }, // Tube 0 Digit 6
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 0,
+                    }, // Tube 0 Digit 7
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 1,
+                    }, // Tube 0 Digit 8
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 13,
+                    }, // Tube 0 Digit 9
+                ],
+            },
+            PwmDriver {
+                digit: [
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 5,
+                    }, // Tube 1 Digit 0
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 6,
+                    }, // Tube 1 Digit 1
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 7,
+                    }, // Tube 1 Digit 2
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 2,
+                    }, // Tube 1 Digit 3
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 4,
+                    }, // Tube 1 Digit 4
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 1,
+                    }, // Tube 1 Digit 5
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 4,
+                    }, // Tube 1 Digit 6
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 2,
+                    }, // Tube 1 Digit 7
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 3,
+                    }, // Tube 1 Digit 8
+                    DigitToPin {
+                        address: PCA9685_ADDR_1,
+                        pin: 3,
+                    }, // Tube 1 Digit 9
+                ],
+            },
+            PwmDriver {
+                digit: [
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 8,
+                    }, // Tube 2 Digit 0
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 9,
+                    }, // Tube 2 Digit 1
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 10,
+                    }, // Tube 2 Digit 2
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 12,
+                    }, // Tube 2 Digit 3
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 12,
+                    }, // Tube 2 Digit 4
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 13,
+                    }, // Tube 2 Digit 5
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 11,
+                    }, // Tube 2 Digit 6
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 14,
+                    }, // Tube 2 Digit 7
+                    DigitToPin {
+                        address: PCA9685_ADDR_2,
+                        pin: 11,
+                    }, // Tube 2 Digit 8
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 13,
+                    }, // Tube 2 Digit 9
+                ],
+            },
+            PwmDriver {
+                digit: [
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 5,
+                    }, // Tube 3 Digit 0
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 6,
+                    }, // Tube 3 Digit 1
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 7,
+                    }, // Tube 3 Digit 2
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 2,
+                    }, // Tube 3 Digit 3
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 14,
+                    }, // Tube 3 Digit 4
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 15,
+                    }, // Tube 3 Digit 5
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 4,
+                    }, // Tube 3 Digit 6
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 1,
+                    }, // Tube 3 Digit 7
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 0,
+                    }, // Tube 3 Digit 8
+                    DigitToPin {
+                        address: PCA9685_ADDR_3,
+                        pin: 3,
+                    }, // Tube 3 Digit 9
+                ],
+            },
+        ],
+        dot_address: PCA9685_ADDR_2,
+        dot_pin: 15,
+    }
+};
+
+enum DigitState {
+    Decrementing,
+    Incrementing
+}
+
+struct Digit {
+    current_state: DigitState,
+    value: u32,
+    pwm_start: u32,
+    pwm_end: u32,
+    updated: bool,
+}
+
+struct Tube {
+    digits: [Digit; 10],
+    last_active: usize,
+    refresh_last: usize,
+    active: bool,
+    fade_duration: u32,
+}
+
+struct Clock {
+    tubes: [Tube; 4],
+}

+ 9 - 0
Nixie_Firmware_Rust/src/pca9685.rs

@@ -40,6 +40,7 @@ register_bitfields![
 ];
 
 register_structs! {
+    #[repr(packed(1))]
     Pca9685Registers {
         (0x00 => mode1: InMemoryRegister<u8, MODE_1::Register>),
         (0x01 => mode2: InMemoryRegister<u8, MODE_2::Register>),
@@ -56,6 +57,14 @@ register_structs! {
     }
 }
 
+#[test]
+fn offset_test() {
+    assert_eq!(offset_of!(Pca9685Registers => led).get_byte_offset(), 0x6);
+    assert_eq!(offset_of!(Pca9685Registers => all_led).get_byte_offset(), 0xFA);
+    assert_eq!(offset_of!(Pca9685Registers => prescale).get_byte_offset(), 0xFE);
+    assert_eq!(offset_of!(Pca9685Registers => testmode).get_byte_offset(), 0xFF);
+}
+
 pub fn init(address: u8, i2c: &mut impl _embedded_hal_blocking_i2c_Write) {
     let mode1_reg: InMemoryRegister<u8, MODE_1::Register> = InMemoryRegister::new(0);
     let mode2_reg: InMemoryRegister<u8, MODE_2::Register> = InMemoryRegister::new(0);