-
Notifications
You must be signed in to change notification settings - Fork 182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds DMA support for ADC1 #73
Conversation
It looks good in first view. I'll test it tomorrow . But can you modify it to support a sequence (array) of ADC channels, not a one? |
It looks like you measure voltage each adcclk cycle. See ADCx.CR2.EXTSEL + TIMx.CR2.MMS |
@TheZoq2 , @therealprof
Unsafe user fns for advanced manual configure if required functionality is not present in HAL yet? |
Right now, the only way I can think of is exposing a trait and trust the users to implement it, like we do with the pwm, which I think is totally fine, we just need to provide some documentation. However, there are probably others ways to achieve this, but either way I would like it to be like another with_dma method to keep the api simple for simple cases.
Not exactly at each adcclk cycle, it takes more for conversion, but I get what you are saying. Anyway, I would say it would be the same thing as I said above.
I think you can already do that with the ::ptr() method, and I also think they will make steal really "steal" things, see rust-embedded/svd2rust#238 and rust-embedded/cortex-m#147. |
I know, that I can use ptr (and I often do so). But it creates new instance, that leads to many bugs. This PR I approve to merge as it is. Maybe later I will try to extend this API. |
let (_buf, adc_dma) = adc_dma.read(buf).wait(); | ||
asm::bkpt(); | ||
|
||
let (_adc1, _adc_ch0, _dma_ch1) = adc_dma.split(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you please clarify what are these lines for ? Even more, maybe it worth adding short comments for all logical blocks in this example ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, now I also changed some of your adc code, please take a look to make sure I did not do something wrong, thanks.
To clarify my last commit: |
if two_adc_cycles > already_delayed { | ||
delay(two_adc_cycles - already_delayed); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to datasheet, calibration is recommended after each power-up. If so, then why don't we move all the delays into power_up() function rather than keeping several pieces in different places ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, that is a good point, but since it is just recommended I kinda did not want to enforce it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably didn't make myself clear. I mean, why don't we move that additional delay code into power_up function ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it would enforce that we delayed for calibration every time we power_up even when we don't want to calibrate it. Right now, it does not matter, since the methods are not public and we are powering up only once and we are calibrating. But maybe in some future implementation that could be different.
Anyway, I also think that we should calibrate after each power up, but I did not want to enforce it, otherwise I would just move the calibration code to power up and remove the calibration method.
self.power_down(); | ||
self.disable_clock(apb2); | ||
self.rb | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any reason why you don't want to return $ADC from this function ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, no, but it is returning ADC. isn't it ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, typo. Sorry for confusion. The question is "why do you want to return ADC from this function". I am asking not because I have any objections, I just want to understand the use-case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it is a singleton, I am returning it so people can use it to start the adc again if they want to.
@@ -375,9 +403,7 @@ macro_rules! adc_hal { | |||
type Error = (); | |||
|
|||
fn read(&mut self, _pin: &mut PIN) -> nb::Result<WORD, Self::Error> { | |||
self.power_up(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, we opt to sacrifice power consumption for accuracy here. Right ? Alternatively we could calibrate before each read, but that might be tooooo slow...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is correct, and if you don't want to calibrate you still would need to wait 1us. Moreover, that was the reason I created the release method, so if you want to save power you could use it only in the time you needed it, in contrast to have to wait 1us+ every conversion.
And as a side note, I think that the first time you would call the old method it would cause some problem, because the adc was already up, so if you set the ADON bit you would start a conversion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I confirm the observation regarding the first measurement in the previous approach. However with new approach (w/o power_up/power_down around convert) all the values look strange, not only the first one. At least this is the case for temperature sensor. I need to do more testing using voltage inputs...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did more tests with various ADC clock settings. It turns out that sampling settings for temperature sensor (suggested in my previous PR #67 ) should be modified as follows to work well with introduced delays:
diff --git a/src/adc.rs b/src/adc.rs
index b1a73c2..d4752a9 100644
--- a/src/adc.rs
+++ b/src/adc.rs
@@ -468,7 +468,7 @@ impl Adc<ADC1> {
2_400_001 ... 3_100_000 => AdcSampleTime::T_28,
3_100_001 ... 4_000_000 => AdcSampleTime::T_41,
4_000_001 ... 5_000_000 => AdcSampleTime::T_55,
- 5_000_001 ... 10_000_000 => AdcSampleTime::T_71,
+ 5_000_001 ... 14_000_000 => AdcSampleTime::T_71,
_ => AdcSampleTime::T_239,
};
Could you please meld this fixup into you PR ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thank you for helping out :)
LGTM. Thanks ! |
@@ -429,14 +461,14 @@ impl Adc<ADC1> { | |||
// recommended ADC sampling for temperature sensor is 17.1 usec, | |||
// so use the following approximate settings | |||
// to support all ADC frequencies | |||
let sample_time = match self.clk.0 { | |||
let sample_time = match self.clocks.adcclk().0 { | |||
0 ... 1_200_000 => AdcSampleTime::T_1, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...
syntax is deprecated in nightly. Use ..=
instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for catching ! Maybe I can do it it in a separate 'cleanup' PR ? Otherwise we are going to have too much unrelated things in this DMA PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks.
I also had to make some minor changes in the dma and serial files to better accommodate the adc, but they were non breaking, the examples remain the same and compile fine.
I would like @burrbull to take a look to make sure that I didn't make any changes that would have a bad effect on present or future implementations.
I tested the rx transfer on the stm32f103 and it seems that everything is working as expected. I will probably hard test the circular implementation too, but it should be working fine. I also fixed a typo in a comment on the RCC.