HAL update

This commit is contained in:
Robin Müller 2025-02-14 09:44:13 +01:00
parent 6cbba8414c
commit bf41b59a24
11 changed files with 104 additions and 324 deletions

View File

@ -122,14 +122,14 @@ fn main() -> ! {
}
TestCase::Pulse => {
let mut output_pulsed = pinsa.pa0.into_push_pull_output();
output_pulsed.pulse_mode(true, PinState::Low);
output_pulsed.configure_pulse_mode(true, PinState::Low);
rprintln!("Pulsing high 10 times..");
output_pulsed.set_low().unwrap();
for _ in 0..10 {
output_pulsed.set_high().unwrap();
cortex_m::asm::delay(25_000_000);
}
output_pulsed.pulse_mode(true, PinState::High);
output_pulsed.configure_pulse_mode(true, PinState::High);
rprintln!("Pulsing low 10 times..");
for _ in 0..10 {
output_pulsed.set_low().unwrap();
@ -137,15 +137,12 @@ fn main() -> ! {
}
}
TestCase::DelayGpio => {
let mut out_0 = pinsa
.pa0
.into_readable_push_pull_output()
.delay(true, false);
let mut out_1 = pinsa
.pa1
.into_readable_push_pull_output()
.delay(false, true);
let mut out_2 = pinsa.pa3.into_readable_push_pull_output().delay(true, true);
let mut out_0 = pinsa.pa0.into_readable_push_pull_output();
out_0.configure_delay(true, false);
let mut out_1 = pinsa.pa1.into_readable_push_pull_output();
out_1.configure_delay(false, true);
let mut out_2 = pinsa.pa3.into_readable_push_pull_output();
out_2.configure_delay(true, true);
for _ in 0..20 {
out_0.toggle().unwrap();
out_1.toggle().unwrap();

View File

@ -8,6 +8,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased]
## [v0.10.0]
## Added
- A lot of missing `defmt::Format` implementations.
## Changed
- Missing GPIO API replacements from `x` to `configure_x`
## [v0.9.0]
## Fixed

View File

@ -29,7 +29,7 @@ static_cell = "2"
thiserror = { version = "2", default-features = false }
void = { version = "1", default-features = false }
once_cell = {version = "1", default-features = false }
va108xx = { version = "0.4", default-features = false, features = ["critical-section"] }
va108xx = { version = "0.5", path = "../va108xx", default-features = false, features = ["critical-section", "defmt"] }
embassy-sync = "0.6"
defmt = { version = "0.3", optional = true }
@ -42,7 +42,7 @@ portable-atomic = "1"
[features]
default = ["rt"]
rt = ["va108xx/rt"]
defmt = ["dep:defmt", "fugit/defmt"]
defmt = ["dep:defmt", "fugit/defmt", "embedded-hal/defmt-03"]
[package.metadata.docs.rs]
all-features = true

View File

@ -115,7 +115,7 @@ impl InputPinFuture {
EDGE_DETECTION[pin_id_to_offset(pin.id())]
.store(false, core::sync::atomic::Ordering::Relaxed);
pin.interrupt_edge(
pin.configure_edge_interrupt(
edge,
InterruptConfig::new(irq, true, true),
Some(sys_cfg),

View File

@ -181,6 +181,7 @@ pub struct DynPinId {
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
/// access the corresponding regsiters.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) struct DynRegisters(DynPinId);
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
@ -392,11 +393,15 @@ impl DynPin {
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
pub fn configure_delay(
&mut self,
delay_1: bool,
delay_2: bool,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Output(_) => {
self.regs.delay(delay_1, delay_2);
Ok(self)
self.regs.configure_delay(delay_1, delay_2);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
}
@ -406,7 +411,7 @@ impl DynPin {
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
#[inline]
pub fn pulse_mode(
pub fn configure_pulse_mode(
&mut self,
enable: bool,
default_state: PinState,
@ -422,14 +427,14 @@ impl DynPin {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn filter_type(
pub fn configure_filter_type(
&mut self,
filter: FilterType,
clksel: FilterClkSel,
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) => {
self.regs.filter_type(filter, clksel);
self.regs.configure_filter_type(filter, clksel);
Ok(())
}
_ => Err(InvalidPinTypeError(self.mode)),
@ -437,7 +442,7 @@ impl DynPin {
}
#[inline]
pub fn interrupt_edge(
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
@ -446,7 +451,7 @@ impl DynPin {
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_edge(edge_type);
self.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(())
}
@ -455,7 +460,7 @@ impl DynPin {
}
#[inline]
pub fn interrupt_level(
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
@ -464,7 +469,7 @@ impl DynPin {
) -> Result<(), InvalidPinTypeError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self.regs.interrupt_level(level_type);
self.regs.configure_level_interrupt(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
Ok(())
}

View File

@ -89,6 +89,7 @@ use paste::paste;
//==================================================================================================
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
@ -96,12 +97,14 @@ pub enum InterruptEdge {
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PinState {
Low = 0,
High = 1,
@ -353,6 +356,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
}
}
#[inline]
pub fn id(&self) -> DynPinId {
self.inner.id()
}
@ -482,11 +486,6 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.inner.regs.write_pin(false)
}
#[inline]
pub(crate) fn _toggle_with_toggle_reg(&mut self) {
self.inner.regs.toggle();
}
#[inline]
pub(crate) fn _is_low(&self) -> bool {
!self.inner.regs.read_pin()
@ -599,7 +598,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_edge(edge_type);
self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
@ -610,7 +609,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_level(level_type);
self.inner.regs.configure_level_interrupt(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
}
@ -622,23 +621,34 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
/// - Delay 2: 2
/// - Delay 1 + Delay 2: 3
#[inline]
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
self.inner.regs.delay(delay_1, delay_2);
self
pub fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
self.inner.regs.configure_delay(delay_1, delay_2);
}
#[inline]
pub fn toggle_with_toggle_reg(&mut self) {
self._toggle_with_toggle_reg()
self.inner.regs.toggle()
}
#[deprecated(
since = "0.9.0",
note = "Please use the `configure_pulse_mode` method instead"
)]
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.configure_pulse_mode(enable, default_state);
}
/// See p.52 of the programmers guide for more information.
/// When configured for pulse mode, a given pin will set the non-default state for exactly
/// one clock cycle before returning to the configured default state
pub fn pulse_mode(&mut self, enable: bool, default_state: PinState) {
pub fn configure_pulse_mode(&mut self, enable: bool, default_state: PinState) {
self.inner.regs.pulse_mode(enable, default_state);
}
#[deprecated(
since = "0.9.0",
note = "Please use the `configure_edge_interrupt` method instead"
)]
pub fn interrupt_edge(
&mut self,
edge_type: InterruptEdge,
@ -646,18 +656,43 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_edge(edge_type);
self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
pub fn interrupt_level(
pub fn configure_edge_interrupt(
&mut self,
edge_type: InterruptEdge,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.configure_edge_interrupt(edge_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
#[deprecated(
since = "0.9.0",
note = "Please use the `configure_level_interrupt` method instead"
)]
pub fn level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.interrupt_level(level_type);
self.configure_level_interrupt(level_type, irq_cfg, syscfg, irqsel);
}
pub fn configure_level_interrupt(
&mut self,
level_type: InterruptLevel,
irq_cfg: InterruptConfig,
syscfg: Option<&mut Sysconfig>,
irqsel: Option<&mut Irqsel>,
) {
self.inner.regs.configure_level_interrupt(level_type);
self.irq_enb(irq_cfg, syscfg, irqsel);
}
}
@ -666,7 +701,7 @@ impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
/// See p.37 and p.38 of the programmers guide for more information.
#[inline]
pub fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.inner.regs.filter_type(filter, clksel);
self.inner.regs.configure_filter_type(filter, clksel);
}
}

View File

@ -240,7 +240,7 @@ pub(super) unsafe trait RegisterInterface {
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
/// When using edge mode, it is possible to generate interrupts on both edges as well
#[inline]
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
fn configure_edge_interrupt(&mut self, edge_type: InterruptEdge) {
unsafe {
self.port_reg()
.irq_sen()
@ -267,7 +267,7 @@ pub(super) unsafe trait RegisterInterface {
/// Configure which edge or level type triggers an interrupt
#[inline]
fn interrupt_level(&mut self, level: InterruptLevel) {
fn configure_level_interrupt(&mut self, level: InterruptLevel) {
unsafe {
self.port_reg()
.irq_sen()
@ -286,7 +286,7 @@ pub(super) unsafe trait RegisterInterface {
/// Only useful for input pins
#[inline]
fn filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
fn configure_filter_type(&mut self, filter: FilterType, clksel: FilterClkSel) {
self.iocfg_port().modify(|_, w| {
// Safety: Only write to register for this Pin ID
unsafe {
@ -349,7 +349,7 @@ pub(super) unsafe trait RegisterInterface {
}
/// Only useful for output pins
fn delay(&self, delay_1: bool, delay_2: bool) {
fn configure_delay(&mut self, delay_1: bool, delay_2: bool) {
let portreg = self.port_reg();
unsafe {
if delay_1 {

View File

@ -36,8 +36,6 @@ pub struct InvalidTimingParamsError;
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
//#[error("Invalid timing parameters")]
//InvalidTimingParams,
#[error("arbitration lost")]
ArbitrationLost,
#[error("nack address")]
@ -82,6 +80,7 @@ impl embedded_hal::i2c::Error for Error {
}
#[derive(Debug, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum I2cCmd {
Start = 0b00,
Stop = 0b10,
@ -252,6 +251,8 @@ impl Default for MasterConfig {
impl Sealed for MasterConfig {}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SlaveConfig {
pub tx_fe_mode: FifoEmptyMode,
pub rx_fe_mode: FifoEmptyMode,
@ -455,13 +456,6 @@ impl<I2c: Instance> I2cBase<I2c> {
}
}
// Unique mode to use the loopback functionality
// pub struct I2cLoopback<I2C> {
// i2c_base: I2cBase<I2C>,
// master_cfg: MasterConfig,
// slave_cfg: SlaveConfig,
// }
//==================================================================================================
// I2C Master
//==================================================================================================
@ -673,275 +667,6 @@ impl<I2c: Instance, Addr> I2cMaster<I2c, Addr> {
}
}
/*
macro_rules! i2c_master {
($($I2CX:path: ($i2cx:ident, $clk_enb:path),)+) => {
$(
impl<ADDR> I2cMaster<$I2CX, ADDR> {
pub fn $i2cx(
i2c: $I2CX,
cfg: MasterConfig,
sys_clk: impl Into<Hertz> + Copy,
speed_mode: I2cSpeed,
sys_cfg: Option<&mut pac::Sysconfig>,
) -> Self {
I2cMaster {
i2c_base: I2cBase::$i2cx(
i2c,
sys_clk,
speed_mode,
Some(&cfg),
None,
sys_cfg
),
_addr: PhantomData,
}
.enable_master()
}
#[inline]
pub fn cancel_transfer(&self) {
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) });
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.i2c_base.i2c.fifo_clr().write(|w| w.txfifo().set_bit());
}
#[inline]
pub fn clear_rx_fifo(&self) {
self.i2c_base.i2c.fifo_clr().write(|w| w.rxfifo().set_bit());
}
#[inline]
pub fn enable_master(self) -> Self {
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().set_bit());
self
}
#[inline]
pub fn disable_master(self) -> Self {
self.i2c_base.i2c.ctrl().modify(|_, w| w.enable().clear_bit());
self
}
#[inline(always)]
fn load_fifo(&self, word: u8) {
self.i2c_base
.i2c
.data()
.write(|w| unsafe { w.bits(word as u32) });
}
#[inline(always)]
fn read_fifo(&self) -> u8 {
self.i2c_base.i2c.data().read().bits() as u8
}
fn error_handler_write(&mut self, init_cmd: &I2cCmd) {
self.clear_tx_fifo();
if *init_cmd == I2cCmd::Start {
self.i2c_base.stop_cmd()
}
}
fn write_base(
&mut self,
addr: I2cAddress,
init_cmd: I2cCmd,
bytes: impl IntoIterator<Item = u8>,
) -> Result<(), Error> {
let mut iter = bytes.into_iter();
// Load address
let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr);
self.i2c_base.i2c.address().write(|w| unsafe {
w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit)
});
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(init_cmd as u32) });
let mut load_if_next_available = || {
if let Some(next_byte) = iter.next() {
self.load_fifo(next_byte);
}
};
loop {
let status_reader = self.i2c_base.i2c.status().read();
if status_reader.arblost().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::ArbitrationLost);
} else if status_reader.nackaddr().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::NackAddr);
} else if status_reader.nackdata().bit_is_set() {
self.error_handler_write(&init_cmd);
return Err(Error::NackData);
} else if status_reader.idle().bit_is_set() {
return Ok(());
} else {
while !status_reader.txnfull().bit_is_set() {
load_if_next_available();
}
}
}
}
fn write_from_buffer(
&mut self,
init_cmd: I2cCmd,
addr: I2cAddress,
output: &[u8],
) -> Result<(), Error> {
let len = output.len();
// It should theoretically possible to transfer larger data sizes by tracking
// the number of sent words and setting it to 0x7fe as soon as only that many
// bytes are remaining. However, large transfer like this are not common. This
// feature will therefore not be supported for now.
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Load number of words
self.i2c_base
.i2c
.words()
.write(|w| unsafe { w.bits(len as u32) });
let mut bytes = output.iter();
// FIFO has a depth of 16. We load slightly above the trigger level
// but not all of it because the transaction might fail immediately
const FILL_DEPTH: usize = 12;
// load the FIFO
for _ in 0..core::cmp::min(FILL_DEPTH, len) {
self.load_fifo(*bytes.next().unwrap());
}
self.write_base(addr, init_cmd, output.iter().cloned())
}
fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> {
let len = buffer.len();
// It should theoretically possible to transfer larger data sizes by tracking
// the number of sent words and setting it to 0x7fe as soon as only that many
// bytes are remaining. However, large transfer like this are not common. This
// feature will therefore not be supported for now.
if len > 0x7fe {
return Err(Error::DataTooLarge);
}
// Clear the receive FIFO
self.clear_rx_fifo();
// Load number of words
self.i2c_base
.i2c
.words()
.write(|w| unsafe { w.bits(len as u32) });
let (addr, addr_mode_bit) = match addr {
I2cAddress::Regular(addr) => (addr as u16, 0 << 15),
I2cAddress::TenBit(addr) => (addr, 1 << 15),
};
// Load address
self.i2c_base.i2c.address().write(|w| unsafe {
w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit)
});
let mut buf_iter = buffer.iter_mut();
let mut read_bytes = 0;
// Start receive transfer
self.i2c_base
.i2c
.cmd()
.write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) });
let mut read_if_next_available = || {
if let Some(next_byte) = buf_iter.next() {
*next_byte = self.read_fifo();
}
};
loop {
let status_reader = self.i2c_base.i2c.status().read();
if status_reader.arblost().bit_is_set() {
self.clear_rx_fifo();
return Err(Error::ArbitrationLost);
} else if status_reader.nackaddr().bit_is_set() {
self.clear_rx_fifo();
return Err(Error::NackAddr);
} else if status_reader.idle().bit_is_set() {
if read_bytes != len {
return Err(Error::InsufficientDataReceived);
}
return Ok(());
} else if status_reader.rxnempty().bit_is_set() {
read_if_next_available();
read_bytes += 1;
}
}
}
}
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, SevenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c for I2cMaster<$I2CX, SevenBitAddress> {
fn transaction(
&mut self,
address: SevenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_internal(I2cAddress::Regular(address), buf)?,
Operation::Write(buf) => self.write_from_buffer(
I2cCmd::StartWithStop,
I2cAddress::Regular(address),
buf,
)?,
}
}
Ok(())
}
}
impl embedded_hal::i2c::ErrorType for I2cMaster<$I2CX, TenBitAddress> {
type Error = Error;
}
impl embedded_hal::i2c::I2c<TenBitAddress> for I2cMaster<$I2CX, TenBitAddress> {
fn transaction(
&mut self,
address: TenBitAddress,
operations: &mut [Operation<'_>],
) -> Result<(), Self::Error> {
for operation in operations {
match operation {
Operation::Read(buf) => self.read_internal(I2cAddress::TenBit(address), buf)?,
Operation::Write(buf) => self.write_from_buffer(
I2cCmd::StartWithStop,
I2cAddress::TenBit(address),
buf,
)?,
}
}
Ok(())
}
}
)+
}
}
i2c_master!(
pac::I2ca: (i2ca, PeripheralClocks::I2c0),
pac::I2cb: (i2cb, PeripheralClocks::I2c1),
);
*/
//======================================================================================
// Embedded HAL I2C implementations
//======================================================================================

View File

@ -31,7 +31,7 @@ pub enum PortSel {
PortB,
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PeripheralSelect {
PortA = 0,
@ -54,6 +54,7 @@ pub enum PeripheralSelect {
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might want to
/// perform those steps themselves.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InterruptConfig {
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
pub id: pac::Interrupt,

View File

@ -33,6 +33,7 @@ use embedded_hal::spi::{Mode, MODE_0};
const FILL_DEPTH: usize = 12;
pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
pub const BMSKIPDATA_MASK: u32 = 1 << 30;
pub const DEFAULT_CLK_DIV: u16 = 2;
@ -288,6 +289,7 @@ pub trait TransferConfigProvider {
/// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfigWithHwcs<HwCs> {
pub hw_cs: Option<HwCs>,
pub cfg: TransferConfig,
@ -296,6 +298,7 @@ pub struct TransferConfigWithHwcs<HwCs> {
/// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor.
#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfig {
pub clk_cfg: Option<SpiClkConfig>,
pub mode: Option<Mode>,
@ -383,6 +386,8 @@ impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs>
}
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiConfig {
clk: SpiClkConfig,
// SPI mode configuration
@ -532,6 +537,7 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
pins: Pins,
}
#[inline(always)]
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
match mode {
embedded_hal::spi::MODE_0 => (false, false),
@ -575,6 +581,7 @@ impl SpiClkConfig {
}
#[derive(Debug, thiserror::Error)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SpiClkConfigError {
#[error("division by zero")]
DivIsZero,

View File

@ -30,7 +30,7 @@ For full details on the autgenerated API, please see the
- [`defmt`](https://defmt.ferrous-systems.com/): Add support for `defmt` by adding the
[`defmt::Format`](https://defmt.ferrous-systems.com/format) derive on many types.
- [`debug`]: Add `Debug` derives for various structures
- `debug`: Add `Debug` derives for various structures
## Regenerating the PAC