<dirbaio[m]1> means write 1 to DMA_SxCR.EN
<dirbaio[m]1> and probably configure the other bits correctly if it's not another step
<firefrommoonligh> Oh... So that's the channel config steps?
<firefrommoonligh> setting the channel, periph and mem addresses, priority, incr etc?
<firefrommoonligh> (And ccr enable)
<dirbaio[m]1> I guess yea
<firefrommoonligh> Given that's missing from the steps -gotta be. Thank you
<firefrommoonligh> `read_dma` sets `ADC_CFGR_DMAEN`, sets `CFGR_DMACFG` to oneshot, and configures/enables the DMA channel
<firefrommoonligh> Did I miss anything?
<firefrommoonligh> * `read_dma` sets `ADC_CFGR_DMAEN`, sets `CFGR_DMACFG` to oneshot, starts a single-length conversion, then configures/enables the DMA channel
<firefrommoonligh> * `read_dma` sets `ADC_CFGR_DMAEN`, sets `CFGR_DMACFG` to oneshot, starts a single-length conversion, then configures/enables the DMA channel, pointing to the `sr` register
<dirbaio[m]1> got full code? For this one, or spi
<firefrommoonligh> Sure do!
<firefrommoonligh> Focusing on ADC for now since it's simpler (Although not as explicitly documented)
<firefrommoonligh> What I posted was the user-side code
<firefrommoonligh> One-shot ADC/DMA reading
<firefrommoonligh> (Although it seems like ADC via DMA really shines with sequences, I'm going for minimum working solution for now)
<dirbaio[m]1> which chip is it?
<firefrommoonligh> L4
<firefrommoonligh> (Btw, I think the dma module code is correct, and it's a macro mess, so probably not worth lookint at)
<firefrommoonligh> * (Btw, I think the dma module code is correct, and it's a macro-and-match mess, so probably not worth lookint at)
<dirbaio[m]1> here you may have to loop to wait for the channel to stop
<dirbaio[m]1> writing EN=0 takes a bit of time, you ahve to loop reading until EN changes to 0 afterwards
<firefrommoonligh> (I think putting `start_conversion` in there, btw, is wrong, since that blocks)
<dirbaio[m]1> but if it's not already running it's probably not that (?)
<firefrommoonligh> Good point
<firefrommoonligh> Going to add that waiting-to-stop code
<firefrommoonligh> (Although currently, the user needs to stop it manually, but that's still a good guard)
<dirbaio[m]1> isn't the direction wrong?
<firefrommoonligh> Oh shit - sure is
<firefrommoonligh> Great find
<firefrommoonligh> There's still something going on, but I appreciate those 2 fixes. The direction issue would certainly have caused this as well
<dirbaio[m]1> still nothing? :o
<firefrommoonligh> Nope
<firefrommoonligh> I'm missing something fundamental or obvious with DMA
<firefrommoonligh> It's just not doing anything
<dirbaio[m]1> after starting the dma, does the EN bit change to 1->0 ?
<dirbaio[m]1> that'd indicate DMA has done something and has finished
<dirbaio[m]1> there's also a dma error bit, you could check that
<firefrommoonligh> Awesome. I've stepped away, but will try those, and see what other bits are setting or not setting
<dirbaio[m]1> also I think you're reading from buf before waiting for dma to finish
<dirbaio[m]1> also buf should be a &mut instead of a &
<dirbaio[m]1> maybe you're seeing evil compiler optimizations
<dirbaio[m]1> and to be safe from compiler optimizations you maybe need `compiler_fence(Ordering::SeqCst)` before and after DMAing
<firefrommoonligh> Could be that too. I tried waiting for periph and dma transfer complete flags, but those would hang
<firefrommoonligh> Good catch on buf. I may have done something wrong re the `embedded_dma` traits there
<firefrommoonligh> I'll try the fencing next, as well as trying to read bits like the ones you proposed, and see what's taking and what's not
<firefrommoonligh> Goal: Make DMA the default API for comm protocols, ADC, and DACs, on my current and future projects, and public HAL
<firefrommoonligh> * Goal: Make DMA the default API for comm protocols, ADC, and DACs, on my current and future projects, and public HAL code and examples
<firefrommoonligh> Relegating non-DMA to EH-compatibility for generic drivers
<dirbaio[m]1> embedded_dma only gives you safety if you own the `B` btw
<firefrommoonligh> I have a lot of open questiosn about how embedded_dma etc works, but am hoping to save that for after I get the basics working
<firefrommoonligh> And figure out what should go in the DMA module vice HAL periph vice user code (and examples)
<firefrommoonligh> On the plus side, all this RM spellunking's let me clean up the ADC module and add functionality to it
<firefrommoonligh> Even if I'm striking out on DMA
<henrik_alser[m]> Yeah your read would take an owned WriteBuffer
<henrik_alser[m]> (+’static)
<firefrommoonligh> Oh?
<firefrommoonligh> `embedded_dma::WriteBuffer` for the ADC read?
<firefrommoonligh> Super WIP
<henrik_alser[m]> Yes it refers to the buffer as being ”writable”
<firefrommoonligh> but am hoping it will work in a memory-fragile way for now while I config the hardware
<firefrommoonligh> I have the 2 structs almost identical
<dirbaio[m]1> firefrommoonlight: i'd suggest you first get it working with `&mut [u8]` instead of embedded_dma :)
<dirbaio[m]1> * firefrommoonlight: i'd suggest you to first get it working with `&mut [u8]` instead of embedded_dma :)
<firefrommoonligh> Good point
<firefrommoonligh> I'm going with that once I get back on my main comp
<firefrommoonligh> I'll try to add embedded_dma later. Great idea
<dirbaio[m]1> also using `&mut [u8]` is fully sound DMA as long as you only have blocking APIs
<dirbaio[m]1> * also using `&mut [u8]` is fully sound for DMA as long as you only have blocking APIs
<henrik_alser[m]> Here’s an example how i implemented it for i2c in nrf-hal of this is of any help https://github.com/nrf-rs/nrf-hal/blob/a8dddf1314ccf731d7b8a8234f8dd51e0f55dc7a/nrf-hal-common/src/twis.rs#L396
<dirbaio[m]1> hmm it should use ` B: StaticWriteBuffer<Word = W>` though
<dirbaio[m]1> instead of ` B: WriteBuffer<Word = W> + 'static,`
<henrik_alser[m]> You can also check the spis, i2s and pwm modules in there
<henrik_alser[m]> Yes it was made before StaticWriteBuffer existed :P
<dirbaio[m]1> ah right =D
<henrik_alser[m]> So should be updated i guess, but at the moment they’re the same
<dirbaio[m]1> and there was the idea of only having StaticXBuffer
<dirbaio[m]1> because people get confused
<dirbaio[m]1> which I left to rot, oops 😅
<henrik_alser[m]> Haha, good idea though!
<dirbaio[m]1> `B: WriteBuffer` is almost always unsound
<dirbaio[m]1> `B: WriteBuffer + 'static` and `B: StaticWriteBuffer` are exactly the same
<dirbaio[m]1> except with `StaticWriteBuffer` you can use a non-static buffer with "only" unsafe
<dirbaio[m]1> instead of super-dangerous transmutes
<firefrommoonligh> Sweet example henrik_alser Could certainly apply the memory-management parts to stm
<henrik_alser[m]> I guess one could come up with some use case where a stack allocated buffer would be safe but
<dirbaio[m]1> well the idea was to let you use stack-buffers with unsafe
<dirbaio[m]1> which is totally fine if you don't leak the Transfer
<henrik_alser[m]> Yeah
<firefrommoonligh> I think the Rust embedded world is going places, dudes. So much progress in several areas over the past ~1 year
<firefrommoonligh> It's rapidly changing for the better
<dirbaio[m]1> with `+ 'static` you can't use stack-allocated buffers at all
<dirbaio[m]1> unless you do this
<dirbaio[m]1> ☠️
<dirbaio[m]1> which is so dangeorous it also needs an `#[allow]` 🤣
<henrik_alser[m]> #[yolo]
<GrantM11235> Why does it need to be mutable?
<dirbaio[m]1> 🤔
<dirbaio[m]1> ...no idea
<henrik_alser[m]> The buffer?
<dirbaio[m]1> Transfer always requires write access even if it's just going to read from it (?)
<henrik_alser[m]> In the spi case it will overwrite it though
<dirbaio[m]1> yea but that's not SPI 🤣
<dirbaio[m]1> okay so the #[allow] is needed due to this quirk in the API
<dirbaio[m]1> * okay so the #[allow] is needed due to this quirk in the API, not due to embedded_dma
<dirbaio[m]1> oh and f4xx-hal does use StaticWriteBuffer so maybe the transmute wouldn't be needed otherwise
<dirbaio[m]1> oh well
<dirbaio[m]1> master no longer uses f4xx-hal anyway
<henrik_alser[m]> <dirbaio[m]1 "yea but that's not SPI 🤣"> Ah i thought of they all used the same and it had to be a worst case scenario
<dirbaio[m]1> Transfer is shared, that may be why yup
<dirbaio[m]1> I don't think Transfer will do bidirectional transfers in f4xx-hal SPI though
<dirbaio[m]1> it'll read or write, not both
<firefrommoonligh> So... SPI and DMA: GIven this concern you brought up, what's the process to handle it via DMA?
<dirbaio[m]1> so the API could accept a readonly buf
<firefrommoonligh> Since I think you need to read and write alternating every byte in spi?
<dirbaio[m]1> * so the API could accept a readonly buf for mem2peri transfers
<firefrommoonligh> It's not just an issue of writing/reading a whole buffer in order
<dirbaio[m]1> firefrommoonlight: with DMA you have to use 2 channels, one for rx and one for tx
<dirbaio[m]1> and probably 2 buffers?
<henrik_alser[m]> Oh ok! So no full duplex?
<firefrommoonligh> Like, to read, you write something (eg the address), then read, each byte
<dirbaio[m]1> I think you can't do full duplex with the current api, no
<firefrommoonligh> To write, I think you still generally do a read after (?)
<henrik_alser[m]> Usually you can use the same buffer since it will read and write on different edges
<henrik_alser[m]> But haven’t checked out how it works in this implementation
<dirbaio[m]1> no idea whether you can point both channels at the same buffer
<dirbaio[m]1> seems dangerous
<henrik_alser[m]> You can in some mcu families at least (like nrf)
<dirbaio[m]1> and it's useful to be able to use separate buffers anyway
<dirbaio[m]1> maybe you want to tx some data without trashing it because you need it later for something else
<henrik_alser[m]> Unless they’re huge:)
<firefrommoonligh> Thank you for the SPI info
<firefrommoonligh> That's outstanding
<firefrommoonligh> In the blocking APIs I've been using, you're doing constant alternating writes and reads, even to just write a buf
<dirbaio[m]1> yeah these are ultra weird
<firefrommoonligh> Like, the HAL will impl a EH `send` and `read` from EH, and impl default eh::FUllduplex, which blocks and alternates the two
<firefrommoonligh> This is why I'm focusing on ADC DMA for now!!
<firefrommoonligh> I'd been using offboard ADCs in my projects, but STM32 ADC seems full-up
<firefrommoonligh> Unrelated: Why do the VREF pins only exist on high-pin packages?
<firefrommoonligh> If you want good ADC accuracy, can you just hook up a voltage ref to the VDDA and VSSA pins?
<firefrommoonligh> What's the effective difference between that and the big-package VREF?
<firefrommoonligh> Is the dif that VDDA ha to be close to VDD, but VREF can be diff?
<firefrommoonligh> And otherwise either is fine?
<henrik_alser[m]> Vdda is the power supply for the adc, dac, rc oscillator, plls etc, Vref+/- are just the reference voltage for the ADC so can be connected to something higher precision or low voltage for better precision at low voltages for example
<henrik_alser[m]> On the low count packages it's just internally connected to Vdda
<henrik_alser[m]> So if you don't need other precision than that you can just connect them to Vdda/Vssa yeah
<henrik_alser[m]> * Vdda is the power supply for the adc, dac, rc oscillator, plls etc, Vref+/- are just the reference voltage for the ADC/DAC so can be connected to something higher precision or low voltage for better precision at low voltages for example
<henrik_alser[m]> If Vdda < 2.4V Vref must be equal to Vdda however
<henrik_alser[m]> And if Vdda >= 2.4V Vref can be in the range from 2.4V to Vdda
<henrik_alser[m]> And yeah Vdda must be equal to Vdd btw
<henrik_alser[m]> And Vssa to Vss
<firefrommoonligh> I appreciate it!
<firefrommoonligh> It's surprising this feature is package-specific. I'm guessing there's no way to MUX it like they do with peripheral pins
<firefrommoonligh> Another unrelated Q: It looks like SPI can hardware-manage CS pins. The pin state is set by hardware automatically. It's set up as you do with the other pins, ie as an alt fn. Doesn't this defeat the purpose, eg switching between multiple devices? I could see how they might design this where you have a cfg field to choose one of several pins for a given write/read, but that's not how it's set up
<firefrommoonligh> So, seems... useless?
<firefrommoonligh> Maybe it's for when you only have one device? Seems like a good fit there
<henrik_alser[m]> <firefrommoonligh "It's surprising this feature is "> No i think a gpio frontend would sacrifice too much in terms of precision, noise and temperature stability to be useable for a ref input
<firefrommoonligh> But for that many devices could just hard set it low. Although some need it to go high between writes
<firefrommoonligh> That makes sense!
<henrik_alser[m]> Yes it’s for single device use only, can be handy as you don’t have to be juggling around the cs pin as a separate resource
<firefrommoonligh> Makes sense!
<firefrommoonligh> (And for devices where you can't get away with hard-setting CS low)
<firefrommoonligh> Hmm... DMA en is being set
<firefrommoonligh> * Hmm... DMA en is being set and cleared. It shows set it I read immediately after commanding the xfer. Then shows clear on subsequent checks. So, this appears to be working. Still no updates to the buf though, after adding the fencing and changing it to a &mut write buf.
<thalesfragoso[m]> <dirbaio[m]1 "Transfer always requires write a"> Yeah, this was an initial work, I have a PR for not requiring for both
<thalesfragoso[m]> Which reminds me that I need to fix conflicts
<firefrommoonligh> Oh shit! dirbaio strikes again. The transfer error flag is set
<thalesfragoso[m]> firefrommoonlight: ADC should use u16, no ?
<firefrommoonligh> Yep
<thalesfragoso[m]> Alignment might also be a problem
<firefrommoonligh> *actually that's for non-DMA code only, nvm,
<firefrommoonligh> *Tangent: Thoughts on my `read_volatile` comment above? Note that just using the PAC interface works fine (non-DMA)
<thalesfragoso[m]> why you're writing both address to cpar ?
<thalesfragoso[m]> * why are you writing both address to cpar ?
<thalesfragoso[m]> * why are you writing both addresses to cpar ?
<firefrommoonligh> Sorry, error in my code merging
<thalesfragoso[m]> Also, buf should probably be of u16 instead of u8
<firefrommoonligh> `cmar` for the first line
<thalesfragoso[m]> Should also be `&self.buf[0]` not `self.buf[0]`
<thalesfragoso[m]> actually, better do self.buf.as_ptr()
<thalesfragoso[m]> * actually, better do `self.buf.as_ptr()`
<firefrommoonligh> `^^^^^^ method not found in `u8`` for `.as_ptr()`
<firefrommoonligh> Oh nvm!
<thalesfragoso[m]> you were passing the first element of the buffer (probably zero) as the memory address
<firefrommoonligh> `.as_ptr()` : `types differ in mutability`
<firefrommoonligh> `.as_mut()`: `expected `u8`, found slice `[u8]``
<thalesfragoso[m]> `as_mut_ptr`
<firefrommoonligh> Oh shit! That would explain this...
<firefrommoonligh> So... Improvement! That was definitely a critical error
<firefrommoonligh> DMA en is staying set, no errors, and the buffer is being written!
<firefrommoonligh> Although the reading is not quite right. Could be the earlier errors you pointed out
<henrik_alser[m]> Yeah check alignment too
<firefrommoonligh> I think the writes to cmar and cpar need to be 32 bits, since they're registesr
<firefrommoonligh> if using the PAC API
<firefrommoonligh> What would you change re alignment?
<firefrommoonligh> Each word as a u16 instead of u8?
<thalesfragoso[m]> The buffer should be of u16s or at least aligned to a 2 bytes boundary
<firefrommoonligh> Oh shit! Thanks
<firefrommoonligh> Changed the `embedde_dma` impls to generic instead of u8
<henrik_alser[m]> Or yeah 2
<thalesfragoso[m]> [u16; 8] is already aligned to 2
<firefrommoonligh> Oh sweet. I need to look up Size
<firefrommoonligh> * Oh sweet. I need to look up Sized
<henrik_alser[m]> True haha, some peripherals need 4
<henrik_alser[m]> Don't know about this one?
<firefrommoonligh> Switching to u16 still didn't make the readings right. Going to try the alignment
<firefrommoonligh> (This is still a huge progress from before)
<thalesfragoso[m]> are you configuring the correct memory size in the DMA registers ?
<firefrommoonligh> Oh uh... nope! Had set to U8
<firefrommoonligh> If the array is no longer bytes (ie u8), there is a complication
<firefrommoonligh> Maybe multiply it by 2 when using u16
<henrik_alser[m]> `len * core::mem::size_of::<W>();`
<GrantM11235> I think you are supposed to set the number of words, not the number of bytes
<thalesfragoso[m]> yeah, you set the number of transfers
<firefrommoonligh> Typo in RM?
<thalesfragoso[m]> so, still len
<thalesfragoso[m]> <firefrommoonligh "Typo in RM?"> I don't think so
<firefrommoonligh> Or maybe that was a SPI-specific note, where it assumes u8?
<thalesfragoso[m]> not sure, all ST's DMA I have worked use number of words in ndtr
<thalesfragoso[m]> * not sure, all ST's DMA I have worked use number of transfers in ndtr
<firefrommoonligh> Um bros, it's working
<firefrommoonligh> after that u16 change
<firefrommoonligh> in datasize field
<firefrommoonligh> Thank you so much
<dirbaio[m]1> 🚀
<firefrommoonligh> I don't have a good answer on wheather to multiply len * 2; both work in this case :/
<firefrommoonligh> Can't get the `core::mem::size_of` to work, without providing info on W
<GrantM11235> If you multiply by 2 it will corrupt the memory directly after the buffer
<firefrommoonligh> So, `DMA_CNDTR` is by word, not byte?
<dirbaio[m]1> which is nice because it prevents from dma-transfering Potato values or whatever which is likely UB :)
<firefrommoonligh> This note on the field implies word ("data items"): `It is decremented after each single DMA ‘read followed by write’ transfer, indicating
<firefrommoonligh> the remaining amount of data items to transfer`
<henrik_alser[m]> (Was thinking if you used the embedded-dma traits later)
<firefrommoonligh> Also, in a description of DMA in RM: `This register contains the remaining number of data items to transfer (number of AHB
<firefrommoonligh> ‘read followed by write’ transfers).`
<henrik_alser[m]> (with `StaticWriteBuffer<Word = W>`)
<firefrommoonligh> another vote for the size set in `psize` and `msize`
<firefrommoonligh> That's what I used most recently
<henrik_alser[m]> That's probably right! In nrf it's bytes however
<firefrommoonligh> *Note that `as_mut_ptr()` fix
<firefrommoonligh> I think the `total number of bytes` line was UART-specific
<henrik_alser[m]> So yeah if you're allowing stack buffers only in unsafe it should be alright i guess
<thalesfragoso[m]> Actually `WriteBuffer` is already ok for non 'static stuff and should work with `&'a mut [T] where T: Word` already
<thalesfragoso[m]> but `WriteBuffer` by itself isn't usually enough for a safe DMA API
<henrik_alser[m]> Yes that's what i meant :)
<henrik_alser[m]> * Ahh true
<firefrommoonligh> It seems like `StaticWriteBuffer` might be safer, but I don't understand the details
<thalesfragoso[m]> there are docs :)
<thalesfragoso[m]> Basically, if the thing isn't `'static` you can `mem::forget` the transfer and cause memory corruption if your repurpose the memory buffer
<thalesfragoso[m]> * Basically, if the thing isn't `'static` you can `mem::forget` the transfer and cause memory corruption if you repurpose the memory buffer
<henrik_alser[m]> (That's what i was referring to when i said i thought it was ok in unsafe like you did it above)
<henrik_alser[m]> But you could provide a safe alternative too with StaticWriteBuffer?
<firefrommoonligh> Sounds like an edge case, especially if you're not familiar with `mem::forget`!
<firefrommoonligh> Yeah - no downside in offering both
<firefrommoonligh> Other than a more complex API, since I guess you'd need to implement separate dma functions for each periph, which means 2 extra fns in comm protocols for read and write
<firefrommoonligh> unless you know of a better way
<firefrommoonligh> (I'm going to tackle SPI next using the buf fixes you, thalesfrago, and dirbaio made)
<firefrommoonligh> Current ADC DMA API:
<firefrommoonligh> Certainly more verbose than non-DMA
<firefrommoonligh> *error: buf is `&mut`