3 ms·
It seems like a lot of the problems stem from the definition of each microcontroller port being a different type. So you can't simply pass P0 or P1, you have to
by leoedin 6y ago
It seems like a lot of the problems stem from the definition of each microcontroller port being a different type. So you can't simply pass P0 or P1, you have to choose at compile time which one it is.
I wonder if there's a better way to do this. Perhaps making GPIO peripherals all the same type, with some sort of feature flag for each pin. It would simplify a lot of things.
From the point of view of an embedded developer tired of C/C++, there's a lot of attractive features in Rust for embedded. But I haven't tried to write more than a trivial project in it. I wonder if this is a fatal flaw or just a problem with how the embedded HAL is defined.
- rhn_mk1 6y agoMaybe defining a "Port" trait and using the dynamic type instead of the static one is a solution.
- rcxdude 6y agoPerhaps, but it sucks from a performance point of view. In embedded for a lot of operations you want the HAL to compile down to one or two instructions (GPIO being a classic example of this). Dynamic indirection is proportionally very expensive here (though in embedded a lot of the other costs normally associated with pointers are a lot less, since the memory hierarchy is very shallow).
- pas 6y agoenum_dispatch to the rescue! (or at least that is what it says on the tin: https://docs.rs/enum_dispatch/0.3.5/enum_dispatch/ https://docs.rs/enum_dispatch/0.3.5/enum_dispatch/ )
- ikskuh 6y agoMost of them are actually the same and on several microcontrollers you can even just access them as an array of structs, selecting each port at runtime. HALs try to hide these facts which you can only find out about in the hardware documentation Most GPIO defs are just "this is a struct at 0xAABBCCDD"
- lynaghk 6y agoAuthor here. Both my Zig code and the Rust peripheral access crate model the pins as distinct types, which I think is correct --- the pins have different memory addresses and sometimes (depending on the microcontroller) distinct sets of controlling registers. The tricky part in Rust is how to make things generic across distinct types. Zig's comptime lets you sort of "duck type" it (but with compile-time checking that all of the methods exist, etc.), whereas Rust requires that you explicitly introduce a trait and implement trait methods across the types. The embedded HAL crates do this with extensive use of macros, for example: https://github.com/nrf-rs/nrf-hal/blob/aae17943efc24baffe30b9a808fb099836e6bf94/nrf-hal-common/src/gpio.rs https://github.com/nrf-rs/nrf-hal/blob/aae17943efc24baffe30b... This solution makes sense given the constraints of Rust, but there's quite a cost in terms of compiler time and cognitive overhead to understand what is going on. (Aside: I didn't use the HAL in my Rust firmware, that's a higher layer of abstraction; I only used the PAC crates.)
- leoedin 6y agoInstances of types also have different memory addresses - that doesn't mean they're different types. I think it's a hard problem because every microcontroller is different. A pretty common pattern in C land is to define a peripheral struct (eg I2C, GPIO) which has a 32 bit uint for each register in the peripheral, and then create a pointer to the physical memory address for each peripheral. That means you can write functions which take an I2C peripheral without knowing which one - and so if you decide later to move over to I2C2 it's just a case of changing one variable. That works because broadly there's very little difference between I2C1 and I2C2, or GPIO0 and GPIO1, in the microcontroller. If they start being very different then you'd have problems with that approach.
- rcxdude 6y agoYeah, that's a better way to do it. I've been following rust for embedded with great interest but I'm not so sure the HAL work is really going in a good direction. There seems to be a lot of awkward design decisions going into the interfaces (and to be clear, designing even a simple GPIO interface which satisfies even most users is Hard, let along anything which works for something like a serial port). I've a feeling if I started using it in anger I would fairly quickly just write my own.