cleanups
This commit is contained in:
257
Software/CAN_Sensor/src/main.rs
Normal file
257
Software/CAN_Sensor/src/main.rs
Normal file
@@ -0,0 +1,257 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
extern crate alloc;
|
||||
|
||||
use crate::hal::peripherals::CAN1;
|
||||
use core::fmt::Write as _;
|
||||
use ch32_hal::gpio::{Level, Output, Speed};
|
||||
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
|
||||
use ch32_hal::can;
|
||||
use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode};
|
||||
use ch32_hal::mode::{Blocking};
|
||||
use ch32_hal::peripherals::USBD;
|
||||
// use ch32_hal::delay::Delay;
|
||||
use embassy_executor::{Spawner, task};
|
||||
use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
|
||||
use embassy_usb::{Builder, UsbDevice};
|
||||
use embassy_futures::yield_now;
|
||||
use hal::usbd::{Driver};
|
||||
use hal::{bind_interrupts};
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::channel::{Channel};
|
||||
use embassy_time::{Instant, Duration};
|
||||
use embedded_can::blocking::Can as bcan;
|
||||
use embedded_can::StandardId;
|
||||
use {ch32_hal as hal, panic_halt as _};
|
||||
|
||||
macro_rules! mk_static {
|
||||
($t:ty,$val:expr) => {{
|
||||
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
|
||||
#[deny(unused_attributes)]
|
||||
let x = STATIC_CELL.uninit().write(($val));
|
||||
x
|
||||
}};
|
||||
}
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>;
|
||||
});
|
||||
|
||||
|
||||
use embedded_alloc::LlffHeap as Heap;
|
||||
|
||||
#[global_allocator]
|
||||
static HEAP: Heap = Heap::empty();
|
||||
|
||||
|
||||
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
|
||||
|
||||
#[embassy_executor::main(entry = "qingke_rt::entry")]
|
||||
async fn main(spawner: Spawner) {
|
||||
unsafe {
|
||||
static mut HEAP_SPACE: [u8; 4096] = [0; 4096]; // 4 KiB heap, adjust as needed
|
||||
HEAP.init(HEAP_SPACE.as_ptr() as usize, HEAP_SPACE.len());
|
||||
}
|
||||
|
||||
let p = hal::init(hal::Config {
|
||||
rcc: hal::rcc::Config::SYSCLK_FREQ_144MHZ_HSI,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Build driver and USB stack using 'static buffers
|
||||
let driver = Driver::new(p.USBD, Irqs, p.PA12, p.PA11);
|
||||
|
||||
let mut config = embassy_usb::Config::new(0xC0DE, 0xCAFE);
|
||||
config.manufacturer = Some("Embassy");
|
||||
config.product = Some("USB-serial example");
|
||||
config.serial_number = Some("12345678");
|
||||
config.max_power = 100;
|
||||
config.max_packet_size_0 = 64;
|
||||
|
||||
// Windows compatibility requires these; CDC-ACM
|
||||
config.device_class = 0x02;
|
||||
config.device_sub_class = 0x02;
|
||||
config.device_protocol = 0x00;
|
||||
config.composite_with_iads = false;
|
||||
|
||||
let mut builder = Builder::new(
|
||||
driver,
|
||||
config,
|
||||
mk_static!([u8;256], [0; 256]),
|
||||
mk_static!([u8;256], [0; 256]),
|
||||
&mut [], // no msos descriptors
|
||||
mk_static!([u8;64], [0; 64]),
|
||||
);
|
||||
// Initialize CDC state and create CDC-ACM class
|
||||
let class = mk_static!(CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>,
|
||||
CdcAcmClass::new(
|
||||
&mut builder,
|
||||
mk_static!(State, State::new()),
|
||||
64
|
||||
)
|
||||
);
|
||||
|
||||
// Build USB device
|
||||
let usb = mk_static!(UsbDevice<Driver<USBD>>, builder.build()) ;
|
||||
|
||||
// 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 led = Output::new(p.PB2, Level::Low, Speed::Low);
|
||||
|
||||
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
|
||||
let adc = Adc::new(p.ADC1, Default::default());
|
||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
||||
|
||||
let config = can::can::Config::default();
|
||||
let can: Can<'static, CAN1 , Blocking> = Can::new_blocking(p.CAN1, p.PB8, p.PB9, CanFifo::Fifo1, CanMode::Normal, 500_000, config).expect("Valid");
|
||||
let mut filter = CanFilter::new_id_list();
|
||||
|
||||
filter
|
||||
.get(0)
|
||||
.unwrap()
|
||||
.set(StandardId::new(0x580 | 0x42).unwrap().into(), Default::default());
|
||||
|
||||
can.add_filter(CanFilter::accept_all());
|
||||
|
||||
|
||||
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(q_out, led, adc, ain, can)).unwrap();
|
||||
|
||||
|
||||
|
||||
// Prevent main from exiting
|
||||
core::future::pending::<()>().await;
|
||||
}
|
||||
|
||||
#[task]
|
||||
async fn worker(
|
||||
mut q: Output<'static>,
|
||||
mut led: Output<'static>,
|
||||
mut adc: Adc<'static, hal::peripherals::ADC1>,
|
||||
mut ain: hal::peripherals::PA1,
|
||||
mut can: Can<'static, CAN1, Blocking>,
|
||||
) {
|
||||
// 555 emulation state: Q initially Low
|
||||
let mut q_high = false;
|
||||
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
|
||||
|
||||
loop {
|
||||
// Count rising edges of Q in a 100 ms window
|
||||
let start = Instant::now();
|
||||
let mut pulses: u32 = 0;
|
||||
let mut last_q = q_high;
|
||||
|
||||
while Instant::now().checked_duration_since(start).unwrap_or(Duration::from_millis(0))
|
||||
< Duration::from_millis(1000)
|
||||
{
|
||||
// Sample the analog input (Threshold/Trigger on A1)
|
||||
let val: u16 = adc.convert(&mut ain, SampleTime::CYCLES28_5);
|
||||
|
||||
// 555 core behavior:
|
||||
// - If input <= 1/3 Vref => set Q high (trigger)
|
||||
// - If input >= 2/3 Vref => set Q low (threshold)
|
||||
// - Otherwise keep previous Q state (hysteresis)
|
||||
if val <= low_th {
|
||||
q_high = true;
|
||||
} else if val >= high_th {
|
||||
q_high = false;
|
||||
}
|
||||
|
||||
// Drive output pin accordingly
|
||||
if q_high {
|
||||
q.set_high();
|
||||
led.set_high();
|
||||
} else {
|
||||
q.set_low();
|
||||
led.set_low();
|
||||
}
|
||||
|
||||
// Count rising edges
|
||||
if !last_q && q_high {
|
||||
pulses = pulses.saturating_add(1);
|
||||
}
|
||||
last_q = q_high;
|
||||
|
||||
// Yield to allow USB and other tasks to run
|
||||
yield_now().await;
|
||||
}
|
||||
|
||||
// Compute frequency from 100 ms window
|
||||
let freq_hz = pulses; // pulses per 0.1s => Hz
|
||||
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(
|
||||
&mut msg,
|
||||
"555 window=100ms pulses={} freq={} Hz (A1->Q on PB0)\r\n",
|
||||
pulses, freq_hz
|
||||
);
|
||||
log(msg);
|
||||
|
||||
let address = StandardId::new(0x580 | 0x42).unwrap();
|
||||
let moisture = CanFrame::new(address, &[freq_hz as u8]).unwrap();
|
||||
match bcan::transmit(&mut can, &moisture) {
|
||||
Ok(..) => {
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(
|
||||
&mut msg,
|
||||
"Send to canbus"
|
||||
);
|
||||
log(msg);
|
||||
}
|
||||
Err(err) => {
|
||||
|
||||
|
||||
|
||||
let mut msg: heapless::String<128> = heapless::String::new();
|
||||
let _ = write!(
|
||||
&mut msg,
|
||||
"err {}"
|
||||
,err
|
||||
);
|
||||
log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn log(message: heapless::String<128>) {
|
||||
match LOG_CH.try_send(message) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[task]
|
||||
async fn usb_task(usb: &'static mut UsbDevice<'static, Driver<'static, hal::peripherals::USBD>>) {
|
||||
usb.run().await;
|
||||
}
|
||||
|
||||
#[task]
|
||||
async fn usb_writer(
|
||||
class: &'static mut CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>
|
||||
) {
|
||||
loop {
|
||||
|
||||
class.wait_connection().await;
|
||||
printer(class).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn printer(class: &mut CdcAcmClass<'static, Driver<'static, USBD>>) {
|
||||
loop {
|
||||
let msg = LOG_CH.receive().await;
|
||||
match class.write_packet(msg.as_bytes()).await {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
// Disconnected or endpoint disabled
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user