Skip to content
Snippets Groups Projects
Commit e9e2a7d8 authored by Benjamin Koch's avatar Benjamin Koch
Browse files

add measurement for auto-baud

parent 77f3e320
No related branches found
No related tags found
No related merge requests found
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
use defmt::*; use defmt::*;
use embassy_executor::Spawner; use embassy_executor::Spawner;
//use embassy_futures::join::join;
use embassy_futures::select::select; use embassy_futures::select::select;
use embassy_futures::select::Either::*; use embassy_futures::select::Either::*;
use embassy_rp::gpio; use embassy_rp::gpio;
...@@ -11,13 +12,18 @@ use embassy_time::{Duration, Timer}; ...@@ -11,13 +12,18 @@ use embassy_time::{Duration, Timer};
use embassy_rp::gpio::{Input, Level, Output, Pull, Flex}; use embassy_rp::gpio::{Input, Level, Output, Pull, Flex};
use embassy_rp::i2c; use embassy_rp::i2c;
use embassy_rp::interrupt; use embassy_rp::interrupt;
use embedded_hal_async::i2c::I2c; //use embedded_hal_async::i2c::I2c;
use embassy_embedded_hal::SetConfig; 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::uart::{self, UartRx, Parity};
use embassy_rp::pwm::{self, Pwm}; 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 {defmt_rtt as _, panic_probe as _};
use heapless::String; use heapless::String;
use fixed::traits::ToFixed;
use fixed_macro::types::U56F8;
#[embassy_executor::task] #[embassy_executor::task]
async fn reader(mut rx: UartRx<'static, UART0, uart::Async>) { 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<(), ()> ...@@ -45,7 +51,7 @@ fn append_hex2<const N : usize>(s : &mut String<N>, num : u8) -> Result<(), ()>
append_hex1(s, num & 0xf) append_hex1(s, num & 0xf)
} }
#[derive(Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum ScanResult { enum ScanResult {
NAck, NAck,
...@@ -204,12 +210,13 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0, ...@@ -204,12 +210,13 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0,
use i2c::Error::*; use i2c::Error::*;
use i2c::AbortReason::*; use i2c::AbortReason::*;
const MAX_ADDRESS : u8 = 128;
let mut prev_scan_result = [ScanResult::Reserved; MAX_ADDRESS as usize];
loop { loop {
if true { 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]; let mut scan_result = [ScanResult::Error; MAX_ADDRESS as usize];
for i in 0..MAX_ADDRESS { 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 { 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, ...@@ -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; if prev_scan_result.iter().zip(scan_result.iter()).any(|(x,y)| x != y) {
let mut msg = String::<MSG_LEN>::new(); prev_scan_result = scan_result;
match format_scan_result(&mut msg, scan_result) { const MSG_LEN : usize = (MAX_ADDRESS as usize)*3 + (MAX_ADDRESS as usize)/16*6;
Ok(()) => info!("I2C scan result:\n{}", msg), let mut msg = String::<MSG_LEN>::new();
Err(()) => info!("I2C scan result: too long for our buffer!"), 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; Timer::after(Duration::from_secs(1)).await;
...@@ -235,7 +245,7 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0, ...@@ -235,7 +245,7 @@ async fn i2c_task(mut i2c_peri: embassy_rp::peripherals::I2C0,
} }
#[embassy_executor::task] #[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(); let mut c: pwm::Config = Default::default();
c.top = 0x8000; c.top = 0x8000;
c.compare_a = 8; c.compare_a = 8;
...@@ -258,6 +268,175 @@ async fn beeper_task(mut pwm_channel: embassy_rp::peripherals::PWM_CH3, mut beep ...@@ -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] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default()); let p = embassy_rp::init(Default::default());
...@@ -284,6 +463,7 @@ async fn main(spawner: Spawner) { ...@@ -284,6 +463,7 @@ async fn main(spawner: Spawner) {
let _tx_en = p.PIN_15; let _tx_en = p.PIN_15;
let _tx = p.PIN_16; let _tx = p.PIN_16;
let rx = p.PIN_17; 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 _matrix_in2 = Input::new(p.PIN_18, Pull::Up);
let _reed3 = Input::new(p.PIN_19, Pull::Up); let _reed3 = Input::new(p.PIN_19, Pull::Up);
let _reed4 = Input::new(p.PIN_20, Pull::Up); let _reed4 = Input::new(p.PIN_20, Pull::Up);
...@@ -315,14 +495,16 @@ async fn main(spawner: Spawner) { ...@@ -315,14 +495,16 @@ async fn main(spawner: Spawner) {
); );
unwrap!(spawner.spawn(reader(uart_rx))); 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 { loop {
info!("led on!");
led_b.set_high(); led_b.set_high();
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
info!("led off!");
led_b.set_low(); led_b.set_low();
Timer::after(Duration::from_secs(1)).await; Timer::after(Duration::from_secs(1)).await;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment