Skip to content

Commit

Permalink
#29 change CAN tx/rx to allow non-blocking
Browse files Browse the repository at this point in the history
  • Loading branch information
HusseinAbdelhamid committed Oct 14, 2024
1 parent 8885078 commit c1931db
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 34 deletions.
98 changes: 68 additions & 30 deletions src/can.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ pub enum Error<B, CS> {
InvalidRamAddress(u16),
/// Payload buffer length not a multiple of 4 bytes
InvalidBufferSize(usize),
/// RX Fifo buffer is empty
RxFifoEmpty,
/// TX fifo buffer is full
TxFifoFull,
}

impl<B, CS> From<BusError<B, CS>> for Error<B, CS> {
Expand Down Expand Up @@ -95,29 +99,35 @@ pub trait CanController {
type Error;

/// Transmit CAN message
fn transmit<const L: usize, T: MessageType<L>>(&mut self, message: &TxMessage<T, L>) -> Result<(), Self::Error>;
/// * `blocking`: if true, function blocks until TX fifo buffer is empty
fn transmit<const L: usize, T: MessageType<L>>(
&mut self,
message: &TxMessage<T, L>,
blocking: bool,
) -> Result<(), Self::Error>;

/// Receive CAN message
fn receive<const L: usize>(&mut self, data: &mut [u8; L]) -> Result<(), Self::Error>;
/// * `blocking`: if true, function blocks until RX fifo contains at least one message
fn receive<const L: usize>(&mut self, data: &mut [u8; L], blocking: bool) -> Result<(), Self::Error>;
/// Set corresponding filter and mask registers
fn set_filter_object(&mut self, filter: Filter) -> Result<(), Self::Error>;
}

impl<B: Transfer<u8>, CS: OutputPin, CLK: Clock> CanController for MCP2517<B, CS, CLK> {
type Error = Error<B::Error, CS::Error>;

fn transmit<const L: usize, T: MessageType<L>>(&mut self, message: &TxMessage<T, L>) -> Result<(), Self::Error> {
// make sure there is space for new message in TX FIFO
// read byte 0 of TX FIFO status register
let status_reg_addr = Self::fifo_status_register(FIFO_TX_INDEX);

let mut txfifo_status_byte0 = self.read_register(status_reg_addr)?;
let mut txfifo_status_reg0 = FifoStatusReg0::from(txfifo_status_byte0);

// block until there is room available for new message in TX FIFO
while !txfifo_status_reg0.tfnrfnif() {
txfifo_status_byte0 = self.read_register(status_reg_addr)?;
txfifo_status_reg0 = FifoStatusReg0::from(txfifo_status_byte0);
fn transmit<const L: usize, T: MessageType<L>>(
&mut self,
message: &TxMessage<T, L>,
blocking: bool,
) -> Result<(), Self::Error> {
let fifo_status_reg = Self::fifo_status_register(FIFO_TX_INDEX);

// Check if TX fifo is full
if blocking {
while !self.fifo_not_full(fifo_status_reg)? {}
} else if !self.fifo_not_full(fifo_status_reg)? {
return Err(Error::TxFifoFull);
}

// make sure length of payload is consistent with CAN operation mode
Expand All @@ -143,28 +153,22 @@ impl<B: Transfer<u8>, CS: OutputPin, CLK: Clock> CanController for MCP2517<B, CS
// Request transmission (set txreq) and set uinc in TX FIFO control register byte 1
self.write_register(fifo_control_reg1, 0x03)?;

// read TX FIFO control register byte 1
let mut txfifo_control_byte1 = self.read_register(fifo_control_reg1)?;
let mut txfifo_control_reg = FifoControlReg1::from(txfifo_control_byte1);

// block till txreq is cleared confirming that all messages in TX FIFO are transmitted
while txfifo_control_reg.txreq() {
txfifo_control_byte1 = self.read_register(fifo_control_reg1)?;
txfifo_control_reg = FifoControlReg1::from(txfifo_control_byte1);
// block till TXREQ is cleared confirming that all messages in TX FIFO are transmitted
if blocking {
while !self.txfifo_cleared(fifo_control_reg1)? {}
}

Ok(())
}

fn receive<const L: usize>(&mut self, data: &mut [u8; L]) -> Result<(), Self::Error> {
fn receive<const L: usize>(&mut self, data: &mut [u8; L], blocking: bool) -> Result<(), Self::Error> {
let fifo_status_reg = Self::fifo_status_register(FIFO_RX_INDEX);

let mut rxfifo_status_byte0 = self.read_register(fifo_status_reg)?;
let mut rxfifo_status_reg0 = FifoStatusReg0::from(rxfifo_status_byte0);

// block until fifo rx contains at least one message
while !rxfifo_status_reg0.tfnrfnif() {
rxfifo_status_byte0 = self.read_register(fifo_status_reg)?;
rxfifo_status_reg0 = FifoStatusReg0::from(rxfifo_status_byte0);
// Make sure RX fifo is not empty
if blocking {
while !self.fifo_not_empty(fifo_status_reg)? {}
} else if !self.fifo_not_empty(fifo_status_reg)? {
return Err(Error::RxFifoEmpty);
}

let user_address = self.read32(Self::fifo_user_address_register(FIFO_RX_INDEX))?;
Expand Down Expand Up @@ -475,6 +479,40 @@ impl<B: Transfer<u8>, CS: OutputPin, CLK: Clock> MCP2517<B, CS, CLK> {
buffer
}

/// Returns true if TX fifo is not full
fn fifo_not_full(&mut self, fifo_reg_addr: u16) -> Result<bool, BusError<B::Error, CS::Error>> {
let txfifo_status_byte0 = self.read_register(fifo_reg_addr)?;
let txfifo_status_reg0 = FifoStatusReg0::from(txfifo_status_byte0);

if txfifo_status_reg0.tfnrfnif() {
return Ok(true);
}
Ok(false)
}

/// Returns true if RX fifo is not empty
fn fifo_not_empty(&mut self, fifo_reg_addr: u16) -> Result<bool, BusError<B::Error, CS::Error>> {
let rxfifo_status_byte0 = self.read_register(fifo_reg_addr)?;
let rxfifo_status_reg0 = FifoStatusReg0::from(rxfifo_status_byte0);

if rxfifo_status_reg0.tfnrfnif() {
return Ok(true);
}
Ok(false)
}

/// 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<bool, BusError<B::Error, CS::Error>> {
// 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);

if txfifo_control_reg.txreq() {
return Ok(false);
}
Ok(true)
}

/// Returns the configuration register address for the given FIFO index
fn fifo_control_register(fifo_index: u8) -> u16 {
0x05C + 12 * (fifo_index as u16 - 1)
Expand Down
44 changes: 40 additions & 4 deletions src/tests/can.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ fn test_transmit_can20() {
// 2nd attempt -> txreq cleared -> all messages inside tx fifo have been transmitted
mocks.mock_register_read::<0x00>([0x30, 0x69], &mut seq);

mocks.into_controller().transmit(&tx_message_copy).unwrap();
mocks.into_controller().transmit(&tx_message_copy, true).unwrap();
}

#[test]
Expand Down Expand Up @@ -390,7 +390,7 @@ fn test_transmit_can20_3_bytes() {
// 2nd attempt -> txreq cleared -> all messages inside tx fifo have been transmitted
mocks.mock_register_read::<0x00>([0x30, 0x69], &mut seq);

mocks.into_controller().transmit(&tx_message_copy).unwrap();
mocks.into_controller().transmit(&tx_message_copy, true).unwrap();
}

#[test]
Expand Down Expand Up @@ -496,7 +496,7 @@ fn test_transmit_can_fd() {
// 2nd attempt -> txreq cleared -> all messages inside tx fifo have been transmitted
mocks.mock_register_read::<0x00>([0x30, 0x69], &mut seq);

mocks.into_controller().transmit(&tx_message_copy).unwrap();
mocks.into_controller().transmit(&tx_message_copy, true).unwrap();
}

#[test]
Expand Down Expand Up @@ -584,13 +584,49 @@ fn test_receive() {
.return_const(Ok(()))
.in_sequence(&mut seq);

let result = mocks.into_controller().receive(&mut message_buff);
let result = mocks.into_controller().receive(&mut message_buff, true);

assert!(result.is_ok());

assert_eq!(message_buff, [1, 2, 3, 4, 5, 6, 7, 8]);
}

#[test]
fn test_receive_fifo_empty() {
let mut mocks = Mocks::default();

let mut seq = Sequence::new();

let mut message_buff = [0u8; 8];

// status register read (fifo not empty flag is not set)
mocks.mock_register_read::<0b0000_0000>([0x30, 0x60], &mut seq);

let result = mocks.into_controller().receive(&mut message_buff, false);

assert_eq!(result.unwrap_err(), Error::RxFifoEmpty);
}

#[test]
fn test_transmit_fifo_full() {
let mut mocks = Mocks::default();
let mut seq = Sequence::new();
let payload: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
let payload_bytes = Bytes::copy_from_slice(&payload);

let msg_type = Can20::<8> {};

let identifier = ExtendedId::new(EXTENDED_ID).unwrap();
let tx_message = TxMessage::new(msg_type, payload_bytes, Id::Extended(identifier)).unwrap();

// mock fifo status register read byte 0 (1st attempt) -> tx fifo full
mocks.mock_register_read::<0b0000_0000>([0x30, 0x6C], &mut seq);

let res = mocks.into_controller().transmit(&tx_message, false);

assert_eq!(res.unwrap_err(), Error::TxFifoFull);
}

#[test]
fn test_reset_command() {
let mut mocks = Mocks::default();
Expand Down

0 comments on commit c1931db

Please sign in to comment.