Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNS - CAN IO Trait #72

Merged
merged 30 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
64da3b6
CAN IO code
jpfbastos Jan 23, 2025
bcc58af
rustfmt
jpfbastos Jan 23, 2025
694c62a
added derive to the io file
jpfbastos Jan 23, 2025
d1831f5
added envelope struct, comments and the try_ functions
jpfbastos Jan 30, 2025
eed5c85
clippy is still flagging some stuff, but they seem to be edge cases
jpfbastos Jan 30, 2025
811fdba
see if it builds now
jpfbastos Jan 30, 2025
c2a93fc
matched embassy errors to CanError
jpfbastos Jan 30, 2025
14a49da
lol missed semicolon
jpfbastos Jan 30, 2025
8d55d2b
scrapped async because they're a pain
jpfbastos Jan 30, 2025
7fd9f24
removed blocking from Can
jpfbastos Jan 30, 2025
98f7ee8
Merge branch 'main' into hype-77-can-io-implementation
jpfbastos Jan 30, 2025
9ae813e
Merge branch 'main' into hype-77-can-io-implementation
davidbeechey Jan 30, 2025
102146f
restore lockfiles
davidbeechey Jan 30, 2025
f4754dd
fix big long import chains
davidbeechey Jan 30, 2025
6d520a6
fixed comment in definition of write_frame, de-nested some of the mat…
jpfbastos Feb 1, 2025
32279f1
fixed bug
jpfbastos Feb 1, 2025
bf3f270
ok my bad this should be it
jpfbastos Feb 1, 2025
f53e20d
:)
jpfbastos Feb 1, 2025
2d81bfa
remove hyped-can-derive (surely it can't be this straightforward)
jpfbastos Feb 5, 2025
6f48046
removed from cargo.toml
jpfbastos Feb 5, 2025
60467eb
and from io
jpfbastos Feb 5, 2025
16337b9
remove .lock files
jpfbastos Feb 5, 2025
8ce9ccb
og cargo.lock files for boards
jpfbastos Feb 6, 2025
3fe70e6
and the main one
jpfbastos Feb 6, 2025
f9bdf9c
ok this is it
jpfbastos Feb 6, 2025
c8460cb
actually no now this is it
jpfbastos Feb 6, 2025
f39d76d
ok now yes
jpfbastos Feb 6, 2025
cc26de1
start making `fail_read` and `fail_write` use mutex
davidbeechey Feb 6, 2025
e3649b0
david said sod it
jpfbastos Feb 6, 2025
05792f4
haha we're back
jpfbastos Feb 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion boards/stm32f767zi/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use embassy_stm32::adc::{Adc, AnyAdcChannel, Instance};
use embassy_stm32::gpio::{Input, Output};
use embassy_stm32::{i2c::I2c, mode::Blocking};
use embassy_sync::blocking_mutex::{raw::NoopRawMutex, Mutex};

use hyped_adc::HypedAdc;
use hyped_adc_derive::HypedAdc;
use hyped_gpio::{HypedGpioInputPin, HypedGpioOutputPin};
Expand Down
9 changes: 9 additions & 0 deletions lib/io/hyped_can/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "hyped_can"
version = "0.1.0"
edition = "2021"

[dependencies]
heapless = "0.8.0"
embassy-sync = { version = "0.6.0", features = ["defmt"], git = "https://github.com/embassy-rs/embassy", rev = "1c466b81e6af6b34b1f706318cc0870a459550b7"}
embassy-time = { version = "0.3.1", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"], git = "https://github.com/embassy-rs/embassy", rev = "1c466b81e6af6b34b1f706318cc0870a459550b7"}
149 changes: 149 additions & 0 deletions lib/io/hyped_can/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#![no_std]

/// CAN errors that can occur
/// From: https://docs.embassy.dev/embassy-stm32/git/stm32f767zi/can/enums/enum.BusError.html,
/// https://docs.embassy.dev/embassy-stm32/git/stm32f767zi/can/enum.TryWriteError.html
/// https://docs.embassy.dev/embassy-stm32/git/stm32f767zi/can/enum.TryReadError.html,
/// and https://docs.embassy.dev/embassy-stm32/git/stm32f767zi/can/enums/enum.FrameCreateError.html
#[derive(Debug)]
pub enum CanError {
Stuff,
Form,
Acknowledge,
BitRecessive,
BitDominant,
Crc,
Software,
BusOff,
BusPassive,
BusWarning,
Full,
Empty,
Unknown,
NotEnoughData,
InvalidDataLength,
InvalidCanId,
}

#[derive(Clone)]
pub struct HypedCanFrame {
pub can_id: u32, // 32 bit CAN_ID + EFF/RTR/ERR flags
pub data: [u8; 8], // data that is sent over CAN, split into bytes
}

pub type Timestamp = embassy_time::Instant;

#[derive(Clone)]
pub struct HypedEnvelope {
/// Reception time.
pub ts: Timestamp,
/// The actual CAN frame.
pub frame: HypedCanFrame,
}

/// CAN trait used to abstract the CAN operations
pub trait HypedCan {
/// Attempts to read a CAN frame without blocking.
///
/// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue.
fn read_frame(&mut self) -> Result<HypedEnvelope, CanError>;
/// Attempts to transmit a frame without blocking.
///
/// Returns [Err(CanError::Full)] if the frame can not be queued for transmission now.
///
/// The frame will only be accepted if there is no frame with the same priority already queued. This is done
/// to work around a hardware limitation that could lead to out-of-order delivery of frames with the same priority.
fn write_frame(&mut self, frame: &HypedCanFrame) -> Result<(), CanError>;
}

pub mod mock_can {
use core::cell::RefCell;
use embassy_sync::blocking_mutex::{raw::CriticalSectionRawMutex, Mutex};
use heapless::Deque;

use crate::HypedCanFrame;

/// A fixed-size map of CAN frames
type CanValues = Deque<HypedCanFrame, 8>;

/// A mock CAN instance which can be used for testing
pub struct MockCan<'a> {
/// Values that have been read from the CAN bus
frames_to_read: &'a Mutex<CriticalSectionRawMutex, RefCell<CanValues>>,
/// Values that have been sent over the CAN bus
frames_sent: CanValues,
/// Whether to fail reading frames
fail_read: bool,
/// Whether to fail writing frames
fail_write: bool,
}

impl crate::HypedCan for MockCan<'_> {
fn read_frame(&mut self) -> Result<super::HypedEnvelope, super::CanError> {
if self.fail_read {
return Err(super::CanError::Unknown);
}
self.frames_to_read.lock(|frames_to_read| {
match frames_to_read.borrow_mut().pop_front() {
Some(frame) => Ok(super::HypedEnvelope {
ts: embassy_time::Instant::now(),
frame,
}),
None => Err(super::CanError::Empty),
}
})
}

fn write_frame(&mut self, frame: &super::HypedCanFrame) -> Result<(), super::CanError> {
if self.fail_write {
return Err(super::CanError::Unknown);
}
match self.frames_sent.push_front(frame.clone()) {
Ok(_) => Ok(()),
Err(_) => Err(super::CanError::Unknown),
}
}
}

impl MockCan<'_> {
pub fn new(
frames_to_read: &'static Mutex<CriticalSectionRawMutex, RefCell<CanValues>>,
) -> Self {
MockCan::new_with_failures(frames_to_read, false, false)
}

pub fn new_with_failures(
frames_to_read: &'static Mutex<CriticalSectionRawMutex, RefCell<CanValues>>,
fail_read: bool,
fail_write: bool,
) -> Self {
MockCan {
frames_to_read,
frames_sent: CanValues::new(),
fail_read,
fail_write,
}
}

/// Get the values that have been sent over the CAN bus
pub fn get_can_frames(&self) -> &CanValues {
&self.frames_sent
}

pub fn set_read_to_fail(&mut self) {
self.fail_read = true;
}

pub fn set_write_to_fail(&mut self) {
self.fail_write = true;
}

pub fn set_read_to_pass(&mut self) {
self.fail_read = false;
}

pub fn set_write_to_pass(&mut self) {
self.fail_write = false;
}
}
}
2 changes: 1 addition & 1 deletion lib/io/hyped_i2c/hyped_i2c_derive/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 42 additions & 47 deletions lib/io/hyped_i2c/hyped_i2c_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ fn impl_hyped_i2c(ast: &syn::DeriveInput) -> TokenStream {
let (impl_generics, ty_generics, _) = generics.split_for_impl();
let gen = quote! {
impl #impl_generics HypedI2c for #name #ty_generics{
/// Read a byte from a register on a device

fn read_byte(&mut self, device_address: u8, register_address: u8) -> Option<u8> {
let mut read = [0];
let result = self.i2c.lock(|i2c| {
Expand All @@ -32,26 +32,24 @@ fn impl_hyped_i2c(ast: &syn::DeriveInput) -> TokenStream {
}
}

/// Read a byte from a register with a 16-bit address on a device
fn read_byte_16(&mut self, device_address: u8, register_address: u16) -> Option<u8> {
let register_addr_hi = (register_address >> 8) as u8 & 0xFF;
let register_addr_lo = register_address as u8 & 0xFF;
let mut read = [0];
let result = self.i2c.lock(|i2c| {
i2c.borrow_mut().blocking_write_read(
device_address,
[register_addr_hi, register_addr_lo].as_ref(),
&mut read,
)
});
match result {
Ok(_) => Some(read[0]),
Err(_) => None,
}
}
fn read_byte_16(&mut self, device_address: u8, register_address: u16) -> Option<u8> {
let register_addr_hi = (register_address >> 8) as u8 & 0xFF;
let register_addr_lo = register_address as u8 & 0xFF;
let mut read = [0];
let result = self.i2c.lock(|i2c| {
i2c.borrow_mut().blocking_write_read(
device_address,
[register_addr_hi, register_addr_lo].as_ref(),
&mut read,
)
});
match result {
Ok(_) => Some(read[0]),
Err(_) => None,
}
}


/// Write a byte to a register on a device
fn write_byte_to_register(
&mut self,
device_address: u8,
Expand All @@ -76,7 +74,6 @@ fn impl_hyped_i2c(ast: &syn::DeriveInput) -> TokenStream {
}
}

/// Write a byte to a device
fn write_byte(&mut self, device_address: u8, data: u8) -> Result<(), I2cError> {
let result = self.i2c.lock(|i2c| {
i2c.borrow_mut().blocking_write(device_address, [data].as_ref())
Expand All @@ -95,33 +92,31 @@ fn impl_hyped_i2c(ast: &syn::DeriveInput) -> TokenStream {
}
}

// Write a byte to a register with a 16-bit address on a device
fn write_byte_to_register_16(
&mut self,
device_address: u8,
register_address: u16,
data: u8,
) -> Result<(), I2cError> {
let register_addr_hi = (register_address >> 8) as u8;
let register_addr_lo = register_address as u8;
let result = self.i2c.lock(|i2c| {
i2c.borrow_mut()
.blocking_write(device_address, [register_addr_hi, register_addr_lo, data].as_ref())
});
match result {
Ok(_) => Ok(()),
Err(e) => Err(match e {
embassy_stm32::i2c::Error::Bus => I2cError::Bus,
embassy_stm32::i2c::Error::Arbitration => I2cError::Arbitration,
embassy_stm32::i2c::Error::Nack => I2cError::Nack,
embassy_stm32::i2c::Error::Timeout => I2cError::Timeout,
embassy_stm32::i2c::Error::Crc => I2cError::Crc,
embassy_stm32::i2c::Error::Overrun => I2cError::Overrun,
embassy_stm32::i2c::Error::ZeroLengthTransfer => I2cError::ZeroLengthTransfer,
}),
}
}

fn write_byte_to_register_16(
&mut self,
device_address: u8,
register_address: u16,
data: u8,
) -> Result<(), I2cError> {
let register_addr_hi = (register_address >> 8) as u8;
let register_addr_lo = register_address as u8;
let result = self.i2c.lock(|i2c| {
i2c.borrow_mut()
.blocking_write(device_address, [register_addr_hi, register_addr_lo, data].as_ref())
});
match result {
Ok(_) => Ok(()),
Err(e) => Err(match e {
embassy_stm32::i2c::Error::Bus => I2cError::Bus,
embassy_stm32::i2c::Error::Arbitration => I2cError::Arbitration,
embassy_stm32::i2c::Error::Nack => I2cError::Nack,
embassy_stm32::i2c::Error::Timeout => I2cError::Timeout,
embassy_stm32::i2c::Error::Crc => I2cError::Crc,
embassy_stm32::i2c::Error::Overrun => I2cError::Overrun,
embassy_stm32::i2c::Error::ZeroLengthTransfer => I2cError::ZeroLengthTransfer,
}),
}
}
}

impl #impl_generics #name #ty_generics {
Expand Down
5 changes: 5 additions & 0 deletions lib/io/hyped_i2c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,25 @@ pub enum I2cError {

/// I2C trait used to abstract the I2C peripheral
pub trait HypedI2c {
/// Read a byte from a register on a device
fn read_byte(&mut self, device_address: u8, register_address: u8) -> Option<u8>;
/// Read a byte from a 16-bit register on a device
fn read_byte_16(&mut self, device_address: u8, register_address: u16) -> Option<u8>;
/// Write a byte to a register on a device
fn write_byte_to_register(
&mut self,
device_address: u8,
register_address: u8,
data: u8,
) -> Result<(), I2cError>;
// Write a byte to a 16-bit register on a device
fn write_byte_to_register_16(
&mut self,
device_address: u8,
register_address: u16,
data: u8,
) -> Result<(), I2cError>;
/// Write a byte to a device
fn write_byte(&mut self, device_address: u8, data: u8) -> Result<(), I2cError>;
}

Expand Down