diff --git a/Cargo.toml b/Cargo.toml
index cfe5af67..b3e2e217 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,7 @@ stm32g4 = { version = "0.16.0", features = ["atomics"] }
paste = "1.0"
fugit = "0.3.7"
stm32-usbd = { version = "0.7.0", optional = true }
-fixed = { version = "1.28.0", optional = true }
+fixed = { version = "1.28.0" }
embedded-io = "0.6"
stm32-hrtim = { version = "0.1.0", optional = true }
@@ -82,9 +82,23 @@ usb = ["dep:stm32-usbd"]
stm32g431 = ["stm32g4/stm32g431", "cat2"]
stm32g441 = ["stm32g4/stm32g441", "cat2"]
stm32g473 = ["stm32g4/stm32g473", "cat3", "adc3", "adc4", "adc5"]
-stm32g474 = ["stm32g4/stm32g474", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g474"]
+stm32g474 = [
+ "stm32g4/stm32g474",
+ "cat3",
+ "adc3",
+ "adc4",
+ "adc5",
+ "stm32-hrtim/stm32g474",
+]
stm32g483 = ["stm32g4/stm32g483", "cat3", "adc3", "adc4", "adc5"]
-stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g484"]
+stm32g484 = [
+ "stm32g4/stm32g484",
+ "cat3",
+ "adc3",
+ "adc4",
+ "adc5",
+ "stm32-hrtim/stm32g484",
+]
stm32g491 = ["stm32g4/stm32g491", "cat4", "adc3"]
stm32g4a1 = ["stm32g4/stm32g4a1", "cat4", "adc3"]
@@ -106,9 +120,10 @@ defmt = [
"embedded-hal/defmt-03",
"embedded-io/defmt-03",
"embedded-test/defmt",
- "stm32-hrtim?/defmt"
+ "stm32-hrtim?/defmt",
]
-cordic = ["dep:fixed"]
+cordic = []
+fmac = []
adc3 = []
adc4 = []
adc5 = []
@@ -174,6 +189,10 @@ required-features = ["usb"]
name = "cordic"
required-features = ["cordic"]
+[[example]]
+name = "fmac"
+required-features = ["fmac"]
+
[[example]]
name = "hrtim-adc-trigger"
required-features = ["hrtim"]
diff --git a/Embed.toml b/Embed.toml
new file mode 100644
index 00000000..74bc2415
--- /dev/null
+++ b/Embed.toml
@@ -0,0 +1,2 @@
+[default.rtt]
+enabled = true
diff --git a/examples/button.rs b/examples/button.rs
index c3530b90..a1c12a9d 100644
--- a/examples/button.rs
+++ b/examples/button.rs
@@ -5,14 +5,13 @@ use stm32g4xx_hal::{
//delay::{DelayExt, SYSTDelayExt},
gpio::{self, ExtiPin, GpioExt, Input, SignalEdge},
rcc::RccExt,
- stm32,
- stm32::{interrupt, Interrupt},
+ stm32::{self, interrupt, Interrupt},
syscfg::SysCfgExt,
};
use core::cell::RefCell;
use core::sync::atomic::{AtomicBool, Ordering};
-use cortex_m::{asm::wfi, interrupt::Mutex};
+use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
type ButtonPin = gpio::PC13;
@@ -52,8 +51,14 @@ fn main() -> ! {
utils::logger::init();
let mut dp = stm32::Peripherals::take().expect("cannot take peripherals");
+
let mut rcc = dp.RCC.constrain();
- let mut syscfg = dp.SYSCFG.constrain();
+
+ // Workaround for RTT when using wfi instruction
+ // Enable an AHB peripheral clock for debug probe with wfi
+ rcc.ahb1enr().modify(|_, w| w.dma1en().set_bit());
+
+ let mut syscfg = dp.SYSCFG.constrain(&mut rcc);
println!("Led Init");
// Configure PA5 pin to blink LED
@@ -80,7 +85,7 @@ fn main() -> ! {
println!("Start Loop");
loop {
- wfi();
+ cortex_m::asm::wfi();
println!("Check");
if G_LED_ON.load(Ordering::Relaxed) {
diff --git a/examples/fmac.rs b/examples/fmac.rs
new file mode 100644
index 00000000..dd273ac8
--- /dev/null
+++ b/examples/fmac.rs
@@ -0,0 +1,97 @@
+#![deny(warnings)]
+#![deny(unsafe_code)]
+#![no_main]
+#![no_std]
+
+use hal::prelude::*;
+use hal::stm32;
+use stm32g4xx_hal as hal;
+
+use cortex_m_rt::entry;
+
+#[macro_use]
+mod utils;
+
+use utils::logger::info;
+
+use stm32g4xx_hal::fmac::{
+ buffer::{BufferLayout, Watermark},
+ function::{Gain, IIR},
+ Buffer, FmacExt,
+};
+
+use fixed::types::I1F15;
+
+#[entry]
+fn main() -> ! {
+ utils::logger::init();
+
+ info!("start");
+ let dp = stm32::Peripherals::take().expect("cannot take peripherals");
+ let mut rcc = dp.RCC.constrain();
+
+ // The FMAC has an internal memory area of 256x16bit words that must be allocated to the
+ // three different buffers named X1 (input buffer), X2 (coefficients), and Y (output buffer).
+ //
+ // The BufferLayout struct takes three generic consts and will calculate the base offsets at compile time,
+ // which must be passed as a generic parameter to the constrain method of the FMAC instance.
+ //
+ // Create an FMAC instance with a memory layout of 32 inputs, 2 coefficients, and 1 output buffer words
+ //
+ // For IIR filters, RM0440 indicates X2 coeffecient buffer should be 2N + 2M where
+ // N is the number of feedforward coefficients and M is the number of feedback coefficients.
+ //
+ // Following constrain(), the buffer layout has been applied to the peripheral and cannot be changed.
+ let mut fmac = dp
+ .FMAC
+ .constrain::>(&mut rcc)
+ .reset();
+
+ // Configure an IIR filter with 1 feedforward, and 1 feedback coefficient,
+ // with both coefficients set to 1.0.
+ //
+ // This is equivalent to a multiply accumulate operation
+ // Y[n] = ∑ X1[n] * X2[0] + Y[n-1] * X2[1]
+ //
+ // The inputs will be set to ~0.01 (to nearest fixed point representation), and the output will increment
+ // by 0.01 into Y for each input sample, and the final read of Y should be about 0.319
+
+ // Fill the input buffer with 0.01
+ fmac.preload_buffer(Buffer::X1, |_index| I1F15::from_num(0.01));
+
+ // Fill the coefficients with I1F15::MAX (max value representable by I1F15, ~1.0)
+ fmac.preload_buffer(Buffer::X2, |_index| I1F15::MAX);
+ fmac.preload_buffer(Buffer::Y, |_index| I1F15::ZERO);
+
+ // Watermarks can be set on the X1 and Y buffers
+ // to control when the input empty and output full
+ // flags are asserted to cause early interrupts or DMA
+ // to avoid underflow or overflow conditions.
+ fmac.set_watermark(Buffer::X1, Watermark::Threshold1);
+ fmac.set_watermark(Buffer::Y, Watermark::Threshold1);
+
+ // Select the IIR function, specifying the number of
+ // feedforward and feedback coefficients, and the gain
+ fmac.select_function(IIR {
+ feedforward_coeffs: 1,
+ feedback_coeffs: 1,
+ gain: Gain::ZeroDB,
+ });
+
+ let fmac = fmac.start();
+
+ info!("Input buffer full: {}", fmac.is_input_full());
+
+ info!("Waiting for result");
+ while !fmac.is_result_available() {}
+
+ info!("Reading results");
+
+ let mut count = 0;
+ loop {
+ while let Some(output) = fmac.read() {
+ count += 1;
+ info!("Output {}: {}", count, output.to_num::());
+ }
+ }
+}
diff --git a/src/fmac.rs b/src/fmac.rs
new file mode 100644
index 00000000..dde2a605
--- /dev/null
+++ b/src/fmac.rs
@@ -0,0 +1,380 @@
+//! Filter math accelerator (FMAC) peripheral
+//!
+//! The FMAC can perform FIR, IIR (direct form 1), and vector functions such as dot product
+
+use fixed::types::I1F15;
+
+use crate::{
+ fmac::{
+ buffer::{BufferLayoutConfig, Watermark},
+ dma::{FmacDmaReader, FmacDmaWriter},
+ function::{Function, LoadX1, LoadX2, LoadY},
+ },
+ rcc::Rcc,
+ stm32::FMAC,
+};
+use core::marker::PhantomData;
+
+pub mod buffer;
+pub mod dma;
+pub mod function;
+pub mod interrupt;
+
+pub trait State {}
+
+pub struct Stopped;
+impl State for Stopped {}
+
+pub struct Running;
+impl State for Running {}
+
+pub struct StoppedDma;
+impl State for StoppedDma {}
+
+pub struct RunningDma;
+impl State for RunningDma {}
+
+pub struct Fmac {
+ _phantom: PhantomData<(Layout, S)>,
+}
+
+impl Fmac {}
+
+/// Buffer selection
+pub enum Buffer {
+ X1,
+ X2,
+ Y,
+}
+
+impl Fmac {
+ /// Start the FMAC in DMA mode, consuming self and return Fmac in the RunningDma state
+ #[inline(always)]
+ pub fn start_dma(self) -> Fmac {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.param().modify(|_, w| w.start().set_bit());
+ Fmac::next_state()
+ }
+}
+
+impl Fmac {
+ /// Configure the FMAC buffers using the layout provided to the constrain method
+ fn configure_buffers(&mut self) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.x1bufcfg().modify(|_, w| unsafe {
+ w.x1_base()
+ .bits(Layout::X1_BASE)
+ .x1_buf_size()
+ .bits(Layout::X1_SIZE)
+ });
+
+ fmac.x2bufcfg().modify(|_, w| unsafe {
+ w.x2_base()
+ .bits(Layout::X2_BASE)
+ .x2_buf_size()
+ .bits(Layout::X2_SIZE)
+ });
+
+ fmac.ybufcfg().modify(|_, w| unsafe {
+ w.y_base()
+ .bits(Layout::Y_BASE)
+ .y_buf_size()
+ .bits(Layout::Y_SIZE)
+ });
+ }
+
+ /// Preloads a [`Buffer`] with data. The closure is called to get the next element to write,
+ /// and will be called Layout::_SIZE times, expecting an I1F15 fixed point in return.
+ ///
+ /// This is only for initialization and should not be used during normal operation.
+ #[inline(always)]
+ pub fn preload_buffer(&mut self, buffer: Buffer, mut f: impl FnMut(u8) -> I1F15) {
+ let fmac = unsafe { &*FMAC::ptr() };
+
+ let word_count = match buffer {
+ Buffer::X1 => Layout::X1_SIZE,
+ Buffer::X2 => Layout::X2_SIZE,
+ Buffer::Y => Layout::Y_SIZE,
+ };
+
+ match buffer {
+ Buffer::X1 => self.select_function(LoadX1(word_count)),
+ Buffer::X2 => self.select_function(LoadX2(word_count, 0)),
+ Buffer::Y => self.select_function(LoadY(word_count)),
+ }
+
+ // Set the start bit before writing data. This will be reset by hardware when the load operation is complete
+ fmac.param().modify(|_, w| w.start().set_bit());
+
+ for index in 0..word_count {
+ // Get a word to write from the closure
+ let word = f(index);
+ fmac.wdata()
+ .write(|w| unsafe { w.bits(word.to_bits() as u32) });
+ }
+
+ // Wait for the start bit to be cleared by hardware
+ while fmac.param().read().start().bit_is_set() {}
+ }
+
+ /// Select the FMAC function, and set the input parameters
+ #[inline(always)]
+ pub fn select_function(&mut self, function: impl Function) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.param()
+ .modify(|_, w| unsafe { w.func().bits(function.id()) });
+
+ self.set_parameters(
+ function.p(),
+ function.q().unwrap_or(0),
+ function.r().unwrap_or(0),
+ );
+ }
+
+ /// Set FMAC input parameters. These are function dependent
+ #[inline(always)]
+ fn set_parameters(&mut self, p: u8, q: u8, r: u8) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.param()
+ .modify(|_, w| unsafe { w.p().bits(p).q().bits(q).r().bits(r) });
+ }
+
+ /// Set FMAC clipping mode
+ ///
+ /// true: Values from accumulator are saturated to the Q1.15 range [-1,1]
+ /// false: Values from accumulator will wrap
+ #[inline(always)]
+ pub fn set_clipping(&mut self, clipping: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.clipen().bit(clipping));
+ }
+
+ /// Set the [`Watermark`] level of the specified [`Buffer`]
+ ///
+ /// The X2 buffer does not support watermarks, as it is typically for
+ /// constant coefficients. It will silently ignore the watermark setting.
+ ///
+ /// For the X1 buffer, the watermark level defines the threshold for setting the Y1FULL flag.
+ ///
+ /// For the Y buffer, the watermark level defines the threshold for setting the YEMPTY flag.
+ /// It must be set to Threshold1 when using DMA mode.
+ #[inline(always)]
+ pub fn set_watermark(&mut self, buffer: Buffer, watermark: Watermark) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ match buffer {
+ Buffer::X1 => {
+ fmac.x1bufcfg()
+ .modify(|_, w| unsafe { w.full_wm().bits(watermark as u8) });
+ }
+ Buffer::Y => {
+ fmac.ybufcfg()
+ .modify(|_, w| unsafe { w.empty_wm().bits(watermark as u8) });
+ }
+ _ => {}
+ }
+ }
+
+ /// Start the FMAC, consuming self and return Fmac in the Running state
+ #[inline(always)]
+ pub fn start(self) -> Fmac {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.param().modify(|_, w| w.start().set_bit());
+ Fmac::next_state()
+ }
+
+ /// Acquire DMA reader and writer handles for the FMAC.
+ /// This returns a tuple of Fmac in the StoppedDma state, FmacDmaReader, and FmacDmaWriter.
+ /// The start_dma() method must be called before the FMAC starts after the reader/writer have been assigned
+ /// to Transfers.
+ #[inline(always)]
+ pub fn acquire_dma(self) -> (Fmac, FmacDmaReader, FmacDmaWriter) {
+ let fmac = unsafe { &*FMAC::ptr() };
+
+ (
+ Fmac::next_state(),
+ FmacDmaReader::new(fmac.rdata() as *const _ as *mut u32),
+ FmacDmaWriter::new(fmac.wdata() as *const _ as *mut u32),
+ )
+ }
+}
+
+impl Fmac {
+ /// Transition to the next state
+ fn next_state() -> Self {
+ Fmac {
+ _phantom: PhantomData,
+ }
+ }
+
+ /// Reset the FMAC. Resets read pointers, internal logic,
+ /// status register, and clears parameters.
+ ///
+ /// Following a reset, the FMAC is in the stopped state,
+ /// and can be restarted again by selecting a new Function
+ /// and issuing a start.
+ #[inline(always)]
+ pub fn reset(self) -> Fmac {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.reset().set_bit());
+ // Wait for reset to be cleared by hardware
+ while fmac.cr().read().reset().bit_is_set() {}
+ Fmac::next_state()
+ }
+
+ /// Get the saturation error status.
+ /// If this bit is set, it can only be cleared by resetting the FMAC.
+ #[inline(always)]
+ pub fn saturation_error_status(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().sat().bit_is_set()
+ }
+
+ /// Get the underflow error status.
+ /// If this bit is set, it can only be cleared by resetting the FMAC.
+ #[inline(always)]
+ pub fn underflow_error_status(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().unfl().bit_is_set()
+ }
+
+ /// Get the ovrerflow error status.
+ /// If this bit is set, it can only be cleared by resetting the FMAC.
+ #[inline(always)]
+ pub fn overflow_error_status(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().ovfl().bit_is_set()
+ }
+
+ /// Get the X1 Buffer full status.
+ ///
+ /// If the flag is set, the X1 buffer is full and no more data can be written to it.
+ /// The flag is set and cleared by hardware, or a reset.
+ #[inline(always)]
+ pub fn x1_full_status(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().x1full().bit_is_set()
+ }
+
+ /// Get the Y Buffer empty status
+ ///
+ /// If the flag is set, the Y buffer is empty and no more data can be read from it.
+ /// The flag is set and cleared by hardware, or a reset.
+ #[inline(always)]
+ pub fn y_empty_status(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().yempty().bit_is_set()
+ }
+
+ /// Enable read DMA from the RDATA register
+ #[inline(always)]
+ pub fn enable_read_dma(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.dmaren().bit(enable));
+ }
+
+ /// Enable write DMA to the WDATA register
+ #[inline(always)]
+ pub fn enable_write_dma(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.dmawen().bit(enable));
+ }
+}
+
+impl Fmac {
+ #[inline(always)]
+ pub fn write(&mut self, data: I1F15) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.wdata()
+ .write(|w| unsafe { w.bits(data.to_bits() as u32) });
+ }
+}
+
+impl Fmac {
+ /// Stop the FMAC operation, clearing the START bit in the PARAM register.
+ /// Consumes self and returns Fmac in the Stopped state, where it can be reconfigured or started again.
+ ///
+ /// # Returns
+ ///
+ /// * Fmac in the Stopped state
+ pub fn stop(self) -> Fmac {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.param().modify(|_, w| w.start().clear_bit());
+ Fmac::next_state()
+ }
+
+ /// Check if there is a result available in the output buffer.
+ ///
+ /// # Returns
+ ///
+ /// * true if there are pending results which can be read
+ /// * false if the Y buffer is empty
+ pub fn is_result_available(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().yempty().bit_is_clear()
+ }
+
+ /// Check if the input buffer (X1) is full
+ ///
+ /// # Returns
+ ///
+ /// * true if the input buffer is full
+ /// * false if the input buffer is not full
+ pub fn is_input_full(&self) -> bool {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.sr().read().x1full().bit_is_set()
+ }
+
+ /// Read the result from the RDATA register.
+ /// The Y buffer read pointer is automatically incremented on each read.
+ ///
+ /// # Returns
+ ///
+ /// * None if the Y buffer is empty
+ /// * Some Fixed point Q1.15 value result as [`fixed::I1F15`] type
+ #[inline(always)]
+ pub fn read(&self) -> Option {
+ if self.is_result_available() {
+ let fmac = unsafe { &*FMAC::ptr() };
+ let result = fmac.rdata().read().bits() as i16;
+ Some(I1F15::from_bits(result))
+ } else {
+ None
+ }
+ }
+
+ /// Write input data to the WDATA register.
+ /// The X1 buffer write pointer is automatically incremented on each write.
+ ///
+ /// # Arguments
+ ///
+ /// * `data` - The input data to write to the FMAC.
+ #[inline(always)]
+ pub fn write(&mut self, data: I1F15) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.wdata()
+ .write(|w| unsafe { w.bits(data.to_bits() as u32) });
+ }
+}
+
+/// Extension trait for constraining the FMAC peripheral.
+pub trait FmacExt {
+ /// Constrain the FMAC peripheral.
+ ///
+ /// # Arguments
+ ///
+ /// * `rcc` - The RCC peripheral for enabling the FMAC clock.
+ fn constrain(self, rcc: &mut Rcc) -> Fmac;
+}
+
+impl FmacExt for FMAC {
+ fn constrain(self, rcc: &mut Rcc) -> Fmac {
+ rcc.rb.ahb1rstr().modify(|_, w| w.fmacrst().set_bit());
+ rcc.rb.ahb1rstr().modify(|_, w| w.fmacrst().clear_bit());
+
+ rcc.rb.ahb1enr().modify(|_, w| w.fmacen().set_bit());
+
+ let mut fmac = Fmac::::next_state().reset();
+ fmac.configure_buffers();
+ fmac
+ }
+}
diff --git a/src/fmac/buffer.rs b/src/fmac/buffer.rs
new file mode 100644
index 00000000..28937a19
--- /dev/null
+++ b/src/fmac/buffer.rs
@@ -0,0 +1,54 @@
+//! FMAC Buffer configuration
+
+/// Buffer watermark thresholds
+#[repr(u8)]
+pub enum Watermark {
+ Threshold1 = 0,
+ Threshold2 = 1,
+ Threshold4 = 2,
+ Threshold8 = 3,
+}
+
+/// FMAC Buffer layout configuration trait
+pub trait BufferLayoutConfig {
+ const X1_BASE: u8;
+ const X2_BASE: u8;
+ const Y_BASE: u8;
+
+ const X1_SIZE: u8;
+ const X2_SIZE: u8;
+ const Y_SIZE: u8;
+
+ // Verify that the total size doesn't exceed FMAC memory
+ const _ASSERT_SIZE: () = assert!(
+ (Self::Y_BASE + Self::Y_SIZE) as u16 <= 256,
+ "Total buffer size exceeds FMAC memory capacity"
+ );
+}
+
+/// FMAC buffer layout
+///
+/// Note: For IIR filters, minimum coefficient size is 2N + 2M
+/// where N is the number of feedforward coefficients and M is the number of feedback coefficients.
+///
+/// The base address of the layout can be specified, and defaults to 0.
+/// This allows for more flexibility in memory allocation for configuring
+/// multiple layouts within the FMAC memory.
+pub struct BufferLayout<
+ const INPUT_SIZE: u8,
+ const COEFFICIENT_SIZE: u8,
+ const OUTPUT_SIZE: u8,
+ const BASE: u8 = 0,
+>;
+
+impl
+ BufferLayoutConfig for BufferLayout
+{
+ const X1_BASE: u8 = BASE;
+ const X2_BASE: u8 = Self::X1_BASE + INPUT_SIZE;
+ const Y_BASE: u8 = Self::X2_BASE + COEFFICIENT_SIZE;
+
+ const X1_SIZE: u8 = INPUT_SIZE;
+ const X2_SIZE: u8 = COEFFICIENT_SIZE;
+ const Y_SIZE: u8 = OUTPUT_SIZE;
+}
diff --git a/src/fmac/dma.rs b/src/fmac/dma.rs
new file mode 100644
index 00000000..52a12bfe
--- /dev/null
+++ b/src/fmac/dma.rs
@@ -0,0 +1,55 @@
+//! DMA support for the FMAC peripheral.
+
+use embedded_dma::ReadBuffer;
+
+use crate::dma::{
+ mux::DmaMuxResources, traits::TargetAddress, MemoryToPeripheral, PeripheralToMemory,
+};
+
+/// Write handle for DMA transfers to the FMAC peripheral.
+pub struct FmacDmaWriter {
+ wdata: *mut u32,
+}
+
+impl FmacDmaWriter {
+ pub(crate) fn new(wdata: *mut u32) -> Self {
+ Self { wdata }
+ }
+}
+
+/// Read handle for DMA transfers from the FMAC peripheral.
+pub struct FmacDmaReader {
+ rdata: *mut u32,
+}
+
+impl FmacDmaReader {
+ pub(crate) fn new(rdata: *mut u32) -> Self {
+ Self { rdata }
+ }
+}
+
+unsafe impl TargetAddress for FmacDmaReader {
+ type MemSize = u32;
+ const REQUEST_LINE: Option = Some(DmaMuxResources::FMAC_Read as u8);
+
+ fn address(&self) -> u32 {
+ self.rdata as u32
+ }
+}
+
+unsafe impl ReadBuffer for FmacDmaReader {
+ type Word = u32;
+
+ unsafe fn read_buffer(&self) -> (*const Self::Word, usize) {
+ (self.rdata as *const u32, 1)
+ }
+}
+
+unsafe impl TargetAddress for FmacDmaWriter {
+ type MemSize = u32;
+ const REQUEST_LINE: Option = Some(DmaMuxResources::FMAC_Write as u8);
+
+ fn address(&self) -> u32 {
+ self.wdata as u32
+ }
+}
diff --git a/src/fmac/function.rs b/src/fmac/function.rs
new file mode 100644
index 00000000..d136490a
--- /dev/null
+++ b/src/fmac/function.rs
@@ -0,0 +1,127 @@
+//! FMAC Functions
+
+/// Programmable gain parameter in the range 0dB to 42dB in 6dB increments.
+#[repr(u8)]
+#[derive(Copy, Clone)]
+pub enum Gain {
+ ZeroDB = 0,
+ SixDB = 1,
+ TwelveDB = 2,
+ EighteenDB = 3,
+ TwentyFourDB = 4,
+ ThirtyDB = 5,
+ ThirtySixDB = 6,
+ FortyTwoDB = 7,
+}
+
+/// FMAC Function trait. This defines the function specific data that is loaded into the PARAM register
+pub trait Function {
+ /// The function ID loaded into the FUNC field
+ const ID: u8;
+
+ #[inline(always)]
+ fn id(&self) -> u8 {
+ Self::ID
+ }
+
+ /// Returns the value of the P parameter
+ fn p(&self) -> u8;
+
+ /// Returns the value of the Q parameter, or None if not used
+ fn q(&self) -> Option {
+ None
+ }
+
+ /// Returns the value of the R parameter, or None if not used
+ fn r(&self) -> Option {
+ None
+ }
+}
+
+/// Load X1 buffer with specified length
+pub struct LoadX1(pub u8);
+/// Load X2 buffer with length of first set, and second set of coefficients
+/// The second length may be zero if the second set of coefficients is not used.
+pub struct LoadX2(pub u8, pub u8);
+/// Load Y buffer with specified length
+pub struct LoadY(pub u8);
+
+impl Function for LoadX1 {
+ const ID: u8 = 1;
+
+ fn p(&self) -> u8 {
+ self.0
+ }
+}
+
+impl Function for LoadX2 {
+ const ID: u8 = 2;
+
+ fn p(&self) -> u8 {
+ self.0
+ }
+
+ fn q(&self) -> Option {
+ Some(self.1)
+ }
+}
+
+impl Function for LoadY {
+ const ID: u8 = 3;
+
+ fn p(&self) -> u8 {
+ self.0
+ }
+}
+
+/// FIR / Convolution / Dot Product function
+pub struct Convolution {
+ /// Number of coefficients in the X2 buffer
+ pub length: u8,
+ /// Gain parameter in the range 0dB to 42dB in 6dB increments
+ pub gain: Gain,
+}
+
+/// Finite Impulse Response (FIR) / Convolution / Dot Product function
+impl Function for Convolution {
+ const ID: u8 = 8;
+
+ #[inline(always)]
+ fn p(&self) -> u8 {
+ self.length
+ }
+
+ #[inline(always)]
+ fn r(&self) -> Option {
+ Some(self.gain as u8)
+ }
+}
+
+/// Infinite Impulse Response (IIR) filter / Multiply Accumulate
+pub struct IIR {
+ /// The number of feedforward coefficients in the X2 buffer
+ /// X2 buffer size should be at least 2*feedforward_coeffs + 2*feedback_coeffs
+ pub feedforward_coeffs: u8,
+ /// The number of feedback coefficients in the X2 buffer
+ /// X2 buffer size should be at least 2*feedforward_coeffs + 2*feedback_coeffs
+ pub feedback_coeffs: u8,
+ /// Gain parameter in the range 0dB to 42dB in 6dB increments
+ pub gain: Gain,
+}
+
+impl Function for IIR {
+ const ID: u8 = 9;
+
+ #[inline(always)]
+ fn p(&self) -> u8 {
+ self.feedforward_coeffs
+ }
+
+ fn q(&self) -> Option {
+ Some(self.feedback_coeffs)
+ }
+
+ fn r(&self) -> Option {
+ Some(self.gain as u8)
+ }
+}
diff --git a/src/fmac/interrupt.rs b/src/fmac/interrupt.rs
new file mode 100644
index 00000000..b0da93b7
--- /dev/null
+++ b/src/fmac/interrupt.rs
@@ -0,0 +1,43 @@
+use super::buffer::BufferLayoutConfig;
+use super::Fmac;
+use super::State;
+use crate::stm32::FMAC;
+
+impl Fmac {
+ /// Enable saturation error interrupt
+ #[inline(always)]
+ pub fn enable_saturation_error_interrupt(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.satien().bit(enable));
+ }
+
+ /// Enable underflow error interrupt
+ #[inline(always)]
+ pub fn enable_underflow_error_interrupt(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.unflien().bit(enable));
+ }
+
+ /// Enable overflow error interrupt
+ #[inline(always)]
+ pub fn enable_overflow_error_interrupt(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.ovflien().bit(enable));
+ }
+
+ /// Enable write interrupts. The write interrupt is raised when the X1 (operand)
+ /// buffer is not full, and there is space available to write to the FMAC.
+ #[inline(always)]
+ pub fn enable_write_interrupt(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.wien().bit(enable));
+ }
+
+ /// Enable read interrupts. The read interrupt is raised when the Y (result)
+ /// buffer is not empty, and there is data available to read from the FMAC.
+ #[inline(always)]
+ pub fn enable_read_interrupt(&mut self, enable: bool) {
+ let fmac = unsafe { &*FMAC::ptr() };
+ fmac.cr().modify(|_, w| w.rien().bit(enable));
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index c8b663b4..95c0610a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -69,6 +69,8 @@ pub mod can;
pub mod comparator;
#[cfg(feature = "cordic")]
pub mod cordic;
+#[cfg(feature = "fmac")]
+pub mod fmac;
// pub mod crc;
pub mod dac;
pub mod delay;
diff --git a/src/syscfg.rs b/src/syscfg.rs
index b13833bb..097f6850 100644
--- a/src/syscfg.rs
+++ b/src/syscfg.rs
@@ -1,25 +1,17 @@
-use crate::bb;
-use crate::stm32::{RCC, SYSCFG};
+use crate::rcc::Rcc;
+use crate::stm32::SYSCFG;
use core::ops::Deref;
/// Extension trait that constrains the `SYSCFG` peripheral
pub trait SysCfgExt {
/// Constrains the `SYSCFG` peripheral so it plays nicely with the other abstractions
- fn constrain(self) -> SysCfg;
+ fn constrain(self, rcc: &mut Rcc) -> SysCfg;
}
impl SysCfgExt for SYSCFG {
- fn constrain(self) -> SysCfg {
- unsafe {
- // NOTE(unsafe) this reference will only be used for atomic writes with no side effects.
- let rcc = &(*RCC::ptr());
-
- // Enable clock.
- bb::set(&rcc.apb2enr(), 0);
-
- // Stall the pipeline to work around erratum 2.1.13 (DM00037591)
- cortex_m::asm::dsb();
- }
+ fn constrain(self, rcc: &mut Rcc) -> SysCfg {
+ // Enable SYSCFG peripheral clock in APB2ENR register
+ rcc.apb2enr().modify(|_, w| w.syscfgen().set_bit());
SysCfg(self)
}