Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC - Page 1
Forum section: electronics / general

author: QWERTYQWQ
author_rank: Contributor
timestamp: November 24, 2024, 05:13:53 pm
content: Hello,I’m facing an issue with an STM32F103C8T6 microcontroller in my project, where I aim to read 4 NTC sensors. I'm seeking your help regarding a problem I haven't encountered before. Below, you can see the circuit schematic. This schematic was reverse-engineered from an existing NTC reading device available in the market. While the original system used an ATmega168P, my system is based on an STM32 microcontroller.Anyway, I built the circuit on a manufactured PCB. When no NTC is connected to the circuit, I expect the adc_averagevalues to be around 4096, but instead, I’m observing values around 2300. Similarly, connecting an NTC sensor to one pin affects the ADC readings of the other pins, causing their values to change as well.I’ve spent the entire weekend troubleshooting this perplexing issue. Below, I’m sharing the code for reference. Here, I’m running the STM32 ADC in poll mode and attempting to filter out noise by averaging the readings. While I’m able to achieve precise measurements with a single ADC channel, I’m struggling significantly when reading multiple channels, encountering various unexpected problems.Code:Code:static void MX_ADC1_Init(void){/* USER CODE BEGIN ADC1_Init 0 *//* USER CODE END ADC1_Init 0 */ADC_ChannelConfTypeDef sConfig = {0};/* USER CODE BEGIN ADC1_Init 1 *//* USER CODE END ADC1_Init 1 *//** Common config*/hadc1.Instance = ADC1;hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;hadc1.Init.ContinuousConvMode = ENABLE;hadc1.Init.DiscontinuousConvMode = DISABLE;hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;hadc1.Init.NbrOfConversion = 1;if (HAL_ADC_Init(&hadc1) != HAL_OK){Error_Handler();}/** Configure Regular Channel*/// sConfig.Channel = ADC_CHANNEL_4;// sConfig.Rank = ADC_REGULAR_RANK_1;// sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;// if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)// {// Error_Handler();// }// /** Configure Regular Channel// */// sConfig.Channel = ADC_CHANNEL_5;// sConfig.Rank = ADC_REGULAR_RANK_2;// if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)// {// Error_Handler();// }// /** Configure Regular Channel// */// sConfig.Channel = ADC_CHANNEL_6;// sConfig.Rank = ADC_REGULAR_RANK_3;// if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)// {// Error_Handler();// }// /** Configure Regular Channel// */// sConfig.Channel = ADC_CHANNEL_7;// sConfig.Rank = ADC_REGULAR_RANK_4;// if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)// {// Error_Handler();// }/* USER CODE BEGIN ADC1_Init 2 *//* USER CODE END ADC1_Init 2 */}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 */HAL_Delay(1000);/* 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_ADC1_Init();MX_RTC_Init();MX_SPI2_Init();MX_USART2_UART_Init();/* USER CODE BEGIN 2 */FlashIndex = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1); // SRAM memory kaydedilen NOR Flash index degeriHAL_ADCEx_Calibration_Start(&hadc1);HAL_UARTEx_ReceiveToIdle_IT(&huart2, RxData, 256);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */Run_LED();UpdateTemperature();}/* USER CODE END 3 */}void UpdateTemperature(void) {for(int i = 0; i < 4; i++) // 4 ADC kanalini sirayla okumak için döngü{// Kanali seçswitch(i) {case 0:ADC_CHANNEL4(); // 0. kanalbreak;case 1:ADC_CHANNEL5(); // 1. kanalbreak;case 2:ADC_CHANNEL6(); // 2. kanalbreak;case 3:ADC_CHANNEL7(); // 3. kanalbreak;}adc_sum = 0; // Kanal basina toplami sifirlafor(int j = 0; j < num_samples; j++) // Her kanal için 20 ölçüm al{HAL_ADC_Start(&hadc1); // ADC'yi baslatHAL_ADC_PollForConversion(&hadc1, 1); // Poll ADC'yi, timeout = 1msAD_RES = HAL_ADC_GetValue(&hadc1); // ADC sonucunu aladc_sum += AD_RES; // Her ölçümü toplamaya ekleHAL_ADC_Stop(&hadc1); // ADC'yi durdur//HAL_Delay(10); // Küçük bir gecikme ekle}adc_average = adc_sum / num_samples; // 20 ölçümün ortalamasini al}void ADC_CHANNEL4(void){ADC_ChannelConfTypeDef sConfig = {0};sConfig.Channel = ADC_CHANNEL_4;sConfig.Rank = ADC_REGULAR_RANK_1;sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}}void ADC_CHANNEL5(void){ADC_ChannelConfTypeDef sConfig = {0};/** Configure Regular Channel*/sConfig.Channel = ADC_CHANNEL_5;sConfig.Rank = ADC_REGULAR_RANK_1;sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}}void ADC_CHANNEL6(void){ADC_ChannelConfTypeDef sConfig = {0};/** Configure Regular Channel*/sConfig.Channel = ADC_CHANNEL_6;sConfig.Rank = ADC_REGULAR_RANK_1;sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}}void ADC_CHANNEL7(void){ADC_ChannelConfTypeDef sConfig = {0};/** Configure Regular Channel*/sConfig.Channel = ADC_CHANNEL_7;sConfig.Rank = ADC_REGULAR_RANK_1;sConfig.SamplingTime = ADC_SAMPLETIME_239CYCLES_5;if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK){Error_Handler();}}Thank you.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 24, 2024, 06:14:36 pm
content: First thing to discard is a too short adquisition time¡, so try the largest value instead 55 cycles.(Though 55 cycles shoulf be more than enough).This looks wrong, you have 4 channels, thus 4 conversions, not one.hadc1.Init.NbrOfConversion = 1;

author: QWERTYQWQ
author_rank: Contributor
timestamp: November 24, 2024, 06:51:32 pm
content: Actually, the cycle value is set to the maximum, which is ADC_SAMPLETIME_239CYCLES_5. The 55-cycle value you see is commented out, meaning it is not included in the code. That part was generated by CubeMX. In my implementation, I reinitialize the ADC for each channel as if I were performing a single ADC reading for each one.On a very popular website, I saw an example of multi-channel ADC reading implemented in a similar way, which seemed logical to me. In that example, hadc1.Init.NbrOfConversion was set to 1 (again, as if only one ADC reading was being performed).

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 24, 2024, 07:08:30 pm
content: My advice is to use DMA. Makes things so much better!I'm attaching a sample project, gets really simple using HAL.Only one interrupt is generated, when all the channels * number of samples were done by the DMA.In this example I'm using 4 channels, 16 samples per channel. When you get the adc flag, the entire 64 samples are done.Tested, working in my bluepill.

author: ajb
author_rank: Super Contributor
timestamp: November 25, 2024, 04:53:56 pm
content: It looks like you are configuring the ADC for scan mode with `hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE`. I'm not familiar with how the HAL functions interact with that mode, but could explain why channels seem to influence each other. The usual suspect for that is inadequate sampling time.

author: DavidAlfa
author_rank: Super Contributor
timestamp: November 25, 2024, 08:19:17 pm
content: FromRM0008:QuoteSo, you can't use Continuous mode without DMA, why?https://electronics.stackexchange.com/questions/504118/stm32-why-cant-i-use-scan-mode-in-interrupt-driven-adcQuoteI don't remember well, it's been a while, but it seems you can use Discontinuous mode (Which interestingly also enables Scan mode), making a controlled scan of the channels.Everytime you start the ADC, it converts a channel and skips to the next one. When all channels where done, it starts over.This would be the behavior for 4 channels:ADC_Start -> Converts ch1ADC_Start -> Converts ch2ADC_Start -> Converts ch3ADC_Start -> Converts ch4ADC_Start -> Converts ch1ADC_Start -> Converts ch2...Attaching an example (No interrupts, no DMA, fully poll-driven), also tested on my bluepill.