From e9e2a7d823402cae6acbd7a0859ae2298af0d630 Mon Sep 17 00:00:00 2001
From: Benjamin Koch <bbbsnowball@gmail.com>
Date: Tue, 16 May 2023 03:32:21 +0200
Subject: [PATCH] add measurement for auto-baud

---
 firmware/rust1/src/bin/heizung.rs | 210 ++++++++++++++++++++++++++++--
 1 file changed, 196 insertions(+), 14 deletions(-)

diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index e7c2c14..385117b 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -4,6 +4,7 @@
 
 use defmt::*;
 use embassy_executor::Spawner;
+//use embassy_futures::join::join;
 use embassy_futures::select::select;
 use embassy_futures::select::Either::*;
 use embassy_rp::gpio;
@@ -11,13 +12,18 @@ use embassy_time::{Duration, Timer};
 use embassy_rp::gpio::{Input, Level, Output, Pull, Flex};
 use embassy_rp::i2c;
 use embassy_rp::interrupt;
-use embedded_hal_async::i2c::I2c;
+//use embedded_hal_async::i2c::I2c;
 use embassy_embedded_hal::SetConfig;
-use embassy_rp::peripherals::{UART0, I2C0};
+use embassy_rp::peripherals::{UART0};
 use embassy_rp::uart::{self, UartRx, Parity};
 use embassy_rp::pwm::{self, Pwm};
+use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection};
+use embassy_rp::relocate::RelocatedProgram;
+use embassy_rp::Peripheral;
 use {defmt_rtt as _, panic_probe as _};
 use heapless::String;
+use fixed::traits::ToFixed;
+use fixed_macro::types::U56F8;
 
 #[embassy_executor::task]
 async fn reader(mut rx: UartRx<'static, UART0, uart::Async>) {
@@ -45,7 +51,7 @@ fn append_hex2<const N : usize>(s : &mut String<N>, num : u8) -> Result<(), ()>
     append_hex1(s, num & 0xf)
 }
 
-#[derive(Copy, Clone, Debug)]
+#[derive(PartialEq, Eq, Copy, Clone, Debug)]
 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
 enum ScanResult {
     NAck,
@@ -204,12 +210,13 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0,
     use i2c::Error::*;
     use i2c::AbortReason::*;
 
+    const MAX_ADDRESS : u8 = 128;
+    let mut prev_scan_result = [ScanResult::Reserved; MAX_ADDRESS as usize];
     loop {
         if true {
-            let mut i2c = i2c::I2c::new_async(&mut i2c_peri, &mut scl, &mut sda, &mut i2c_irq, i2c_config);
+            let _i2c = i2c::I2c::new_async(&mut i2c_peri, &mut scl, &mut sda, &mut i2c_irq, i2c_config);
         }
 
-        const MAX_ADDRESS : u8 = 128;
         let mut scan_result = [ScanResult::Error; MAX_ADDRESS as usize];
         for i in 0..MAX_ADDRESS {
             scan_result[i as usize] = match i2c_write_zero_bytes::<_, _, 400_000>(i as u16, &mut scl, &mut sda).await {
@@ -223,11 +230,14 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0,
             }
         }
 
-        const MSG_LEN : usize = (MAX_ADDRESS as usize)*3 + (MAX_ADDRESS as usize)/16*6;
-        let mut msg = String::<MSG_LEN>::new();
-        match format_scan_result(&mut msg, scan_result) {
-            Ok(()) => info!("I2C scan result:\n{}", msg),
-            Err(()) => info!("I2C scan result: too long for our buffer!"),
+        if prev_scan_result.iter().zip(scan_result.iter()).any(|(x,y)| x != y) {
+            prev_scan_result = scan_result;
+            const MSG_LEN : usize = (MAX_ADDRESS as usize)*3 + (MAX_ADDRESS as usize)/16*6;
+            let mut msg = String::<MSG_LEN>::new();
+            match format_scan_result(&mut msg, scan_result) {
+                Ok(()) => info!("I2C scan result:\n{}", msg),
+                Err(()) => info!("I2C scan result: too long for our buffer!"),
+            }
         }
 
         Timer::after(Duration::from_secs(1)).await;
@@ -235,7 +245,7 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0,
 }
 
 #[embassy_executor::task]
-async fn beeper_task(mut pwm_channel: embassy_rp::peripherals::PWM_CH3, mut beeper_pin: embassy_rp::peripherals::PIN_6) {
+async fn beeper_task(pwm_channel: embassy_rp::peripherals::PWM_CH3, beeper_pin: embassy_rp::peripherals::PIN_6) {
     let mut c: pwm::Config = Default::default();
     c.top = 0x8000;
     c.compare_a = 8;
@@ -258,6 +268,175 @@ async fn beeper_task(mut pwm_channel: embassy_rp::peripherals::PWM_CH3, mut beep
     }
 }
 
+
+fn pin_io<P: embassy_rp::gpio::Pin>(pin: &P) -> embassy_rp::pac::io::Gpio {
+    use embassy_rp::gpio::Bank;
+    use embassy_rp::pac;
+
+    let block = match pin.bank() {
+        Bank::Bank0 => pac::IO_BANK0,
+        Bank::Qspi => pac::IO_QSPI,
+    };
+    block.gpio(pin.pin() as _)
+}
+
+async fn debug_print_pio_addr(sm: embassy_rp::pac::pio::StateMachine) {
+    let mut prev = 42u8;
+    loop {
+        let addr = unsafe { sm.addr().read().addr() };
+        if prev != addr {
+            prev = addr;
+            info!("SM addr: {}", addr);
+        }
+        Timer::after(Duration::from_millis(200)).await;
+    }
+}
+
+#[embassy_executor::task]
+async fn autobaud_task(pio0: embassy_rp::peripherals::PIO0, rx_pin: embassy_rp::peripherals::PIN_17,
+        dma_channel: embassy_rp::peripherals::DMA_CH0) {
+    let Pio {
+        mut common,
+        sm0: mut sm,
+        ..
+    } = Pio::new(pio0);
+
+    sm.set_enable(false);
+
+    let prg = pio_proc::pio_asm!(
+        ".origin 0",
+
+        // First, pull waiting time for "long 1" from input FIFO.
+        "pull",
+
+        // Send zero to RX FIFO to signal start of next measurement.
+        ".wrap_target",
+        "timeout:",
+        "set x, 0",
+        "mov isr, x",
+        "push",
+
+        // Wait for pin to be high for a longer time (UART is idle), copy waiting time to X
+        // and decrement until done, start again when pin changes.
+        "wait_idle:",
+        "wait 1 pin 0",
+        "mov x, osr",
+        "continue_wait_idle:",
+        "jmp pin still_one",
+        "jmp wait_idle",  // we got a zero, start again
+        "still_one:",
+        "jmp x--, continue_wait_idle",
+
+        // We have a long 1, i.e. UART is idle. Wait for start bit.
+        "have_long_high:",
+        "wait 0 pin 0",
+        "nop [4]",
+        "jmp pin wait_idle",  // abort if zero was just a glitch
+
+        // We want to measure 9 bits (start bit plus eight (or nine) data bits). However, we need
+        // different code for high and low so it's easier to measure 4x2 bits. Weuse Y as the loop counter.
+        "set y, 4",
+
+        // Start counting. We count down from the value in OSR (which is our timeout).
+        "measure_next:",
+        "mov x, osr",
+        "measure_low:",
+        "jmp pin changed_to_one",
+        //"nop",
+        "jmp x-- measure_low",
+        "jmp timeout",
+        "changed_to_one:",
+        "mov isr, x"
+        "push",
+
+        // Start counting, again, but for a high pulse.
+        "mov x, osr",
+        "measure_high:",
+        "jmp pin still_high2",
+        "jmp changed_to_low",
+        "still_high2:",
+        "jmp x-- measure_high",
+        "jmp timeout",
+        "changed_to_low:",
+        "mov isr, x"
+        "push",
+        "jmp y-- measure_next",
+
+        // We are done.
+        "irq set 0",
+        ".wrap",
+    );
+
+    let relocated = RelocatedProgram::new(&prg.program);
+    let mut cfg = Config::default();
+    cfg.use_program(&common.load_program(&relocated), &[]);
+    const SM_FREQ: u32 = 125_000_000;
+    cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(125_000_000 /* SM_FREQ */)).to_fixed();
+    cfg.shift_in = ShiftConfig {
+        auto_fill: false,
+        threshold: 32,
+        direction: ShiftDirection::Left,
+    };
+    cfg.shift_out = ShiftConfig {
+        auto_fill: false,
+        threshold: 32,
+        direction: ShiftDirection::Right,
+    };
+    let rx_pin_pio = common.make_pio_pin(unsafe { rx_pin.clone_unchecked() });
+    cfg.set_in_pins(&[&rx_pin_pio]);
+    cfg.set_jmp_pin(&rx_pin_pio);
+
+    sm.set_config(&cfg);
+    sm.set_enable(true);
+
+    // set rx pin function back to UART
+    unsafe {
+        pin_io(&rx_pin).ctrl().write(|w| w.set_funcsel(embassy_rp::pac::io::vals::Gpio17ctrlFuncsel::UART0_RX.0));
+    }
+
+    // Timeout: Modbus wants 1.5 ms between frames so we make this a bit smaller. SM runs at 25 MHz
+    // but we need 4 clocks per loop.
+    let timeout_start_value = (SM_FREQ as f32 * 1.2e-3 / 4.) as u32;
+    sm.tx().push(timeout_start_value);
+    info!("timeout_start_value: {} = 0x{:08x}", timeout_start_value, timeout_start_value);
+
+    let mut dma_in_ref = dma_channel.into_ref();
+    let mut din = [42u32; 4];
+    let mut bit_index = 0;
+    loop {
+        let x = select(
+            sm.rx().dma_pull(dma_in_ref.reborrow(), &mut din),
+            debug_print_pio_addr(embassy_rp::pac::PIO0.sm(0)),
+        ).await;
+        match x {
+            First(_) => {
+                for i in 0..din.len() {
+                    if din[i] == 0 {
+                        bit_index = 0;
+                        info!("SM in {}: {:08x} -> start", i, din[i]);
+                    } else {
+                        let mut delay = timeout_start_value - din[i];
+                        if bit_index == 0 {
+                            delay += 7;
+                        } else if bit_index%2 == 1 {
+                            //delay += 4;
+                            delay += 3;
+                        } else {
+                            delay += 3;
+                        }
+                        let millis = (delay) as f32 / SM_FREQ as f32 * 1000. * 2.;
+                        let baud = 1000. / millis;
+                        info!("SM in {} ({}): {:08x} -> {} -> {} ms -> {}", i, bit_index, din[i], delay, millis, baud);
+                        bit_index += 1;
+                    }
+                }
+            },
+            _ => {
+            },
+        }
+    }
+}
+
 #[embassy_executor::main]
 async fn main(spawner: Spawner) {
     let p = embassy_rp::init(Default::default());
@@ -284,6 +463,7 @@ async fn main(spawner: Spawner) {
     let _tx_en = p.PIN_15;
     let _tx = p.PIN_16;
     let rx = p.PIN_17;
+    let rx_for_autobaud = unsafe { rx.clone_unchecked() };  //FIXME move UART and auto-baud into same task
     let _matrix_in2 = Input::new(p.PIN_18, Pull::Up);
     let _reed3 = Input::new(p.PIN_19, Pull::Up);
     let _reed4 = Input::new(p.PIN_20, Pull::Up);
@@ -315,14 +495,16 @@ async fn main(spawner: Spawner) {
     );
     unwrap!(spawner.spawn(reader(uart_rx)));
 
-    unwrap!(spawner.spawn(beeper_task(p.PWM_CH3, led_r)));
+    if false {
+        unwrap!(spawner.spawn(beeper_task(p.PWM_CH3, led_r)));
+    }
+
+    unwrap!(spawner.spawn(autobaud_task(p.PIO0, rx_for_autobaud, p.DMA_CH0)));
 
     loop {
-        info!("led on!");
         led_b.set_high();
         Timer::after(Duration::from_secs(1)).await;
 
-        info!("led off!");
         led_b.set_low();
         Timer::after(Duration::from_secs(1)).await;
     }
-- 
GitLab