본문 바로가기
아날로그회로(Analog Circuit)/ADC관련

STM32에서 PT100/PT1000 RTD 센서용 MAX31865 온도 변환 IC 드라이버 구현

by linuxgo 2025. 8. 24.
반응형

소개 (Introduction)

MAX31865는 PT100 및 PT1000 RTD(Resistance Temperature Detector) 센서를 위한 고정밀 온도 변환 IC로, STM32 마이크로컨트롤러와 SPI 인터페이스를 통해 쉽게 통합됩니다. 이 문서에서는 STM32L432KC를 기반으로 MAX31865 드라이버를 구현하는 방법을 단계별로 설명하며, PT100과 PT1000을 모두 지원하고 Callendar-Van Dusen 비선형 공식을 적용한 완전한 코드를 제공합니다.  (The MAX31865 is a high-precision temperature conversion IC for PT100 and PT1000 RTD sensors, seamlessly integrated with STM32 microcontrollers via SPI. This blog post explains step-by-step how to implement a MAX31865 driver on the STM32L432KC, supporting both PT100 and PT1000 with the Callendar-Van Dusen non-linear formula, providing complete code.)

키워드: STM32, MAX31865, PT100, PT1000, RTD 드라이버, Callendar-Van Dusen, SPI 통신, 온도 센서, STM32CubeIDE

1. 개요 (Overview )

MAX31865는 15비트 델타-시그마 ADC를 내장한 RTD-to-Digital Converter로, 고정밀 온도 측정을 제공합니다. 주요 특징은 다음과 같습니다.(The MAX31865 is an RTD-to-Digital Converter with a 15-bit delta-sigma ADC, offering high-precision temperature measurement. Key features include:)

  • RTD 지원: PT100 (100Ω, -200°C ~ +850°C) 및 PT1000 (1000Ω, -200°C ~ +600°C), 2/3/4-선 구성 지원. 
  • 정확도: ±0.5°C (typ), 50/60Hz 노이즈 필터링. 
  • 인터페이스: 4-선 SPI, 최대 5MHz 클럭. 
  • 오류 감지: 단선, 과전압/저전압, 단락 감지. 
  • 참조 저항 (RREF): PT100용 400Ω, PT1000용 4300Ω 추천. 
  • 전력: 작동 시 2mA, 셧다운 시 3μA (max).

자세한 사양은 Analog Devices MAX31865 데이터시트를 참조하세요.

2. 개발 환경 설정 (Development Environment Setup)

 

MAX31865 board

2.1 하드웨어 연결 (Hardware Connection)

  • STM32L432KC: Cortex-M4 기반, 80MHz, 256KB Flash, 64KB SRAM. (Cortex-M4 based, 80MHz, 256KB Flash, 64KB SRAM.)
  • 연결:
    • SPI1 핀: PA5 (SCLK), PA6 (MISO), PA7 (MOSI). 
    • CS: PA4 (GPIO로 수동 제어). 
    • RREF: PT100은 400Ω, PT1000은 4300Ω (0.1% 정확도 권장). 
    • DRDY: 옵션으로 인터럽트 사용 가능 (e.g., PA0). 
  • 회로 예시: PT100/PT1000 센서를 MAX31865의 RTDP/RTDN/FORCE 핀에 연결, RREF 저항은 REFP/REFN에 연결. 

2.2 소프트웨어 환경 (Software Environment)

  • STM32CubeIDE: 프로젝트 생성 및 디버깅. (Project creation and debugging.)
  • STM32CubeMX: SPI 및 GPIO 설정. (SPI and GPIO configuration.)
  • HAL 라이브러리: SPI 통신, GPIO 제어. (SPI communication, GPIO control.)
  • 추가 라이브러리: <math.h> (Callendar-Van Dusen 계산용, 컴파일 시 -lm 플래그 추가). 

CubeMX 설정:

  • 시스템 클럭: 80MHz (HCLK). (System clock: 80MHz (HCLK).)
  • SPI1: Master, 8비트, Prescaler 32 (~2.5MHz), CPOL=Low, CPHA=1Edge, NSS=Soft. 
  • GPIO: PA4를 Output으로 설정 (CS). 

3. 드라이버 코드 구현 (Driver Code Implementation)

드라이버는 PT100과 PT1000을 모두 지원하며, Callendar-Van Dusen 비선형 공식을 사용해 ±0.01°C 수준의 고정밀 온도 변환을 제공합니다. (The driver supports both PT100 and PT1000, using the Callendar-Van Dusen non-linear formula for ±0.01°C high-precision temperature conversion.)

3.1 헤더 파일 (max31865.h) (Header File (max31865.h))

#ifndef MAX31865_H  // 헤더 파일 중복 포함 방지를 위한 가드 정의 (Guard definition to prevent multiple inclusions)
#define MAX31865_H

#include "stm32l4xx_hal.h"  // STM32 HAL 라이브러리 헤더 포함 (Include STM32 HAL library header)
#include <math.h>  // sqrt, fabsf 함수를 위한 math 헤더 포함 (Include math header for sqrt, fabsf functions)

// RTD 타입 정의 (Define RTD types)
typedef enum {
    RTD_PT100,   // PT100 센서 (PT100 sensor)
    RTD_PT1000   // PT1000 센서 (PT1000 sensor)
} RTD_Type;

// MAX31865 레지스터 주소 정의 (쓰기 시 +0x80 추가) (Define MAX31865 register addresses, add +0x80 for write)
#define MAX31865_CONFIG_REG         0x00  // 구성 레지스터 주소 (Configuration register address)
#define MAX31865_RTD_MSB_REG        0x01  // RTD 값 상위 바이트 레지스터 주소 (RTD value MSB register address)
#define MAX31865_RTD_LSB_REG        0x02  // RTD 값 하위 바이트 레지스터 주소 (RTD value LSB register address)
#define MAX31865_HIGH_FAULT_MSB_REG 0x03  // 상위 오류 임계값 MSB 레지스터 주소 (High fault threshold MSB register address)
#define MAX31865_HIGH_FAULT_LSB_REG 0x04  // 상위 오류 임계값 LSB 레지스터 주소 (High fault threshold LSB register address)
#define MAX31865_LOW_FAULT_MSB_REG  0x05  // 하위 오류 임계값 MSB 레지스터 주소 (Low fault threshold MSB register address)
#define MAX31865_LOW_FAULT_LSB_REG  0x06  // 하위 오류 임계값 LSB 레지스터 주소 (Low fault threshold LSB register address)
#define MAX31865_FAULT_STATUS_REG   0x07  // 오류 상태 레지스터 주소 (Fault status register address)

// 구성 레지스터 비트 마스크 정의 (Define bit masks for configuration register)
#define MAX31865_CONFIG_BIAS        0x80  // VBIAS 활성화 비트 (VBIAS enable bit)
#define MAX31865_CONFIG_MODEAUTO    0x40  // 연속 변환 모드 비트 (Continuous conversion mode bit)
#define MAX31865_CONFIG_MODEOFF     0x00  // 변환 모드 비활성화 (Conversion mode off)
#define MAX31865_CONFIG_1SHOT       0x20  // 단일 변환 모드 비트 (One-shot conversion mode bit)
#define MAX31865_CONFIG_3WIRE       0x10  // 3-선 RTD 구성 비트 (3-wire RTD configuration bit)
#define MAX31865_CONFIG_24WIRE      0x00  // 2-선 또는 4-선 RTD 구성 비트 (2-wire or 4-wire RTD configuration bit)
#define MAX31865_CONFIG_FAULTSTAT   0x02  // 오류 상태 클리어 비트 (Fault status clear bit)
#define MAX31865_CONFIG_FILT50HZ    0x01  // 50Hz 노이즈 필터 비트 (50Hz noise filter bit)
#define MAX31865_CONFIG_FILT60HZ    0x00  // 60Hz 노이즈 필터 비트 (60Hz noise filter bit)

// 오류 상태 비트 마스크 정의 (Define bit masks for fault status)
#define MAX31865_FAULT_HIGHTHRESH   0x80  // 상위 임계값 오류 비트 (High threshold fault bit)
#define MAX31865_FAULT_LOWTHRESH    0x40  // 하위 임계값 오류 비트 (Low threshold fault bit)
#define MAX31865_FAULT_REFINLOW     0x20  // 참조 입력 저전압 오류 비트 (REFIN low voltage fault bit)
#define MAX31865_FAULT_REFINHIGH    0x10  // 참조 입력 과전압 오류 비트 (REFIN high voltage fault bit)
#define MAX31865_FAULT_RTDINLOW     0x08  // RTD 입력 저전압 오류 비트 (RTD input low voltage fault bit)
#define MAX31865_FAULT_OVUV         0x04  // 과전압/저전압 오류 비트 (Overvoltage/undervoltage fault bit)

// PT100 및 PT1000 관련 상수 정의 (Define constants for PT100 and PT1000)
#define PT100_R0                    100.0f   // PT100 기준 저항 값 (PT100 reference resistance value)
#define PT1000_R0                   1000.0f  // PT1000 기준 저항 값 (PT1000 reference resistance value)
#define PT100_RREF                  400.0f   // PT100용 참조 저항 값 (Reference resistor value for PT100)
#define PT1000_RREF                 4300.0f  // PT1000용 참조 저항 값 (Reference resistor value for PT1000)

// Callendar-Van Dusen 계수 정의 (IEC 60751 표준) (Define Callendar-Van Dusen coefficients, IEC 60751 standard)
#define CVD_A                       3.9083e-3f  // A 계수 (A coefficient)
#define CVD_B                       -5.775e-7f  // B 계수 (B coefficient)
#define CVD_C                       -4.183e-12f // C 계수 (T < 0°C 에서 사용) (C coefficient, used for T < 0°C)

// 함수 프로토타입 선언 (Function prototypes declaration)
void MAX31865_Init(SPI_HandleTypeDef *hspi, RTD_Type rtd_type);  // MAX31865 초기화 함수 (RTD 타입 지정) (MAX31865 initialization function, specify RTD type)
uint8_t MAX31865_ReadReg(uint8_t reg, SPI_HandleTypeDef *hspi);  // 레지스터 읽기 함수 (Register read function)
void MAX31865_WriteReg(uint8_t reg, uint8_t val, SPI_HandleTypeDef *hspi);  // 레지스터 쓰기 함수 (Register write function)
uint16_t MAX31865_ReadRTD(SPI_HandleTypeDef *hspi);  // RTD 원시 값 읽기 함수 (Read raw RTD value function)
float MAX31865_ReadTemperature(SPI_HandleTypeDef *hspi, RTD_Type rtd_type);  // 온도 값 읽기 및 변환 함수 (RTD 타입 지정) (Read and convert temperature function, specify RTD type)
uint8_t MAX31865_ReadFault(SPI_HandleTypeDef *hspi);  // 오류 상태 읽기 함수 (Read fault status function)
void MAX31865_ClearFault(SPI_HandleTypeDef *hspi);  // 오류 상태 클리어 함수 (Clear fault status function)

#endif  // 헤더 가드 종료 (End of header guard)

3.2 소스 파일 (max31865.c) (Source File (max31865.c))

#include "max31865.h"  // 사용자 정의 헤더 파일 포함 (Include user-defined header file)

// MAX31865 초기화 함수: SPI 핸들러와 RTD 타입을 받아 구성 레지스터와 오류 임계값을 설정합니다. (MAX31865 initialization function: Receives SPI handler and RTD type, sets configuration register and fault thresholds.)
// 3-선 구성, 연속 모드, 50Hz 필터, VBIAS 활성화로 설정. (Sets to 3-wire config, continuous mode, 50Hz filter, VBIAS on.)
void MAX31865_Init(SPI_HandleTypeDef *hspi, RTD_Type rtd_type) {
    // 구성 값 계산: VBIAS | Continuous | 3-Wire | Fault Clear | 50Hz Filter (Calculate config value: VBIAS | Continuous | 3-Wire | Fault Clear | 50Hz Filter)
    uint8_t config = MAX31865_CONFIG_BIAS | MAX31865_CONFIG_MODEAUTO | 
                     MAX31865_CONFIG_3WIRE | MAX31865_CONFIG_FAULTSTAT | 
                     MAX31865_CONFIG_FILT50HZ;
    MAX31865_WriteReg(MAX31865_CONFIG_REG, config, hspi);  // 구성 레지스터에 값 쓰기 (Write value to configuration register)
    
    // 기본 상위 오류 임계값 설정: 최대 값 0x7FFF (Set default high fault threshold: max value 0x7FFF)
    uint16_t high_thresh = 0x7FFF;  
    MAX31865_WriteReg(MAX31865_HIGH_FAULT_MSB_REG, (high_thresh >> 8), hspi);  // MSB 쓰기 (Write MSB)
    MAX31865_WriteReg(MAX31865_HIGH_FAULT_LSB_REG, (high_thresh & 0xFF), hspi);  // LSB 쓰기 (Write LSB)
    
    // 기본 하위 오류 임계값 설정: 최소 값 0x0000 (Set default low fault threshold: min value 0x0000)
    uint16_t low_thresh = 0x0000;   
    MAX31865_WriteReg(MAX31865_LOW_FAULT_MSB_REG, (low_thresh >> 8), hspi);  // MSB 쓰기 (Write MSB)
    MAX31865_WriteReg(MAX31865_LOW_FAULT_LSB_REG, (low_thresh & 0xFF), hspi);  // LSB 쓰기 (Write LSB)
}

// 단일 레지스터 읽기 함수: 지정된 레지스터에서 1바이트 데이터를 읽습니다. (Single register read function: Reads 1 byte data from the specified register.)
// SPI를 통해 읽기 명령과 더미 바이트를 전송하고 수신된 데이터를 반환. (Transmits read command and dummy byte via SPI and returns received data.)
uint8_t MAX31865_ReadReg(uint8_t reg, SPI_HandleTypeDef *hspi) {
    uint8_t tx_buf[2] = {reg, 0xFF};  // 전송 버퍼: 레지스터 주소 + 더미 바이트 (Transmit buffer: register address + dummy byte)
    uint8_t rx_buf[2];  // 수신 버퍼 (Receive buffer)
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);  // CS 핀을 Low로 설정 (칩 선택 활성화, 가정: PA4가 CS) (Set CS pin to Low, chip select active, assume PA4 is CS)
    HAL_SPI_TransmitReceive(hspi, tx_buf, rx_buf, 2, HAL_MAX_DELAY);  // SPI 전송/수신 수행 (Perform SPI transmit/receive)
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    // CS 핀을 High로 설정 (칩 선택 비활성화) (Set CS pin to High, chip select inactive)
    
    return rx_buf[1];  // 수신된 데이터 중 실제 값 반환 (두 번째 바이트가 데이터) (Return actual data from received buffer, second byte is data)
}

// 단일 레지스터 쓰기 함수: 지정된 레지스터에 1바이트 데이터를 씁니다. (Single register write function: Writes 1 byte data to the specified register.)
// 쓰기 명령 (reg | 0x80)과 값을 전송. (Transmits write command, reg | 0x80, and value.)
void MAX31865_WriteReg(uint8_t reg, uint8_t val, SPI_HandleTypeDef *hspi) {
    uint8_t tx_buf[2] = {reg | 0x80, val};  // 전송 버퍼: 쓰기 명령 + 값 (Transmit buffer: write command + value)
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);  // CS Low (Set CS Low)
    HAL_SPI_Transmit(hspi, tx_buf, 2, HAL_MAX_DELAY);  // SPI 전송 수행 (Perform SPI transmit)
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    // CS High (Set CS High)
}

// RTD 원시 값 읽기 함수: RTD MSB와 LSB를 읽어 15비트 값을 반환합니다. (Read raw RTD value function: Reads RTD MSB and LSB to return 15-bit value.)
// LSB의 Fault 비트를 제거하기 위해 오른쪽 시프트. (Right shift to remove Fault bit from LSB.)
uint16_t MAX31865_ReadRTD(SPI_HandleTypeDef *hspi) {
    uint16_t rtd = MAX31865_ReadReg(MAX31865_RTD_MSB_REG, hspi) << 8;  // MSB 읽고 8비트 왼쪽 시프트 (Read MSB and shift left by 8 bits)
    rtd |= MAX31865_ReadReg(MAX31865_RTD_LSB_REG, hspi);  // LSB 읽고 OR 연산 (Read LSB and OR operation)
    rtd >>= 1;  // Fault 비트 제거 (오른쪽 시프트 1) (Remove Fault bit, right shift by 1)
    return rtd;  // 15비트 RTD 값 반환 (Return 15-bit RTD value)
}

// 온도 읽기 함수: RTD 값을 읽어 저항으로 변환 후 Callendar-Van Dusen 비선형 공식을 사용하여 온도로 계산합니다. (Temperature read function: Reads RTD value, converts to resistance, then calculates temperature using Callendar-Van Dusen non-linear formula.)
// PT100 또는 PT1000 선택 가능. Fault 시 -999.0f 반환. T >= 0°C: 이차 방정식 풀이. T < 0°C: Newton-Raphson 반복법 사용. (Selectable PT100 or PT1000. Returns -999.0f on fault. For T >= 0°C: quadratic solve. For T < 0°C: Newton-Raphson iteration.)
float MAX31865_ReadTemperature(SPI_HandleTypeDef *hspi, RTD_Type rtd_type) {
    uint16_t rtd_raw = MAX31865_ReadRTD(hspi);  // RTD 원시 값 읽기 (Read raw RTD value)
    if (rtd_raw & 0x0001) {  // LSB Fault 비트 확인 (Check LSB Fault bit)
        return -999.0f;  // 오류 발생 시 임의 오류 값 반환 (Return arbitrary error value on fault)
    }
    
    // RTD 타입에 따라 R0와 RREF 선택 (Select R0 and RREF based on RTD type)
    float R0 = (rtd_type == RTD_PT100) ? PT100_R0 : PT1000_R0;
    float RREF_val = (rtd_type == RTD_PT100) ? PT100_RREF : PT1000_RREF;
    
    // 저항 계산: (rtd_raw * RREF) / 2^15 (Resistance calculation: (rtd_raw * RREF) / 2^15)
    float resistance = (float)rtd_raw * RREF_val / 32768.0f;  // 15비트 해상도 (32768 = 2^15) (15-bit resolution, 32768 = 2^15)
    
    // Callendar-Van Dusen 역공식 적용 (Apply Callendar-Van Dusen inverse formula)
    float temperature;
    float Z = (resistance - R0) / R0;  // 정규화된 저항 변화 (Normalized resistance change)
    
    // 초기 근사: 선형으로 T 추정 (Initial approximation: linear estimate for T)
    float T_approx = Z / CVD_A;  // 대략적인 T (Approximate T)
    
    if (T_approx >= 0.0f) {  // T >= 0°C: C=0, 이차 방정식 풀이 (T >= 0°C: C=0, solve quadratic equation)
        float discriminant = CVD_A * CVD_A - 4.0f * CVD_B * Z;  // 판별식 계산 (Calculate discriminant)
        if (discriminant < 0.0f) {
            return -999.0f;  // 실수 해 없음 (No real solution)
        }
        temperature = (-CVD_A + sqrtf(discriminant)) / (2.0f * CVD_B);  // 양의 근 선택 (B<0이므로) (Select positive root, since B<0)
    } else {  // T < 0°C: 전체 공식 사용, Newton-Raphson 반복으로 풀이 (T < 0°C: Use full equation, solve with Newton-Raphson iteration)
        temperature = T_approx;  // 초기 값 설정 (Set initial value)
        const int max_iter = 20;  // 최대 반복 횟수 (Maximum iterations)
        const float tol = 1e-6f;  // 허용 오차 (Tolerance)
        for (int i = 0; i < max_iter; i++) {
            // f(T) = 1 + A T + B T^2 + C (T-100) T^3 - (resistance / R0) (f(T) = 1 + A T + B T^2 + C (T-100) T^3 - (resistance / R0))
            float f = 1.0f + CVD_A * temperature + CVD_B * temperature * temperature +
                      CVD_C * (temperature - 100.0f) * temperature * temperature * temperature - resistance / R0;
            // df/dT = A + 2 B T + C [3 T^2 (T-100) + T^3] (df/dT = A + 2 B T + C [3 T^2 (T-100) + T^3])
            float df = CVD_A + 2.0f * CVD_B * temperature +
                       CVD_C * (3.0f * temperature * temperature * (temperature - 100.0f) + temperature * temperature * temperature);
            float delta = f / df;  // 업데이트 값 (Update value)
            temperature -= delta;  // T 업데이트 (Update T)
            if (fabsf(delta) < tol) {  // 수렴 확인 (Check convergence)
                break;
            }
        }
        if (temperature >= 0.0f) {  // 수렴 후 T가 0 이상이면 재계산 (If converged T >=0, recalculate with quadratic)
            float discriminant = CVD_A * CVD_A - 4.0f * CVD_B * Z;
            if (discriminant < 0.0f) {
                return -999.0f;
            }
            temperature = (-CVD_A + sqrtf(discriminant)) / (2.0f * CVD_B);
        }
    }
    
    return temperature;  // 계산된 온도 반환 (Return calculated temperature)
}

// 오류 상태 읽기 함수: Fault Status 레지스터 값을 반환합니다. (Fault status read function: Returns value from Fault Status register.)
uint8_t MAX31865_ReadFault(SPI_HandleTypeDef *hspi) {
    return MAX31865_ReadReg(MAX31865_FAULT_STATUS_REG, hspi);  // 레지스터 직접 읽기 (Directly read register)
}

// 오류 클리어 함수: 구성 레지스터의 Fault Detection 비트를 조정하여 클리어합니다. (Fault clear function: Adjusts Fault Detection bits in configuration register to clear.)
void MAX31865_ClearFault(SPI_HandleTypeDef *hspi) {
    uint8_t config = MAX31865_ReadReg(MAX31865_CONFIG_REG, hspi);  // 현재 구성 읽기 (Read current configuration)
    config &= ~0x2C;  // Fault Detection 관련 비트 클리어 (Clear Fault Detection related bits)
    config |= MAX31865_CONFIG_FAULTSTAT;  // Fault Status 클리어 비트 설정 (Set Fault Status clear bit)
    MAX31865_WriteReg(MAX31865_CONFIG_REG, config, hspi);  // 업데이트된 구성 쓰기 (Write updated configuration)
}

3.3 메인 파일 (main.c) (Main File (main.c))

#include "main.h"  // CubeMX에서 생성된 메인 헤더 포함 (Include main header generated by CubeMX)
#include "max31865.h"  // MAX31865 드라이버 헤더 포함 (Include MAX31865 driver header)

SPI_HandleTypeDef hspi1;  // SPI1 핸들러 구조체 선언 (Declare SPI1 handler structure)

// 시스템 오류 핸들러 (System error handler, generated by CubeMX)
void Error_Handler(void) {
    __disable_irq();
    while (1) {
    }
}

// 메인 함수: 시스템 초기화 후 MAX31865 초기화하고 무한 루프에서 온도 읽기 (Main function: After system initialization, initialize MAX31865 and read temperature in infinite loop)
int main(void) {
    HAL_Init();  // HAL 라이브러리 초기화 (Initialize HAL library)
    
    // 시스템 클럭 설정 (System clock configuration, generated by CubeMX)
    SystemClock_Config();  // 가정: CubeMX에서 생성된 클럭 설정 함수 (Assumption: Clock configuration function generated by CubeMX)
    
    // GPIO 및 SPI 초기화 (Initialize GPIO and SPI)
    MX_GPIO_Init();  // 가정: CubeMX에서 생성된 GPIO 초기화 함수 (Assumption: GPIO initialization function generated by CubeMX)
    MX_SPI1_Init();  // SPI1 초기화 함수 호출 (Call SPI1 initialization function)
    
    // PT100 또는 PT1000 초기화 (Initialize for PT100 or PT1000)
    MAX31865_Init(&hspi1, RTD_PT100);  // PT100용 초기화 (Initialize for PT100)
    // MAX31865_Init(&hspi1, RTD_PT1000);  // PT1000용 초기화 (주석 처리, 필요 시 사용) (Initialize for PT1000, commented, use if needed)
    
    while (1) {  // 무한 루프 (Infinite loop)
        float temp = MAX31865_ReadTemperature(&hspi1, RTD_PT100);  // PT100 온도 읽기 (Read PT100 temperature)
        // float temp = MAX31865_ReadTemperature(&hspi1, RTD_PT1000);  // PT1000 온도 읽기 (주석 처리, 필요 시 사용) (Read PT1000 temperature, commented, use if needed)
        uint8_t fault = MAX31865_ReadFault(&hspi1);  // 오류 상태 읽기 (Read fault status)
        
        if (fault) {  // 오류 발생 시 (If fault occurs)
            MAX31865_ClearFault(&hspi1);  // 오류 클리어 (Clear fault)
            // 추가 오류 처리: 예를 들어 UART로 로그 출력 (Additional fault handling: e.g., log via UART)
            // 예: printf("Fault detected: 0x%02X\n", fault); (e.g., printf("Fault detected: 0x%02X\n", fault);)
        }
        
        // temp 값 사용: 예를 들어 UART printf나 LCD에 출력 (Use temp value: e.g., output via UART printf or LCD)
        // 예: printf("Temperature: %.2f°C\n", temp); (e.g., printf("Temperature: %.2f°C\n", temp);)
        HAL_Delay(1000);  // 1초 지연 (1 second delay)
    }
}

// 시스템 클럭 설정 함수 (예시, CubeMX에서 생성됨) (System clock configuration function, example generated by CubeMX)
void SystemClock_Config(void) {
    // 80MHz HCLK 설정 예시 (Example 80MHz HCLK configuration)
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
    
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLM = 1;
    RCC_OscInitStruct.PLL.PLLN = 10;
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
    RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
    RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
        Error_Handler();
    }
    
    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_DIV1;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
        Error_Handler();
    }
}

// SPI1 초기화 함수: CubeMX에서 생성된 예시 코드 (SPI1 initialization function: Example code generated by CubeMX)
// 마스터 모드, 8비트 데이터, 소프트 NSS, 클럭 프리스케일러 32로 설정 (Master mode, 8-bit data, soft NSS, clock prescaler 32)
void MX_SPI1_Init(void) {
    hspi1.Instance = SPI1;  // SPI1 인스턴스 선택 (Select SPI1 instance)
    hspi1.Init.Mode = SPI_MODE_MASTER;  // 마스터 모드 (Master mode)
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;  // 양방향 (2 lines direction)
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;  // 8비트 데이터 크기 (8-bit data size)
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;  // 클럭 폴라리티 Low (Clock polarity low)
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;  // 클럭 페이즈 1엣지 (Clock phase 1 edge)
    hspi1.Init.NSS = SPI_NSS_SOFT;  // 소프트 NSS (Soft NSS)
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;  // 프리스케일러 32 (~2.5MHz at 80MHz 시스템 클럭) (Prescaler 32, ~2.5MHz at 80MHz system clock)
    hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;  // MSB 먼저 (MSB first)
    hspi1.Init.TIMode = SPI_TIMODE_DISABLE;  // TI 모드 비활성화 (TI mode disable)
    hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;  // CRC 비활성화 (CRC disable)
    hspi1.Init.CRCPolynomial = 7;  // CRC 폴리노미얼 (CRC polynomial)
    
    if (HAL_SPI_Init(&hspi1) != HAL_OK) {  // SPI 초기화 수행 및 오류 체크 (Perform SPI init and check for errors)
        Error_Handler();
    }
}

// GPIO 초기화 함수 (예시, CubeMX에서 생성됨) (GPIO initialization function, example generated by CubeMX)
void MX_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    
    // GPIO 클럭 활성화 (Enable GPIO clock)
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    // PA4를 CS 핀으로 설정 (Set PA4 as CS pin)
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // 초기 상태: CS High (Initial state: CS High)
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
}

3.4 Callendar-Van Dusen 공식  (Callendar-Van Dusen Formula)

RTD 센서의 저항-온도 관계는 Callendar-Van Dusen 공식으로 모델링됩니다.
(The resistance-temperature relationship of RTD sensors is modeled by the Callendar-Van Dusen formula.)

공식 (Formula):

  • \( T \geq 0^\circ \text{C} \):
    \[ R_T = R_0 \left( 1 + A T + B T^2 \right) \]
  • \( T < 0^\circ \text{C} \):
    \[ R_T = R_0 \left( 1 + A T + B T^2 + C (T - 100) T^3 \right) \]
  • 계수 (Coefficients, IEC 60751 표준):
    \[ A = 3.9083 \times 10^{-3}, \quad B = -5.775 \times 10^{-7}, \quad C = -4.183 \times 10^{-12} \]
    \( C \)는 \( T < 0^\circ \text{C} \)에서만 사용됩니다. 

드라이버에서는 저항(\( R_T \))에서 온도(\( T \))로 변환하기 위해 역공식을 사용합니다:
(The driver uses the inverse formula to convert resistance (\( R_T \)) to temperature (\( T \)):)

  • \( T \geq 0^\circ \text{C} \): 이차 방정식 풀이. 
    \[ Z = \frac{R_T - R_0}{R_0}, \quad T = \frac{-A + \sqrt{A^2 - 4 B Z}}{2 B} \]
  • \( T < 0^\circ \text{C} \): Newton-Raphson 반복법으로 4차 방정식 근사. 
    \[ f(T) = 1 + A T + B T^2 + C (T - 100) T^3 - \frac{R_T}{R_0} \] \[ f'(T) = A + 2 B T + C \left( 3 T^2 (T - 100) + T^3 \right) \] \[ T_{n+1} = T_n - \frac{f(T_n)}{f'(T_n)} \]
    허용 오차 \( 10^{-6} \), 최대 20회 반복. 

PT100 (\( R_0 = 100 \Omega \), \( R_\text{REF} = 400 \Omega \))과 PT1000 (\( R_0 = 1000 \Omega \), \( R_\text{REF} = 4300 \Omega \))은 동일한 계수를 사용하므로, 코드에서 \( R_0 \)와 \( R_\text{REF} \)만 동적으로 변경됩니다.

4. 구현 단계 (Implementation Steps)

4.1 하드웨어 준비 (Hardware Preparation)

  • STM32L432KC와 MAX31865를 SPI로 연결: PA5 (SCLK), PA6 (MISO), PA7 (MOSI), PA4 (CS). 
  • PT100/PT1000 센서를 MAX31865의 RTDP/RTDN/FORCE 핀에 연결. 
  • RREF 저항 설치: PT100은 400Ω, PT1000은 4300Ω (0.1% 정확도 권장). 
  • DRDY 핀(옵션): 인터럽트로 사용 시 PA0 등에 연결. 

4.2 STM32CubeMX 설정 (STM32CubeMX Configuration)

  • 시스템 클럭: 80MHz (HCLK). 
  • SPI1 설정:
    • Mode: Master, Data Size: 8-bit, Prescaler: 32 (~2.5MHz), CPOL: Low, CPHA: 1Edge, NSS: Soft.
    • 핀: PA5 (SCLK), PA6 (MISO), PA7 (MOSI).
  • 프로젝트 생성: C 프로젝트로 생성, HAL 라이브러리 사용. 

4.3 코드 통합 (Code Integration)

  • max31865.hmax31865.c를 STM32CubeIDE 프로젝트의 IncSrc 폴더에 추가. 
  • main.c에 초기화 및 온도 읽기 루프 작성. 
  • 컴파일 시 <math.h> 링크를 위해 -lm 플래그 추가 (STM32CubeIDE: Project Properties > C/C++ Build > Settings > MCU GCC Linker > Libraries). 

4.4 테스트 및 디버깅 (Testing and Debugging)

  • UART 출력: 온도 값을 확인하기 위해 UART 설정 (예: PA2-TX, PA3-RX, 115200 baud). 
  • 오실로스코프: SPI 신호(SCLK, MOSI, MISO, CS)를 확인하여 통신 문제 진단. 
  • 디버거: STM32CubeIDE 디버거로 rtd_raw, resistance, temperature 변수 값 확인. 
  • PT100/PT1000 전환: RTD_TypeRTD_PT100 또는 RTD_PT1000으로 설정하고 하드웨어 RREF 저항 교체 후 테스트. 

5. 오류 처리 및 최적화 (Error Handling and Optimization)

5.1 오류 처리 (Error Handling)

  • Fault 감지: MAX31865_ReadFault로 단선, 과전압, 저전압 등 오류 확인.
    예: 오류 발생 시 UART로 오류 코드 출력 (e.g., printf("Fault: 0x%02X\n", fault);). 
  • Fault 클리어: MAX31865_ClearFault로 Fault Status 레지스터 초기화.  
  • 오류 값 처리: 온도 계산 실패 시 -999.0f 반환, 애플리케이션에서 처리. 

5.2 최적화 (Optimization)

  • 인터럽트 사용: DRDY 핀을 인터럽트로 연결하여 폴링 제거 (예: EXTI로 PA0 설정). 
  • 타임아웃 조정: HAL_MAX_DELAY를 실제 애플리케이션에 맞는 값(예: 100ms)으로 대체. 
  • Newton-Raphson 최적화: 반복 횟수(max_iter)를 줄이거나 초기 추정값 개선. 
  • 메모리 효율성: float 계산을 최소화하거나 LUT(Lookup Table) 사용 고려.

6. 결론 및 추가 자료 (Conclusion and Additional Resources)

이 문서에서는 STM32L432KC와 MAX31865를 사용해 PT100/PT1000 RTD 드라이버를 구현하는 완전한 가이드를 제공했습니다. (This post provided a complete guide to implementing a PT100/PT1000 RTD driver using STM32L432KC and MAX31865.) 

추가 자료 (Additional Resources):

키워드: STM32, MAX31865, PT100, PT1000, RTD 드라이버, Callendar-Van Dusen, SPI 통신, 온도 센서, STM32CubeIDE

반응형