18

STM32 ve NTC Sensörü İle Sıcaklık Ölçme

Sıcaklığa bağlı olarak direnci değişen devre elemanlarına Termistör denir. İki farklı çeşidi vardır. “Negative Thermocouple” ifadesinin kısaltması olan NTC  ve “Positive Thermocouple” ifadesinin kısaltması olan PTC . İkisinin de sıcaklığa bağlı olarak direnci değişir. Farkları ise birinin direnci sıcaklık ile artar iken diğerinin azalmasıdır.

  • NTC: Sıcaklık arttıkça direnci azalır.
  • PTC: Sıcaklık arttıkça direnci artar.

Bu direnç değişimleri doğrusal değildir. Bu yüzden sıcaklık hesaplamaları yapılırken bazı formüllerden yararlanılır. Bu formüllerden biri Stein-Hart denklemidir.

Stein-Hart equation

Stein-Hart Denklemi

Bu denklemde “R” değişkeni “T(kelvin)” derecede ölçülen NTC‘nin direnç değeridir. Bu formülü kod içerisinde kullanabilmemiz için A,B ve C katsayılarını bilmemiz gerekir. Bu katsayılar NTC sıcaklık-direnç tablosuna göre hesaplanır. NTC tablosundan 3 farklı derece seçip ve bu derecelerin karşılığı olan direnç değerlerini formülde yerine yazarak 3 adet 3 bilinmeyenli denklem elde edilir. Buradan A,B ve C katsayıları bulunarak formüle yazılabilir. Ben hazır hesaplanmış olan katsayıları kullandım. Ayrıca linkteki siteden NTC sıcaklık-direnç tablosundan seçeceğiniz üç farklı değer için kendiniz A,B ve C katsayıları hesaplatıp kullanabilirsiniz.

NTC Bağlantısı

STM32 ile NTC arasındaki bağlantı yukarıdaki gibi yapılabilir. 10k‘lık NTC yine 10k bir direnç ile pull-down bağlantı yapılarak ADC pinine bağlanmıştır.

Formüle dikkat edilecek olursa sıcaklık değerini hesaplayabilmek için NTC’nin direncini bilmemiz gerekir. Yukarıdaki bağlantıda gerilim bölücü mantığından yola çıkarak NTC’nin direnci rahatlıkla hesaplanabilir. Fakat denklem sadeleştirildiği zaman sadece ADC’den okunan 12 bitlik değeri bilmemiz yeterlidir.

Yukarıdaki denklemde görüldüğü gibi gerilim bölücüden yola çıkarak NTC nin direnci hesaplanabilir. Burada görüldüğü gibi ADC referans voltajı ve NTC nin besleme voltajı birbirini götürürür. Denklem sadeleştiği zaman aşağıdaki fonksiyondaki gibi olur.

double Thermistor(int analogValue){

 double temperature;
 temperature = log(((40950000 / analogValue) - 10000));// 4095 ADC çözünürlüğü(12 bit)
 temperature = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * temperature * temperature)) * temperature);
 temperature = temperature - 273.15;
 return temperature;
}

NOT: Denklemde dikkat ettiyseniz logaritmik işlem yapılmaktadır. Bunu yapabilmek için “math.h” kütüphanesi projeye dahil edilmelidir.

ADC’ den okunan değer bu fonksiyona gönderildiğinde geriye sıcaklık bilgisini derece cinsinden döndürmektedir.

CubeMx Ayarları

STM32-NTC Kodlarının Tamamı

/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2019 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_hal.h"

/* USER CODE BEGIN Includes */
#include <math.h>
/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
int adc_raw,temp;
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
double Thermistor(int analogValue){

 double temperature;
 temperature = log(((40950000 / analogValue) - 10000));
 temperature = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * temperature * temperature)) * temperature);
 temperature = temperature - 273.15;
 return temperature;
}
/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

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_ADC1_Init();

  /* USER CODE BEGIN 2 */
	HAL_ADC_Start(&hadc1);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		adc_raw = HAL_ADC_GetValue(&hadc1);
		temp = Thermistor(adc_raw);
		HAL_Delay(200);
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}

/** System Clock Configuration
*/
void SystemClock_Config(void)
{

  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
  RCC_PeriphCLKInitTypeDef PeriphClkInit;

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL7;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV4;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}

/* ADC1 init function */
static void MX_ADC1_Init(void)
{

  ADC_ChannelConfTypeDef sConfig;

    /**Common config 
    */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  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(__FILE__, __LINE__);
  }

    /**Configure Regular Channel 
    */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_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.
  * @param  None
  * @retval None
  */
void _Error_Handler(char * file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  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

/**
  * @}
  */ 

/**
  * @}
*/ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

STMStudio programı yardımı ile “temp” isimli sıcaklık değişkeninin değerini gözlemleyebilirsiniz. Eğer daha sağlıklı bir sıcaklık değeri okumak isterseniz basit bir yazılımsal filtre uygulayabilirsiniz. Örneğin; 20 defa ADC değeri okuyup bunun aritmetik ortalamasını alıp Thermistor() fonksiyonuna gönderebilirsiniz.

İyi Çalışmalar…

Mehmet Topuz

18 Comments

    • Kodda NTC direncinin hesaplandığı kısım şu satırdır.
      temperature = log(((40950000 / analogValue) - 10000));
      burada logaritmanın içerisindeki kısım, (3,3xADC)/4095=(10^4/(10^4+R))x3,3 eşitliğinin sadeleşmiş halidir.Dediğin değişikliği yapar isek bu eşitlikte gerilim bölücüyü ilgilendiren kısım olan 10^4/(10^4+R) hesaplamasını R/(10^4+R) şeklinde düzenlemek gerekir. Burayı düzenleyip denklemi tekrar sadeleştirdiğimizde logaritma fonksiyonun içine yazılacak denklem (ADCx10^4)/(4095-ADC) şeklinde olur. Yani temperature = log(((40950000 / analogValue) - 10000)); satırını temperature = log((analogvalue*10000)/(4095-analogvalue)); şeklinde değiştirince sıcaklık değeri olarak yine aynı sonucu verecektir.

      • Hocam ben bunu picte yapmaya çalışıyorum.Bu formülü kullandığımda sıcaklık değeri 6 derece fazla çıkıyor?Sebebi ne olabilir?

        • Kullandığın PIC’in ADC çözünürlüğü 12 bit olmayabilir. Bu örnekte STM32F103 12 bit ADC ye sahip olduğu için formülde 4095 yazıldı. Hangi PIC’i kullanıyorsun bilmiyorum ama PIC16F877A da ADC çözünürlüğü 10 bittir mesala. Eğer senin kullandığında böyle ise formülde 4095 yazan yere 1023 yazman gerekir. Birde bu örnekte NTC yi 3.3 volt ile besledim senin 5 volt ile beslemen gerekir.

          • Yazıda belirtmeyi unutmuşum ben NTC olarak linkteki NTC yi kullanmıştım. Ayrıca formülde 10k’lık direncin tam 10k olduğunu kabul ederek yazıyoruz. Genelde böyle ölçümler için toleransı düşük dirençler kullanılır. Kullandığın direnç renklerine göre 10k olsa bile ölçtüğünde 10k çıkmayabilir. Tam 10k olmasa bile 6 derecelik bir sapma yapacağını zannetmiyorum gerçi. Bir de direnç değerini ölçüp formüldeki 10000 yerine direnç değerini yaz. Böylece daha yakın sonuçlar alabilirsin fakat çok hassas bir ölçüm bekleme. Hassas bir ölçüm için ekstradan filtre devresi tarzı bir şey gerekebilir. Basitçe NTC ye paralel 100nF kutupsuz bir kondansatör atabilirsin. Bunun haricinde aklıma başka bir çözüm gelmiyor İlker.
            Link: https://www.direnc.net/10k-su-gecirmez-kablolu-ntc-50cm

  1. Mehmet hocam merhabalar
    Stm32f103ct8 kullanıyorum . Hocam NTC sıcaklığı ölçümü gayet güzel fakat bir sorunum. Var + sıcaklık normal. Sıcaklığı eksiye düşürdüğümde ekranda
    . – işaretinin gözükmesin istiyorum örnek – 12 gibi bunu Text olarak nasıl yazdırabilirim

    • Merhaba,
      Ölçtüğünüz sıcaklık değerini aşağıdaki örnekteki gibi bir string e çevirebilirsiniz.

      int value = -12;
      char string[15];
      sprintf(string,”Sicaklik: %d”,value);

      sprintf fonksiyonunu kullanabilmek için string.h kütüphanesini dahil etmeniz gerekir. Bir de Thermistor fonksiyonu double olarak değer döndürür. Double için %d yerine %lf kullanmanız gerek. Daha sonra bu string i ekrana gönderebilirsiniz.

    • math.h kütüphanesindeki log() fonksiyonu doğal logaritmanın karşılığıdır. Yani log(R) demek aslında denklemdeki ln(R) ile aynıdır. Matematikte log10 ifadesini logaritma 10 tabanında 10 diye öğrendik. Kafa karışıklığı buradan kaynaklanıyor. math.h kütüphanesinde ise log(10) fonksiyonu logaritma e tabanında 10 demektir. Onluk tabandaki logaritma için ise log10() fonksiyonu kullanılır.

  2. Mehmet Hocam, evdeki LED aydınlatmaya ait 12VDV 150W 12.5A SMTP de girişteki NTC üzerindeki değer okunamayacak şekilde patlamış.
    Takip eden Köprü diod ve kondansatörlerle beraber değiştireceğim. NTC için ne önerirsiniz?

    • Hocam üzerinde yazan değere eşit bir NTC’nin işinizi göreceğini düşünüyorum. Güç kaynaklarında kullanılan NTC ler akım sınırlama amacı ile kullanılıyordu galiba. NTC lerin siyah yuvarlak kutupsuz kondansatörlere benzeyenlerinden kullanabilirsiniz ama direnç değerinden emin olamıyorum hocam. Güç kaynakları için düşük değerdeki NTC ler kullanılır. Linkteki NTC gibi olanlardan deneyebilirsiniz. https://www.direnc.net/10r-ntc

    • Üzerindeki değer okunmuyor demişsiniz sonradan gördüm. Aynı marka model güç kaynağının üzerinden NTC değerine bakmak lazım hocam kesin bir şey diyemiyorum ama tahminimce düşük değerdedir. 50 ohm dan küçüktür. Belki 1 ohm bile olabilir.

  3. ben ntc ile daha önce hiç çalışmadım. Projenizi birebir uyguladım fakat stmstudio da main.c içindeki temp seçeneğini seçiyorum 140 ile 170 arası değişen sürekli durmadan hareket eden sonuçlar ortaya çıkıyor. Acaba nerede yanlış yapıyorum. ölçtüğüm değerleri nerede okuyabilirim. Yardımcı olursanız sevinirim

    • ADC’den yanlış ölçüm yapıyor olabilirsiniz. NTC’ye verdiğiniz gerilimi ve stm’in gerilimini ölçü aleti ile kontrol edin. Stm’in gerilimi 3.3 volttan düşük ise adc ölçümü hatalı çıkar. NTC ve seri bağlı olan direnc 10k olması gerek aksi takdirde formülde değişiklik yapılması gerekir.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir