-
Couldn't load subscription status.
- Fork 155
changed mcp2515 driver to use nonblocking spi #817
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
changed mcp2515 driver to use nonblocking spi #817
Conversation
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.
Nice!
Thanks for taking care of this!
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| } | ||
|
|
||
| template <typename SPI, typename CS, typename INT> | ||
| modm::ResumableResult<bool> |
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.
Shouldn't update() return modm::ResumableResult<void>?
Because nobody (in the main loop) will handle the return value...
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.
it's just the return value of sendMessage indicating if a message was sent this cycle ... not really useful but i thought why not :D ... iwill remove 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.
it is void now
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.
it is void now
No it's not.
dc57d6f to
03639b4
Compare
|
Another addition: This driver thould use the Off-topic discussion: In the case mentioned above the SPI clock has to be limited to 1MHz or 2MHz (ADIS16470 limitation), but for the MCP2515 a higher SPI clock would be desirable. This probably affects many (SPI) drivers, what would be the most elegant way to solve this in modm? |
That requires to reconfigure SPI timings when the |
|
@rleh I have quickly hacked together a sub-optimal implementation for switching the SPI baudrate with multiple devices. This is just a rough proof of concept for STM32 only and makes changes to the https://github.com/chris-durand/modm/tree/wip_spi_user_conf_handler |
I don't know what that is bit I'll look into it tomorrow (after our work meeting stuff) |
The configuration handler is actually quite simple. If you have multiple devices on one bus that require different SPI modes you'll need to reconfigure the mode whenever you run a transfer on the next device. You can define a callback that configures the required parameters and modm will call it automatically when needed. Here is an example. EDIT: if you mean the |
Yeah the second one .. you explained what it does quite well, thanks :) |
@rleh @chris-durand |
|
SpiDevice test seems to be broken? Or did I do something wrong? |
That is because of my experimental commit that was cherry-picked. It is only a proof of concept for STM32. Remove it and everything will be fine :) |
|
I think it would actually make sense to force all SPI drivers to always set all "relevant" SPI options, everything else doesn't work consistently and leads to strange behavior, especially when multiple SPI drivers access SPI in different orders one after the other. The "relevant" SPI options:
To enforce this I would suggest to implement these three values (with usable default values) in addition to the configurationHandler in the The configurationHandler is optional and should only be used to configure additional IOs/multiplexers/... . What do you think @chris-durand? |
Well I dont see why the SpiDevice is currently not a default base class with mandatory (pure virtual) overrides
|
I agree that it makes sense to make it mandatory for the SPI modes. I am not sure how it would work for the frequency. The frequency is a compile time template parameter in the SPI master because we want to verify it at compile-time. We would need a second API that allows runtime values without compile-time checking to make it configurable. Furthermore the clock configuration could require some other platform specific settings on future platforms. I would prefer to decouple it from the device drivers. I would rather let the user provide a call back for the speed configuration that is called when the SPI device is acquired. |
I think there is no need for runtime polymorphism and dynamic dispatch for that use case. Couldn't we just pass a configuration as a non-type template parameter to the It's not a real is-a relationship for let's say an MCP2515 and a generic SPI device. I would argue a MCP2515 is not a specific version of an SPI interface to be used as such. SPI is an implementation detail, in its public interface it's a CAN interface. |
Looking at the STM32 SPI driver it is actually the opposite. I would change the |
static polymorph is a thing :/ |
839b40a to
f94d31a
Compare
Yes, but it is normally not done using Which virtual functions are you thinking of when you said |
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.
Thanks again for refactoring this driver!
src/modm/driver/can/mcp2515_impl.hpp
Outdated
|
|
||
| using namespace mcp2515; | ||
|
|
||
| while(!this->acquireMaster()){}; |
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.
This will block forever if the SPI is acquired by another driver. The code that is releasing the SPI won't run with the cooperative resumable function execution model when you busy-wait in this loop. This function needs to be resumable for it to work. Then you can do RF_WAIT_UNTIL(this->acquireMaster());.
When we have gotten rid of resumable functions in favor of fibers everything will be better I assume...
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.
it's in the part i left blocking (initialization) ... the whole init could be changed to be non blocking, but that's probably not important
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.
Maybe create a initializeWithPrescalerBlocking() function that calls RF_CALL_BLOCKING(initializeWithPrescaler(...))?
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| void | ||
| modm::Mcp2515<SPI, CS, INT>::writeRegister(uint8_t address, uint8_t data) | ||
| { | ||
| while(!this->acquireMaster()){}; |
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.
Same as above. The program will get stuck here if the SpiMaster has been acquired already.
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.
these functions are called from initialize(), which should probably be blocking (I kept it blocking)
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.
Imagine the following situation: You use the same SpiMaster with another driver and then initialize the Mcp2515 while another transaction is running from the other device. Then the initialize() will deadlock forever. Even if that situation is not that common, it will cause nasty bugs.
If I am not mistaken every other initialization function that writes SPI / I²C in modm drivers is resumable. If something should be forced to execute blocking there is always RF_CALL_BLOCKING().
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.
Yeah I am aware of that limitation .. I am still playing around with other stuff so I will try to get the init stuff properly set up later :) just have to do other things for a while :D
either pure virtual or a crtp adapter, where release and acquire master is done in every spi command automatically. you mutex bus access (which is a wise thing to do) . The fact that a driver can decide to NOT do that is really weird to me :D |
Ah, now I understand what you would like to do. Enforcing that the lock on the SPI is held while it is used sounds like a good idea. |
When I added the config handler, it was intended to be set by the application and NOT the driver, to avoid exactly this issue. The developer knows what other devices are on the same bus and thus knows what settings to change. If you only have one device on the bus, you don't need the config handler at all, it is really supposed to be only a user config handler, not a driver config handler. |
|
Yeah, I'm fine with that too. It's more thought out with the config struct. |
|
this whole discussion is now geared towwards changing the whole device / driver api ... as I see it that is a necessary step because (as I said earlier) the api seems to be a little messy. But does that mean this has to be done in this specific pr? my mcp driver in general:
does this matter now anymore? |
This does not have to happen in this PR, in my view it is even nicer to do it in a separate PR. |
|
I guess @kikass13 might be asking if we should change the API of the MCP2515 driver, not the |
|
@chris-durand currently i like the fact that this thing is static. I dislike the "mix" but i can live with it ... I have some software running this thing currently (successfully), and I really depend on the fact, that the can interfaces can be used statically.
At least the current implementation works for me
|
Can't you in the class where you passed the static class as a template argument instantiate an object of the template argument? Then it works with both static and non-static classes. |
sure, i was just trying to explain that the old "design" could still be valid. @rleh you blocked my initial changes because I changed the API, so changing it back again now seems kind of redundant :D |
| #ifndef MODM_MCP2515_HPP | ||
| #error "Don't include this file directly, use 'mcp2515.hpp' instead!" | ||
| #endif | ||
|
|
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.
Unnecessary whitespace change.
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.
Not resolved.
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| if(not modm_assert_continue_ignore(rxQueue.push(messageBuffer_), "mcp2515.can.tx", | ||
| "CAN transmit software buffer overflowed!", 1)){ | ||
| /// ignore | ||
| } |
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.
Remove the if statement if the body is empty.
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.
well it was there before ... it shows intent .. so i kept it for now
|
@rleh i have changed all the points you made (hopefully to your satisfaction) :) |
814c603 to
5aa143e
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.
- Inside
initializeWithPrescaler()SPI access is performed without locking the bus (usingSpiDevice::acquireMaster()/::releaseMaster()) writeRegister(),readRegister()andbitModify()also perform SPI access without usingSpiDevice::acquireMaster()/::releaseMaster()and are not resumable
You have to switch from the update() resumable function to a protothread, and inside the protothread you can then do the initialization stuff, triggered by a flag set from the initialize() function.
| <option name="modm:driver:mcp2515:buffer.tx">32</option> | ||
| <option name="modm:driver:mcp2515:buffer.rx">32</option> |
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.
32 is already the default value. Remove?
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.
not a good idea, this shows intent .. nobody would know that this exists otherwise
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.
Everybody that reads the documentation or uses lbuild discover-options will know about it...
| namespace mcp2515{ | ||
| namespace options{ | ||
|
|
||
| %% if options["buffer.tx"] > 0 |
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.
| %% if options["buffer.tx"] > 0 |
buffer.tx is enforced to be within range 1..(2^16-2) by the lbuild module.
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.
(Same for rx buffer)
| #ifndef MODM_MCP2515_HPP | ||
| #error "Don't include this file directly, use 'mcp2515.hpp' instead!" | ||
| #endif | ||
|
|
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.
Not resolved.
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| } | ||
|
|
||
| template <typename SPI, typename CS, typename INT> | ||
| modm::ResumableResult<bool> |
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.
it is void now
No it's not.
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| template <typename SPI, typename CS, typename INT> | ||
| modm::ResumableResult<bool> | ||
| modm::Mcp2515<SPI, CS, INT>::update(){ | ||
| using namespace mcp2515; | ||
|
|
||
| RF_BEGIN(); |
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.
Shouldn't that rather be a Protothread and not a resumable function?
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.
dunno what you mean. As the api is right now (static), there exists a protothread in the user code (which you told me in a prior version of this driver update, that it should be consistent to the general driver api) ... which one is 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.
Hint:
| template <typename SPI, typename CS, typename INT> | |
| modm::ResumableResult<bool> | |
| modm::Mcp2515<SPI, CS, INT>::update(){ | |
| using namespace mcp2515; | |
| RF_BEGIN(); | |
| template <typename SPI, typename CS, typename INT> | |
| void | |
| modm::Mcp2515<SPI, CS, INT>::run() | |
| { | |
| PT_BEGIN(); | |
| //... |
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.
then the user hjas two protothreads runnign inside each other?
why should the thread be in the driver instead of the user deciding when or in which context update is called?
Hint:
- not making a point clear / answering the implicity question of
what do you actually want to do and whydoes not help me :D
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| if ((readStatus(READ_STATUS) & (TXB2CNTRL_TXREQ | TXB1CNTRL_TXREQ | TXB0CNTRL_TXREQ)) == | ||
| RF_BEGIN(); | ||
|
|
||
| tempS = true; |
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.
Meaningful variable name or explanation please...
src/modm/driver/can/mcp2515_impl.hpp
Outdated
| ready = false; | ||
| } | ||
| return ready; |
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.
| ready = false; | |
| } | |
| return ready; | |
| return false; | |
| } | |
| return true; |
| modm::Mcp2515<SPI, CS, INT>::writeRegister(uint8_t address, uint8_t data) | ||
| { | ||
| chipSelect.reset(); | ||
|
|
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.
Still a lot of unnecessary whitespace change in this part of the file.
|
@kikass13 Please stop marking my change requests which are not resolved as resolved. |
0ff3eef to
7959cdc
Compare
7959cdc to
3b3ee65
Compare
3b3ee65 to
3db3bcd
Compare
attachConfigurationHandlerfor switching device spi typesor discovery boardattachConfigurationHandlerfor switching device clock speeds