Busan IT/AVR 컨트롤러

타이머를 활용한 7-segment count up

newind2000 2015. 4. 21. 12:38

타이머를  활용한 7-segment count up


학습목표

- 내부 타이머와 인터럽트를 활용하여 1초에 한번 숫자가 올라가는 카운트 업 회로와 코드를 만들 수 있다.


타이머(timer)와 카운터(counter)의 차이

타이머는 주로 컴퓨터 내부 클록(clock)을 사용하고, 카운터는 외부 클록을 사용한다.

 

<타이머>

타이머는 작동 방식은 주어진 비트 수의 최대숫자까지를 클록을 활용한 신호로 숫자를 센 다음에 최대자리수를 초과한 수가 되면 0으로 다시 초기화되면서 내부 인터럽트(interrupt)를 발생시킨다.

 

*** 1초 내부 카운터를 만들어 보기 ***

 

우리가 사용하는 ATmega256016MHz이다. 이 말은 1초에 16000000번의 클럭이 나온다는 것이다. 1번의 클럭이 나오는 시간을 계산해 보면,

1/16000000 = 0.0000000625

알아보기 쉽게 하기 위하여,

0.0000000625 * 1000 = 0.0000625ms(밀리 세컨)

0.0000625 * 1000 = 0.0625us(마이크로 세컨)

0.0625 * 1000 = 62.5ns(나노 세컨)

 

여기에 분주비(pre-scaler)1024로 설정하고 8bit임으로 카운터가 256까지 가능함으로,

 

62.5ns * 1024(분주비) * 256(8비트 카운터) = 0.016384

 

이것을 일초로 한번 카운터를 하려면,

 

1/0.016384 = 61.03515625 에 한번 카운터가 작동하게 해야 한다다.

 

지난 시간에 사용했던 코드에 이 숫자를 사용하여 카운터가 1초인지 확인해 보자!

 

I. 서술

 

내부 인터럽트가 0.016384만에 한번 일어나는 것을 착안하여. 1초에 한번 LED에 불을 켜기 위해서는 카운터를 활용하여 내부 인터럽트가 31(61/2)번 발생할 때 발생할 때 LED에 값을 반전 시키면 불이 1초마다 한 번씩 들어오게 된다.

 

주의할 점은 interrupt안에 카운터 변수 값을 초기화해주는 명령어를 넣으면 카운터가 올라가지 않기 때문에 전역변수로 선언하여 카운터를 올려야 한다.

 

II. 코딩

1. 지난 번에 사용했던 코드에서 인터럽트 안에 카운터를 사용하여 1초에 한번 불이 들어오도록 카운터 값을 31로 넣어준다.

 

/*** 코드 ***/

<main.c>

#include <atm2560.h>
#include "SMART.h"

unsigned char LED=0xFF;
volatile unsigned int uiCnt;
unsigned char ucCnt=0;

int main(void)
{
  /*** 포트 설정 ***/

  DDRC = 0xFF;
  PORTC = LED;
  unsigned char ucCnt=0;
  


  /*** 타이머 카운터를 위한 설정 ***/

  SREG &= 0x7F;  //Global interrupt
  TIMSK0 = 0x01;  //Timer 0 overflow
  TCCR0A = 0x00;  //Timer Mode : 일반 모드
  TCCR0B = 0x05;  //분주비 1024
  TCNT0 = 0x00;  //Timer Counter 초기화
  SREG |= 0x80;  //Global Interrupt 
  
  while(1)
  {

    ;
    /*for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    for(uiCnt=0; uiCnt<62500; ++uiCnt)
      TCNT0 = 0x01;
    
    while(TCNT0!=0);*/

          
  }
  
  return 0;
}

void __vector_23(void)
{    
    ++ucCnt;

    if(ucCnt==31)
    {
      LED ^= 0xFF;
      PORTC = LED;
      ucCnt=0;
    }
}

<smart.h>

#ifndef __SMART_H__
#define __SMART_H__

/**** General Purpose Register A - L ****/

#define  PORTA (*((volatile unsigned char*)0x22))
#define   DDRA  (*((volatile unsigned char*)0x21))
#define   PINA  (*((volatile unsigned char*)0x20))

#define  PORTB (*((volatile unsigned char*)0x25))
#define   DDRB  (*((volatile unsigned char*)0x24))
#define   PINB  (*((volatile unsigned char*)0x23))

#define  PORTC (*((volatile unsigned char*)0x28))
#define   DDRC  (*((volatile unsigned char*)0x27))
#define   PINC  (*((volatile unsigned char*)0x26))

#define  PORTD (*((volatile unsigned char*)0x2B))
#define   DDRD  (*((volatile unsigned char*)0x2A))
#define   PIND  (*((volatile unsigned char*)0x29))

#define  PORTE (*((volatile unsigned char*)0x2E))
#define   DDRE  (*((volatile unsigned char*)0x2D))
#define   PINE  (*((volatile unsigned char*)0x2C))

#define  PORTF (*((volatile unsigned char*)0x31))
#define   DDRF  (*((volatile unsigned char*)0x30))
#define   PINF  (*((volatile unsigned char*)0x2F))

#define  PORTG (*((volatile unsigned char*)0x34))
#define   DDRG  (*((volatile unsigned char*)0x33))
#define   PING  (*((volatile unsigned char*)0x32))

#define  PORTH (*((volatile unsigned char*)0x102))
#define   DDRH  (*((volatile unsigned char*)0x101))
#define   PINH  (*((volatile unsigned char*)0x100))

#define  PORTJ (*((volatile unsigned char*)0x105))
#define   DDRJ  (*((volatile unsigned char*)0x104))
#define   PINJ  (*((volatile unsigned char*)0x103))

#define  PORTK (*((volatile unsigned char*)0x108))
#define   DDRK  (*((volatile unsigned char*)0x107))
#define   PINK  (*((volatile unsigned char*)0x106))

#define  PORTL (*((volatile unsigned char*)0x10B))
#define   DDRL  (*((volatile unsigned char*)0x10A))
#define   PINL  (*((volatile unsigned char*)0x109))


/* 인터럽트 사용을 위한 레지스터 */

#define EICRA (*((volatile unsigned char*)0x69))
#define EICRB (*((volatile unsigned char*)0x6A))
#define EIMSK (*((volatile unsigned char*)0x3D))
#define EIFR  (*((volatile unsigned char*)0x3C))
#define SREG (*((volatile unsigned char*)0x5F)) 

/* PCINT 사용을 위한 레지스터 */

#define PCICR (*((volatile unsigned char*)0x68))
#define PCIFR (*((volatile unsigned char*)0x3B))
#define PCMSK2 (*((volatile unsigned char*)0x6D))
#define PCMSK1 (*((volatile unsigned char*)0x6C))
#define PCMSK0 (*((volatile unsigned char*)0x6B))

/* 타이머 오버 플로우 */

// General Timer/counter Control Register
#define GTCCR (*((volatile unsigned char*)0x43)) 

// Register for Timer/Counter 0
#define OCR0A   (*((volatile unsigned char*)0x47)) // 타이머 카운터 비교 레지스터
#define OCR0B   (*((volatile unsigned char*)0x48))
#define TCCR0A   (*((volatile unsigned char*)0x44))
#define TCCR0B   (*((volatile unsigned char*)0x45))
#define TCNT0   (*((volatile unsigned char*)0x46))
#define TIFR0   (*((volatile unsigned char*)0x35))
#define TIMSK0   (*((volatile unsigned char*)0x6E))


/* CPU 동작시간을 맞춰주기 위한 Dealy문과 값 지정 */

#define  Delay(x)    for(uiCnt=0; uiCnt<(dNum1); ++uiCnt)

#define  dNum1 50000
#define  dNum2 300000
#define  dNum3 10000000

/* 함수의 원형 */

void __vector_23(void)__attribute__((signal,used,externally_visible));


#endif //__SMART_H__



해당 코드를 완성하였으면 FND를 활용하여 시계를 만들어 보자!


I. 서술

예전에 짜 놓았던 7segment카운트 업 코드를 삽입하여 1초에 1번씩 올라가는 카운터에 맞춰 시계를 만들어 보자.


II. 작업 나누기

0. 우선 새로운 소일거리를 위한 새로운 폴더를 만든다.

- 코드

- 회로

1. 과거에 짜 놓았던 7segment 코딩을 넣어 붙힌다.

2. 내부 인터럽트가 발생하면 FND가 1씩 증가하고 0이 되면 다시 리셋이 되는 코드를 넣어준다.

4. 프로테우스로 회로를 작성하여 해당 코드를 넣고 작동하는지 확인한다.

3. 프로테우스에서 작동된 것이 확인 되면 브레드보드와 7segment에 연결하여 작동하는지 확인한다.



/*** 코드 ***/

#include <atm2560.h>
#include "SMART.h"

unsigned char FND[10= {0x400x790x240x300x190x120x020x580x000x10};
volatile unsigned int uiCnt;
unsigned char ucCnt=0, ucCnt2=0;

int main(void)
{
  /*** 포트 설정 ***/

  DDRC = 0xFF;
  PORTC = 0xFF;

  /*** 타이머 카운터를 위한 설정 ***/

  SREG &= 0x7F;  //Global interrupt
  TIMSK0 = 0x01;  //Timer 0 overflow
  TCCR0A = 0x00;  //Timer Mode : 일반 모드
  TCCR0B = 0x05;  //분주비 1024
  TCNT0 = 0x00;  //Timer Counter 초기화
  SREG |= 0x80;  //Global Interrupt 
  
  while(1)
  {

    ;
          
  }
  
  return 0;
}

void __vector_23(void)
{    
    ++ucCnt;

    if(ucCnt==61)
    {
      PORTC=FND[ucCnt2%10];
      ++ucCnt2;
      ucCnt=0;
      if(ucCnt2==250)
        ucCnt2=0;
    }
}


 



반응형