diff options
| -rw-r--r-- | embassy-stm32/src/can/fdcan.rs | 714 | ||||
| -rw-r--r-- | embassy-stm32/src/rcc/h.rs | 7 |
2 files changed, 706 insertions, 15 deletions
diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 0cc2559cf..faf4af73f 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs | |||
| @@ -1,14 +1,577 @@ | |||
| 1 | use crate::peripherals; | 1 | use core::future::poll_fn; |
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::ops::{Deref, DerefMut}; | ||
| 4 | use core::task::Poll; | ||
| 5 | |||
| 6 | use cfg_if::cfg_if; | ||
| 7 | use embassy_hal_internal::{into_ref, PeripheralRef}; | ||
| 8 | pub use fdcan::frame::{FrameFormat, RxFrameInfo, TxFrameHeader}; | ||
| 9 | pub use fdcan::id::{ExtendedId, Id, StandardId}; | ||
| 10 | use fdcan::message_ram::RegisterBlock; | ||
| 11 | use fdcan::{self, LastErrorCode}; | ||
| 12 | pub use fdcan::{config, filter}; | ||
| 13 | |||
| 14 | use crate::gpio::sealed::AFType; | ||
| 15 | use crate::interrupt::typelevel::Interrupt; | ||
| 16 | use crate::rcc::RccPeripheral; | ||
| 17 | use crate::{interrupt, peripherals, Peripheral}; | ||
| 18 | |||
| 19 | pub mod enums; | ||
| 20 | use enums::*; | ||
| 21 | pub mod util; | ||
| 22 | |||
| 23 | /// CAN Frame returned by read | ||
| 24 | pub struct RxFrame { | ||
| 25 | /// CAN Header info: frame ID, data length and other meta | ||
| 26 | pub header: RxFrameInfo, | ||
| 27 | /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data | ||
| 28 | pub data: Data, | ||
| 29 | /// Reception time. | ||
| 30 | #[cfg(feature = "time")] | ||
| 31 | pub timestamp: embassy_time::Instant, | ||
| 32 | } | ||
| 33 | |||
| 34 | /// CAN frame used for write | ||
| 35 | pub struct TxFrame { | ||
| 36 | /// CAN Header info: frame ID, data length and other meta | ||
| 37 | pub header: TxFrameHeader, | ||
| 38 | /// CAN(0-8 bytes) or FDCAN(0-64 bytes) Frame data | ||
| 39 | pub data: Data, | ||
| 40 | } | ||
| 41 | |||
| 42 | impl TxFrame { | ||
| 43 | /// Create new TX frame from header and data | ||
| 44 | pub fn new(header: TxFrameHeader, data: &[u8]) -> Option<Self> { | ||
| 45 | if data.len() < header.len as usize { | ||
| 46 | return None; | ||
| 47 | } | ||
| 48 | |||
| 49 | let Some(data) = Data::new(data) else { return None }; | ||
| 50 | |||
| 51 | Some(TxFrame { header, data }) | ||
| 52 | } | ||
| 53 | |||
| 54 | fn from_preserved(header: TxFrameHeader, data32: &[u32]) -> Option<Self> { | ||
| 55 | let mut data = [0u8; 64]; | ||
| 56 | |||
| 57 | for i in 0..data32.len() { | ||
| 58 | data[4 * i..][..4].copy_from_slice(&data32[i].to_le_bytes()); | ||
| 59 | } | ||
| 60 | |||
| 61 | let Some(data) = Data::new(&data) else { return None }; | ||
| 62 | |||
| 63 | Some(TxFrame { header, data }) | ||
| 64 | } | ||
| 65 | |||
| 66 | /// Access frame data. Slice length will match header. | ||
| 67 | pub fn data(&self) -> &[u8] { | ||
| 68 | &self.data.bytes[..(self.header.len as usize)] | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | impl RxFrame { | ||
| 73 | pub(crate) fn new( | ||
| 74 | header: RxFrameInfo, | ||
| 75 | data: &[u8], | ||
| 76 | #[cfg(feature = "time")] timestamp: embassy_time::Instant, | ||
| 77 | ) -> Self { | ||
| 78 | let data = Data::new(&data).unwrap_or_else(|| Data::empty()); | ||
| 79 | |||
| 80 | RxFrame { | ||
| 81 | header, | ||
| 82 | data, | ||
| 83 | #[cfg(feature = "time")] | ||
| 84 | timestamp, | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | /// Access frame data. Slice length will match header. | ||
| 89 | pub fn data(&self) -> &[u8] { | ||
| 90 | &self.data.bytes[..(self.header.len as usize)] | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | /// Payload of a (FD)CAN data frame. | ||
| 95 | /// | ||
| 96 | /// Contains 0 to 64 Bytes of data. | ||
| 97 | #[derive(Debug, Copy, Clone)] | ||
| 98 | pub struct Data { | ||
| 99 | pub(crate) bytes: [u8; 64], | ||
| 100 | } | ||
| 101 | |||
| 102 | impl Data { | ||
| 103 | /// Creates a data payload from a raw byte slice. | ||
| 104 | /// | ||
| 105 | /// Returns `None` if `data` is more than 64 bytes (which is the maximum) or | ||
| 106 | /// cannot be represented with an FDCAN DLC. | ||
| 107 | pub fn new(data: &[u8]) -> Option<Self> { | ||
| 108 | if !Data::is_valid_len(data.len()) { | ||
| 109 | return None; | ||
| 110 | } | ||
| 111 | |||
| 112 | let mut bytes = [0; 64]; | ||
| 113 | bytes[..data.len()].copy_from_slice(data); | ||
| 114 | |||
| 115 | Some(Self { bytes }) | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Raw read access to data. | ||
| 119 | pub fn raw(&self) -> &[u8] { | ||
| 120 | &self.bytes | ||
| 121 | } | ||
| 122 | |||
| 123 | /// Checks if the length can be encoded in FDCAN DLC field. | ||
| 124 | pub const fn is_valid_len(len: usize) -> bool { | ||
| 125 | match len { | ||
| 126 | 0..=8 => true, | ||
| 127 | 12 => true, | ||
| 128 | 16 => true, | ||
| 129 | 20 => true, | ||
| 130 | 24 => true, | ||
| 131 | 32 => true, | ||
| 132 | 48 => true, | ||
| 133 | 64 => true, | ||
| 134 | _ => false, | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | /// Creates an empty data payload containing 0 bytes. | ||
| 139 | #[inline] | ||
| 140 | pub const fn empty() -> Self { | ||
| 141 | Self { bytes: [0; 64] } | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Interrupt handler channel 0. | ||
| 146 | pub struct IT0InterruptHandler<T: Instance> { | ||
| 147 | _phantom: PhantomData<T>, | ||
| 148 | } | ||
| 149 | |||
| 150 | // We use IT0 for everything currently | ||
| 151 | impl<T: Instance> interrupt::typelevel::Handler<T::IT0Interrupt> for IT0InterruptHandler<T> { | ||
| 152 | unsafe fn on_interrupt() { | ||
| 153 | let regs = T::regs(); | ||
| 154 | |||
| 155 | let ir = regs.ir().read(); | ||
| 156 | |||
| 157 | if ir.tc() { | ||
| 158 | regs.ir().write(|w| w.set_tc(true)); | ||
| 159 | T::state().tx_waker.wake(); | ||
| 160 | } | ||
| 161 | |||
| 162 | if ir.tefn() { | ||
| 163 | regs.ir().write(|w| w.set_tefn(true)); | ||
| 164 | T::state().tx_waker.wake(); | ||
| 165 | } | ||
| 166 | |||
| 167 | if ir.ped() || ir.pea() { | ||
| 168 | regs.ir().write(|w| { | ||
| 169 | w.set_ped(true); | ||
| 170 | w.set_pea(true); | ||
| 171 | }); | ||
| 172 | } | ||
| 173 | |||
| 174 | if ir.rfn(0) { | ||
| 175 | regs.ir().write(|w| w.set_rfn(0, true)); | ||
| 176 | T::state().rx_waker.wake(); | ||
| 177 | } | ||
| 178 | |||
| 179 | if ir.rfn(1) { | ||
| 180 | regs.ir().write(|w| w.set_rfn(1, true)); | ||
| 181 | T::state().rx_waker.wake(); | ||
| 182 | } | ||
| 183 | } | ||
| 184 | } | ||
| 185 | |||
| 186 | /// Interrupt handler channel 1. | ||
| 187 | pub struct IT1InterruptHandler<T: Instance> { | ||
| 188 | _phantom: PhantomData<T>, | ||
| 189 | } | ||
| 190 | |||
| 191 | impl<T: Instance> interrupt::typelevel::Handler<T::IT1Interrupt> for IT1InterruptHandler<T> { | ||
| 192 | unsafe fn on_interrupt() {} | ||
| 193 | } | ||
| 194 | |||
| 195 | impl BusError { | ||
| 196 | fn try_from(lec: LastErrorCode) -> Option<BusError> { | ||
| 197 | match lec { | ||
| 198 | LastErrorCode::AckError => Some(BusError::Acknowledge), | ||
| 199 | // `0` data bit encodes a dominant state. `1` data bit is recessive. | ||
| 200 | // Bit0Error: During transmit, the node wanted to send a 0 but monitored a 1 | ||
| 201 | LastErrorCode::Bit0Error => Some(BusError::BitRecessive), | ||
| 202 | LastErrorCode::Bit1Error => Some(BusError::BitDominant), | ||
| 203 | LastErrorCode::CRCError => Some(BusError::Crc), | ||
| 204 | LastErrorCode::FormError => Some(BusError::Form), | ||
| 205 | LastErrorCode::StuffError => Some(BusError::Stuff), | ||
| 206 | _ => None, | ||
| 207 | } | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | /// Operating modes trait | ||
| 212 | pub trait FdcanOperatingMode {} | ||
| 213 | impl FdcanOperatingMode for fdcan::PoweredDownMode {} | ||
| 214 | impl FdcanOperatingMode for fdcan::ConfigMode {} | ||
| 215 | impl FdcanOperatingMode for fdcan::InternalLoopbackMode {} | ||
| 216 | impl FdcanOperatingMode for fdcan::ExternalLoopbackMode {} | ||
| 217 | impl FdcanOperatingMode for fdcan::NormalOperationMode {} | ||
| 218 | impl FdcanOperatingMode for fdcan::RestrictedOperationMode {} | ||
| 219 | impl FdcanOperatingMode for fdcan::BusMonitoringMode {} | ||
| 220 | impl FdcanOperatingMode for fdcan::TestMode {} | ||
| 221 | |||
| 222 | /// FDCAN Instance | ||
| 223 | pub struct Fdcan<'d, T: Instance, M: FdcanOperatingMode> { | ||
| 224 | /// Reference to internals. | ||
| 225 | pub can: fdcan::FdCan<FdcanInstance<'d, T>, M>, | ||
| 226 | ns_per_timer_tick: u64, // For FDCAN internal timer | ||
| 227 | } | ||
| 228 | |||
| 229 | fn calc_ns_per_timer_tick<T: Instance>(mode: config::FrameTransmissionConfig) -> u64 { | ||
| 230 | match mode { | ||
| 231 | // Use timestamp from Rx FIFO to adjust timestamp reported to user | ||
| 232 | config::FrameTransmissionConfig::ClassicCanOnly => { | ||
| 233 | let freq = T::frequency(); | ||
| 234 | let prescale: u64 = | ||
| 235 | ({ T::regs().nbtp().read().nbrp() } + 1) as u64 * ({ T::regs().tscc().read().tcp() } + 1) as u64; | ||
| 236 | 1_000_000_000 as u64 / (freq.0 as u64 * prescale) | ||
| 237 | } | ||
| 238 | // For VBR this is too hard because the FDCAN timer switches clock rate you need to configure to use | ||
| 239 | // timer3 instead which is too hard to do from this module. | ||
| 240 | _ => 0, | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | #[cfg(feature = "time")] | ||
| 245 | fn calc_timestamp<T: Instance>(ns_per_timer_tick: u64, ts_val: u16) -> embassy_time::Instant { | ||
| 246 | let now_embassy = embassy_time::Instant::now(); | ||
| 247 | if ns_per_timer_tick == 0 { | ||
| 248 | return now_embassy; | ||
| 249 | } | ||
| 250 | let now_can = { T::regs().tscv().read().tsc() }; | ||
| 251 | let delta = now_can.overflowing_sub(ts_val).0 as u64; | ||
| 252 | let ns = ns_per_timer_tick * delta as u64; | ||
| 253 | now_embassy - embassy_time::Duration::from_nanos(ns) | ||
| 254 | } | ||
| 255 | |||
| 256 | fn curr_error<T: Instance>() -> Option<BusError> { | ||
| 257 | let err = { T::regs().psr().read() }; | ||
| 258 | if err.bo() { | ||
| 259 | return Some(BusError::BusOff); | ||
| 260 | } else if err.ep() { | ||
| 261 | return Some(BusError::BusPassive); | ||
| 262 | } else if err.ew() { | ||
| 263 | return Some(BusError::BusWarning); | ||
| 264 | } else { | ||
| 265 | cfg_if! { | ||
| 266 | if #[cfg(stm32h7)] { | ||
| 267 | let lec = err.lec(); | ||
| 268 | } else { | ||
| 269 | let lec = err.lec().to_bits(); | ||
| 270 | } | ||
| 271 | } | ||
| 272 | if let Ok(err) = LastErrorCode::try_from(lec) { | ||
| 273 | return BusError::try_from(err); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | None | ||
| 277 | } | ||
| 278 | |||
| 279 | impl<'d, T: Instance> Fdcan<'d, T, fdcan::ConfigMode> { | ||
| 280 | /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. | ||
| 281 | /// You must call [Fdcan::enable_non_blocking] to use the peripheral. | ||
| 282 | pub fn new( | ||
| 283 | peri: impl Peripheral<P = T> + 'd, | ||
| 284 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | ||
| 285 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | ||
| 286 | _irqs: impl interrupt::typelevel::Binding<T::IT0Interrupt, IT0InterruptHandler<T>> | ||
| 287 | + interrupt::typelevel::Binding<T::IT1Interrupt, IT1InterruptHandler<T>> | ||
| 288 | + 'd, | ||
| 289 | ) -> Fdcan<'d, T, fdcan::ConfigMode> { | ||
| 290 | into_ref!(peri, rx, tx); | ||
| 291 | |||
| 292 | rx.set_as_af(rx.af_num(), AFType::Input); | ||
| 293 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | ||
| 294 | |||
| 295 | T::enable_and_reset(); | ||
| 296 | |||
| 297 | rx.set_as_af(rx.af_num(), AFType::Input); | ||
| 298 | tx.set_as_af(tx.af_num(), AFType::OutputPushPull); | ||
| 299 | |||
| 300 | let mut can = fdcan::FdCan::new(FdcanInstance(peri)).into_config_mode(); | ||
| 301 | |||
| 302 | T::configure_msg_ram(); | ||
| 303 | unsafe { | ||
| 304 | // Enable timestamping | ||
| 305 | #[cfg(not(stm32h7))] | ||
| 306 | T::regs() | ||
| 307 | .tscc() | ||
| 308 | .write(|w| w.set_tss(stm32_metapac::can::vals::Tss::INCREMENT)); | ||
| 309 | #[cfg(stm32h7)] | ||
| 310 | T::regs().tscc().write(|w| w.set_tss(0x01)); | ||
| 311 | |||
| 312 | T::IT0Interrupt::unpend(); // Not unsafe | ||
| 313 | T::IT0Interrupt::enable(); | ||
| 314 | |||
| 315 | T::IT1Interrupt::unpend(); // Not unsafe | ||
| 316 | T::IT1Interrupt::enable(); | ||
| 317 | |||
| 318 | // this isn't really documented in the reference manual | ||
| 319 | // but corresponding txbtie bit has to be set for the TC (TxComplete) interrupt to fire | ||
| 320 | T::regs().txbtie().write(|w| w.0 = 0xffff_ffff); | ||
| 321 | } | ||
| 322 | |||
| 323 | can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo0NewMsg); | ||
| 324 | can.enable_interrupt(fdcan::interrupt::Interrupt::RxFifo1NewMsg); | ||
| 325 | can.enable_interrupt(fdcan::interrupt::Interrupt::TxComplete); | ||
| 326 | can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_0, true); | ||
| 327 | can.enable_interrupt_line(fdcan::interrupt::InterruptLine::_1, true); | ||
| 328 | |||
| 329 | let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(can.get_config().frame_transmit); | ||
| 330 | Self { can, ns_per_timer_tick } | ||
| 331 | } | ||
| 332 | |||
| 333 | /// Configures the bit timings calculated from supplied bitrate. | ||
| 334 | pub fn set_bitrate(&mut self, bitrate: u32) { | ||
| 335 | let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); | ||
| 336 | self.can.set_nominal_bit_timing(config::NominalBitTiming { | ||
| 337 | sync_jump_width: bit_timing.sync_jump_width, | ||
| 338 | prescaler: bit_timing.prescaler, | ||
| 339 | seg1: bit_timing.seg1, | ||
| 340 | seg2: bit_timing.seg2, | ||
| 341 | }); | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | macro_rules! impl_transition { | ||
| 346 | ($from_mode:ident, $to_mode:ident, $name:ident, $func: ident) => { | ||
| 347 | impl<'d, T: Instance> Fdcan<'d, T, fdcan::$from_mode> { | ||
| 348 | /// Transition from $from_mode:ident mode to $to_mode:ident mode | ||
| 349 | pub fn $name(self) -> Fdcan<'d, T, fdcan::$to_mode> { | ||
| 350 | let ns_per_timer_tick = calc_ns_per_timer_tick::<T>(self.can.get_config().frame_transmit); | ||
| 351 | Fdcan { | ||
| 352 | can: self.can.$func(), | ||
| 353 | ns_per_timer_tick, | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | }; | ||
| 358 | } | ||
| 359 | |||
| 360 | impl_transition!(PoweredDownMode, ConfigMode, into_config_mode, into_config_mode); | ||
| 361 | impl_transition!(InternalLoopbackMode, ConfigMode, into_config_mode, into_config_mode); | ||
| 362 | |||
| 363 | impl_transition!(ConfigMode, NormalOperationMode, into_normal_mode, into_normal); | ||
| 364 | impl_transition!( | ||
| 365 | ConfigMode, | ||
| 366 | ExternalLoopbackMode, | ||
| 367 | into_external_loopback_mode, | ||
| 368 | into_external_loopback | ||
| 369 | ); | ||
| 370 | impl_transition!( | ||
| 371 | ConfigMode, | ||
| 372 | InternalLoopbackMode, | ||
| 373 | into_internal_loopback_mode, | ||
| 374 | into_internal_loopback | ||
| 375 | ); | ||
| 376 | |||
| 377 | impl<'d, T: Instance, M: FdcanOperatingMode> Fdcan<'d, T, M> | ||
| 378 | where | ||
| 379 | M: fdcan::Transmit, | ||
| 380 | M: fdcan::Receive, | ||
| 381 | { | ||
| 382 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 383 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 384 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 385 | /// transmitted, then tries again. | ||
| 386 | pub async fn write(&mut self, frame: &TxFrame) -> Option<TxFrame> { | ||
| 387 | poll_fn(|cx| { | ||
| 388 | T::state().tx_waker.register(cx.waker()); | ||
| 389 | if let Ok(dropped) = self | ||
| 390 | .can | ||
| 391 | .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { | ||
| 392 | TxFrame::from_preserved(hdr, data32) | ||
| 393 | }) | ||
| 394 | { | ||
| 395 | return Poll::Ready(dropped.flatten()); | ||
| 396 | } | ||
| 397 | |||
| 398 | // Couldn't replace any lower priority frames. Need to wait for some mailboxes | ||
| 399 | // to clear. | ||
| 400 | Poll::Pending | ||
| 401 | }) | ||
| 402 | .await | ||
| 403 | } | ||
| 404 | |||
| 405 | /// Flush one of the TX mailboxes. | ||
| 406 | pub async fn flush(&self, mb: fdcan::Mailbox) { | ||
| 407 | poll_fn(|cx| { | ||
| 408 | T::state().tx_waker.register(cx.waker()); | ||
| 409 | |||
| 410 | let idx: u8 = mb.into(); | ||
| 411 | let idx = 1 << idx; | ||
| 412 | if !T::regs().txbrp().read().trp(idx) { | ||
| 413 | return Poll::Ready(()); | ||
| 414 | } | ||
| 415 | |||
| 416 | Poll::Pending | ||
| 417 | }) | ||
| 418 | .await; | ||
| 419 | } | ||
| 420 | |||
| 421 | /// Returns the next received message frame | ||
| 422 | pub async fn read(&mut self) -> Result<RxFrame, BusError> { | ||
| 423 | poll_fn(|cx| { | ||
| 424 | T::state().err_waker.register(cx.waker()); | ||
| 425 | T::state().rx_waker.register(cx.waker()); | ||
| 426 | |||
| 427 | let mut buffer: [u8; 64] = [0; 64]; | ||
| 428 | if let Ok(rx) = self.can.receive0(&mut buffer) { | ||
| 429 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 430 | // TODO: report overrun? | ||
| 431 | // for now we just drop it | ||
| 432 | |||
| 433 | let frame: RxFrame = RxFrame::new( | ||
| 434 | rx.unwrap(), | ||
| 435 | &buffer, | ||
| 436 | #[cfg(feature = "time")] | ||
| 437 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 438 | ); | ||
| 439 | return Poll::Ready(Ok(frame)); | ||
| 440 | } else if let Ok(rx) = self.can.receive1(&mut buffer) { | ||
| 441 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 442 | // TODO: report overrun? | ||
| 443 | // for now we just drop it | ||
| 444 | |||
| 445 | let frame: RxFrame = RxFrame::new( | ||
| 446 | rx.unwrap(), | ||
| 447 | &buffer, | ||
| 448 | #[cfg(feature = "time")] | ||
| 449 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 450 | ); | ||
| 451 | return Poll::Ready(Ok(frame)); | ||
| 452 | } else if let Some(err) = curr_error::<T>() { | ||
| 453 | // TODO: this is probably wrong | ||
| 454 | return Poll::Ready(Err(err)); | ||
| 455 | } | ||
| 456 | Poll::Pending | ||
| 457 | }) | ||
| 458 | .await | ||
| 459 | } | ||
| 460 | |||
| 461 | /// Split instance into separate Tx(write) and Rx(read) portions | ||
| 462 | pub fn split<'c>(&'c mut self) -> (FdcanTx<'c, 'd, T, M>, FdcanRx<'c, 'd, T, M>) { | ||
| 463 | let (mut _control, tx, rx0, rx1) = self.can.split_by_ref(); | ||
| 464 | ( | ||
| 465 | FdcanTx { _control, tx }, | ||
| 466 | FdcanRx { | ||
| 467 | rx0, | ||
| 468 | rx1, | ||
| 469 | ns_per_timer_tick: self.ns_per_timer_tick, | ||
| 470 | }, | ||
| 471 | ) | ||
| 472 | } | ||
| 473 | } | ||
| 474 | |||
| 475 | /// FDCAN Tx only Instance | ||
| 476 | pub struct FdcanTx<'c, 'd, T: Instance, M: fdcan::Transmit> { | ||
| 477 | _control: &'c mut fdcan::FdCanControl<FdcanInstance<'d, T>, M>, | ||
| 478 | tx: &'c mut fdcan::Tx<FdcanInstance<'d, T>, M>, | ||
| 479 | } | ||
| 480 | |||
| 481 | impl<'c, 'd, T: Instance, M: fdcan::Transmit> FdcanTx<'c, 'd, T, M> { | ||
| 482 | /// Queues the message to be sent but exerts backpressure. If a lower-priority | ||
| 483 | /// frame is dropped from the mailbox, it is returned. If no lower-priority frames | ||
| 484 | /// can be replaced, this call asynchronously waits for a frame to be successfully | ||
| 485 | /// transmitted, then tries again. | ||
| 486 | pub async fn write(&mut self, frame: &TxFrame) -> Option<TxFrame> { | ||
| 487 | poll_fn(|cx| { | ||
| 488 | T::state().tx_waker.register(cx.waker()); | ||
| 489 | if let Ok(dropped) = self | ||
| 490 | .tx | ||
| 491 | .transmit_preserve(frame.header, &frame.data.bytes, &mut |_, hdr, data32| { | ||
| 492 | TxFrame::from_preserved(hdr, data32) | ||
| 493 | }) | ||
| 494 | { | ||
| 495 | return Poll::Ready(dropped.flatten()); | ||
| 496 | } | ||
| 497 | |||
| 498 | // Couldn't replace any lower priority frames. Need to wait for some mailboxes | ||
| 499 | // to clear. | ||
| 500 | Poll::Pending | ||
| 501 | }) | ||
| 502 | .await | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | /// FDCAN Rx only Instance | ||
| 507 | #[allow(dead_code)] | ||
| 508 | pub struct FdcanRx<'c, 'd, T: Instance, M: fdcan::Receive> { | ||
| 509 | rx0: &'c mut fdcan::Rx<FdcanInstance<'d, T>, M, fdcan::Fifo0>, | ||
| 510 | rx1: &'c mut fdcan::Rx<FdcanInstance<'d, T>, M, fdcan::Fifo1>, | ||
| 511 | ns_per_timer_tick: u64, // For FDCAN internal timer | ||
| 512 | } | ||
| 513 | |||
| 514 | impl<'c, 'd, T: Instance, M: fdcan::Receive> FdcanRx<'c, 'd, T, M> { | ||
| 515 | /// Returns the next received message frame | ||
| 516 | pub async fn read(&mut self) -> Result<RxFrame, BusError> { | ||
| 517 | poll_fn(|cx| { | ||
| 518 | T::state().err_waker.register(cx.waker()); | ||
| 519 | T::state().rx_waker.register(cx.waker()); | ||
| 520 | |||
| 521 | let mut buffer: [u8; 64] = [0; 64]; | ||
| 522 | if let Ok(rx) = self.rx0.receive(&mut buffer) { | ||
| 523 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 524 | // TODO: report overrun? | ||
| 525 | // for now we just drop it | ||
| 526 | let frame: RxFrame = RxFrame::new( | ||
| 527 | rx.unwrap(), | ||
| 528 | &buffer, | ||
| 529 | #[cfg(feature = "time")] | ||
| 530 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 531 | ); | ||
| 532 | return Poll::Ready(Ok(frame)); | ||
| 533 | } else if let Ok(rx) = self.rx1.receive(&mut buffer) { | ||
| 534 | // rx: fdcan::ReceiveOverrun<RxFrameInfo> | ||
| 535 | // TODO: report overrun? | ||
| 536 | // for now we just drop it | ||
| 537 | let frame: RxFrame = RxFrame::new( | ||
| 538 | rx.unwrap(), | ||
| 539 | &buffer, | ||
| 540 | #[cfg(feature = "time")] | ||
| 541 | calc_timestamp::<T>(self.ns_per_timer_tick, rx.unwrap().time_stamp), | ||
| 542 | ); | ||
| 543 | return Poll::Ready(Ok(frame)); | ||
| 544 | } else if let Some(err) = curr_error::<T>() { | ||
| 545 | // TODO: this is probably wrong | ||
| 546 | return Poll::Ready(Err(err)); | ||
| 547 | } | ||
| 548 | |||
| 549 | Poll::Pending | ||
| 550 | }) | ||
| 551 | .await | ||
| 552 | } | ||
| 553 | } | ||
| 554 | impl<'d, T: Instance, M: FdcanOperatingMode> Deref for Fdcan<'d, T, M> { | ||
| 555 | type Target = fdcan::FdCan<FdcanInstance<'d, T>, M>; | ||
| 556 | |||
| 557 | fn deref(&self) -> &Self::Target { | ||
| 558 | &self.can | ||
| 559 | } | ||
| 560 | } | ||
| 561 | |||
| 562 | impl<'d, T: Instance, M: FdcanOperatingMode> DerefMut for Fdcan<'d, T, M> { | ||
| 563 | fn deref_mut(&mut self) -> &mut Self::Target { | ||
| 564 | &mut self.can | ||
| 565 | } | ||
| 566 | } | ||
| 2 | 567 | ||
| 3 | pub(crate) mod sealed { | 568 | pub(crate) mod sealed { |
| 4 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 5 | use embassy_sync::channel::Channel; | ||
| 6 | use embassy_sync::waitqueue::AtomicWaker; | 569 | use embassy_sync::waitqueue::AtomicWaker; |
| 7 | 570 | ||
| 8 | pub struct State { | 571 | pub struct State { |
| 9 | pub tx_waker: AtomicWaker, | 572 | pub tx_waker: AtomicWaker, |
| 10 | pub err_waker: AtomicWaker, | 573 | pub err_waker: AtomicWaker, |
| 11 | pub rx_queue: Channel<CriticalSectionRawMutex, (u16, bxcan::Frame), 32>, | 574 | pub rx_waker: AtomicWaker, |
| 12 | } | 575 | } |
| 13 | 576 | ||
| 14 | impl State { | 577 | impl State { |
| @@ -16,25 +579,122 @@ pub(crate) mod sealed { | |||
| 16 | Self { | 579 | Self { |
| 17 | tx_waker: AtomicWaker::new(), | 580 | tx_waker: AtomicWaker::new(), |
| 18 | err_waker: AtomicWaker::new(), | 581 | err_waker: AtomicWaker::new(), |
| 19 | rx_queue: Channel::new(), | 582 | rx_waker: AtomicWaker::new(), |
| 20 | } | 583 | } |
| 21 | } | 584 | } |
| 22 | } | 585 | } |
| 23 | 586 | ||
| 24 | pub trait Instance { | 587 | pub trait Instance { |
| 588 | const REGISTERS: *mut fdcan::RegisterBlock; | ||
| 589 | const MSG_RAM: *mut fdcan::message_ram::RegisterBlock; | ||
| 590 | const MSG_RAM_OFFSET: usize; | ||
| 591 | |||
| 25 | fn regs() -> &'static crate::pac::can::Fdcan; | 592 | fn regs() -> &'static crate::pac::can::Fdcan; |
| 26 | fn state() -> &'static State; | 593 | fn state() -> &'static State; |
| 594 | |||
| 595 | #[cfg(not(stm32h7))] | ||
| 596 | fn configure_msg_ram() {} | ||
| 597 | |||
| 598 | #[cfg(stm32h7)] | ||
| 599 | fn configure_msg_ram() { | ||
| 600 | let r = Self::regs(); | ||
| 601 | |||
| 602 | use fdcan::message_ram::*; | ||
| 603 | let mut offset_words = Self::MSG_RAM_OFFSET as u16; | ||
| 604 | |||
| 605 | // 11-bit filter | ||
| 606 | r.sidfc().modify(|w| w.set_flssa(offset_words)); | ||
| 607 | offset_words += STANDARD_FILTER_MAX as u16; | ||
| 608 | |||
| 609 | // 29-bit filter | ||
| 610 | r.xidfc().modify(|w| w.set_flesa(offset_words)); | ||
| 611 | offset_words += 2 * EXTENDED_FILTER_MAX as u16; | ||
| 612 | |||
| 613 | // Rx FIFO 0 and 1 | ||
| 614 | for i in 0..=1 { | ||
| 615 | r.rxfc(i).modify(|w| { | ||
| 616 | w.set_fsa(offset_words); | ||
| 617 | w.set_fs(RX_FIFO_MAX); | ||
| 618 | w.set_fwm(RX_FIFO_MAX); | ||
| 619 | }); | ||
| 620 | offset_words += 18 * RX_FIFO_MAX as u16; | ||
| 621 | } | ||
| 622 | |||
| 623 | // Rx buffer - see below | ||
| 624 | // Tx event FIFO | ||
| 625 | r.txefc().modify(|w| { | ||
| 626 | w.set_efsa(offset_words); | ||
| 627 | w.set_efs(TX_EVENT_MAX); | ||
| 628 | w.set_efwm(TX_EVENT_MAX); | ||
| 629 | }); | ||
| 630 | offset_words += 2 * TX_EVENT_MAX as u16; | ||
| 631 | |||
| 632 | // Tx buffers | ||
| 633 | r.txbc().modify(|w| { | ||
| 634 | w.set_tbsa(offset_words); | ||
| 635 | w.set_tfqs(TX_FIFO_MAX); | ||
| 636 | }); | ||
| 637 | offset_words += 18 * TX_FIFO_MAX as u16; | ||
| 638 | |||
| 639 | // Rx Buffer - not used | ||
| 640 | r.rxbc().modify(|w| { | ||
| 641 | w.set_rbsa(offset_words); | ||
| 642 | }); | ||
| 643 | |||
| 644 | // TX event FIFO? | ||
| 645 | // Trigger memory? | ||
| 646 | |||
| 647 | // Set the element sizes to 16 bytes | ||
| 648 | r.rxesc().modify(|w| { | ||
| 649 | w.set_rbds(0b111); | ||
| 650 | for i in 0..=1 { | ||
| 651 | w.set_fds(i, 0b111); | ||
| 652 | } | ||
| 653 | }); | ||
| 654 | r.txesc().modify(|w| { | ||
| 655 | w.set_tbds(0b111); | ||
| 656 | }) | ||
| 657 | } | ||
| 27 | } | 658 | } |
| 28 | } | 659 | } |
| 29 | 660 | ||
| 30 | /// Interruptable FDCAN instance. | 661 | /// Trait for FDCAN interrupt channel 0 |
| 31 | pub trait InterruptableInstance {} | 662 | pub trait IT0Instance { |
| 32 | /// FDCAN instance. | 663 | /// Type for FDCAN interrupt channel 0 |
| 33 | pub trait Instance: sealed::Instance + InterruptableInstance + 'static {} | 664 | type IT0Interrupt: crate::interrupt::typelevel::Interrupt; |
| 665 | } | ||
| 34 | 666 | ||
| 35 | foreach_peripheral!( | 667 | /// Trait for FDCAN interrupt channel 1 |
| 36 | (can, $inst:ident) => { | 668 | pub trait IT1Instance { |
| 669 | /// Type for FDCAN interrupt channel 1 | ||
| 670 | type IT1Interrupt: crate::interrupt::typelevel::Interrupt; | ||
| 671 | } | ||
| 672 | |||
| 673 | /// InterruptableInstance trait | ||
| 674 | pub trait InterruptableInstance: IT0Instance + IT1Instance {} | ||
| 675 | /// Instance trait | ||
| 676 | pub trait Instance: sealed::Instance + RccPeripheral + InterruptableInstance + 'static {} | ||
| 677 | /// Fdcan Instance struct | ||
| 678 | pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); | ||
| 679 | |||
| 680 | unsafe impl<'d, T: Instance> fdcan::message_ram::Instance for FdcanInstance<'d, T> { | ||
| 681 | const MSG_RAM: *mut RegisterBlock = T::MSG_RAM; | ||
| 682 | } | ||
| 683 | |||
| 684 | unsafe impl<'d, T: Instance> fdcan::Instance for FdcanInstance<'d, T> | ||
| 685 | where | ||
| 686 | FdcanInstance<'d, T>: fdcan::message_ram::Instance, | ||
| 687 | { | ||
| 688 | const REGISTERS: *mut fdcan::RegisterBlock = T::REGISTERS; | ||
| 689 | } | ||
| 690 | |||
| 691 | macro_rules! impl_fdcan { | ||
| 692 | ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { | ||
| 37 | impl sealed::Instance for peripherals::$inst { | 693 | impl sealed::Instance for peripherals::$inst { |
| 694 | const REGISTERS: *mut fdcan::RegisterBlock = crate::pac::$inst.as_ptr() as *mut _; | ||
| 695 | const MSG_RAM: *mut fdcan::message_ram::RegisterBlock = crate::pac::$msg_ram_inst.as_ptr() as *mut _; | ||
| 696 | const MSG_RAM_OFFSET: usize = $msg_ram_offset; | ||
| 697 | |||
| 38 | fn regs() -> &'static crate::pac::can::Fdcan { | 698 | fn regs() -> &'static crate::pac::can::Fdcan { |
| 39 | &crate::pac::$inst | 699 | &crate::pac::$inst |
| 40 | } | 700 | } |
| @@ -47,8 +707,40 @@ foreach_peripheral!( | |||
| 47 | 707 | ||
| 48 | impl Instance for peripherals::$inst {} | 708 | impl Instance for peripherals::$inst {} |
| 49 | 709 | ||
| 710 | foreach_interrupt!( | ||
| 711 | ($inst,can,FDCAN,IT0,$irq:ident) => { | ||
| 712 | impl IT0Instance for peripherals::$inst { | ||
| 713 | type IT0Interrupt = crate::interrupt::typelevel::$irq; | ||
| 714 | } | ||
| 715 | }; | ||
| 716 | ($inst,can,FDCAN,IT1,$irq:ident) => { | ||
| 717 | impl IT1Instance for peripherals::$inst { | ||
| 718 | type IT1Interrupt = crate::interrupt::typelevel::$irq; | ||
| 719 | } | ||
| 720 | }; | ||
| 721 | ); | ||
| 722 | |||
| 50 | impl InterruptableInstance for peripherals::$inst {} | 723 | impl InterruptableInstance for peripherals::$inst {} |
| 51 | }; | 724 | }; |
| 725 | |||
| 726 | ($inst:ident, $msg_ram_inst:ident) => { | ||
| 727 | impl_fdcan!($inst, $msg_ram_inst, 0); | ||
| 728 | }; | ||
| 729 | } | ||
| 730 | |||
| 731 | #[cfg(not(stm32h7))] | ||
| 732 | foreach_peripheral!( | ||
| 733 | (can, FDCAN) => { impl_fdcan!(FDCAN, FDCANRAM); }; | ||
| 734 | (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM1); }; | ||
| 735 | (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM2); }; | ||
| 736 | (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM3); }; | ||
| 737 | ); | ||
| 738 | |||
| 739 | #[cfg(stm32h7)] | ||
| 740 | foreach_peripheral!( | ||
| 741 | (can, FDCAN1) => { impl_fdcan!(FDCAN1, FDCANRAM, 0x0000); }; | ||
| 742 | (can, FDCAN2) => { impl_fdcan!(FDCAN2, FDCANRAM, 0x0C00); }; | ||
| 743 | (can, FDCAN3) => { impl_fdcan!(FDCAN3, FDCANRAM, 0x1800); }; | ||
| 52 | ); | 744 | ); |
| 53 | 745 | ||
| 54 | pin_trait!(RxPin, Instance); | 746 | pin_trait!(RxPin, Instance); |
diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 85e12637e..dcaf2dced 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs | |||
| @@ -6,10 +6,9 @@ use crate::pac::pwr::vals::Vos; | |||
| 6 | pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; | 6 | pub use crate::pac::rcc::vals::Adcdacsel as AdcClockSource; |
| 7 | #[cfg(stm32h7)] | 7 | #[cfg(stm32h7)] |
| 8 | pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; | 8 | pub use crate::pac::rcc::vals::Adcsel as AdcClockSource; |
| 9 | pub use crate::pac::rcc::vals::Fdcansel as FdCanClockSource; | ||
| 10 | pub use crate::pac::rcc::vals::{ | 9 | pub use crate::pac::rcc::vals::{ |
| 11 | Ckpersel as PerClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, | 10 | Ckpersel as PerClockSource, Fdcansel as FdCanClockSource, Hsidiv as HSIPrescaler, Plldiv as PllDiv, |
| 12 | Pllsrc as PllSource, Sw as Sysclk, | 11 | Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Sw as Sysclk, |
| 13 | }; | 12 | }; |
| 14 | use crate::pac::rcc::vals::{Ckpersel, Pllrge, Pllvcosel, Timpre}; | 13 | use crate::pac::rcc::vals::{Ckpersel, Pllrge, Pllvcosel, Timpre}; |
| 15 | use crate::pac::{FLASH, PWR, RCC}; | 14 | use crate::pac::{FLASH, PWR, RCC}; |
| @@ -591,7 +590,7 @@ pub(crate) unsafe fn init(config: Config) { | |||
| 591 | RCC.ccipr5().modify(|w| { | 590 | RCC.ccipr5().modify(|w| { |
| 592 | w.set_ckpersel(config.per_clock_source); | 591 | w.set_ckpersel(config.per_clock_source); |
| 593 | w.set_adcdacsel(config.adc_clock_source); | 592 | w.set_adcdacsel(config.adc_clock_source); |
| 594 | w.set_fdcan1sel(config.fdcan_clock_source) | 593 | w.set_fdcan12sel(config.fdcan_clock_source) |
| 595 | }); | 594 | }); |
| 596 | } | 595 | } |
| 597 | 596 | ||
