2025-05-23 3:19 AM
Hello !
I have to admit that I am stumped on this problem, I've been trying to make it work for the past few days.
My issue is with an STM32F301K8T6 (LQFP32) and its ADCs : I have two reflective optical sensors (ITR8307) that are connected to ADC_CHANNEL_12 and ADC_CHANNEL_11. The value read with an oscilloscope are good (they are read directly on the pin so it isn't a soldering issue), they are going from roughly 3.3V to 0.6V.
The issue is that the value read from the code doesn't really change, it gives me some random value on startup and oscillates around it, almost as if it was floating. I have tried polling for a single channel, scan mode, DMA, setting the sample time very high and very low, but nothing works... The impedance of the circuit is pretty low (a few kOhms at most), and it works perfectly with an ESP32.
All of the VDD and VSS pins are reading the correct values, however one thing I've noticed is the value given by the ADCs are shifting when I apply 0V or 3.3V to the PA1 pin. This pin is just the ADC_CHANNEL_2 so I don't really why it would be the case...
I would be very grateful for any advice or idea !
Below is the code that I've used for initializing the ADC and the main loop.
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = ENABLE;
hadc1.Init.NbrOfDiscConversion = 1;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_181CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_11;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
while (1)
{
uint16_t rawValues[2];
char msg[20];
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
rawValues[0]=HAL_ADC_GetValue(&hadc1);
rawValues[1]=HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
sprintf(msg, "Val : %hu \r\t\t", rawValues[0]);
HAL_UART_Transmit(&huart1, (uint8_t *) msg, strlen(msg), HAL_MAX_DELAY);
sprintf(msg, "Val2 : %hu \r\n", rawValues[1]);
HAL_UART_Transmit(&huart1, (uint8_t *) msg, strlen(msg), HAL_MAX_DELAY);
HAL_Delay(1000);
}
}
Solved! Go to Solution.
2025-05-23 5:39 PM
As @TDK , you should use the DMA for multiple channels.
See attached project. It uses the DMA in circular mode and Timer15 to periodically start the ADC conversion.
You can set a ready flag from the ADC callback. Then check the flag in the main while loop and print the results.
#include "main.h"
#define ADC_DATA_SIZE 2
uint16_t rawValues[ADC_DATA_SIZE];
volatile bool adc_rdy = false;
// called before main while loop
void PollingInit(void)
{
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)rawValues, ADC_DATA_SIZE);
}
// called from inside main while loop
void PollingRoutine(void)
{
char msg[20] = {0};
if(adc_rdy)
{
adc_rdy = false;
sprintf(msg, "Val : %hu \r\t\t", rawValues[0]);
HAL_UART_Transmit(&huart1, (uint8_t *) msg, strlen(msg), 100);
sprintf(msg, "Val2 : %hu \r\n", rawValues[1]);
HAL_UART_Transmit(&huart1, (uint8_t *) msg, strlen(msg), 100);
HAL_Delay(1000);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc == &hadc1)
{
adc_rdy = true;
}
}
2025-05-23 4:04 AM
Hi,
1. missing calibration - do calibrate first. ->
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
2. try only 1 rank and no continuous setting or anything, just plain normal 1 conversion. Check, if correct values then.
2025-05-23 7:15 AM
Use DMA to convert multiple values. The ADC does not buffer data like you are expecting here.
2025-05-23 12:59 PM - edited 2025-05-23 12:59 PM
Thank you for the quick answers !
Unfortunately, neither of them worked : the sensor is pulled up, but the value read by the code below is about 250, fluctuating between 170 and 310 (even though the voltage seems to be a stable 3V). Here is the code that I have tried :
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_12;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_7CYCLES_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
I am not using any external oscillator, but maybe this is the issue ? The ADC1 seems to be connected to HSI and run at 16 MHz based on what CubeMX is showing...
Thank you so much for taking the time to help me and have a good day !
2025-05-23 1:18 PM
Are the pins initialized as Analog Input mode in GPIO_Init?
Are the necessary peripheral clocks enabled?
2025-05-23 1:37 PM
Thanks for the input ! I have tried to set the pin as an analog pin with the following code :
void configureAnalogInput(GPIO_TypeDef* port, uint16_t pin) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = pin;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(port, &GPIO_InitStruct);
}
// [in main]
configureAnalogInput(GPIOB, GPIO_PIN_1);
It still read the same values as before unfortunately...
For the clock sources, I attached a screenshot from CubeMX, and I have the following line in my system clock initialization :
PeriphClkInit.Adc1ClockSelection = RCC_ADC1PLLCLK_DIV1;
2025-05-23 2:11 PM
And the GPIOB clock is enabled?
2025-05-23 2:16 PM
I have to say that I'm not very familiar with clocks yet, but there's the following line in MX_GPIO_Init (which is called in the main function) :
__HAL_RCC_GPIOB_CLK_ENABLE();
2025-05-23 3:58 PM
Tie the pin to gnd instead. Does it read 0? Tie it to VDD. Does it read max?
if not, pin is not hooked up as you think it is.
if yes, pin is not being driven as you expect.
2025-05-23 5:39 PM
As @TDK , you should use the DMA for multiple channels.
See attached project. It uses the DMA in circular mode and Timer15 to periodically start the ADC conversion.
You can set a ready flag from the ADC callback. Then check the flag in the main while loop and print the results.
#include "main.h"
#define ADC_DATA_SIZE 2
uint16_t rawValues[ADC_DATA_SIZE];
volatile bool adc_rdy = false;
// called before main while loop
void PollingInit(void)
{
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*)rawValues, ADC_DATA_SIZE);
}
// called from inside main while loop
void PollingRoutine(void)
{
char msg[20] = {0};
if(adc_rdy)
{
adc_rdy = false;
sprintf(msg, "Val : %hu \r\t\t", rawValues[0]);
HAL_UART_Transmit(&huart1, (uint8_t *) msg, strlen(msg), 100);
sprintf(msg, "Val2 : %hu \r\n", rawValues[1]);
HAL_UART_Transmit(&huart1, (uint8_t *) msg, strlen(msg), 100);
HAL_Delay(1000);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc == &hadc1)
{
adc_rdy = true;
}
}