Some things you could try:
Check that the clock for the ADC is enabled -- so in the SystemClock_Config()
functionlook for the line that sets PeriphClkInit.PeriphClockSelection
and make sure it contains RCC_PERIPHCLK_ADC
.
Check that the frequency of the clock for the ADC is acceptable -- use STM32CubeMX and look at the "Clock Configuration" tab and make sure it isn't throwing up any errors.
Your code is running the ADC as a one-shot, but is configuring it for continuous sampling -- hadc1.Init.ContinuousConvMode = ENABLE;
. It may be worth setting this to DISABLE
instead.
Old answer left here for posterity:
I'm seeing the same issue (HAL_ADC_Start() returning HAL_ERROR), although with a PlaformIO + STM32CubeMX build. In this case PlatformIO builds the CMSIS code and the CubeMX HAL driver code separately into two libraries: libFrameworkCMSISDevice.a and libFrameworkHALDriver.a.
In the libFrameworkHALDriver library there exist a number of functions that are defined twice, once with weak linkage, and a second time with strong linkage.
For me, the final link produces output that only has the weak version of many of these functions. For the HAL_ADC_Start() function in question it only has the weak version, which does nothing and returns HAL_ERROR, which is the symptom reported in the question.
Looking into this some more it appears that when linking static libraries, the linker doesn't override weak symbols seen early in the link with strong ones seen later. E.g. See this discussion: https://stackoverflow.com/a/37191811/1770902.
Thus if this is what is causing your issue solution would appear to be to put the files with the strong symbols earlier in the link than the weak symbols they should override.
In my case I haven't yet figured out how to fix this issue as there doesn't appear to be any way to influence the order in which the PlatformIO library builds have included the object files.
The analog and digital pin numbers are not the same.
PB3 is analog pin 3, that's why it works.
PB2 is analog pin 1.
PB4 is analog pin 2.
Best Answer
The nice thing about AVR is code ports very easily from processor to processor. I have a set of functions on file for setting up the peripherals.
Here's my ADC initialization function:
The sei() function serves as a global interrupt enable, and only needs to be called once if multiple interrupts are being used.
The ADMUX register has the ADC set up with the internal 2.56V reference, the conversion result left adjusted so the most significant bits are in ADCH, and converting ADC0, single ended.
The ADSRA register has the ADC enabled, in interrupt mode, and sets the prescaler to \$\frac{f_{osc}}{16}\$. This register also houses the conversion complete interrupt flag.
This line starts the conversion. It's important to note that the ADC is not free running. A new conversion must be started after each conversion.
My bare bones interrupt service routine code:
The most significant part of the result is in ADCH. ADCL contains the least significant bits. Read from ADCH as necessary. If you have multiple channels you're converting single ended, this is a good place to handle the multiplexing. For example:
I strongly recommend reading the datasheet to understand the operation of the ADC before you start writing code. Atmel's datasheets are pretty good, and offer good explanations and some sample code.