Author Topic: Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC  (Read 1030 times)

0 Members and 1 Guest are viewing this topic.

Offline QWERTYQWQTopic starter

  • Newbie
  • Posts: 9
  • Country: tc
Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC
« on: November 24, 2024, 05:13:53 pm »
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_average values 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: [Select]
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 degeri
HAL_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. kanal
                break;
            case 1:
                ADC_CHANNEL5(); // 1. kanal
                break;
            case 2:
                ADC_CHANNEL6(); // 2. kanal
                break;
            case 3:
                ADC_CHANNEL7(); // 3. kanal
                break;
        }   
       
        adc_sum[i] = 0; // Kanal basina toplami sifirla
       
        for(int j = 0; j < num_samples; j++) // Her kanal için 20 ölçüm al
        {
            HAL_ADC_Start(&hadc1); // ADC'yi baslat
            HAL_ADC_PollForConversion(&hadc1, 1); // Poll ADC'yi, timeout = 1ms
            AD_RES = HAL_ADC_GetValue(&hadc1); // ADC sonucunu al
            adc_sum[i] += AD_RES; // Her ölçümü toplamaya ekle
            HAL_ADC_Stop(&hadc1); // ADC'yi durdur
            //HAL_Delay(10); // Küçük bir gecikme ekle
        }

       adc_average[i] = adc_sum[i] / 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.
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6331
  • Country: es
Re: Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC
« Reply #1 on: November 24, 2024, 06:14:36 pm »
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;
« Last Edit: November 24, 2024, 07:07:53 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 

Offline QWERTYQWQTopic starter

  • Newbie
  • Posts: 9
  • Country: tc
Re: Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC
« Reply #2 on: November 24, 2024, 06:51:32 pm »
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).
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6331
  • Country: es
Re: Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC
« Reply #3 on: November 24, 2024, 07:08:30 pm »
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.
« Last Edit: November 25, 2024, 02:34:32 pm by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: bingo600, pardo-bsso, QWERTYQWQ

Online ajb

  • Super Contributor
  • ***
  • Posts: 2766
  • Country: us
Re: Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC
« Reply #4 on: November 25, 2024, 04:53:56 pm »
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. 
 

Offline DavidAlfa

  • Super Contributor
  • ***
  • Posts: 6331
  • Country: es
Re: Challenges in Multi-Channel NTC Reading with STM32F103C8T6 ADC
« Reply #5 on: November 25, 2024, 08:19:17 pm »
From RM0008:
Quote
When using scan mode, DMA bit must be set and the direct memory access controller is used to transfer the converted data of regular group channels to SRAM after each update of the ADC_DR register.

So, 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-adc
Quote
Unlike F4 series, ADC in F1 series can generate only one (single) interrupt at the end of the whole scanning sequence. That's why DMA is a must when using scanning mode.

I 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 ch1
ADC_Start -> Converts ch2
ADC_Start -> Converts ch3
ADC_Start -> Converts ch4
ADC_Start -> Converts ch1
ADC_Start -> Converts ch2
...

Attaching an example (No interrupts, no DMA, fully poll-driven), also tested on my bluepill.
« Last Edit: November 26, 2024, 04:35:05 am by DavidAlfa »
Hantek DSO2x1x            Drive        FAQ          DON'T BUY HANTEK! (Aka HALF-MADE)
Stm32 Soldering FW      Forum      Github      Donate
 
The following users thanked this post: bingo600


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf