<cr1901>
A client doing embedded Rust stuff asked me "why does the Peripherals struct have e.g. "USART1: USART { baseAddress: 0x42…00 }, USART2: USART { baseAddress: 0x42…10 }", for identical peripherals, and I could only give them hypothetical answer
<cr1901>
Instead, each Peripheral in the Peripherals struct in a PAC is a distinct type
<cr1901>
"It's always been done this way" is not a satisfying answer :P
HumanG33k has joined #rust-embedded
<firefrommoonligh>
<jannic[m]> "I think the choice of providing..." <- I do some current measurement using shunt + amps. It's just multiplying by a constant
<firefrommoonligh>
So; concur on no special current functionality
<firefrommoonligh>
So, for a voltage measurement you probably have a voltage divider. For current, you have a shunt resistance and amp factor. Either way, you convert the raw ADC reading to a voltage, then multiply that by the constant associated with the divider or shunt+amp
starblue has quit [Ping timeout: 256 seconds]
emerent has quit [Ping timeout: 264 seconds]
starblue has joined #rust-embedded
emerent has joined #rust-embedded
AdinAck[m] has quit [Quit: Idle timeout reached: 172800s]
notgull has joined #rust-embedded
reitermarkus[m] has joined #rust-embedded
<reitermarkus[m]>
The choice of voltmeter/ammeter is not arbitrary. ADCs either measure voltage or current. Of course this then mostly needs to be converted into some other unit, e.g. temperature. The point is you can then implement a sensor driver using any ADC, as long as it supports the required voltage/current range for the sensor.
<reitermarkus[m]>
Also, whether you see these traits as arbitrary depends on your definition of ADCs. I guess you could argue many sensors are ADCs and vice-versa, so maybe a separate `embedded-adc` or `embedded-sensor` crate makes sense for these traits.
<JamesMunns[m]>
<mameluc[m]> "ah okay it is the same as linker..." <- btw, from memory I think Jorge chose `.x` instead of the more common `.ld` extension, because c-m-rt does templating, where traditional linker scripts are just plain text files
<JamesMunns[m]>
I could be misremembering/imagining that. But 100%, these are just normal linker scripts :)
<GrantM11235[m]>
I think some C SDKs use .x
GabeR[m] has joined #rust-embedded
<GabeR[m]>
Hello, sorry to jump in. Does anyone know of any examples on how to use embedded-hal-bus for sharing an I2C bus across threads? I'm a total beginner and can't seem to figure out how to do it myself.
<JamesMunns[m]>
> sharing an I2C bus across threads
<JamesMunns[m]>
Just checking, are you running on Linux? Or what kind of threads do you mean?
<GabeR[m]>
I'm working on an ESP-32, which has std support. I've been trying to use a MutexDevice. The problem is that I'm not experienced enough in rust to really figure out how to make it work without having some examples to look at.
<JamesMunns[m]>
You can also drop by #esp-rs:matrix.org, where more folks might know the specific answers!
<GabeR[m]>
I've tried the following, but I can't seem to pass this into a thread because its a borrowed value that might not live long enough. (I think that's what the error is telling me).
<GabeR[m]>
let i2c_mutex: Mutex<I2cDriver<'_>> = Mutex::new(i2c_driver);
<GabeR[m]>
let bsec_i2c = MutexDevice::new(&i2c_mutex);
IlPalazzo-ojiisa has quit [Quit: Leaving.]
<JamesMunns[m]>
You might want to heap allocated it, with Arc
<JamesMunns[m]>
let i2c_mutex: Arc<Mutex<I2cDriver<'_>>> = Arc::new(Mutex::new(i2c_driver));
<JamesMunns[m]>
let bsec_i2c = i2c_mutex.clone();
<JamesMunns[m]>
the clone there just increases the reference count, so they are both pointing to the same mutex.
<M9names[m]>
<GrantM11235[m]> "I think some C SDKs use .x" <- GNU binutils ld has a script for generating target-specific ld scripts with this extension (and additional letters for variations. I assume it's specifically to avoid being confused with with user .ld scripts
<JamesMunns[m]>
Yeah! The lifetime issue was because MutexDevice takes a reference like Grant said, and you can't send non-'static references to other threads, since the original thread could end while the second one was still running (so the reference could possibly "not live long enough"!)
<JamesMunns[m]>
Thanks for correcting me GrantM11235, TIL :)
<GabeR[m]>
Yeah. Was realizing that from the error. Could not find the way around it though and was not able to find a lot of info in the crate's documentation that pointed me in the right direction.
<GabeR[m]>
Thanks again
<GrantM11235[m]>
If you want to avoid the arc for some reason, you make use a `&'static Mutex` with a lazy static or with `Box::leak`
<GrantM11235[m]>
* If you want to avoid the arc for some reason, you can make a `&'static Mutex` with a lazy static or with `Box::leak`
<GabeR[m]>
Might have to look into that in the future. Any pros vs cons for Arc vs &'static Mutex?
<GrantM11235[m]>
A `&'static T` can be copied for free, but that doesn't matter too much since it is super cheap to clone an Arc
<GabeR[m]>
I'm guessing that `&'static` has an additional advantage of not having to allocate memory on the heap?
<GrantM11235[m]>
Yes, a lazy static won't use the heap (but Box::leak would)
<GabeR[m]>
Might have to look into that. Having everything statically allocated at compile time is nice.
<GrantM11235[m]>
The Arc will `Drop` the inner type when you are done using it from all threads, but a `&'static` will stick around forever. This might be important if dropping the i2c driver disables the peripheral in order to reduce power usage. The embassy HALs do this for example, but I don't know if the esp hal does
<GabeR[m]>
I don't know either. First time using rust for any embedded stuff. (And 3rd or 4th time using Rust ever).
<GabeR[m]>
I only learned the ESP32 C SDK in the past month or two. Decided to try re-writing what I had done so far using their Rust API and see how they compare. Still up in the air because there might be some features missing in the rust one I want.
<GabeR[m]>
I've been liking rust in general though, which is why I wanted to try it. Just a little antsy about the rather large dependency tree even a simple program can have.
<swaits[m]>
<dirbaio[m]> "yes, SWD..." <- Hey, thanks for helping me with this yesterday. Picked up an ST-Link v2 clone for $6 from Amazon and I'm now up and running. Now the blinky example from embassy just works. Appreciate it!
<swaits[m]>
<swaits[m]> "Hey, thanks for helping me..." <- Also, after poking around and running most of the examples I have to say that embassy is insanely nice.
notgull has quit [Ping timeout: 264 seconds]
user1__ has quit [Ping timeout: 264 seconds]
stuw1 has joined #rust-embedded
stuw1 has quit [Ping timeout: 264 seconds]
stuw1 has joined #rust-embedded
ello has left #rust-embedded [#rust-embedded]
<adamgreig[m]>
cr1901: that comment was about when I made it public, yea, though I'd started working on it 9mo prior in June 2017
<adamgreig[m]>
Good history digging :p
<cr1901>
adamgreig[m]: Wasn't me who found it, it was my client (nite owl time). They asked a good question about "why don't Peripheral struct members share types between copies of identical peripherals"?
<cr1901>
(Other than speculating hypotheticals, I don't have a concrete answer other than "that's just the way it's done- Peripheral UARTx gets a UARTx type".)
<JamesMunns[m]>
svd2rust I don't think ever did much of trying to figure out if two instances were the same
<JamesMunns[m]>
it could be as simple as "the svd repeated the definition twice, svd2rust didn't merge them". There are some "repeated instantiation" features in the svd format, but not all manufacturers use it
<cr1901>
<derivedFrom> or something else?
<JamesMunns[m]>
honestly I haven't looked in forever
<JamesMunns[m]>
I know there's a few ways to specify exact duplicates
<cr1901>
I mean, same. Why look it up when I can get someone else to answer? :)
<JamesMunns[m]>
but honestly the earlier versions of svd2rust probably just quote!'d out code in a for loop anyway, even if all the instances were exactly the same :D
* cr1901
doesn't feel like thinking rn
<cr1901>
I mean, that tracks w/ a bunch of Rust software- Rust makes you write better software, but you can still make useful stuff w/ rough edges.
<cr1901>
(Combined w/ "SVD the format is nice, but vendors tend to put very little care into the stuff they emit with it." I know firsthand that NXP and STM's are both pretty bad.)
<adamgreig[m]>
In my head it did use the same type for peripheral instances derived from each other, but I'd have to double check
<adamgreig[m]>
It certainly doesn't try and detect that identical but not derived instances are actually the same and merge them, though stm32ral did that
<adamgreig[m]>
And chiptool (used for embassy PACs) and raltool (imxrt?) naturally ends up with identical instances too
<cr1901>
identical instances as in "different types" or "merged types"?
<romancardenas[m]>
[`riscv-rt` 0.12.1](https://crates.io/crates/riscv-rt) includes a patch to avoid the spurious errors shown by LLVM in release builds
<firefrommoonligh>
<reitermarkus[m]> "The choice of voltmeter/..." <- > <@reitermarkus:matrix.org> The choice of voltmeter/ammeter is not arbitrary. ADCs either measure voltage or current. Of course this then mostly needs to be converted into some other unit, e.g. temperature. The point is you can then implement a sensor driver... (full message at <https://catircservices.org/_matrix/media/v3/download/catircservices.org/DhrjoLfGEqksuPChbSezwIeu>)
<firefrommoonligh>
* I'm not really following. Context: I make and sell a power module that has 14 ADC inputs that are all voltage and current measurements. What advantage would this provide over the register reading -> voltage -> interpreted voltage or current (optionally calibrate as another step)?
<firefrommoonligh>
Reading -> voltage is probably handled by the HAL or ADC module. voltage -> interpreted voltage or current is multiplying by a constant determined by the passives on your board.
<firefrommoonligh>
* I'm not following. Context: I make and sell a power module that has 14 ADC inputs that are all voltage and current measurements. What advantage would this provide over the register reading -> voltage -> interpreted voltage or current (optionally calibrate as another step)?
<firefrommoonligh>
* I'm not following. Context: I make and sell a power module that has 14 ADC inputs that are all voltage and current measurements. What advantage would this provide over the register reading -> raw voltage -> interpreted voltage or current (optionally calibrate as another step)?
<firefrommoonligh>
* Reading -> raw voltage is, * ADC module. Raw voltage ->
<reitermarkus[m]>
The problem is, it doesn't make sense to have a trait for reading the raw value, because you will still need to manage the conversion from raw value to voltage. Wit a Voltmeter trait, it doesn't matter what underlying ADC you use to measure voltage.
<reitermarkus[m]>
s/Wit/With/, s//`/, s//`/
<dirbaio[m]>
I think we should focus on collecting examples of drivers using the 0.2 adc traits
<dirbaio[m]>
checking what they want out of the trait
<dirbaio[m]>
then designing a triat for that
<dirbaio[m]>
s/triat/trait/
<dirbaio[m]>
I don't agree with "ADCs measure either voltage or current therefore the trait should do that"
<dirbaio[m]>
in many cases a driver might want "a number from MIN to MAX, I don't care about the actual voltage"
<chrysn[m]>
AIU the 0.2 ADC traits neither clearly measured voltage or current, but just gave some point on the linear scale between 0 and 1 (well, u16::MAX) on whatever that ADC precisely does.
<dirbaio[m]>
for example if reading a potentiometer you just want the 0%-100% position
<reitermarkus[m]>
But how do you know that the potentiometer corresponds exactly to the voltage range the ADC measures?
<reitermarkus[m]>
I get the point, but you cannot simply use any ADC in this case.
<reitermarkus[m]>
Then again, I guess you can say the same about not being able to use any ADC no matter what use case.
<dirbaio[m]>
yeah you don't
<dirbaio[m]>
in both the "volts" and the "just a number" case you have to "calibrate" the potentiometer
<dirbaio[m]>
"0% is X, 100% is Y"
<dirbaio[m]>
my point is for this case you don't need mV
<dirbaio[m]>
it's easier to go "raw ADC reading -> 0%-100%"
<dirbaio[m]>
than "raw ADC reading -> mV -> 0%-100%"
<dirbaio[m]>
requiring HALs to give mV is a bit burdensome, you often can't know without knowing the value of external resistors or reference voltages on the board outside the MCU.
<dirbaio[m]>
so it ahs a cost
<dirbaio[m]>
* so it has a cost
<dirbaio[m]>
i'm not saying we shouldn't do it, i'm saying that if we do it it should be for a reason
<dirbaio[m]>
so
<dirbaio[m]>
we should look at existing drivers using ADC, for each consider whether they'd be better off with a mV reading or a raw reading, then decide
<dirbaio[m]>
otherwise we're just guessing
<firefrommoonligh>
The API level I want out of the ADC, personally, is voltage measured. That's what an ADC does
<firefrommoonligh>
Raw bits is acceptable too if you know the conversion factor (eg determined by bit depth and ref V)
<firefrommoonligh>
* the ADC driver, personally,
<reitermarkus[m]>
Yeah, I agree with looking at what drivers do. For my own projects it's mostly just sensors that correspond to a voltage range. I already tried looking for other examples on GitHub before, but most of the results are just implementations of the OneShot trait, not usages thereof.
<dirbaio[m]>
yeah there's very little uses :S
<firefrommoonligh>
And I guess in practice, now that you mention one-shot, the most-common pattern I use is continuous reads to DMA, with a digital filter applied
<dirbaio[m]>
don't most of these analog sensors need calibrating anyway?
<firefrommoonligh>
I use one-shot readings for some cases too; depends on the application
<firefrommoonligh>
dirbaio[m]: Yea def
<firefrommoonligh>
Linear with slope and offset, or 3-pt polynomial is fine for many cases
<firefrommoonligh>
Easy, but example that you need post-processing
<dirbaio[m]>
then if you're calibrating anyway, why is it better to calibrate against a mV reading than a raw reading?
<dirbaio[m]>
you can take the raw reading and do the slope+offset / poly thing with it directly
<firefrommoonligh>
I guess it depends. Probably raw reading
<firefrommoonligh>
(I think I usually calibrate the volts in my code, but the raw reading is probably better; either works)
<dirbaio[m]>
would be more precise even, because converting from raw to mV will need some rounding
<firefrommoonligh>
Concur
<dirbaio[m]>
So I'm really not sure what the trait should be 😅
xiretza[cis] has joined #rust-embedded
<xiretza[cis]>
there definitely needs to be a raw reading API, a utility for converting to voltage including calibration can be composed on top
<holo[m]>
holo[m]: im not getting any message when connecting with netcat to this port
<reitermarkus[m]>
Yeah, I came to the conclusion that a trait for reading the raw value doesn't really make sense, at least not without any context. You would need to somehow add conversion factor and the unit after the conversion factor is applied. I mean, what exactly can you implement generically for a raw value you know nothing about?
<mameluc[m]>
my 2cnt is that mV or any other unit is outside the scope of what an ADC peripheral does. I use adcs to measure conductivity capacity and wind direction. Should those be in the traits too?
<mameluc[m]>
s/capacity/capacitance/
<holo[m]>
<chrysn[m]> "On the need for a buffer: On..." <- is there posibility to use this heapless::Vec with tcp socket? i saw it in the example its used but switched to this to_slice() because socket.write_all require slice not Vec
marmrt[m] has joined #rust-embedded
<marmrt[m]>
generic unit trait
<holo[m]>
holo[m]: im learning on rasberry pico W
<xiretza[cis]>
the ADC doesn't know anything either other than its full range, doing anything with the raw value is always application-specific and shouldn't be part of an ADC driver
<xiretza[cis]>
put differently, there's nothing hardware-specific that could be done by an ADC driver - converting a raw reading to e.g. a voltage is always the same process, regardless of hardware/driver
<reitermarkus[m]>
Yes, it's the same process, but not with the same conversion factor. And as I said, without having a conversion factor and unit a trait doesn't make sense to me.
<xiretza[cis]>
the conversion factor needs to be provided by the application, the driver doesn't generally know the ADC reference voltage being applied to the MCU pin
<xiretza[cis]>
and at the point, you just have a plain function that takes a raw ADC reading and some constant parameters and converts it to a voltage, that's not something to be implemented by an ADC driver
<reitermarkus[m]>
So if it is always application specific, as you said, why do you need a trait in the first place?
<reitermarkus[m]>
I guess you can be generic over the ADCs of a micro-controller, but implementing a driver based on such a trait is impossible without the driver itself also having to manage the conversion factor externally.
<holo[m]>
<chrysn[m]> "On not seeing anything, can..." <- acording to it
<firefrommoonligh>
My main thought, as an ADC user, is that I think it will be tough to make this work with the trigger/read/DMA/cal/passive-compensation/voltage-conversion/DSP pipeline that is common
<firefrommoonligh>
I'm not saying you can't do it
<marmrt[m]>
wouldn't the conversion trait be tied to whatever sensor you're using?
<marmrt[m]>
The temperature is inherently tied to your NTC resistor. The current tied to your shunt resistor.
<firefrommoonligh>
But what I look for is "Can this library or API make my program better". Better may mean saving me work, making the API cleaner, exposing useful functionality etc
<firefrommoonligh>
I brought up my power module because the origin pt was a trait for voltage and current readings; that project does loads of those! But I am not sure how I would use a trait to improve the code base
<marmrt[m]>
The ADC trait should help the user set single shot or continuous mode. Maybe set measurement frequency.
<firefrommoonligh>
That's a bit on the ADC's configuration register
<firefrommoonligh>
(In this specific case)
<swaits[m]>
Re: conversions of units and quantities, you might look at the uom crate. Of course you still need to figure out the function to map your inputs to meaningful values. But after that, uom is extremely nice.
<firefrommoonligh>
The measurement frequency likely involves an external trigger, eg a timer
lehmrob has quit [Ping timeout: 264 seconds]
Ecco_ has quit [Read error: Connection reset by peer]
Ecco has joined #rust-embedded
<holo[m]>
<chrysn[m]> "On not seeing anything, can..." <- Ahh ok its working i just needed to wait longer on socket (even few seconds) to recive it
<holo[m]>
Thank you
<reitermarkus[m]>
<firefrommoonligh> "My main thought, as an ADC user,..." <- What do you mean by *this* here? I can imagine it working similarly to the second async example in my PR, where you trigger the measurement and then wait for the ready-pin, or in this case wait for the end of the pipeline. Of course in this case you need to implement the trait using the pipeline, rather than implementing the pipeline based on the trait, maybe that's
<reitermarkus[m]>
what you meant.
<firefrommoonligh>
I don't see the value add
<firefrommoonligh>
Or how it would work in practice
<firefrommoonligh>
But maybe I am not the audience
<firefrommoonligh>
Mainly bc the hardware interaction is not too tough, and the post-processing is application-dependent
<firefrommoonligh>
Maybe you are; make the thing and see if it helps
<firefrommoonligh>
And I am confused about where in the pipeline your trait would sit
<reitermarkus[m]>
I guess in practice the trait would pretty much be the pipeline up to voltage-conversion, so yeah, it doesn't add much value for implementing the pipeline.
<firefrommoonligh>
I think maybe it would help if you considered (or posted?) your own ADC use cases. I'm curious what uses others here do. I alluded to mine; mainly voltage measurement, current measurement, and a potentiometer-based analog wind-direction sensor
<firefrommoonligh>
And, write the trait, an implementation, and firmware that uses it. See what the API is like with it, and without
<firefrommoonligh>
I had a struggle with ADCs I posted here lately that took a while to figure out! Turns out stm32H7's ADC has an extra bit you have to set depending on clock speed! That was a struggle
<firefrommoonligh>
* I had a struggle with ADCs I posted here lately that took a while to figure out! Turns out stm32H7's ADC has an extra bit you have to set depending on clock speed!
<reitermarkus[m]>
I did post one example in the PR: A water depth sensor that outputs 0-5V, corresponding to 0-5m. The use case is for monitoring the water level, so a simple loop reading the voltage. In my specific case, I'm using an ADS1115, but as long as an ADC can measure between 0 and 5V, any ADC can be used to implement a `WaterDepthSensor<T: Voltmeter>` driver.
<GrantM11235[m]>
<reitermarkus[m]> "I did post one example in the PR..." <- I think that example is *too* simple, it doesn't really do anything. `WaterDepthSensor` would just have `fn get_depth_mm(&mut self) -> u32 { self.adc.get_mv() }`
<reitermarkus[m]>
<GrantM11235[m]> "It's not a driver, it's just [`..." <- Even if it is in this case, the point is you can use any ADC you want to construct it, given it supports the 0-5V range.
<reitermarkus[m]>
handle_readings
<GrantM11235[m]>
Why not just replace the driver with `fn convert_mv_to_mm(mv: u32) -> u32 { mv }`?
<GrantM11235[m]>
That would also allow you to use dma to read a bunch of samples into a buffer and then convert them afterwards
<reitermarkus[m]>
I think we're talking about two different use cases. I agree the trait doesn't really work with DMA.
<vollbrecht[m]>
reitermarkus: i think when talking about adc's its the wrong abstraction. The "simplest" way to specify them are by there discrete resolution. The second parameter of note is maybe what the adc uses as a reference. This parameter is mostly a voltage but i think for a generalized adc you would also want to specify it. If you have this you can build a more meaningful general abstraction ontop of that. But minimum baseline are
<adamgreig[m]>
you can write the whole body as just `(i16::from_be_bytes([buffer[2], buffer[3]]) << 4) >> 4` if you wanted to golf it, but in any event don't need the mut
<barnabyw[m]>
yeah that’s what I ended up doing
<barnabyw[m]>
with a comment to remind me what’s going on there
<firefrommoonligh>
I think adamgreig may have actually told me about that bit shifting hack vice a more complicated way I was using previously...
<firefrommoonligh>
Want to know what sucks considerably more? Dealing with 16-bit floating points
<firefrommoonligh>
(And we can also debate about the use case of them given they're pretty rough anyway...)
<barnabyw[m]>
ah yeah that’s a nice generic solution to my problem
<barnabyw[m]>
provided they’re always right-aligned within the i32
<cgc17[m]>
Hi all, I’m on the hunt for the “hello world” of how to program an interrupt. Any good beginner resources using rust specifically you are aware of? I’m using the nrf52833. I see all the pieces available in the PAC and HAL crates but not sure how to actually use them yet.