aboutsummaryrefslogtreecommitdiff
path: root/embassy-stm32/src/spi/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-stm32/src/spi/mod.rs')
-rw-r--r--embassy-stm32/src/spi/mod.rs109
1 files changed, 101 insertions, 8 deletions
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs
index abb80ed26..c90e0cef4 100644
--- a/embassy-stm32/src/spi/mod.rs
+++ b/embassy-stm32/src/spi/mod.rs
@@ -54,6 +54,16 @@ pub enum BitOrder {
54 MsbFirst, 54 MsbFirst,
55} 55}
56 56
57/// SPI Direction.
58#[derive(Debug, PartialEq, Eq, Clone, Copy)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60pub enum Direction {
61 /// Transmit
62 Transmit,
63 /// Receive
64 Receive,
65}
66
57/// SPI configuration. 67/// SPI configuration.
58#[non_exhaustive] 68#[non_exhaustive]
59#[derive(Copy, Clone)] 69#[derive(Copy, Clone)]
@@ -72,6 +82,10 @@ pub struct Config {
72 /// signal rise/fall speed (slew rate) - defaults to `Medium`. 82 /// signal rise/fall speed (slew rate) - defaults to `Medium`.
73 /// Increase for high SPI speeds. Change to `Low` to reduce ringing. 83 /// Increase for high SPI speeds. Change to `Low` to reduce ringing.
74 pub gpio_speed: Speed, 84 pub gpio_speed: Speed,
85 /// If True sets SSOE to zero even if SPI is in Master Mode.
86 /// NSS output enabled (SSM = 0, SSOE = 1): The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled.
87 /// NSS output disabled (SSM = 0, SSOE = 0): For devices set as slave, the NSS pin acts as a classical NSS input: the slave is selected when NSS is low and deselected when NSS high.
88 pub nss_output_disable: bool,
75} 89}
76 90
77impl Default for Config { 91impl Default for Config {
@@ -82,6 +96,7 @@ impl Default for Config {
82 frequency: Hertz(1_000_000), 96 frequency: Hertz(1_000_000),
83 miso_pull: Pull::None, 97 miso_pull: Pull::None,
84 gpio_speed: Speed::VeryHigh, 98 gpio_speed: Speed::VeryHigh,
99 nss_output_disable: false,
85 } 100 }
86 } 101 }
87} 102}
@@ -215,13 +230,35 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
215 let cpol = config.raw_polarity(); 230 let cpol = config.raw_polarity();
216 let lsbfirst = config.raw_byte_order(); 231 let lsbfirst = config.raw_byte_order();
217 232
218 self.info.rcc.enable_and_reset(); 233 self.info.rcc.enable_and_reset_without_stop();
234
235 /*
236 - Software NSS management (SSM = 1)
237 The slave select information is driven internally by the value of the SSI bit in the
238 SPI_CR1 register. The external NSS pin remains free for other application uses.
239
240 - Hardware NSS management (SSM = 0)
241 Two configurations are possible depending on the NSS output configuration (SSOE bit
242 in register SPI_CR1).
243
244 -- NSS output enabled (SSM = 0, SSOE = 1)
245 This configuration is used only when the device operates in master mode. The
246 NSS signal is driven low when the master starts the communication and is kept
247 low until the SPI is disabled.
248
249 -- NSS output disabled (SSM = 0, SSOE = 0)
250 This configuration allows multimaster capability for devices operating in master
251 mode. For devices set as slave, the NSS pin acts as a classical NSS input: the
252 slave is selected when NSS is low and deselected when NSS high
253 */
254 let ssm = self.nss.is_none();
219 255
220 let regs = self.info.regs; 256 let regs = self.info.regs;
221 #[cfg(any(spi_v1, spi_v2))] 257 #[cfg(any(spi_v1, spi_v2))]
222 { 258 {
259 let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable;
223 regs.cr2().modify(|w| { 260 regs.cr2().modify(|w| {
224 w.set_ssoe(false); 261 w.set_ssoe(ssoe);
225 }); 262 });
226 regs.cr1().modify(|w| { 263 regs.cr1().modify(|w| {
227 w.set_cpha(cpha); 264 w.set_cpha(cpha);
@@ -232,7 +269,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
232 w.set_spe(true); 269 w.set_spe(true);
233 w.set_lsbfirst(lsbfirst); 270 w.set_lsbfirst(lsbfirst);
234 w.set_ssi(CM::MASTER == vals::Mstr::MASTER); 271 w.set_ssi(CM::MASTER == vals::Mstr::MASTER);
235 w.set_ssm(CM::MASTER == vals::Mstr::MASTER); 272 w.set_ssm(ssm);
236 w.set_crcen(false); 273 w.set_crcen(false);
237 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); 274 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
238 // we're doing "fake rxonly", by actually writing one 275 // we're doing "fake rxonly", by actually writing one
@@ -244,11 +281,12 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
244 } 281 }
245 #[cfg(spi_v3)] 282 #[cfg(spi_v3)]
246 { 283 {
284 let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable;
247 regs.cr2().modify(|w| { 285 regs.cr2().modify(|w| {
248 let (ds, frxth) = <u8 as SealedWord>::CONFIG; 286 let (ds, frxth) = <u8 as SealedWord>::CONFIG;
249 w.set_frxth(frxth); 287 w.set_frxth(frxth);
250 w.set_ds(ds); 288 w.set_ds(ds);
251 w.set_ssoe(false); 289 w.set_ssoe(ssoe);
252 }); 290 });
253 regs.cr1().modify(|w| { 291 regs.cr1().modify(|w| {
254 w.set_cpha(cpha); 292 w.set_cpha(cpha);
@@ -258,7 +296,7 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
258 w.set_br(br); 296 w.set_br(br);
259 w.set_lsbfirst(lsbfirst); 297 w.set_lsbfirst(lsbfirst);
260 w.set_ssi(CM::MASTER == vals::Mstr::MASTER); 298 w.set_ssi(CM::MASTER == vals::Mstr::MASTER);
261 w.set_ssm(CM::MASTER == vals::Mstr::MASTER); 299 w.set_ssm(ssm);
262 w.set_crcen(false); 300 w.set_crcen(false);
263 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); 301 w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL);
264 w.set_spe(true); 302 w.set_spe(true);
@@ -266,14 +304,14 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
266 } 304 }
267 #[cfg(any(spi_v4, spi_v5, spi_v6))] 305 #[cfg(any(spi_v4, spi_v5, spi_v6))]
268 { 306 {
307 let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable;
269 regs.ifcr().write(|w| w.0 = 0xffff_ffff); 308 regs.ifcr().write(|w| w.0 = 0xffff_ffff);
270 regs.cfg2().modify(|w| { 309 regs.cfg2().modify(|w| {
271 //w.set_ssoe(true); 310 w.set_ssoe(ssoe);
272 w.set_ssoe(false);
273 w.set_cpha(cpha); 311 w.set_cpha(cpha);
274 w.set_cpol(cpol); 312 w.set_cpol(cpol);
275 w.set_lsbfirst(lsbfirst); 313 w.set_lsbfirst(lsbfirst);
276 w.set_ssm(CM::MASTER == vals::Master::MASTER); 314 w.set_ssm(ssm);
277 w.set_master(CM::MASTER); 315 w.set_master(CM::MASTER);
278 w.set_comm(vals::Comm::FULL_DUPLEX); 316 w.set_comm(vals::Comm::FULL_DUPLEX);
279 w.set_ssom(vals::Ssom::ASSERTED); 317 w.set_ssom(vals::Ssom::ASSERTED);
@@ -348,6 +386,20 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
348 Ok(()) 386 Ok(())
349 } 387 }
350 388
389 /// Set SPI direction
390 #[cfg(any(spi_v1, spi_v2, spi_v3))]
391 pub fn set_direction(&mut self, dir: Option<Direction>) {
392 let (bidimode, bidioe) = match dir {
393 Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT),
394 Some(Direction::Receive) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::RECEIVE),
395 None => (vals::Bidimode::UNIDIRECTIONAL, vals::Bidioe::TRANSMIT),
396 };
397 self.info.regs.cr1().modify(|w| {
398 w.set_bidimode(bidimode);
399 w.set_bidioe(bidioe);
400 });
401 }
402
351 /// Get current SPI configuration. 403 /// Get current SPI configuration.
352 pub fn get_current_config(&self) -> Config { 404 pub fn get_current_config(&self) -> Config {
353 #[cfg(any(spi_v1, spi_v2, spi_v3))] 405 #[cfg(any(spi_v1, spi_v2, spi_v3))]
@@ -357,6 +409,11 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
357 #[cfg(any(spi_v4, spi_v5, spi_v6))] 409 #[cfg(any(spi_v4, spi_v5, spi_v6))]
358 let cfg1 = self.info.regs.cfg1().read(); 410 let cfg1 = self.info.regs.cfg1().read();
359 411
412 #[cfg(any(spi_v1, spi_v2, spi_v3))]
413 let ssoe = self.info.regs.cr2().read().ssoe();
414 #[cfg(any(spi_v4, spi_v5, spi_v6))]
415 let ssoe = cfg.ssoe();
416
360 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { 417 let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW {
361 Polarity::IdleLow 418 Polarity::IdleLow
362 } else { 419 } else {
@@ -386,12 +443,16 @@ impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> {
386 443
387 let frequency = compute_frequency(self.kernel_clock, br); 444 let frequency = compute_frequency(self.kernel_clock, br);
388 445
446 // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled
447 let nss_output_disable = !ssoe || cfg.ssm();
448
389 Config { 449 Config {
390 mode: Mode { polarity, phase }, 450 mode: Mode { polarity, phase },
391 bit_order, 451 bit_order,
392 frequency, 452 frequency,
393 miso_pull, 453 miso_pull,
394 gpio_speed: self.gpio_speed, 454 gpio_speed: self.gpio_speed,
455 nss_output_disable,
395 } 456 }
396 } 457 }
397 458
@@ -708,6 +769,30 @@ impl<'d> Spi<'d, Async, Master> {
708 ) 769 )
709 } 770 }
710 771
772 /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode
773 #[cfg(any(spi_v1, spi_v2, spi_v3))]
774 pub fn new_bidi<T: Instance, #[cfg(afio)] A>(
775 peri: Peri<'d, T>,
776 sck: Peri<'d, if_afio!(impl SckPin<T, A>)>,
777 sdio: Peri<'d, if_afio!(impl MosiPin<T, A>)>,
778 tx_dma: Peri<'d, impl TxDma<T>>,
779 rx_dma: Peri<'d, impl RxDma<T>>,
780 config: Config,
781 ) -> Self {
782 let mut this = Self::new_inner(
783 peri,
784 new_pin!(sck, config.sck_af()),
785 new_pin!(sdio, AfType::output(OutputType::PushPull, config.gpio_speed)),
786 None,
787 None,
788 new_dma!(tx_dma),
789 new_dma!(rx_dma),
790 config,
791 );
792 this.set_direction(Some(Direction::Transmit));
793 this
794 }
795
711 /// Create a new SPI driver, in TX-only mode, without SCK pin. 796 /// Create a new SPI driver, in TX-only mode, without SCK pin.
712 /// 797 ///
713 /// This can be useful for bit-banging non-SPI protocols. 798 /// This can be useful for bit-banging non-SPI protocols.
@@ -763,6 +848,7 @@ impl<'d> Spi<'d, Async, Master> {
763impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { 848impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
764 /// SPI write, using DMA. 849 /// SPI write, using DMA.
765 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { 850 pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> {
851 let _scoped_block_stop = self.info.rcc.block_stop();
766 if data.is_empty() { 852 if data.is_empty() {
767 return Ok(()); 853 return Ok(());
768 } 854 }
@@ -794,6 +880,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
794 /// SPI read, using DMA. 880 /// SPI read, using DMA.
795 #[cfg(any(spi_v4, spi_v5, spi_v6))] 881 #[cfg(any(spi_v4, spi_v5, spi_v6))]
796 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 882 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
883 let _scoped_block_stop = self.info.rcc.block_stop();
797 if data.is_empty() { 884 if data.is_empty() {
798 return Ok(()); 885 return Ok(());
799 } 886 }
@@ -881,6 +968,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
881 /// SPI read, using DMA. 968 /// SPI read, using DMA.
882 #[cfg(any(spi_v1, spi_v2, spi_v3))] 969 #[cfg(any(spi_v1, spi_v2, spi_v3))]
883 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 970 pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
971 let _scoped_block_stop = self.info.rcc.block_stop();
884 if data.is_empty() { 972 if data.is_empty() {
885 return Ok(()); 973 return Ok(());
886 } 974 }
@@ -928,6 +1016,7 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
928 } 1016 }
929 1017
930 async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { 1018 async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> {
1019 let _scoped_block_stop = self.info.rcc.block_stop();
931 assert_eq!(read.len(), write.len()); 1020 assert_eq!(read.len(), write.len());
932 if read.len() == 0 { 1021 if read.len() == 0 {
933 return Ok(()); 1022 return Ok(());
@@ -979,6 +1068,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
979 /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. 1068 /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored.
980 /// If `write` is shorter it is padded with zero bytes. 1069 /// If `write` is shorter it is padded with zero bytes.
981 pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { 1070 pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> {
1071 let _scoped_block_stop = self.info.rcc.block_stop();
1072
982 self.transfer_inner(read, write).await 1073 self.transfer_inner(read, write).await
983 } 1074 }
984 1075
@@ -986,6 +1077,8 @@ impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> {
986 /// 1077 ///
987 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. 1078 /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time.
988 pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { 1079 pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> {
1080 let _scoped_block_stop = self.info.rcc.block_stop();
1081
989 self.transfer_inner(data, data).await 1082 self.transfer_inner(data, data).await
990 } 1083 }
991} 1084}