2021-06-21 11:57 PM
Hi,
I'm trying to read ADC value from multiple ADC channels but reading the same ADC value for all channels.
This is my code snippet. Please support to fix it asap.
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc;
/* USER CODE BEGIN PV */
uint16_t ADC_VAL[3];
uint8_t adc_index=0;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC_Init();
/* USER CODE BEGIN 2 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
ADC_VAL[1]=HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
sConfig.Rank = 0;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, HAL_MAX_DELAY);
ADC_VAL[2]=HAL_ADC_GetValue(&hadc);
sConfig.Rank = 0;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Stop(&hadc);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief ADC Initialization Function
* @param None
* @retval None
*/
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
2021-06-29 02:03 AM
Hello @VDutt.1
Thanks for you post,
Could you please give me more details about your problem?
Khouloud
2021-07-02 09:05 AM
Hi Khouloud,
I believe I am having the same problem. My board is a Nucleo-L031K6 and I am connecting 2 analog inputs. If I sample only one (either one) the ADC count is correct. When I try to sample both inputs the counts are not correct. I tried very similar code on a Nucleo-F401RE and it works. On the F401RE IOC configuration tool the analog setup has more options (number of conversions, rank, etc). These options do not appear for the L031K6 board. Any help is greatly valued. I have fought this for 2 days. My code that does not work for the L031K6 is attached:
Doug
2021-07-05 08:07 AM
Hi @VDutt.1 and @DJack.8
From your code samples, I see you do not use the same STM32 MCU, but your issue is similar.
Note for Doug: STM32L0 and STM32F4 ADC have not the same features: on STM32L0, channel rank in the sequence cannot be configured, it is fixed to channel number.
The issue is due to overwritten data: when a sequence of several ADC channels is configured, in default configuration, a ADC start event makes ADC converts all channels successively in burst. Therefore, data register is overwritten by each new conversion data. If you do not fetch each conversion data on time, you get the only last one.
I propose 3 solutions to your problem:
1. Use discontinuous mode
In this mode, ADC does not convert all channels in burst but requires a ADC start event for each channel.
Setting: "hadc1.Init.DiscontinuousConvMode = ENABLE;"
Sequence principle:
HAL_ADC_Start(hadc1);
HAL_ADC_PollForConversion(&hadc1, <timeout>);
data_conversion_1 = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Start(hadc1);
HAL_ADC_PollForConversion(&hadc1, <timeout>);
data_conversion_2 = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(hadc1);
2. Poll for each conversion (not recommneded)
Not recommended, because of your CPU is busy at another task, then you will miss some conversion data.
Sequence principle:
HAL_ADC_Start(hadc1);
HAL_ADC_PollForConversion(&hadc1, <timeout>);
data_conversion_1 = HAL_ADC_GetValue(&hadc1);
<do not process anything else here !!!>
HAL_ADC_PollForConversion(&hadc1, <timeout>);
data_conversion_2 = HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(hadc1);
3. Use DMA transfer (recommended)
ADC converts all channels successively in burst and DMA transfers data in RAM (no CPU intervention).
Then user program can access all conversion data in a table: [data_conversion_1; data_conversion_2]
You can refer to examples in FW packages.
For example in STM32L0 FW package:
Using ADC LL driver:
...\Firmware\Projects\NUCLEO-L073RZ\Examples_LL\ADC\ADC_MultiChannelSingleConversion
Using ADC HAL driver:
...\Firmware\Projects\NUCLEO-L053R8\Examples\ADC\ADC_Sequencer
Best regards
Philippe
2021-07-05 03:31 PM
Does this work with f7xx and h7xx CPUs, too?
2021-07-06 12:43 AM
Yes, ADC configuration differs slightly due to specific features, but all STM32 series ADC operate on the same principle regarding sequencer and DMA transfer.
2021-07-06 12:37 PM
Hi Philippe,
I used your suggestion #1 and was able to sample two channels. However there are a couple of issues that concern me about the code. Below is my version of suggestion #1.
Issue 1.
If I comment off the lines:
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) // required for code to sample both channels, why?
{
Error_Handler();
}
it stops sampling both channels. Likewise if I place a:
HAL_ADC_Stop(&hadc);
in the channel 0 section it stops sampling both channels even though I restart HAL_ADC later.
I attached the complete code as file. I am running this on a Nucelo-L031K6 board with the STM32L031.
Thanks for your help,
Doug
while (1)
{
HAL_Delay(1000);
ADC_ChannelConfTypeDef sConfig = {0};
// sample channel 0
sConfig.Channel = ADC_CHANNEL_0;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) // required for code to sample both channels, why?
{
Error_Handler();
}
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 1000);
ADC_VAL[0] = HAL_ADC_GetValue(&hadc);
// HAL_ADC_Stop(&hadc); // A Stop here causes a fail at sampling both channels, why?
// send count to UART
strcpy((char*)buf, "ADC CH0 = ");
HAL_UART_Transmit(&huart2, buf, strlen((char*)buf), HAL_MAX_DELAY);
sprintf((char*)buf2,"%u \r\n", ADC_VAL[0]);
HAL_UART_Transmit(&huart2, (uint8_t*)buf2, strlen(buf2), HAL_MAX_DELAY);
//HAL_ADC_Stop(&hadc);
HAL_Delay(250);
// sample channel 3
sConfig.Channel = ADC_CHANNEL_3;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 1000);
ADC_VAL[2] = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
// send count to UART
strcpy((char*)buf, "ADC CH3 = ");
HAL_UART_Transmit(&huart2, buf, strlen((char*)buf), HAL_MAX_DELAY);
sprintf((char*)buf2,"%u \r\n", ADC_VAL[2]);
HAL_UART_Transmit(&huart2, (uint8_t*)buf2, strlen(buf2), HAL_MAX_DELAY);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_39CYCLES_5;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = ENABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}