This commit is contained in:
parent
8ca46b26c4
commit
dc77f3a129
10
bootloader/src/lib.rs
Normal file
10
bootloader/src/lib.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
/// Simple trait which makes swapping the NVM easier. NVMs only need to implement this interface.
|
||||||
|
pub trait NvmInterface {
|
||||||
|
fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Infallible>;
|
||||||
|
fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Infallible>;
|
||||||
|
fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, Infallible>;
|
||||||
|
}
|
@ -1,34 +1,15 @@
|
|||||||
//! Vorago bootloader which can boot from two images.
|
//! Vorago bootloader which can boot from two images.
|
||||||
//!
|
|
||||||
//! Bootloader memory map
|
|
||||||
//!
|
|
||||||
//! * <0x0> Bootloader start <code up to 0x3FFE bytes>
|
|
||||||
//! * <0x3FFE> Bootloader CRC <halfword>
|
|
||||||
//! * <0x4000> App image A start <code up to 0x1DFFC (~120K) bytes>
|
|
||||||
//! * <0x21FFC> App image A CRC check length <halfword>
|
|
||||||
//! * <0x21FFE> App image A CRC check value <halfword>
|
|
||||||
//! * <0x22000> App image B start <code up to 0x1DFFC (~120K) bytes>
|
|
||||||
//! * <0x3FFFC> App image B CRC check length <halfword>
|
|
||||||
//! * <0x3FFFE> App image B CRC check value <halfword>
|
|
||||||
//! * <0x40000> <end>
|
|
||||||
//!
|
|
||||||
//! As opposed to the Vorago example code, this bootloader assumes a 40 MHz external clock
|
|
||||||
//! but does not scale that clock up.
|
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
use bootloader::NvmInterface;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use crc::{Crc, CRC_16_IBM_3740};
|
use crc::{Crc, CRC_16_IBM_3740};
|
||||||
use embedded_hal_bus::spi::{ExclusiveDevice, NoDelay};
|
|
||||||
#[cfg(not(feature = "rtt-panic"))]
|
#[cfg(not(feature = "rtt-panic"))]
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
#[cfg(feature = "rtt-panic")]
|
#[cfg(feature = "rtt-panic")]
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{pac, time::Hertz};
|
||||||
pac,
|
|
||||||
spi::{RomMiso, RomMosi, RomSck, Spi, SpiClkConfig, SpiConfig},
|
|
||||||
time::Hertz,
|
|
||||||
};
|
|
||||||
use vorago_reb1::m95m01::M95M01;
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
|
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
|
||||||
@ -84,10 +65,22 @@ enum AppSel {
|
|||||||
B,
|
B,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complex type, but this is the price we pay for nice abstraction. It is also very explicit.
|
pub struct NvmWrapper(pub M95M01);
|
||||||
pub type Nvm = M95M01<
|
|
||||||
ExclusiveDevice<Spi<pac::Spic, (RomSck, RomMiso, RomMosi), u8>, dummy_pin::DummyPin, NoDelay>,
|
// Newtype pattern. We could now more easily swap the used NVM type.
|
||||||
>;
|
impl NvmInterface for NvmWrapper {
|
||||||
|
fn write(&mut self, address: u32, data: &[u8]) -> Result<(), core::convert::Infallible> {
|
||||||
|
self.0.write(address, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), core::convert::Infallible> {
|
||||||
|
self.0.read(address, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, core::convert::Infallible> {
|
||||||
|
self.0.verify(address, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
@ -98,17 +91,7 @@ fn main() -> ! {
|
|||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
|
||||||
let spi = Spi::new(
|
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
|
||||||
&mut dp.sysconfig,
|
|
||||||
CLOCK_FREQ,
|
|
||||||
dp.spic,
|
|
||||||
(RomSck, RomMiso, RomMosi),
|
|
||||||
// These values are taken from the vorago bootloader app, don't want to experiment here..
|
|
||||||
SpiConfig::default().clk_cfg(SpiClkConfig::new(2, 4)),
|
|
||||||
);
|
|
||||||
let mut nvm =
|
|
||||||
M95M01::new(ExclusiveDevice::new_no_delay(spi, dummy_pin::DummyPin::new_low()).unwrap())
|
|
||||||
.expect("creating NVM structure failed");
|
|
||||||
|
|
||||||
if FLASH_SELF {
|
if FLASH_SELF {
|
||||||
let mut first_four_bytes: [u8; 4] = [0; 4];
|
let mut first_four_bytes: [u8; 4] = [0; 4];
|
||||||
@ -153,6 +136,8 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut nvm = NvmWrapper(nvm);
|
||||||
|
|
||||||
// Check bootloader's CRC (and write it if blank)
|
// Check bootloader's CRC (and write it if blank)
|
||||||
check_own_crc(&dp.sysconfig, &cp, &mut nvm);
|
check_own_crc(&dp.sysconfig, &cp, &mut nvm);
|
||||||
|
|
||||||
@ -170,7 +155,7 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_own_crc(sysconfig: &pac::Sysconfig, cp: &cortex_m::Peripherals, nvm: &mut Nvm) {
|
fn check_own_crc(sysconfig: &pac::Sysconfig, cp: &cortex_m::Peripherals, nvm: &mut NvmWrapper) {
|
||||||
let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
|
let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
|
||||||
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic
|
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic
|
||||||
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
||||||
|
@ -81,7 +81,6 @@ fn main() -> ! {
|
|||||||
dp.spia,
|
dp.spia,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
spia.set_fill_word(FILL_WORD);
|
spia.set_fill_word(FILL_WORD);
|
||||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
@ -98,7 +97,6 @@ fn main() -> ! {
|
|||||||
dp.spia,
|
dp.spia,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
spia.set_fill_word(FILL_WORD);
|
spia.set_fill_word(FILL_WORD);
|
||||||
spia_ref.borrow_mut().replace(spia.downgrade());
|
spia_ref.borrow_mut().replace(spia.downgrade());
|
||||||
@ -115,7 +113,6 @@ fn main() -> ! {
|
|||||||
dp.spib,
|
dp.spib,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
None,
|
|
||||||
);
|
);
|
||||||
spib.set_fill_word(FILL_WORD);
|
spib.set_fill_word(FILL_WORD);
|
||||||
spib_ref.borrow_mut().replace(spib.downgrade());
|
spib_ref.borrow_mut().replace(spib.downgrade());
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
//! API for the SPI peripheral
|
//! API for the SPI peripheral.
|
||||||
|
//!
|
||||||
|
//! The main abstraction provided by this module are the [Spi] and the [SpiBase] structure.
|
||||||
|
//! These provide the [embedded_hal::spi] traits, but also offer a low level interface
|
||||||
|
//! via the [SpiLowLevel] trait.
|
||||||
//!
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/spi.rs)
|
//! - [Blocking SPI example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/spi.rs)
|
||||||
|
//! - [REB1 ADC example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/max11519-adc.rs)
|
||||||
|
//! - [REB1 EEPROM library](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/src/m95m01.rs)
|
||||||
use crate::{
|
use crate::{
|
||||||
clock::enable_peripheral_clock,
|
clock::enable_peripheral_clock,
|
||||||
gpio::pin::{
|
gpio::pin::{
|
||||||
@ -225,9 +231,13 @@ hw_cs_pins!(
|
|||||||
|
|
||||||
// SPIC
|
// SPIC
|
||||||
|
|
||||||
|
// Dummy pin defintion for the ROM SCK.
|
||||||
pub struct RomSck;
|
pub struct RomSck;
|
||||||
|
// Dummy pin defintion for the ROM MOSI.
|
||||||
pub struct RomMosi;
|
pub struct RomMosi;
|
||||||
|
// Dummy pin defintion for the ROM MISO.
|
||||||
pub struct RomMiso;
|
pub struct RomMiso;
|
||||||
|
// Dummy pin defintion for the ROM chip select.
|
||||||
pub struct RomCs;
|
pub struct RomCs;
|
||||||
|
|
||||||
impl Sealed for RomSck {}
|
impl Sealed for RomSck {}
|
||||||
@ -372,6 +382,7 @@ pub struct SpiConfig {
|
|||||||
/// duration of multiple data words. Defaults to true.
|
/// duration of multiple data words. Defaults to true.
|
||||||
pub blockmode: bool,
|
pub blockmode: bool,
|
||||||
/// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty.
|
/// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty.
|
||||||
|
/// Currently enabled by default.
|
||||||
pub bmstall: bool,
|
pub bmstall: bool,
|
||||||
/// By default, configure SPI for master mode (ms == false)
|
/// By default, configure SPI for master mode (ms == false)
|
||||||
ms: bool,
|
ms: bool,
|
||||||
@ -460,6 +471,36 @@ impl WordProvider for u16 {
|
|||||||
// Spi
|
// Spi
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Low level access trait for the SPI peripheral.
|
||||||
|
pub trait SpiLowLevel {
|
||||||
|
/// Low level function to write a word to the SPI FIFO but also checks whether
|
||||||
|
/// there is actually data in the FIFO.
|
||||||
|
///
|
||||||
|
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
|
||||||
|
fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible>;
|
||||||
|
|
||||||
|
/// Low level function to write a word to the SPI FIFO without checking whether
|
||||||
|
/// there FIFO is full.
|
||||||
|
///
|
||||||
|
/// This does not necesarily mean there is a space in the FIFO available.
|
||||||
|
/// Use [Self::write_fifo] function to write a word into the FIFO reliably.
|
||||||
|
fn write_fifo_unchecked(&self, data: u32);
|
||||||
|
|
||||||
|
/// Low level function to read a word from the SPI FIFO. Must be preceeded by a
|
||||||
|
/// [Self::write_fifo] call.
|
||||||
|
///
|
||||||
|
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
|
||||||
|
fn read_fifo(&self) -> nb::Result<u32, Infallible>;
|
||||||
|
|
||||||
|
/// Low level function to read a word from from the SPI FIFO.
|
||||||
|
///
|
||||||
|
/// This does not necesarily mean there is a word in the FIFO available.
|
||||||
|
/// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
|
||||||
|
/// API.
|
||||||
|
/// You might also need to mask the value to ignore the BMSTART/BMSTOP bit.
|
||||||
|
fn read_fifo_unchecked(&self) -> u32;
|
||||||
|
}
|
||||||
|
|
||||||
pub struct SpiBase<SpiInstance, Word = u8> {
|
pub struct SpiBase<SpiInstance, Word = u8> {
|
||||||
spi: SpiInstance,
|
spi: SpiInstance,
|
||||||
cfg: SpiConfig,
|
cfg: SpiConfig,
|
||||||
@ -467,6 +508,7 @@ pub struct SpiBase<SpiInstance, Word = u8> {
|
|||||||
/// Fill word for read-only SPI transactions.
|
/// Fill word for read-only SPI transactions.
|
||||||
pub fill_word: Word,
|
pub fill_word: Word,
|
||||||
blockmode: bool,
|
blockmode: bool,
|
||||||
|
bmstall: bool,
|
||||||
word: PhantomData<Word>,
|
word: PhantomData<Word>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,150 +633,6 @@ pub fn clk_div_for_target_clock(
|
|||||||
// Re-export this so it can be used for the constructor
|
// Re-export this so it can be used for the constructor
|
||||||
pub use crate::typelevel::NoneT;
|
pub use crate::typelevel::NoneT;
|
||||||
|
|
||||||
impl<
|
|
||||||
SpiI: Instance,
|
|
||||||
Sck: PinSck<SpiI>,
|
|
||||||
Miso: PinMiso<SpiI>,
|
|
||||||
Mosi: PinMosi<SpiI>,
|
|
||||||
Word: WordProvider,
|
|
||||||
> Spi<SpiI, (Sck, Miso, Mosi), Word>
|
|
||||||
where
|
|
||||||
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
|
||||||
{
|
|
||||||
/// Create a new SPI struct
|
|
||||||
///
|
|
||||||
/// You can delete the pin type information by calling the
|
|
||||||
/// [`downgrade`](Self::downgrade) function
|
|
||||||
///
|
|
||||||
/// ## Arguments
|
|
||||||
/// * `spi` - SPI bus to use
|
|
||||||
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed
|
|
||||||
/// to ensure the pins can not be used for other purposes anymore
|
|
||||||
/// * `spi_cfg` - Configuration specific to the SPI bus
|
|
||||||
/// * `transfer_cfg` - Optional initial transfer configuration which includes
|
|
||||||
/// configuration which can change across individual SPI transfers like SPI mode
|
|
||||||
/// or SPI clock. If only one device is connected, this configuration only needs
|
|
||||||
/// to be done once.
|
|
||||||
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
|
|
||||||
pub fn new(
|
|
||||||
syscfg: &mut pac::Sysconfig,
|
|
||||||
sys_clk: impl Into<Hertz> + Copy,
|
|
||||||
spi: SpiI,
|
|
||||||
pins: (Sck, Miso, Mosi),
|
|
||||||
spi_cfg: SpiConfig,
|
|
||||||
) -> Self {
|
|
||||||
enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
|
||||||
let SpiConfig {
|
|
||||||
clk,
|
|
||||||
init_mode,
|
|
||||||
blockmode,
|
|
||||||
bmstall,
|
|
||||||
ms,
|
|
||||||
slave_output_disable,
|
|
||||||
loopback_mode,
|
|
||||||
master_delayer_capture,
|
|
||||||
} = spi_cfg;
|
|
||||||
|
|
||||||
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(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(loopback_mode);
|
|
||||||
w.sod().bit(slave_output_disable);
|
|
||||||
w.ms().bit(ms);
|
|
||||||
w.mdlycap().bit(master_delayer_capture);
|
|
||||||
w.blockmode().bit(blockmode);
|
|
||||||
w.bmstall().bit(bmstall);
|
|
||||||
unsafe { w.ss().bits(0) }
|
|
||||||
});
|
|
||||||
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()
|
|
||||||
});
|
|
||||||
// Enable the peripheral as the last step as recommended in the
|
|
||||||
// programmers guide
|
|
||||||
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
|
||||||
Spi {
|
|
||||||
inner: SpiBase {
|
|
||||||
spi,
|
|
||||||
cfg: spi_cfg,
|
|
||||||
sys_clk: sys_clk.into(),
|
|
||||||
fill_word: Default::default(),
|
|
||||||
blockmode,
|
|
||||||
word: PhantomData,
|
|
||||||
},
|
|
||||||
pins,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate::delegate! {
|
|
||||||
to self.inner {
|
|
||||||
#[inline]
|
|
||||||
pub fn cfg_clock(&mut self, cfg: SpiClkConfig);
|
|
||||||
|
|
||||||
#[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;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn spi(&self) -> &SpiI;
|
|
||||||
|
|
||||||
/// Configure the hardware chip select given a hardware chip select ID.
|
|
||||||
#[inline]
|
|
||||||
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId);
|
|
||||||
|
|
||||||
/// Configure the hardware chip select given a physical hardware CS pin.
|
|
||||||
#[inline]
|
|
||||||
pub fn cfg_hw_cs_with_pin<HwCs: OptionalHwCs<SpiI>>(&mut self, _hwcs: &HwCs);
|
|
||||||
|
|
||||||
/// Disables the hardware chip select functionality. This can be used when performing
|
|
||||||
/// external chip select handling, for example with GPIO pins.
|
|
||||||
#[inline]
|
|
||||||
pub fn cfg_hw_cs_disable(&mut self);
|
|
||||||
|
|
||||||
/// Utility function to configure all relevant transfer parameters in one go.
|
|
||||||
/// This is useful if multiple devices with different clock and mode configurations
|
|
||||||
/// are connected to one bus.
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Releases the SPI peripheral and associated pins
|
|
||||||
pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) {
|
|
||||||
(self.inner.spi, self.pins, self.inner.cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn downgrade(self) -> SpiBase<SpiI, Word> {
|
|
||||||
self.inner
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<SpiInstance: Instance, Word: WordProvider> SpiBase<SpiInstance, Word>
|
impl<SpiInstance: Instance, Word: WordProvider> SpiBase<SpiInstance, Word>
|
||||||
where
|
where
|
||||||
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||||
@ -855,34 +753,6 @@ where
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a word to the slave
|
|
||||||
#[inline(always)]
|
|
||||||
fn send_blocking(&self, data: u32) {
|
|
||||||
// TODO: Upper limit for wait cycles to avoid complete hangups?
|
|
||||||
while self.spi.status().read().tnf().bit_is_clear() {}
|
|
||||||
self.send(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn send(&self, data: u32) {
|
|
||||||
self.spi.data().write(|w| unsafe { w.bits(data) });
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a word from the slave. Must be preceeded by a [`send`](Self::send) call
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_blocking(&self) -> Word {
|
|
||||||
// TODO: Upper limit for wait cycles to avoid complete hangups?
|
|
||||||
while self.spi.status().read().rne().bit_is_clear() {}
|
|
||||||
self.read_single_word()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn read_single_word(&self) -> Word {
|
|
||||||
(self.spi.data().read().bits() & Word::MASK)
|
|
||||||
.try_into()
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_internal(&self) {
|
fn flush_internal(&self) {
|
||||||
let mut status_reg = self.spi.status().read();
|
let mut status_reg = self.spi.status().read();
|
||||||
while status_reg.tfe().bit_is_clear()
|
while status_reg.tfe().bit_is_clear()
|
||||||
@ -890,7 +760,7 @@ where
|
|||||||
|| status_reg.busy().bit_is_set()
|
|| status_reg.busy().bit_is_set()
|
||||||
{
|
{
|
||||||
if status_reg.rne().bit_is_set() {
|
if status_reg.rne().bit_is_set() {
|
||||||
self.read_single_word();
|
self.read_fifo_unchecked();
|
||||||
}
|
}
|
||||||
status_reg = self.spi.status().read();
|
status_reg = self.spi.status().read();
|
||||||
}
|
}
|
||||||
@ -904,18 +774,20 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the actual bytes sent.
|
// The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
|
||||||
|
// initialization. Returns the amount of written bytes.
|
||||||
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
|
fn initial_send_fifo_pumping_with_words(&self, words: &[Word]) -> usize {
|
||||||
if self.blockmode {
|
if self.blockmode {
|
||||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
||||||
}
|
}
|
||||||
// Fill the first half of the write FIFO
|
// Fill the first half of the write FIFO
|
||||||
let mut current_write_idx = 0;
|
let mut current_write_idx = 0;
|
||||||
for _ in 0..core::cmp::min(FILL_DEPTH, words.len()) {
|
let smaller_idx = core::cmp::min(FILL_DEPTH, words.len());
|
||||||
if current_write_idx == words.len() - 1 {
|
for _ in 0..smaller_idx {
|
||||||
self.send_blocking(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
|
if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
|
||||||
|
self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
|
||||||
} else {
|
} else {
|
||||||
self.send_blocking(words[current_write_idx].into());
|
self.write_fifo_unchecked(words[current_write_idx].into());
|
||||||
}
|
}
|
||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
@ -925,17 +797,20 @@ where
|
|||||||
current_write_idx
|
current_write_idx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
|
||||||
|
// initialization.
|
||||||
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
|
fn initial_send_fifo_pumping_with_fill_words(&self, send_len: usize) -> usize {
|
||||||
if self.blockmode {
|
if self.blockmode {
|
||||||
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
self.spi.ctrl1().modify(|_, w| w.mtxpause().set_bit())
|
||||||
}
|
}
|
||||||
// Fill the first half of the write FIFO
|
// Fill the first half of the write FIFO
|
||||||
let mut current_write_idx = 0;
|
let mut current_write_idx = 0;
|
||||||
for _ in 0..core::cmp::min(FILL_DEPTH, send_len) {
|
let smaller_idx = core::cmp::min(FILL_DEPTH, send_len);
|
||||||
if current_write_idx == send_len - 1 {
|
for _ in 0..smaller_idx {
|
||||||
self.send_blocking(self.fill_word.into() | BMSTART_BMSTOP_MASK);
|
if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
|
||||||
|
self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK);
|
||||||
} else {
|
} else {
|
||||||
self.send_blocking(self.fill_word.into());
|
self.write_fifo_unchecked(self.fill_word.into());
|
||||||
}
|
}
|
||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
@ -946,51 +821,35 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Changing the word size also requires a type conversion
|
impl<SpiInstance: Instance, Word: WordProvider> SpiLowLevel for SpiBase<SpiInstance, Word>
|
||||||
impl<SpiI: Instance, Sck: PinSck<SpiI>, Miso: PinMiso<SpiI>, Mosi: PinMosi<SpiI>>
|
where
|
||||||
From<Spi<SpiI, (Sck, Miso, Mosi), u8>> for Spi<SpiI, (Sck, Miso, Mosi), u16>
|
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||||
{
|
{
|
||||||
fn from(old_spi: Spi<SpiI, (Sck, Miso, Mosi), u8>) -> Self {
|
#[inline(always)]
|
||||||
old_spi
|
fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible> {
|
||||||
.inner
|
if self.spi.status().read().tnf().bit_is_clear() {
|
||||||
.spi
|
return Err(nb::Error::WouldBlock);
|
||||||
.ctrl0()
|
|
||||||
.modify(|_, w| unsafe { w.size().bits(WordSize::SixteenBits as u8) });
|
|
||||||
Spi {
|
|
||||||
inner: SpiBase {
|
|
||||||
spi: old_spi.inner.spi,
|
|
||||||
cfg: old_spi.inner.cfg,
|
|
||||||
blockmode: old_spi.inner.blockmode,
|
|
||||||
fill_word: Default::default(),
|
|
||||||
sys_clk: old_spi.inner.sys_clk,
|
|
||||||
word: PhantomData,
|
|
||||||
},
|
|
||||||
pins: old_spi.pins,
|
|
||||||
}
|
}
|
||||||
|
self.write_fifo_unchecked(data);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Changing the word size also requires a type conversion
|
#[inline(always)]
|
||||||
impl<SpiI: Instance, Sck: PinSck<SpiI>, Miso: PinMiso<SpiI>, Mosi: PinMosi<SpiI>>
|
fn write_fifo_unchecked(&self, data: u32) {
|
||||||
From<Spi<SpiI, (Sck, Miso, Mosi), u16>> for Spi<SpiI, (Sck, Miso, Mosi), u8>
|
self.spi.data().write(|w| unsafe { w.bits(data) });
|
||||||
{
|
}
|
||||||
fn from(old_spi: Spi<SpiI, (Sck, Miso, Mosi), u16>) -> Self {
|
|
||||||
old_spi
|
#[inline(always)]
|
||||||
.inner
|
fn read_fifo(&self) -> nb::Result<u32, Infallible> {
|
||||||
.spi
|
if self.spi.status().read().rne().bit_is_clear() {
|
||||||
.ctrl0()
|
return Err(nb::Error::WouldBlock);
|
||||||
.modify(|_, w| unsafe { w.size().bits(WordSize::EightBits as u8) });
|
|
||||||
Spi {
|
|
||||||
inner: SpiBase {
|
|
||||||
spi: old_spi.inner.spi,
|
|
||||||
cfg: old_spi.inner.cfg,
|
|
||||||
blockmode: old_spi.inner.blockmode,
|
|
||||||
sys_clk: old_spi.inner.sys_clk,
|
|
||||||
fill_word: Default::default(),
|
|
||||||
word: PhantomData,
|
|
||||||
},
|
|
||||||
pins: old_spi.pins,
|
|
||||||
}
|
}
|
||||||
|
Ok(self.read_fifo_unchecked())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_fifo_unchecked(&self) -> u32 {
|
||||||
|
self.spi.data().read().bits()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1003,19 +862,21 @@ where
|
|||||||
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||||
{
|
{
|
||||||
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||||
//self.transfer_preparation(words)?;
|
self.transfer_preparation(words)?;
|
||||||
let mut current_read_idx = 0;
|
let mut current_read_idx = 0;
|
||||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
|
let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
|
||||||
loop {
|
loop {
|
||||||
if current_read_idx < words.len() {
|
if current_read_idx < words.len() {
|
||||||
words[current_read_idx] = self.read_blocking();
|
words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
current_read_idx += 1;
|
current_read_idx += 1;
|
||||||
}
|
}
|
||||||
if current_write_idx < words.len() {
|
if current_write_idx < words.len() {
|
||||||
if current_write_idx == words.len() - 1 {
|
if current_write_idx == words.len() - 1 && self.bmstall {
|
||||||
self.send_blocking(self.fill_word.into() | BMSTART_BMSTOP_MASK);
|
nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))?;
|
||||||
} else {
|
} else {
|
||||||
self.send_blocking(self.fill_word.into());
|
nb::block!(self.write_fifo(self.fill_word.into()))?;
|
||||||
}
|
}
|
||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
@ -1027,13 +888,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
|
fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
|
||||||
// self.transfer_preparation(words)?;
|
self.transfer_preparation(words)?;
|
||||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
|
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
|
||||||
while current_write_idx < words.len() {
|
while current_write_idx < words.len() {
|
||||||
if current_write_idx == words.len() - 1 {
|
if current_write_idx == words.len() - 1 && self.bmstall {
|
||||||
self.send_blocking(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
|
nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))?;
|
||||||
} else {
|
} else {
|
||||||
self.send_blocking(words[current_write_idx].into());
|
nb::block!(self.write_fifo(words[current_write_idx].into()))?;
|
||||||
}
|
}
|
||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
// Ignore received words.
|
// Ignore received words.
|
||||||
@ -1045,20 +906,24 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
|
fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
|
||||||
//self.transfer_preparation(write)?;
|
self.transfer_preparation(write)?;
|
||||||
let mut current_read_idx = 0;
|
let mut current_read_idx = 0;
|
||||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(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() {
|
while current_read_idx < read.len() || current_write_idx < write.len() {
|
||||||
if current_write_idx < write.len() {
|
if current_write_idx < write.len() {
|
||||||
if current_write_idx == write.len() - 1 {
|
if current_write_idx == write.len() - 1 && self.bmstall {
|
||||||
self.send_blocking(write[current_write_idx].into() | BMSTART_BMSTOP_MASK);
|
nb::block!(
|
||||||
|
self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK)
|
||||||
|
)?;
|
||||||
} else {
|
} else {
|
||||||
self.send_blocking(write[current_write_idx].into());
|
nb::block!(self.write_fifo(write[current_write_idx].into()))?;
|
||||||
}
|
}
|
||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
if current_read_idx < read.len() {
|
if current_read_idx < read.len() {
|
||||||
read[current_read_idx] = self.read_blocking();
|
read[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
current_read_idx += 1;
|
current_read_idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1067,21 +932,25 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
|
||||||
//self.transfer_preparation(words)?;
|
self.transfer_preparation(words)?;
|
||||||
let mut current_read_idx = 0;
|
let mut current_read_idx = 0;
|
||||||
let mut current_write_idx = self.initial_send_fifo_pumping_with_words(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() {
|
while current_read_idx < words.len() || current_write_idx < words.len() {
|
||||||
if current_write_idx < words.len() {
|
if current_write_idx < words.len() {
|
||||||
if current_write_idx == words.len() - 1 {
|
if current_write_idx == words.len() - 1 && self.bmstall {
|
||||||
self.send_blocking(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
|
nb::block!(
|
||||||
|
self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK)
|
||||||
|
)?;
|
||||||
} else {
|
} else {
|
||||||
self.send_blocking(words[current_write_idx].into());
|
nb::block!(self.write_fifo(words[current_write_idx].into()))?;
|
||||||
}
|
}
|
||||||
current_write_idx += 1;
|
current_write_idx += 1;
|
||||||
}
|
}
|
||||||
if current_read_idx < words.len() && current_read_idx < current_write_idx {
|
if current_read_idx < words.len() && current_read_idx < current_write_idx {
|
||||||
words[current_read_idx] = self.read_blocking();
|
words[current_read_idx] = (nb::block!(self.read_fifo())? & Word::MASK)
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
current_read_idx += 1;
|
current_read_idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1094,6 +963,199 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
SpiI: Instance,
|
||||||
|
Sck: PinSck<SpiI>,
|
||||||
|
Miso: PinMiso<SpiI>,
|
||||||
|
Mosi: PinMosi<SpiI>,
|
||||||
|
Word: WordProvider,
|
||||||
|
> Spi<SpiI, (Sck, Miso, Mosi), Word>
|
||||||
|
where
|
||||||
|
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
/// Create a new SPI struct
|
||||||
|
///
|
||||||
|
/// You can delete the pin type information by calling the
|
||||||
|
/// [`downgrade`](Self::downgrade) function
|
||||||
|
///
|
||||||
|
/// ## Arguments
|
||||||
|
/// * `syscfg` - Can be passed optionally to enable the peripheral clock
|
||||||
|
/// * `sys_clk` - System clock
|
||||||
|
/// * `spi` - SPI bus to use
|
||||||
|
/// * `pins` - Pins to be used for SPI transactions. These pins are consumed
|
||||||
|
/// to ensure the pins can not be used for other purposes anymore
|
||||||
|
/// * `spi_cfg` - Configuration specific to the SPI bus
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut pac::Sysconfig,
|
||||||
|
sys_clk: impl Into<Hertz>,
|
||||||
|
spi: SpiI,
|
||||||
|
pins: (Sck, Miso, Mosi),
|
||||||
|
spi_cfg: SpiConfig,
|
||||||
|
) -> Self {
|
||||||
|
enable_peripheral_clock(syscfg, SpiI::PERIPH_SEL);
|
||||||
|
let SpiConfig {
|
||||||
|
clk,
|
||||||
|
init_mode,
|
||||||
|
blockmode,
|
||||||
|
bmstall,
|
||||||
|
ms,
|
||||||
|
slave_output_disable,
|
||||||
|
loopback_mode,
|
||||||
|
master_delayer_capture,
|
||||||
|
} = spi_cfg;
|
||||||
|
|
||||||
|
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(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(loopback_mode);
|
||||||
|
w.sod().bit(slave_output_disable);
|
||||||
|
w.ms().bit(ms);
|
||||||
|
w.mdlycap().bit(master_delayer_capture);
|
||||||
|
w.blockmode().bit(blockmode);
|
||||||
|
w.bmstall().bit(bmstall);
|
||||||
|
unsafe { w.ss().bits(0) }
|
||||||
|
});
|
||||||
|
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()
|
||||||
|
});
|
||||||
|
// Enable the peripheral as the last step as recommended in the
|
||||||
|
// programmers guide
|
||||||
|
spi.ctrl1().modify(|_, w| w.enable().set_bit());
|
||||||
|
Spi {
|
||||||
|
inner: SpiBase {
|
||||||
|
spi,
|
||||||
|
cfg: spi_cfg,
|
||||||
|
sys_clk: sys_clk.into(),
|
||||||
|
fill_word: Default::default(),
|
||||||
|
bmstall,
|
||||||
|
blockmode,
|
||||||
|
word: PhantomData,
|
||||||
|
},
|
||||||
|
pins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate::delegate! {
|
||||||
|
to self.inner {
|
||||||
|
#[inline]
|
||||||
|
pub fn cfg_clock(&mut self, cfg: SpiClkConfig);
|
||||||
|
|
||||||
|
#[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;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn spi(&self) -> &SpiI;
|
||||||
|
|
||||||
|
/// Configure the hardware chip select given a hardware chip select ID.
|
||||||
|
#[inline]
|
||||||
|
pub fn cfg_hw_cs(&mut self, hw_cs: HwChipSelectId);
|
||||||
|
|
||||||
|
/// Configure the hardware chip select given a physical hardware CS pin.
|
||||||
|
#[inline]
|
||||||
|
pub fn cfg_hw_cs_with_pin<HwCs: OptionalHwCs<SpiI>>(&mut self, _hwcs: &HwCs);
|
||||||
|
|
||||||
|
/// Disables the hardware chip select functionality. This can be used when performing
|
||||||
|
/// external chip select handling, for example with GPIO pins.
|
||||||
|
#[inline]
|
||||||
|
pub fn cfg_hw_cs_disable(&mut self);
|
||||||
|
|
||||||
|
/// Utility function to configure all relevant transfer parameters in one go.
|
||||||
|
/// This is useful if multiple devices with different clock and mode configurations
|
||||||
|
/// are connected to one bus.
|
||||||
|
pub fn cfg_transfer<HwCs: OptionalHwCs<SpiI>>(
|
||||||
|
&mut self, transfer_cfg: &TransferConfigWithHwcs<HwCs>
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Low level function to write a word to the SPI FIFO but also checks whether
|
||||||
|
/// there is actually data in the FIFO.
|
||||||
|
///
|
||||||
|
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible>;
|
||||||
|
|
||||||
|
/// Low level function to write a word to the SPI FIFO.
|
||||||
|
///
|
||||||
|
/// This does not necesarily mean there is a space in the FIFO available.
|
||||||
|
/// Use [Self::write_fifo] function to write a word into the FIFO reliably using the
|
||||||
|
/// [nb] API.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn write_fifo_unchecked(&self, data: u32);
|
||||||
|
|
||||||
|
/// Low level function to read a word from the SPI FIFO. Must be preceeded by a
|
||||||
|
/// [Self::write_fifo] call.
|
||||||
|
///
|
||||||
|
/// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_fifo(&self) -> nb::Result<u32, Infallible>;
|
||||||
|
|
||||||
|
/// Low level function to read a word from from the SPI FIFO.
|
||||||
|
///
|
||||||
|
/// This does not necesarily mean there is a word in the FIFO available.
|
||||||
|
/// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
|
||||||
|
/// API.
|
||||||
|
/// You might also need to mask the value to ignore the BMSTART/BMSTOP bit.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read_fifo_unchecked(&self) -> u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_fill_word(&mut self, fill_word: Word) {
|
||||||
|
self.inner.fill_word = fill_word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Releases the SPI peripheral and associated pins
|
||||||
|
pub fn release(self) -> (SpiI, (Sck, Miso, Mosi), SpiConfig) {
|
||||||
|
(self.inner.spi, self.pins, self.inner.cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn downgrade(self) -> SpiBase<SpiI, Word> {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
SpiI: Instance,
|
||||||
|
Sck: PinSck<SpiI>,
|
||||||
|
Miso: PinMiso<SpiI>,
|
||||||
|
Mosi: PinMosi<SpiI>,
|
||||||
|
Word: WordProvider,
|
||||||
|
> SpiLowLevel for Spi<SpiI, (Sck, Miso, Mosi), Word>
|
||||||
|
where
|
||||||
|
<Word as TryFrom<u32>>::Error: core::fmt::Debug,
|
||||||
|
{
|
||||||
|
delegate::delegate! {
|
||||||
|
to self.inner {
|
||||||
|
fn write_fifo(&self, data: u32) -> nb::Result<(), Infallible>;
|
||||||
|
fn write_fifo_unchecked(&self, data: u32);
|
||||||
|
fn read_fifo(&self) -> nb::Result<u32, Infallible>;
|
||||||
|
fn read_fifo_unchecked(&self) -> u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<
|
impl<
|
||||||
SpiI: Instance,
|
SpiI: Instance,
|
||||||
Word: WordProvider,
|
Word: WordProvider,
|
||||||
@ -1125,3 +1187,53 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Changing the word size also requires a type conversion
|
||||||
|
impl<SpiI: Instance, Sck: PinSck<SpiI>, Miso: PinMiso<SpiI>, Mosi: PinMosi<SpiI>>
|
||||||
|
From<Spi<SpiI, (Sck, Miso, Mosi), u8>> for Spi<SpiI, (Sck, Miso, Mosi), u16>
|
||||||
|
{
|
||||||
|
fn from(old_spi: Spi<SpiI, (Sck, Miso, Mosi), u8>) -> Self {
|
||||||
|
old_spi
|
||||||
|
.inner
|
||||||
|
.spi
|
||||||
|
.ctrl0()
|
||||||
|
.modify(|_, w| unsafe { w.size().bits(WordSize::SixteenBits as u8) });
|
||||||
|
Spi {
|
||||||
|
inner: SpiBase {
|
||||||
|
spi: old_spi.inner.spi,
|
||||||
|
cfg: old_spi.inner.cfg,
|
||||||
|
blockmode: old_spi.inner.blockmode,
|
||||||
|
fill_word: Default::default(),
|
||||||
|
bmstall: old_spi.inner.bmstall,
|
||||||
|
sys_clk: old_spi.inner.sys_clk,
|
||||||
|
word: PhantomData,
|
||||||
|
},
|
||||||
|
pins: old_spi.pins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Changing the word size also requires a type conversion
|
||||||
|
impl<SpiI: Instance, Sck: PinSck<SpiI>, Miso: PinMiso<SpiI>, Mosi: PinMosi<SpiI>>
|
||||||
|
From<Spi<SpiI, (Sck, Miso, Mosi), u16>> for Spi<SpiI, (Sck, Miso, Mosi), u8>
|
||||||
|
{
|
||||||
|
fn from(old_spi: Spi<SpiI, (Sck, Miso, Mosi), u16>) -> Self {
|
||||||
|
old_spi
|
||||||
|
.inner
|
||||||
|
.spi
|
||||||
|
.ctrl0()
|
||||||
|
.modify(|_, w| unsafe { w.size().bits(WordSize::EightBits as u8) });
|
||||||
|
Spi {
|
||||||
|
inner: SpiBase {
|
||||||
|
spi: old_spi.inner.spi,
|
||||||
|
cfg: old_spi.inner.cfg,
|
||||||
|
blockmode: old_spi.inner.blockmode,
|
||||||
|
bmstall: old_spi.inner.bmstall,
|
||||||
|
sys_clk: old_spi.inner.sys_clk,
|
||||||
|
fill_word: Default::default(),
|
||||||
|
word: PhantomData,
|
||||||
|
},
|
||||||
|
pins: old_spi.pins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::spi::SpiBus;
|
use embedded_hal::spi::{SpiBus, MODE_3};
|
||||||
use embedded_hal::{delay::DelayNs, digital::OutputPin};
|
use embedded_hal::{delay::DelayNs, digital::OutputPin};
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
@ -14,7 +14,7 @@ use va108xx_hal::{
|
|||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
spi::{Spi, SpiConfig, TransferConfigWithHwcs},
|
spi::{Spi, SpiConfig},
|
||||||
timer::set_up_ms_delay_provider,
|
timer::set_up_ms_delay_provider,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -32,7 +32,6 @@ fn main() -> ! {
|
|||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
let mut delay = set_up_ms_delay_provider(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let spi_cfg = SpiConfig::default();
|
|
||||||
let (sck, mosi, miso) = (
|
let (sck, mosi, miso) = (
|
||||||
pinsa.pa20.into_funsel_2(),
|
pinsa.pa20.into_funsel_2(),
|
||||||
pinsa.pa19.into_funsel_2(),
|
pinsa.pa19.into_funsel_2(),
|
||||||
@ -46,21 +45,20 @@ fn main() -> ! {
|
|||||||
.set_high()
|
.set_high()
|
||||||
.expect("Setting ADC chip select high failed");
|
.expect("Setting ADC chip select high failed");
|
||||||
|
|
||||||
let transfer_cfg = TransferConfigWithHwcs::new(
|
let spi_cfg = SpiConfig::default()
|
||||||
Some(SpiClkConfig::from_clk(50.MHz(), 1.MHz()).expect("creating SPI clock config failed")),
|
.clk_cfg(
|
||||||
Some(embedded_hal::spi::MODE_3),
|
SpiClkConfig::from_clk(50.MHz(), 1.MHz()).expect("creating SPI clock config failed"),
|
||||||
Some(cs_pin),
|
)
|
||||||
false,
|
.mode(MODE_3)
|
||||||
true,
|
.slave_output_disable(true);
|
||||||
);
|
|
||||||
let mut spi = Spi::new(
|
let mut spi = Spi::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
dp.spib,
|
dp.spib,
|
||||||
(sck, miso, mosi),
|
(sck, miso, mosi),
|
||||||
spi_cfg,
|
spi_cfg,
|
||||||
Some(&transfer_cfg.downgrade()),
|
|
||||||
);
|
);
|
||||||
|
spi.cfg_hw_cs_with_pin(&cs_pin);
|
||||||
|
|
||||||
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
let mut tx_rx_buf: [u8; 3] = [0; 3];
|
||||||
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
tx_rx_buf[0] = READ_MASK | DEVID_REG;
|
||||||
|
@ -9,7 +9,7 @@ use core::convert::Infallible;
|
|||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal::spi::{SpiBus, SpiDevice};
|
use embedded_hal::spi::{SpiBus, SpiDevice, MODE_0};
|
||||||
use embedded_hal::{delay::DelayNs, spi};
|
use embedded_hal::{delay::DelayNs, spi};
|
||||||
use max116xx_10bit::VoltageRefMode;
|
use max116xx_10bit::VoltageRefMode;
|
||||||
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
use max116xx_10bit::{AveragingConversions, AveragingResults};
|
||||||
@ -21,7 +21,7 @@ use va108xx_hal::{
|
|||||||
gpio::PinsA,
|
gpio::PinsA,
|
||||||
pac::{self, interrupt},
|
pac::{self, interrupt},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
spi::{Spi, SpiBase, SpiConfig, TransferConfigWithHwcs},
|
spi::{Spi, SpiBase, SpiConfig},
|
||||||
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg},
|
timer::{default_ms_irq_handler, set_up_ms_tick, DelayMs, IrqCfg},
|
||||||
};
|
};
|
||||||
use va108xx_hal::{port_mux, FunSel, PortSel};
|
use va108xx_hal::{port_mux, FunSel, PortSel};
|
||||||
@ -124,7 +124,10 @@ fn main() -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
let pinsa = PinsA::new(&mut dp.sysconfig, None, dp.porta);
|
||||||
let spi_cfg = SpiConfig::default().clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap());
|
let spi_cfg = SpiConfig::default()
|
||||||
|
.clk_cfg(SpiClkConfig::from_clk(SYS_CLK, 3.MHz()).unwrap())
|
||||||
|
.mode(MODE_0)
|
||||||
|
.blockmode(true);
|
||||||
let (sck, mosi, miso) = (
|
let (sck, mosi, miso) = (
|
||||||
pinsa.pa20.into_funsel_2(),
|
pinsa.pa20.into_funsel_2(),
|
||||||
pinsa.pa19.into_funsel_2(),
|
pinsa.pa19.into_funsel_2(),
|
||||||
@ -143,7 +146,6 @@ fn main() -> ! {
|
|||||||
.set_high()
|
.set_high()
|
||||||
.expect("Setting accelerometer chip select high failed");
|
.expect("Setting accelerometer chip select high failed");
|
||||||
|
|
||||||
let transfer_cfg = TransferConfigWithHwcs::new_no_hw_cs(None, Some(spi::MODE_0), true, false);
|
|
||||||
let spi = Spi::new(
|
let spi = Spi::new(
|
||||||
&mut dp.sysconfig,
|
&mut dp.sysconfig,
|
||||||
50.MHz(),
|
50.MHz(),
|
||||||
|
@ -1,23 +1,13 @@
|
|||||||
|
//! Example application which interfaces with the boot EEPROM.
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::spi::{SpiBus, MODE_0};
|
use embedded_hal::delay::DelayNs;
|
||||||
use embedded_hal_bus::spi::ExclusiveDevice;
|
|
||||||
use panic_rtt_target as _;
|
use panic_rtt_target as _;
|
||||||
use rtt_target::{rprintln, rtt_init_print};
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
use va108xx_hal::{
|
use va108xx_hal::{pac, pwm::CountDownTimer, time::Hertz};
|
||||||
pac,
|
use vorago_reb1::m95m01::M95M01;
|
||||||
spi::{
|
|
||||||
RomCs, RomMiso, RomMosi, RomSck, Spi, SpiClkConfig, SpiConfig, TransferConfigWithHwcs,
|
|
||||||
BMSTART_BMSTOP_MASK,
|
|
||||||
},
|
|
||||||
time::Hertz,
|
|
||||||
};
|
|
||||||
use vorago_reb1::m95m01::{
|
|
||||||
regs::{RDSR, WREN},
|
|
||||||
StatusReg, M95M01,
|
|
||||||
};
|
|
||||||
|
|
||||||
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
@ -27,49 +17,48 @@ fn main() -> ! {
|
|||||||
rprintln!("-- VA108XX REB1 NVM example --");
|
rprintln!("-- VA108XX REB1 NVM example --");
|
||||||
|
|
||||||
let mut dp = pac::Peripherals::take().unwrap();
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
let cp = cortex_m::Peripherals::take().unwrap();
|
|
||||||
|
|
||||||
let mut spi = Spi::<pac::Spic, (RomSck, RomMiso, RomMosi)>::new(
|
let mut timer = CountDownTimer::new(&mut dp.sysconfig, CLOCK_FREQ, dp.tim0);
|
||||||
&mut dp.sysconfig,
|
let mut nvm = M95M01::new(&mut dp.sysconfig, CLOCK_FREQ, dp.spic);
|
||||||
CLOCK_FREQ,
|
|
||||||
dp.spic,
|
|
||||||
(RomSck, RomMiso, RomMosi),
|
|
||||||
// These values are taken from the vorago bootloader app, don't want to experiment here..
|
|
||||||
SpiConfig::default().clk_cfg(SpiClkConfig::new(2, 4)),
|
|
||||||
);
|
|
||||||
let mut read_buf: [u8; 2] = [0; 2];
|
|
||||||
let spi = spi.spi();
|
|
||||||
unsafe {
|
|
||||||
spi.data().write(|w| w.bits(RDSR.into()));
|
|
||||||
spi.data().write(|w| w.bits(0 | BMSTART_BMSTOP_MASK));
|
|
||||||
}
|
|
||||||
while spi.status().read().tfe().bit_is_clear() {}
|
|
||||||
while spi.status().read().rne().bit_is_clear() {}
|
|
||||||
let dummy = spi.data().read().bits();
|
|
||||||
while spi.status().read().rne().bit_is_clear() {}
|
|
||||||
let reg = StatusReg(spi.data().read().bits() as u8);
|
|
||||||
rprintln!("status reg {:?}", reg);
|
|
||||||
//spi.transfer(&mut read_buf, &[RDSR, 0]);
|
|
||||||
rprintln!("read buf {:?}", read_buf);
|
|
||||||
//spi.write(&[WREN]);
|
|
||||||
/*
|
|
||||||
let mut nvm =
|
|
||||||
M95M01::new(ExclusiveDevice::new_no_delay(spi, dummy_pin::DummyPin::new_low()).unwrap())
|
|
||||||
.expect("creating NVM structure failed");
|
|
||||||
let status_reg = nvm.read_status_reg().expect("reading status reg failed");
|
let status_reg = nvm.read_status_reg().expect("reading status reg failed");
|
||||||
rprintln!("status reg: {:?}", status_reg);
|
|
||||||
if status_reg.zero_segment() == 0b111 {
|
if status_reg.zero_segment() == 0b111 {
|
||||||
panic!("status register unexpected values");
|
panic!("status register unexpected values");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut orig_content: [u8; 16] = [0; 16];
|
||||||
let mut read_buf: [u8; 16] = [0; 16];
|
let mut read_buf: [u8; 16] = [0; 16];
|
||||||
nvm.read(0x4000, &mut read_buf[0..4])
|
let write_buf: [u8; 16] = [0; 16];
|
||||||
.expect("reading NVM failed");
|
for (idx, val) in read_buf.iter_mut().enumerate() {
|
||||||
rprintln!("NVM address 0x4000: {:x?}", &read_buf[0..4]);
|
*val = idx as u8;
|
||||||
let write_buf: [u8; 4] = [1, 2, 3, 4];
|
}
|
||||||
nvm.write(0x4000, &write_buf).unwrap();
|
nvm.read(0x4000, &mut orig_content).unwrap();
|
||||||
|
|
||||||
|
// One byte write and read.
|
||||||
|
nvm.write(0x4000, &write_buf[0..1]).unwrap();
|
||||||
|
nvm.read(0x4000, &mut read_buf[0..1]).unwrap();
|
||||||
|
assert_eq!(write_buf[0], read_buf[0]);
|
||||||
|
read_buf.fill(0);
|
||||||
|
|
||||||
|
// Four bytes write and read.
|
||||||
|
nvm.write(0x4000, &write_buf[0..4]).unwrap();
|
||||||
nvm.read(0x4000, &mut read_buf[0..4]).unwrap();
|
nvm.read(0x4000, &mut read_buf[0..4]).unwrap();
|
||||||
assert_eq!(&read_buf[0..4], write_buf);
|
assert_eq!(&read_buf[0..4], &write_buf[0..4]);
|
||||||
*/
|
read_buf.fill(0);
|
||||||
loop {}
|
|
||||||
|
// Full sixteen bytes
|
||||||
|
nvm.write(0x4000, &write_buf).unwrap();
|
||||||
|
nvm.read(0x4000, &mut read_buf).unwrap();
|
||||||
|
assert_eq!(&read_buf, &write_buf);
|
||||||
|
read_buf.fill(0);
|
||||||
|
|
||||||
|
// 3 bytes
|
||||||
|
nvm.write(0x4000, &write_buf[0..3]).unwrap();
|
||||||
|
nvm.read(0x4000, &mut read_buf[0..3]).unwrap();
|
||||||
|
assert_eq!(&read_buf[0..3], &write_buf[0..3]);
|
||||||
|
|
||||||
|
// Write back original content.
|
||||||
|
nvm.write(0x4000, &orig_content).unwrap();
|
||||||
|
loop {
|
||||||
|
timer.delay_ms(500);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
use core::fmt::Debug;
|
//! Basic driver for the ST M95M01 EEPROM memory.
|
||||||
use embedded_hal::spi::{Operation, SpiDevice};
|
//!
|
||||||
|
//! This driver is used by the provided bootloader application for the REB1
|
||||||
|
//! board. It provides a convenient wrapper around the HAL SPI to interface
|
||||||
|
//! with the EEPROM memory of the REB1 board.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! - [REB1 EEPROM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/nvm.rs)
|
||||||
|
use core::convert::Infallible;
|
||||||
|
use embedded_hal::spi::SpiBus;
|
||||||
|
|
||||||
bitfield::bitfield! {
|
bitfield::bitfield! {
|
||||||
pub struct StatusReg(u8);
|
pub struct StatusReg(u8);
|
||||||
@ -29,42 +38,43 @@ pub mod regs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
use regs::*;
|
use regs::*;
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac,
|
||||||
|
prelude::*,
|
||||||
|
spi::{RomMiso, RomMosi, RomSck, Spi, SpiConfig, BMSTART_BMSTOP_MASK},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub type RomSpi = Spi<pac::Spic, (RomSck, RomMiso, RomMosi), u8>;
|
||||||
|
|
||||||
/// Driver for the ST device M95M01 EEPROM memory.
|
/// Driver for the ST device M95M01 EEPROM memory.
|
||||||
pub struct M95M01<Spi: SpiDevice> {
|
///
|
||||||
spi: Spi,
|
/// Specialized for the requirements of the VA108XX MCUs.
|
||||||
|
pub struct M95M01 {
|
||||||
|
pub spi: RomSpi,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl M95M01 {
|
||||||
pub enum Error<SpiError: Debug> {
|
pub fn new(syscfg: &mut pac::Sysconfig, sys_clk: impl Into<Hertz>, spi: pac::Spic) -> Self {
|
||||||
Spi(SpiError),
|
let spi = RomSpi::new(
|
||||||
BufTooShort,
|
syscfg,
|
||||||
}
|
sys_clk,
|
||||||
|
spi,
|
||||||
impl<SpiError: Debug> From<SpiError> for Error<SpiError> {
|
(RomSck, RomMiso, RomMosi),
|
||||||
fn from(value: SpiError) -> Self {
|
SpiConfig::default(),
|
||||||
Self::Spi(value)
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Spi: SpiDevice> M95M01<Spi>
|
|
||||||
where
|
|
||||||
Spi::Error: Debug,
|
|
||||||
{
|
|
||||||
pub fn new(spi: Spi) -> Result<Self, Spi::Error> {
|
|
||||||
let mut spi_dev = Self { spi };
|
let mut spi_dev = Self { spi };
|
||||||
spi_dev.clear_block_protection()?;
|
spi_dev.clear_block_protection().unwrap();
|
||||||
Ok(spi_dev)
|
spi_dev
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn release(mut self) -> Result<Spi, Spi::Error> {
|
pub fn release(mut self) -> pac::Spic {
|
||||||
self.set_block_protection()?;
|
self.set_block_protection().unwrap();
|
||||||
Ok(self.spi)
|
self.spi.release().0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until the write-in-progress state is cleared. This exposes a [nb] API, so this function
|
// Wait until the write-in-progress state is cleared. This exposes a [nb] API, so this function
|
||||||
// will return [nb::Error::WouldBlock] if the EEPROM is still busy.
|
// will return [nb::Error::WouldBlock] if the EEPROM is still busy.
|
||||||
pub fn writes_are_done(&mut self) -> nb::Result<(), Spi::Error> {
|
pub fn writes_are_done(&mut self) -> nb::Result<(), Infallible> {
|
||||||
let rdsr = self.read_status_reg()?;
|
let rdsr = self.read_status_reg()?;
|
||||||
if rdsr.write_in_progress() {
|
if rdsr.write_in_progress() {
|
||||||
return Err(nb::Error::WouldBlock);
|
return Err(nb::Error::WouldBlock);
|
||||||
@ -72,82 +82,91 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_status_reg(&mut self) -> Result<StatusReg, Spi::Error> {
|
pub fn read_status_reg(&mut self) -> Result<StatusReg, Infallible> {
|
||||||
let mut write_read: [u8; 2] = [regs::RDSR, 0x00];
|
let mut write_read: [u8; 2] = [regs::RDSR, 0x00];
|
||||||
self.spi.transfer_in_place(&mut write_read)?;
|
self.spi.transfer_in_place(&mut write_read)?;
|
||||||
Ok(StatusReg(write_read[1]))
|
Ok(StatusReg(write_read[1]))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_enable(&mut self) -> Result<(), Spi::Error> {
|
pub fn write_enable(&mut self) -> Result<(), Infallible> {
|
||||||
self.spi.write(&[regs::WREN])
|
self.spi.write(&[regs::WREN])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_block_protection(&mut self) -> Result<(), Spi::Error> {
|
pub fn clear_block_protection(&mut self) -> Result<(), Infallible> {
|
||||||
// Has to be written separately.
|
// Has to be written separately.
|
||||||
self.spi.write(&[WREN])?;
|
self.write_enable()?;
|
||||||
self.spi.write(&[WRSR, 0x00])
|
self.spi.write(&[WRSR, 0x00])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_block_protection(&mut self) -> Result<(), Spi::Error> {
|
pub fn set_block_protection(&mut self) -> Result<(), Infallible> {
|
||||||
let mut reg = StatusReg(0);
|
let mut reg = StatusReg(0);
|
||||||
reg.set_block_protection_bits(0b11);
|
reg.set_block_protection_bits(0b11);
|
||||||
self.spi.write(&[WREN, WRSR, reg.0])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Spi::Error> {
|
|
||||||
nb::block!(self.writes_are_done())?;
|
|
||||||
self.write_enable()?;
|
self.write_enable()?;
|
||||||
self.spi.transaction(&mut [
|
self.spi.write(&[WRSR, reg.0])
|
||||||
Operation::Write(&[
|
|
||||||
WRITE,
|
|
||||||
((address >> 16) & 0xff) as u8,
|
|
||||||
((address >> 8) & 0xff) as u8,
|
|
||||||
(address & 0xff) as u8,
|
|
||||||
]),
|
|
||||||
Operation::Write(data),
|
|
||||||
])?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Error<Spi::Error>> {
|
fn common_init_write_and_read(&mut self, address: u32, reg: u8) -> Result<(), Infallible> {
|
||||||
if buf.len() < buf.len() {
|
nb::block!(self.writes_are_done())?;
|
||||||
return Err(Error::BufTooShort);
|
self.spi.flush()?;
|
||||||
|
if reg == WRITE {
|
||||||
|
self.write_enable()?;
|
||||||
|
self.spi.write_fifo_unchecked(WRITE as u32);
|
||||||
|
} else {
|
||||||
|
self.spi.write_fifo_unchecked(READ as u32);
|
||||||
}
|
}
|
||||||
nb::block!(self.writes_are_done())?;
|
self.spi.write_fifo_unchecked((address >> 16) & 0xff);
|
||||||
|
self.spi.write_fifo_unchecked((address >> 8) & 0xff);
|
||||||
self.spi.transaction(&mut [
|
self.spi.write_fifo_unchecked(address & 0xff);
|
||||||
Operation::Write(&[
|
|
||||||
READ,
|
|
||||||
((address >> 16) & 0xff) as u8,
|
|
||||||
((address >> 8) & 0xff) as u8,
|
|
||||||
(address & 0xff) as u8,
|
|
||||||
]),
|
|
||||||
Operation::Read(buf),
|
|
||||||
])?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, Spi::Error> {
|
fn common_read(&mut self, address: u32) -> Result<(), Infallible> {
|
||||||
|
self.common_init_write_and_read(address, READ)?;
|
||||||
|
for _ in 0..4 {
|
||||||
|
// Pump the FIFO.
|
||||||
|
self.spi.write_fifo_unchecked(0);
|
||||||
|
// Ignore the first 4 bytes.
|
||||||
|
self.spi.read_fifo_unchecked();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, address: u32, data: &[u8]) -> Result<(), Infallible> {
|
||||||
|
self.common_init_write_and_read(address, WRITE)?;
|
||||||
|
for val in data.iter().take(data.len() - 1) {
|
||||||
|
nb::block!(self.spi.write_fifo(*val as u32))?;
|
||||||
|
self.spi.read_fifo_unchecked();
|
||||||
|
}
|
||||||
|
nb::block!(self
|
||||||
|
.spi
|
||||||
|
.write_fifo(*data.last().unwrap() as u32 | BMSTART_BMSTOP_MASK))?;
|
||||||
|
self.spi.flush()?;
|
||||||
nb::block!(self.writes_are_done())?;
|
nb::block!(self.writes_are_done())?;
|
||||||
// Write the read command and address
|
Ok(())
|
||||||
self.spi.write(&[
|
}
|
||||||
READ,
|
|
||||||
((address >> 16) & 0xff) as u8,
|
|
||||||
((address >> 8) & 0xff) as u8,
|
|
||||||
(address & 0xff) as u8,
|
|
||||||
])?;
|
|
||||||
|
|
||||||
// Read and compare each byte in place
|
pub fn read(&mut self, address: u32, buf: &mut [u8]) -> Result<(), Infallible> {
|
||||||
for original_byte in data.iter() {
|
self.common_read(address)?;
|
||||||
let mut read_byte = [0u8];
|
for val in buf.iter_mut() {
|
||||||
self.spi.read(&mut read_byte)?;
|
nb::block!(self.spi.write_fifo(0))?;
|
||||||
|
*val = (nb::block!(self.spi.read_fifo()).unwrap() & 0xff) as u8;
|
||||||
|
}
|
||||||
|
nb::block!(self.spi.write_fifo(BMSTART_BMSTOP_MASK))?;
|
||||||
|
self.spi.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Compare read byte with original
|
pub fn verify(&mut self, address: u32, data: &[u8]) -> Result<bool, Infallible> {
|
||||||
if read_byte[0] != *original_byte {
|
self.common_read(address)?;
|
||||||
|
for val in data.iter() {
|
||||||
|
nb::block!(self.spi.write_fifo(0))?;
|
||||||
|
let read_val = (nb::block!(self.spi.read_fifo()).unwrap() & 0xff) as u8;
|
||||||
|
if read_val != *val {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
nb::block!(self.spi.write_fifo(BMSTART_BMSTOP_MASK))?;
|
||||||
|
self.spi.flush()?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,6 +355,30 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "cortex-debug",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "REB1 NVM Example",
|
||||||
|
"servertype": "jlink",
|
||||||
|
"cwd": "${workspaceRoot}",
|
||||||
|
"device": "Cortex-M0",
|
||||||
|
"svdFile": "./va108xx/svd/va108xx.svd.patched",
|
||||||
|
"preLaunchTask": "reb1-nvm",
|
||||||
|
"executable": "${workspaceFolder}/target/thumbv6m-none-eabi/debug/examples/nvm",
|
||||||
|
"interface": "jtag",
|
||||||
|
"runToEntryPoint": "main",
|
||||||
|
"rttConfig": {
|
||||||
|
"enabled": true,
|
||||||
|
"address": "auto",
|
||||||
|
"decoders": [
|
||||||
|
{
|
||||||
|
"port": 0,
|
||||||
|
"timestamp": true,
|
||||||
|
"type": "console"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
@ -154,8 +154,6 @@
|
|||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"-p",
|
|
||||||
"vorago-reb1",
|
|
||||||
"--example",
|
"--example",
|
||||||
"blinky-leds",
|
"blinky-leds",
|
||||||
],
|
],
|
||||||
@ -170,8 +168,6 @@
|
|||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"-p",
|
|
||||||
"vorago-reb1",
|
|
||||||
"--example",
|
"--example",
|
||||||
"blinky-button-irq",
|
"blinky-button-irq",
|
||||||
],
|
],
|
||||||
@ -186,8 +182,6 @@
|
|||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"-p",
|
|
||||||
"vorago-reb1",
|
|
||||||
"--example",
|
"--example",
|
||||||
"adt75-temp-sensor",
|
"adt75-temp-sensor",
|
||||||
],
|
],
|
||||||
@ -202,8 +196,6 @@
|
|||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"-p",
|
|
||||||
"vorago-reb1",
|
|
||||||
"--example",
|
"--example",
|
||||||
"blinky-button-rtic",
|
"blinky-button-rtic",
|
||||||
],
|
],
|
||||||
@ -218,8 +210,6 @@
|
|||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"-p",
|
|
||||||
"vorago-reb1",
|
|
||||||
"--example",
|
"--example",
|
||||||
"adxl343-accelerometer"
|
"adxl343-accelerometer"
|
||||||
],
|
],
|
||||||
@ -234,8 +224,6 @@
|
|||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build",
|
"build",
|
||||||
"-p",
|
|
||||||
"vorago-reb1",
|
|
||||||
"--example",
|
"--example",
|
||||||
"max11619-adc",
|
"max11619-adc",
|
||||||
],
|
],
|
||||||
@ -244,6 +232,20 @@
|
|||||||
"isDefault": true
|
"isDefault": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"label": "reb1-nvm",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--example",
|
||||||
|
"nvm",
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"label": "rtic-example",
|
"label": "rtic-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
|
Loading…
Reference in New Issue
Block a user