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:
@@ -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<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")]
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user