BASIC4MCU | 질문게시판 | lm75a 온도센서 관련 질문입니다.
페이지 정보
작성자 hanmw0707 작성일2024-12-01 10:15 조회2,940회 댓글6건본문
기기는 ATMEGA 128A 입니다.
스위치를 누르면 온도계, 체온계 바뀌는 설정이고
온도가 섭씨 30도 이상이거나 20도 미만일때 버저가 울리는 코드를 구현해봤는데 센서 코드를 이상하게 짠건지, FND 설정을 잘못한건지 이상하게 작동합니다.
코드는
#define F_CPU 16000000UL // CPU 클록 값=16 MHz
#define F_SCK 40000UL // SCK 클록 값=40 KHz
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
// LM75A 센서와 관련된 정의
#define LM75A_ADDR 0x98 // 0b10011000, 7비트를 1비트 left shift
#define LM75A_CONFIG_REG 1
#define LM75A_TEMP_REG 0
#define STOP 0 // 체온계 모드
#define GO 1 // 온도계 모드
// 전역변수
unsigned char digit[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67};
volatile int temperature = 0; // 현재 온도
volatile int stop_temp = 0; // 정지 시 온도
volatile int state = GO;
int sw = 0;
volatile uint8_t alarm_flag = 0; // 알람 상태를 나타내는 변수
void init_twi_port() {
DDRC = 0xff; DDRG = 0xff; // FND 출력 세팅
TWSR = TWSR & 0xfc; // Prescaler 값=00(1배)
TWBR = (F_CPU / F_SCK - 16) / 2; // 공식 참조, bit rate 설정
DDRB |= 0x01; // PORTB0을 출력으로 설정 (버저용)
}
// TWI로 2바이트 데이터 읽기
int read_twi_2byte_nopreset(char reg) {
char high_byte, low_byte;
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x08); // START 전송
TWDR = LM75A_ADDR | 0; // SLA+W 준비, W=0
TWCR = (1 << TWINT) | (1 << TWEN);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x18); // SLA+W 전송
TWDR = reg; // LM75A Reg 값 준비
TWCR = (1 << TWINT) | (1 << TWEN);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x28); // LM75A Reg 값 전송
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x10); // RESTART 전송
TWDR = LM75A_ADDR | 1; // SLA+R 준비, R=1
TWCR = (1 << TWINT) | (1 << TWEN);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x40); // SLA+R 전송
TWCR = (1 << TWINT) | (1 << TWEN | 1 << TWEA);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x50); // 1st DATA 준비
high_byte = TWDR; // 1st DATA 수신
TWCR = (1 << TWINT) | (1 << TWEN);
while (((TWCR & (1 << TWINT)) == 0x00) || (TWSR & 0xf8) != 0x58); // 2nd DATA 준비
low_byte = TWDR; // 2nd DATA 수신
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
while ((TWCR & (1 << TWSTO))); // STOP 확인
return ((high_byte << 8) | low_byte); // 수신 DATA 리턴
}
// FND 디스플레이 함수
void display_FND(int value) {
char digit[12] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67, 0x00, 0x40}; // ‘0’~‘9’,‘ ’,‘-’
char fnd_sel[4] = {0x01, 0x02, 0x04, 0x08};
int value_int, value_deci, num[4], i;
if ((value & 0x8000) != 0x8000) num[3] = 10; // Sign 비트 체크
else {
num[3] = 11; // 음수인 경우는 ‘-’ 디스플레이
value = (~value) + 1; // 2’s Complement 값을 취함
}
value_int = (char)((value & 0x7f00) >> 8); // High Byte bit6~0 값(정수 값)
value_deci = (char)(value & 0x0080); // Low Byte bit7 값(소수 첫째자리값)
num[2] = (value_int / 10) % 10;
num[1] = value_int % 10;
num[0] = (value_deci == 0x80) ? 5 : 0; // 소수 첫째자리가 1이면 0.5에 해당하므로 5를 디스플레이
for (i = 0; i < 4; i++) {
PORTC = digit[num[i]];
PORTG = fnd_sel[i];
if (i == 1) PORTC |= 0x80; // 소수점 표시
if (i % 2) _delay_ms(2); // 2번은 2ms 지연
else _delay_ms(3); // 2번은 3ms 지연
}
}
// INT4 인터럽트 서비스 루틴
ISR(INT4_vect) {
_delay_ms(100); // 스위치 바운스 시간 동안 기다림
EIFR |= 1 << 4; // 바운스에 의해 생긴 인터럽트는 무효화
if ((PINE & 0x10) != 0x00) return; // SW1이 눌려진 것이 아니면
if (state == STOP) state = GO; // STOP 상태라면 GO 상태로 변경
else {
state = STOP; // GO 상태라면 STOP 상태로 변경
stop_temp = read_twi_2byte_nopreset(LM75A_TEMP_REG); // 현재 온도 저장
}
}
// 알람 및 디스플레이 제어
void check_alarm() {
if (temperature > 3000 || temperature < 2000) { // 30°C 이상 또는 20°C 이하일 경우
alarm_flag = 1; // 알람 활성화
PORTB |= 0x01; // PORTB0 핀을 HIGH로 설정하여 버저 울림
} else {
alarm_flag = 0; // 알람 비활성화
PORTB &= ~0x01; // PORTB0 핀을 LOW로 설정하여 버저 끔
}
}
int main(void) {
int i;
DDRC = 0xff; // C 포트는 FND 데이터 출력 신호
DDRG = 0x0f; // G 포트는 FND 선택 출력 신호
DDRE = 0xef; // 0b11101111, PE4(SW1)는 입력
EICRB = 0x02; // INT4 트리거 모드는 하강 에지(falling edge)
EIMSK = 0x10; // INT4 인터럽트 인애이블
SREG |= 0x80; // SREG의 I(Interrupt Enable)비트(bit7)‘1’로 세트
init_twi_port(); // TWI 및 포트 초기화
while (1) { // 온도값 읽어 FND 디스플레이
for (i = 0; i < 60; i++) { // 0.6초 동안 처리
if (state == GO) { // 온도계 모드인 경우
if ((i == 0) || (i == 30)) temperature = read_twi_2byte_nopreset(LM75A_TEMP_REG); // 온도 값 읽기
display_FND(temperature); // 온도 디스플레이
check_alarm(); // 알람 체크
} else { // 체온계 모드인 경우
if (i < 30) {
temperature = stop_temp;
display_FND(temperature);
} else {
PORTG = 0x00;
_delay_ms(10); // 0.3초 동안 디스플레이 끄기
}
}
}
}
}
이렇습니다.정상적인 작동은 고사하고, FND 디스플레이에 소수점이 왜 두번째자리에 디스플레이 되는지도 모르겠고, 온도 센서가 8도에 고정된거같이 오작동하는데 선생님들의 고견을 여쭙고, 그에 따른 도움을 받고싶습니다.
댓글 6
조회수 2,940master님의 댓글
master 작성일
display_FND(int value) 함수를 보니
if ((value & 0x8000) != 0x8000) num[3] = 10; // Sign 비트 체크
num[3]이 최상위 자리입니다.(부호)
hanmw0707님의 댓글
hanmw0707 작성일감사합니다 선생님, 그러면 온도 센서에는 문제가 없는거겠죠? 계속 온도가 섭씨 8.5~8.10 사이만 왔다갔다해서 기계문제인가 궁금합니다.
master님의 댓글
master 작성일
void display_FND(int value) {
char digit[12]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7c,0x07,0x7f,0x67,0x00,0x40};
PORTC=digit[1]; PORTG=1; _delay_ms(1); PORTG=0;
PORTC=digit[2]; PORTG=2; _delay_ms(1); PORTG=0;
PORTC=digit[3]; PORTG=4; _delay_ms(1); PORTG=0;
PORTC=digit[4]; PORTG=8; _delay_ms(1); PORTG=0;
}
이 함수를 구동해서 4321이 제대로 표시되는지부터 확인 해보세요
hanmw0707님의 댓글
hanmw0707 작성일FND를 확인해보니 맨 마지막 자리가 8로 뜨는거빼고는 정상작동합니다. 저항문제인지 맨 마지막 자리가 8로떠서 이 부분만 해결하면 될것같습니다.
hanmw0707님의 댓글
hanmw0707 작성일아까 코드 관련해서 고견 남겨주셔서 감사합니다. 선생님. 추가적으로 질문드리고 싶은 게 있는데, 제가 추가로 저 회로에 버저를 달고 싶은데 공간이 부족할 것 같아 저렇게 빽빽하게 저항을 구성했습니다. 혹시 저항을 여유있게 연결하면서 버저를 연결할 방법이 있을 지 여쭙고 싶습니다.
master님의 댓글
master 작성일부저 정도의 공간은 충분한 것으로 보입니다.