start of plantsensor

This commit is contained in:
2025-10-06 13:18:03 +02:00
parent a3cdd92af8
commit 4ba68182e5
27 changed files with 2980 additions and 0 deletions

207
rust_can_sensor/src/main.rs Normal file
View File

@@ -0,0 +1,207 @@
#![no_std]
#![no_main]
use core::fmt::Write as _;
use ch32_hal::gpio::{Level, Output, Speed};
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
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, TrySendError};
use embassy_time::{Timer, Instant, Duration};
use heapless::String;
use {ch32_hal as hal, panic_halt as _};
bind_interrupts!(struct Irqs {
USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>;
});
// 'static storage for USB descriptors and state so we can spawn tasks
static mut USB_CONFIG_DESCRIPTOR: [u8; 256] = [0; 256];
static mut USB_BOS_DESCRIPTOR: [u8; 256] = [0; 256];
static mut USB_CONTROL_BUF: [u8; 64] = [0; 64];
static mut CDC_STATE: core::mem::MaybeUninit<State<'static>> = core::mem::MaybeUninit::uninit();
static mut USB_DEVICE: core::mem::MaybeUninit<UsbDevice<'static, Driver<'static, hal::peripherals::USBD>>> = core::mem::MaybeUninit::uninit();
static mut CDC_CLASS: core::mem::MaybeUninit<CdcAcmClass<'static, Driver<'static, hal::peripherals::USBD>>> = core::mem::MaybeUninit::uninit();
static LOG_CH: Channel<CriticalSectionRawMutex, heapless::String<128>, 8> = Channel::new();
#[embassy_executor::main(entry = "qingke_rt::entry")]
async fn main(spawner: Spawner) {
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 usb = unsafe {
let mut builder = Builder::new(
driver,
config,
&mut USB_CONFIG_DESCRIPTOR,
&mut USB_BOS_DESCRIPTOR,
&mut [], // no msos descriptors
&mut USB_CONTROL_BUF,
);
// Initialize CDC state and create CDC-ACM class
CDC_STATE.write(State::new());
let class = {
let state_ref: &mut State<'static> = CDC_STATE.assume_init_mut();
CdcAcmClass::new(&mut builder, state_ref, 64)
};
CDC_CLASS.write(class);
// Build USB device
let dev = builder.build();
USB_DEVICE.write(dev);
USB_DEVICE.assume_init_mut()
};
// 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());
let ain = p.PA1;
// Spawn independent tasks using 'static references
unsafe {
let class = CDC_CLASS.assume_init_mut();
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)).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,
) {
// 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);
}
}
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;
}
}
}
}