소개 (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)
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.h
와max31865.c
를 STM32CubeIDE 프로젝트의Inc
및Src
폴더에 추가.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_Type
을RTD_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):
- STM32L432KC 데이터시트 (STM32L432KC Datasheet)
- MAX31865 데이터시트 (MAX31865 Datasheet)
키워드: STM32, MAX31865, PT100, PT1000, RTD 드라이버, Callendar-Van Dusen, SPI 통신, 온도 센서, STM32CubeIDE
'아날로그회로(Analog Circuit) > ADC관련' 카테고리의 다른 글
STM32 HAL 기반 ADS114S08 ADC 드라이버 구현 (0) | 2025.08.24 |
---|---|
PGA302 Sensor Signal Conditioner IC STM32 I2C 드라이버 구현 | 압력 센서 보정 및 통신 예제(PGA302 I2C Driver Implementation Using STM32) (0) | 2025.08.20 |
ZSC31050 Sensor Signal Conditioner IC I²C 인터페이스 설정 및 데이터 처리 코드 구현 (0) | 2025.08.16 |
ZSSC3230 Sensor Signal Conditioner IC 보정 절차 및 Calibration Math 분석 (1) | 2025.08.15 |
ZSSC3230 Sensor Signal Conditioner IC STM32을 사용한 I2C 코드 구현 (0) | 2025.08.15 |
ZSSC3230 Sensor Signal Conditioner IC 센서 Raw Data 측정 설정 절차 (0) | 2025.08.15 |
ZSSC3230 Sensor Signal Conditioner IC 전체 측정 전 NVM 설정 절차 (0) | 2025.08.15 |
ZSSC3230 Sensor Signal Conditioner IC 사양서 요약 (1) | 2025.08.14 |