I am trying to write an embedded-hal-async SPI driver.(Pixy2 camera)
There should be a function approximately:
pub async get_blocks(&mut self) -> Result<&[Block]>
The amount of blocks(length of data) is known only when the packet header is read.
Block is a struct.
pub struct Block {
pub signature: u16,
pub x: u16,
pub y: u16,
pub width: u16,
pub height: u16,
pub angle: i16,
pub index: u8,
pub age: u8,
}
How do I efficienctly(no alloc) and idiomatically implement the reading of the data from SPI and getting this array from the data?
Btw I am referencing this C++ code:
//
// begin license header
//
// This file is part of Pixy CMUcam5 or "Pixy" for short
//
// All Pixy source code is provided under the terms of the
// GNU General Public License v2 (http://www.gnu.org/licenses/gpl-2.0.html).
// Those wishing to use Pixy source code, software and/or
// technologies under different licensing terms should contact us at
// cmucam@cs.cmu.edu. Such licensing terms are available for
// all portions of the Pixy codebase presented here.
//
// end license header
//
// This file is for defining the Block struct and the Pixy template class version 2.
// (TPixy2). TPixy takes a communication link as a template parameter so that
// all communication modes (SPI, I2C and UART) can share the same code.
//
#ifndef _PIXY2CCC_H
This file has been truncated. show original
//
// begin license header
//
// This file is part of Pixy CMUcam5 or "Pixy" for short
//
// All Pixy source code is provided under the terms of the
// GNU General Public License v2 (http://www.gnu.org/licenses/gpl-2.0.html).
// Those wishing to use Pixy source code, software and/or
// technologies under different licensing terms should contact us at
// cmucam@cs.cmu.edu. Such licensing terms are available for
// all portions of the Pixy codebase presented here.
//
// end license header
//
// Main Pixy template class. This class takes a link class and uses
// it to communicate with Pixy over I2C, SPI, UART or USB using the
// Pixy packet protocol.
#ifndef _TPIXY2_H
#define _TPIXY2_H
This file has been truncated. show original
I tried something very broken with bytemuck, zerocopy and hacky and it does not work, better not including that here.
Should I put a buffer into the Pixy2 driver sturct field?
How such things are usually solved?
if you want a no alloc solution, basically there's only two way to provide the backing memory: caller provided and static storage.
btw, it seems the C++ code does allocate the buffer internally? or am I looking at the wrong place?
uint8_t *m_bufPayload;
uint8_t m_type;
uint8_t m_length;
bool m_cs;
};
template <class LinkType> TPixy2<LinkType>::TPixy2() : ccc(this), line(this), video(this)
{
// allocate buffer space for send/receive
m_buf = (uint8_t *)malloc(PIXY_BUFFERSIZE);
// shifted buffer is used for sending, so we have space to write header information
m_bufPayload = m_buf + PIXY_SEND_HEADER_SIZE;
frameWidth = frameHeight = 0;
version = NULL;
}
template <class LinkType> TPixy2<LinkType>::~TPixy2()
{
m_link.close();
free(m_buf);
In addition to nerditation's answer, if it is hard for you to provide a buffer that will go between the driver and the user (though imo cycling two static buffers is the best solution), you can just accept a closure that you'll call when a new block arrives. Then the user can decide what to do with the block themselves, like push into a heapless::Vec or something.
Thank you for the responses!
I decided to go with a buffer in the Pixy2 struct field:
const BUF_SIZE: usize = 0x104;
pub struct Pixy2<SPI> {
spi: SPI,
buf: [u8; BUF_SIZE],
}
And then using zerocopy like:
return Ok(<[Block]>::ref_from_bytes(&self.buf[0..len]).unwrap());
// Returns Result<&[Block]>;
Turns out
[repr(C, packed)]
is very important for Block struct, otherwise it crashes on casting with alignment errors.
(However this makes it is hard to use defmt for this)
It works but maybe not idiomatic. Improvements suggestions are welcome.
My code:
use embedded_hal_async::spi::{Operation, SpiDevice};
use zerocopy::{FromBytes as _, IntoBytes as _};
use zerocopy_derive::{FromBytes, Immutable, IntoBytes, KnownLayout};
use crate::{
Result,
error::Error,
packets::{self, Request, Response},
};
const BUF_SIZE: usize = 0x104;
pub struct Pixy2<SPI> {
spi: SPI,
buf: [u8; BUF_SIZE],
}
impl<SPI> Pixy2<SPI>
where
SPI: SpiDevice,
{
This file has been truncated. show original
@nerditation I am not sure, but the c++ code seems to allocate buffer only on instantiation of the driver type.
I do not want the caller to deal with buffers, I wanted easy to use API.
Btw here is usage example if you are interested
You need buf's start to be aligned to whatever Block's required alignment is. (It is 2 bytes.)
You could do it by putting a [Block; 0] before it and marking the struct with #[repr(C)] simply; it will probably help with defmt.
Any chance that your data comes in pairs of bytes at least, so that you could use u16?