From d144d200ca39b64d18bbbdbe3dc3292f59120dc7 Mon Sep 17 00:00:00 2001 From: Hussein Hazem Date: Thu, 5 Dec 2024 11:34:46 +0100 Subject: [PATCH 1/5] #32 migrate to embedded-hal v1.0 --- Cargo.toml | 3 +- example/Cargo.toml | 1 + example/src/main.rs | 21 +- src/can.rs | 203 ++++------ src/example.rs | 140 ++++--- src/lib.rs | 7 +- src/message.rs | 2 +- src/mocks.rs | 51 ++- src/tests/can.rs | 944 +++++++++++++++----------------------------- src/tests/filter.rs | 177 +-------- 10 files changed, 552 insertions(+), 997 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index edac02a..4c41d0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,10 @@ documentation = "https://docs.rs/mcp2517" byteorder = { version = "^1.3.0", default-features = false } bytes = { version = "1.6.0", default-features = false } embedded-can = "0.4.1" -embedded-hal = { version = "0.2.7", features = ["unproven"] } +embedded-hal = { version = "1.0.0" } embedded-time = "0.12.1" log = "0.4.17" modular-bitfield-msb = "0.11.2" -serde = { version = "1.0.197", features = ["derive"], default-features = false } [dev-dependencies] diff --git a/example/Cargo.toml b/example/Cargo.toml index 12e97da..5a441c4 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -10,6 +10,7 @@ mcp2517 = { path = "..", version = "*" } # Embedded crates embedded-hal = "1.0.0" +embedded-hal-bus = "0.2.0" embedded-time = "0.12.1" embedded-alloc = "0.5.1" critical-section = "1.1.2" diff --git a/example/src/main.rs b/example/src/main.rs index 24414e1..437facb 100644 --- a/example/src/main.rs +++ b/example/src/main.rs @@ -10,6 +10,7 @@ pub mod mutex; use crate::clock::SystemClock; use crate::heap::Heap; use bytes::Bytes; +use core::cell::RefCell; use core::fmt::Write; use embedded_can::{Id, StandardId}; use embedded_hal::delay::DelayNs; @@ -36,6 +37,7 @@ use bsp::{ Spi, Timer, }, }; +use embedded_hal_bus::spi::{NoDelay, RefCellDevice}; const XTAL_FREQ_HZ: u32 = 12_000_000u32; @@ -94,7 +96,11 @@ fn main() -> ! { ) .unwrap(); - let mut can_controller: MCP2517<_, _, SystemClock> = MCP2517::new(spi, pin_cs); + let spi_bus = RefCell::new(spi); + + let device = RefCellDevice::new(&spi_bus, pin_cs, NoDelay).unwrap(); + + let mut can_controller: MCP2517<_, _> = MCP2517::new(device); // Setup clk config let clk_config = ClockConfiguration { @@ -115,6 +121,7 @@ fn main() -> ! { bit_rate: BitRateConfig::default(), }; + let _ = can_controller.reset(); if let Err(_) = can_controller.configure(&config, &sys_clk) { panic!() } @@ -126,17 +133,15 @@ fn main() -> ! { // Set mask to match if only 2 LSB of ID match with filter filter.set_mask_standard_id(0xFF); let _ = can_controller.set_filter_object(filter); - // Create message frame + let payload_8 = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; let message_type = Can20::<8> {}; - let payload = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; - let pl_bytes = Bytes::copy_from_slice(&payload); - let can_message = TxMessage::new(message_type, pl_bytes, can_id).unwrap(); + let pl_8bytes = Bytes::copy_from_slice(&payload_8); + let can_message = TxMessage::new(message_type, pl_8bytes, can_id).unwrap(); let mut receive_buffer = [0u8; 8]; - loop { - can_controller.transmit(&can_message, true).unwrap(); + let _ = can_controller.transmit(&can_message, true); uart.write_raw(b"can message sent\n\r").unwrap(); timer.delay_ms(500); @@ -149,7 +154,7 @@ fn main() -> ! { uart.write_fmt(format_args!("{val}\n\r")).unwrap(); } } - Err(e) => uart.write_fmt(format_args!("error reading message {:?}\n\r", e)).unwrap(), + Err(_) => uart.write_fmt(format_args!("error reading message")).unwrap(), } timer.delay_ms(500); diff --git a/src/can.rs b/src/can.rs index 3c22de5..237ea83 100644 --- a/src/can.rs +++ b/src/can.rs @@ -6,11 +6,10 @@ //!# use mcp2517::example::*; //!# //! let sys_clk = ExampleClock::default(); -//! let spi_bus = ExampleSPIBus::default(); -//! let cs_pin = ExampleCSPin{}; +//! let spi_dev = ExampleSPIDevice::default(); //! //! // Initialize controller object -//! let mut can_controller = MCP2517::new(spi_bus,cs_pin); +//! let mut can_controller = MCP2517::new(spi_dev); //! //! // Use default configuration settings //! let can_config = Configuration::default(); @@ -18,17 +17,16 @@ //! // Configure CAN controller //! can_controller.configure(&can_config, &sys_clk).unwrap(); //! ``` -use crate::can::BusError::{CSError, TransferError}; -use crate::can::ConfigError::{ClockError, ConfigurationModeTimeout, RequestModeTimeout}; + use crate::config::{ClockConfiguration, Configuration}; use crate::filter::Filter; use crate::message::{MessageType, TxMessage}; use crate::registers::{FifoControlReg1, FifoStatusReg0, C1NBTCFG}; use crate::status::{OperationMode, OperationStatus, OscillatorStatus}; use byteorder::{BigEndian, ByteOrder, LittleEndian}; +use core::fmt::Debug; use core::marker::PhantomData; -use embedded_hal::blocking::spi::Transfer; -use embedded_hal::digital::v2::OutputPin; +use embedded_hal::spi::{Operation as SpiOperation, SpiDevice}; use embedded_time::duration::Milliseconds; use embedded_time::Clock; use log::debug; @@ -45,39 +43,27 @@ const FIFO_RX_INDEX: u8 = 1; /// FIFO index for transmitting CAN messages const FIFO_TX_INDEX: u8 = 2; -/// General SPI Errors -#[derive(Debug, PartialEq)] -pub enum BusError { - /// Failed setting state of CS pin - CSError(CS), - - /// SPI transfer failed - TransferError(B), +#[derive(Debug)] +pub enum SpiError> { + BusError(D::Error), +} +impl> PartialEq for SpiError { + fn eq(&self, other: &Self) -> bool { + matches!((self, other), (Self::BusError(_), Self::BusError(_))) + } } -/// Configuration errors +/// Possible CAN errors during Configuration/Transmission/Reception #[derive(Debug, PartialEq)] -pub enum ConfigError { - /// Low level bus communication error - BusError(BusError), - +pub enum CanError> { + /// SPI bus transfer error + BusErr(SpiError), /// Internal clock error ClockError, - /// No configuration mode within timeout of 2 ms ConfigurationModeTimeout, - /// Device did not enter given request mode within timeout of 2 ms RequestModeTimeout, -} - -/// Possible errors transmitting CAN message -#[derive(Debug, PartialEq)] -pub enum Error { - /// Configuration error - ConfigErr(ConfigError), - /// SPI bus transfer error - BusErr(BusError), /// Invalid payload bytes length error InvalidPayloadLength(usize), /// Invalid Ram Address region error @@ -90,25 +76,16 @@ pub enum Error { TxFifoFullErr, } -impl From> for Error { - fn from(value: BusError) -> Self { - Error::BusErr(value) - } -} - -impl From> for Error { - fn from(value: ConfigError) -> Self { - Error::ConfigErr(value) +impl> From> for CanError { + fn from(value: SpiError) -> Self { + CanError::BusErr(value) } } /// Main MCP2517 CAN controller device -pub struct MCP2517, CS: OutputPin, CLK: Clock> { - /// SPI bus - bus: B, - - /// CS pin - pin_cs: CS, +pub struct MCP2517, CLK: Clock> { + /// Device on SPI bus + device: D, /// System clock clock: PhantomData, @@ -133,8 +110,12 @@ pub trait CanController { fn set_filter_object(&mut self, filter: Filter) -> Result<(), Self::Error>; } -impl, CS: OutputPin, CLK: Clock> CanController for MCP2517 { - type Error = Error; +impl CanController for MCP2517 +where + D: SpiDevice, + CLK: Clock, +{ + type Error = CanError; fn transmit>( &mut self, @@ -146,7 +127,7 @@ impl, CS: OutputPin, CLK: Clock> CanController for MCP2517, CS: OutputPin, CLK: Clock> CanController for MCP2517 8 && operation_status.mode != OperationMode::NormalCANFD { - return Err(Error::InvalidPayloadLength(message.buff.len())); + return Err(CanError::InvalidPayloadLength(message.buff.len())); } // get address in which to write next message in TX FIFO (should not be read in configuration mode) @@ -187,7 +168,7 @@ impl, CS: OutputPin, CLK: Clock> CanController for MCP2517, CS: OutputPin, CLK: Clock> CanController for MCP2517, CS: OutputPin, CLK: Clock> MCP2517 { - pub fn new(bus: B, pin_cs: CS) -> Self { +impl MCP2517 +where + D: SpiDevice, + CLK: Clock, +{ + pub fn new(spi_dev: D) -> Self { Self { - bus, - pin_cs, + device: spi_dev, clock: Default::default(), } } /// Configures the controller with the given settings - pub fn configure(&mut self, config: &Configuration, clock: &CLK) -> Result<(), ConfigError> { - self.enable_mode(OperationMode::Configuration, clock, ConfigurationModeTimeout)?; + pub fn configure(&mut self, config: &Configuration, clock: &CLK) -> Result<(), CanError> { + self.enable_mode(OperationMode::Configuration, clock, CanError::ConfigurationModeTimeout)?; self.write_register(REGISTER_OSC, config.clock.as_register())?; @@ -268,13 +252,13 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { self.enable_filter(FIFO_RX_INDEX, 0)?; - self.enable_mode(config.mode.to_operation_mode(), clock, RequestModeTimeout)?; + self.enable_mode(config.mode.to_operation_mode(), clock, CanError::RequestModeTimeout)?; Ok(()) } /// Disable corresponding filter - pub fn disable_filter(&mut self, filter_index: u8) -> Result<(), BusError> { + pub fn disable_filter(&mut self, filter_index: u8) -> Result<(), CanError> { let filter_reg = Self::filter_control_register_byte(filter_index); self.write_register(filter_reg, 0x00)?; @@ -282,21 +266,21 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// Reads and returns the operation status - pub fn read_operation_status(&mut self) -> Result> { + pub fn read_operation_status(&mut self) -> Result> { let data = self.read_register(REGISTER_C1CON + 2)?; Ok(OperationStatus::from_register(data)) } /// Reads and returns the oscillator status - pub fn read_oscillator_status(&mut self) -> Result> { + pub fn read_oscillator_status(&mut self) -> Result> { let data = self.read_register(REGISTER_OSC + 1)?; Ok(OscillatorStatus::from_register(data)) } /// Reads and returns the current clock configuration - pub fn read_clock_configuration(&mut self) -> Result> { + pub fn read_clock_configuration(&mut self) -> Result> { let data = self.read_register(REGISTER_OSC)?; Ok(ClockConfiguration::from_register(data)) @@ -304,15 +288,10 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { /// Enters the given mode, aborts all running transactions /// and waits max. 2 ms for the given mode to be reached - fn enable_mode( - &mut self, - mode: OperationMode, - clock: &CLK, - timeout_error: ConfigError, - ) -> Result<(), ConfigError> { + fn enable_mode(&mut self, mode: OperationMode, clock: &CLK, timeout_error: CanError) -> Result<(), CanError> { self.write_register(REGISTER_C1CON + 3, mode as u8 | (1 << 3))?; - let target = clock.try_now()?.checked_add(Milliseconds::new(2)).ok_or(ClockError)?; + let target = clock.try_now()?.checked_add(Milliseconds::new(2)).ok_or(CanError::ClockError)?; let mut current_mode = None; @@ -329,7 +308,7 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// Enable filter for corresponding RX FIFO - pub fn enable_filter(&mut self, fifo_index: u8, filter_index: u8) -> Result<(), BusError> { + pub fn enable_filter(&mut self, fifo_index: u8, filter_index: u8) -> Result<(), CanError> { let filter_control_reg = Self::filter_control_register_byte(filter_index); // Filter must be disabled to modify FmBP @@ -345,7 +324,7 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// Writes a single register byte - fn write_register(&mut self, register: u16, value: u8) -> Result<(), BusError> { + fn write_register(&mut self, register: u16, value: u8) -> Result<(), SpiError> { let mut buffer = self.cmd_buffer(register, Operation::Write); buffer[2] = value; @@ -354,7 +333,7 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// 4-byte SFR write - fn write32(&mut self, register: u16, value: u32) -> Result<(), BusError> { + fn write32(&mut self, register: u16, value: u32) -> Result<(), SpiError> { let mut buffer = [0u8; 6]; let command = (register & 0x0FFF) | ((Operation::Write as u16) << 12); @@ -364,15 +343,13 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { buffer[1] = (command & 0xFF) as u8; buffer[2..].copy_from_slice(&value_bytes); - self.pin_cs.set_low().map_err(CSError)?; - self.bus.transfer(&mut buffer).map_err(TransferError)?; - self.pin_cs.set_high().map_err(CSError)?; + self.device.write(&buffer).map_err(SpiError::BusError)?; Ok(()) } /// Reset internal register to default and switch to Configuration mode - pub fn reset(&mut self) -> Result<(), BusError> { + pub fn reset(&mut self) -> Result<(), CanError> { let mut buffer = self.cmd_buffer(0u16, Operation::Reset); self.transfer(&mut buffer)?; @@ -380,11 +357,7 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// Insert message object in TX FIFO - fn write_fifo( - &mut self, - register: u16, - message: &TxMessage, - ) -> Result<(), Error> + fn write_fifo(&mut self, register: u16, message: &TxMessage) -> Result<(), CanError> where T: MessageType, { @@ -405,25 +378,19 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { let num = BigEndian::read_u32(word); LittleEndian::write_u32(word, num); } - - self.pin_cs.set_low().map_err(CSError)?; - self.bus.transfer(&mut buffer).map_err(TransferError)?; - self.bus.transfer(&mut data).map_err(TransferError)?; - self.pin_cs.set_high().map_err(CSError)?; + let mut operations = [SpiOperation::Write(&buffer), SpiOperation::Write(&data)]; + self.device.transaction(&mut operations).map_err(SpiError::BusError)?; Ok(()) } /// Read message from RX FIFO - pub(crate) fn read_fifo( - &mut self, - register: u16, - data: &mut [u8; L], - ) -> Result<(), Error> { + pub(crate) fn read_fifo(&mut self, register: u16, data: &mut [u8; L]) -> Result<(), CanError> { if L % 4 != 0 { - return Err(Error::InvalidBufferSize(L)); + return Err(CanError::InvalidBufferSize(L)); } + // Skip Transmit message object header let payload_address = register + 8; let mut buffer = [0u8; 2]; @@ -432,60 +399,54 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { buffer[0] = (command >> 8) as u8; buffer[1] = (command & 0xFF) as u8; - self.pin_cs.set_low().map_err(CSError)?; - self.bus.transfer(&mut buffer).map_err(TransferError)?; - self.bus.transfer(data).map_err(TransferError)?; - self.pin_cs.set_high().map_err(CSError)?; + let mut operations = [SpiOperation::Write(&buffer), SpiOperation::Read(data)]; + self.device.transaction(&mut operations).map_err(SpiError::BusError)?; Ok(()) } /// 4-byte SFR read - fn read32(&mut self, register: u16) -> Result> { - // create 6 byte cmd buffer (2 bytes cmd+addr , 4 bytes for register value) - let mut buffer = [0u8; 6]; + fn read32(&mut self, register: u16) -> Result> { + // create cmd buffer (2 bytes cmd+addr) + let mut buffer = [0u8; 2]; + // payload received buffer + let mut data = [0u8; 4]; let command = (register & 0x0FFF) | ((Operation::Read as u16) << 12); buffer[0] = (command >> 8) as u8; buffer[1] = (command & 0xFF) as u8; - self.pin_cs.set_low().map_err(CSError)?; - self.bus.transfer(&mut buffer).map_err(TransferError)?; - self.pin_cs.set_high().map_err(CSError)?; - - let slice = &buffer[2..]; + let mut operations = [SpiOperation::Write(&buffer), SpiOperation::Read(&mut data)]; + self.device.transaction(&mut operations).map_err(SpiError::BusError)?; // SFR addresses are at the LSB of the registers // so last read byte is the MSB of the register // and since bitfield_msb is used, order of bytes is reversed - let result = u32::from_le_bytes(slice.try_into().expect("wrong slice length")); - + let result = u32::from_le_bytes(data); Ok(result) } /// Verify address within RAM bounds - fn verify_ram_address(&self, addr: u16, data_length: usize) -> Result<(), Error> { + fn verify_ram_address(&self, addr: u16, data_length: usize) -> Result<(), CanError> { if addr < 0x400 || (addr + (data_length as u16)) > 0xBFF { - return Err(Error::InvalidRamAddress(addr)); + return Err(CanError::InvalidRamAddress(addr)); } Ok(()) } /// Reads a single register byte - fn read_register(&mut self, register: u16) -> Result> { + fn read_register(&mut self, register: u16) -> Result> { let mut buffer = self.cmd_buffer(register, Operation::Read); self.transfer(&mut buffer) } /// Executes a SPI transfer with three bytes buffer and returns the last byte received - fn transfer(&mut self, buffer: &mut [u8]) -> Result> { - self.pin_cs.set_low().map_err(CSError)?; - let result = self.bus.transfer(buffer).map_err(TransferError); - self.pin_cs.set_high().map_err(CSError)?; + fn transfer(&mut self, buffer: &mut [u8]) -> Result> { + self.device.transfer_in_place(buffer).map_err(SpiError::BusError)?; - Ok(result?[2]) + Ok(buffer[2]) } /// Creates a three byte command buffer for the given register @@ -500,7 +461,7 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// Returns if the TX/RX fifo not full/empty flag is set - fn fifo_tfnrfnif(&mut self, fifo_reg_addr: u16) -> Result> { + fn fifo_tfnrfnif(&mut self, fifo_reg_addr: u16) -> Result> { let txfifo_status_byte0 = self.read_register(fifo_reg_addr)?; let txfifo_status_reg0 = FifoStatusReg0::from(txfifo_status_byte0); @@ -511,7 +472,7 @@ impl, CS: OutputPin, CLK: Clock> MCP2517 { } /// Returns true if `TXREQ` bit of TX fifo is cleared i.e. all messages contained are transmitted - fn txfifo_cleared(&mut self, fifo_ctrl_reg: u16) -> Result> { + fn txfifo_cleared(&mut self, fifo_ctrl_reg: u16) -> Result> { // read TX FIFO control register byte 1 let txfifo_control_byte1 = self.read_register(fifo_ctrl_reg)?; let txfifo_control_reg = FifoControlReg1::from(txfifo_control_byte1); @@ -561,14 +522,8 @@ enum Operation { Read = 0b0011, } -impl From for ConfigError { +impl From for CanError { fn from(_error: embedded_time::clock::Error) -> Self { - ClockError - } -} - -impl From> for ConfigError { - fn from(value: BusError) -> Self { - Self::BusError(value) + CanError::ClockError } } diff --git a/src/example.rs b/src/example.rs index 2ef5ce9..befac9e 100644 --- a/src/example.rs +++ b/src/example.rs @@ -5,81 +5,133 @@ use alloc::vec; use alloc::vec::Vec; use core::cell::RefCell; use core::convert::Infallible; -use embedded_hal::blocking::spi::Transfer; -use embedded_hal::digital::v2::OutputPin; +use embedded_hal::spi::{ErrorType, Operation, SpiDevice}; use embedded_time::clock::Error; use embedded_time::duration::{Duration, Fraction}; use embedded_time::fixed_point::FixedPoint; use embedded_time::timer::param::{Armed, OneShot}; use embedded_time::{Clock, Instant, Timer}; -#[derive(Default)] -pub struct ExampleSPIBus { +#[derive(Default, Debug)] +pub struct ExampleSPIDevice { read_calls: u32, } -impl Transfer for ExampleSPIBus { +impl ErrorType for ExampleSPIDevice { type Error = Infallible; +} + +impl SpiDevice for ExampleSPIDevice { + fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> { + if operations[0] == Operation::Write(&[0x30, 0x70]) { + // C1FIFOUA2 + if let Operation::Read(read) = &mut operations[1] { + read.copy_from_slice(&[0, 0, 0x04, 0xA2]); + return Ok(()); + } + } - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { - // write command -> returns empty buffer - if (words[0] >> 4) == 0x2 { - return Ok(&[0u8; 3]); + if operations[0] == Operation::Write(&[0x30, 0x64]) { + // C1FIFOUA1 + if let Operation::Read(read) = &mut operations[1] { + read.copy_from_slice(&[0, 0, 0x04, 0x7C]); + return Ok(()); + } } - // RAM read command - if words.len() == 8 && words == [0u8; 8] { - words.iter_mut().enumerate().for_each(|(i, val)| { - *val += (i + 1) as u8; - }); - return Ok(&[0u8; 8]); + // RAM Read command + if let Operation::Write(_) = operations[0] { + if operations.len() == 2 { + if let Operation::Read(read) = &mut operations[1] { + if read.len() == 8 { + read.iter_mut().enumerate().for_each(|(i, val)| { + *val += (i + 1) as u8; + }); + return Ok(()); + } + } + } + } + + Ok(()) + } + + fn transfer_in_place(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> { + if (buf[0] >> 4) == 0x2 { + return Ok(()); } // SFR Read command - if words[0] >= 0x3 { - return match words[1] { + if buf[0] == 0x30 { + match buf[1] { // addr: C1CON reg 2 0x2 => { // configuration mode if self.read_calls == 0 { self.read_calls += 1; - return Ok(&[0, 0, 0b1001_0100]); + buf.copy_from_slice(&[0, 0, 0b1001_0100]); + return Ok(()); } // return operation mode NormalCANFD mode (called in configure and during transmission) - Ok(&[0x0, 0x0, 0b0000_0000]) + buf.copy_from_slice(&[0x0, 0x0, 0b0000_0000]); } // C1FIFOSTA2 - 0x6C => Ok(&[0, 0, 0x1]), - // C1FIFOUA2 (2 extra bytes in beginning for cmd+addr) - 0x70 => Ok(&[0, 0, 0, 0, 0x04, 0xA2]), + 0x6C => buf.copy_from_slice(&[0, 0, 0x1]), // C1FIFOCON2 register 1 - 0x69 => Ok(&[0, 0, 0]), + 0x69 => buf.copy_from_slice(&[0, 0, 0]), // C1FIFOSTA1 - 0x60 => Ok(&[0, 0, 0x1]), - // C1FIFOUA1 - 0x64 => Ok(&[0, 0, 0, 0x04, 0x7C]), - - _ => Ok(&[0, 0, 0]), - }; + 0x60 => buf.copy_from_slice(&[0, 0, 0x1]), + _ => {} + } } - - Ok(&[0u8; 3]) - } -} - -pub struct ExampleCSPin {} - -impl OutputPin for ExampleCSPin { - type Error = Infallible; - - fn set_low(&mut self) -> Result<(), Self::Error> { - Ok(()) - } - - fn set_high(&mut self) -> Result<(), Self::Error> { Ok(()) } + // fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + // // write command -> returns empty buffer + // if (write[0] >> 4) == 0x2 { + // return Ok(()); + // } + + // // RAM read command + // if read.len() == 8 && read == [0u8; 8] { + // read.iter_mut().enumerate().for_each(|(i, val)| { + // *val += (i + 1) as u8; + // }); + // return Ok(()); + // } + + // // SFR Read command + // if write[0] >= 0x3 { + // match write[1] { + // // addr: C1CON reg 2 + // 0x2 => { + // // configuration mode + // if self.read_calls == 0 { + // self.read_calls += 1; + // read.copy_from_slice(&[0, 0, 0b1001_0100]); + // } + + // // return operation mode NormalCANFD mode (called in configure and during transmission) + // read.copy_from_slice(&[0x0, 0x0, 0b0000_0000]); + // } + // // C1FIFOSTA2 + // 0x6C => read.copy_from_slice(&[0, 0, 0x1]), + // // C1FIFOUA2 (2 extra bytes in beginning for cmd+addr) + // 0x70 => read.copy_from_slice(&[0, 0, 0, 0, 0x04, 0xA2]), + // // C1FIFOCON2 register 1 + // 0x69 => read.copy_from_slice(&[0, 0, 0]), + // // C1FIFOSTA1 + // 0x60 => read.copy_from_slice(&[0, 0, 0x1]), + // // C1FIFOUA1 + // 0x64 => read.copy_from_slice(&[0, 0, 0, 0x04, 0x7C]), + + // _ => {} + // }; + // } + + // Ok(()) + // } } #[derive(Debug, PartialEq, Eq)] diff --git a/src/lib.rs b/src/lib.rs index f6b802a..b86998d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ //!## CAN TX/RX example //! //!``` -//!use mcp2517::example::{ExampleClock,ExampleCSPin,ExampleSPIBus}; +//!use mcp2517::example::{ExampleClock,ExampleSPIDevice}; //!use mcp2517::can::{MCP2517,CanController}; //!use mcp2517::message::{Can20,TxMessage}; //!use mcp2517::filter::Filter; @@ -24,11 +24,10 @@ //!use bytes::Bytes; //!use embedded_can::{Id,StandardId}; //! -//!let cs_pin = ExampleCSPin{}; -//!let spi_bus = ExampleSPIBus::default(); +//!let spi_dev = ExampleSPIDevice::default(); //!let clock = ExampleClock::default(); //! -//!let mut controller = MCP2517::new(spi_bus, cs_pin); +//!let mut controller = MCP2517::new(spi_dev); //! //!// configure CAN controller //!controller diff --git a/src/message.rs b/src/message.rs index ada997f..a0bd180 100644 --- a/src/message.rs +++ b/src/message.rs @@ -204,7 +204,7 @@ impl MessageType for CanFd { /// Transmit Message Object #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct TxMessage, const L: usize> { - /// first 2 bytes of Transmit Message Object + /// first 8 bytes of Transmit Message Object representing header pub(crate) header: TxHeader, /// Payload bytes of Message Object pub(crate) buff: Bytes, diff --git a/src/mocks.rs b/src/mocks.rs index 2e83553..78f9957 100644 --- a/src/mocks.rs +++ b/src/mocks.rs @@ -1,8 +1,9 @@ use alloc::vec::Vec; use core::cell::RefCell; -use embedded_hal::blocking::spi::Transfer; -use embedded_hal::digital::v2::OutputPin; -use embedded_time::clock::Error; +use core::fmt::{Debug, Formatter}; +use embedded_hal::spi::{Error, ErrorType, Operation}; +use embedded_hal::spi::{ErrorKind, SpiDevice}; +use embedded_time::clock::Error as ClockError; use embedded_time::duration::Duration; use embedded_time::fixed_point::FixedPoint; use embedded_time::fraction::Fraction; @@ -27,9 +28,9 @@ impl Clock for TestClock { type T = u64; const SCALING_FACTOR: Fraction = Fraction::new(1, 1_000_000); - fn try_now(&self) -> Result, Error> { + fn try_now(&self) -> Result, ClockError> { if self.next_instants.borrow().len() == 0 { - return Err(Error::Unspecified); + return Err(ClockError::Unspecified); } Ok(Instant::new(self.next_instants.borrow_mut().remove(0))) @@ -43,23 +44,43 @@ impl Clock for TestClock { } } -mock! { - pub SPIBus {} +pub struct MockDeviceBuilder { + device: MockSPIDevice, +} - impl Transfer for SPIBus{ - type Error = u32; +#[derive(Debug, Clone)] +pub enum SPIError { + Error1, +} - fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'static [u8], u32>; +impl Error for SPIError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other } } mock! { - pub Pin {} + pub SPIDevice {} - impl OutputPin for Pin { - type Error = u32; + impl ErrorType for SPIDevice { + type Error = SPIError; + } + impl SpiDevice for SPIDevice { - fn set_low(&mut self) -> Result<(), u32>; - fn set_high(&mut self) -> Result<(), u32>; + fn transaction<'a>( + &mut self, + operations: &mut [Operation<'a, u8>] + ) -> Result<(), SPIError>; + } + + impl PartialEq for SPIDevice { + fn eq(&self, _other: &Self) -> bool { + true + } + } + impl Debug for SPIDevice { + fn fmt<'a>(&self, f: &mut Formatter<'a>) -> core::fmt::Result { + f.debug_struct("MockSpiDevice").finish() + } } } diff --git a/src/tests/can.rs b/src/tests/can.rs index 6932b79..25c7d72 100644 --- a/src/tests/can.rs +++ b/src/tests/can.rs @@ -1,120 +1,81 @@ use crate::can::CanController; -use crate::can::{BusError, ConfigError, Error, MCP2517}; +use crate::can::{CanError, MCP2517}; use crate::config::{ BitRateConfig, ClockConfiguration, ClockOutputDivisor, Configuration, FifoConfiguration, PLLSetting, PayloadSize, RequestMode, RetransmissionAttempts, SystemClockDivisor, }; +use crate::example::{ExampleClock, ExampleSPIDevice}; use crate::message::{Can20, CanFd, TxMessage}; -use crate::mocks::{MockPin, MockSPIBus, TestClock}; +use crate::mocks::{MockSPIDevice, SPIError, TestClock}; use crate::status::OperationMode; use alloc::vec; use byteorder::{BigEndian, ByteOrder, LittleEndian}; use bytes::Bytes; use embedded_can::{ExtendedId, Id}; +use embedded_hal::spi::Operation; use mockall::Sequence; -#[test] -fn test_configure_correct() { - let clock = TestClock::new(vec![ - 100, // Config mode: Timer start, - 200, // Config mode: First expiration check - 300, // Config mode: Second expiration check - 10_000, // Request mode: Timer start - 10_100, // Request mode: First expiration check - ]); - - let mut bus = MockSPIBus::new(); - // Request configuration mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x3, 0b0000_1100], data); - Ok(&[0x0, 0x0, 0x0]) - }); - - // Still in normal mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x30, 0x2, 0x0], data); - Ok(&[0x0, 0x0, 0b0001_0100]) - }); - - // Configuration mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x30, 0x2, 0x0], data); - Ok(&[0x0, 0x0, 0b1001_0100]) - }); - +fn expect_config(spi_dev: &mut Mocks, seq: &mut Sequence) { // Writing clock configuration - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x2E, 0x0, 0b0110_0001], data); - Ok(&[0x0, 0x0, 0x0]) - }); + spi_dev.expect_register_write([0x2E, 0x0, 0b0110_0001], seq); // Writing NBT configuration register - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x04, 1, 15, 62, 0], data); - Ok(&[0u8; 6]) - }); + spi_dev.expect_write([0x20, 0x04, 1, 15, 62, 0], seq); // Writing RX FIFO configuration - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x5F, 0b0000_1111], data); - Ok(&[0x0, 0x0, 0x0]) - }); + spi_dev.expect_register_write([0x20, 0x5F, 0b0000_1111], seq); // Writing TX FIFO configuration - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x6A, 0b0010_1010], data); - Ok(&[0x0, 0x0, 0x0]) - }); + spi_dev.expect_register_write([0x20, 0x6A, 0b0010_1010], seq); // Writing TX FIFO configuration - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x6B, 0b0001_0011], data); - Ok(&[0x0, 0x0, 0x0]) - }); + spi_dev.expect_register_write([0x20, 0x6B, 0b0001_0011], seq); // Writing TX FIFO configuration - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x68, 0b1000_0000], data); - Ok(&[0x0, 0x0, 0x0]) - }); + spi_dev.expect_register_write([0x20, 0x68, 0b1000_0000], seq); // Enable filter for RX Fifo // filter disable - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x21, 0xD0, 0x00], data); - Ok(&[0u8, 0u8, 0u8]) - }); + spi_dev.expect_register_write([0x21, 0xD0, 0x00], seq); // write F02BP - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x21, 0xD0, 0x01], data); - Ok(&[0u8, 0u8, 0u8]) - }); + spi_dev.expect_register_write([0x21, 0xD0, 0x01], seq); // enable filter - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x21, 0xD0, 0b1000_0001], data); - Ok(&[0u8, 0u8, 0u8]) - }); + spi_dev.expect_register_write([0x21, 0xD0, 0b1000_0001], seq); +} + +#[test] +fn test_configure_correct() { + let clock = TestClock::new(vec![ + 100, // Config mode: Timer start, + 200, // Config mode: First expiration check + 300, // Config mode: Second expiration check + 10_000, // Request mode: Timer start + 10_100, // Request mode: First expiration check + ]); + + let mut mock = Mocks::new(); + let mut sequence = Sequence::new(); + + // Request configuration mode + mock.expect_register_write([0x20, 0x3, 0b0000_1100], &mut sequence); + + // Still in normal mode + mock.mock_register_read::<0b0001_0100>([0x30, 0x2], &mut sequence); + + // Configuration mode + mock.mock_register_read::<0b1001_0100>([0x30, 0x2], &mut sequence); + + expect_config(&mut mock, &mut sequence); // Request normal CAN 2.0B mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x3, 0b0000_1110], data); - Ok(&[0x0, 0x0, 0x0]) - }); + mock.expect_register_write([0x20, 0x3, 0b0000_1110], &mut sequence); // Request mode reached - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x30, 0x2, 0x0], data); - Ok(&[0x0, 0x0, 0b1100_0000]) - }); + mock.mock_register_read::<0b1100_0000>([0x30, 0x2], &mut sequence); - let mut pin_cs = MockPin::new(); - pin_cs.expect_set_low().times(14).return_const(Ok(())); - pin_cs.expect_set_high().times(14).return_const(Ok(())); - - let mut controller = MCP2517::new(bus, pin_cs); - controller + mock.into_controller() .configure( &Configuration { clock: ClockConfiguration { @@ -146,34 +107,20 @@ fn test_configure_mode_timeout() { 200, // First expiration check 2500, // Second expiration check ]); + let mut seq = Sequence::new(); - let mut bus = MockSPIBus::new(); - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x3, 0xC], data); - Ok(&[0x0, 0x0, 0x0]) - }); + let mut mock = Mocks::new(); + mock.expect_register_write([0x20, 0x3, 0xC], &mut seq); // Still in normal mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x30, 0x2, 0x0], data); - Ok(&[0x0, 0x0, 0b0001_0100]) - }); + mock.mock_register_read::<0b0001_0100>([0x30, 0x2], &mut seq); // Configuration mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x30, 0x2, 0x0], data); - Ok(&[0x0, 0x0, 0b0001_0100]) - }); - - let mut pin_cs = MockPin::new(); - pin_cs.expect_set_low().times(3).return_const(Ok(())); - pin_cs.expect_set_high().times(3).return_const(Ok(())); - - let mut controller = MCP2517::new(bus, pin_cs); - assert_eq!( - ConfigError::ConfigurationModeTimeout, - controller.configure(&Configuration::default(), &clock).unwrap_err() - ); + mock.mock_register_read::<0b0001_0100>([0x30, 0x2], &mut seq); + + let res = mock.into_controller().configure(&Configuration::default(), &clock); + + assert_eq!(CanError::ConfigurationModeTimeout, res.unwrap_err()); } const EXTENDED_ID: u32 = 0x14C92A2B; //0b000(1_0100_1100_10)(01_0010_1010_0010_1011) @@ -206,76 +153,20 @@ fn test_transmit_can20() { // mock writing message in RAM specified by fifo user address (0x4A2) // transfer cmd+tx_header - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - let mut cmd_and_header_buffer = [0u8; 10]; - cmd_and_header_buffer[0] = 0x28; - cmd_and_header_buffer[1] = 0xA2; - - cmd_and_header_buffer[2..].copy_from_slice(&tx_message.header.into_bytes()); - - for chunk in cmd_and_header_buffer[2..].chunks_exact_mut(4) { - let num = BigEndian::read_u32(chunk); - LittleEndian::write_u32(chunk, num); - } - - assert_eq!(cmd_and_header_buffer, data); - Ok(&[0u8; 10]) - }) - .in_sequence(&mut seq); - - // transfer payload - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!(payload, data); - Ok(&[0u8; 8]) - }) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - // mock setting of bits txreq and uinc - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x20, 0x69, 0x03], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + let mut cmd_and_header_buffer = [0u8; 10]; + cmd_and_header_buffer[0] = 0x28; + cmd_and_header_buffer[1] = 0xA2; + + cmd_and_header_buffer[2..].copy_from_slice(&tx_message.header.into_bytes()); + + for chunk in cmd_and_header_buffer[2..].chunks_exact_mut(4) { + let num = BigEndian::read_u32(chunk); + LittleEndian::write_u32(chunk, num); + } + + mocks.expect_fifo_write_transaction(cmd_and_header_buffer, payload, &mut seq); + + mocks.expect_register_write([0x20, 0x69, 0x03], &mut seq); // mock reading of fifo control register // 1st attempt -> txreq still set ->not all messages inside tx fifo have been transmitted @@ -293,7 +184,7 @@ fn test_transmit_can20_3_bytes() { let payload: [u8; 3] = [1, 2, 3]; let payload_bytes = Bytes::copy_from_slice(&payload); - let msg_type = Can20::<8> {}; + let msg_type = Can20::<4> {}; let identifier = ExtendedId::new(EXTENDED_ID).unwrap(); let tx_message = TxMessage::new(msg_type, payload_bytes, Id::Extended(identifier)).unwrap(); @@ -313,76 +204,20 @@ fn test_transmit_can20_3_bytes() { // mock writing message in RAM specified by fifo user address (0x4A2) // transfer cmd+tx_header - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - let mut cmd_and_header_buffer = [0u8; 10]; - cmd_and_header_buffer[0] = 0x28; - cmd_and_header_buffer[1] = 0xA2; - - cmd_and_header_buffer[2..].copy_from_slice(&tx_message.header.into_bytes()); - - for chunk in cmd_and_header_buffer[2..].chunks_exact_mut(4) { - let num = BigEndian::read_u32(chunk); - LittleEndian::write_u32(chunk, num); - } - - assert_eq!(cmd_and_header_buffer, data); - Ok(&[0u8; 10]) - }) - .in_sequence(&mut seq); - - // transfer payload - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!(payload, data[..payload.len()]); - Ok(&[0u8; 8]) - }) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - // mock setting of bits txreq and uinc - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x20, 0x69, 0x03], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + let mut cmd_and_header_buffer = [0u8; 10]; + cmd_and_header_buffer[0] = 0x28; + cmd_and_header_buffer[1] = 0xA2; + + cmd_and_header_buffer[2..].copy_from_slice(&tx_message.header.into_bytes()); + + for chunk in cmd_and_header_buffer[2..].chunks_exact_mut(4) { + let num = BigEndian::read_u32(chunk); + LittleEndian::write_u32(chunk, num); + } + + mocks.expect_fifo_write_transaction(cmd_and_header_buffer, payload, &mut seq); + + mocks.expect_register_write([0x20, 0x69, 0x03], &mut seq); // mock reading of fifo control register // 1st attempt -> txreq still set ->not all messages inside tx fifo have been transmitted @@ -420,75 +255,21 @@ fn test_transmit_can_fd() { // mock writing message in RAM specified by fifo user address (0x4A2) // transfer cmd+tx_header - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - let mut cmd_and_header_buffer = [0u8; 10]; - cmd_and_header_buffer[0] = 0x28; - cmd_and_header_buffer[1] = 0xA2; - cmd_and_header_buffer[2..].copy_from_slice(&tx_message.header.into_bytes()); - - for chunk in cmd_and_header_buffer[2..].chunks_exact_mut(4) { - let num = BigEndian::read_u32(chunk); - LittleEndian::write_u32(chunk, num); - } - - assert_eq!(cmd_and_header_buffer, data); - Ok(&[0u8; 10]) - }) - .in_sequence(&mut seq); - - // transfer payload - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!(payload, data); - Ok(&[1u8; 64]) - }) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - // mock setting of bits txreq and uinc - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x20, 0x69, 0x03], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + + let mut cmd_and_header_buffer = [0u8; 10]; + cmd_and_header_buffer[0] = 0x28; + cmd_and_header_buffer[1] = 0xA2; + + cmd_and_header_buffer[2..].copy_from_slice(&tx_message.header.into_bytes()); + + for chunk in cmd_and_header_buffer[2..].chunks_exact_mut(4) { + let num = BigEndian::read_u32(chunk); + LittleEndian::write_u32(chunk, num); + } + + mocks.expect_fifo_write_transaction(cmd_and_header_buffer, payload, &mut seq); + + mocks.expect_register_write([0x20, 0x69, 0x03], &mut seq); // mock reading of fifo control register // 1st attempt -> txreq still set ->not all messages inside tx fifo have been transmitted @@ -505,7 +286,7 @@ fn test_read_fifo_invalid_payload_buffer_size() { let mut buff = [0u8; 3]; let result = mocks.into_controller().read_fifo(0x123, &mut buff); - assert_eq!(result.unwrap_err(), Error::InvalidBufferSize(3)); + assert_eq!(result.unwrap_err(), CanError::InvalidBufferSize(3)); } #[test] @@ -527,62 +308,9 @@ fn test_receive() { // Message read from RAM address (0x47C+8) to start reading received message object payload // transfer cmd+address - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x38, 0x84], data); - Ok(&[0u8; 2]) - }) - .in_sequence(&mut seq); - - // transfer message_buff where message bytes are placed - mocks - .bus - .expect_transfer() - .times(1) - .returning(|data| { - assert_eq!([0u8; 8], data); - data.copy_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]); - Ok(&[1, 2, 3, 4, 5, 6, 7, 8]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - // set uinc bit in RX FIFO control register - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x20, 0x5D, 0b0000_0001], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_fifo_read_transaction([0x38, 0x84], [1, 2, 3, 4, 5, 6, 7, 8], &mut seq); + + mocks.expect_register_write([0x20, 0x5D, 0b0000_0001], &mut seq); let result = mocks.into_controller().receive(&mut message_buff, true); @@ -604,7 +332,7 @@ fn test_receive_fifo_empty() { let result = mocks.into_controller().receive(&mut message_buff, false); - assert_eq!(result.unwrap_err(), Error::RxFifoEmptyErr); + assert_eq!(result.unwrap_err(), CanError::RxFifoEmptyErr); } #[test] @@ -624,32 +352,14 @@ fn test_transmit_fifo_full() { let res = mocks.into_controller().transmit(&tx_message, false); - assert_eq!(res.unwrap_err(), Error::TxFifoFullErr); + assert_eq!(res.unwrap_err(), CanError::TxFifoFullErr); } #[test] fn test_reset_command() { let mut mocks = Mocks::default(); let mut seq = Sequence::new(); - - mocks.bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x00, 0x00, 0x00], data); - Ok(&[0u8; 3]) - }); - - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x0; 3], &mut seq); mocks.into_controller().reset().unwrap(); } @@ -665,89 +375,68 @@ fn test_request_mode_timeout() { 15_000, // Request mode: Second expiration check (expired) ]); - let mut bus = MockSPIBus::new(); + let mut mock = Mocks::new(); + let mut seq = Sequence::new(); + // Request configuration mode - bus.expect_transfer().times(1).returning(move |_| Ok(&[0x0, 0x0, 0x0])); + mock.expect_register_write([0x20, 0x3, 0b0000_1100], &mut seq); // Still in normal mode - bus.expect_transfer().times(1).returning(move |_| Ok(&[0x0, 0x0, 0b0001_0100])); + mock.mock_register_read::<0b0001_0100>([0x30, 0x2], &mut seq); // Configuration mode - bus.expect_transfer().times(1).returning(move |_| Ok(&[0x0, 0x0, 0b1001_0100])); - - // Writing configuration registers - bus.expect_transfer().times(6).returning(move |_| Ok(&[0x0, 0x0, 0x0])); - - // Enable filter for RX Fifo - // filter disable - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x21, 0xD0, 0x00], data); - Ok(&[0u8, 0u8, 0u8]) - }); - - // write F02BP - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x21, 0xD0, 0x01], data); - Ok(&[0u8, 0u8, 0u8]) - }); + mock.mock_register_read::<0b1001_0100>([0x30, 0x2], &mut seq); - // enable filter - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x21, 0xD0, 0b1000_0001], data); - Ok(&[0u8, 0u8, 0u8]) - }); + expect_config(&mut mock, &mut seq); // Request normal CAN FD mode - bus.expect_transfer().times(1).returning(move |data| { - assert_eq!([0x20, 0x3, 0b0000_1000], data); - Ok(&[0x0, 0x0, 0x0]) - }); + mock.expect_register_write([0x20, 0x3, 0b0000_1000], &mut seq); // Still configuration mode - bus.expect_transfer().times(2).returning(move |data| { - assert_eq!([0x30, 0x2, 0x0], data); - Ok(&[0x0, 0x0, 0b1001_0100]) - }); - - let mut pin_cs = MockPin::new(); - pin_cs.expect_set_low().times(15).return_const(Ok(())); - pin_cs.expect_set_high().times(15).return_const(Ok(())); - - let mut controller = MCP2517::new(bus, pin_cs); - assert_eq!( - ConfigError::RequestModeTimeout, - controller.configure(&Configuration::default(), &clock).unwrap_err() - ); -} + mock.mock_register_read::<0b1001_0100>([0x30, 0x2], &mut seq); + // Still configuration mode + mock.mock_register_read::<0b1001_0100>([0x30, 0x2], &mut seq); -#[test] -fn test_configure_cs_pin_error() { - let clock = TestClock::new(vec![]); - let mut mocks = Mocks::default(); - mocks.mock_cs_error(); - - assert_eq!( - ConfigError::BusError(BusError::CSError(21)), - mocks - .into_controller() - .configure(&Configuration::default(), &clock) - .unwrap_err() - ); + match mock + .into_controller() + .configure( + &Configuration { + clock: ClockConfiguration { + clock_output: ClockOutputDivisor::DivideBy10, + system_clock: SystemClockDivisor::DivideBy1, + disable_clock: false, + pll: PLLSetting::TenTimesPLL, + }, + fifo: FifoConfiguration { + rx_size: 16, + tx_attempts: RetransmissionAttempts::Three, + tx_priority: 10, + pl_size: PayloadSize::EightBytes, + tx_size: 20, + tx_enable: true, + }, + mode: RequestMode::NormalCANFD, + bit_rate: BitRateConfig::default(), + }, + &clock, + ) + .unwrap_err() + { + CanError::RequestModeTimeout => {} + _ => panic!("unexpected error type"), + } } #[test] fn test_configure_transfer_error() { let clock = TestClock::new(vec![]); - let mut mocks = Mocks::default(); - mocks.mock_transfer_error(); + let mut mock = Mocks::default(); + mock.mock_transfer_error(); - assert_eq!( - ConfigError::BusError(BusError::TransferError(55)), - mocks - .into_controller() - .configure(&Configuration::default(), &clock) - .unwrap_err() - ); + match mock.into_controller().configure(&Configuration::default(), &clock).unwrap_err() { + CanError::BusErr(_) => {} + _ => panic!("unexpected error type"), + } } #[test] @@ -767,26 +456,15 @@ fn test_read_operation_status_correct() { assert!(!status.restrict_retransmission); } -#[test] -fn test_read_operation_status_cs_error() { - let mut mocks = Mocks::default(); - mocks.mock_cs_error(); - - assert_eq!( - BusError::CSError(21), - mocks.into_controller().read_operation_status().unwrap_err() - ); -} - #[test] fn test_read_operation_status_transfer_error() { let mut mocks = Mocks::default(); mocks.mock_transfer_error(); - assert_eq!( - BusError::TransferError(55), - mocks.into_controller().read_operation_status().unwrap_err() - ); + match mocks.into_controller().read_operation_status().unwrap_err() { + CanError::BusErr(_) => {} + _ => panic!("Unexpected error type"), + } } #[test] @@ -803,26 +481,15 @@ fn test_read_oscillator_status_correct() { assert!(!status.pll_ready); } -#[test] -fn test_read_oscillator_status_cs_error() { - let mut mocks = Mocks::default(); - mocks.mock_cs_error(); - - assert_eq!( - BusError::CSError(21), - mocks.into_controller().read_oscillator_status().unwrap_err() - ); -} - #[test] fn test_read_oscillator_transfer_error() { let mut mocks = Mocks::default(); mocks.mock_transfer_error(); - assert_eq!( - BusError::TransferError(55), - mocks.into_controller().read_oscillator_status().unwrap_err() - ); + match mocks.into_controller().read_oscillator_status().unwrap_err() { + CanError::BusErr(_) => {} + _ => panic!("Unexpected error type"), + } } #[test] @@ -840,26 +507,15 @@ fn test_read_clock_configuration_correct() { assert_eq!(PLLSetting::DirectXTALOscillator, status.pll); } -#[test] -fn test_read_clock_configuration_cs_error() { - let mut mocks = Mocks::default(); - mocks.mock_cs_error(); - - assert_eq!( - BusError::CSError(21), - mocks.into_controller().read_clock_configuration().unwrap_err() - ); -} - #[test] fn test_read_clock_configuration_transfer_error() { let mut mocks = Mocks::default(); mocks.mock_transfer_error(); - assert_eq!( - BusError::TransferError(55), - mocks.into_controller().read_clock_configuration().unwrap_err() - ); + match mocks.into_controller().read_clock_configuration().unwrap_err() { + CanError::BusErr(_) => {} + _ => panic!("Unexpected error type"), + } } #[test] @@ -867,75 +523,14 @@ fn test_filter_enable() { let mut mocks = Mocks::default(); let mut seq = Sequence::new(); - // Disable filter 2 - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD2, 0x00], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x21, 0xD2, 0x00], &mut seq); // write the fifo index where the message that matches the filter is stored // Fifo rx index is 1 in our case - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD2, 0x01], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - - // Set FLTENm to enable filter - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD2, 0x81], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + + mocks.expect_register_write([0x21, 0xD2, 0x01], &mut seq); + + mocks.expect_register_write([0x21, 0xD2, 0x81], &mut seq); let result = mocks.into_controller().enable_filter(1, 2); @@ -948,99 +543,188 @@ fn test_filter_disable() { let mut seq = Sequence::new(); // Disable filter 6 - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - // byte0+byte1 -> cmd+addr - // byte2 -> byte value written - assert_eq!([0x21, 0xD6, 0x00], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x21, 0xD6, 0x00], &mut seq); let result = mocks.into_controller().disable_filter(6); assert!(result.is_ok()); } -#[derive(Default)] +#[derive(Default, Debug, PartialEq)] pub(crate) struct Mocks { - pub(crate) bus: MockSPIBus, - pub(crate) pin_cs: MockPin, + pub(crate) device: MockSPIDevice, } impl Mocks { - pub fn into_controller(self) -> MCP2517 { - MCP2517::new(self.bus, self.pin_cs) + pub fn new() -> Self { + Self { + device: MockSPIDevice::new(), + } + } + pub fn into_controller(self) -> MCP2517 { + MCP2517::new(self.device) } /// Simulates a SPI transfer fault pub fn mock_transfer_error(&mut self) { - self.bus.expect_transfer().times(1).return_const(Err(55)); - self.pin_cs.expect_set_low().times(1).return_const(Ok(())); - self.pin_cs.expect_set_high().times(1).return_const(Ok(())); - } - - /// Simulates a CS pin set error - pub fn mock_cs_error(&mut self) { - self.pin_cs.expect_set_low().times(1).return_const(Err(21)); + self.device.expect_transaction().times(1).return_const(Err(SPIError::Error1)); } /// Mocks the reading of a single register byte pub fn mock_register_read(&mut self, expected_command: [u8; 2], seq: &mut Sequence) { let expected_buffer = [expected_command[0], expected_command[1], 0x0]; - self.pin_cs.expect_set_low().times(1).return_const(Ok(())).in_sequence(seq); - - self.bus - .expect_transfer() + self.device + .expect_transaction() .times(1) - .returning(move |data| { - assert_eq!(expected_buffer, data); - Ok(&[0x0, 0x0, REG]) + .returning(move |operation| { + assert_eq!(operation.len(), 1); + match &mut operation[0] { + Operation::TransferInPlace(buff) => { + assert_eq!(expected_buffer, *buff); + buff.copy_from_slice(&[0x0, 0x0, REG]); + } + _ => panic!("unexpected operation {:?}", operation[0]), + } + Ok(()) }) .in_sequence(seq); - - self.pin_cs.expect_set_high().times(1).return_const(Ok(())).in_sequence(seq); } /// mocks 4-byte register read pub fn mock_read32(&mut self, expected_command: [u8; 2], seq: &mut Sequence) { - let expected_buffer = [expected_command[0], expected_command[1], 0u8, 0u8, 0u8, 0u8]; + let expected_buffer = [expected_command[0], expected_command[1]]; + + self.device + .expect_transaction() + .times(1) + .returning(move |operation| { + assert_eq!(operation.len(), 2); + match &operation[0] { + Operation::Write(buff) => { + assert_eq!(expected_buffer, *buff); + } + _ => panic!("unexpected operation {:?}", operation[0]), + } + match &mut operation[1] { + Operation::Read(read) => { + assert_eq!(read.len(), 4); + read.copy_from_slice(&[REG as u8, (REG >> 8) as u8, (REG >> 16) as u8, (REG >> 24) as u8]); + } + _ => panic!("unexpected operation {:?}", operation[1]), + } + Ok(()) + }) + .in_sequence(seq); + } + + pub fn expect_register_write(&mut self, expected_write: [u8; 3], sequence: &mut Sequence) { + self.device + .expect_transaction() + .times(1) + .returning(move |operation| { + assert_eq!(operation.len(), 1); + + match &operation[0] { + Operation::TransferInPlace(buff) => { + assert_eq!(expected_write, *buff); + } + _ => panic!("Unexpected operation received {:?}", operation[0]), + } - self.pin_cs.expect_set_low().times(1).return_const(Ok(())).in_sequence(seq); + Ok(()) + }) + .in_sequence(sequence); + } - self.bus - .expect_transfer() + pub fn expect_write(&mut self, expected_write: [u8; L], sequence: &mut Sequence) { + self.device + .expect_transaction() .times(1) - .returning(move |data| { - assert_eq!(expected_buffer, data); - data.copy_from_slice(&[ - 0x0, - 0x0, - REG as u8, - (REG >> 8) as u8, - (REG >> 16) as u8, - (REG >> 24) as u8, - ]); - Ok(&[0u8; 6]) + .returning(move |operation| { + assert_eq!(operation.len(), 1); + + match operation[0] { + Operation::Write(write) => { + assert_eq!(write, expected_write); + } + _ => panic!("Unexpected operation received {:?}", operation[0]), + } + + Ok(()) + }) + .in_sequence(sequence); + } + + pub fn expect_fifo_write_transaction( + &mut self, + header: [u8; 10], + payload: [u8; L], + seq: &mut Sequence, + ) { + self.device + .expect_transaction() + .times(1) + .returning(move |operation| { + assert_eq!(operation.len(), 2); + match &operation[0] { + Operation::Write(write) => { + assert_eq!(*write, header); + } + _ => panic!("Unexpected operation received {:?}", operation[0]), + } + match operation[1] { + Operation::Write(write) => { + assert_eq!(write[..payload.len()], payload); + } + _ => panic!("Unexpected operation received {:?}", operation[0]), + } + Ok(()) }) .in_sequence(seq); + } - self.pin_cs.expect_set_high().times(1).return_const(Ok(())).in_sequence(seq); + pub fn expect_fifo_read_transaction( + &mut self, + command: [u8; 2], + payload_received: [u8; L], + seq: &mut Sequence, + ) { + self.device + .expect_transaction() + .times(1) + .returning(move |operation| { + assert_eq!(operation.len(), 2); + match operation[0] { + Operation::Write(write) => { + assert_eq!(write, command); + } + _ => panic!("Unexpected operation received {:?}", operation[0]), + } + + match &mut operation[1] { + Operation::Read(read) => { + read.copy_from_slice(&payload_received); + } + _ => panic!("Unexpected operation received {:?}", operation[0]), + } + Ok(()) + }) + .in_sequence(seq); } } + +#[test] +fn example_test() { + let sys_clk = ExampleClock::default(); + let spi_dev = ExampleSPIDevice::default(); + + // Initialize controller object + let mut can_controller = MCP2517::new(spi_dev); + + // Use default configuration settings + let can_config = Configuration::default(); + + // Configure CAN controller + can_controller.configure(&can_config, &sys_clk).unwrap(); +} diff --git a/src/tests/filter.rs b/src/tests/filter.rs index 7ba2fb5..744d7c9 100644 --- a/src/tests/filter.rs +++ b/src/tests/filter.rs @@ -23,96 +23,15 @@ fn test_set_filter_object_standard_id() { let mut mocks = Mocks::default(); // disable filter 0 - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD1, 0x00], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x21, 0xD1, 0x00], &mut seq); // write filter value - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xF8, 0xA5, 0x6, 0x0, 0x0], data); - Ok(&[0u8; 2]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - + mocks.expect_write([0x21, 0xF8, 0xA5, 0x6, 0x0, 0x0], &mut seq); // write mask value - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xFC, 0x3, 0u8, 0u8, 0x40], data); - Ok(&[0u8; 6]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_write([0x21, 0xFC, 0x3, 0u8, 0u8, 0x40], &mut seq); // enable filter - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD1, 0x81], data); - Ok(&[0u8; 6]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x21, 0xD1, 0x81], &mut seq); let result = mocks.into_controller().set_filter_object(filter); @@ -132,96 +51,16 @@ fn test_set_filter_object_extended_id() { let mut mocks = Mocks::default(); // disable filter 0 - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD0, 0x00], data); - Ok(&[0u8; 3]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x21, 0xD0, 0x00], &mut seq); // write filter value - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xF0, 0x32, 0x5D, 0x51, 0x09], data); - Ok(&[0u8; 2]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_write([0x21, 0xF0, 0x32, 0x5D, 0x51, 0x09], &mut seq); // write mask value - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xF4, 0u8, 0x6, 0u8, 0u8], data); - Ok(&[0u8; 6]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_write([0x21, 0xF4, 0u8, 0x6, 0u8, 0u8], &mut seq); // enable filter - mocks - .pin_cs - .expect_set_low() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); - mocks - .bus - .expect_transfer() - .times(1) - .returning(move |data| { - assert_eq!([0x21, 0xD0, 0x81], data); - Ok(&[0u8; 6]) - }) - .in_sequence(&mut seq); - mocks - .pin_cs - .expect_set_high() - .times(1) - .return_const(Ok(())) - .in_sequence(&mut seq); + mocks.expect_register_write([0x21, 0xD0, 0x81], &mut seq); let result_extended = mocks.into_controller().set_filter_object(filter); From eda8e887ff4b641beebc423c24eec2217b95c6b4 Mon Sep 17 00:00:00 2001 From: Hussein Hazem Date: Thu, 5 Dec 2024 11:45:21 +0100 Subject: [PATCH 2/5] #32 add comments --- src/tests/can.rs | 25 +++++++------------------ src/tests/filter.rs | 8 ++++---- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/tests/can.rs b/src/tests/can.rs index 25c7d72..707458f 100644 --- a/src/tests/can.rs +++ b/src/tests/can.rs @@ -4,7 +4,6 @@ use crate::config::{ BitRateConfig, ClockConfiguration, ClockOutputDivisor, Configuration, FifoConfiguration, PLLSetting, PayloadSize, RequestMode, RetransmissionAttempts, SystemClockDivisor, }; -use crate::example::{ExampleClock, ExampleSPIDevice}; use crate::message::{Can20, CanFd, TxMessage}; use crate::mocks::{MockSPIDevice, SPIError, TestClock}; use crate::status::OperationMode; @@ -15,12 +14,13 @@ use embedded_can::{ExtendedId, Id}; use embedded_hal::spi::Operation; use mockall::Sequence; +/// CAN configuration mock fn expect_config(spi_dev: &mut Mocks, seq: &mut Sequence) { // Writing clock configuration spi_dev.expect_register_write([0x2E, 0x0, 0b0110_0001], seq); // Writing NBT configuration register - spi_dev.expect_write([0x20, 0x04, 1, 15, 62, 0], seq); + spi_dev.mock_write32([0x20, 0x04, 1, 15, 62, 0], seq); // Writing RX FIFO configuration spi_dev.expect_register_write([0x20, 0x5F, 0b0000_1111], seq); @@ -618,6 +618,7 @@ impl Mocks { .in_sequence(seq); } + /// Mock write of single register (1 byte) using SPI transfer pub fn expect_register_write(&mut self, expected_write: [u8; 3], sequence: &mut Sequence) { self.device .expect_transaction() @@ -637,7 +638,8 @@ impl Mocks { .in_sequence(sequence); } - pub fn expect_write(&mut self, expected_write: [u8; L], sequence: &mut Sequence) { + /// Mock write 4-byte register write + pub fn mock_write32(&mut self, expected_write: [u8; 6], sequence: &mut Sequence) { self.device .expect_transaction() .times(1) @@ -656,6 +658,7 @@ impl Mocks { .in_sequence(sequence); } + /// Mock write operation to TX FIFO pub fn expect_fifo_write_transaction( &mut self, header: [u8; 10], @@ -684,6 +687,7 @@ impl Mocks { .in_sequence(seq); } + /// Mock read operation of RX FIFO pub fn expect_fifo_read_transaction( &mut self, command: [u8; 2], @@ -713,18 +717,3 @@ impl Mocks { .in_sequence(seq); } } - -#[test] -fn example_test() { - let sys_clk = ExampleClock::default(); - let spi_dev = ExampleSPIDevice::default(); - - // Initialize controller object - let mut can_controller = MCP2517::new(spi_dev); - - // Use default configuration settings - let can_config = Configuration::default(); - - // Configure CAN controller - can_controller.configure(&can_config, &sys_clk).unwrap(); -} diff --git a/src/tests/filter.rs b/src/tests/filter.rs index 744d7c9..4cb0c35 100644 --- a/src/tests/filter.rs +++ b/src/tests/filter.rs @@ -26,9 +26,9 @@ fn test_set_filter_object_standard_id() { mocks.expect_register_write([0x21, 0xD1, 0x00], &mut seq); // write filter value - mocks.expect_write([0x21, 0xF8, 0xA5, 0x6, 0x0, 0x0], &mut seq); + mocks.mock_write32([0x21, 0xF8, 0xA5, 0x6, 0x0, 0x0], &mut seq); // write mask value - mocks.expect_write([0x21, 0xFC, 0x3, 0u8, 0u8, 0x40], &mut seq); + mocks.mock_write32([0x21, 0xFC, 0x3, 0u8, 0u8, 0x40], &mut seq); // enable filter mocks.expect_register_write([0x21, 0xD1, 0x81], &mut seq); @@ -54,10 +54,10 @@ fn test_set_filter_object_extended_id() { mocks.expect_register_write([0x21, 0xD0, 0x00], &mut seq); // write filter value - mocks.expect_write([0x21, 0xF0, 0x32, 0x5D, 0x51, 0x09], &mut seq); + mocks.mock_write32([0x21, 0xF0, 0x32, 0x5D, 0x51, 0x09], &mut seq); // write mask value - mocks.expect_write([0x21, 0xF4, 0u8, 0x6, 0u8, 0u8], &mut seq); + mocks.mock_write32([0x21, 0xF4, 0u8, 0x6, 0u8, 0u8], &mut seq); // enable filter mocks.expect_register_write([0x21, 0xD0, 0x81], &mut seq); From a116dfb6c9ac36bfaa1289c74972f095796843f9 Mon Sep 17 00:00:00 2001 From: Hussein Hazem Date: Thu, 5 Dec 2024 11:48:52 +0100 Subject: [PATCH 3/5] #32 rm commented out code --- src/example.rs | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/src/example.rs b/src/example.rs index befac9e..efdaff3 100644 --- a/src/example.rs +++ b/src/example.rs @@ -87,51 +87,6 @@ impl SpiDevice for ExampleSPIDevice { } Ok(()) } - // fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { - // // write command -> returns empty buffer - // if (write[0] >> 4) == 0x2 { - // return Ok(()); - // } - - // // RAM read command - // if read.len() == 8 && read == [0u8; 8] { - // read.iter_mut().enumerate().for_each(|(i, val)| { - // *val += (i + 1) as u8; - // }); - // return Ok(()); - // } - - // // SFR Read command - // if write[0] >= 0x3 { - // match write[1] { - // // addr: C1CON reg 2 - // 0x2 => { - // // configuration mode - // if self.read_calls == 0 { - // self.read_calls += 1; - // read.copy_from_slice(&[0, 0, 0b1001_0100]); - // } - - // // return operation mode NormalCANFD mode (called in configure and during transmission) - // read.copy_from_slice(&[0x0, 0x0, 0b0000_0000]); - // } - // // C1FIFOSTA2 - // 0x6C => read.copy_from_slice(&[0, 0, 0x1]), - // // C1FIFOUA2 (2 extra bytes in beginning for cmd+addr) - // 0x70 => read.copy_from_slice(&[0, 0, 0, 0, 0x04, 0xA2]), - // // C1FIFOCON2 register 1 - // 0x69 => read.copy_from_slice(&[0, 0, 0]), - // // C1FIFOSTA1 - // 0x60 => read.copy_from_slice(&[0, 0, 0x1]), - // // C1FIFOUA1 - // 0x64 => read.copy_from_slice(&[0, 0, 0, 0x04, 0x7C]), - - // _ => {} - // }; - // } - - // Ok(()) - // } } #[derive(Debug, PartialEq, Eq)] From 5fc688f2ece6aa94340db1f2bf183dc29eb5e315 Mon Sep 17 00:00:00 2001 From: Hussein Hazem Date: Mon, 9 Dec 2024 12:39:25 +0100 Subject: [PATCH 4/5] #32 simplify example code --- src/example.rs | 23 +++++++--------- src/tests/can.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 16 deletions(-) diff --git a/src/example.rs b/src/example.rs index efdaff3..48a2958 100644 --- a/src/example.rs +++ b/src/example.rs @@ -26,7 +26,7 @@ impl SpiDevice for ExampleSPIDevice { if operations[0] == Operation::Write(&[0x30, 0x70]) { // C1FIFOUA2 if let Operation::Read(read) = &mut operations[1] { - read.copy_from_slice(&[0, 0, 0x04, 0xA2]); + read.copy_from_slice(&[0xA2, 0x04, 0x0, 0x0]); return Ok(()); } } @@ -34,23 +34,20 @@ impl SpiDevice for ExampleSPIDevice { if operations[0] == Operation::Write(&[0x30, 0x64]) { // C1FIFOUA1 if let Operation::Read(read) = &mut operations[1] { - read.copy_from_slice(&[0, 0, 0x04, 0x7C]); + read.copy_from_slice(&[0x7C, 0x04, 0x0, 0x0]); return Ok(()); } } - // RAM Read command - if let Operation::Write(_) = operations[0] { - if operations.len() == 2 { - if let Operation::Read(read) = &mut operations[1] { - if read.len() == 8 { - read.iter_mut().enumerate().for_each(|(i, val)| { - *val += (i + 1) as u8; - }); - return Ok(()); - } - } + // Read RX fifo (payload received) + if operations[0] != Operation::Write(&[0x38, 0x84]) { + return Ok(()); + } + if let Operation::Read(words) = &mut operations[1] { + if words.len() != 8 { + return Ok(()); } + words.copy_from_slice(&[0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); } Ok(()) diff --git a/src/tests/can.rs b/src/tests/can.rs index 707458f..b98f697 100644 --- a/src/tests/can.rs +++ b/src/tests/can.rs @@ -1,16 +1,18 @@ use crate::can::CanController; use crate::can::{CanError, MCP2517}; use crate::config::{ - BitRateConfig, ClockConfiguration, ClockOutputDivisor, Configuration, FifoConfiguration, PLLSetting, PayloadSize, - RequestMode, RetransmissionAttempts, SystemClockDivisor, + BitRateConfig, CanBaudRate, ClockConfiguration, ClockOutputDivisor, Configuration, FifoConfiguration, PLLSetting, + PayloadSize, RequestMode, RetransmissionAttempts, SysClk, SystemClockDivisor, }; +use crate::example::{ExampleClock, ExampleSPIDevice}; +use crate::filter::Filter; use crate::message::{Can20, CanFd, TxMessage}; use crate::mocks::{MockSPIDevice, SPIError, TestClock}; use crate::status::OperationMode; use alloc::vec; use byteorder::{BigEndian, ByteOrder, LittleEndian}; use bytes::Bytes; -use embedded_can::{ExtendedId, Id}; +use embedded_can::{ExtendedId, Id, StandardId}; use embedded_hal::spi::Operation; use mockall::Sequence; @@ -717,3 +719,64 @@ impl Mocks { .in_sequence(seq); } } + +#[test] +fn test_lib() { + let spi_dev = ExampleSPIDevice::default(); + let clock = ExampleClock::default(); + + let mut controller = MCP2517::new(spi_dev); + + // configure CAN controller + controller + .configure( + &Configuration { + clock: ClockConfiguration { + clock_output: ClockOutputDivisor::DivideBy10, + system_clock: SystemClockDivisor::DivideBy1, + disable_clock: false, + pll: PLLSetting::TenTimesPLL, + }, + fifo: FifoConfiguration { + rx_size: 16, + tx_attempts: RetransmissionAttempts::Three, + tx_priority: 10, + pl_size: PayloadSize::EightBytes, + tx_size: 20, + tx_enable: true, + }, + mode: RequestMode::NormalCANFD, + bit_rate: BitRateConfig { + sys_clk: SysClk::MHz20, + can_speed: CanBaudRate::Kpbs500, + }, + }, + &clock, + ) + .unwrap(); + + // Create message frame + let can_id = Id::Standard(StandardId::new(0x55).unwrap()); + + // Important note: Generic arg for message type for CAN2.0 + // should be either 4 or 8, the DLC will be based off the + // length of the payload buffer. So for a payload of 5 bytes + // you can only use Can20::<8> as the message type + let message_type = Can20::<8> {}; + let payload = [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]; + let pl_bytes = Bytes::copy_from_slice(&payload); + let can_message = TxMessage::new(message_type, pl_bytes, can_id).unwrap(); + + // Create and set filter object + let filter = Filter::new(can_id, 0).unwrap(); + let _ = controller.set_filter_object(filter); + + // Transmit CAN message in blocking mode + controller.transmit(&can_message, true).unwrap(); + + // Receive CAN message in blocking mode + let mut buff = [0u8; 8]; + let result = controller.receive(&mut buff, true); + assert!(result.is_ok()); + assert_eq!(buff, [0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); +} From b5ed5f89abd1eb00688352e753ca4b82ee148f32 Mon Sep 17 00:00:00 2001 From: Hussein Hazem Date: Mon, 9 Dec 2024 12:56:17 +0100 Subject: [PATCH 5/5] #32 add RP2350 targets to QA pipeline --- .github/workflows/qa.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/qa.yaml b/.github/workflows/qa.yaml index 8ac7bb8..fab5a1a 100644 --- a/.github/workflows/qa.yaml +++ b/.github/workflows/qa.yaml @@ -58,6 +58,8 @@ jobs: matrix: target: - thumbv7m-none-eabi + - thumbv8m.main-none-eabihf + - riscv32imac-unknown-none-elf rust: - stable - beta