config for sensor

This commit is contained in:
2025-10-31 14:01:43 +01:00
parent cafe1b264e
commit 8b0734d029
7 changed files with 35424 additions and 5199 deletions

View File

@@ -6037,7 +6037,7 @@
(descr "SOT, 3 Pin (JEDEC TO-236 Var AB https://www.jedec.org/document_search?search_api_views_fulltext=TO-236), generated with kicad-footprint-generator ipc_gullwing_generator.py")
(tags "SOT TO_SOT_SMD")
(property "Reference" "D6"
(at 0 -2.4 180)
(at -1.9875 -2.25 180)
(layer "F.SilkS")
(uuid "ba224372-284d-4e83-bdee-3dcc26b3a1ec")
(effects
@@ -18704,7 +18704,7 @@
(descr "SOT, 3 Pin (JEDEC TO-236 Var AB https://www.jedec.org/document_search?search_api_views_fulltext=TO-236), generated with kicad-footprint-generator ipc_gullwing_generator.py")
(tags "SOT TO_SOT_SMD")
(property "Reference" "D1"
(at 0 -2.4 0)
(at 0 2.3 0)
(layer "F.SilkS")
(uuid "9750a299-8bf6-4149-9d59-b90e672d444d")
(effects
@@ -22662,7 +22662,7 @@
(descr "SOT, 3 Pin (JEDEC TO-236 Var AB https://www.jedec.org/document_search?search_api_views_fulltext=TO-236), generated with kicad-footprint-generator ipc_gullwing_generator.py")
(tags "SOT TO_SOT_SMD")
(property "Reference" "D4"
(at 0 -2.4 0)
(at 2.9875 -0.25 0)
(layer "F.SilkS")
(uuid "f9abdc1a-0e87-4699-bd29-df7c0baae9cd")
(effects
@@ -27517,7 +27517,7 @@
)
)
(gr_text "Extension Module"
(at 212.58 97.67 0)
(at 212.516642 96.15145 0)
(layer "F.SilkS")
(uuid "e56a58c6-c9a0-4ac6-ae66-550392612e86")
(effects

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"board": {
"active_layer": 0,
"active_layer": 25,
"active_layer_preset": "",
"auto_track_width": true,
"hidden_netclasses": [],

View File

@@ -51,7 +51,13 @@
"min_clearance": 0.5
}
},
"diff_pair_dimensions": [],
"diff_pair_dimensions": [
{
"gap": 0.0,
"via_gap": 0.0,
"width": 0.0
}
],
"drc_exclusions": [],
"meta": {
"version": 2
@@ -95,8 +101,8 @@
"pth_inside_courtyard": "ignore",
"shorting_items": "error",
"silk_edge_clearance": "warning",
"silk_over_copper": "warning",
"silk_overlap": "warning",
"silk_over_copper": "ignore",
"silk_overlap": "ignore",
"skew_out_of_range": "error",
"solder_mask_bridge": "error",
"starved_thermal": "error",
@@ -133,7 +139,7 @@
"min_track_width": 0.0,
"min_via_annular_width": 0.1,
"min_via_diameter": 0.5,
"solder_mask_to_copper_clearance": 0.0,
"solder_mask_to_copper_clearance": 0.005,
"use_height_for_length_calcs": true
},
"teardrop_options": [
@@ -180,7 +186,12 @@
"td_width_to_size_filter_ratio": 0.9
}
],
"track_widths": [],
"track_widths": [
0.0,
0.2,
0.5,
1.0
],
"tuning_pattern_settings": {
"diff_pair_defaults": {
"corner_radius_percentage": 80,
@@ -207,7 +218,12 @@
"spacing": 0.6
}
},
"via_dimensions": [],
"via_dimensions": [
{
"diameter": 0.0,
"drill": 0.0
}
],
"zones_allow_external_fillets": false
},
"ipc2581": {
@@ -431,7 +447,7 @@
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"power_pin_not_driven": "ignore",
"same_local_global_label": "warning",
"similar_label_and_power": "warning",
"similar_labels": "warning",
@@ -818,6 +834,24 @@
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
},
{
"group_by": false,
"label": "LCSC_PART_NUMBER",
"name": "LCSC_PART_NUMBER",
"show": false
},
{
"group_by": false,
"label": "Sim.Device",
"name": "Sim.Device",
"show": false
},
{
"group_by": false,
"label": "Sim.Type",
"name": "Sim.Type",
"show": false
}
],
"filter_string": "",

File diff suppressed because it is too large Load Diff

View File

@@ -51,3 +51,11 @@ strip = true # symbols are not flashed to the microcontroller, so don't strip
lto = true
debug = false
opt-level = "z" # Optimize for size.
[[bin]]
name = "sensor"
path = "src/main.rs"
test = false
bench = false
doctest = false

View File

@@ -3,12 +3,12 @@
extern crate alloc;
use crate::hal::peripherals::CAN1;
use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
use canapi::id::{plant_id, MessageKind, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
use canapi::SensorSlot;
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
use ch32_hal::can;
use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode};
use ch32_hal::gpio::{Level, Output, Speed};
use ch32_hal::gpio::{Flex, Level, Output, Pull, Speed};
use ch32_hal::mode::NonBlocking;
use ch32_hal::peripherals::USBD;
use core::fmt::Write as _;
@@ -23,6 +23,9 @@ 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) => {{
@@ -37,20 +40,14 @@ bind_interrupts!(struct Irqs {
USB_LP_CAN1_RX0 => hal::usbd::InterruptHandler<hal::peripherals::USBD>;
});
use embedded_alloc::LlffHeap as Heap;
use embedded_can::nb::Can as nb_can;
use log::log;
use qingke::riscv::asm::delay;
#[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) {
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
//
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());
@@ -97,9 +94,53 @@ async fn main(spawner: Spawner) {
// 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);
let mut info = Output::new(p.PB2, Level::Low, Speed::Low);
let info = Output::new(p.PA3, 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);
// 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;
if res_pb3.is_none() || res_pb4.is_none() || res_pb5.is_none() || res_pb6.is_none() || res_pb7.is_none() {
// 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));
log(msg);
blink_error(&mut info, code).await;
};
let slot = if res_pb3.unwrap() { SensorSlot::B } else { SensorSlot::A };
let mut addr: u8 = 0;
if res_pb4.unwrap() { addr |= 1; }
if res_pb5.unwrap() { addr |= 2; }
if res_pb6.unwrap() { addr |= 4; }
if res_pb7.unwrap() { addr |= 8; }
// Log startup configuration and derived CAN IDs
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 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);
}
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
let adc = Adc::new(p.ADC1, Default::default());
@@ -120,36 +161,59 @@ async fn main(spawner: Spawner) {
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();
spawner.spawn(worker(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;
}
// 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> {
pin.set_as_input(Pull::Down);
Timer::after_millis(2).await;
let low_read = pin.is_high();
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 }
}
async fn blink_error(mut info_led: &mut Output<'static>, code: u8) -> !{
loop {
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B)
for _ in 0..code {
info_led.set_high();
Timer::after_millis(200).await;
info_led.set_low();
Timer::after_millis(200).await;
}
// Pause between sequences
Timer::after_secs(2).await;
}
}
#[task]
async fn worker(
mut q: Output<'static>,
mut led: Output<'static>,
mut info: 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
) {
// 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
let moisture_address =
StandardId::new(plant_id(MOISTURE_DATA_OFFSET, SensorSlot::A, 0)).unwrap();
let identity_address =
StandardId::new(plant_id(IDENTIFY_CMD_OFFSET, SensorSlot::A, 0)).unwrap();
let mut filter = CanFilter::new_id_list();
filter
.get(0)
.unwrap()
.set(identity_address.into(), Default::default());
.set(identify_id.into(), Default::default());
can.add_filter(filter);
//can.add_filter(CanFilter::accept_all());
@@ -206,7 +270,7 @@ async fn worker(
);
log(msg);
let mut moisture = CanFrame::new(moisture_address, &[freq_hz as u8]).unwrap();
let mut moisture = CanFrame::new(moisture_id, &[freq_hz as u8]).unwrap();
match can.transmit(&mut moisture) {
Ok(..) => {
let mut msg: heapless::String<128> = heapless::String::new();
@@ -230,15 +294,15 @@ async fn worker(
&mut msg,
"Received from canbus: {:?} ident is {:?} \r\n",
s_frame.as_raw(),
identity_address.as_raw()
identify_id.as_raw()
);
log(msg);
if s_frame.as_raw() == identity_address.as_raw() {
if s_frame.as_raw() == identify_id.as_raw() {
for _ in 0..10 {
Timer::after_millis(250).await;
led.toggle();
info.toggle();
}
led.set_low();
info.set_low();
}
}
Id::Extended(_) => {}