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

implement FullDuplex for Spi #122

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

implement FullDuplex for Spi #122

wants to merge 1 commit into from

Conversation

dkm
Copy link

@dkm dkm commented Nov 3, 2020

This is a very simple impl for FullDuplex. The Transfer impl could be removed in favor of the default that is available by the availability of FullDuplex.

@dkm dkm closed this Nov 3, 2020
@dkm
Copy link
Author

dkm commented Nov 3, 2020

Looks like it's not working, will push a correct version later :)

@dkm
Copy link
Author

dkm commented Nov 3, 2020

Not 100% sure it's the best way to implement, but as far as I could test, it works (tested with ws2812-spi and a 10 leds chain).
If you think some refactoring can be done, do not hesitate, happy to learn and implement :)

src/spi.rs Outdated
fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
self.check_send()?;
self.send_u8(byte);
self.check_send().ok();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you discarding the result of the check_send the second time?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've simply reused the code from the write that does the same thing. You are right that it should raise any error instead of discarding it. Maybe this is also true in write (same file) ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, we should check the datasheet to see if the status register needs to be read after writing the data to the data register.
I believe that the propose of this construct is to check the status of errors such as Overrun, etc. so I propose that we should implement method check_errors that only checks for the errors and then propagate its result using the ? operator.

What do you think, @therealprof ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a good idea. If you think it's worth implementing, I can do it :) If you have other idea (eg. refactoring), I would be happy to help.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good plan, we might want to be careful about which errors we propagate and which ones we drop but we can look at that later when we have the check_errors function I guess.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll draft something and add it here :)

@dkm dkm force-pushed the master branch 8 times, most recently from d9a3f0b to f890bcc Compare November 15, 2020 13:18
@dkm
Copy link
Author

dkm commented Nov 15, 2020

I've tried to implement the check_errors. I'm not 100% sure my usage of nb::Result / Result is the best (or at least the more idiomatic). I've made it return a Result as it's not dealing with blocking anymore, but maybe that was not a good idea. Let me know if you think I should do it differently !

Comment on lines +540 to +539
match self.check_errors() {
Ok(_) => Ok(()),
Err(e) => Err(nb::Error::Other(e)),
}
Copy link
Member

@therealprof therealprof Nov 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couldn't this be written more obviously as:

Suggested change
match self.check_errors() {
Ok(_) => Ok(()),
Err(e) => Err(nb::Error::Other(e)),
}
self.check_errors().map_err(|e| nb::Error::Other(e))

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of that, but it seems you can only use it to change the error, not the Result type wrapping everything (Result vs nb::Result here). I get this:

rror[E0308]: mismatched types
   --> src/spi.rs:482:9
    |
475 |     fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
    |                                     --------------------- expected `core::result::Result<(), nb::Error<spi::Error>>` because of return type
...
482 |         self.check_errors().map_err(|e| Err(nb::Error::Other(e)))
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `nb::Error`, found enum `core::result::Result`

But maybe I'm not doing something correctly ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my bad. Cut'n'pasto. Of course it needs to be (corrected above, too):

self.check_errors().map_err(|e| nb::Error::Other(e))

map_err() automatically wraps the value returned from the closure in an Err, cf. here: https://doc.rust-lang.org/src/core/result.rs.html#592

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that the map_err isn't needed at all. Check for example the check_send method, where check_errors is called directly with the ? operator. There might be an automatic Into trait implementation for it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works with ? yes (and I was surprised), but not if using the statement for returning (ie. without ?). I'll fix the map_err except if you have something better in mind ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that the map_err isn't needed at all. Check for example the check_send method, where check_errors is called directly with the ? operator. There might be an automatic Into trait implementation for it.

Depends on the signature. If we're replying the right Result type then we can just use it directly. Indeed we can also use ? if we have proper conversion implementations available, however IIRC using ? as the last operation in a function is frowned upon and linted by clippy. ;)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, sorry, I missed that it was the last command in function. LGTM then with map_err.

@dkm
Copy link
Author

dkm commented Nov 16, 2020

Thanks for the help !
Also, I've left the Transfer impl as relying on the FullDuplex would cause set_bidi to be called for every byte in a mult-byte transfer... Not sure if this overhead is a problem or not, so I left the custom Transfer impl as-is.

@therealprof
Copy link
Member

Has anyone had a chance to test those changes in real life yet?

@dkm
Copy link
Author

dkm commented Nov 19, 2020

I guess I could hookup a nRF24L01, capture some exchanges with analyzer to provide some better test if nobody can test this with an easier setup ? I don't have anything else (except for the ws2812 I'm using that motivated this change)

@dkm
Copy link
Author

dkm commented Dec 9, 2020

Sorry, I forgot to think this through. I don't have anything to test besides this ws2812 setup as all my F0 are soldered on my keyboards, so there's no way I can monitor sck and miso pins...

@matoushybl
Copy link
Contributor

If you design a test scenario, I have a lot of hardware to test on.

@dkm
Copy link
Author

dkm commented Jan 13, 2021

Sorry, I forgot to think this through. I don't have anything to test besides this ws2812 setup as all my F0 are soldered on my keyboards, so there's no way I can monitor sck and miso pins...

If you design a test scenario, I have a lot of hardware to test on.

back on this... I was hoping I could find some f0, but found none :/.
I can try to write a small program so you only need to wire something, compile and load ? Do you have some nrf24lo1 ? I could execute the program on another mcu to check it works and let you see if it's works similarly on f0?

@matoushybl
Copy link
Contributor

Sure, no problem. I believe that I have the NRF so there should be no problems.

I believe that the peripheral on F4 is similar, so that you can debug on it and then send it to me to try out on the F0.

@dkm
Copy link
Author

dkm commented Jan 14, 2021

even better, I have some brand new f4 here!

check_errors only checks for Overrun, CRC error and ModeFault flags.

Signed-off-by: Marc Poulhiès <[email protected]>
@easybe
Copy link

easybe commented Oct 23, 2022

Hi. What is the status on this? Can I help in any way?

@therealprof
Copy link
Member

@easybe Seems dormant to me. If you can test it, we can certainly get it merged.

@easybe
Copy link

easybe commented Oct 23, 2022

OK, I should be able to do that.

@dkm
Copy link
Author

dkm commented Oct 23, 2022 via email

@dkm dkm force-pushed the master branch 2 times, most recently from db4b2ce to 68b434a Compare October 16, 2024 16:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants