diff options
Diffstat (limited to 'embassy-stm32/src/spi/mod.rs')
| -rw-r--r-- | embassy-stm32/src/spi/mod.rs | 109 |
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))] | ||
| 60 | pub 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 | ||
| 77 | impl Default for Config { | 91 | impl 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> { | |||
| 763 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | 848 | impl<'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 | } |
