cr1901 has quit [Remote host closed the connection]
mrkajetanp has quit [Ping timeout: 240 seconds]
<re_irc>
<eldruin> PSA: Here is the roadmap for the traits that have been removed (https://github.com/rust-embedded/embedded-hal/issues/357) from "embedded-hal" ahead of the 1.0.0 release. This includes the input capture, PWM, QEI, timer and watchdog traits. Please participate in the issues for each trait/module if you are interested in having them back.
<re_irc>
pub enum DapState<
<re_irc>
<korken89> Trait masters, does anyone know how to solve this:
<re_irc>
<korken89> It seems to want a "PhantomData"
<re_irc>
<chrysn (@chrysn:matrix.org)> eldruin: I don't quite see why the unbound types are so much of an issue. Wouldn't the users just declare their requirements on the peripherals' types?
<re_irc>
<chrysn (@chrysn:matrix.org)> e.g., on anything time related, they'd require that a timer has a TryFromcore::time::Duration, thus limiting the timers to those that actually implement the conversion, and most will, and those that can't (or fail) become visible as build-time (or run-time in case the TryFrom errs; future maybe-const traits might improve that) erros.
<re_irc>
For example, if somebody creates a device driver that receives a `CountDown` struct, it needs to specify what its `Time` type should be. If they choose a type coming from `fugit`, somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides `CountDown` with `Time` types defined in `embedded-time`. It is also not possible to implement `CountDown` for `Time` types defined by `fugit` in a...
<re_irc>
<eldruin> I do not have the time to discuss this right now but I added an example to the roadmap issue:
<re_irc>
I would suggest that you comment on the relevant issues and/or tomorrow in our weekly meeting. Your participation would certainly be valuable.
<re_irc>
... straight-forward way due to the orphan rule.
<re_irc>
<eldruin> * > For example, if somebody creates a device driver that receives a "CountDown" struct, it needs to specify what its "Time" type should be. If they choose a type coming from "fugit", somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides "CountDown" with "Time" types defined in "embedded-time". It is also not possible to implement "CountDown" for "Time" types defined by "fu
<re_irc>
<chrysn (@chrysn:matrix.org)> Thanks, I'll come back to that there or on the meeting.
<re_irc>
<eldruin> I do not have the time to discuss this right now but I added an example to the roadmap issue:
<re_irc>
> For example, if somebody creates a device driver that receives a "CountDown" struct, it needs to specify what its "Time" type should be. If they choose a type coming from "fugit", somebody else cannot use this driver if the HAL implementation for the MCU they are using only provides "CountDown" with "Time" types defined in "embedded-time". It is also not possible for the user to implement "CountDown" for "Time" types defined by...
<re_irc>
... "fugit" in a straight-forward way due to the orphan rule.
<re_irc>
> In summary, it is not possible for anybody to start a countdown for a certain duration in a generic way, without it being tied to a particular time implementation and thus forcing everybody to use that one.
<re_irc>
I would suggest that you comment on the relevant issues and/or tomorrow in our weekly meeting. Your participation would certainly be valuable.
<re_irc>
<dngrs (spookyvision@github)> > It is also not possible for the user to implement CountDown for Time types defined by fugit in a straight-forward way due to the orphan rule.
<re_irc>
Indeed I've been inconvenienced by that in the past. Hoping a better solution will emerge!
ni has quit [Quit: WeeChat 3.0]
ni has joined #rust-embedded
gsalazar has joined #rust-embedded
cr1901_ has joined #rust-embedded
cr1901 has quit [Ping timeout: 240 seconds]
dreamcat4 has joined #rust-embedded
gsalazar has quit [Remote host closed the connection]
gsalazar has joined #rust-embedded
<re_irc>
<chmanie> I am writing a driver for a configurable adc/dac and I would like to employ typestates, just like in the HALs. Sadly it doesn't seem to be easy as I only have one SPI instance that I wouldn't be able to share between ports. Is there a way to do this without drifting off into unsafe territory and reimplementing a lot of the SPI functionality? I really would like the driver to be portable
<re_irc>
<James Munns> Any examples of what kind of state you're trying to represent?
<re_irc>
<dirbaio> you can share the SPI with RefCell or shared_bus
<re_irc>
<dirbaio> the ADC ports will be "Port<'a>" isntead of just "Port" though
<re_irc>
<dirbaio> you can share the SPI with RefCell or shared_bus, between all theports
<re_irc>
<dirbaio> you can share the SPI with RefCell or shared_bus, between all the adc ports
<re_irc>
<James Munns> Ideally, that gets abstracted away in the "T: spi::Transfer" bound or whatever tho
<re_irc>
<James Munns> not sure what chmanie is trying to represent in type state tho
<re_irc>
<chmanie> James yeah, the chip basically allows for configuration of the individual ports that then influence their capabilities. So I'd have one type (struct) per mode with different methods
<re_irc>
<James Munns> Yeah! Let me write you up a quick example
<re_irc>
<chmanie> Thank you!
<re_irc>
<chmanie> shared_bus seems promising for one, thanks dirbaio
<re_irc>
<chmanie> Even though that seems more targeted to actually sharing the bus between drivers, it might work for my case
<re_irc>
<James Munns> so, with this kind of layout, it shouldn't matter WHAT kind of SPI port you use, as long as it impls "embedded_hal::blocking::spi::Transfer", which both a "native" and a "shared bus" SPI impl would do
<re_irc>
<James Munns> Lemme know if it doesn't make sense, or if I totally missed the point somewhere :D
<re_irc>
<chmanie> It totally does make sense, only maybe I didn't get my point across well. The thing is, I really have 19 individual ports that can have 12 different modes each. In your example it seems to me that it would configure the entire Adc to one mode. Well I have that only 12^19 different Modes for the whole chip :D
<re_irc>
<James Munns> ohhhhhhh
<re_irc>
<chmanie> I hope that makes sense
<re_irc>
<James Munns> yeah... you could use type states for that, but it would get... nasty.
<re_irc>
<chmanie> Yeah I thought so :(
<re_irc>
<chmanie> But I really appreciate your example, it should be added to the embedded rust typestate docs
<re_irc>
<James Munns> and since you'll probably be checking/changing that at runtime, you might be better off with something like:
<re_irc>
<dirbaio> allow splitting the Adc into 19 AdcPort structs, then each gets its own typestate
<re_irc>
<James Munns> Yeah, that's a good solution too
<re_irc>
<James Munns> but like, if you're changing modes at all at runtime (outside of initial setup), you almost certainly want an enum instead of a typestate
<re_irc>
<dirbaio> does the API of a port change depending on the mode though? with ADCs usually all you can do is "measure() -> Voltage"
<re_irc>
<dirbaio> if it doesnt change, then typestates are more trouble than they're worth
<re_irc>
<dirbaio> * the API
<re_irc>
<chmanie> dirbaio, it's actually a DAC/ADC/GPIO expander thing. MAX11300
<re_irc>
<dirbaio> ahhh, makes sense then 👍️
<re_irc>
<chmanie> I wanted to split it up into 20 (sorry, it's 20) structs using typestates but then I get back to the original question. How could I share the SPI instance between all of them? Is that where RefCell or the shared_bus come in?
<re_irc>
<James Munns> yeah, exactly!
<re_irc>
<dirbaio> yup!
<re_irc>
<James Munns> shared_bus lets you take one "T: Transfer", and split it into many handles that all implement "T: Transfer" (with various sharing schemes)
<re_irc>
<chmanie> Okay, cool. I'll try my luck. If it's bad, I'll just do runtime checks (even though I like the typestate programming a lot)
<re_irc>
<James Munns> As someone who helped push/document typestate programming a lot: It is a _very_ cool tool, but with _very_ sharp edges
<re_irc>
<chmanie> Haha
<re_irc>
<James Munns> like, you almost always want a "runtime checked" version to fall back on
<re_irc>
<James Munns> e.g. for something like, "depending on a user command, switch this GPIO between input and output mode"
<re_irc>
<dirbaio> also, often you need to share the SPI bus _and_ some other data
<re_irc>
in that case you can share a "&RefCell<AdcState>" instead, where that struct contains both: "struct AdcState<T: Transfer>{ spi: T, shared_data: u8 }"
<re_irc>
<James Munns> typestate is AWESOME for static configuration, but AWFUL if you ever need to dynamically change anything
<re_irc>
<chmanie> Yeah, the chip for example also allows for changing modes on the fly. That would mean I'd need to cover all state transitions. Maybe this is a bit too much for now :)
<re_irc>
<James Munns> it's less about covering all the transitions. that's _relatively_ easy.
<re_irc>
<James Munns> the hard part is: if your user wants to switch at run time, they HAVE to use something like:
<re_irc>
Dac(AdcDac<..., DAC>),
<re_irc>
enum DacOrAdc {
<re_irc>
Adc(AdcDac<..., ADC>),
<re_irc>
}
<re_irc>
<James Munns> and re-implement pass-through methods that match on every case they ever want to use it
<re_irc>
<dirbaio> the dreaded typestate enum spaghetti 🍝
<re_irc>
<chmanie> Ooof
<re_irc>
<James Munns> Which to be fair, is probably roughly the same actual cost as runtime checking (e.g. asserting mode is correct before using the ".get_voltage()" ADC method, for example), but is much less intuitive
<re_irc>
<James Munns> it's sort of a hard game to play though. Type state is the best way to elide all runtime checks, but honestly sometimes you want to do stuff at runtime
<re_irc>
<James Munns> The best pattern I've found is to add ANOTHER typestate, like "AdcDac<..., RUNTIME>", which you can treat as whatever, and has all the runtime checks in it
<re_irc>
<dirbaio> if you have two modes which are "Adc" and "TurboAdc" whose ".get_voltage()" would otherwise be the same, the compiler will still duplicate all the code
<re_irc>
<chmanie> Hmm, that's interesting
<re_irc>
<dirbaio> all the "AdcDac" fns get duplicated
<re_irc>
<James Munns> Yeah, that's the other catch, though you can work around that (by having inner "unchecked" methods that both variants call)
<re_irc>
<dirbaio> +for each state
<re_irc>
<James Munns> Anyway, it's definitely worth giving it a try! But it's good to know that benefits come with different costs (which you may or may not care about)
<re_irc>
<chmanie> This has been very educational, thanks you two! I'll try to see how it looks (and start with just a few modes in the beginning). It'll be good training nevertheless
<re_irc>
<James Munns> chmanie make sure you come back and let us know what you learned! Or even write a blog post about it! I think there's room for a lot more discussion of the options and tradeoffs here, even if you decide not to use type states deeply or at all!