From 979f9825658a45365414528f232bf160032e029b Mon Sep 17 00:00:00 2001 From: Empire Date: Fri, 13 Feb 2026 21:53:48 +0100 Subject: [PATCH] add: implement CAN communication channels, create `can_task` for RX/TX handling, enhance error detection, and reorganize configuration validation logic --- Software/CAN_Sensor/src/main.rs | 221 +++++++++++++++++++------------- 1 file changed, 130 insertions(+), 91 deletions(-) diff --git a/Software/CAN_Sensor/src/main.rs b/Software/CAN_Sensor/src/main.rs index 23438b6..794abac 100644 --- a/Software/CAN_Sensor/src/main.rs +++ b/Software/CAN_Sensor/src/main.rs @@ -3,7 +3,7 @@ extern crate alloc; use crate::hal::peripherals::CAN1; -use canapi::id::{plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET}; +use canapi::id::{plant_id, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET}; use canapi::SensorSlot; use ch32_hal::adc::{Adc, SampleTime, ADC_MAX}; use ch32_hal::can; @@ -16,7 +16,7 @@ use embassy_executor::{task, Spawner}; use embassy_futures::yield_now; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; -use embassy_time::{Delay, Duration, Instant, Timer}; +use embassy_time::{Duration, Instant, Timer}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::{Builder, UsbDevice}; use embedded_alloc::LlffHeap as Heap; @@ -42,6 +42,8 @@ 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(); #[embassy_executor::main(entry = "qingke_rt::entry")] async fn main(spawner: Spawner) { @@ -66,7 +68,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 mut warn = mk_static!(Output, Output::new(p.PA9, 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 // PB3: Sensor A/B selector (Low=A, High=B) @@ -105,12 +107,16 @@ async fn main(spawner: Spawner) { } let moisture_id = plant_id(MOISTURE_DATA_OFFSET, slot, addr as u16); let identify_id = plant_id(IDENTIFY_CMD_OFFSET, slot, addr as u16); + let standard_identify_id = StandardId::new(identify_id).unwrap(); + //is any floating, or invalid addr (only 1-8 are valid) let invalid_config = sensor_ab_config.is_none() || sensor_address_bit_1_config.is_none() || sensor_address_bit_2_config.is_none() || sensor_address_bit_3_config.is_none() - || sensor_address_bit_4_config.is_none(); + || sensor_address_bit_4_config.is_none() + || addr == 0 + || addr > 8; let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE); config.manufacturer = Some("Can Sensor v0.2"); @@ -170,23 +176,36 @@ async fn main(spawner: Spawner) { 3 } else if sensor_address_bit_4_config.is_none() { 4 - } else { + } else if sensor_ab_config.is_none() { 5 - }; // PB3 -> 5 - let which = match code { - 1 => "PB4", - 2 => "PB5", - 3 => "PB6", - 4 => "PB7", - _ => "PB3 (A/B)", + } else { + 6 // Invalid address (0 or > 8) }; - let _ = core::fmt::Write::write_fmt( - &mut msg, - format_args!( - "Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n", - which, code - ), - ); + let which = match code { + 1 => "PB4 (bit 1)", + 2 => "PB5 (bit 2)", + 3 => "PB6 (bit 3)", + 4 => "PB7 (bit 4)", + 5 => "PB3 (A/B)", + _ => "Address (0 or > 8)", + }; + if code == 6 { + let _ = core::fmt::Write::write_fmt( + &mut msg, + format_args!( + "Invalid address {} (only 1-8 allowed) -> blinking code {}. Fix jumpers.\r\n", + addr, code + ), + ); + } else { + let _ = core::fmt::Write::write_fmt( + &mut msg, + format_args!( + "Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n", + which, code + ), + ); + } log(msg); spawner.spawn(blink_error(warn, code)).unwrap(); } else { @@ -224,18 +243,22 @@ async fn main(spawner: Spawner) { .expect("Valid"); ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2)); + can.add_filter(CanFilter::accept_all()); + // 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(); + // move Q output, LED, ADC and analog input into worker task spawner .spawn(worker( probe_gnd, q_out, info, - warn, adc, ain, - can, StandardId::new(moisture_id).unwrap(), - StandardId::new(identify_id).unwrap(), + standard_identify_id, )) .unwrap(); } @@ -280,15 +303,87 @@ async fn blink_error(info_led: &'static mut Output<'static>, code: u8) -> ! { } } +#[task] +async fn can_task( + mut can: Can<'static, CAN1, NonBlocking>, + warn: &'static mut Output<'static>, + identify_id: StandardId, +) { + loop { + match can.receive() { + Ok(frame) => { + match frame.id() { + Id::Standard(s_frame) => { + let mut msg: heapless::String<128> = heapless::String::new(); + let _ = write!( + &mut msg, + "Received from canbus: {:?} ident is {:?} \r\n", + s_frame.as_raw(), + identify_id.as_raw() + ); + log(msg); + if s_frame.as_raw() == identify_id.as_raw() { + CAN_RX_CH.send(frame).await; + } + } + Id::Extended(_) => {} + } + } + Err(nb::Error::WouldBlock) => { + // No frame available + } + Err(nb::Error::Other(err)) => { + for _ in 0..3 { + warn.set_high(); + Timer::after_millis(100).await; + warn.set_low(); + Timer::after_millis(100).await; + } + let mut msg: heapless::String<128> = heapless::String::new(); + let _ = write!(&mut msg, "rx err {:?}", err); + log(msg); + } + } + + while let Ok(mut frame) = CAN_TX_CH.try_receive() { + match can.transmit(&mut frame) { + Ok(..) => { + } + Err(nb::Error::WouldBlock) => { + for _ in 0..2 { + warn.set_high(); + Timer::after_millis(100).await; + warn.set_low(); + Timer::after_millis(100).await; + } + let mut msg: heapless::String<128> = heapless::String::new(); + let _ = write!(&mut msg, "canbus out buffer full"); + log(msg); + } + Err(nb::Error::Other(err)) => { + for _ in 0..3 { + warn.set_high(); + Timer::after_millis(100).await; + warn.set_low(); + Timer::after_millis(100).await; + } + let mut msg: heapless::String<128> = heapless::String::new(); + let _ = write!(&mut msg, "tx err {:?}", err); + log(msg); + } + } + } + yield_now().await; + } +} + #[task] async fn worker( mut probe_gnd: Flex<'static>, mut q: Output<'static>, mut info: Output<'static>, - mut warn: &'static mut Output<'static>, mut adc: Adc<'static, hal::peripherals::ADC1>, mut ain: hal::peripherals::PA1, - mut can: Can<'static, CAN1, NonBlocking>, moisture_id: StandardId, identify_id: StandardId, ) { @@ -297,16 +392,6 @@ async fn worker( let low_th: u16 = (ADC_MAX as u16) / 3; // ~1/3 Vref let high_th: u16 = ((ADC_MAX as u32 * 2) / 3) as u16; // ~2/3 Vref - let mut filter = CanFilter::new_id_list(); - - filter - .get(0) - .unwrap() - .set(identify_id.into(), Default::default()); - - //can.add_filter(filter); - can.add_filter(CanFilter::accept_all()); - loop { // Count rising edges of Q in a 100 ms window let start = Instant::now(); @@ -352,74 +437,28 @@ async fn worker( } probe_gnd.set_as_input(Pull::None); - let freq_hz: u32 = pulses * (1000/probe_duration.as_millis()).into(); // pulses per 0.1s => Hz + let freq_hz: u32 = pulses * (1000 / probe_duration.as_millis()) as u32; // pulses per 0.1s => Hz let mut msg: heapless::String<128> = heapless::String::new(); let _ = write!( &mut msg, - "555 window={}ms pulses={} freq={} Hz (A1->Q on PB0)\r\n", + "555 window={}ms pulses={} freq={} Hz (A1->Q on PB0) id={:?}\r\n", probe_duration.as_millis(), pulses, - freq_hz + freq_hz, + identify_id.as_raw() ); log(msg); - let mut moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap(); - match can.transmit(&mut moisture) { - Ok(..) => { - let mut msg: heapless::String<128> = heapless::String::new(); - let _ = write!(&mut msg, "Send to canbus"); - log(msg); - } - Err(err) => { - for _ in 0..3 { - warn.set_high(); - Timer::after_millis(100).await; - warn.set_low(); - Timer::after_millis(100).await; - } - let mut msg: heapless::String<128> = heapless::String::new(); - let _ = write!(&mut msg, "err {:?}", err); - log(msg); - } - } + let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap(); + CAN_TX_CH.send(moisture).await; - loop { - let mut msg: heapless::String<128> = heapless::String::new(); - let _ = write!( - &mut msg, - "Check identity addr received: {:#x} \r\n", - identify_id.as_raw() - ); - log(msg); - - yield_now().await; - match can.receive() { - Ok(frame) => match frame.id() { - Id::Standard(s_frame) => { - let mut msg: heapless::String<128> = heapless::String::new(); - let _ = write!( - &mut msg, - "Received from canbus: {:?} ident is {:?} \r\n", - s_frame.as_raw(), - identify_id.as_raw() - ); - log(msg); - if s_frame.as_raw() == identify_id.as_raw() { - for _ in 0..10 { - Timer::after_millis(250).await; - info.toggle(); - } - info.set_low(); - } - } - Id::Extended(_) => {} - }, - - Err(err) => { - break; - } + while let Ok(_frame) = CAN_RX_CH.try_receive() { + for _ in 0..10 { + Timer::after_millis(250).await; + info.toggle(); } + info.set_low(); } } }