생성자
반드시 생성자가 있어야 하는 경우
객체를 만들 때 반드시 생성자가 있어야 하는 경우가 있다. 예를 들어 멤버 변수 중에 const 속성을 가진 것이 있거나 레퍼런스 타입이 있다면 반드시 생성자를 필요로 한다. 왜냐하면 이런 변수들은 처음에 반드시 초기화 되어야 하기 때문이다.

 // 멤버 초기화 리스트를 사용하여 초기화
#include <iostream>
using namespace std;

class NeedConstructor
{
public:
  const int maxCount;
  int &ref;
  int sample;

  // 생성자
  NeedConstructor();
  NeedConstructor(int count, int& number);
};

NeedConstructor::NeedConstructor()
:maxCount(100), ref(sample)    // 생성자 초기화 리스트 
{
  sample = 200;
}

NeedConstructor::NeedConstructor(int count, int& number)
:maxCount(count), ref(number)  // 생성자 초기화 리스트
{
  sample = 200;
}

int main()
{
  int number = 400;

  // 객체를 생성한다.
  NeedConstructor cr(300, number);

  // 내용을 출력한다.
  cout << "cr.maxCount = " << cr.maxCount << endl;
  cout << "cr.ref = " << cr.ref << endl;

  return 0;
}


 // 생성자를 사용해서 임시 객체 만들기
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자들
  Point();
  Point(int initX, int initY);
  Point(const Point& pt);
};

Point::Point()
{
  x = 0;
  y = 0;
}

Point::Point(int initX, int initY)
{
  x = initX;
  y = initY;
}

Point::Point(const Point& pt)
{
  x = pt.x;
  y = pt.y;
}

void Point::Print()
{
  cout << "(" << x << ", " << y << ")" << endl;
}

// Point 객체를 인자로 요구하는 함수
void Area(const Point& pt)
{
  // (0, 0)과 pt가 이루는 사각형의 면적을 구한다.
  int area = pt.x * pt.y;

  // 결과 출력
  cout << "(0, 0)과 이루는 사각형의 면적 = " << area << endl;  
}

int main()
{
  // 현재 x, y의 정수 값을 가지고 있다고 가정
  int x = 5;
  int y = 7;

  // Area()함수를 호출하기 위하여
  // Point 객체를 만든다.
  Point temp(x, y);

  // 함수 호출
  Area(temp);

  return 0;
}

// 임시 객체는 Area()함수 호출이 끝나면 자동적으로 소멸한다.

   소멸자
소멸자는 객체가 소멸할 때 자동적으로 호출되는 함수다. 소멸자의 이름은 생성자의 이름 앞에 틸드(~)를 붙인 형태를 갖는다. 또한 소멸자는 클래스에 오직 하나만 존재할 수 있다.

// 간단한 동적 배열 클래스
#include <iostream>
using namespace std;

class DynamicArray
{
public:
  int *arr;

  // 생성자
  DynamicArray(int arraySize);

  // 소멸자
  ~DynamicArray();
};

DynamicArray::DynamicArray(int arraySize)
{
  // 동적으로 메모리를 할당한다.
  arr = new int[arraySize];
}

DynamicArray::~DynamicArray()
{
  // 메모리를 해제한다
  delete[] arr;
  arr = NULL;
}

int main()
{
  // 몇 개의 정수를 입력할지 물어본다.
  int size;
  cout << "몇 개의 정수를 입력하시겠소? ";
  cin >> size;

  // 필요한 만큼의 메모리를 준비한다.
  DynamicArray da(size);

  // 정수를 입력 받는다.
  for(int i = 0 ; i < size ; ++i)
  {
    cin >> da.arr[i];
  }

  // 역순으로 정수를 출력한다.
  for(int i = size - 1 ; i >= 0 ; --i)
  {
    cout << da.arr[i] << " ";
  }
  cout << endl;

  return 0;
}


   Timer Counter
 TCCRx : 타이머/카운터 제어 레지스터(T/C Control Register)
FOCx, WGMx0, COMx1, COMx0, WGMx1, CSx2, CSx1, CSx0 등의 비트로 구성되어 있는데, 주로 동작 모드 및 프리스케일러 설정
 TCNTx : 타이머/카운터 레지스터(Timer Counter Register)
실제 카운터 되는 수치가 저장되는 레지스터
 TIMSK : 타이머/카운터 인터럽트 마스크 레지스터(Interrupt Mask Register)
OCIE2, TOIE2, TICIE1, OC1E1A, OC1E1B, TOIE1, OCIE0, TOIE0 등의 비트로 구성되어 있으며, 타이머의 동작 상태에 따라 여러가지 방법으로 인터럽트를 발생시킬 수 있는데, 이 인터럽트를 가능하게, 또는 불가능하게 설정하는 레지스터

 Prescaler
Prescaler는 타이머에 공급하는 입력클럭의 속도를 조절하는 분주기다. 분주기라는 말 자체가 의미 하듯이 클럭을 쪼갠다는, 즉 속도를 느리게 한다는 말이다. 예를 들어 8Mhz의 입력 클럭을 2분주하면 4Mhz가 된다. 8Mhz의 입력 클럭을 16분주하면 0.5Mhz가 된다.

Atmega128에는 4개의 타임 카운터가 있다. T/C0, T/C1, T/C2, T/C3이 있는데 0번과, 2번은 8비트 카운터이고 1번과 3번은 16비트 카운터이다. 오늘 실습에서는 T/C0 카운터를 이용하여 1초의 시간을 만들어 보았다. T/C0 카운터는 노멀모드로Overflow 인터럽트를 이용하여 구현하였다.

기본적으로 우리가 사용하는 Atmega128은 16Mhz로 동작한다. 즉 1초에 16,000,000번을 카운트 할 수 있는데, 우리가 사용하는 8비트 카운터 레지스터로는 255까지만 카운트가 가능하다. 그러므로 이 클럭을 쪼깨서 사용해야 한다.

 1초를 만드는 원리
1. Prescaler를 64로 주면 16Mhz 신호가 250000hz 신호로 바꾼다.(1초에 250000만번 카운트 할 수 있다는 의미다.)
2. TCNT0 레지스터의 초기값을 6으로 주고 250을 카운트 한다. (1/1000초(1ms)가 만들어 진다.) 여기서 초기값을 6으로 주는 이유는 Overflow 인터럽트를 이용하기 때문이다. 256을 카운트 하는 순간 오버플로우가 발생해서 인터럽트가 발생하게 되는 것이다.
3. 오버플로우 인터럽트가 발생할 때마다 카운트를 해서 1000번을 하면 1초가 만들어진다.

 // Timer Counter를 이용해서 Delay_ms 구현하여 LED를 1초마다 깜빡이는 코드
#include <avr/signal.h>
#include <avr/interrupt.h>

#define CPU_CLOCK        16000000    // CPU clock = 16Mhz
#define TICKS_PER_SEC    1000        // Ticks per sec = 1000
#define PRESCALER        64            // 클럭의 배수

volatile unsigned int g_elapsed_time;    // 시간 변수

void initLED(void);                        // LED 초기화
void setTCCR0(void);                    // TCCR0 설정
void initTCNT0(void);                    // TCNT0 초기화
void setTIMSK(void);                    // TIMSK 설정
void toggleLED(char *state);            // LED 반전
void sleep(unsigned int elapsed_time);   
SIGNAL(SIG_OVERFLOW0);                    // timer0의 오버플로우 함수

int main(void)
{
    char state = 0;

    initLED();

    setTCCR0();

    initTCNT0();

    setTIMSK();

    sei();    // SREG 레지스터에서 인터럽트 활성화

    while(1)
    {
        toggleLED(&state);
        sleep(1000);    // 1초 대기
    }
    return 0;
}

void initLED(void)                        // LED 초기화
{
    DDRF = 0xff;
    PORTF = 0xff;
}

void setTCCR0(void)                        // TCCR0 설정
{
    TCCR0 = 0x04;    // Normal모드, Prescaler 64로 설정
}

void initTCNT0(void)                    // TCNT0 초기화
{
    TCNT0 = 256 - (CPU_CLOCK / TICKS_PER_SEC / PRESCALER);
}

void setTIMSK(void)                        // TIMSK 설정
{
    TIMSK = 0x01;    // Overflow 인터럽트 설정
}

void toggleLED(char *state)                // LED 반전
{
    if(*state == 1)
    {
        PORTF = 0x00;
        *state = 0;
    }
    else
    {
        PORTF = 0xff;
        *state = 1;
    }
}

void sleep(unsigned int elapsed_time)
{
    g_elapsed_time = 0;

    while(g_elapsed_time < elapsed_time);
}

SIGNAL(SIG_OVERFLOW0)                    // timer0의 오버플로우 함수
{
    g_elapsed_time++;

    // TCNT0 레지스터 초기화
    TCNT0 = 256 - (CPU_CLOCK / TICKS_PER_SEC / PRESCALER);
}



+ Recent posts