diff options
| author | xoviat <[email protected]> | 2023-05-29 09:40:37 -0500 |
|---|---|---|
| committer | xoviat <[email protected]> | 2023-05-29 09:40:37 -0500 |
| commit | 403cbb1dc955376237f035e841d387d947f2181f (patch) | |
| tree | 08b65f5f706548d261c8b922adf168c84194705f | |
| parent | 0a136c308eb3a949a7bca2f7f688277adb085489 (diff) | |
| parent | 8d7abeb06fbe3e19db3cae3f5220725969ecbb81 (diff) | |
Merge commit '8d7abeb06fbe3e19db3cae3f5220725969ecbb81' of https://github.com/Lytehorse/embassy into can
| -rw-r--r-- | embassy-stm32/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/can/bxcan.rs | 374 |
2 files changed, 371 insertions, 4 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4e29bb32f..5d4b26a36 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -69,6 +69,7 @@ cfg-if = "1.0.0" | |||
| 69 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | 69 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } |
| 70 | chrono = { version = "^0.4", default-features = false, optional = true} | 70 | chrono = { version = "^0.4", default-features = false, optional = true} |
| 71 | bit_field = "0.10.2" | 71 | bit_field = "0.10.2" |
| 72 | heapless = { version = "0.7.5", default-features = false } | ||
| 72 | 73 | ||
| 73 | [dev-dependencies] | 74 | [dev-dependencies] |
| 74 | critical-section = { version = "1.1", features = ["std"] } | 75 | critical-section = { version = "1.1", features = ["std"] } |
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index bd92b35a0..734efdc02 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs | |||
| @@ -1,16 +1,41 @@ | |||
| 1 | use core::future::poll_fn; | ||
| 1 | use core::ops::{Deref, DerefMut}; | 2 | use core::ops::{Deref, DerefMut}; |
| 3 | use core::task::Poll; | ||
| 2 | 4 | ||
| 3 | pub use bxcan; | 5 | pub use bxcan; |
| 6 | use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; | ||
| 4 | use embassy_hal_common::{into_ref, PeripheralRef}; | 7 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 5 | 8 | ||
| 6 | use crate::gpio::sealed::AFType; | 9 | use crate::gpio::sealed::AFType; |
| 10 | use crate::interrupt::InterruptExt; | ||
| 11 | use crate::pac::can::vals::{Lec, RirIde}; | ||
| 7 | use crate::rcc::RccPeripheral; | 12 | use crate::rcc::RccPeripheral; |
| 13 | use crate::time::Hertz; | ||
| 8 | use crate::{peripherals, Peripheral}; | 14 | use crate::{peripherals, Peripheral}; |
| 9 | 15 | ||
| 10 | pub struct Can<'d, T: Instance> { | 16 | pub struct Can<'d, T: Instance> { |
| 11 | can: bxcan::Can<BxcanInstance<'d, T>>, | 17 | can: bxcan::Can<BxcanInstance<'d, T>>, |
| 12 | } | 18 | } |
| 13 | 19 | ||
| 20 | #[derive(Debug)] | ||
| 21 | pub enum BusError { | ||
| 22 | Stuff, | ||
| 23 | Form, | ||
| 24 | Acknowledge, | ||
| 25 | BitRecessive, | ||
| 26 | BitDominant, | ||
| 27 | Crc, | ||
| 28 | Software, | ||
| 29 | BusOff, | ||
| 30 | BusPassive, | ||
| 31 | BusWarning, | ||
| 32 | } | ||
| 33 | |||
| 34 | pub enum FrameOrError { | ||
| 35 | Frame(Frame), | ||
| 36 | Error(BusError), | ||
| 37 | } | ||
| 38 | |||
| 14 | impl<'d, T: Instance> Can<'d, T> { | 39 | impl<'d, T: Instance> Can<'d, T> { |
| 15 | /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. | 40 | /// Creates a new Bxcan instance, blocking for 11 recessive bits to sync with the CAN bus. |
| 16 | pub fn new( | 41 | pub fn new( |
| @@ -39,8 +64,12 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 39 | peri: impl Peripheral<P = T> + 'd, | 64 | peri: impl Peripheral<P = T> + 'd, |
| 40 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, | 65 | rx: impl Peripheral<P = impl RxPin<T>> + 'd, |
| 41 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, | 66 | tx: impl Peripheral<P = impl TxPin<T>> + 'd, |
| 67 | tx_irq: impl Peripheral<P = T::TXInterrupt> + 'd, | ||
| 68 | rx0_irq: impl Peripheral<P = T::RX0Interrupt> + 'd, | ||
| 69 | rx1_irq: impl Peripheral<P = T::RX1Interrupt> + 'd, | ||
| 70 | sce_irq: impl Peripheral<P = T::SCEInterrupt> + 'd, | ||
| 42 | ) -> Self { | 71 | ) -> Self { |
| 43 | into_ref!(peri, rx, tx); | 72 | into_ref!(peri, rx, tx, tx_irq, rx0_irq, rx1_irq, sce_irq); |
| 44 | 73 | ||
| 45 | unsafe { | 74 | unsafe { |
| 46 | rx.set_as_af(rx.af_num(), AFType::Input); | 75 | rx.set_as_af(rx.af_num(), AFType::Input); |
| @@ -50,10 +79,246 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 50 | T::enable(); | 79 | T::enable(); |
| 51 | T::reset(); | 80 | T::reset(); |
| 52 | 81 | ||
| 53 | Self { | 82 | tx_irq.unpend(); |
| 54 | can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), | 83 | tx_irq.set_handler(Self::tx_interrupt); |
| 84 | tx_irq.enable(); | ||
| 85 | |||
| 86 | rx0_irq.unpend(); | ||
| 87 | rx0_irq.set_handler(Self::rx0_interrupt); | ||
| 88 | rx0_irq.enable(); | ||
| 89 | |||
| 90 | rx1_irq.unpend(); | ||
| 91 | rx1_irq.set_handler(Self::rx1_interrupt); | ||
| 92 | rx1_irq.enable(); | ||
| 93 | |||
| 94 | sce_irq.unpend(); | ||
| 95 | sce_irq.set_handler(Self::sce_interrupt); | ||
| 96 | sce_irq.enable(); | ||
| 97 | |||
| 98 | let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); | ||
| 99 | Self { can } | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn set_bitrate(&mut self, bitrate: u32) { | ||
| 103 | let bit_timing = Self::calc_bxcan_timings(T::frequency(), bitrate).unwrap(); | ||
| 104 | self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); | ||
| 105 | } | ||
| 106 | |||
| 107 | pub async fn transmit(&mut self, frame: &Frame) { | ||
| 108 | let tx_status = self.queue_transmit(frame).await; | ||
| 109 | self.wait_transission(tx_status.mailbox()).await; | ||
| 110 | } | ||
| 111 | |||
| 112 | async fn queue_transmit(&mut self, frame: &Frame) -> bxcan::TransmitStatus { | ||
| 113 | poll_fn(|cx| { | ||
| 114 | if let Ok(status) = self.can.transmit(frame) { | ||
| 115 | return Poll::Ready(status); | ||
| 116 | } | ||
| 117 | T::state().tx_waker.register(cx.waker()); | ||
| 118 | Poll::Pending | ||
| 119 | }) | ||
| 120 | .await | ||
| 121 | } | ||
| 122 | |||
| 123 | async fn wait_transission(&self, mb: bxcan::Mailbox) { | ||
| 124 | poll_fn(|cx| unsafe { | ||
| 125 | if T::regs().tsr().read().tme(mb.index()) { | ||
| 126 | return Poll::Ready(()); | ||
| 127 | } | ||
| 128 | T::state().tx_waker.register(cx.waker()); | ||
| 129 | Poll::Pending | ||
| 130 | }) | ||
| 131 | .await; | ||
| 132 | } | ||
| 133 | |||
| 134 | pub async fn receive_frame_or_error(&mut self) -> FrameOrError { | ||
| 135 | poll_fn(|cx| { | ||
| 136 | if let Some(frame) = T::state().rx_queue.dequeue() { | ||
| 137 | return Poll::Ready(FrameOrError::Frame(frame)); | ||
| 138 | } else if let Some(err) = self.curr_error() { | ||
| 139 | return Poll::Ready(FrameOrError::Error(err)); | ||
| 140 | } | ||
| 141 | T::state().rx_waker.register(cx.waker()); | ||
| 142 | T::state().err_waker.register(cx.waker()); | ||
| 143 | Poll::Pending | ||
| 144 | }) | ||
| 145 | .await | ||
| 146 | } | ||
| 147 | |||
| 148 | fn curr_error(&self) -> Option<BusError> { | ||
| 149 | let err = unsafe { T::regs().esr().read() }; | ||
| 150 | if err.boff() { | ||
| 151 | return Some(BusError::BusOff); | ||
| 152 | } else if err.epvf() { | ||
| 153 | return Some(BusError::BusPassive); | ||
| 154 | } else if err.ewgf() { | ||
| 155 | return Some(BusError::BusWarning); | ||
| 156 | } else if let Some(err) = err.lec().into_bus_err() { | ||
| 157 | return Some(err); | ||
| 55 | } | 158 | } |
| 159 | None | ||
| 56 | } | 160 | } |
| 161 | |||
| 162 | unsafe fn sce_interrupt(_: *mut ()) { | ||
| 163 | let msr = T::regs().msr(); | ||
| 164 | let msr_val = msr.read(); | ||
| 165 | |||
| 166 | if msr_val.erri() { | ||
| 167 | msr.modify(|v| v.set_erri(true)); | ||
| 168 | T::state().err_waker.wake(); | ||
| 169 | return; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | unsafe fn tx_interrupt(_: *mut ()) { | ||
| 174 | T::regs().tsr().write(|v| { | ||
| 175 | v.set_rqcp(0, true); | ||
| 176 | v.set_rqcp(1, true); | ||
| 177 | v.set_rqcp(2, true); | ||
| 178 | }); | ||
| 179 | T::state().tx_waker.wake(); | ||
| 180 | } | ||
| 181 | |||
| 182 | unsafe fn rx0_interrupt(_: *mut ()) { | ||
| 183 | Self::receive_fifo(RxFifo::Fifo0); | ||
| 184 | } | ||
| 185 | |||
| 186 | unsafe fn rx1_interrupt(_: *mut ()) { | ||
| 187 | Self::receive_fifo(RxFifo::Fifi1); | ||
| 188 | } | ||
| 189 | |||
| 190 | unsafe fn receive_fifo(fifo: RxFifo) { | ||
| 191 | let state = T::state(); | ||
| 192 | let regs = T::regs(); | ||
| 193 | let fifo_idx = match fifo { | ||
| 194 | RxFifo::Fifo0 => 0usize, | ||
| 195 | RxFifo::Fifi1 => 1usize, | ||
| 196 | }; | ||
| 197 | let rfr = regs.rfr(fifo_idx); | ||
| 198 | let fifo = regs.rx(fifo_idx); | ||
| 199 | |||
| 200 | // If there are no pending messages, there is nothing to do | ||
| 201 | if rfr.read().fmp() == 0 { | ||
| 202 | return; | ||
| 203 | } | ||
| 204 | |||
| 205 | let rir = fifo.rir().read(); | ||
| 206 | let id = if rir.ide() == RirIde::STANDARD { | ||
| 207 | Id::from(StandardId::new_unchecked(rir.stid())) | ||
| 208 | } else { | ||
| 209 | Id::from(ExtendedId::new_unchecked(rir.exid())) | ||
| 210 | }; | ||
| 211 | let data_len = fifo.rdtr().read().dlc() as usize; | ||
| 212 | let mut data: [u8; 8] = [0; 8]; | ||
| 213 | data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); | ||
| 214 | data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); | ||
| 215 | |||
| 216 | let frame = Frame::new_data(id, Data::new(&data[0..data_len]).unwrap()); | ||
| 217 | |||
| 218 | rfr.modify(|v| v.set_rfom(true)); | ||
| 219 | |||
| 220 | match state.rx_queue.enqueue(frame) { | ||
| 221 | Ok(_) => {} | ||
| 222 | Err(_) => defmt::error!("RX queue overflow"), | ||
| 223 | } | ||
| 224 | state.rx_waker.wake(); | ||
| 225 | } | ||
| 226 | |||
| 227 | pub fn calc_bxcan_timings(periph_clock: Hertz, can_bitrate: u32) -> Option<u32> { | ||
| 228 | const BS1_MAX: u8 = 16; | ||
| 229 | const BS2_MAX: u8 = 8; | ||
| 230 | const MAX_SAMPLE_POINT_PERMILL: u16 = 900; | ||
| 231 | |||
| 232 | let periph_clock = periph_clock.0; | ||
| 233 | |||
| 234 | if can_bitrate < 1000 { | ||
| 235 | return None; | ||
| 236 | } | ||
| 237 | |||
| 238 | // Ref. "Automatic Baudrate Detection in CANopen Networks", U. Koppe, MicroControl GmbH & Co. KG | ||
| 239 | // CAN in Automation, 2003 | ||
| 240 | // | ||
| 241 | // According to the source, optimal quanta per bit are: | ||
| 242 | // Bitrate Optimal Maximum | ||
| 243 | // 1000 kbps 8 10 | ||
| 244 | // 500 kbps 16 17 | ||
| 245 | // 250 kbps 16 17 | ||
| 246 | // 125 kbps 16 17 | ||
| 247 | let max_quanta_per_bit: u8 = if can_bitrate >= 1_000_000 { 10 } else { 17 }; | ||
| 248 | |||
| 249 | // Computing (prescaler * BS): | ||
| 250 | // BITRATE = 1 / (PRESCALER * (1 / PCLK) * (1 + BS1 + BS2)) -- See the Reference Manual | ||
| 251 | // BITRATE = PCLK / (PRESCALER * (1 + BS1 + BS2)) -- Simplified | ||
| 252 | // let: | ||
| 253 | // BS = 1 + BS1 + BS2 -- Number of time quanta per bit | ||
| 254 | // PRESCALER_BS = PRESCALER * BS | ||
| 255 | // ==> | ||
| 256 | // PRESCALER_BS = PCLK / BITRATE | ||
| 257 | let prescaler_bs = periph_clock / can_bitrate; | ||
| 258 | |||
| 259 | // Searching for such prescaler value so that the number of quanta per bit is highest. | ||
| 260 | let mut bs1_bs2_sum = max_quanta_per_bit - 1; | ||
| 261 | while (prescaler_bs % (1 + bs1_bs2_sum) as u32) != 0 { | ||
| 262 | if bs1_bs2_sum <= 2 { | ||
| 263 | return None; // No solution | ||
| 264 | } | ||
| 265 | bs1_bs2_sum -= 1; | ||
| 266 | } | ||
| 267 | |||
| 268 | let prescaler = prescaler_bs / (1 + bs1_bs2_sum) as u32; | ||
| 269 | if (prescaler < 1) || (prescaler > 1024) { | ||
| 270 | return None; // No solution | ||
| 271 | } | ||
| 272 | |||
| 273 | // Now we have a constraint: (BS1 + BS2) == bs1_bs2_sum. | ||
| 274 | // We need to find such values so that the sample point is as close as possible to the optimal value, | ||
| 275 | // which is 87.5%, which is 7/8. | ||
| 276 | // | ||
| 277 | // Solve[(1 + bs1)/(1 + bs1 + bs2) == 7/8, bs2] (* Where 7/8 is 0.875, the recommended sample point location *) | ||
| 278 | // {{bs2 -> (1 + bs1)/7}} | ||
| 279 | // | ||
| 280 | // Hence: | ||
| 281 | // bs2 = (1 + bs1) / 7 | ||
| 282 | // bs1 = (7 * bs1_bs2_sum - 1) / 8 | ||
| 283 | // | ||
| 284 | // Sample point location can be computed as follows: | ||
| 285 | // Sample point location = (1 + bs1) / (1 + bs1 + bs2) | ||
| 286 | // | ||
| 287 | // Since the optimal solution is so close to the maximum, we prepare two solutions, and then pick the best one: | ||
| 288 | // - With rounding to nearest | ||
| 289 | // - With rounding to zero | ||
| 290 | let mut bs1 = ((7 * bs1_bs2_sum - 1) + 4) / 8; // Trying rounding to nearest first | ||
| 291 | let mut bs2 = bs1_bs2_sum - bs1; | ||
| 292 | assert!(bs1_bs2_sum > bs1); | ||
| 293 | |||
| 294 | let sample_point_permill = 1000 * ((1 + bs1) / (1 + bs1 + bs2)) as u16; | ||
| 295 | if sample_point_permill > MAX_SAMPLE_POINT_PERMILL { | ||
| 296 | // Nope, too far; now rounding to zero | ||
| 297 | bs1 = (7 * bs1_bs2_sum - 1) / 8; | ||
| 298 | bs2 = bs1_bs2_sum - bs1; | ||
| 299 | } | ||
| 300 | |||
| 301 | // Check is BS1 and BS2 are in range | ||
| 302 | if (bs1 < 1) || (bs1 > BS1_MAX) || (bs2 < 1) || (bs2 > BS2_MAX) { | ||
| 303 | return None; | ||
| 304 | } | ||
| 305 | |||
| 306 | // Check if final bitrate matches the requested | ||
| 307 | if can_bitrate != (periph_clock / (prescaler * (1 + bs1 + bs2) as u32)) { | ||
| 308 | return None; | ||
| 309 | } | ||
| 310 | |||
| 311 | // One is recommended by DS-015, CANOpen, and DeviceNet | ||
| 312 | let sjw = 1; | ||
| 313 | |||
| 314 | // Pack into BTR register values | ||
| 315 | Some((sjw - 1) << 24 | (bs1 as u32 - 1) << 16 | (bs2 as u32 - 1) << 20 | (prescaler as u32 - 1)) | ||
| 316 | } | ||
| 317 | } | ||
| 318 | |||
| 319 | enum RxFifo { | ||
| 320 | Fifo0, | ||
| 321 | Fifi1, | ||
| 57 | } | 322 | } |
| 58 | 323 | ||
| 59 | impl<'d, T: Instance> Drop for Can<'d, T> { | 324 | impl<'d, T: Instance> Drop for Can<'d, T> { |
| @@ -80,14 +345,53 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { | |||
| 80 | } | 345 | } |
| 81 | 346 | ||
| 82 | pub(crate) mod sealed { | 347 | pub(crate) mod sealed { |
| 348 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 349 | use heapless::mpmc::Q8; | ||
| 350 | |||
| 351 | pub struct State { | ||
| 352 | pub tx_waker: AtomicWaker, | ||
| 353 | pub rx_waker: AtomicWaker, | ||
| 354 | pub err_waker: AtomicWaker, | ||
| 355 | pub rx_queue: Q8<bxcan::Frame>, | ||
| 356 | } | ||
| 357 | |||
| 358 | impl State { | ||
| 359 | pub const fn new() -> Self { | ||
| 360 | Self { | ||
| 361 | tx_waker: AtomicWaker::new(), | ||
| 362 | rx_waker: AtomicWaker::new(), | ||
| 363 | err_waker: AtomicWaker::new(), | ||
| 364 | rx_queue: Q8::new(), | ||
| 365 | } | ||
| 366 | } | ||
| 367 | } | ||
| 368 | |||
| 83 | pub trait Instance { | 369 | pub trait Instance { |
| 84 | const REGISTERS: *mut bxcan::RegisterBlock; | 370 | const REGISTERS: *mut bxcan::RegisterBlock; |
| 85 | 371 | ||
| 86 | fn regs() -> &'static crate::pac::can::Can; | 372 | fn regs() -> &'static crate::pac::can::Can; |
| 373 | fn state() -> &'static State; | ||
| 87 | } | 374 | } |
| 88 | } | 375 | } |
| 89 | 376 | ||
| 90 | pub trait Instance: sealed::Instance + RccPeripheral {} | 377 | pub trait TXInstance { |
| 378 | type TXInterrupt: crate::interrupt::Interrupt; | ||
| 379 | } | ||
| 380 | |||
| 381 | pub trait RX0Instance { | ||
| 382 | type RX0Interrupt: crate::interrupt::Interrupt; | ||
| 383 | } | ||
| 384 | |||
| 385 | pub trait RX1Instance { | ||
| 386 | type RX1Interrupt: crate::interrupt::Interrupt; | ||
| 387 | } | ||
| 388 | |||
| 389 | pub trait SCEInstance { | ||
| 390 | type SCEInterrupt: crate::interrupt::Interrupt; | ||
| 391 | } | ||
| 392 | |||
| 393 | pub trait InterruptableInstance: TXInstance + RX0Instance + RX1Instance + SCEInstance {} | ||
| 394 | pub trait Instance: sealed::Instance + RccPeripheral + InterruptableInstance + 'static {} | ||
| 91 | 395 | ||
| 92 | pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); | 396 | pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); |
| 93 | 397 | ||
| @@ -103,10 +407,39 @@ foreach_peripheral!( | |||
| 103 | fn regs() -> &'static crate::pac::can::Can { | 407 | fn regs() -> &'static crate::pac::can::Can { |
| 104 | &crate::pac::$inst | 408 | &crate::pac::$inst |
| 105 | } | 409 | } |
| 410 | |||
| 411 | fn state() -> &'static sealed::State { | ||
| 412 | static STATE: sealed::State = sealed::State::new(); | ||
| 413 | &STATE | ||
| 414 | } | ||
| 106 | } | 415 | } |
| 107 | 416 | ||
| 108 | impl Instance for peripherals::$inst {} | 417 | impl Instance for peripherals::$inst {} |
| 109 | 418 | ||
| 419 | foreach_interrupt!( | ||
| 420 | ($inst,can,CAN,TX,$irq:ident) => { | ||
| 421 | impl TXInstance for peripherals::$inst { | ||
| 422 | type TXInterrupt = crate::interrupt::$irq; | ||
| 423 | } | ||
| 424 | }; | ||
| 425 | ($inst,can,CAN,RX0,$irq:ident) => { | ||
| 426 | impl RX0Instance for peripherals::$inst { | ||
| 427 | type RX0Interrupt = crate::interrupt::$irq; | ||
| 428 | } | ||
| 429 | }; | ||
| 430 | ($inst,can,CAN,RX1,$irq:ident) => { | ||
| 431 | impl RX1Instance for peripherals::$inst { | ||
| 432 | type RX1Interrupt = crate::interrupt::$irq; | ||
| 433 | } | ||
| 434 | }; | ||
| 435 | ($inst,can,CAN,SCE,$irq:ident) => { | ||
| 436 | impl SCEInstance for peripherals::$inst { | ||
| 437 | type SCEInterrupt = crate::interrupt::$irq; | ||
| 438 | } | ||
| 439 | }; | ||
| 440 | ); | ||
| 441 | |||
| 442 | impl InterruptableInstance for peripherals::$inst {} | ||
| 110 | }; | 443 | }; |
| 111 | ); | 444 | ); |
| 112 | 445 | ||
| @@ -147,3 +480,36 @@ foreach_peripheral!( | |||
| 147 | 480 | ||
| 148 | pin_trait!(RxPin, Instance); | 481 | pin_trait!(RxPin, Instance); |
| 149 | pin_trait!(TxPin, Instance); | 482 | pin_trait!(TxPin, Instance); |
| 483 | |||
| 484 | trait Index { | ||
| 485 | fn index(&self) -> usize; | ||
| 486 | } | ||
| 487 | |||
| 488 | impl Index for bxcan::Mailbox { | ||
| 489 | fn index(&self) -> usize { | ||
| 490 | match self { | ||
| 491 | bxcan::Mailbox::Mailbox0 => 0, | ||
| 492 | bxcan::Mailbox::Mailbox1 => 1, | ||
| 493 | bxcan::Mailbox::Mailbox2 => 2, | ||
| 494 | } | ||
| 495 | } | ||
| 496 | } | ||
| 497 | |||
| 498 | trait IntoBusError { | ||
| 499 | fn into_bus_err(self) -> Option<BusError>; | ||
| 500 | } | ||
| 501 | |||
| 502 | impl IntoBusError for Lec { | ||
| 503 | fn into_bus_err(self) -> Option<BusError> { | ||
| 504 | match self { | ||
| 505 | Lec::STUFF => Some(BusError::Stuff), | ||
| 506 | Lec::FORM => Some(BusError::Form), | ||
| 507 | Lec::ACK => Some(BusError::Acknowledge), | ||
| 508 | Lec::BITRECESSIVE => Some(BusError::BitRecessive), | ||
| 509 | Lec::BITDOMINANT => Some(BusError::BitDominant), | ||
| 510 | Lec::CRC => Some(BusError::Crc), | ||
| 511 | Lec::CUSTOM => Some(BusError::Software), | ||
| 512 | _ => None, | ||
| 513 | } | ||
| 514 | } | ||
| 515 | } | ||
