<adamgreig[m]>
would you mind opening an issue on the embedded-hal repo with some links? it sounds like possibly it would be worth adding a "send a dummy byte without cs" operation or something...
<adamgreig[m]>
it is actually really hard to do this on linux though, thinking about it, because it can set the spidev config to not use cs, but it can't read the current config, so it can't restore whatever the user set to start with
<adamgreig[m]>
so maybe in the end the best option is for the mmc library to ask for two spidevice objects, one that's a dummy that's just used to send the 8 clocks, even though it's pretty annoying
<fu5ha[m]>
> would you mind opening an issue on the embedded-hal repo with some links?
<fu5ha[m]>
Sure, can do that tomorrow and also tag the mmc library author to see what they think
<dngrsspookyvisio>
maybe it broke again, or there's a constellation I overlooked tho
StephenD[m] has joined #rust-embedded
<StephenD[m]>
I have a weird problem. Stack-allocating a large-ish struct (a few kb) it causing my stm32 to hard fault. Is this a known issue? If not I can throw together a minimal example sometime in the next few days
<JamesMunns[m]>
Do you pass it by value, either to a function, or returning it?
<JamesMunns[m]>
if yes - then yeah, rust makes pretty crazy stack copies, especially if you are not in release mode.
<StephenD[m]>
Is there a way to make rust be a bit smarter?
<StephenD[m]>
Or I guess, what's the recommended way to do this?
<JamesMunns[m]>
there's not a catch-all answer, other than "try not to do that". If it's a singleton or similar, you can use something like StaticCell or `cortex_m::singleton!()` to allocate the storage as a static, and then hold a `&'static mut [u8; 2048]` instead of a `[u8; 2048]`
<StephenD[m]>
here is a cat picture as a token of gratitude
crabbedhaloablut has joined #rust-embedded
kvark[m] has quit [Quit: Idle timeout reached: 172800s]
sajattack[m] has quit [Quit: Idle timeout reached: 172800s]
duderonomy has joined #rust-embedded
duderonomy has quit [Remote host closed the connection]
duderonomy has joined #rust-embedded
emerent has quit [Ping timeout: 240 seconds]
emerent has joined #rust-embedded
troykings has joined #rust-embedded
notgull has quit [Ping timeout: 255 seconds]
notgull has joined #rust-embedded
<Henk[m]>
<wucke13[m]> "Henk: Is RTIC an option?" <- Sure is!
duderonomy has quit [Remote host closed the connection]
duderonomy has joined #rust-embedded
troykings has quit [Quit: troykings]
<BenPye[m]>
Trying to figure out why LTO isn't doing what I want is the most annoying thing...
<BenPye[m]>
Binary crate links in N static libraries, they depend on some constant that gets defined externally. If I put the constant in the binary crate the constant ends up in .rodata and not optimised out, if I put it in literally any other object file and link that in, it'll get optimised out - doesn't land in .rodata and my codegen is better
<BenPye[m]>
It sort of looks like LLVM is merging all the external stuff into one module, whilst the binary crate is separate - which would explain it - but it doesn't explain any of the why....
<FlixtheNewbie[m]>
It makes a copy, but thre is no reason to
<FlixtheNewbie[m]>
The compiler could analyze that code and just fill the reserved space in Local
<dirbaio[m]>
yeah rust is quite dumb about moving big things around
<dirbaio[m]>
it used to be even worse in the past, it'll get better in the future
<FlixtheNewbie[m]>
dirbaio[m]: I've read discussion about that for years, so I'm not sure when it'll happen
<dirbaio[m]>
once you've been burned 2-3 times by it, you learn to not pass big things by value π€£
<kevlar700KevinCh>
Ah, right. Ada has in out for by reference. So in Rust you could just use Rusts version of a pointer. Why not do that anyway. Syntactic sugar?
<FlixtheNewbie[m]>
When moves are supported by the compiler, you tend to use them, it's convenient. And I'm not an embedded dev, so I don't mind huge copies usually
<FlixtheNewbie[m]>
* copies usually at work
<FlixtheNewbie[m]>
But yeah, on my firmware project, I learnt how to avoid that now :)
<FlixtheNewbie[m]>
"a bit more", like having more than 1 field per struct π
<FlixtheNewbie[m]>
This still shouldn't happen
<dirbaio[m]>
yea rust isn't that dumb, but it's still very dumb :(
<RobertJrdens[m]>
FlixtheNewbie[m]: You make it seem you know how to fix it.
<dirbaio[m]>
you can pass the super secret flag -Zdumb-stack-copies=no to disable these dumb stack copies
<FlixtheNewbie[m]>
RobertJrdens[m]: I know how it *should* work, no how to concretely fix it, because, I'm not a compiler backend developer. The space exists in the `Local` struct, so the data could be written there directly.
<FreeKill[m]>
Right but the function you're calling returns your object to you on the stack. It doesn't receive a pointer to an allocation.
<FreeKill[m]>
So your compiler would have to do a lot of work not only to elide the return copy, but somehow also maneuver the callee to place it's returned object in some other arbitrary place in your stack
danielb[m] has quit [Quit: Idle timeout reached: 172800s]
<FreeKill[m]>
I am not a compiler developer either, but I can't imagine a mechanism that would be able to make that transformation, it sounds very difficult
<RobertJrdens[m]>
worse: you'd also have to prevent the function from using more than that bit of the stack!
<FreeKill[m]>
Yep π
<FreeKill[m]>
Return value optimisation with nested function calls, into a receiving type which is not even identical - just has the copy elided type as a member... Sounds like you could get a PhD for it π
<FlixtheNewbie[m]>
No, I mean, the compiler does its job, and then the optimizer removes this.
<FlixtheNewbie[m]>
I blame LLVM, not the Rust team. Or maybe the Rust team doesn't allow LLVM to trigger this optimization
<FreeKill[m]>
I'm saying the optimisation is crazy hard
<FlixtheNewbie[m]>
Well, like some others: LLVM is a crazy piece of tech, right?
<FreeKill[m]>
But it's not magic. If you want to do an optimisation, there needs to be a path to performing it
<FreeKill[m]>
How do you recognise this behaviour, and what do you do replace it with? If you look at Cpp which has a pretty mature copy elision story, it only guarantees it in extremely limited cases
<JamesMunns[m]>
<FreeKill[m]> "I am not a compiler developer..." <- Yeah, someone else mentioned it, but RVO/NRVO is the name of this class of optimizations.
<JamesMunns[m]>
LLVM sometimes does it, but it's heuristic based, and doesn't always hit. There's talk about getting rust to be able to handle it as a MIR optimization to better guarantee it.
<FreeKill[m]>
Even so this is more ambitious than either RVO or NRVO
<JamesMunns[m]>
<kevlar700KevinCh> "> <@jamesmunns:beeper.com> if..." <- What a weird paper cut to make a stand about, and what an odd place to choose to make a stand about it.
<JamesMunns[m]>
FreeKill[m]: Yeah, I supposed this is like "guaranteed tail call elimination", but for stack usage.
<JamesMunns[m]>
Semantically Rust is doing what you asked, we just wish it would automatically do the outptr simulation we want it to do.
<BenPye[m]>
JamesMunns[m]: I wouldn't mind having actual outptrs as a compromise, I know there was an RFC some time ago for an `&uninit`
<BenPye[m]>
s/an/a/, s/RFC/suggestion/
<JamesMunns[m]>
I mean MaybeUninit is right there.
<JamesMunns[m]>
It's just not as pleasant to use :)
<BenPye[m]>
Right, but that doesn't let you guarantee that it's actually initialized
<JamesMunns[m]>
Nope, totally agree having lang level support for that would be very neat
<FreeKill[m]>
Hmmm I wonder what that would look like. Because the variable must presumably be created prior to calling that function
<JamesMunns[m]>
You can do let variable ahead of time, and initialize it later already
<FreeKill[m]>
I... Actually did not know that π
<JamesMunns[m]>
The uninit ref could allow you to take an uninit, write only reference, and track whether it is initialized or not, it's just way harder to do nonlocally I would assume
<FreeKill[m]>
"track whether it's initialised or not" seems like the hard part
<fu5ha[m]>
adamgreig: So, thinking about the SD/MMC thing more, I think it's actually "okay" with the current design of transactions; the SD/MMC implementation of `SpiDevice` is free to just de-assert CS early and clock the bus again before it conceptually unlocks the bus and ends the transaction
<adamgreig[m]>
How so?
<adamgreig[m]>
The SD driver is just given an SpiDevice , it doesn't impl it
<adamgreig[m]>
So it can only issue operations inside a transaction that always asserts cs at the start and desserts at the very end
<fu5ha[m]>
oh, right π€¦ββοΈ the SpiDevice is implemented by embedded-hal-bus or whatever. Yeah okay
<FreeKill[m]>
JamesMunns[m]: I wonder if it would be better just to have some guarantees about rvo
<adamgreig[m]>
If the SD driver was given the SpiBus instead it could do what you describe, but then it's not shared
<fu5ha[m]>
Yeah
chrysn[m] has joined #rust-embedded
<chrysn[m]>
Working on embedded-nal once again, I'm doing a lot of conversions between its SocketAddr and the underlying stack's corresponding type. As I didn't find anything on the issue tracker: Has it ever been considered whether it'd make sense for a UDP stack to have an associated type that is interconvertible with SocketAddr?
<JamesMunns[m]>
(it also guarantees the value is only ever initialized exactly once, since it is not mut)
<fu5ha[m]>
adamgreig: https://github.com/rust-embedded/embedded-hal/issues/478 this feels very related. It's basically another symptom of the same thing except instead of "deassert CS" during transaction, it's "change other pins mid-transaction". As I said in my comment there it feels bad to require multiple ways of "sharing an SpiBus" when `SpiDevice` is supposed to be exactly the way to do that, as far as I can tell. Feels like there should
<fu5ha[m]>
either just be a "way to share an `SpiBus`" and device goes away, or `SpiDevice` should be made generic enough to support these use cases
<dirbaio[m]>
fu5ha: I'd appreciate if you familiarized yourself with why the current solution is the way it is before calling it "silly"
<dirbaio[m]>
there are reasons why the SpiDevice is the way it is
<dirbaio[m]>
biggest is linux spidev API (and some RTOSs APIs) require the list of operations up front
<dirbaio[m]>
solutions that are "Mutex<SpiBus>" can't be implemented on these platforms
<dirbaio[m]>
we actually had an SpiDevice like that before, it was changed to the current solution after much discussion
<FreeKill[m]>
JamesMunns[m]: I mean in the type system. It is very common to do stuff (in C) like "if this function returns true, then your out vars are valid". How do we express that in the type system?
<FreeKill[m]>
We already have the result type for expressing this in a returned value, I don't know how it would play nicely with outvars
<dirbaio[m]>
with links to meeting minutes / chatlog
<dirbaio[m]>
about your particular problem
<dirbaio[m]>
it can be solved with a custom SpiDevice impl that does the dummy cycles after CS deassert
<dirbaio[m]>
* particular problem with SD cards
<adamgreig[m]>
FreeKill[m]: > <@larunite:matrix.org> I mean in the type system. It is very common to do stuff (in C) like "if this function returns true, then your out vars are valid". How do we express that in the type system?
<adamgreig[m]>
> We already have the result type for expressing this in a returned value, I don't know how it would play nicely with outvars
<adamgreig[m]>
You can return an option of a slice into the mut slice that was passed in, which sort of does this
<adamgreig[m]>
dirbaio[m]: But not on Linux? Isn't it kind of weird to need a custom SpiDevice impl for a particular driver?
<dirbaio[m]>
on Linux you'd have to use SpiBus and userspace-managed CS
<fu5ha[m]>
dirbaio[m]: Not calling the trait itself nor the work that has gone into it silly, just that it feels from a first principles bad to have multiple methods of doing what seems like the same thing from a user's perspective. And not sure how I'm meant to familiarize myself with the reasons a system is the way it is if they are not documented π
<dirbaio[m]>
docs document how the system works
<dirbaio[m]>
they don't have to document all the discussions that were had until arriving to how the system works
<dirbaio[m]>
there's PRs, issues, meeting minutes for that
<fu5ha[m]>
dirbaio[m]: So then how should the SD/MMC driver be implemented? It needs to implement itself over both some custom `SpiDevice` impl and over all of `&RefCell<SpiBus`, `Mutex<SpiBus>`, and `CriticalSection<SpiBus>`?
<JamesMunns[m]>
fwiw: these questions do come up decently often. It almost might be worth having an "embedded hal reference/book" that documents some of this
<dirbaio[m]>
yes
<dirbaio[m]>
fu5ha[m]: yes
<adamgreig[m]>
How does the custom SpiDevice thing possibly compose?
<JamesMunns[m]>
or, I might suggest that some cases (like perhaps sd/mmc) just don't fit into the "SpiDevice" hole, and might benefit from their own trait rather than trying to force it.
<adamgreig[m]>
What if you share the spi bus with this custom SD thing and some other device that also needs a custom SpiDevice impl?
<dirbaio[m]>
adamgreig[m]: you can make a regular SpiDevice and the custom one share that `&RefCell<SpiBus>`
<fu5ha[m]>
dirbaio[m]: Respectfully disagree. Of course the docs should not go into full detail about all the decisions that lead to it, but especially in the case of something that feels counter intuitive or restrictive, it is very nice to have a section which documents the reasons for why. I think this is extremely common even in reference-level docs throughout the Rust standard library and the rest of the ecosystem. Even a couple sentences
<fu5ha[m]>
like "`SpiDevice` uses atomic transactions which are restrictive to outside manipulation to be able to support platforms where this is required. If you need XXX, try YYY instead, and see ZZZ page for more info/discussion"
<dirbaio[m]>
it composes fine
<adamgreig[m]>
It seems like less work to just have the SD driver require the real SpiDevice and one dummy SpiDevice, if we're into workarounds
<dirbaio[m]>
fu5ha[m]: that's another thing, that's documenting limitations of the design and suggest ways to overcome them
<dirbaio[m]>
I agree having it would be good
<dirbaio[m]>
can you send a PR?
<adamgreig[m]>
JamesMunns[m]: I guess "their own trait" is exactly this custom SpiDevice that we're talking about
<dirbaio[m]>
well that's a custom impl, not trait
<adamgreig[m]>
Ah, I guess I see what you mean. The SD driver provides an SpiDevice impl that consumes an SpiBus and also works as a regular SpiDevice for other drivers?
<dirbaio[m]>
or well, kinda. for SD the "semantics" are the same (lock bus, do transaction, unlock), just that there's extra dummy cycles
<dirbaio[m]>
so you can reuse SpiDevice yes
<dirbaio[m]>
for displays with DC pin the "semantics" are more complex, you have to tell it when to toggle DC
<dirbaio[m]>
so you can't use SpiDevice, you can do a custom trait
<fu5ha[m]>
adamgreig[m]: It shouldnt' "consume" the SpiBus, it should take a sharing method to it. For example a &RefCell<SpiBus> or &Mutex<SpiBus>. That way other drivers can be shared the same way
<adamgreig[m]>
I guess the point is that standard SpiDevice isn't suitable for SD cards because of this, so either the driver needs to somehow take an SpiBus (maybe via a custom SpiDevice that can therefore still be shared), or some workaround like two SpiDevice, or we need to add another operation to SpiDevice like we did for delay
<dirbaio[m]>
you can also skip SpiDevice completely and make the driver take `&RefCell<Bus>, OutputPIn`. but then you're tying it to a particular way of doing locking (RefCell)
<dirbaio[m]>
so there's still some value to doing a custom trait in the SPI display case, so it can work with different locking methods
<fu5ha[m]>
This makes me think there should be a trait for "shared bus" generically which abstracts over `&RefCell<Bus>`, `&Mutex<Bus>`, `&CriticalSection<Bus>`
<dirbaio[m]>
fu5ha[m]: that's exactly what the old version of SpiDevice was
<adamgreig[m]>
Some way for a driver to say "I need an SpiBus but I can still share it with others"?
<dirbaio[m]>
we decided to drop it in favor of the current trait
<dirbaio[m]>
and we decided to not have two traits because of ecosystem fragmentation
<adamgreig[m]>
though we perhaps hoped not many things would need to share the bus, and I think that's still broadly true
<dirbaio[m]>
the only "real" exception we have is displays with DC pins
<dirbaio[m]>
the SD case i'd say it's a "configuration" issue
<fu5ha[m]>
Well, the alternatives are either:
<fu5ha[m]>
* special drivers, such as SD/MMC or displays, now need 4 (more?) constructors: One that takes `Bus`, one that takes`&RefCell<Bus>`, one that takes `&Mutex<Bus>`, and one that takes `&CriticalSection<Bus>`
<fu5ha[m]>
* Well, the alternative is that special drivers, such as SD/MMC or displays, now need 4 (more?) constructors: One that takes `Bus`, one that takes`&RefCell<Bus>`, one that takes `&Mutex<Bus>`, and one that takes `&CriticalSection<Bus>`
<dirbaio[m]>
"this device needs 8 dummy cycles after CS deassert" is comparable to "this device needs max 1Mhz and SPI mode 3"
<dirbaio[m]>
the traits don't deal with such "configuration"
<adamgreig[m]>
is it? I'd expect HALs to provide the latter as a config but not the former
<adamgreig[m]>
it's more of an "SpiDevice-provider" issue than a "SPI bus configuration" issue, right?
<dirbaio[m]>
"SpiDevice configuration issue"
<fu5ha[m]>
<dirbaio[m]> "can you send a PR?" <- Also forgot to reply to this but yeah I can send one later
<dirbaio[m]>
same as "this device needs 100ns between CS assert and first clock edge"
<adamgreig[m]>
we could add it as a method to the embedded-hal-bus objects, so you can tell them "make me an spidevice that does this weird thing at the end"
<dirbaio[m]>
it's still "below" the SpiDevice trait
<dirbaio[m]>
but "above" SpiBus
<adamgreig[m]>
which I guess is what you're getting at with the driver itself providing a custom SpiDevice
<dirbaio[m]>
so it'd go in the SpiBus->SpiDevice adapters yep
<adamgreig[m]>
but now everyone implementing the "SpiBus to SpiDevice" needs to do this not-part-of-the-trait thing, and your sd card driver magically will/won't work if you use a different SpiDevice, even though it's outside the type system
<dirbaio[m]>
yeah
<dirbaio[m]>
we already have that with SPI frequency and mode for SpiBus though
<dirbaio[m]>
IMO it's "okay"
<adamgreig[m]>
so I guess in that case it is better to have the driver provide its own SdCardSpiDevice so at least you can't use the wrong one?
<fu5ha[m]>
Yeah this is the sort of thing where it's like, if you're sharing a bus, you inherently have configurations that need to be "lowest common denominator" of all devices on the bus
<adamgreig[m]>
well yea except all the HALs already offer a way to configure the freq and mode, whereas nothing that exists offers a way to say "do 8 clock pulses after each transaction"
<fu5ha[m]>
I think the answer actually is something like proposed here: https://github.com/rust-embedded/embedded-hal/issues/478#issuecomment-1660123780 that allows abstracting over the action of "get exclusive access to a resource", which we can use with SpiBus for this case to implement the display and SD driver cases. And SpiDevice would still remain for drivers that are implementable with its restrictions for the purpose of Linux etc
<fu5ha[m]>
interop. embedded-hal-bus could even take advantage of that Share trait to be more flexible with creation of SpiDevice impls
<dirbaio[m]>
again, that's what the old SpiDevice looked like
<dirbaio[m]>
we actively decided to not do it
<adamgreig[m]>
at the time part of that decision was "can't be implemented on linux", along with "is horrible with async" iirc
<dirbaio[m]>
yep
<dirbaio[m]>
also it's not dyn-safe
<adamgreig[m]>
but I don't think we had LCD screens and SD cards various slightly off-piste requirements in mind, so our imagination of what drivers would need SpiBus directly was more niche things like smart leds
<dirbaio[m]>
iirc spi displays were brought up, we were okay with them requiring a custom trait
<adamgreig[m]>
if there are more things that still need to share an SpiBus, they're going to come up with some way to do this, or worst case just each require to own the SpiBus, so we could at least provide a suggestion that might help interop?
<fu5ha[m]>
Sure, and I understand now why SpiDevice in its current form is desirable for the reasons you've outlined. However as we've discussed there's also the use case of sharing a SpiBus (or I guess the i2c equivalent or other resources) in a less-restrictive way
<fu5ha[m]>
Currently that requires drivers to know all the possible ways of validly sharing the bus and implement support for all of them
<adamgreig[m]>
the worry was that if we have a way to share an SpiBus, lots of drivers would use that instead of SpiDevice, and then not work in places where you can only get the SpiDevice
<fu5ha[m]>
I hope that would be help-able with docs, but yeah
<dirbaio[m]>
you can document "please do A instead of B, it's better due to X, Y, Z", and people will still do B
<fu5ha[m]>
fu5ha[m]: And if someone comes up with their own way (a custom critical-section/mutex impl?) then they'd be SOL since drivers would have no way to know about it
<fu5ha[m]>
dirbaio[m]: Of course, but I don't think that means we should just stop people from doing B altogether when it is valid in some cases :P
<dirbaio[m]>
fu5ha[m]: driver would ideally do `trait DisplayDevice`, implement it for RefCell, Mutex, CriticalSection. So the user can still do an impl for SuperSpecialMutex
<dirbaio[m]>
fu5ha[m]: not having B in embedded-hal doesn't prevent a driver from from doing it. Driver can still do B in their own crate. It interoperates fine with the rest of the ecosystem, you can even put a DisplayDevice and a regular SpiDevice on the same shared bus.
<dirbaio[m]>
the goal of not having B in embedded-hal is to ensure everyone that can do A, does A.
<fu5ha[m]>
dirbaio[m]: Hmmm, true
<dirbaio[m]>
by making it the path of least resistance
<dirbaio[m]>
if someone really really needs B they can still do it, in their own crate
<dirbaio[m]>
or if it's a common issue across a class of devices, you can have a shared crate like display-interface so not every driver has to reinvent t
<dirbaio[m]>
s//`/, s//`/, s/t/it/
<adamgreig[m]>
so do we provide an example of the "best" way to do B when you need a custom impl, to make it still possible for things to share nicely?
<fu5ha[m]>
Okay. I think this can work indeed. It should just get documented at least, and maybe even link to display-interface as an example of it working. I'll put sending some docs PRs on my list :)
Guest7221 has left #rust-embedded [Error from remote client]
<adamgreig[m]>
at least with the latest version of linux-embedded-hal it is possible to get an SpiBus too, to participate in these shenanigans
<adamgreig[m]>
though I guess it's quite rare for a linux device to need to talk to an sd card over spi anyway
<dirbaio[m]>
with the caveat that you then have to manage all CSs in userspace
<dirbaio[m]>
you can't mix kernel-managed and userspace-managed CSs
<adamgreig[m]>
indeed, it's quite a caveat
<adamgreig[m]>
but if you can change the devicetree it's not too bad
<dirbaio[m]>
and I think kernel-managed CS is good because you can then use the kernel spi drivers
<dirbaio[m]>
like
<dirbaio[m]>
if you have a shared bus where one device does have a kernel driver, and another doesn't so you want to run the driver from userspace
<dirbaio[m]>
I believe kernel-managed CS allows that
<adamgreig[m]>
yea
<adamgreig[m]>
or you have a device being used via spidev in one program, and your device in your rust program, the kernel will share them
<adamgreig[m]>
whereas once you have an spibus from rust, nothing else on the entire system can be allowed to use that bus*
<adamgreig[m]>
*technically this isn't really true either, it's only a problem for devices that don't use CS at all, because the rust SpiBus still locks the bus for the duration of its transactions, it just also drives random other gpio for cs
<adamgreig[m]>
so if you have a smart led this won't work at all, but actually if you have like an SD card or an LCD display or whatever, you're still guaranteed no other kernel-managed devices will stomp on your data while you're inside your transaction
<dirbaio[m]>
they can still stomp between "assert cs" and "do transaction"
<adamgreig[m]>
ah, of course, it has to assert cs before the transaction starts, yes
<adamgreig[m]>
shame the kernel doesn't expose a way to lock the bus to userspace
pronvis has joined #rust-embedded
pronvis has quit [Client Quit]
pronvis has joined #rust-embedded
<pronvis>
Hello guys!
<pronvis>
I am working on my own project where I need to communicate with TMC2209 via UART on only one pin. So I am looking for pins that can switch between input and output modes at runtime. But, looks like on `embedded-hal` they remove them. Any ideas how to do so?
<M9names[m]>
Have you checked the datasheet for recommendations on how to wire it up?
<Lumpio->
The datasheet says it's single line half duplex
<pronvis>
Yep, single line half duplex, so it should work on one pin. Also I checked Klipper (3d printer firmware) sourcecode and found that for UART communication with TMC2209 they use single pin.
<pronvis>
On Arduino they use SoftwareSerial to communicate with tmc2209. So I am writing my own SoftSerial on Rust. Sending messages works fine - tmc2209 understand my commands. But How to implement reads?
marmrt[m] has joined #rust-embedded
<marmrt[m]>
My guess is that single pin UART is niche enough that HALs mights not support it, if so you'll have to use the direct register access from the PAC to implement the functionality yourself
<pronvis>
Hmmm, got it. At least it is a way to workaround. For now I am using `embedded-hal: 0.2.7` and there is a `pub trait IoPin<TInput, TOutput>`, I will try to use it first, but, I dont understand what generic types it expects from me?
<pronvis>
also, there is no functions in `stm32f1xx_hal::gpio::Pin` to create `IoPin`. Maybe someone have some examples on how to create it?
<dirbaio[m]>
Very few hals impl it, I think the f4 one does
<dirbaio[m]>
Anyway iopin doesn't let you do uart
<pronvis>
why not?
<dirbaio[m]>
Because it's gpio, not uart
<dirbaio[m]>
I mean sure you can bitbang it but that's not ideal
<dirbaio[m]>
Is the uart opendrain? If so maybe you can get away with using a regular opendrain uart on 2 pins and shorting them to the single rxtx line
<pronvis>
Currently I am writing bits by `set_high`, `set_low` on timer interrupts. What is the best way?
<diondokter[m]>
On STM you can swap the TX and RX pins, so with being sneaky you could create a single pin uart with that :D
<M9names[m]>
Also: this feels much more complex than the datasheet solution of connecting UART tx to Rx via a resistor so the device can drive it harder
<dirbaio[m]>
pronvis: Yeah that's bitbanging
<dirbaio[m]>
You'll run into issues if the core is busy with other things like interrupts, breaking timing
<pronvis>
yep, but I dont know better way
<dirbaio[m]>
You likely want to use the hardware uart
<dirbaio[m]>
Reconfiguring it on the fly when switching rx/tx
<dirbaio[m]>
For example with the "swap pins" dion mentioned
<diondokter[m]>
Pretty easy to do on nRF as well
<pronvis>
I need to control 4 tmc2209 and the pins they are sitting on is pc10, pc11, pc12, pd2. Pc10 and Pc11 is hardware pins for UART3 on my chip. So, I cant use hardware uart here
<dirbaio[m]>
You can use uart on pc10, the deinit it, then on pc11, then deinit
<M9names[m]>
Is this existing hardware you can't modify?
<dirbaio[m]>
Aa long as you don't need to talk to both at the same time
<dirbaio[m]>
I guess pc12, pd2 go to other hardware uarts?
<pronvis>
> Is this existing hardware you can't modify?
<pronvis>
yes
<pronvis>
also, I need to control all 4 motors at the same time
<dirbaio[m]>
π¬
<dirbaio[m]>
Oof
<pronvis>
I use BigTreeTech SKR e3-dip v1.1
<dirbaio[m]>
But surely you can loop over all motors really fast controlling each in turns
<M9names[m]>
You're just setting stepper Params via UART, right? The actual driving is via step-dir using gpio
<pronvis>
:You're just setting stepper Params via UART, right? The actual driving is via step-dir using gpio"
<pronvis>
No - you can control it. Commands looks like: set_speed to X, for example
IlPalazzo-ojiisa has joined #rust-embedded
<pronvis>
And this is what I am trying to achieve - control steppers via "high level" commands.
<pronvis>
it is better, cause tmc2209 have smart chip that control current dynamically and can be super silent
<raulvt[m]>
diondokter[m]: thks!
<raulvt[m]>
just was thinking about if it can use some trhead::sleep, so the other running tasks can run while it waits
<raulvt[m]>
I'm using it on rpi4
Guest7221 has joined #rust-embedded
<raulvt[m]>
I wasn't aware that TMC can receive step/dir signals from uart, so you can use a single wire to do everything
<M9names[m]>
No, step/dir are different pins
<M9names[m]>
and their special sauce microstepping still works when you control them this way
<pronvis>
I dont even need to do step/dir manually, cause tmc chip will do that for me, if I send command to "set speed to X" for example. But those commands goes via uart. But I also want to get responses from TMC, to get current current, for example
<pronvis>
Looks like the best way (or only one way?) to do that is bitbanging. And control PAC to change pin to input mode on reads, am I right?
<pronvis>
I would like to avoid using CPU here, but dont know better ways. Also I am not expert in embedded programming
JonathanDickinso has quit [Quit: Idle timeout reached: 172800s]
<dirbaio[m]>
why is it a problem for reads but not writes?
almindor[m] has joined #rust-embedded
<almindor[m]>
writes never require DC pin switch within a single transaction
<dirbaio[m]>
isn't it "start transaction, write command, switch DC, write data, end transaction"?
<almindor[m]>
right now it's set DC, start tx, send cmd, end tx, switch DC, start tx, send data (big chunk here), end tx, <do whatever>
<dirbaio[m]>
ahhh you can split it like that
<dirbaio[m]>
vs for reads the display "forgets" what you were trying to read if you end the transaction early?
<almindor[m]>
note that there is also technically some timing requirement for DC and transfer interaction there
<almindor[m]>
well that, plus you also really want to perhaps do them quickly next to each other without re-asserting cs
<almindor[m]>
in a funky way the DC is sort of like a secondary CS, it's kind of bothersome, I wished they solved it in a different way but here we are
<almindor[m]>
in essence tho the issue is as always linux-hal compatibility forcing a bad hand elsewhere
<dirbaio[m]>
> do them quickly next to each other without re-asserting cs
<dirbaio[m]>
but that would apply to writes too, right?
<almindor[m]>
technically, but you usually do one big region at a time
<almindor[m]>
it'll probably have feelable consequences once we add things like blitting (e.g. diff drawing)
<almindor[m]>
you might end up with a bunch of regions you wanna draw ASAP
<almindor[m]>
add some vsync/timing issues with another pin and there we are
<almindor[m]>
big mess
<almindor[m]>
and I know people think that displays are always dedicated SPI but it's not always the case oddly enough
<waveguide[m]>
adafruit has display+sdcard combos with single spi bus interfaces I thought
<almindor[m]>
we'll need to address this broadly, I think this might be a good point to discuss things like the vsync GPIO pin as well and make sure things end up in the right abstraction points
<waveguide[m]>
is there a driver that has cs control done by hardware btw?
<waveguide[m]>
like sam e70 hates/fails to work with software cs
<waveguide[m]>
* to work properly with software
<almindor[m]>
umm, hw spi is usually part of the MCU master?
<almindor[m]>
i drive my display using hw SPI but sw works fine too
<dirbaio[m]>
and it all interoperates fine. For example you can put a RefCellDisplay and a standard RefCellDevice on the same bus
<waveguide[m]>
yes, but what controls cs, like the above cs: impl OutputPin seems to imply a software controlled cs line
<dirbaio[m]>
the only thing the "shared bus trait" you propose fu5ha is it'd allow merging "RefCellDisplay, CriticalSectionDisplay, MutexDisplay" into "SharedDisplay"
<dirbaio[m]>
* propose fu5ha would gain us is it'd
<almindor[m]>
waveguide[m]: of that's catch num. 3 because you need the possibility of software CS
<almindor[m]>
technically you want to make it an Option<CS> kind of thing
<dirbaio[m]>
waveguide: SpiDevice allows for "hardware CS" implementations.
<almindor[m]>
if it'd be me I'd dump linux compat and go back to the closure :P
<dirbaio[m]>
in that case the HAL would implement SpiDevice directly, instead of implementing SpiBus and let the user use the software-CS wrappers in embedded-hal-bus
<dirbaio[m]>
and such "hardware CS" SpiDevice impl would be usable with SpiDeviceDisplay just fine
<dirbaio[m]>
there's no HAL implementing hardware-CS as far as I know, currently
<dirbaio[m]>
but it's not impossible
<FlixtheNewbie[m]>
I want to put more data in my FLASH, but I don't know how to do. Suppose that I provide a "constructor" function in my library, allowing the user to create an object: Foo::new(data: [u32; 64]). That data is read only, so it's not supposed to be in RAM. How can I do that? Can I just slap const in the function signature?
<FlixtheNewbie[m]>
* I want to put more data in my FLASH, but I don't know how to do. Suppose that I provide a "constructor" function in my library, allowing the user to create an object: `Foo::new(data: [u32; 64]) -> Foo`. That data is read only, so it's not supposed to be in RAM. How can I do that? Can I just slap `const` in the function signature?
<dirbaio[m]>
dirbaio[m]: `static`s go in flash
<dirbaio[m]>
dirbaio[m]: and then pass around a `&Foo`, never a `Foo`
<dirbaio[m]>
dirbaio[m]: the `&Foo` will point to the data in flash, so it'll never consume RAM
<dirbaio[m]>
almindor[m]: the closure has other problems
<dirbaio[m]>
- doesn't work with async (should be fixed in a future version of rust, but likely years away)
<FlixtheNewbie[m]>
dirbaio[m]: So, my function signature will look like `Foo::new(data: &'static [u32; 64]) -> Foo`? So that I'm sure the user use the FLASH when consuming my API?
<dirbaio[m]>
dirbaio[m]: there's no way to do `&'static in_flash [u32; 64]`
<fu5ha[m]>
<dirbaio[m]> "the only thing the "shared bus..." <- Yes which is nice because now user of CustomLock<Bus> can use SharedDisplay and take advantage of `display-interfacr` handling conversion from its DataFormat enum to the raw underlying Word transfers. I'm sure there are parallels in other interfaces where the high level interface trait takes some higher level format that has logic to convert into byte/bit form that actually is
<fu5ha[m]>
transferred over the bus, which otherwise needs to be rewritten per implementation.
<almindor[m]>
dirbaio[m]: you could technically do it with the linker
<FlixtheNewbie[m]>
almindor[m]: Well, the user can fuck up, right, but my API allows to use the FLASH at least
<almindor[m]>
FlixtheNewbie[m]: add your data in the linker script and reference it as something
<dirbaio[m]>
IMO using custom locks is very very rare
<FlixtheNewbie[m]>
almindor[m]: I'm writing an arch-agnostic library anyway
<FlixtheNewbie[m]>
FlixtheNewbie[m]: So, it's the user's job not to mess up
<dirbaio[m]>
and it's still possible to use them, just that the user has to do their own impl
<dirbaio[m]>
dirbaio[m]: "global static" make sthe symbol "not exported" kinda
<dirbaio[m]>
dirbaio[m]: yes
<dirbaio[m]>
yes
<FreeKill[m]>
dirbaio[m]: Yeah, tbf its kind of C being inconsistent within itselfπ static means two things
<FlixtheNewbie[m]>
FreeKill[m]: Thanks for the explanation guys.
<dirbaio[m]>
FlixtheNewbie[m]: the equivalent in rust would be visibility, sort of
<dirbaio[m]>
FlixtheNewbie[m]: ```rust
<dirbaio[m]>
pub static X: u32 = 0; // public
<dirbaio[m]>
static Y: u32 = 0; // private
<dirbaio[m]>
dirbaio[m]: yeah C is weird
<fu5ha[m]>
dirbaio[m]: This is the API I would propose. And I feel it's not a big deal to not be object safe, though I haven't fully justified this belief. But I don't see a reason you'd need a trait object over this trait
<dirbaio[m]>
fu5ha[m]: i've definitely used `dyn` in the past, with uart, not spi
<almindor[m]>
isn't there an interrupt he could use?
<dirbaio[m]>
to abstract over "hardware raw uart" and "muxed uart"
<dirbaio[m]>
also HALs tend to put the peripheral instance in generics. `Spi<SPI1>`, `Spi<SPI2>`
<dirbaio[m]>
if you have some complex logic that you want to run on either SPI1 or SPI2
<dirbaio[m]>
using generics will duplicate the complex logic in code due to monomorphization
<fu5ha[m]>
If you need this you could define your own dyn-safe trait on top of the statically typed ones provided by e-h (or we could provide one if we wanted), I think
<fu5ha[m]>
dirbaio[m]: Hm. Would have to think about that more, I'm not used to thinking about code size optimization
<dirbaio[m]>
vs with dyn you can instantiate the complex logic only once. Will be a bit slower, but much smaller than 2 monomorphized copies.
<dirbaio[m]>
(IMO it's a HAL antipattern to have generics like `Spi<SPI1>, Spi<SPI2>`. I believe it's [feasible](https://github.com/embassy-rs/embassy/pull/980) to have just `Spi`, in which case the problem would go away)
<dirbaio[m]>
(but even if hals did that, other use cases for dyn remain)
<dirbaio[m]>
IMO giving up on dyn is the wrong tradeoff
<dirbaio[m]>
lack of dyn affects everyone
<dirbaio[m]>
vs having the closure/mutex trait helps make a few niche use cases nicer (displays with DC, sdcards)
<dirbaio[m]>
and, again, it makes these use cases just nicer. They're already doable with the current SpiDevice.
<dirbaio[m]>
and for these niche use cases you can make a crate like display-interface
<dirbaio[m]>
sure you write some boilerplate in it, the ExclusiveDisplay, RefCellDisplay, CriticalSectionDisplay, MutexDisplay
<dirbaio[m]>
but once that's done, end users can use it with good ergonomics
<dirbaio[m]>
the only case where it hurts the end-user ergonomics is display_interface with a custom mutex, which is the niche of the niche.
<dirbaio[m]>
RefCell, CriticalSection, std::Mutex cover 99% of the use cases
<FlixtheNewbie[m]>
Is there a way to only display no_std crates in a crates.io research?
<almindor[m]>
<dirbaio[m]> "lib.rs has nice filtering https:..." <- one thing to be careful about lib.rs is that AFAIK they remove crates from their list based on political affiliation, if you don't find something there it might still exist
<dirbaio[m]>
they remove just cryptocurrencies
<almindor[m]>
so far...
<kevlar700KevinCh>
I expect Rust might have something to offer in particular in the area of safe data structures?
<almindor[m]>
kevlar700KevinCh: what do you mean "safe" specifically?
<kevlar700KevinCh>
almindor[m]: Not concurrency, just safe deallocation etc.?
<almindor[m]>
that should be the default
<almindor[m]>
allocs are usually OS level handled tho, in no-std it'd depend on what you use "underneath"
<almindor[m]>
it'd not be part of a collection in most cases I guess
<kevlar700KevinCh>
How about containers of a fixed size that let you delete objects from the middle?
therealprof[m] has quit [Quit: Idle timeout reached: 172800s]
romancardenas[m] has quit [Quit: Idle timeout reached: 172800s]
jannic[m] has quit [Quit: Idle timeout reached: 172800s]
Guest7221 has left #rust-embedded [Error from remote client]
<andresovela[m]>
Are there no loggers in the Rust ecosystem that allow for module-level filtering?
<andresovela[m]>
It seems that every single one has a global max level
<dirbaio[m]>
env_logger has RUST_LOG, defmt has DEFMT_LOG
<dirbaio[m]>
which do support filtering per module
<andresovela[m]>
Right, but have you tried using them?
<andresovela[m]>
It's a pain in the ass
<andresovela[m]>
At least DEFMT_LOG
<andresovela[m]>
Not sure about RUST_LOG
<dirbaio[m]>
why? it works fine
<dirbaio[m]>
DEFMT_LOG=info,some_module=trace cargo run ...
<andresovela[m]>
The length of the command gets ridiculous with any non-trivial application
<andresovela[m]>
And error prone
<dngrsspookyvisio>
it's not impossible to write a wrapper
<andresovela[m]>
Yeah I'm not saying it's impossible at all, I'm just surprised that this is the status quo in the ecosystem
<dngrsspookyvisio>
I suppose in most cases one global level is good enough, and if you do need a complex config I'd bet that there's already a tool that sets env vars from toml
<dirbaio[m]>
you can put DEFMT_LOG in [env] in .cargo/config.toml
<dirbaio[m]>
beware there's a cargo bug that makes it not recompile when you change it
<dngrsspookyvisio>
ok, maybe not for the required nesting
<dirbaio[m]>
you need to cargo clean
barafael[m] has joined #rust-embedded
<barafael[m]>
I am finally getting to write my first Rust bare metal app. The GPIO APIs everywhere use `wait_for_high().await` etc. everywhere. It's not a problem ofc, I just prefer `until_high().await`...
<dirbaio[m]>
well you certainly could make the proc macros read [package.metadata.defmt]
<dngrsspookyvisio>
though I continue ambivalent about config files. They get annoying when working in a team⦠"that was not the logging config I wanted"
<dirbaio[m]>
still, it seems a bit cursed
<dngrsspookyvisio>
* though I continue to be ambivalent about config files. They get annoying when working in a team⦠"that was not the logging config I wanted"
<dngrsspookyvisio>
slightly cursed is my usual energy
<dngrsspookyvisio>
but I'll also not go and implement this
<dirbaio[m]>
if they're all from the same HAL it's likely the error is the same
<dirbaio[m]>
(if it's not then you have to wrap it in something that makes the error type the same, for example the ErrorKind)
brazuca has quit [Quit: Client closed]
Jonathan[m]1 has quit [Quit: Idle timeout reached: 172800s]
<fu5ha[m]>
<thejpster[m]> "Does anyone have thoughts on..." <- Oh hi! I don't have thoughts on this (sorry to hijack!) but I was going to open an issue on embedded-sdmmc but may as well just ask here instead to begin with. Have you looked at the embedded-hal 1.0 spi traits (particularly `SpiDevice`) and have opinions on how it can work with sdmmc? If I understand the spec correctly (after reading the comment that exists in the current version
<fu5ha[m]>
following this message https://matrix.to/#/!BHcierreUuwCMxVqOf:matrix.org/$m-7Nogb7SnhQe5Oi5QU2RF_gXGyYAZqum1p7w63gk50?via=matrix.org&via=catircservices.org&via=fehler-in-der-matrix.de but also more above if you look for my messages
<fu5ha[m]>
that CS needs to be handled separately to clock with CS deasserted), it is necessary to clock up to 8 times after a transaction until the card will fully release MISO line on the bus. This is not currently possible with (or should I say, not guaranteed by any implementation of) the 1.0 `SpiDevice`. A few potential solutions have been proposed to this, that are either a bit of a hack or are a bit cumbersome for sdmmc. Some discussion