본문 바로가기
# Semiconductor/- Semicon Academy

[Harman 세미콘 아카데미] 18일차 - Timer/Counter, Interrupt 실습

by Graffitio 2023. 7. 12.
[Harman 세미콘 아카데미] 18일차 - Timer/Counter, Interrupt 실습
728x90
반응형
[Timer/Counter]

 

#define F_CPU 160000000UL
#include <avr/io.h>
#include <avr/interrupt.h> // 인터럽트 사용을 위한 헤더파일
#include <stdio.h>

int cnt; // 카운트 값을 저장할 변수 선언

ISR(TIMER0_OVF_vect){ // 타이머/카운터 0번의 오버플로우 인터럽트 서비스 루틴 함수
                      // 
	cnt++;
	TCNT0 = 131; // 약 2ms를 만들기 위한 초기값
	
	if(cnt == 500) // 500번째 인터럽트 발생 시 LED 출력을 변경함. 약 1초(1,000ms)
	{
		PORTD = ~PORTD; // 출력 반전
		cnt = 0;
	}
}


int main(void)
{
	DDRD = 0xff;
	//PORTD = 0x00; // 다 꺼진 것을 시작
	PORTD = 0xaa; // 모양을 좀 더 이쁘게
	
	//256 분주 //
	TCCR0 = (1<<CS02) | (1<<CS01) | (0<<CS00); // 분주를 해야 하니까 CS01 02 00 에  값을 대입
	
	// Overflow interrupt enable //
	TIMSK = (1<<TOIE0);
	
	sei(); // 인터럽트를 사용하기 위해, 글로벌 인터럽트를 활성화시켜줘야 한다.
	
	
	while (1)
	{
	}
}

 


 

[Interrupt]

 

Interrupt 예제

 

버튼 신호에 따라 반응하는 interrupt

 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// 인터럽트 서비스 루틴 정의 //
ISR(INT4_vect) // 외부 인터럽트 INT4(PORTD-4) IST 정의
{ 
	PORTD ^= (1<<0); // 반전시켜서 보낼 것이다.
}

ISR(INT5_vect)
{
	PORTD ^= (1<<0);
}



int main(void)
{
	// INT4 가 Falling edge(1 0)일 때, 인터럽트 발생
    EICRB |=  (1<<ISC41) | (0<<ISC40); // button4는 누를 때(1->0) 출력
	
	// INT5 Rising edge(1 1)일 때, 인터럽트 발생
	EICRB |= (1<<ISC51) | (1<<ISC50); // button5는 눌렀다가 뗄 때(1->0) 출력
	
	// INT4, INT5 인터럽트 활성화
	EIMSK |= (1<<INT5) | (1<<INT4);
	
	// 버튼 입력 설정
	DDRE &= ~(1<<DDRE5) | ~(1<<DDRE4); // PORTE의 4번과 5번 핀에 해당하는 DDR을 0으로 비트마스킹
                                       // 0으로 설정해야 입력을 받음 
	
	DDRD = 0xff; // LED 출력 설정
	
	sei(); // SREG I[7](I의 8번째 비트) 를 1로 set
	       // 반대 개념은 cli(); -> 0으로 만들어주는 것
	
    while (1) 
    {
    }
}

 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

uint8_t ledShift = 0xfe; // 1111 1110 으로 시작

ISR(INT4_vect)
{
	ledShift = ledShift << 1; // 함수 호출 시, led 한 칸 좌시프트
	ledShift = ledShift | 0x01; // 한 칸 시프트하면, 0bit가 0으로 채워지므로 0x01과 OR 연산
	
	if (ledShift == 0xff) // 끝까지 가면, 초기화
	{
		ledShift = 0xfe;
	}
	PORTD = ledShift;
}


int main(void)
{
	DDRD = 0xff;
	DDRE &= ~(1<<DDRE4);
	
	EICRB |= (1<<ISC41) | (0<<ISC40); // falling edge일 때 동작
	EIMSK |= (1<<INT4); // INT4에 해당하는 핀의 인터럽트 활성화
	
	sei();
	
    while (1) 
    {
    }
}

 

<Debounce code 추가>

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

// 이제까지는 디바운스 코드를 안 썼다.
// while문은 계속 돌아가는 것이지만, 그 외부의 함수는 1번 실행하고 초기화된다.
// 따라서 버튼을 작동시켰을 때 사용되는 디바운스 코드는 while문 안으로 들어가야 한다.

volatile uint8_t ledShift = 0xfe;
uint8_t buttonPressed = 0; // 초기값은 0

ISR(INT4_vect)
{
	buttonPressed = 1; // ISR 함수가 호출되면 buttonPressed 상태만 변화시킴(반전)
}


int main(void)
{
    DDRD = 0xff; // PORTD all 출력으로 설정
	DDRE &= ~(1<<DDRE4); // PORTE 4번 핀 입력 설정
	PORTD = ledShift;  // 전역변수 선언해놨으니 사용 가능
                       // 초기화
	
	// 4번 핀을 인터럽트로 사용하기 위한 준비 //
	EICRB |= (1<<ISC41) | (0<<ISC40); // INT4 핀을 falling edge로 사용할 것이다.
	EIMSK |= (1<<INT4); // 4번 핀의 인터럽트 활성화
	sei(); // 글로벌 인터럽트 활성화
	
    while (1) 
    {
		if (buttonPressed) // buttonPressed 눌린 상태라면 = 인터럽트가 발생하였다면?
                           // 초기값 0, 버튼 누르는 순간 ISR 함수 호출로 1로 바뀜
		{
			_delay_ms(50);
			if (!(PINE & (1<<PINE4))) // 버튼이 계속 눌려져 있는지 확인
                                      // 풀업 저항으로 인해 pin4는 1로 유지 중
			              // 버튼을 누르는 순간, pin4의 입력이 1->0으로 바뀜
                                      // PINE = xxx0xxxx (x = N/A, PIN의 초기값은 N/A)
				      // 1<<PINE4 : 00010000
				      // 둘이 AND연산하면, 00000000 = 0
				      // ∴ 버튼이 눌려있다면? 이라는 의미가 된다.
			{
				ledShift = (ledShift << 1 ) | 0x01; // 한 칸 좌시프트하고, 빈 칸 1로 채워줌
				if (ledShift == 0xff) // 끝까지 갔다면, 초기화
				{
					ledShift = 0xfe;
				}
				PORTD = ledShift; // 끝까지 안 갔다면, 시프트한 ledShift를 출력해라
			}
			buttonPressed = 0; // 버튼 안 눌려 있다면, buttonPressed = 0
		}
    }
}

 


 

Interrupt의 활용

 

1. FND 설치

 

PINE6 : g, PINE5 : f, PINE4 : e, PINE3 : d, PINE2 : c,   PINE1 : b,  PINE0 : a 연결

 

 

2. Interrupt 발생할 때마다 FND 카운트 1씩 증가하게끔 코딩

 

#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

int cnt; // 전역변수로 카운트 선언
uint8_t buttonState = 0; // 디바운스 코드 작성을 위해 선언

// Segment 배열 선언 //
uint8_t seg_arr[] ={
	0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x27, 0x7f, 0x67
};


ISR(INT4_vect) // PORTE 4번 핀의 인터럽트 서비스 루틴 생성
{
	buttonState = 1; // 인터럽트 발생 시, 실행
}


int main(void)
{ 
    DDRA = 0xff; // PORTA all 출력 모드
	DDRE &= ~(1<<PINE4); // PORTE 입력 모드
	
	// 4번 핀 인터럽트 활성화 //
	EICRB |= (1<<ISC41) | (0<<ISC40);
	EIMSK |= (1<<INT4);
	sei();

    while (1) 
    {
		if (buttonState)
		{
			_delay_ms(50);
			if (!(PINE & (1<<PINE4))) 
                			// 눌러진 상태라면 PINE3의 입력이 1->0, 
                                      // 1<<PINE4 : 00010000
                                      // PINE = xxx0xxxx
                                      // 둘이 AND 연산하면, 00000000
			{
				if(cnt<10)
				{
					PORTA = seg_arr[cnt]; // seg_arr의 cnt번째 element를 출력
					cnt++;
					if(cnt == 10) cnt=0; // 끝까지 가면, 초기화
				}
			}
			buttonState = 0;
		}
			
    }
}

 

728x90
반응형