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