diff --git a/Software/CAN_Sensor/src/main.rs b/Software/CAN_Sensor/src/main.rs index 794abac..3bf6734 100644 --- a/Software/CAN_Sensor/src/main.rs +++ b/Software/CAN_Sensor/src/main.rs @@ -12,6 +12,7 @@ use ch32_hal::gpio::{Flex, Level, Output, Pull, Speed}; use ch32_hal::mode::NonBlocking; use ch32_hal::peripherals::USBD; use core::fmt::Write as _; +use core::sync::atomic::{AtomicBool, Ordering}; use embassy_executor::{task, Spawner}; use embassy_futures::yield_now; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; @@ -42,9 +43,10 @@ bind_interrupts!(struct Irqs { #[global_allocator] static HEAP: Heap = Heap::empty(); static LOG_CH: Channel, 8> = Channel::new(); -static CAN_RX_CH: Channel = Channel::new(); static CAN_TX_CH: Channel = Channel::new(); +static BEACON: AtomicBool = AtomicBool::new(false); + #[embassy_executor::main(entry = "qingke_rt::entry")] async fn main(spawner: Spawner) { ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2)); @@ -67,7 +69,7 @@ async fn main(spawner: Spawner) { // Create GPIO for 555 Q output (PB0) let q_out = Output::new(p.PA0, Level::Low, Speed::Low); - let info = Output::new(p.PA10, Level::Low, Speed::Low); + let info = mk_static!(Output, Output::new(p.PA10, Level::Low, Speed::Low)); let warn = mk_static!(Output, Output::new(p.PA9, Level::Low, Speed::Low)); // Read configuration switches on PB3..PB7 at startup with floating detection @@ -207,7 +209,7 @@ async fn main(spawner: Spawner) { ); } log(msg); - spawner.spawn(blink_error(warn, code)).unwrap(); + spawner.spawn(blink_error_task(warn, info, 2, code)).unwrap(); } else { // Log startup configuration and derived CAN IDs @@ -247,17 +249,19 @@ async fn main(spawner: Spawner) { // let mut filter = CanFilter::new_id_list(); // filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default()); // can.add_filter(filter); - spawner.spawn(can_task(can, warn, standard_identify_id)).unwrap(); + let standard_moisture_id = StandardId::new(moisture_id).unwrap(); + spawner + .spawn(can_task(can,info, warn, standard_identify_id, standard_moisture_id)) + .unwrap(); // move Q output, LED, ADC and analog input into worker task spawner .spawn(worker( probe_gnd, q_out, - info, adc, ain, - StandardId::new(moisture_id).unwrap(), + standard_moisture_id, standard_identify_id, )) .unwrap(); @@ -288,27 +292,44 @@ async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option { None } } -#[task] -async fn blink_error(info_led: &'static mut Output<'static>, code: u8) -> ! { +async fn blink_error_loop(info_led: &mut Output<'static>, warn_led: &mut Output<'static>, c_i: u8, c_w: u8) -> ! { loop { - // code: 1-4 for PB4..PB7, 5 for PB3 (A/B) - for _ in 0..code { + // code: 1-4 for PB4..PB7, 5 for PB3 (A/B), 7 for CAN address collision + for _ in 0..c_i { info_led.set_high(); Timer::after_millis(200).await; info_led.set_low(); Timer::after_millis(200).await; } + for _ in 0..c_w { + warn_led.set_high(); + Timer::after_millis(200).await; + warn_led.set_low(); + Timer::after_millis(200).await; + } // Pause between sequences Timer::after_secs(2).await; } } +#[task] +async fn blink_error_task(info_led: &'static mut Output<'static>, warn_led: &'static mut Output<'static>, c_i: u8, c_w: u8) -> ! { + blink_error_loop(info_led, warn_led, c_i, c_w).await +} + #[task] async fn can_task( mut can: Can<'static, CAN1, NonBlocking>, + info: &'static mut Output<'static>, warn: &'static mut Output<'static>, identify_id: StandardId, + moisture_id: StandardId, ) { + // Non-blocking beacon blink timing. + // We keep this inside the CAN task so it can't stall other tasks (like `worker`) with `await`s. + let mut next_beacon_toggle = Instant::now(); + let beacon_period = Duration::from_millis(50); + loop { match can.receive() { Ok(frame) => { @@ -322,8 +343,15 @@ async fn can_task( identify_id.as_raw() ); log(msg); + if s_frame.as_raw() == moisture_id.as_raw() { + //trigger collision detection on other side as well + let _ = can.transmit(&frame); + // We should never receive moisture packets addressed to ourselves. + // If we do, another node likely uses the same jumper configuration. + blink_error_loop(info, warn, 1,2).await; + } if s_frame.as_raw() == identify_id.as_raw() { - CAN_RX_CH.send(frame).await; + BEACON.store(true, Ordering::Relaxed); } } Id::Extended(_) => {} @@ -345,6 +373,15 @@ async fn can_task( } } + if BEACON.load(Ordering::Relaxed) { + let now = Instant::now(); + if now >= next_beacon_toggle { + info.toggle(); + // Move the schedule forward; if we fell behind, resync to "now". + next_beacon_toggle = now + beacon_period; + } + } + while let Ok(mut frame) = CAN_TX_CH.try_receive() { match can.transmit(&mut frame) { Ok(..) => { @@ -381,7 +418,6 @@ async fn can_task( async fn worker( mut probe_gnd: Flex<'static>, mut q: Output<'static>, - mut info: Output<'static>, mut adc: Adc<'static, hal::peripherals::ADC1>, mut ain: hal::peripherals::PA1, moisture_id: StandardId, @@ -452,14 +488,6 @@ async fn worker( let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap(); CAN_TX_CH.send(moisture).await; - - while let Ok(_frame) = CAN_RX_CH.try_receive() { - for _ in 0..10 { - Timer::after_millis(250).await; - info.toggle(); - } - info.set_low(); - } } }