add: implement CAN communication channels, create can_task for RX/TX handling, enhance error detection, and reorganize configuration validation logic

This commit is contained in:
2026-02-13 21:53:48 +01:00
parent e6f8e34f7d
commit 979f982565

View File

@@ -3,7 +3,7 @@
extern crate alloc; extern crate alloc;
use crate::hal::peripherals::CAN1; 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 canapi::SensorSlot;
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX}; use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
use ch32_hal::can; use ch32_hal::can;
@@ -16,7 +16,7 @@ use embassy_executor::{task, Spawner};
use embassy_futures::yield_now; use embassy_futures::yield_now;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::channel::Channel; 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::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::{Builder, UsbDevice}; use embassy_usb::{Builder, UsbDevice};
use embedded_alloc::LlffHeap as Heap; use embedded_alloc::LlffHeap as Heap;
@@ -42,6 +42,8 @@ bind_interrupts!(struct Irqs {
#[global_allocator] #[global_allocator]
static HEAP: Heap = Heap::empty(); static HEAP: Heap = Heap::empty();
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new(); static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
static CAN_RX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new();
static CAN_TX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new();
#[embassy_executor::main(entry = "qingke_rt::entry")] #[embassy_executor::main(entry = "qingke_rt::entry")]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
@@ -66,7 +68,7 @@ async fn main(spawner: Spawner) {
// Create GPIO for 555 Q output (PB0) // Create GPIO for 555 Q output (PB0)
let q_out = Output::new(p.PA0, Level::Low, Speed::Low); let q_out = Output::new(p.PA0, Level::Low, Speed::Low);
let info = Output::new(p.PA10, 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 // Read configuration switches on PB3..PB7 at startup with floating detection
// PB3: Sensor A/B selector (Low=A, High=B) // 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 moisture_id = plant_id(MOISTURE_DATA_OFFSET, slot, addr as u16);
let identify_id = plant_id(IDENTIFY_CMD_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() let invalid_config = sensor_ab_config.is_none()
|| sensor_address_bit_1_config.is_none() || sensor_address_bit_1_config.is_none()
|| sensor_address_bit_2_config.is_none() || sensor_address_bit_2_config.is_none()
|| sensor_address_bit_3_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); let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
config.manufacturer = Some("Can Sensor v0.2"); config.manufacturer = Some("Can Sensor v0.2");
@@ -170,23 +176,36 @@ async fn main(spawner: Spawner) {
3 3
} else if sensor_address_bit_4_config.is_none() { } else if sensor_address_bit_4_config.is_none() {
4 4
} else { } else if sensor_ab_config.is_none() {
5 5
}; // PB3 -> 5 } else {
let which = match code { 6 // Invalid address (0 or > 8)
1 => "PB4",
2 => "PB5",
3 => "PB6",
4 => "PB7",
_ => "PB3 (A/B)",
}; };
let _ = core::fmt::Write::write_fmt( let which = match code {
&mut msg, 1 => "PB4 (bit 1)",
format_args!( 2 => "PB5 (bit 2)",
"Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n", 3 => "PB6 (bit 3)",
which, code 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); log(msg);
spawner.spawn(blink_error(warn, code)).unwrap(); spawner.spawn(blink_error(warn, code)).unwrap();
} else { } else {
@@ -224,18 +243,22 @@ async fn main(spawner: Spawner) {
.expect("Valid"); .expect("Valid");
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2)); 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 // move Q output, LED, ADC and analog input into worker task
spawner spawner
.spawn(worker( .spawn(worker(
probe_gnd, probe_gnd,
q_out, q_out,
info, info,
warn,
adc, adc,
ain, ain,
can,
StandardId::new(moisture_id).unwrap(), StandardId::new(moisture_id).unwrap(),
StandardId::new(identify_id).unwrap(), standard_identify_id,
)) ))
.unwrap(); .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] #[task]
async fn worker( async fn worker(
mut probe_gnd: Flex<'static>, mut probe_gnd: Flex<'static>,
mut q: Output<'static>, mut q: Output<'static>,
mut info: Output<'static>, mut info: Output<'static>,
mut warn: &'static mut Output<'static>,
mut adc: Adc<'static, hal::peripherals::ADC1>, mut adc: Adc<'static, hal::peripherals::ADC1>,
mut ain: hal::peripherals::PA1, mut ain: hal::peripherals::PA1,
mut can: Can<'static, CAN1, NonBlocking>,
moisture_id: StandardId, moisture_id: StandardId,
identify_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 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 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 { loop {
// Count rising edges of Q in a 100 ms window // Count rising edges of Q in a 100 ms window
let start = Instant::now(); let start = Instant::now();
@@ -352,74 +437,28 @@ async fn worker(
} }
probe_gnd.set_as_input(Pull::None); 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 mut msg: heapless::String<128> = heapless::String::new();
let _ = write!( let _ = write!(
&mut msg, &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(), probe_duration.as_millis(),
pulses, pulses,
freq_hz freq_hz,
identify_id.as_raw()
); );
log(msg); log(msg);
let mut moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap(); let moisture = CanFrame::new(moisture_id, &(freq_hz as u32).to_be_bytes()).unwrap();
match can.transmit(&mut moisture) { CAN_TX_CH.send(moisture).await;
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);
}
}
loop { while let Ok(_frame) = CAN_RX_CH.try_receive() {
let mut msg: heapless::String<128> = heapless::String::new(); for _ in 0..10 {
let _ = write!( Timer::after_millis(250).await;
&mut msg, info.toggle();
"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;
}
} }
info.set_low();
} }
} }
} }