-
Notifications
You must be signed in to change notification settings - Fork 234
spi: make SpiDevice transaction take an operation slice instead of a closure. #443
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
Conversation
415f64c
to
5cf01db
Compare
5cf01db
to
08f7e13
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
471b487
to
b8b5489
Compare
hey seems reasonable to me! a couple of questions:
|
445: bus/i2c: add RefCell, CriticalSection and Mutex shared bus implementations. r=eldruin a=Dirbaio Requires #440 Same as #443 but for I2C. This adds a few bus sharing implementations, with varying tradeoffs: - `RefCellDevice`: single thread only - `CriticalSectionDevice`: thread-safe, coarse locking, nostd. - `MutexDevice`: thread-safe, fine-grained locking, std only. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
implementations with hardware CS would directly implement |
b8b5489
to
d3836ad
Compare
@ryankurte @eldruin @therealprof could you please take a look? I'd really like to get this unblocked :) |
d3836ad
to
2217a92
Compare
2217a92
to
b6764ec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Vollbrecht made an interesting comment about read_transaction
but it seems only a mild HAL-impl-side inconvenience:
fn read_transaction(&mut self, operations: &mut [&mut [Word]]) -> Result<(), Self::Error>;
Its a lot of duplication here from transaction. The problem is the transaction takes an &mut [Operation<'_, u8>] but this read_transaction is &mut [&mut [u8]]. We would need something that converts the &mut [u8] in an Operation::Read
It seems to me like this should be fine to implement on linux-embedded-hal
but we should check on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aight thanks for all your work / bearing with me / lgtm!
note that one consequence of the move from closures is that it is no longer possible to represent devices that need to do things within as CS assertion using SpiDevice
, where this behaviour is required drivers should instead bind SpiBus
with an OutputPin
(ie. implement software/userspace CS control themselves)
It seems to me like this should be fine to implement on linux-embedded-hal but we should check on it.
worst case we can duplicate the spidev call which has no problems with a list of arrays (in fact, i believe we already have to throw around some vecs to map from operations to the actual ioctl)
Yes, it's somewhat annoying but I think it's better than taking a The duplication also shows up in #444 . It won't affect most HALs though, as they'll likely implement only
Yeah I think so too. It has to convert from either |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this in this week's meeting.
As @ryankurte also mentioned, there are some things that are not possible anymore.
@GrantM11235 Came up with two interesting examples of these:
// with the slices approach we would need to create copies of the data
fn send_data(&mut self, data: &[u16]) {
self.spi.transaction( |bus|
for chunk in data.chunks(32) {
let mut buffer = [0_u16; 32];
let mut buffer = &mut buffer[0..chunk.len()];
buffer.copy_from_slice(chunk);
for word in buffer {
*word = word.to_be();
}
let buffer = transmute_u16_to_u8(buffer);
bus.send(buffer);
}
)
}
// this version is more efficient than what would be possible with slices
fn send_packet(&mut self, packet: &[u8]) {
let mut control_flow = Continue;
while control_flow == Continue {
self.spi.transaction( |bus| {
let mut first_byte = [COMMAND_SEND];
bus.transfer_inplace(&mut first_byte);
let [status_byte] = first_byte;
if check_can_send(status_byte) {
bus.write(packet);
control_flow = Break;
}
});
// maybe add a delay here
}
}
The MAX3421E driver might benefit from the closure-based approach.
On balance, it is clear that the slice-based approach is simpler to understand, to implement and to use while probably covering > 90% of the use cases as well as resolving the problem with the linux implementation.
As such, let's take the slices-based approach for 1.0 and let's discuss re-adding a closure-based approach in a future release.
Thank you everybody for your work, insights and the fruitful discussions.
bors r+
444: bus/spi: add RefCell, CriticalSection and Mutex shared bus implementations. r=eldruin a=Dirbaio Requires #443 This adds a few bus sharing implementations, with varying tradeoffs: - `RefCellDevice`: single thread only - `CriticalSectionDevice`: thread-safe, coarse locking, nostd. - `MutexDevice`: thread-safe, fine-grained locking, std only. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
1306: Update embedded-hal r=Dirbaio a=Dirbaio - [x] Wait for merge rust-embedded/embedded-hal#443 - [x] Wait for release - [x] embassy-embedded-hal - [x] embassy-nrf - [x] embassy-stm32 - [x] embassy-rp Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
1306: Update embedded-hal r=Dirbaio a=Dirbaio - [x] Wait for merge rust-embedded/embedded-hal#443 - [x] Wait for release - [x] embassy-embedded-hal - [x] embassy-nrf - [x] embassy-stm32 - [x] embassy-rp Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
461: spi: remove write-only and read-only traits. r=Dirbaio a=Dirbaio When introducing the Bus/Device split in #351, I kept the ability to represent "read-only" and "write-only" buses, with separate `SpiBusRead`, `SpiBusWrite` buses. This didn't have much discussion, as it was the logical consequence of keeping the separation in the already existing traits (`Read`, `Write`, `Transfer`, ...). Later, in #443, when switching from the closure-based API to the operation-slice-based API, this required adding `SpiDeviceRead`, `SpiDeviceWrite` as well, because you can no longer put a bound on the underlying bus with the new API. This means we now have *seven* traits, which we can reduce to *two* if we drop the distinction. So, is the distinction worth it? I've always been on the fence about it, now I'm sort of leaning towards no. First, using write-only or read-only SPI is rare. - write-only SPI: for SPI displays you don't want to read from, or to abuse it to bitbang stuff like ws2812b, - read-only SPI: some ADCs that can't be configured at all, you just clock out bits. Or even weirder abuses, like to build a logic analyzer Second, for it to be useful HALs have to track "read-onlyness / write-onlyness" in the type signature, so a read-only SPI really only implements `SpiBusRead` and not `SpiBus`. HALs already have enough generics. For example, Embassy HALs don't implement the distinction (you can create MOSI-only or MISO-only SPIs, but they all impl the full `SpiBus`, because I didn't want to make the types carry pin information). Third, it makes the API easier to use. Simpler, users only have to import one trait, docs have all the methods in one place. Much less boilerplate to impl the traits (look at how shorter `embedded-hal-bus` gets!). So I think we should remove them. Co-authored-by: Dario Nieuwenhuis <dirbaio@dirbaio.net>
As discussed in today's WG meeting.