본문 바로가기

AVR/AVR 연습, Tutorial

AVR 타이머/카운터 비교매치 인터럽트로 0.5초 마다 LED 깜박이기 - AVR 연습



오랜만에 찾아온 AVR 연습 포스팅입니다.^

비교 매치 인터럽트를 무엇을 만들어서 연습 포스팅으로 올릴까 고민하다가 또 LED로 결정했고, 아이디어는 블로그에 질문 주신 분이 MCU의 유휴 상태를 표시하기 위해서 타이머를 사용하는 것에 얻어 메인 프로그램 (2진수 출력)을 하면서 0.5초 간격으로 MCU의 유휴상태를 표시하기 위한 노랑 LED를 깜박이는 예제를 짰습니다.



회로도를 볼까요.!




TINY2313모듈에 저항 LED만 연결해놓은 자주 본 간결한 회로입니다.

우측 LED 8개가 2진수 증가 데이터를 출력할 B포트이고, D포트의 0번핀이 0.5초 비교매치 인터럽트 발생시마다 MCU의 유휴상태를 나타내어 줄 노란색 LED입니다.

그럼 이제 소스코드 보실까요.!



CodeVision AVR용 소스코드


#include <tiny2313.h>    // tiny2313의 입출력 관련 헤더파일을 포함합니다.

#include <delay.h>    // 지연함수 관련 헤더파일을 포함합니다.


unsigned char state=0, count=15;    // 부호없는 변수 state 초기값 0과, count 초기값 15 변수를 생성합니다.


interrupt [TIM0_COMPA] void timer0_compa_isr(void)    // 비교매치 인터럽트 발생시 점프

{

  count--;    // count 변수 값을 1 감소합니다.

  if(count==0)    // 만약 count 변수가 0과 같다면.

  {

    if(state==0)    //    그리고 state변수도 0과 같다면.

    {

    PORTD=0x01;    // 포트D의 0번핀에 5V (논리1) 출력

    state=1;    // state 변수를 1로 설정합니다.

    }

    else    // 아니라면

    {

    PORTD=0x00;    // 포트D의 0번핀에 0V (논리0) 출력

    state=0;    // state 변수를 0으로 설정합니다.

    }

  count=15;    // count 변수 값을 15로 설정합니다.

  } 

}


void main(void)    // main() 함수를 호출합니다.

{

unsigned char i=0;    // 부호없는 변수 초기값이 0인 i를 생성합니다.


PORTB=0x00;    // 포트B의 초기값을 전체 0V (논리0)으로 설정합니다.

DDRB=0xFF;    // 포트B의 입출력 상태를 전체 (논리1) 출력으로 설정합니다.


PORTD=0x00;    // 포트D의 초기값을 전체 0V (논리0)으로 설정합니다.

DDRD=0x01;    // 포트D의 입출력 상태를 0번 포트만 (논리1) 출력으로 설정합니다.


TCCR0A=(1<<WGM01) | (1<<WGM00);    // 타이머, 카운터 설정을 CTC모드로 설정합니다.

TCCR0B=0x05;    // 타이머, 카운터 컨트롤 레지스터B 클럭을 1024 분주 해 사용합니다.

TCNT0=0x00;    // 타이머, 카운터0번 카운팅 레지스터 0으로 초기화

OCR0A=0xFA;    // TCNT0과 비교값 OCR0A값을 250으로 설정


TIMSK=0x01;    // 타이머, 카운터 마스터 레지스터, 타이머, 카운터0 비교매치 인터럽트 허용


#asm("sei")    // 전체 인터럽트를 허용합니다.


while (1)    // 아래 구문을 무한 반복합니다.

      {

      PORTB=i++;    // 포트B에 i 증가 값을 출력합니다.

      delay_ms(200);    // 위 상태를 0.2초 간 유지합니다.

      }

}



AVR Studio 용 소스코드


#include <avr/io.h>    // AVR 입출력 관련 기본 헤더파일을 포함합니다.

#define F_CPU 8000000UL    // 사용하는 크리스탈 주파수를 상수로 설정합니다.

#include <util/delay.h>    // 지연 함수 관련 헤더파일을 포함합니다.

#include <avr/interrupt.h>    // 인터럽트 발생 관련 헤더파일을 포함합니다.


volatile unsigned char state=0, count=15;    // 최적화 하지않는 부호없는 변수 state와 count를 생성합니다. 초기값은 각각 0, 15입니다.


ISR(TIMER0_COMPA_vect)    // 비교매치 인터럽트 발생시 여기로 점프합니다.

{

  count--;    // count 값을 1 감소시킵니다.

  if(count==0)    // count 변수값이 0과 같으면

  {

    if(state==0)    // 그리고 state 변수 값이 0과 같으면

    {

    PORTD=0x01;    // 포트D의 0번 핀에 5V (논리1)을 출력합니다.

    state=1;    // state 변수값을 1로 설정합니다.

    }

    else    // 아니라면

    {

    PORTD=0x00;    // 포트D의 0번 핀에 0V (논리0)을 출력합니다.

    state=0;    // state 변수값을 0으로 설정합니다.

    }

  count=15;    // count 변수값을 15로 설정합니다.

  } 

}


int main(void)    // main()함수를 호출합니다.

{

unsigned char i=0;    // 부호없는 초기값이 0인 변수 i를 생성합니다.


PORTB=0x00;    // 포트B의 초기값을 전체 0V (논리0)으로 설정합니다.

DDRB=0xFF;    // 포트B의 입출력 상태를 전체 (논리1) 출력으로 설정합니다.


PORTD=0x00;    // 포트D의 초기값을 전체 0V (논리0)으로 설정합니다.

DDRD=0x01;    // 포트D의 입출력 상태를 0번 포트만 (논리1) 출력으로 설정합니다.


TCCR0A=(1<<WGM01) | (1<<WGM00);    // 타이머, 카운터 설정을 CTC모드로 설정합니다.

TCCR0B=0x05;    // 타이머, 카운터 컨트롤 레지스터B 클럭을 1024 분주 해 사용합니다.

TCNT0=0x00;    // 타이머, 카운터0번 카운팅 레지스터 0으로 초기화

OCR0A=0xFA;    // TCNT0과 비교값 OCR0A값을 250으로 설정


TIMSK=0x01;    // 타이머, 카운터 마스터 레지스터, 타이머, 카운터0 비교매치 인터럽트 허용


sei();    // 전체 인터럽트를 허용합니다.


while (1)    // 아래 구문을 무한 반복합니다.

      {

      PORTB=i++;    // 포트B에 i 증가 값을 출력합니다.

      _delay_ms(200);    // 위 상태를 0.2초 간 유지합니다.

      }


return 0; // main()함수에 반환 값은 0 입니다.

}



소스코드에 대한 부가 설명


타이머, 카운터0 오버플로우와 비교했을 때 달라지는 부분은 인터럽트 벡터 부분과 처리방식입니다. 오버플로우의 경우 8비트 프로세서 기준 8비트값 255를 넘으면 이 것을 오버플로우라 하여 인터럽트가 발생되는 구조로 처리하는 반면 비교 매치는 TCNT0값과 OCR0A 또는 B 값을 비교해서 같으면 인터럽트 벡터로 점프하여 해당 부분을 1회 처리합니다.

계산 시간은 8Mhz 크리스탈 기준 0.000000125초 * 1024 분주 * OCR0A 250 카운팅 하여  0.032초가 나오는데요 아직 0.5초가 아니므로 인터럽트 벡터 부분에 count라는 변수로 15번을 구동하지 않고 count변수가 0 이되면 LED 동작 실행문을 구동합니다. 이 때 시간은 약 0.48초 정도 나옵니다.


동작 영상 보기




2진수로 증가하면서 출력하고 있는 녹색 LED와는 달리 독립적으로 0.5초마다 깜박이는 노란LED의 모습을 볼 수가 있습니다.

다음은 타이머, 카운터 활용 PWM 출력이에요. ㅎㅎ 그런데 언제 쓸지를 모르겠네요 ㅎㅎ