use core::marker::PhantomData;
use crate::{ReadableRegister, WritableRegister};
use arrayvec::ArrayVec;
use bytemuck::Zeroable;
use crc::Algorithm;
#[derive(Default)]
pub struct Crc8Codec<const HEADER_SIZE: usize, const CHUNK_SIZE: usize, C: Crc8Algorithm> {
_algo: PhantomData<C>,
}
pub trait Crc8Algorithm: Default {
fn instance() -> &'static Algorithm<u8>;
}
#[maybe_async_cfg::maybe(
idents(hal(sync = "embedded_hal", async = "embedded_hal_async"), Codec, I2cBoundBus),
sync(feature = "sync"),
async(feature = "async"),
keep_self
)]
impl<const HEADER_SIZE: usize, const CHUNK_SIZE: usize, C: Crc8Algorithm + 'static> crate::i2c::Codec
for Crc8Codec<HEADER_SIZE, CHUNK_SIZE, C>
{
#[inline]
async fn read_register<R, I, A>(bound_bus: &mut crate::i2c::I2cBoundBus<I, A>) -> Result<R, I::Error>
where
R: ReadableRegister,
I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
A: hal::i2c::AddressMode + Copy,
{
let crc = crc::Crc::<u8>::new(C::instance());
let header = &R::ADDRESS.to_be_bytes()[core::mem::size_of_val(&R::ADDRESS) - HEADER_SIZE..];
let mut array = ArrayVec::<_, 64>::new();
unsafe {
array.set_len(R::REGISTER_SIZE + R::REGISTER_SIZE / CHUNK_SIZE);
}
bound_bus
.interface
.write_read(bound_bus.address, header, &mut array)
.await?;
let mut register = R::zeroed();
let data = bytemuck::bytes_of_mut(&mut register);
for (i, x) in array.chunks(CHUNK_SIZE + 1).enumerate() {
let value = &x[0..CHUNK_SIZE];
data[i..i + CHUNK_SIZE].copy_from_slice(value);
let crc_val = crc.checksum(value);
let crc_real = x[CHUNK_SIZE];
if crc_real != crc_val {
panic!("crc failed")
}
}
Ok(register)
}
#[inline]
async fn write_register<R, I, A>(
bound_bus: &mut crate::i2c::I2cBoundBus<I, A>,
register: impl AsRef<R>,
) -> Result<(), I::Error>
where
R: WritableRegister,
I: hal::i2c::I2c<A> + hal::i2c::ErrorType,
A: hal::i2c::AddressMode + Copy,
{
#[repr(C, packed(1))]
#[derive(Copy, Clone, bytemuck::Pod, Zeroable)]
struct Buffer<const HEADER_SIZE: usize, R> {
header: [u8; HEADER_SIZE],
register: R,
}
let crc = crc::Crc::<u8>::new(C::instance());
let mut buffer = Buffer::<{ HEADER_SIZE }, R> {
header: R::ADDRESS.to_be_bytes()[core::mem::size_of_val(&R::ADDRESS) - HEADER_SIZE..]
.try_into()
.expect("Unexpected compile-time header address size. This is a bug in the chosen Codec or embedded-registers."),
register: *register.as_ref(),
};
let data = bytemuck::bytes_of_mut(&mut buffer);
let mut array = ArrayVec::<_, 64>::new();
for x in data.chunks(CHUNK_SIZE) {
array.try_extend_from_slice(x).unwrap();
let crc_val = crc.checksum(x);
array.push(crc_val);
}
bound_bus.interface.write(bound_bus.address, &array).await
}
}