| External Interrupt
: Interrupt의 한 종류로 주로 다른 Device(컴퓨터, 센서 등)에서 발생하는 특정 신호에 의해 발생한다.
들어가기에 앞서..
Signal State 종류 : Low, Rising edge, High, Falling edge
| AVR External Interrupt
: AVR ATmega128a에서 지원하는 External Interrupt는 총 8개이며 설정 및 사용방법은 Datasheet application Note 활용
● EICRA(External Interrupt Control Register- A) : External Interrupt 제어 레지스터(0 ~ 3 → 4개)
- ISCn bit 설정
- 0 0 : Low level 일 때 Interrupt
- 0 1 : 예약된 동작(아무 동작 안함)
- 1 0 : Falling edge 일 때 Interrupt
- 1 1 : Rising edge 일 때 Interrupt
● EICRB(External Interrupt Control Register - B) : External Interrupt 제어 레지스터(4 ~ 7 → 4개)
- ISCn bit 설정
- 0 0 : Rising edge, Falling edge 일 때 발생 → 상태가 변할 때마다 Interrupt 발생
- ...(생략)
▶ EICR : External Interrupt 모드 정의 → 어떤 신호일 때 Interrupt 발생시킬지
● EIMSK(External Interrupt Mask Register) : 해당 External Interrupt 활성화(Enable)
| Ultrasonic(HC-SR04)
: 초음파 송수신 센서(VCC, Trig, Echo, GND 4개 핀)
- 최소 10us 이상의 high level signal
- 8개의 40kHz 초음파를 전송하고 펄스 신호가 돌아오는걸 감지
- 가청 주파수 : 20Hz ~ 20kHz → Ultrasonic은 사람이 들을 수 없음 - 신호가 high level로 돌아오면 초음파의 송신부터 수신까지의 시간으로 거리 계산 가능
- 음속 : 340m/s → 음속과 초음파 전달 시간으로 거리를 구할 수 있음(거리 = 속도 * 시간)
- Ultrasonic 동작 flow
- Trigger pulse : MCU → Ultrasonic 10us High Sinal(Start signal)
- MCU가 Ultrasonic에게 '신호 받을 준비 됐어!' - Tx : 8개의 40kHz 주파수 송신
- Echo pulse : Ultrasonic → MCU High signal
- Ultrasonic이 MCU에게 '주파수 쐈어!' - Rx : 주파수 수신(물체에 반사되어 돌아옴)
- Echo pulse : Ultrasonic → MCU Low signal
- Ultrasonic이 MCU에게 '주파수 돌아왔어!'
- Trigger pulse : MCU → Ultrasonic 10us High Sinal(Start signal)
코드 설계
1. Echo pulse가 High로 유지된 시간으로 거리를 구할 수 있음.
→ Timer/Counter의 TCNT register 사용(클럭 신호마다 +1 저장하는 레지스터)
2. Echo pulse의 상태가 변할 때(Falling edge, Rising edge) Interrupt 발생시켜 어떠한 프로세스 실행
▶ Echo pulse가 Low → High로 변했을 때(Rising edge) Interrupt 발생
시간 측정 시작) Timer/Counter 시작(일정 주기마다 TCNT + 1)
→ Echo pulse가 High → Low로 변했을 때(Falling edge) Interrupt 발생
→ 시간 측정 종료) Timer/Counter TCNT register에 저장된 값으로 시간 측정
HW 구성
- AVR PD2에 Ultrasonic Echo 연결
→ External Inerrupt 2 사용
SW 구성
External Inerrupt 사용 설정 순서(INT2 사용 가정)
- EICRA 레지스터 설정 : ISC21, ISC20 bit 설정 → External Interrupt mode 설정(언제 Interrupt 발생시킬지)
- EIMSK 레지스터 설정 : INT2 bit 설정 → Interrupt 사용 enable
- sei()
- ISR()
● Timer/Counter 1 사용(16bit) : 시간 count용(Interrupt 사용 X) → TIMSK 설정 할 필요 없음
● 1/64 prescaler 설정 : TCCR1B = 0000 0011(2)
● 1/64 분주기 설정 일때 거리(편도)
= TNCT * 340 * 4 * 0.000001 / 2
= TCNT * 0.00068(단위 : 미터)
= TCNT * 0.068(단위 : cm)
구현(Distance Detection System using Ultrasonic sensor)
- 물체와의 거리를 cm 단위로 LCD 출력
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "driver/LCD/LCD.h"
#include "driver/Ultrasonic/Ultrasonic.h"
ISR(INT2_vect) // Rising edge 발생하면 실행
{
DRIVER_Ultransonic_ISR_Process();
}
int main(void)
{
DRIVER_Ultrasonic_init();
DRIVER_LCD_init();
sei();
uint8_t distance;
char buff[30];
while (1)
{
DRIVER_Ultrasonic_trigger(); // Low level 10us
if(DRIVER_Ultrasonic_getCompltFlag()) // complete flag가 있다 == 시간 측정 끝
// -> 외부 Interrupt 발생 시(Rising edge) 시행
{
DRIVER_Ultrasonic_clearCompltFlag(); // 이전 정보 삭제위해 clear
distance = DRIVER_Ultrasonic_getDistance();
sprintf(buff, "distance : %03d", distance);
DRIVER_LCD_writeString_XY(0, 0, buff); // LCD 출력
}
}
}
#include "Ultrasonic.h"
uint8_t Ultrasonic_distanceFlag; // 거리측정 완료 미완료 저장 변수 -> 0 or 1
void DRIVER_Ultrasonic_init()
{
// 초기화 설정
USONIC_DDR &= ~(1<<USONIC_ECHOpin); // Echo pin Input Mode
USONIC_DDR |= (1<<USONIC_TRIGpin); // Trigger pin Output Mode
EICRA |= ((1<<ISC21) | (1<<ISC20)); // Risging edge Interrupt
EIMSK |= (1<<INT2); // INT2 외부 Interrupt Enable
Ultrasonic_distanceFlag = 0;
}
void DRIVER_Ultransonic_ISR_Process()
{
if (USONIC_PIN & (1<<USONIC_ECHOpin)) // Rising edge 모드 일때() 참
// Echo pin masking data = 1이면 Rising edge
// -> Rising edge에서만 실행되는 코드
{
DRIVER_Ultransonic_timerStart(); // Interrupt 발생 시(Rising edge) TimerCounter 통해서 시간 count 시작
DRIVER_Ultransonic_switchInterrupt(FALLING_EDGE); // Falling edge일 때 Interrupt 발생하도록 스위칭
}
else // Fllaing edge에서만 실행되는 코드
{
DRIVER_Ultransonic_timerStop(); // Interrupt 발생 시(Falling edge) 시간 count 정지
DRIVER_Ultrasonic_setCompltFlag(); // 시간 측정 끝났다고 저장
DRIVER_Ultransonic_switchInterrupt(RISING_EDGE); // Rising edge 모드로 스위칭
}
}
void DRIVER_Ultrasonic_trigger()
{
USONIC_PORT |= (1<<USONIC_TRIGpin);
_delay_ms(15); // 15us Low level signal
USONIC_PORT &= ~(1<<USONIC_TRIGpin);
}
void DRIVER_Ultransonic_timerStart()
{
TCNT1 = 0; // TCNT register 값 초기화
// TinerCounter - 16bit, TCNT1, Interrupt는 사용하지 않기때문에 Prescaler만 설정
// Prescaler 1/64 -> 4us마다 TCNT register +1
TCCR1B |= ((0<<CS12) | (1<<CS11) | (1<<CS10));
}
void DRIVER_Ultransonic_timerStop()
{
TCCR1B &= ~((1<<CS12) | (1<<CS11) | (1<<CS10)); // TimerCounter NO CLOCK 설정 (cs bit 0 설정)
}
uint16_t DRIVER_Ultrasonic_getDistance() // 거리 계산
{
/*
1/64 분주기 설정 일때 거리(편도)
= TNCT * 340 * 4 * 0.000001 / 2
= TCNT * 0.00068(단위 : 미터)
= TCNT * 0.068(단위 : cm)
*/
return USONIC_TCNT * 0.068;
}
void DRIVER_Ultransonic_switchInterrupt(uint8_t mode)
{
if (mode == FALLING_EDGE)
{
EICRA &= ~((1<<ISC21) | (1<<ISC20)); // 이전 정보 지우기 위해 초기화
EICRA |= ((1<<ISC21) | (0<<ISC20)); // Falling edge Interrupt 세팅
}
else
{
EICRA &= ~((1<<ISC21) | (1<<ISC20));
EICRA |= ((1<<ISC21) | (1<<ISC20)); // Rising edge Interrupt 세팅
}
}
uint8_t DRIVER_Ultrasonic_getCompltFlag()
{
return Ultrasonic_distanceFlag;
}
void DRIVER_Ultrasonic_setCompltFlag()
{
Ultrasonic_distanceFlag = 1;
}
void DRIVER_Ultrasonic_clearCompltFlag()
{
Ultrasonic_distanceFlag = 0;
}
동작 영상
전체 소스 코드
Made By Minseok KIM
'AVR > Study' 카테고리의 다른 글
[AVR] C언어 OOP(Object-Oriented Programming) 구현 (0) | 2024.02.25 |
---|
Let's Be Happy!
도움이 되었으면 좋겠어요 :)