다채널 온도 모니터링 시스템은 임베디드 환경에서 여러 지점의 온도를 동시에 측정하고 제어하는 데 사용된다. 본 설계에서는 중앙 처리 장치로 8051 계열 마이크로컨트롤러를 택하며, 각 채널의 온도 정보는 독립적인 디지털 온도 트랜스듀서를 통해 수집한다. 수집된 데이터는 내부 알고리즘을 통해 필터링되고 분석되며, 이 결과에 따라 경보 모듈과 외부 구동 회로의 동작 상태가 결정된다. 동시에 측정 온도는 액정 디스플레이를 통해 실시간으로 시각화되며, 사용자는 물리적 입력 장치를 통해 임계값을 직접 설정할 수 있다.
아키텍처 개요
시스템은 크게 네 가지 기능 블록으로 구성된다. 첫째, 센서 인터페이스 계층은 1-Wire 프로토콜을 기반으로 여러 개의 DS18B20 도 센서와 통신한다. 둘째, 중앙 처리 계층에서는 산술 연산과 논리 판단을 수행한다. 셋째, 출력 계층은 피에조 부저와 전자기계식 릴레이를 구동한다. 넷째, 인터페이스 계층은 16x2 문자 LCD와 택트 스위치를 포함한다.
1-Wire 통신 구현
DS18B20은 단일 데이터선으로 전원과 통신을 모두 처리하는 특징이 있다. 다음 코드는 핀 정의와 기본 타이밍 함수를 재구성한 것이다.
#include <reg51.h>
#include <intrins.h>
sbit ONEWIRE_PIN = P1^3; // DS18B20 데이터 핀: P1.3
// 짧은 지연 함수 (약 10μs 단위)
void delay_10us(unsigned int count) {
while (count--) {
_nop_(); _nop_(); _nop_(); _nop_();
}
}
// 긴 지연 함수 (약 1ms 단위)
void delay_ms(unsigned int ms) {
unsigned int i, j;
for (i = 0; i < ms; i++)
for (j = 0; j < 120; j++);
}
// 1-Wire 리셋 및 프리즌스 감지
bit ow_reset(void) {
bit presence;
ONEWIRE_PIN = 0; // 버스 드라이브 로우
delay_10us(50); // 480μs 이상 유지
ONEWIRE_PIN = 1; // 버스 릴리스
delay_10us(7); // 70μs 대기
presence = ONEWIRE_PIN; // 프리즌스 신호 샘플링
delay_10us(50); // 나머지 타임슬롯 완료
return !presence; // 감지 성공 시 1 반환
}
// 마스터에서 슬레이브로 1바이트 전송
void ow_write_byte(unsigned char value) {
unsigned char idx;
for (idx = 0; idx < 8; idx++) {
ONEWIRE_PIN = 0; // 쓰기 타임슬롯 시작
_nop_(); _nop_(); // 짧은 지연
if (value & 0x01) {
ONEWIRE_PIN = 1; // '1' 비트 전송
} else {
ONEWIRE_PIN = 0; // '0' 비트 전송
}
delay_10us(6); // 60μs 유지
ONEWIRE_PIN = 1; // 버스 복원
value >>= 1; // 다음 비트 준비
}
}
// 슬레이브에서 마스터로 1바이트 수신
unsigned char ow_read_byte(void) {
unsigned char idx, result = 0;
for (idx = 0; idx < 8; idx++) {
ONEWIRE_PIN = 0; // 읽기 타임슬롯 시작
_nop_(); _nop_();
ONEWIRE_PIN = 1; // 버스 릴리스
_nop_(); _nop_(); // 샘플링 전 대기
result >>= 1; // 결과 레지스터 시프트
if (ONEWIRE_PIN) {
result |= 0x80; // 수신 비트 기록
}
delay_10us(6); // 타임슬롯 완료 대기
}
return result;
}
위 구현에서 ow_reset 함수는 버스를 초기화하고 연결된 센서의 응답을 확인한다. 쓰기 동작은 마스터가 타임슬롯을 생성하고 해당 구간 내 전압 레벨을 조절하는 방식으로, 읽기 동작은 마스터가 짧은 펄스를 발생시킨 뒤 버스 상태를 샘플링한다.
온도 변환 및 데이터 해석
DS18B20의 스크래치패드에서 얻은 원시 데이터는 16비트 2의 보수 형식으로 저장된다. 다음은 이를 실제 온도로 환산하는 과정이다.
// DS18B20 스크래치패드 읽기 및 온도 계산
float acquire_temperature(void) {
unsigned char lsb, msb;
signed int raw;
float celsius;
ow_reset();
ow_write_byte(0xCC); // 스 ROM
ow_write_byte(0x44); // 온도 변환 시작
delay_ms(750); // 12비트 해상도 기준 변환 대기
ow_reset();
ow_write_byte(0xCC); // 스킵 ROM
ow_write_byte(0xBE); // 스크래치패드 읽기 명령
lsb = ow_read_byte(); // 온도 LSB
msb = ow_read_byte(); // 온도 MSB
raw = (msb << 8) | lsb; // 16비트 값 조합
// 12비트 해상도: 0.0625°C/LSB
celsius = (float)raw * 0.0625;
return celsius;
}
측정된 온도는 저장된 임계값과 비교되어 하위 하드웨어의 동작이 결정된다. 다음 코드는 히스테리시스를 포함한 제어 로직을 보여준다.
sbit BUZZER = P2^4; // 피에조 부저: P2.4 (활성 로우)
sbit RELAY = P2^5; // 릴레이 구동: P2.5 (활성 하이)
#define HYSTERESIS 1.5 // 히스테리시스 폭: 1.5°C
float threshold_high = 35.0; // 상한 임계값
float threshold_low = 30.0; // 하한 임계값 (복원 지점)
// 제어 루프
void process_control(float current_temp) {
static unsigned char alarm_state = 0;
if (current_temp >= threshold_high) {
BUZZER = 0; // 경보 활성화
RELAY = 1; // 냉각 장치 가동
alarm_state = 1;
}
else if (current_temp <= (threshold_high - HYSTERESIS) && alarm_state) {
// 히스테리시스 구간 내 복원
if (current_temp <= threshold_low) {
BUZZER = 1; // 경보 해제
RELAY = 0; // 냉각 장치 정지
alarm_state = 0;
}
}
}
LCD 출력 및 사용자 인터페이스
시스템 상태는 HD44780 호환 LCD에 표시되며, 사용자 입력은 외부 인터럽트를 통해 처리된다.
#include <string.h>
#include <stdio.h>
sbit LCD_RS = P0^0;
sbit LCD_RW = P0^1;
sbit LCD_EN = P0^2;
#define LCD_DATA P0
sbit KEY_SET = P3^2; // 설정 버튼: INT0
sbit KEY_UP = P3^3; // 증가 버튼: INT1
sbit KEY_DOWN = P3^4; // 감소 버튼
// LCD 명령어 전송
void lcd_cmd(unsigned char cmd) {
LCD_RS = 0;
LCD_RW = 0;
LCD_DATA = (cmd & 0xF0);
LCD_EN = 1; LCD_EN = 0;
LCD_DATA = (cmd << 4);
LCD_EN = 1; LCD_EN = 0;
delay_ms(2);
}
// LCD 데이터 전송
void lcd_data(unsigned char dat) {
LCD_RS = 1;
LCD_RW = 0;
LCD_DATA = (dat & 0xF0);
LCD_EN = 1; LCD_EN = 0;
LCD_DATA = (dat << 4);
LCD_EN = 1; LCD_EN = 0;
delay_ms(2);
}
// 문자열 표시
void lcd_string(unsigned char *str) {
while (*str) {
lcd_data(*str++);
}
}
// 온도 정보 화면 갱신
void refresh_display(float temp, float setpoint) {
unsigned char line1[16], line2[16];
sprintf(line1, "T:%5.2f C", temp);
sprintf(line2, "SP:%4.1f C", setpoint);
lcd_cmd(0x80); // 첫 번째 줄
lcd_string(line1);
lcd_cmd(0xC0); // 두 번째 줄
lcd_string(line2);
}
다중 채널 확장
여러 개의 DS18B20을 단일 버스에 연결하려면 각 센서의 64비트 ROM ID를 활용한 개별 주소 지정이 필요하다. 대안으로 각 채널을 독립적인 GPIO 핀에 할당하여 병렬 구조를 구성할 수도 있다.
// 다중 채널 구조체 정의
typedef struct {
sbit *data_pin; // 센서 연결 핀 포인터
float last_reading; // 마지막 측정값
float channel_setpoint; // 채널별 설정값
unsigned char status; // 상태 플래그
} TempChannel;
TempChannel channels[4]; // 최대 4채널 정의
// 채널 순회 측정
void scan_all_channels(void) {
unsigned char i;
for (i = 0; i < 4; i++) {
channels[i].last_reading = acquire_temperature();
process_control(channels[i].last_reading);
}
}
이러한 구조는 향후 채널 수 확장이나 센서 종류 변경 시에도 모듈 단위의 유지보수가 가능하도록 설계되었다.