update: refactor and enhance CAN sensor initialization, reorganize GPIO assignments, improve error detection and logging, and streamline TWAI handling

This commit is contained in:
2026-01-30 22:01:37 +01:00
parent 0c0b62e2ed
commit 355388aa62
3 changed files with 234 additions and 167 deletions

View File

@@ -19,12 +19,12 @@ use embassy_sync::channel::Channel;
use embassy_time::{Delay, Duration, Instant, Timer};
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
use embassy_usb::{Builder, UsbDevice};
use embedded_alloc::LlffHeap as Heap;
use embedded_can::nb::Can as nb_can;
use embedded_can::{Id, StandardId};
use hal::bind_interrupts;
use hal::usbd::Driver;
use {ch32_hal as hal, panic_halt as _};
use embedded_alloc::LlffHeap as Heap;
use embedded_can::nb::Can as nb_can;
macro_rules! mk_static {
($t:ty,$val:expr) => {{
@@ -60,49 +60,74 @@ async fn main(spawner: Spawner) {
// Build driver and USB stack using 'static buffers
let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11);
let mut probe_gnd = Flex::new(p.PB1);
let mut probe_gnd = Flex::new(p.PA2);
probe_gnd.set_as_input(Pull::None);
// Create GPIO for 555 Q output (PB0)
let q_out = Output::new(p.PB0, Level::Low, Speed::Low);
// Built-in LED on PB2 mirrors Q state
let mut info = Output::new(p.PB2, 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 mut 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)
// PB4..PB7: address bits (1,2,4,8)
let mut pb3 = Flex::new(p.PB3);
let mut pb4 = Flex::new(p.PB4);
let mut pb5 = Flex::new(p.PB5);
let mut pb6 = Flex::new(p.PB6);
let mut pb7 = Flex::new(p.PB7);
let mut sensor_ab_pin = Flex::new(p.PA3);
let mut sensor_address_bit_1_pin = Flex::new(p.PA4);
let mut sensor_address_bit_2_pin = Flex::new(p.PA5);
let mut sensor_address_bit_3_pin = Flex::new(p.PA6);
let mut sensor_address_bit_4_pin = Flex::new(p.PA7);
// Validate all config pins; if any is floating, stay in an error loop until fixed
// Try read PB3..PB7
let res_pb3 = detect_stable_pin(&mut pb3).await;
let res_pb4 = detect_stable_pin(&mut pb4).await;
let res_pb5 = detect_stable_pin(&mut pb5).await;
let res_pb6 = detect_stable_pin(&mut pb6).await;
let res_pb7 = detect_stable_pin(&mut pb7).await;
let sensor_ab_config = detect_stable_pin(&mut sensor_ab_pin).await;
let sensor_address_bit_1_config = detect_stable_pin(&mut sensor_address_bit_1_pin).await;
let sensor_address_bit_2_config = detect_stable_pin(&mut sensor_address_bit_2_pin).await;
let sensor_address_bit_3_config = detect_stable_pin(&mut sensor_address_bit_3_pin).await;
let sensor_address_bit_4_config = detect_stable_pin(&mut sensor_address_bit_4_pin).await;
let slot = if res_pb3.unwrap_or(false) { SensorSlot::B } else { SensorSlot::A };
let slot = if sensor_ab_config.unwrap_or(false) {
SensorSlot::B
} else {
SensorSlot::A
};
let mut addr: u8 = 0;
if res_pb4.unwrap_or(false) { addr |= 1; }
if res_pb5.unwrap_or(false) { addr |= 2; }
if res_pb6.unwrap_or(false) { addr |= 4; }
if res_pb7.unwrap_or(false) { addr |= 8; }
if sensor_address_bit_1_config.unwrap_or(false) {
addr |= 1;
}
if sensor_address_bit_2_config.unwrap_or(false) {
addr |= 2;
}
if sensor_address_bit_3_config.unwrap_or(false) {
addr |= 4;
}
if sensor_address_bit_4_config.unwrap_or(false) {
addr |= 8;
}
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 invalid_config = res_pb3.is_none() || res_pb4.is_none() || res_pb5.is_none() || res_pb6.is_none() || res_pb7.is_none();
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();
let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
config.manufacturer = Some("Can Sensor v0.2");
let msg = mk_static!(heapless::String<128>, heapless::String::new());;
let _ = core::fmt::Write::write_fmt(msg, format_args!("Sensor {:?} plant {}", slot, addr));
let msg = mk_static!(heapless::String<128>, heapless::String::new());
if invalid_config {
let _ = core::fmt::Write::write_fmt(
msg,
format_args!(
"CFG err: {:?} {:?} {:?} {:?} {:?}",
to_info(sensor_ab_config), to_info(sensor_address_bit_1_config), to_info(sensor_address_bit_2_config), to_info(sensor_address_bit_3_config), to_info(sensor_address_bit_4_config)
),
);
} else {
let _ = core::fmt::Write::write_fmt(msg, format_args!("Sensor {:?} plant {}", slot, addr));
}
config.product = Some(msg.as_str());
config.serial_number = Some("12345678");
config.max_power = 100;
@@ -131,56 +156,100 @@ async fn main(spawner: Spawner) {
// Build USB device
let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build());
spawner.spawn(usb_task(usb)).unwrap();
spawner.spawn(usb_writer(class)).unwrap();
if invalid_config {
// At least one floating: report and blink code for the first one found.
let mut msg: heapless::String<128> = heapless::String::new();
let code = if res_pb4.is_none() { 1 } else if res_pb5.is_none() { 2 } else if res_pb6.is_none() { 3 } else if res_pb7.is_none() { 4 } else { 5 }; // PB3 -> 5
let which = match code { 1 => "PB4", 2 => "PB5", 3 => "PB6", 4 => "PB7", _ => "PB3 (A/B)" };
let _ = core::fmt::Write::write_fmt(&mut msg, format_args!("Config pin floating detected on {} -> blinking code {}. Fix jumpers.\r\n", which, code));
let code = if sensor_address_bit_1_config.is_none() {
1
} else if sensor_address_bit_2_config.is_none() {
2
} else if sensor_address_bit_3_config.is_none() {
3
} else if sensor_address_bit_4_config.is_none() {
4
} else {
5
}; // PB3 -> 5
let which = match code {
1 => "PB4",
2 => "PB5",
3 => "PB6",
4 => "PB7",
_ => "PB3 (A/B)",
};
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);
blink_error(&mut info, code).await;
};
spawner.spawn(blink_error(warn, code)).unwrap();
} else {
// Log startup configuration and derived CAN IDs
{
let mut msg: heapless::String<128> = heapless::String::new();
let slot_chr = match slot {
SensorSlot::A => 'a',
SensorSlot::B => 'b',
};
let _ = core::fmt::Write::write_fmt(
&mut msg,
format_args!(
"Startup: slot={} addr={} moisture_id=0x{:03X} identity_id=0x{:03X}\r\n",
slot_chr, addr, moisture_id, identify_id
),
);
log(msg);
}
// Log startup configuration and derived CAN IDs
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
let adc = Adc::new(p.ADC1, Default::default());
let ain = p.PA1;
let config = can::can::Config::default();
let can: Can<CAN1, NonBlocking> = Can::new_nb(
p.CAN1,
p.PB8,
p.PB9,
CanFifo::Fifo0,
CanMode::Normal,
125_000,
config,
)
.expect("Valid");
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
{
let mut msg: heapless::String<128> = heapless::String::new();
let slot_chr = match slot { SensorSlot::A => 'a', SensorSlot::B => 'b' };
let _ = core::fmt::Write::write_fmt(&mut msg, format_args!(
"Startup: slot={} addr={} moisture_id=0x{:03X} identity_id=0x{:03X}\r\n",
slot_chr, addr, moisture_id, identify_id
));
log(msg);
// 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(),
))
.unwrap();
}
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
let adc = Adc::new(p.ADC1, Default::default());
let ain = p.PA1;
let config = can::can::Config::default();
let can: Can<CAN1, NonBlocking> = Can::new_nb(
p.CAN1,
p.PB8,
p.PB9,
CanFifo::Fifo0,
CanMode::Normal,
125_000,
config,
)
.expect("Valid");
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
spawner.spawn(usb_task(usb)).unwrap();
spawner.spawn(usb_writer(class)).unwrap();
// move Q output, LED, ADC and analog input into worker task
spawner.spawn(worker(probe_gnd, q_out, info, adc, ain, can, StandardId::new(moisture_id).unwrap(), StandardId::new(identify_id).unwrap())).unwrap();
// Prevent main from exiting
core::future::pending::<()>().await;
}
fn to_info(res: Option<bool>) -> i8 {
match res {
Some(true) => 1,
Some(false) => -1,
None => 0,
}
}
// Helper closure: detect stable pin by comparing readings under Pull::Down and Pull::Up
async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
@@ -190,9 +259,14 @@ async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
pin.set_as_input(Pull::Up);
Timer::after_millis(2).await;
let high_read = pin.is_high();
if low_read == high_read { Some(high_read) } else { None }
if low_read == high_read {
Some(high_read)
} else {
None
}
}
async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
#[task]
async fn blink_error(info_led: &'static mut Output<'static>, code: u8) -> ! {
loop {
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B)
for _ in 0..code {
@@ -206,17 +280,17 @@ async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
}
}
#[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
identify_id: StandardId,
) {
// 555 emulation state: Q initially Low
let mut q_high = false;
@@ -284,8 +358,10 @@ async fn worker(
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", probe_duration.as_millis(),
pulses, freq_hz
"555 window={}ms pulses={} freq={} Hz (A1->Q on PB0)\r\n",
probe_duration.as_millis(),
pulses,
freq_hz
);
log(msg);
@@ -298,9 +374,9 @@ async fn worker(
}
Err(err) => {
for _ in 0..3 {
info.set_high();
warn.set_high();
Timer::after_millis(100).await;
info.set_low();
warn.set_low();
Timer::after_millis(100).await;
}
let mut msg: heapless::String<128> = heapless::String::new();
@@ -318,7 +394,6 @@ async fn worker(
);
log(msg);
yield_now().await;
match can.receive() {
Ok(frame) => match frame.id() {