diff options
Diffstat (limited to 'embassy-stm32/src/spi')
| -rw-r--r-- | embassy-stm32/src/spi/mod.rs | 298 |
1 files changed, 265 insertions, 33 deletions
diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c5373a54d..13ff31a34 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs | |||
| @@ -6,15 +6,15 @@ use core::ptr; | |||
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | 7 | use embassy_embedded_hal::SetConfig; |
| 8 | use embassy_futures::join::join; | 8 | use embassy_futures::join::join; |
| 9 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; | 9 | pub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; |
| 10 | 10 | ||
| 11 | use crate::dma::{word, ChannelAndRequest}; | 11 | use crate::Peri; |
| 12 | use crate::dma::{ChannelAndRequest, word}; | ||
| 12 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; | 13 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
| 13 | use crate::mode::{Async, Blocking, Mode as PeriMode}; | 14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
| 14 | use crate::pac::spi::{regs, vals, Spi as Regs}; | 15 | use crate::pac::spi::{Spi as Regs, regs, vals}; |
| 15 | use crate::rcc::{RccInfo, SealedRccPeripheral}; | 16 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
| 16 | use crate::time::Hertz; | 17 | use crate::time::Hertz; |
| 17 | use crate::Peri; | ||
| 18 | 18 | ||
| 19 | /// SPI error. | 19 | /// SPI error. |
| 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 20 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| @@ -54,6 +54,26 @@ 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 | |||
| 67 | /// Slave Select (SS) pin polarity. | ||
| 68 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | pub enum SlaveSelectPolarity { | ||
| 71 | /// SS active high | ||
| 72 | ActiveHigh, | ||
| 73 | /// SS active low | ||
| 74 | ActiveLow, | ||
| 75 | } | ||
| 76 | |||
| 57 | /// SPI configuration. | 77 | /// SPI configuration. |
| 58 | #[non_exhaustive] | 78 | #[non_exhaustive] |
| 59 | #[derive(Copy, Clone)] | 79 | #[derive(Copy, Clone)] |
| @@ -72,6 +92,13 @@ pub struct Config { | |||
| 72 | /// signal rise/fall speed (slew rate) - defaults to `Medium`. | 92 | /// signal rise/fall speed (slew rate) - defaults to `Medium`. |
| 73 | /// Increase for high SPI speeds. Change to `Low` to reduce ringing. | 93 | /// Increase for high SPI speeds. Change to `Low` to reduce ringing. |
| 74 | pub gpio_speed: Speed, | 94 | pub gpio_speed: Speed, |
| 95 | /// If True sets SSOE to zero even if SPI is in Master Mode. | ||
| 96 | /// 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. | ||
| 97 | /// 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. | ||
| 98 | pub nss_output_disable: bool, | ||
| 99 | /// Slave Select (SS) pin polarity. | ||
| 100 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 101 | pub nss_polarity: SlaveSelectPolarity, | ||
| 75 | } | 102 | } |
| 76 | 103 | ||
| 77 | impl Default for Config { | 104 | impl Default for Config { |
| @@ -82,6 +109,9 @@ impl Default for Config { | |||
| 82 | frequency: Hertz(1_000_000), | 109 | frequency: Hertz(1_000_000), |
| 83 | miso_pull: Pull::None, | 110 | miso_pull: Pull::None, |
| 84 | gpio_speed: Speed::VeryHigh, | 111 | gpio_speed: Speed::VeryHigh, |
| 112 | nss_output_disable: false, | ||
| 113 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 114 | nss_polarity: SlaveSelectPolarity::ActiveHigh, | ||
| 85 | } | 115 | } |
| 86 | } | 116 | } |
| 87 | } | 117 | } |
| @@ -108,6 +138,14 @@ impl Config { | |||
| 108 | } | 138 | } |
| 109 | } | 139 | } |
| 110 | 140 | ||
| 141 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 142 | fn raw_nss_polarity(&self) -> vals::Ssiop { | ||
| 143 | match self.nss_polarity { | ||
| 144 | SlaveSelectPolarity::ActiveHigh => vals::Ssiop::ACTIVE_HIGH, | ||
| 145 | SlaveSelectPolarity::ActiveLow => vals::Ssiop::ACTIVE_LOW, | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 111 | #[cfg(gpio_v1)] | 149 | #[cfg(gpio_v1)] |
| 112 | fn sck_af(&self) -> AfType { | 150 | fn sck_af(&self) -> AfType { |
| 113 | AfType::output(OutputType::PushPull, self.gpio_speed) | 151 | AfType::output(OutputType::PushPull, self.gpio_speed) |
| @@ -125,26 +163,69 @@ impl Config { | |||
| 125 | ) | 163 | ) |
| 126 | } | 164 | } |
| 127 | } | 165 | } |
| 166 | |||
| 167 | /// SPI communication mode | ||
| 168 | pub mod mode { | ||
| 169 | use stm32_metapac::spi::vals; | ||
| 170 | |||
| 171 | trait SealedMode {} | ||
| 172 | |||
| 173 | /// Trait for SPI communication mode operations. | ||
| 174 | #[allow(private_bounds)] | ||
| 175 | pub trait CommunicationMode: SealedMode { | ||
| 176 | /// Spi communication mode | ||
| 177 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 178 | const MASTER: vals::Mstr; | ||
| 179 | /// Spi communication mode | ||
| 180 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 181 | const MASTER: vals::Master; | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Mode allowing for SPI master operations. | ||
| 185 | pub struct Master; | ||
| 186 | /// Mode allowing for SPI slave operations. | ||
| 187 | pub struct Slave; | ||
| 188 | |||
| 189 | impl SealedMode for Master {} | ||
| 190 | impl CommunicationMode for Master { | ||
| 191 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 192 | const MASTER: vals::Mstr = vals::Mstr::MASTER; | ||
| 193 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 194 | const MASTER: vals::Master = vals::Master::MASTER; | ||
| 195 | } | ||
| 196 | |||
| 197 | impl SealedMode for Slave {} | ||
| 198 | impl CommunicationMode for Slave { | ||
| 199 | #[cfg(not(any(spi_v4, spi_v5, spi_v6)))] | ||
| 200 | const MASTER: vals::Mstr = vals::Mstr::SLAVE; | ||
| 201 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 202 | const MASTER: vals::Master = vals::Master::SLAVE; | ||
| 203 | } | ||
| 204 | } | ||
| 205 | use mode::{CommunicationMode, Master, Slave}; | ||
| 206 | |||
| 128 | /// SPI driver. | 207 | /// SPI driver. |
| 129 | pub struct Spi<'d, M: PeriMode> { | 208 | pub struct Spi<'d, M: PeriMode, CM: CommunicationMode> { |
| 130 | pub(crate) info: &'static Info, | 209 | pub(crate) info: &'static Info, |
| 131 | kernel_clock: Hertz, | 210 | kernel_clock: Hertz, |
| 132 | sck: Option<Peri<'d, AnyPin>>, | 211 | sck: Option<Peri<'d, AnyPin>>, |
| 133 | mosi: Option<Peri<'d, AnyPin>>, | 212 | mosi: Option<Peri<'d, AnyPin>>, |
| 134 | miso: Option<Peri<'d, AnyPin>>, | 213 | miso: Option<Peri<'d, AnyPin>>, |
| 214 | nss: Option<Peri<'d, AnyPin>>, | ||
| 135 | tx_dma: Option<ChannelAndRequest<'d>>, | 215 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 136 | rx_dma: Option<ChannelAndRequest<'d>>, | 216 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 137 | _phantom: PhantomData<M>, | 217 | _phantom: PhantomData<(M, CM)>, |
| 138 | current_word_size: word_impl::Config, | 218 | current_word_size: word_impl::Config, |
| 139 | gpio_speed: Speed, | 219 | gpio_speed: Speed, |
| 140 | } | 220 | } |
| 141 | 221 | ||
| 142 | impl<'d, M: PeriMode> Spi<'d, M> { | 222 | impl<'d, M: PeriMode, CM: CommunicationMode> Spi<'d, M, CM> { |
| 143 | fn new_inner<T: Instance>( | 223 | fn new_inner<T: Instance>( |
| 144 | _peri: Peri<'d, T>, | 224 | _peri: Peri<'d, T>, |
| 145 | sck: Option<Peri<'d, AnyPin>>, | 225 | sck: Option<Peri<'d, AnyPin>>, |
| 146 | mosi: Option<Peri<'d, AnyPin>>, | 226 | mosi: Option<Peri<'d, AnyPin>>, |
| 147 | miso: Option<Peri<'d, AnyPin>>, | 227 | miso: Option<Peri<'d, AnyPin>>, |
| 228 | nss: Option<Peri<'d, AnyPin>>, | ||
| 148 | tx_dma: Option<ChannelAndRequest<'d>>, | 229 | tx_dma: Option<ChannelAndRequest<'d>>, |
| 149 | rx_dma: Option<ChannelAndRequest<'d>>, | 230 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 150 | config: Config, | 231 | config: Config, |
| @@ -155,6 +236,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 155 | sck, | 236 | sck, |
| 156 | mosi, | 237 | mosi, |
| 157 | miso, | 238 | miso, |
| 239 | nss, | ||
| 158 | tx_dma, | 240 | tx_dma, |
| 159 | rx_dma, | 241 | rx_dma, |
| 160 | current_word_size: <u8 as SealedWord>::CONFIG, | 242 | current_word_size: <u8 as SealedWord>::CONFIG, |
| @@ -171,24 +253,46 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 171 | let cpol = config.raw_polarity(); | 253 | let cpol = config.raw_polarity(); |
| 172 | let lsbfirst = config.raw_byte_order(); | 254 | let lsbfirst = config.raw_byte_order(); |
| 173 | 255 | ||
| 174 | self.info.rcc.enable_and_reset(); | 256 | self.info.rcc.enable_and_reset_without_stop(); |
| 257 | |||
| 258 | /* | ||
| 259 | - Software NSS management (SSM = 1) | ||
| 260 | The slave select information is driven internally by the value of the SSI bit in the | ||
| 261 | SPI_CR1 register. The external NSS pin remains free for other application uses. | ||
| 262 | |||
| 263 | - Hardware NSS management (SSM = 0) | ||
| 264 | Two configurations are possible depending on the NSS output configuration (SSOE bit | ||
| 265 | in register SPI_CR1). | ||
| 266 | |||
| 267 | -- NSS output enabled (SSM = 0, SSOE = 1) | ||
| 268 | This configuration is used only when the device operates in master mode. The | ||
| 269 | NSS signal is driven low when the master starts the communication and is kept | ||
| 270 | low until the SPI is disabled. | ||
| 271 | |||
| 272 | -- NSS output disabled (SSM = 0, SSOE = 0) | ||
| 273 | This configuration allows multimaster capability for devices operating in master | ||
| 274 | mode. For devices set as slave, the NSS pin acts as a classical NSS input: the | ||
| 275 | slave is selected when NSS is low and deselected when NSS high | ||
| 276 | */ | ||
| 277 | let ssm = self.nss.is_none(); | ||
| 175 | 278 | ||
| 176 | let regs = self.info.regs; | 279 | let regs = self.info.regs; |
| 177 | #[cfg(any(spi_v1, spi_v2))] | 280 | #[cfg(any(spi_v1, spi_v2))] |
| 178 | { | 281 | { |
| 282 | let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable; | ||
| 179 | regs.cr2().modify(|w| { | 283 | regs.cr2().modify(|w| { |
| 180 | w.set_ssoe(false); | 284 | w.set_ssoe(ssoe); |
| 181 | }); | 285 | }); |
| 182 | regs.cr1().modify(|w| { | 286 | regs.cr1().modify(|w| { |
| 183 | w.set_cpha(cpha); | 287 | w.set_cpha(cpha); |
| 184 | w.set_cpol(cpol); | 288 | w.set_cpol(cpol); |
| 185 | 289 | ||
| 186 | w.set_mstr(vals::Mstr::MASTER); | 290 | w.set_mstr(CM::MASTER); |
| 187 | w.set_br(br); | 291 | w.set_br(br); |
| 188 | w.set_spe(true); | 292 | w.set_spe(true); |
| 189 | w.set_lsbfirst(lsbfirst); | 293 | w.set_lsbfirst(lsbfirst); |
| 190 | w.set_ssi(true); | 294 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 191 | w.set_ssm(true); | 295 | w.set_ssm(ssm); |
| 192 | w.set_crcen(false); | 296 | w.set_crcen(false); |
| 193 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 297 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 194 | // we're doing "fake rxonly", by actually writing one | 298 | // we're doing "fake rxonly", by actually writing one |
| @@ -200,21 +304,22 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 200 | } | 304 | } |
| 201 | #[cfg(spi_v3)] | 305 | #[cfg(spi_v3)] |
| 202 | { | 306 | { |
| 307 | let ssoe = CM::MASTER == vals::Mstr::MASTER && !config.nss_output_disable; | ||
| 203 | regs.cr2().modify(|w| { | 308 | regs.cr2().modify(|w| { |
| 204 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; | 309 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; |
| 205 | w.set_frxth(frxth); | 310 | w.set_frxth(frxth); |
| 206 | w.set_ds(ds); | 311 | w.set_ds(ds); |
| 207 | w.set_ssoe(false); | 312 | w.set_ssoe(ssoe); |
| 208 | }); | 313 | }); |
| 209 | regs.cr1().modify(|w| { | 314 | regs.cr1().modify(|w| { |
| 210 | w.set_cpha(cpha); | 315 | w.set_cpha(cpha); |
| 211 | w.set_cpol(cpol); | 316 | w.set_cpol(cpol); |
| 212 | 317 | ||
| 213 | w.set_mstr(vals::Mstr::MASTER); | 318 | w.set_mstr(CM::MASTER); |
| 214 | w.set_br(br); | 319 | w.set_br(br); |
| 215 | w.set_lsbfirst(lsbfirst); | 320 | w.set_lsbfirst(lsbfirst); |
| 216 | w.set_ssi(true); | 321 | w.set_ssi(CM::MASTER == vals::Mstr::MASTER); |
| 217 | w.set_ssm(true); | 322 | w.set_ssm(ssm); |
| 218 | w.set_crcen(false); | 323 | w.set_crcen(false); |
| 219 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); | 324 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
| 220 | w.set_spe(true); | 325 | w.set_spe(true); |
| @@ -222,21 +327,22 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 222 | } | 327 | } |
| 223 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 328 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 224 | { | 329 | { |
| 330 | let ssoe = CM::MASTER == vals::Master::MASTER && !config.nss_output_disable; | ||
| 331 | let ssiop = config.raw_nss_polarity(); | ||
| 225 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); | 332 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); |
| 226 | regs.cfg2().modify(|w| { | 333 | regs.cfg2().modify(|w| { |
| 227 | //w.set_ssoe(true); | 334 | w.set_ssoe(ssoe); |
| 228 | w.set_ssoe(false); | ||
| 229 | w.set_cpha(cpha); | 335 | w.set_cpha(cpha); |
| 230 | w.set_cpol(cpol); | 336 | w.set_cpol(cpol); |
| 231 | w.set_lsbfirst(lsbfirst); | 337 | w.set_lsbfirst(lsbfirst); |
| 232 | w.set_ssm(true); | 338 | w.set_ssm(ssm); |
| 233 | w.set_master(vals::Master::MASTER); | 339 | w.set_master(CM::MASTER); |
| 234 | w.set_comm(vals::Comm::FULL_DUPLEX); | 340 | w.set_comm(vals::Comm::FULL_DUPLEX); |
| 235 | w.set_ssom(vals::Ssom::ASSERTED); | 341 | w.set_ssom(vals::Ssom::ASSERTED); |
| 236 | w.set_midi(0); | 342 | w.set_midi(0); |
| 237 | w.set_mssi(0); | 343 | w.set_mssi(0); |
| 238 | w.set_afcntr(true); | 344 | w.set_afcntr(true); |
| 239 | w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); | 345 | w.set_ssiop(ssiop); |
| 240 | }); | 346 | }); |
| 241 | regs.cfg1().modify(|w| { | 347 | regs.cfg1().modify(|w| { |
| 242 | w.set_crcen(false); | 348 | w.set_crcen(false); |
| @@ -284,6 +390,8 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 284 | 390 | ||
| 285 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 391 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 286 | { | 392 | { |
| 393 | let ssiop = config.raw_nss_polarity(); | ||
| 394 | |||
| 287 | self.info.regs.cr1().modify(|w| { | 395 | self.info.regs.cr1().modify(|w| { |
| 288 | w.set_spe(false); | 396 | w.set_spe(false); |
| 289 | }); | 397 | }); |
| @@ -292,6 +400,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 292 | w.set_cpha(cpha); | 400 | w.set_cpha(cpha); |
| 293 | w.set_cpol(cpol); | 401 | w.set_cpol(cpol); |
| 294 | w.set_lsbfirst(lsbfirst); | 402 | w.set_lsbfirst(lsbfirst); |
| 403 | w.set_ssiop(ssiop); | ||
| 295 | }); | 404 | }); |
| 296 | self.info.regs.cfg1().modify(|w| { | 405 | self.info.regs.cfg1().modify(|w| { |
| 297 | w.set_mbr(br); | 406 | w.set_mbr(br); |
| @@ -304,6 +413,20 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 304 | Ok(()) | 413 | Ok(()) |
| 305 | } | 414 | } |
| 306 | 415 | ||
| 416 | /// Set SPI direction | ||
| 417 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 418 | pub fn set_direction(&mut self, dir: Option<Direction>) { | ||
| 419 | let (bidimode, bidioe) = match dir { | ||
| 420 | Some(Direction::Transmit) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::TRANSMIT), | ||
| 421 | Some(Direction::Receive) => (vals::Bidimode::BIDIRECTIONAL, vals::Bidioe::RECEIVE), | ||
| 422 | None => (vals::Bidimode::UNIDIRECTIONAL, vals::Bidioe::TRANSMIT), | ||
| 423 | }; | ||
| 424 | self.info.regs.cr1().modify(|w| { | ||
| 425 | w.set_bidimode(bidimode); | ||
| 426 | w.set_bidioe(bidioe); | ||
| 427 | }); | ||
| 428 | } | ||
| 429 | |||
| 307 | /// Get current SPI configuration. | 430 | /// Get current SPI configuration. |
| 308 | pub fn get_current_config(&self) -> Config { | 431 | pub fn get_current_config(&self) -> Config { |
| 309 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 432 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| @@ -313,6 +436,11 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 313 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 436 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 314 | let cfg1 = self.info.regs.cfg1().read(); | 437 | let cfg1 = self.info.regs.cfg1().read(); |
| 315 | 438 | ||
| 439 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 440 | let ssoe = self.info.regs.cr2().read().ssoe(); | ||
| 441 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 442 | let ssoe = cfg.ssoe(); | ||
| 443 | |||
| 316 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { | 444 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { |
| 317 | Polarity::IdleLow | 445 | Polarity::IdleLow |
| 318 | } else { | 446 | } else { |
| @@ -342,12 +470,25 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 342 | 470 | ||
| 343 | let frequency = compute_frequency(self.kernel_clock, br); | 471 | let frequency = compute_frequency(self.kernel_clock, br); |
| 344 | 472 | ||
| 473 | // NSS output disabled if SSOE=0 or if SSM=1 software slave management enabled | ||
| 474 | let nss_output_disable = !ssoe || cfg.ssm(); | ||
| 475 | |||
| 476 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 477 | let nss_polarity = if cfg.ssiop() == vals::Ssiop::ACTIVE_LOW { | ||
| 478 | SlaveSelectPolarity::ActiveLow | ||
| 479 | } else { | ||
| 480 | SlaveSelectPolarity::ActiveHigh | ||
| 481 | }; | ||
| 482 | |||
| 345 | Config { | 483 | Config { |
| 346 | mode: Mode { polarity, phase }, | 484 | mode: Mode { polarity, phase }, |
| 347 | bit_order, | 485 | bit_order, |
| 348 | frequency, | 486 | frequency, |
| 349 | miso_pull, | 487 | miso_pull, |
| 350 | gpio_speed: self.gpio_speed, | 488 | gpio_speed: self.gpio_speed, |
| 489 | nss_output_disable, | ||
| 490 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | ||
| 491 | nss_polarity, | ||
| 351 | } | 492 | } |
| 352 | } | 493 | } |
| 353 | 494 | ||
| @@ -469,7 +610,30 @@ impl<'d, M: PeriMode> Spi<'d, M> { | |||
| 469 | } | 610 | } |
| 470 | } | 611 | } |
| 471 | 612 | ||
| 472 | impl<'d> Spi<'d, Blocking> { | 613 | impl<'d> Spi<'d, Blocking, Slave> { |
| 614 | /// Create a new blocking SPI slave driver. | ||
| 615 | pub fn new_blocking_slave<T: Instance, #[cfg(afio)] A>( | ||
| 616 | peri: Peri<'d, T>, | ||
| 617 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 618 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 619 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 620 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 621 | config: Config, | ||
| 622 | ) -> Self { | ||
| 623 | Self::new_inner( | ||
| 624 | peri, | ||
| 625 | new_pin!(sck, config.sck_af()), | ||
| 626 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 627 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 628 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 629 | None, | ||
| 630 | None, | ||
| 631 | config, | ||
| 632 | ) | ||
| 633 | } | ||
| 634 | } | ||
| 635 | |||
| 636 | impl<'d> Spi<'d, Blocking, Master> { | ||
| 473 | /// Create a new blocking SPI driver. | 637 | /// Create a new blocking SPI driver. |
| 474 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( | 638 | pub fn new_blocking<T: Instance, #[cfg(afio)] A>( |
| 475 | peri: Peri<'d, T>, | 639 | peri: Peri<'d, T>, |
| @@ -485,6 +649,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 485 | new_pin!(miso, AfType::input(config.miso_pull)), | 649 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 486 | None, | 650 | None, |
| 487 | None, | 651 | None, |
| 652 | None, | ||
| 488 | config, | 653 | config, |
| 489 | ) | 654 | ) |
| 490 | } | 655 | } |
| @@ -503,6 +668,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 503 | new_pin!(miso, AfType::input(config.miso_pull)), | 668 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 504 | None, | 669 | None, |
| 505 | None, | 670 | None, |
| 671 | None, | ||
| 506 | config, | 672 | config, |
| 507 | ) | 673 | ) |
| 508 | } | 674 | } |
| @@ -521,6 +687,7 @@ impl<'d> Spi<'d, Blocking> { | |||
| 521 | None, | 687 | None, |
| 522 | None, | 688 | None, |
| 523 | None, | 689 | None, |
| 690 | None, | ||
| 524 | config, | 691 | config, |
| 525 | ) | 692 | ) |
| 526 | } | 693 | } |
| @@ -540,12 +707,38 @@ impl<'d> Spi<'d, Blocking> { | |||
| 540 | None, | 707 | None, |
| 541 | None, | 708 | None, |
| 542 | None, | 709 | None, |
| 710 | None, | ||
| 543 | config, | 711 | config, |
| 544 | ) | 712 | ) |
| 545 | } | 713 | } |
| 546 | } | 714 | } |
| 547 | 715 | ||
| 548 | impl<'d> Spi<'d, Async> { | 716 | impl<'d> Spi<'d, Async, Slave> { |
| 717 | /// Create a new SPI slave driver. | ||
| 718 | pub fn new_slave<T: Instance, #[cfg(afio)] A>( | ||
| 719 | peri: Peri<'d, T>, | ||
| 720 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 721 | mosi: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 722 | miso: Peri<'d, if_afio!(impl MisoPin<T, A>)>, | ||
| 723 | cs: Peri<'d, if_afio!(impl CsPin<T, A>)>, | ||
| 724 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 725 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 726 | config: Config, | ||
| 727 | ) -> Self { | ||
| 728 | Self::new_inner( | ||
| 729 | peri, | ||
| 730 | new_pin!(sck, config.sck_af()), | ||
| 731 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 732 | new_pin!(miso, AfType::input(config.miso_pull)), | ||
| 733 | new_pin!(cs, AfType::input(Pull::None)), | ||
| 734 | new_dma!(tx_dma), | ||
| 735 | new_dma!(rx_dma), | ||
| 736 | config, | ||
| 737 | ) | ||
| 738 | } | ||
| 739 | } | ||
| 740 | |||
| 741 | impl<'d> Spi<'d, Async, Master> { | ||
| 549 | /// Create a new SPI driver. | 742 | /// Create a new SPI driver. |
| 550 | pub fn new<T: Instance, #[cfg(afio)] A>( | 743 | pub fn new<T: Instance, #[cfg(afio)] A>( |
| 551 | peri: Peri<'d, T>, | 744 | peri: Peri<'d, T>, |
| @@ -561,6 +754,7 @@ impl<'d> Spi<'d, Async> { | |||
| 561 | new_pin!(sck, config.sck_af()), | 754 | new_pin!(sck, config.sck_af()), |
| 562 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 755 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 563 | new_pin!(miso, AfType::input(config.miso_pull)), | 756 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 757 | None, | ||
| 564 | new_dma!(tx_dma), | 758 | new_dma!(tx_dma), |
| 565 | new_dma!(rx_dma), | 759 | new_dma!(rx_dma), |
| 566 | config, | 760 | config, |
| @@ -581,6 +775,7 @@ impl<'d> Spi<'d, Async> { | |||
| 581 | new_pin!(sck, config.sck_af()), | 775 | new_pin!(sck, config.sck_af()), |
| 582 | None, | 776 | None, |
| 583 | new_pin!(miso, AfType::input(config.miso_pull)), | 777 | new_pin!(miso, AfType::input(config.miso_pull)), |
| 778 | None, | ||
| 584 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 779 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 585 | new_dma!(tx_dma), | 780 | new_dma!(tx_dma), |
| 586 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 781 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| @@ -603,12 +798,37 @@ impl<'d> Spi<'d, Async> { | |||
| 603 | new_pin!(sck, config.sck_af()), | 798 | new_pin!(sck, config.sck_af()), |
| 604 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 799 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 605 | None, | 800 | None, |
| 801 | None, | ||
| 606 | new_dma!(tx_dma), | 802 | new_dma!(tx_dma), |
| 607 | None, | 803 | None, |
| 608 | config, | 804 | config, |
| 609 | ) | 805 | ) |
| 610 | } | 806 | } |
| 611 | 807 | ||
| 808 | /// Create a new SPI driver, in bidirectional mode, specifically in tranmit mode | ||
| 809 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | ||
| 810 | pub fn new_bidi<T: Instance, #[cfg(afio)] A>( | ||
| 811 | peri: Peri<'d, T>, | ||
| 812 | sck: Peri<'d, if_afio!(impl SckPin<T, A>)>, | ||
| 813 | sdio: Peri<'d, if_afio!(impl MosiPin<T, A>)>, | ||
| 814 | tx_dma: Peri<'d, impl TxDma<T>>, | ||
| 815 | rx_dma: Peri<'d, impl RxDma<T>>, | ||
| 816 | config: Config, | ||
| 817 | ) -> Self { | ||
| 818 | let mut this = Self::new_inner( | ||
| 819 | peri, | ||
| 820 | new_pin!(sck, config.sck_af()), | ||
| 821 | new_pin!(sdio, AfType::output(OutputType::PushPull, config.gpio_speed)), | ||
| 822 | None, | ||
| 823 | None, | ||
| 824 | new_dma!(tx_dma), | ||
| 825 | new_dma!(rx_dma), | ||
| 826 | config, | ||
| 827 | ); | ||
| 828 | this.set_direction(Some(Direction::Transmit)); | ||
| 829 | this | ||
| 830 | } | ||
| 831 | |||
| 612 | /// Create a new SPI driver, in TX-only mode, without SCK pin. | 832 | /// Create a new SPI driver, in TX-only mode, without SCK pin. |
| 613 | /// | 833 | /// |
| 614 | /// This can be useful for bit-banging non-SPI protocols. | 834 | /// This can be useful for bit-banging non-SPI protocols. |
| @@ -623,6 +843,7 @@ impl<'d> Spi<'d, Async> { | |||
| 623 | None, | 843 | None, |
| 624 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), | 844 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.gpio_speed)), |
| 625 | None, | 845 | None, |
| 846 | None, | ||
| 626 | new_dma!(tx_dma), | 847 | new_dma!(tx_dma), |
| 627 | None, | 848 | None, |
| 628 | config, | 849 | config, |
| @@ -646,7 +867,7 @@ impl<'d> Spi<'d, Async> { | |||
| 646 | config.bit_order = BitOrder::MsbFirst; | 867 | config.bit_order = BitOrder::MsbFirst; |
| 647 | config.frequency = freq; | 868 | config.frequency = freq; |
| 648 | 869 | ||
| 649 | Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) | 870 | Self::new_inner(peri, None, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) |
| 650 | } | 871 | } |
| 651 | 872 | ||
| 652 | #[allow(dead_code)] | 873 | #[allow(dead_code)] |
| @@ -656,11 +877,14 @@ impl<'d> Spi<'d, Async> { | |||
| 656 | rx_dma: Option<ChannelAndRequest<'d>>, | 877 | rx_dma: Option<ChannelAndRequest<'d>>, |
| 657 | config: Config, | 878 | config: Config, |
| 658 | ) -> Self { | 879 | ) -> Self { |
| 659 | Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) | 880 | Self::new_inner(peri, None, None, None, None, tx_dma, rx_dma, config) |
| 660 | } | 881 | } |
| 882 | } | ||
| 661 | 883 | ||
| 884 | impl<'d, CM: CommunicationMode> Spi<'d, Async, CM> { | ||
| 662 | /// SPI write, using DMA. | 885 | /// SPI write, using DMA. |
| 663 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { | 886 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
| 887 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 664 | if data.is_empty() { | 888 | if data.is_empty() { |
| 665 | return Ok(()); | 889 | return Ok(()); |
| 666 | } | 890 | } |
| @@ -692,6 +916,7 @@ impl<'d> Spi<'d, Async> { | |||
| 692 | /// SPI read, using DMA. | 916 | /// SPI read, using DMA. |
| 693 | #[cfg(any(spi_v4, spi_v5, spi_v6))] | 917 | #[cfg(any(spi_v4, spi_v5, spi_v6))] |
| 694 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 918 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 919 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 695 | if data.is_empty() { | 920 | if data.is_empty() { |
| 696 | return Ok(()); | 921 | return Ok(()); |
| 697 | } | 922 | } |
| @@ -779,6 +1004,7 @@ impl<'d> Spi<'d, Async> { | |||
| 779 | /// SPI read, using DMA. | 1004 | /// SPI read, using DMA. |
| 780 | #[cfg(any(spi_v1, spi_v2, spi_v3))] | 1005 | #[cfg(any(spi_v1, spi_v2, spi_v3))] |
| 781 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 1006 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 1007 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 782 | if data.is_empty() { | 1008 | if data.is_empty() { |
| 783 | return Ok(()); | 1009 | return Ok(()); |
| 784 | } | 1010 | } |
| @@ -826,6 +1052,7 @@ impl<'d> Spi<'d, Async> { | |||
| 826 | } | 1052 | } |
| 827 | 1053 | ||
| 828 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { | 1054 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { |
| 1055 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 829 | assert_eq!(read.len(), write.len()); | 1056 | assert_eq!(read.len(), write.len()); |
| 830 | if read.len() == 0 { | 1057 | if read.len() == 0 { |
| 831 | return Ok(()); | 1058 | return Ok(()); |
| @@ -877,6 +1104,8 @@ impl<'d> Spi<'d, Async> { | |||
| 877 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. | 1104 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. |
| 878 | /// If `write` is shorter it is padded with zero bytes. | 1105 | /// If `write` is shorter it is padded with zero bytes. |
| 879 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { | 1106 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
| 1107 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1108 | |||
| 880 | self.transfer_inner(read, write).await | 1109 | self.transfer_inner(read, write).await |
| 881 | } | 1110 | } |
| 882 | 1111 | ||
| @@ -884,17 +1113,20 @@ impl<'d> Spi<'d, Async> { | |||
| 884 | /// | 1113 | /// |
| 885 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. | 1114 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. |
| 886 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { | 1115 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
| 1116 | let _scoped_block_stop = self.info.rcc.block_stop(); | ||
| 1117 | |||
| 887 | self.transfer_inner(data, data).await | 1118 | self.transfer_inner(data, data).await |
| 888 | } | 1119 | } |
| 889 | } | 1120 | } |
| 890 | 1121 | ||
| 891 | impl<'d, M: PeriMode> Drop for Spi<'d, M> { | 1122 | impl<'d, M: PeriMode, CM: CommunicationMode> Drop for Spi<'d, M, CM> { |
| 892 | fn drop(&mut self) { | 1123 | fn drop(&mut self) { |
| 893 | self.sck.as_ref().map(|x| x.set_as_disconnected()); | 1124 | self.sck.as_ref().map(|x| x.set_as_disconnected()); |
| 894 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); | 1125 | self.mosi.as_ref().map(|x| x.set_as_disconnected()); |
| 895 | self.miso.as_ref().map(|x| x.set_as_disconnected()); | 1126 | self.miso.as_ref().map(|x| x.set_as_disconnected()); |
| 1127 | self.nss.as_ref().map(|x| x.set_as_disconnected()); | ||
| 896 | 1128 | ||
| 897 | self.info.rcc.disable(); | 1129 | self.info.rcc.disable_without_stop(); |
| 898 | } | 1130 | } |
| 899 | } | 1131 | } |
| 900 | 1132 | ||
| @@ -1127,7 +1359,7 @@ fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> { | |||
| 1127 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 | 1359 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 |
| 1128 | macro_rules! impl_blocking { | 1360 | macro_rules! impl_blocking { |
| 1129 | ($w:ident) => { | 1361 | ($w:ident) => { |
| 1130 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { | 1362 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M, CM> { |
| 1131 | type Error = Error; | 1363 | type Error = Error; |
| 1132 | 1364 | ||
| 1133 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { | 1365 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { |
| @@ -1135,7 +1367,7 @@ macro_rules! impl_blocking { | |||
| 1135 | } | 1367 | } |
| 1136 | } | 1368 | } |
| 1137 | 1369 | ||
| 1138 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { | 1370 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M, CM> { |
| 1139 | type Error = Error; | 1371 | type Error = Error; |
| 1140 | 1372 | ||
| 1141 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { | 1373 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { |
| @@ -1149,11 +1381,11 @@ macro_rules! impl_blocking { | |||
| 1149 | impl_blocking!(u8); | 1381 | impl_blocking!(u8); |
| 1150 | impl_blocking!(u16); | 1382 | impl_blocking!(u16); |
| 1151 | 1383 | ||
| 1152 | impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { | 1384 | impl<'d, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::ErrorType for Spi<'d, M, CM> { |
| 1153 | type Error = Error; | 1385 | type Error = Error; |
| 1154 | } | 1386 | } |
| 1155 | 1387 | ||
| 1156 | impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { | 1388 | impl<'d, W: Word, M: PeriMode, CM: CommunicationMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M, CM> { |
| 1157 | fn flush(&mut self) -> Result<(), Self::Error> { | 1389 | fn flush(&mut self) -> Result<(), Self::Error> { |
| 1158 | Ok(()) | 1390 | Ok(()) |
| 1159 | } | 1391 | } |
| @@ -1186,7 +1418,7 @@ impl embedded_hal_1::spi::Error for Error { | |||
| 1186 | } | 1418 | } |
| 1187 | } | 1419 | } |
| 1188 | 1420 | ||
| 1189 | impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { | 1421 | impl<'d, W: Word, CM: CommunicationMode> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async, CM> { |
| 1190 | async fn flush(&mut self) -> Result<(), Self::Error> { | 1422 | async fn flush(&mut self) -> Result<(), Self::Error> { |
| 1191 | Ok(()) | 1423 | Ok(()) |
| 1192 | } | 1424 | } |
| @@ -1328,7 +1560,7 @@ foreach_peripheral!( | |||
| 1328 | }; | 1560 | }; |
| 1329 | ); | 1561 | ); |
| 1330 | 1562 | ||
| 1331 | impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { | 1563 | impl<'d, M: PeriMode, CM: CommunicationMode> SetConfig for Spi<'d, M, CM> { |
| 1332 | type Config = Config; | 1564 | type Config = Config; |
| 1333 | type ConfigError = (); | 1565 | type ConfigError = (); |
| 1334 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { | 1566 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
