continue update package

This commit is contained in:
2024-09-19 16:37:59 +02:00
parent 4bf50bfa88
commit 8434ec872d
13 changed files with 846 additions and 202 deletions

View File

@ -26,6 +26,8 @@ use embedded_hal::spi::{Mode, MODE_0, MODE_1, MODE_2, MODE_3};
// FIFO has a depth of 16.
const FILL_DEPTH: usize = 12;
pub const DEFAULT_CLK_DIV: u16 = 2;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum HwChipSelectId {
Id0 = 0,
@ -221,6 +223,17 @@ hw_cs_pins!(
// SPIC
pub struct RomSck;
pub struct RomMosi;
pub struct RomMiso;
impl Sealed for RomSck {}
impl PinSck<pac::Spic> for RomSck {}
impl Sealed for RomMosi {}
impl PinMosi<pac::Spic> for RomMosi {}
impl Sealed for RomMiso {}
impl PinMiso<pac::Spic> for RomMiso {}
hw_cs_pins!(
pac::Spic, SpiPort::Portc:
(PB9, AltFunc3, HwChipSelectId::Id1, HwCs1SpiCPortB0),
@ -241,35 +254,28 @@ hw_cs_pins!(
// Config
//==================================================================================================
pub trait GenericTransferConfig {
pub trait TransferConfigProvider {
fn sod(&mut self, sod: bool);
fn blockmode(&mut self, blockmode: bool);
fn mode(&mut self, mode: Mode);
fn frequency(&mut self, spi_clk: Hertz);
fn clk_cfg(&mut self, clk_cfg: SpiClkConfig);
fn hw_cs_id(&self) -> u8;
}
/// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves
#[derive(Copy, Clone)]
pub struct TransferConfig<HwCs> {
pub spi_clk: Hertz,
pub mode: Mode,
/// This only works if the Slave Output Disable (SOD) bit of the [`SpiConfig`] is set to
/// false
#[derive(Copy, Clone, Debug)]
pub struct TransferConfigWithHwcs<HwCs> {
pub hw_cs: Option<HwCs>,
pub sod: bool,
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
/// duration of multiple data words
pub blockmode: bool,
pub cfg: CommonTransferConfig,
}
/// Type erased variant of the transfer configuration. This is required to avoid generics in
/// the SPI constructor.
pub struct ReducedTransferConfig {
pub spi_clk: Hertz,
pub mode: Mode,
#[derive(Copy, Clone, Debug)]
pub struct CommonTransferConfig {
pub clk_cfg: Option<SpiClkConfig>,
pub mode: Option<Mode>,
pub sod: bool,
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
@ -278,62 +284,67 @@ pub struct ReducedTransferConfig {
pub hw_cs: HwChipSelectId,
}
impl TransferConfig<NoneT> {
pub fn new_no_hw_cs(spi_clk: impl Into<Hertz>, mode: Mode, blockmode: bool, sod: bool) -> Self {
TransferConfig {
spi_clk: spi_clk.into(),
mode,
impl TransferConfigWithHwcs<NoneT> {
pub fn new_no_hw_cs(
clk_cfg: Option<SpiClkConfig>,
mode: Option<Mode>,
blockmode: bool,
sod: bool,
) -> Self {
TransferConfigWithHwcs {
hw_cs: None,
sod,
blockmode,
cfg: CommonTransferConfig {
clk_cfg,
mode,
sod,
blockmode,
hw_cs: HwChipSelectId::Invalid,
},
}
}
}
impl<HwCs: HwCsProvider> TransferConfig<HwCs> {
impl<HwCs: HwCsProvider> TransferConfigWithHwcs<HwCs> {
pub fn new(
spi_clk: impl Into<Hertz>,
mode: Mode,
clk_cfg: Option<SpiClkConfig>,
mode: Option<Mode>,
hw_cs: Option<HwCs>,
blockmode: bool,
sod: bool,
) -> Self {
TransferConfig {
spi_clk: spi_clk.into(),
mode,
TransferConfigWithHwcs {
hw_cs,
sod,
blockmode,
cfg: CommonTransferConfig {
clk_cfg,
mode,
sod,
blockmode,
hw_cs: HwCs::CS_ID,
},
}
}
pub fn downgrade(self) -> ReducedTransferConfig {
ReducedTransferConfig {
spi_clk: self.spi_clk,
mode: self.mode,
sod: self.sod,
blockmode: self.blockmode,
hw_cs: HwCs::CS_ID,
}
pub fn downgrade(self) -> CommonTransferConfig {
self.cfg
}
}
impl<HwCs: HwCsProvider> GenericTransferConfig for TransferConfig<HwCs> {
impl<HwCs: HwCsProvider> TransferConfigProvider for TransferConfigWithHwcs<HwCs> {
/// Slave Output Disable
fn sod(&mut self, sod: bool) {
self.sod = sod;
self.cfg.sod = sod;
}
fn blockmode(&mut self, blockmode: bool) {
self.blockmode = blockmode;
self.cfg.blockmode = blockmode;
}
fn mode(&mut self, mode: Mode) {
self.mode = mode;
self.cfg.mode = Some(mode);
}
fn frequency(&mut self, spi_clk: Hertz) {
self.spi_clk = spi_clk;
fn clk_cfg(&mut self, clk_cfg: SpiClkConfig) {
self.cfg.clk_cfg = Some(clk_cfg);
}
fn hw_cs_id(&self) -> u8 {
@ -341,26 +352,40 @@ impl<HwCs: HwCsProvider> GenericTransferConfig for TransferConfig<HwCs> {
}
}
#[derive(Default)]
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
pub struct SpiConfig {
/// Serial clock rate divider. Together with the CLKPRESCALE register, it determines
/// the SPI clock rate in master mode. 0 by default. Specifying a higher value
/// limits the maximum attainable SPI speed
pub scrdv: u8,
clk: SpiClkConfig,
/// By default, configure SPI for master mode (ms == false)
ms: bool,
/// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control
sod: bool,
pub slave_output_disable: bool,
/// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally
lbm: bool,
pub loopback_mode: bool,
/// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details
pub mdlycap: bool,
pub master_delayer_capture: bool,
}
impl Default for SpiConfig {
fn default() -> Self {
Self {
// Default value is definitely valid.
clk: SpiClkConfig::from_div(DEFAULT_CLK_DIV).unwrap(),
ms: Default::default(),
slave_output_disable: Default::default(),
loopback_mode: Default::default(),
master_delayer_capture: Default::default(),
}
}
}
impl SpiConfig {
pub fn loopback(mut self, enable: bool) -> Self {
self.lbm = enable;
self.loopback_mode = enable;
self
}
pub fn clk_cfg(mut self, clk_cfg: SpiClkConfig) -> Self {
self.clk = clk_cfg;
self
}
@ -370,7 +395,7 @@ impl SpiConfig {
}
pub fn slave_output_disable(mut self, sod: bool) -> Self {
self.sod = sod;
self.slave_output_disable = sod;
self
}
}
@ -419,6 +444,119 @@ pub struct Spi<SpiInstance, Pins, Word = u8> {
pins: Pins,
}
pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
match mode {
embedded_hal::spi::MODE_0 => (false, false),
embedded_hal::spi::MODE_1 => (false, true),
embedded_hal::spi::MODE_2 => (true, false),
embedded_hal::spi::MODE_3 => (true, true),
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SpiClkConfig {
prescale_val: u16,
scrdv: u8,
}
impl SpiClkConfig {
pub fn prescale_val(&self) -> u16 {
self.prescale_val
}
pub fn scrdv(&self) -> u8 {
self.scrdv
}
}
impl SpiClkConfig {
pub fn new(prescale_val: u16, scrdv: u8) -> Self {
Self {
prescale_val,
scrdv,
}
}
pub fn from_div(div: u16) -> Result<Self, SpiClkConfigError> {
spi_clk_config_from_div(div)
}
pub fn from_clk(sys_clk: impl Into<Hertz>, spi_clk: impl Into<Hertz>) -> Option<Self> {
clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
}
}
#[derive(Debug)]
pub enum SpiClkConfigError {
DivIsZero,
DivideValueNotEven,
ScrdvValueTooLarge,
}
#[inline]
pub fn spi_clk_config_from_div(mut div: u16) -> Result<SpiClkConfig, SpiClkConfigError> {
if div == 0 {
return Err(SpiClkConfigError::DivIsZero);
}
if div % 2 != 0 {
return Err(SpiClkConfigError::DivideValueNotEven);
}
let mut prescale_val = 0;
// find largest (even) prescale value that divides into div
for i in (2..=0xfe).rev().step_by(2) {
if div % i == 0 {
prescale_val = i;
break;
}
}
if prescale_val == 0 {
return Err(SpiClkConfigError::DivideValueNotEven);
}
div /= prescale_val;
if div > u8::MAX as u16 + 1 {
return Err(SpiClkConfigError::ScrdvValueTooLarge);
}
Ok(SpiClkConfig {
prescale_val,
scrdv: (div - 1) as u8,
})
}
#[inline]
pub fn clk_div_for_target_clock(
sys_clk: impl Into<Hertz>,
spi_clk: impl Into<Hertz>,
) -> Option<u16> {
let spi_clk = spi_clk.into();
let sys_clk = sys_clk.into();
if spi_clk > sys_clk {
return None;
}
// Step 1: Calculate raw divider.
let raw_div = sys_clk.raw() / spi_clk.raw();
let remainder = sys_clk.raw() % spi_clk.raw();
// Step 2: Round up if necessary.
let mut rounded_div = if remainder * 2 >= spi_clk.raw() {
raw_div + 1
} else {
raw_div
};
if rounded_div % 2 != 0 {
// Take slower clock conservatively.
rounded_div += 1;
}
if rounded_div > u16::MAX as u32 {
return None;
}
Some(rounded_div as u16)
}
// Re-export this so it can be used for the constructor
pub use crate::typelevel::NoneT;
@ -453,59 +591,56 @@ where
spi: SpiI,
pins: (Sck, Miso, Mosi),
spi_cfg: SpiConfig,
transfer_cfg: Option<&ReducedTransferConfig>,
transfer_cfg: Option<&CommonTransferConfig>,
) -> Self {
enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
let SpiConfig {
scrdv,
clk,
ms,
sod,
lbm,
mdlycap,
slave_output_disable,
loopback_mode,
master_delayer_capture,
} = spi_cfg;
let mut mode = MODE_0;
let mut clk_prescale = 0x02;
let mut init_mode = embedded_hal::spi::MODE_0;
let mut ss = 0;
let mut init_blockmode = false;
if let Some(transfer_cfg) = transfer_cfg {
mode = transfer_cfg.mode;
clk_prescale = sys_clk.into().raw() / (transfer_cfg.spi_clk.raw() * (scrdv as u32 + 1));
if let Some(mode) = transfer_cfg.mode {
init_mode = mode;
}
if transfer_cfg.hw_cs != HwChipSelectId::Invalid {
ss = transfer_cfg.hw_cs as u8;
}
init_blockmode = transfer_cfg.blockmode;
}
let (cpo_bit, cph_bit) = match mode {
MODE_0 => (false, false),
MODE_1 => (false, true),
MODE_2 => (true, false),
MODE_3 => (true, true),
};
let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(init_mode);
spi.ctrl0().write(|w| {
unsafe {
w.size().bits(Word::word_reg());
w.scrdv().bits(scrdv);
w.scrdv().bits(clk.scrdv);
// Clear clock phase and polarity. Will be set to correct value for each
// transfer
w.spo().bit(cpo_bit);
w.sph().bit(cph_bit)
}
});
spi.ctrl1().write(|w| {
w.lbm().bit(lbm);
w.sod().bit(sod);
w.lbm().bit(loopback_mode);
w.sod().bit(slave_output_disable);
w.ms().bit(ms);
w.mdlycap().bit(mdlycap);
w.mdlycap().bit(master_delayer_capture);
w.blockmode().bit(init_blockmode);
unsafe { w.ss().bits(ss) }
});
spi.clkprescale()
.write(|w| unsafe { w.bits(clk.prescale_val as u32) });
spi.fifo_clr().write(|w| {
w.rxfifo().set_bit();
w.txfifo().set_bit()
});
spi.clkprescale().write(|w| unsafe { w.bits(clk_prescale) });
// Enable the peripheral as the last step as recommended in the
// programmers guide
spi.ctrl1().modify(|_, w| w.enable().set_bit());
@ -522,33 +657,33 @@ where
}
}
#[inline]
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
self.inner.cfg_clock(spi_clk);
}
delegate::delegate! {
to self.inner {
#[inline]
pub fn cfg_clock(&mut self, cfg: SpiClkConfig);
#[inline]
pub fn cfg_mode(&mut self, mode: Mode) {
self.inner.cfg_mode(mode);
#[inline]
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError>;
#[inline]
pub fn cfg_mode(&mut self, mode: Mode);
#[inline]
pub fn perid(&self) -> u32;
#[inline]
pub fn fill_word(&self) -> Word;
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(
&mut self, transfer_cfg: &TransferConfigWithHwcs<HwCs>
);
}
}
pub fn set_fill_word(&mut self, fill_word: Word) {
self.inner.fill_word = fill_word;
}
pub fn fill_word(&self) -> Word {
self.inner.fill_word
}
#[inline]
pub fn perid(&self) -> u32 {
self.inner.perid()
}
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(&mut self, transfer_cfg: &TransferConfig<HwCs>) {
self.inner.cfg_transfer(transfer_cfg);
}
/// Releases the SPI peripheral and associated pins
pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) {
(self.inner.spi, self.pins, self.inner.cfg)
@ -564,12 +699,20 @@ where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
#[inline]
pub fn cfg_clock(&mut self, spi_clk: impl Into<Hertz>) {
let clk_prescale =
self.sys_clk.raw() / (spi_clk.into().raw() * (self.cfg.scrdv as u32 + 1));
pub fn cfg_clock(&mut self, cfg: SpiClkConfig) {
self.spi
.ctrl0()
.modify(|_, w| unsafe { w.scrdv().bits(cfg.scrdv) });
self.spi
.clkprescale()
.write(|w| unsafe { w.bits(clk_prescale) });
.write(|w| unsafe { w.bits(cfg.prescale_val as u32) });
}
#[inline]
pub fn cfg_clock_from_div(&mut self, div: u16) -> Result<(), SpiClkConfigError> {
let val = spi_clk_config_from_div(div)?;
self.cfg_clock(val);
Ok(())
}
#[inline]
@ -586,6 +729,11 @@ where
});
}
#[inline]
pub fn fill_word(&self) -> Word {
self.fill_word
}
#[inline]
pub fn clear_tx_fifo(&self) {
self.spi.fifo_clr().write(|w| w.txfifo().set_bit());
@ -629,13 +777,17 @@ where
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiInstance>>(
&mut self,
transfer_cfg: &TransferConfig<HwCs>,
transfer_cfg: &TransferConfigWithHwcs<HwCs>,
) {
self.cfg_clock(transfer_cfg.spi_clk);
self.cfg_mode(transfer_cfg.mode);
self.blockmode = transfer_cfg.blockmode;
if let Some(trans_clk_div) = transfer_cfg.cfg.clk_cfg {
self.cfg_clock(trans_clk_div);
}
if let Some(mode) = transfer_cfg.cfg.mode {
self.cfg_mode(mode);
}
self.blockmode = transfer_cfg.cfg.blockmode;
self.spi.ctrl1().modify(|_, w| {
if transfer_cfg.sod {
if transfer_cfg.cfg.sod {
w.sod().set_bit();
} else if transfer_cfg.hw_cs.is_some() {
w.sod().clear_bit();
@ -645,7 +797,7 @@ where
} else {
w.sod().clear_bit();
}
if transfer_cfg.blockmode {
if transfer_cfg.cfg.blockmode {
w.blockmode().set_bit();
} else {
w.blockmode().clear_bit();
@ -682,34 +834,52 @@ where
.unwrap()
}
fn flush_internal(&self) {
let mut status_reg = self.spi.status().read();
while status_reg.tfe().bit_is_clear()
|| status_reg.rne().bit_is_set()
|| status_reg.busy().bit_is_set()
{
if status_reg.rne().bit_is_set() {
self.read_single_word();
}
status_reg = self.spi.status().read();
}
}
fn transfer_preparation(&self, words: &[Word]) -> Result<(), Infallible> {
if words.is_empty() {
return Ok(());
}
let mut status_reg = self.spi.status().read();
// Wait until all bytes have been transferred.
while status_reg.tfe().bit_is_clear() {
// Ignore all received read words.
if status_reg.rne().bit_is_set() {
self.clear_rx_fifo();
}
status_reg = self.spi.status().read();
}
// Ignore all received read words.
if status_reg.rne().bit_is_set() {
self.clear_rx_fifo();
}
self.flush_internal();
Ok(())
}
fn initial_send_fifo_pumping(&self, words: Option<&[Word]>) -> usize {
// Returns the actual bytes sent.
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
for _ in 0..core::cmp::min(FILL_DEPTH, words.map_or(0, |words| words.len())) {
self.send_blocking(words.map_or(self.fill_word, |words| words[current_write_idx]));
for _ in 0..core::cmp::min(FILL_DEPTH, words.len()) {
self.send_blocking(words[current_write_idx]);
current_write_idx += 1;
}
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().clear_bit())
}
current_write_idx
}
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
if self.blockmode {
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
}
// Fill the first half of the write FIFO
let mut current_write_idx = 0;
for _ in 0..core::cmp::min(FILL_DEPTH, send_len) {
self.send_blocking(self.fill_word);
current_write_idx += 1;
}
if self.blockmode {
@ -778,7 +948,7 @@ where
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.transfer_preparation(words)?;
let mut current_read_idx = 0;
let mut current_write_idx = self.initial_send_fifo_pumping(None);
let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
loop {
if current_write_idx < words.len() {
self.send_blocking(self.fill_word);
@ -797,7 +967,7 @@ where
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
self.transfer_preparation(words)?;
let mut current_write_idx = self.initial_send_fifo_pumping(Some(words));
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
while current_write_idx < words.len() {
self.send_blocking(words[current_write_idx]);
current_write_idx += 1;
@ -812,7 +982,7 @@ where
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.transfer_preparation(write)?;
let mut current_read_idx = 0;
let mut current_write_idx = self.initial_send_fifo_pumping(Some(write));
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write);
while current_read_idx < read.len() || current_write_idx < write.len() {
if current_write_idx < write.len() {
self.send_blocking(write[current_write_idx]);
@ -830,7 +1000,7 @@ where
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.transfer_preparation(words)?;
let mut current_read_idx = 0;
let mut current_write_idx = self.initial_send_fifo_pumping(Some(words));
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
while current_read_idx < words.len() || current_write_idx < words.len() {
if current_write_idx < words.len() {
@ -846,12 +1016,7 @@ where
}
fn flush(&mut self) -> Result<(), Self::Error> {
let status_reg = self.spi.status().read();
while status_reg.tfe().bit_is_clear() || status_reg.rne().bit_is_set() {
if status_reg.rne().bit_is_set() {
self.read_single_word();
}
}
self.flush_internal();
Ok(())
}
}
@ -877,23 +1042,13 @@ impl<
where
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
{
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.inner.read(words)
}
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
self.inner.write(words)
}
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
self.inner.transfer(read, write)
}
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
self.inner.transfer_in_place(words)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.inner.flush()
delegate::delegate! {
to self.inner {
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error>;
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error>;
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error>;
fn flush(&mut self) -> Result<(), Self::Error>;
}
}
}