cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L475V: Using ADC without HAL fails miserably

EThom.3
Senior II

I am officially confused.

I'm trying to use an ADC without the HAL layer, and for some reason I just don't get it to work. With HAL, it works fine.

I have tried comparing register contents, and I've now run into a wall. There must be something the HAL function is doing that I am missing or misunderstanding.

In this test, I am trying to sample ADC2, channel 16, just once, without DMA or anything. With my own code, ADCSTART never goes low after I've started the ADC.

When I check what the HAL code does differently, I simply don't get it.

The HAL setup code (which works):

      ADC2Handle->Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
      ADC2Handle->Init.ContinuousConvMode = DISABLE;
      ADC2Handle->Init.DMAContinuousRequests = DISABLE;
      ADC2Handle->Init.DataAlign = ADC_DATAALIGN_RIGHT;
      ADC2Handle->Init.DiscontinuousConvMode = DISABLE;
      ADC2Handle->Init.EOCSelection = ADC_EOC_SINGLE_CONV;
      ADC2Handle->Init.ExternalTrigConv = ADC_SOFTWARE_START;
      ADC2Handle->Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
      ADC2Handle->Init.LowPowerAutoWait = DISABLE;
      ADC2Handle->Init.NbrOfConversion = 1;
      ADC2Handle->Init.NbrOfDiscConversion = 0;
      ADC2Handle->Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
      ADC2Handle->Init.OversamplingMode = DISABLE;
      ADC2Handle->Init.Resolution = ADC_RESOLUTION_12B;
      ADC2Handle->Init.ScanConvMode = ADC_SCAN_ENABLE;
      HAL_ADC_Init(ADC2Handle);

      ChannelCfg.Channel = 16;
      ChannelCfg.Offset = 0;
      ChannelCfg.OffsetNumber = ADC_OFFSET_NONE;
      ChannelCfg.Rank = ADC_REGULAR_RANK_1;
      ChannelCfg.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
      ChannelCfg.SingleDiff = ADC_SINGLE_ENDED;
      HAL_ADC_ConfigChannel(ADC2Handle, &ChannelCfg);
      HAL_ADC_Start(ADC2Handle);

In short, I just want to sample channel 16 once, with 12 bit resolution and 640.5 cycles.

The main registers I have checked are these:

CR = 0x10000005 (ADC regulator on; regular conversion started; ADC enabled)

CFGR = 0x80001000 (Injected queue disabled; overrun mode)

CFGR2 = 0 (Makes sense, as I don't use oversampling)

SMPR1 = 0x00000007 (This confuses me a lot. Doesn't this just set channel 0 sampling time to 640.5 cycles?

SMPR2 = 0 (... and why is this 0? Shouldn't it have been used for setting the channel 16 sampling time?)

SQR1 = 0 (Even more confusing. Surely, this should be pointing at channel 16 as the one and only channel in the sequence. Instead, it points at channel 0.)

I have also checked that the DMA channel if off, as I don't use DMA for this single sample:

DMA2->CCR4 = 0x00000580 (The main thing here is that the DMA channel is off. Which it should be.)

 

I would have expected SMPR2 to be (at least) 0x001C0000, to set the channel 16 sampling time to 640.5 cycles. Also, I expected SQR1 to be 0x00000400, to indicate channel 16 for the first and only conversion. Instead, both registers read 0, which I simply don't understand.

Have I completely misunderstood how the ADC works? When I read the manual, the SMPRx registers contain the sample times for each channel (regardless of sequence), and the SQRx registers are used for specifying the sample sequence and the number of conversions.

Can anytone shed some light on this?

31 REPLIES 31

Not quite.

What I meant by non-HAL code was writing directly to the registers, such as

ADC2->SQR1 = (7 << ADC_SQR1_SQ2_Pos) | (6 << ADC_SQR1_SQ1_Pos) | 1;

Strangely, I prefer to do this, rather than using the HAL functions.

But my point of the whole thread was actually why the HAL function were filling the registers with junk I didn't understand. That has been sorted out now.

LCE
Principal II

Strangely, I prefer to do this, rather than using the HAL functions.

:D Absolutely not strange!

We should not forget that HAL is written for at least whole controller families, and for all the features which many users don't need, plus the assert stuff, and many (IMO) unnecessary macros for register access.

All that makes some parts of HAL hard to understand / get through.

As a colleague once said: it's ugly.

 

Thank you! Your comment makes me feel less weird. :smiling_face_with_smiling_eyes:

By the way, next time I run into a similarly weird issue, I'll try to remember to do something else to clear my mind before flooding the ST community with incoherent rants.

LCE
Principal II

I think that too many people use HAL without understanding what's actually going on in the STM32.

So for using registers you must understand what's going on, RTFM is a must! ;)

HAL-using people fall on their faces as soon as something's not working as it should, or if something cannot be solved by using HAL.

I'm responsible for the (industrial testing) products I develop, I must understand 100% how it's working, to make sure it's reliable, and in case of problems being able to debug and improve it.

So when using HAL, I make sure that a) I couldn't do it better / faster / more reliable (e.g. some clock & DMA setup for me), or b) the HAL part is not "important", or c) it's a quick way to bring a peripheral up for 1st tests.

And I make sure that the HAL sources I use are not updated automatically (I don't get how one can be so *** careless).

And one should not forget the often incomprehensible concepts and restrictions the Cube framework imposes on the user. Like sequestering the SysTick interrupt for delay loops.
Leaving aside the average quality and robustness of example code, I just don't see the point in installing another 1+GB vendor-specific development environment, especially in a professional environment working with several vendors and architectures.

I understand that ST wants to attract unexperienced students and hobbyist as future customers with a simple "click and drag" GUI. However, I might remind of one of Murphy's laws here, which states :
Build a system that even a f.o.o.l  can use and only a f.o.o.l will want to use it.

I "grew up" with the Atmel 8-bit AVR microcontrollers and command-line GCC. No helpful HAL-like stuff whatsoever. The way forward was the reading the datasheet/manual and writing hex-codes directly to registers.

I guess it is kinda in my blood, so when I in desperation succumb to using HAL functions, I feel that I am becoming an Arduino amateur.

That said, in many STM32s, the clock setup is massive, and getting through it correctly without using the graphic clock configurator is pretty hardcore.

I see, we are going off on a tangent here ... ;)
It was the Z80 for me, when it was still state of the art.

But as a matter of fact that all community members here with a decent experience agree that the ability to read and understand technical manuals (like datasheets & user manuals) is crucial for engineering. Encouraging someone to skip this step is detrimental, I think.

> That said, in many STM32s, the clock setup is massive, and getting through it correctly without using the graphic clock configurator is pretty hardcore.

Which is a good use case for dissecting a provided example, even a Cube one.
The old SPL I continue to use is much less polluted, easier to understand and to strip down. The CMSIS-based core setup was much less polluted by vendor- or OS specific frameworks like Cube.

I only managed to setup the clock for a C031 manually, which is relatively simple.
For other STM board I either use old SPL code as template, or NuttX for the newer ones.

HAL adc driver code works perfectly well, it's hardly rocket science. It just writes to the relevant registers to configure and activate ADC. If you're going to do more advanced stuff and want to know how every line of code works you might spend a lot of time faffing around re-inventing the wheel. I'd suggest choosing your battles and this one isn't worth the effort.

I had made a stupic mistake elsewhere, which made the ADC not work.

As you write, it isn't rocket science, and I actually thing that writing dirctly to registers is often less cumbersome than using HAL functions.

However, in this case I have reverted to HAL before discovering my mistake.

Odd tangents are most welcome. Especially when the problem has been solved.

Z80. To me, that's old-school (no offence intended). At my first job after the university, they were in the process of replacing the old Z80-based boards with something more recent. So I've never had the pleasure of writing software for those.