add: extend CAN task with collision detection and beacon handling, refactor error blinking logic, and optimize GPIO initialization for modularity
This commit is contained in:
@@ -12,6 +12,7 @@ use ch32_hal::gpio::{Flex, Level, Output, Pull, Speed};
|
|||||||
use ch32_hal::mode::NonBlocking;
|
use ch32_hal::mode::NonBlocking;
|
||||||
use ch32_hal::peripherals::USBD;
|
use ch32_hal::peripherals::USBD;
|
||||||
use core::fmt::Write as _;
|
use core::fmt::Write as _;
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use embassy_executor::{task, Spawner};
|
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;
|
||||||
@@ -42,9 +43,10 @@ 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();
|
static CAN_TX_CH: Channel<CriticalSectionRawMutex, CanFrame, 4> = Channel::new();
|
||||||
|
|
||||||
|
static BEACON: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
||||||
async fn main(spawner: Spawner) {
|
async fn main(spawner: Spawner) {
|
||||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
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)
|
// 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 = mk_static!(Output, Output::new(p.PA10, Level::Low, Speed::Low));
|
||||||
let 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
|
||||||
@@ -207,7 +209,7 @@ async fn main(spawner: Spawner) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
log(msg);
|
log(msg);
|
||||||
spawner.spawn(blink_error(warn, code)).unwrap();
|
spawner.spawn(blink_error_task(warn, info, 2, code)).unwrap();
|
||||||
} else {
|
} else {
|
||||||
// Log startup configuration and derived CAN IDs
|
// Log startup configuration and derived CAN IDs
|
||||||
|
|
||||||
@@ -247,17 +249,19 @@ async fn main(spawner: Spawner) {
|
|||||||
// let mut filter = CanFilter::new_id_list();
|
// let mut filter = CanFilter::new_id_list();
|
||||||
// filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default());
|
// filter.get(0).unwrap().set(Id::Standard(standard_identify_id), Default::default());
|
||||||
// can.add_filter(filter);
|
// 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
|
// 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,
|
|
||||||
adc,
|
adc,
|
||||||
ain,
|
ain,
|
||||||
StandardId::new(moisture_id).unwrap(),
|
standard_moisture_id,
|
||||||
standard_identify_id,
|
standard_identify_id,
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -288,27 +292,44 @@ async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[task]
|
async fn blink_error_loop(info_led: &mut Output<'static>, warn_led: &mut Output<'static>, c_i: u8, c_w: u8) -> ! {
|
||||||
async fn blink_error(info_led: &'static mut Output<'static>, code: u8) -> ! {
|
|
||||||
loop {
|
loop {
|
||||||
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B)
|
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B), 7 for CAN address collision
|
||||||
for _ in 0..code {
|
for _ in 0..c_i {
|
||||||
info_led.set_high();
|
info_led.set_high();
|
||||||
Timer::after_millis(200).await;
|
Timer::after_millis(200).await;
|
||||||
info_led.set_low();
|
info_led.set_low();
|
||||||
Timer::after_millis(200).await;
|
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
|
// Pause between sequences
|
||||||
Timer::after_secs(2).await;
|
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]
|
#[task]
|
||||||
async fn can_task(
|
async fn can_task(
|
||||||
mut can: Can<'static, CAN1, NonBlocking>,
|
mut can: Can<'static, CAN1, NonBlocking>,
|
||||||
|
info: &'static mut Output<'static>,
|
||||||
warn: &'static mut Output<'static>,
|
warn: &'static mut Output<'static>,
|
||||||
identify_id: StandardId,
|
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 {
|
loop {
|
||||||
match can.receive() {
|
match can.receive() {
|
||||||
Ok(frame) => {
|
Ok(frame) => {
|
||||||
@@ -322,8 +343,15 @@ async fn can_task(
|
|||||||
identify_id.as_raw()
|
identify_id.as_raw()
|
||||||
);
|
);
|
||||||
log(msg);
|
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() {
|
if s_frame.as_raw() == identify_id.as_raw() {
|
||||||
CAN_RX_CH.send(frame).await;
|
BEACON.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Id::Extended(_) => {}
|
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() {
|
while let Ok(mut frame) = CAN_TX_CH.try_receive() {
|
||||||
match can.transmit(&mut frame) {
|
match can.transmit(&mut frame) {
|
||||||
Ok(..) => {
|
Ok(..) => {
|
||||||
@@ -381,7 +418,6 @@ async fn can_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 adc: Adc<'static, hal::peripherals::ADC1>,
|
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
||||||
mut ain: hal::peripherals::PA1,
|
mut ain: hal::peripherals::PA1,
|
||||||
moisture_id: StandardId,
|
moisture_id: StandardId,
|
||||||
@@ -452,14 +488,6 @@ async fn worker(
|
|||||||
|
|
||||||
let 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();
|
||||||
CAN_TX_CH.send(moisture).await;
|
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user