Crate aps490_pfpu2_mini
source ·Expand description
This RP2040 project provides contact detection with a highly-conductivity/highly-capacitive surface (such as brain tissue) for an autopsy saw. For more information, check out the repo.
§Crate features
triple_status
: Enables the use of 3 LEDs to provide system status. This is the main user interface for the tool, and is enabled by default.rgba_status
: Alternate configuration which uses a single common-anode RGB LED. This is the design which appears in our schematic.trace_avg_samples
: Logs the average voltage difference measured, 250 samples at a time. Seebuffer::Buffers::trace_avg_samples
.trace_indiv_samples
Logs information on every sample recorded. Very noisy! Seeinterrupt::AlignedAverages::trace_high_index
andinterrupt::trace_indiv_samples
disable_switch
: Starts the SysTick timer to check the disable switch status. Never tested this feature, and I’m pretty sure my implementation will cause the system to panic due to poor synchronization. This functionality should be redesigned before enabling the feature.
Features
triple_status
and rgba_status
are
mutually exclusive.§Demo
The following is a simplified (including Rgba
-only lights) implementation of the binary crate
used on our proof-of-concept.
#![no_std]
#![no_main]
use aps490_pfpu2_mini::{
buffer::{create_avg_buffer, Buffers},
components::{LedControl, Rgba, StatusLed, StatusLedBase},
interrupt::{DISABLE_SWITCH, READINGS_FIFO, SIGNAL_GEN, STATUS_LEDS},
};
use cortex_m::peripheral::syst::SystClkSource;
use defmt::{debug, warn};
#[allow(unused_imports)]
use defmt_rtt as _;
use embedded_hal::pwm::SetDutyCycle;
#[allow(unused_imports)]
use panic_probe as _;
use rp2040_hal::{
adc::{Adc, AdcPin},
clocks::init_clocks_and_plls,
dma::{single_buffer, DMAExt, SingleChannel},
entry,
gpio::Pins,
pac,
prelude::*,
pwm::Slices,
Sio, Watchdog,
};
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
pub const XOSC_FREQ_HZ: u32 = 12_000_000;
const SYS_CLOCK_FREQ: u32 = 24_000_000;
pub static SIGNAL_GEN_FREQ_HZ: f32 = 100_000.0;
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();
let mut watchdog = Watchdog::new(pac.WATCHDOG);
let sio = Sio::new(pac.SIO);
let mut clocks = init_clocks_and_plls(
XOSC_FREQ_HZ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();
let mut sysclk_rescale = clocks.system_clock.freq().to_Hz() as f32 / SYS_CLOCK_FREQ as f32;
// Setup status LEDs
let pins = Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
critical_section::with(|cs| {
STATUS_LEDS.replace(cs, Rgba::init(pins.gpio6, pins.gpio7, pins.gpio8));
});
// Initialize and start signal generator
let mut pwm_slices = Slices::new(pac.PWM, &mut pac.RESETS);
pwm_slices.pwm3.set_top(
((clocks.system_clock.freq().to_Hz() as f32 / (SIGNAL_GEN_FREQ_HZ * sysclk_rescale))
- 1.0) as u16,
);
pwm_slices.pwm3.enable();
let mut signal_gen = pwm_slices.pwm3.channel_a;
signal_gen.output_to(pins.gpio22);
signal_gen.set_duty_cycle_percent(50).unwrap();
critical_section::with(|cs| SIGNAL_GEN.replace(cs, Some(signal_gen)));
// Setup ADC pins, DMA, buffers
let mut adc = Adc::new(pac.ADC, &mut pac.RESETS);
let mut adc_pin0 = AdcPin::new(pins.gpio26.into_floating_input()).unwrap();
let mut dma = pac.DMA.split(&mut pac.RESETS);
Buffers::init();
// Setup first transfer
let avg_buffer = create_avg_buffer().unwrap();
let mut readings_fifo = adc
.build_fifo()
.set_channel(&mut adc_pin0)
.clock_divider(
((clocks.system_clock.freq().to_Hz() as f32
/ (2.0 * (SIGNAL_GEN_FREQ_HZ * sysclk_rescale)))
- 1.0) as u16,
0,
)
.shift_8bit()
.enable_dma()
.start_paused();
dma.ch0.enable_irq0();
let adc_dma_transfer =
single_buffer::Config::new(dma.ch0, readings_fifo.dma_read_target(), avg_buffer);
debug!("critical_section: transfer readings FIFO to mutex");
critical_section::with(|cs| READINGS_FIFO.replace(cs, Some(adc_dma_transfer.start())));
readings_fifo.resume();
// Configure and enable SysTick for disable switch
let disable_switch = pins.gpio9.into_pull_down_input();
disable_switch.set_schmitt_enabled(true); // Debouncing
debug!("critical_section: init disable switch");
critical_section::with(|cs| DISABLE_SWITCH.replace(cs, Some(disable_switch)));
let mut syst = core.SYST;
syst.set_clock_source(SystClkSource::Core); // 1 us per tick
syst.set_reload(20_000);
syst.clear_current();
#[cfg(feature = "disable_switch")]
syst.enable_interrupt();
// Begin normal system operation
critical_section::with(|cs| {
#[cfg(feature = "rgba_status")]
StatusLedBase::<Rgba>::set_normal(cs, Some("System initialization complete"));
#[cfg(feature = "triple_status")]
StatusLedBase::<Triple>::set_normal(cs, Some("System initialization complete"));
});
unsafe { pac::NVIC::unmask(pac::Interrupt::DMA_IRQ_0) }
loop {
// All functionality in interrupts
cortex_m::asm::wfi();
}
}
Modules§
- Buffers for recording data from the ADC, and tracking long-term averages from the detection system.
- Configuration for system state and status LED control
- Interrupt and exception handlers, plus static
Mutex
.