diff options
| -rw-r--r-- | embassy-stm32/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy-stm32/src/can/bxcan.rs | 252 |
2 files changed, 237 insertions, 16 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 504caacb2..5e45db360 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml | |||
| @@ -68,6 +68,7 @@ stm32-fmc = "0.2.4" | |||
| 68 | seq-macro = "0.3.0" | 68 | seq-macro = "0.3.0" |
| 69 | cfg-if = "1.0.0" | 69 | cfg-if = "1.0.0" |
| 70 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } | 70 | embedded-io = { version = "0.4.0", features = ["async"], optional = true } |
| 71 | heapless = { version = "0.7.5", default-features = false } | ||
| 71 | 72 | ||
| 72 | [dev-dependencies] | 73 | [dev-dependencies] |
| 73 | critical-section = { version = "1.1", features = ["std"] } | 74 | critical-section = { version = "1.1", features = ["std"] } |
diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 521049ecc..734efdc02 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs | |||
| @@ -3,18 +3,39 @@ use core::ops::{Deref, DerefMut}; | |||
| 3 | use core::task::Poll; | 3 | use core::task::Poll; |
| 4 | 4 | ||
| 5 | pub use bxcan; | 5 | pub use bxcan; |
| 6 | use bxcan::Frame; | 6 | use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; |
| 7 | use embassy_hal_common::{into_ref, PeripheralRef}; | 7 | use embassy_hal_common::{into_ref, PeripheralRef}; |
| 8 | 8 | ||
| 9 | use crate::gpio::sealed::AFType; | 9 | use crate::gpio::sealed::AFType; |
| 10 | use crate::interrupt::{Interrupt, InterruptExt}; | 10 | use crate::interrupt::InterruptExt; |
| 11 | use crate::pac::can::vals::{Lec, RirIde}; | ||
| 11 | use crate::rcc::RccPeripheral; | 12 | use crate::rcc::RccPeripheral; |
| 13 | use crate::time::Hertz; | ||
| 12 | use crate::{peripherals, Peripheral}; | 14 | use crate::{peripherals, Peripheral}; |
| 13 | 15 | ||
| 14 | pub struct Can<'d, T: Instance> { | 16 | pub struct Can<'d, T: Instance> { |
| 15 | can: bxcan::Can<BxcanInstance<'d, T>>, | 17 | can: bxcan::Can<BxcanInstance<'d, T>>, |
| 16 | } | 18 | } |
| 17 | 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 | |||
| 18 | impl<'d, T: Instance> Can<'d, T> { | 39 | impl<'d, T: Instance> Can<'d, T> { |
| 19 | /// 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. |
| 20 | pub fn new( | 41 | pub fn new( |
| @@ -74,12 +95,16 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 74 | sce_irq.set_handler(Self::sce_interrupt); | 95 | sce_irq.set_handler(Self::sce_interrupt); |
| 75 | sce_irq.enable(); | 96 | sce_irq.enable(); |
| 76 | 97 | ||
| 77 | Self { | 98 | let can = bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(); |
| 78 | can: bxcan::Can::builder(BxcanInstance(peri)).leave_disabled(), | 99 | Self { can } |
| 79 | } | 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(); | ||
| 80 | } | 105 | } |
| 81 | 106 | ||
| 82 | pub async fn transmit_async(&mut self, frame: &Frame) { | 107 | pub async fn transmit(&mut self, frame: &Frame) { |
| 83 | let tx_status = self.queue_transmit(frame).await; | 108 | let tx_status = self.queue_transmit(frame).await; |
| 84 | self.wait_transission(tx_status.mailbox()).await; | 109 | self.wait_transission(tx_status.mailbox()).await; |
| 85 | } | 110 | } |
| @@ -95,7 +120,7 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 95 | .await | 120 | .await |
| 96 | } | 121 | } |
| 97 | 122 | ||
| 98 | async fn wait_transission(&mut self, mb: bxcan::Mailbox) { | 123 | async fn wait_transission(&self, mb: bxcan::Mailbox) { |
| 99 | poll_fn(|cx| unsafe { | 124 | poll_fn(|cx| unsafe { |
| 100 | if T::regs().tsr().read().tme(mb.index()) { | 125 | if T::regs().tsr().read().tme(mb.index()) { |
| 101 | return Poll::Ready(()); | 126 | return Poll::Ready(()); |
| @@ -106,6 +131,45 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 106 | .await; | 131 | .await; |
| 107 | } | 132 | } |
| 108 | 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); | ||
| 158 | } | ||
| 159 | None | ||
| 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 | |||
| 109 | unsafe fn tx_interrupt(_: *mut ()) { | 173 | unsafe fn tx_interrupt(_: *mut ()) { |
| 110 | T::regs().tsr().write(|v| { | 174 | T::regs().tsr().write(|v| { |
| 111 | v.set_rqcp(0, true); | 175 | v.set_rqcp(0, true); |
| @@ -115,11 +179,146 @@ impl<'d, T: Instance> Can<'d, T> { | |||
| 115 | T::state().tx_waker.wake(); | 179 | T::state().tx_waker.wake(); |
| 116 | } | 180 | } |
| 117 | 181 | ||
| 118 | unsafe fn rx0_interrupt(_: *mut ()) {} | 182 | unsafe fn rx0_interrupt(_: *mut ()) { |
| 183 | Self::receive_fifo(RxFifo::Fifo0); | ||
| 184 | } | ||
| 119 | 185 | ||
| 120 | unsafe fn rx1_interrupt(_: *mut ()) {} | 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()); | ||
| 121 | 215 | ||
| 122 | unsafe fn sce_interrupt(_: *mut ()) {} | 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, | ||
| 123 | } | 322 | } |
| 124 | 323 | ||
| 125 | impl<'d, T: Instance> Drop for Can<'d, T> { | 324 | impl<'d, T: Instance> Drop for Can<'d, T> { |
| @@ -147,20 +346,22 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { | |||
| 147 | 346 | ||
| 148 | pub(crate) mod sealed { | 347 | pub(crate) mod sealed { |
| 149 | use embassy_sync::waitqueue::AtomicWaker; | 348 | use embassy_sync::waitqueue::AtomicWaker; |
| 349 | use heapless::mpmc::Q8; | ||
| 350 | |||
| 150 | pub struct State { | 351 | pub struct State { |
| 151 | pub tx_waker: AtomicWaker, | 352 | pub tx_waker: AtomicWaker, |
| 152 | pub rx0_waker: AtomicWaker, | 353 | pub rx_waker: AtomicWaker, |
| 153 | pub rx1_waker: AtomicWaker, | 354 | pub err_waker: AtomicWaker, |
| 154 | pub sce_waker: AtomicWaker, | 355 | pub rx_queue: Q8<bxcan::Frame>, |
| 155 | } | 356 | } |
| 156 | 357 | ||
| 157 | impl State { | 358 | impl State { |
| 158 | pub const fn new() -> Self { | 359 | pub const fn new() -> Self { |
| 159 | Self { | 360 | Self { |
| 160 | tx_waker: AtomicWaker::new(), | 361 | tx_waker: AtomicWaker::new(), |
| 161 | rx0_waker: AtomicWaker::new(), | 362 | rx_waker: AtomicWaker::new(), |
| 162 | rx1_waker: AtomicWaker::new(), | 363 | err_waker: AtomicWaker::new(), |
| 163 | sce_waker: AtomicWaker::new(), | 364 | rx_queue: Q8::new(), |
| 164 | } | 365 | } |
| 165 | } | 366 | } |
| 166 | } | 367 | } |
| @@ -293,3 +494,22 @@ impl Index for bxcan::Mailbox { | |||
| 293 | } | 494 | } |
| 294 | } | 495 | } |
| 295 | } | 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 | } | ||
