use core::mem::MaybeUninit;
use stm32h7xx_hal as hal;
use mutex_trait::Mutex;
use super::design_parameters::SampleBuffer;
use super::timers;
use hal::{
dma::{
config::Priority,
dma::{DMAReq, DmaConfig},
traits::TargetAddress,
DMAError, MemoryToPeripheral, PeripheralToMemory, Transfer,
},
spi::{HalDisabledSpi, HalEnabledSpi, HalSpi},
};
#[derive(Copy, Clone, Default)]
pub struct AdcCode(pub u16);
impl AdcCode {
const FULL_SCALE: f32 = 5.0 / 2.0 * 4.096;
const VOLT_PER_LSB: f32 = -Self::FULL_SCALE / i16::MIN as f32;
const LSB_PER_VOLT: f32 = 1. / Self::VOLT_PER_LSB;
}
impl From<u16> for AdcCode {
fn from(value: u16) -> Self {
Self(value)
}
}
impl From<i16> for AdcCode {
fn from(value: i16) -> Self {
Self(value as u16)
}
}
impl From<AdcCode> for i16 {
fn from(code: AdcCode) -> i16 {
code.0 as i16
}
}
impl From<AdcCode> for u16 {
fn from(code: AdcCode) -> u16 {
code.0
}
}
impl From<AdcCode> for f32 {
fn from(code: AdcCode) -> f32 {
i16::from(code) as f32 * AdcCode::VOLT_PER_LSB
}
}
impl TryFrom<f32> for AdcCode {
type Error = ();
fn try_from(voltage: f32) -> Result<AdcCode, ()> {
let code = voltage * Self::LSB_PER_VOLT;
if !(i16::MIN as f32..=i16::MAX as f32).contains(&code) {
Err(())
} else {
Ok(AdcCode::from(code as i16))
}
}
}
#[link_section = ".axisram.buffers"]
static mut SPI_START: MaybeUninit<[u32; 1]> = MaybeUninit::uninit();
#[link_section = ".axisram.buffers"]
static mut SPI_EOT_CLEAR: MaybeUninit<[u32; 1]> = MaybeUninit::uninit();
#[link_section = ".axisram.buffers"]
static mut ADC_BUF: MaybeUninit<[[SampleBuffer; 2]; 2]> = MaybeUninit::uninit();
macro_rules! adc_input {
($name:ident, $index:literal, $trigger_stream:ident, $data_stream:ident, $clear_stream:ident,
$spi:ident, $trigger_channel:ident, $dma_req:ident, $clear_channel:ident, $dma_clear_req:ident) => {
paste::paste! {
struct [< $spi CR >] {
_channel: timers::tim2::$trigger_channel,
}
impl [< $spi CR >] {
pub fn new(_channel: timers::tim2::$trigger_channel) -> Self {
Self { _channel }
}
}
unsafe impl TargetAddress<MemoryToPeripheral> for [< $spi CR >] {
type MemSize = u32;
const REQUEST_LINE: Option<u8> = Some(DMAReq::$dma_req as u8);
fn address(&self) -> usize {
let regs = unsafe { &*hal::stm32::$spi::ptr() };
®s.cr1 as *const _ as usize
}
}
struct [< $spi IFCR >] {
_channel: timers::tim3::$clear_channel,
}
impl [< $spi IFCR >] {
pub fn new(_channel: timers::tim3::$clear_channel) -> Self {
Self { _channel }
}
}
unsafe impl TargetAddress<MemoryToPeripheral> for [< $spi IFCR >] {
type MemSize = u32;
const REQUEST_LINE: Option<u8> = Some(DMAReq::$dma_clear_req as u8);
fn address(&self) -> usize {
let regs = unsafe { &*hal::stm32::$spi::ptr() };
®s.ifcr as *const _ as usize
}
}
pub struct $name {
transfer: Transfer<
hal::dma::dma::$data_stream<hal::stm32::DMA1>,
hal::spi::Spi<hal::stm32::$spi, hal::spi::Disabled, u16>,
PeripheralToMemory,
&'static mut [u16],
hal::dma::DBTransfer,
>,
trigger_transfer: Transfer<
hal::dma::dma::$trigger_stream<hal::stm32::DMA1>,
[< $spi CR >],
MemoryToPeripheral,
&'static mut [u32; 1],
hal::dma::DBTransfer,
>,
clear_transfer: Transfer<
hal::dma::dma::$clear_stream<hal::stm32::DMA1>,
[< $spi IFCR >],
MemoryToPeripheral,
&'static mut [u32; 1],
hal::dma::DBTransfer,
>,
}
impl $name {
pub fn new(
spi: hal::spi::Spi<hal::stm32::$spi, hal::spi::Enabled, u16>,
trigger_stream: hal::dma::dma::$trigger_stream<
hal::stm32::DMA1,
>,
data_stream: hal::dma::dma::$data_stream<hal::stm32::DMA1>,
clear_stream: hal::dma::dma::$clear_stream<hal::stm32::DMA1>,
trigger_channel: timers::tim2::$trigger_channel,
clear_channel: timers::tim3::$clear_channel,
batch_size: usize,
) -> Self {
let clear_config = DmaConfig::default()
.priority(Priority::VeryHigh)
.circular_buffer(true);
let spi_eot_clear = unsafe {
SPI_EOT_CLEAR.write([1 << 3])
};
clear_channel.listen_dma();
clear_channel.to_output_compare(0);
let clear_transfer: Transfer<
_,
_,
MemoryToPeripheral,
_,
_,
> = Transfer::init(
clear_stream,
[< $spi IFCR >]::new(clear_channel),
spi_eot_clear,
None,
clear_config,
);
trigger_channel.listen_dma();
trigger_channel.to_output_compare(2 + $index);
let trigger_config = DmaConfig::default()
.priority(Priority::High)
.circular_buffer(true);
let spi_start = unsafe {
SPI_START.write([0x201])
};
let trigger_transfer: Transfer<
_,
_,
MemoryToPeripheral,
_,
_,
> = Transfer::init(
trigger_stream,
[< $spi CR >]::new(trigger_channel),
spi_start,
None,
trigger_config,
);
let data_config = DmaConfig::default()
.memory_increment(true)
.double_buffer(true)
.transfer_complete_interrupt($index == 1)
.priority(Priority::VeryHigh);
let mut spi = spi.disable();
spi.listen(hal::spi::Event::Error);
let adc_buf = unsafe {
ADC_BUF.write(Default::default())
};
let adc_bufs = adc_buf[$index].split_at_mut(1);
let data_transfer: Transfer<_, _, PeripheralToMemory, _, _> =
Transfer::init(
data_stream,
spi,
&mut adc_bufs.0[0][..batch_size],
Some(&mut adc_bufs.1[0][..batch_size]),
data_config,
);
Self {
transfer: data_transfer,
trigger_transfer,
clear_transfer,
}
}
pub fn start(&mut self) {
self.transfer.start(|spi| {
spi.enable_dma_rx();
spi.inner().cr2.modify(|_, w| w.tsize().bits(1));
spi.inner().cr1.modify(|_, w| w.spe().set_bit());
});
self.clear_transfer.start(|_| {});
self.trigger_transfer.start(|_| {});
}
pub fn with_buffer<F, R>(&mut self, f: F) -> Result<R, DMAError>
where
F: FnOnce(&mut &'static mut [u16]) -> R,
{
unsafe { self.transfer.next_dbm_transfer_with(|buf, _current| f(buf)) }
}
}
impl Mutex for $name {
type Data = &'static mut [u16];
fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
self.with_buffer(f).unwrap()
}
}
}
};
}
adc_input!(
Adc0Input, 0, Stream0, Stream1, Stream2, SPI2, Channel1, Tim2Ch1, Channel1,
Tim3Ch1
);
adc_input!(
Adc1Input, 1, Stream3, Stream4, Stream5, SPI3, Channel2, Tim2Ch2, Channel2,
Tim3Ch2
);