클래스 상속
 기존의 클래스를 상속받아서 새로운 클래스를 만드는 방법이다.
class HTMLWriter : public DocWriter
{
}
DocWriter - 부모 클래스 // HTMLWriter - 자식 클래스

 자식 클래스에는 부모 클래스에 없는 새로운 멤버를 추가할 수도 있고, 부모 클래스에 이미 존재하는 멤버 함수를 새롭게 정의할 수도 있다. 자식 객체를 생성할 때는 자식 클래스의 생성자  뿐만 아니라 부모 클래스의 생성자도 호출된다. 따로 부모 클래스의 생성자 함수를 지정하지 않으면 부모 클래스의 디폴트 생성자가 호출된다. 생성자를 지정해 주려면 초기화 리스트에서 해야 한다.
 부모 클래스 생성자가 자식 클래스의 생성자보다 먼저 호출된다. 반대로 소멸는 자식 클래스의 소멸자가 호출된 후 부모 클래스의 소멸자가 호출된다.
자식 객체 생성 시 : 부모 클래스 생성자 -> 자식 클래스 생성자
자식 객체 소멸 시 : 자식 클래스 소멸자 -> 부모 클래스 소멸자

 부모 클래스의 객체를 자식 클래스로 대입하는 것은 불가능하다. 하지만 반대의 경우 ,즉 자식 클래스의 객체를 부모 클래스로 대입하는 것은 가능하다. 이때 부모 객체와 자식 객체에 공통적으로 있는 멤버들이 1:1로 대입된다.
 자식 클래스의 포인터나 레퍼런스를 사용해서 부모 객체를 가리킬 수 없다. 하지만 반대로 부모 클래스의 포인터나 레퍼런스로 자식 클래스의 객체는 가리킬 수 있다. 이 경우 실제 객체가 무엇이던 간에 상관없이 포인터 타입을 기준으로 호출되는 멤버 함수가 결정된다.

 접근제어
public : 모든 곳으로부터의 접근을 허용 한다.
protected : 자식 클래스의 멤버 함수로부터의 접근만 허용 한다.
private : 자신의 멤버 함수 외에는 접근할 수 없다.
외부로부터 숨겨야 하는 멤버는 protected로 지정한다.
그 밖의 경우는 public으로 지정한다.
반드시 자식 클래스에 숨기고 싶다면 private로 지정한다.

 Point.h
#ifndef POINT_H
#define POINT_H

// Point 클래수를 정의한다
class Point
{
public:
  // 멤버 함수
  void Print();
  void Offset(int x_delta, int y_delta);
  void Offset(const Point& pt);

  // 생성자들
  Point();
  Point(int initialX, int initialY);
  Point(const Point& pt);

  // 소멸자
  ~Point();

  // 접근자
  void SetX(int value);
  void SetY(int value);
  int GetX() const
  {
    return x;
  }
  int GetY() const
  {
    return y;
  }
private:
  // 멤버 변수
  int x, y;
};

#endif

 Point.cpp
#include "Point.h"
#include <iostream>
using namespace std;

void Point::SetX(int value)
{
  if(value < 0)
  {
    x = 0;
  }
  else if(value > 100)
  {
    x = 100;
  }
  else
  {
    x = value;
  }
}

void Point::SetY(int value)
{
  if(value < 0)
  {
    y = 0;
  }
  else if(value > 100)
  {
    y = 100;
  }
  else
  {
    y = value;
  }
}

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

void Point::Offset(int x_delta, int y_delta)
{
  SetX(x + x_delta);
  SetY(y + y_delta);
}

void Point::Offset(const Point& pt)
{
  Offset(pt.x, pt.y);
}

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

Point::Point(int initialX, int initialY)
{
  SetX(initialX);
  SetY(initialY);
}

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

Point::~Point()
{
}


 Rect.h
#ifndef RECT_H
#define RECT_H

#include "Point.h"

class Rect
{
public:
  // 생성자
  Rect();
  Rect(const Point& topLeft, const Point& bottomRight);
  Rect(int left, int top, int right, int bottom);

  // 각 점의 값 지정/얻기
  void SetTopLeft(const Point& topLeft);
  void SetBottomRight(const Point& bottomRight);
  void SetRect(int left, int top, int right, int bottom);
  Point GetTopLeft() const;
  Point GetBottomRight() const;
  void GetRect(int& left, int& top, int& right, int& bottom);

  // 넓이, 높이 계산
  int GetWidth() const;
  int GetHeight() const;

  // 내 용 출력
  void Print() const;

protected:
  Point _topLeft;
  Point _bottomRight;
};

#endif

 Rect.cpp
#include "Rect.h"
#include <iostream>
using namespace std;

Rect::Rect()
{

}

void Rect::SetTopLeft(const Point& topLeft)
{
  _topLeft = topLeft;
}

void Rect::SetBottomRight(const Point& bottomRight)
{
  _bottomRight = bottomRight;
}

void Rect::SetRect(int left, int top, int right, int bottom)
{
  _topLeft.SetX(left); 
  _topLeft.SetY(top); 
  _bottomRight.SetX(right); 
  _bottomRight.SetY(bottom); 
}

Point Rect::GetTopLeft() const
{
  return _topLeft;
}

Point Rect::GetBottomRight() const
{
  return _bottomRight;
}

void Rect::GetRect(int& left, int& top, int& right, int& bottom)
{
  left = _topLeft.GetX(); 
  top = _topLeft.GetY(); 
  right = _bottomRight.GetX(); 
  bottom = _bottomRight.GetY(); 
}

int Rect::GetWidth() const
{
  return (_bottomRight.GetX() - _topLeft.GetX() + 1);
}

int Rect::GetHeight() const
{
  return (_bottomRight.GetY() - _topLeft.GetY() + 1);
}

void Rect::Print() const
{
  cout << "{L=" << _topLeft.GetX() << ", T=" << _topLeft.GetY();
  cout << ", R=" << _bottomRight.GetX() << ", B=" <<
    _bottomRight.GetY() << "}\n";
}

Rect::Rect(const Point& topLeft, const Point& bottomRight)
:_topLeft(topLeft), _bottomRight(bottomRight)
{
}

Rect::Rect(int left, int top, int right, int bottom)
:_topLeft(left, top), _bottomRight(right, bottom)
{
}

 main.cpp
#include "Rect.h"
#include <iostream>
using namespace std;

int main()
{
  Rect rc1;
  Rect rc2(Point(1020), Point(3040));
  Rect rc3(50607080);

  rc1.Print();
  rc2.Print();
  rc3.Print();

  return 0;
}


   RS232

RS232는 표준 인터페이스의 하나로서 데이터를 직렬로 전송하며 대부분의 PC에 기본으로 장착되어 있다. 배선 수가 작으며, 통신 프로그래밍 구현이 쉽다. 하지만 병렬 전송에 비해 전송 속도가 느리고, 일대일 통신만 가능하다는 단점이 있다.


TXD - Transmit Data
비동기식 직렬통신 장치가 외부 장치로 정보를 보낼 직렬통신 데이터가 나오는 신호선이다.

RXD - Receive Data
외부 장치에서 들어오는 직렬통신 데이터를 입력받는 신호선이다
RTS - Ready To Send
컴퓨터와 같은 DTE장치가 모뎀 또는 프린터와 같은 DCE장치에게 데이터를 받을 준비가 됐음을 나타내는 신호선이다.

CTS - Clear To Send
모뎀 또는 프린터와 같은 DCE장치가 컴퓨터와 같은 DTE장치에게 데이터를 받을 준비가 됐음을 나타내는 신호선이다.

DTR - Data Terminal Ready
컴퓨터 또는 터미널이 모뎀에게 자신이 송수신 가능한 상태임을 알리는 신호선이며 일반적으로 컴퓨터등이 전원 인가후 통신 포트를 초기화한 이신호를 출력시킨다.

DSR - Data Set Ready
모뎀이 컴퓨터 또는 터미널에게 자신이 송수신 가능한 상태임을 알려주는 신호선이며 일반적으로 모뎀에 전원 인가 모뎀이 자신의 상태를 파악한 이상이 없을 신호를 출력시킨다.

DCD - Data Carrier Detect
모뎀이 상대편 모뎀과 전화선 등을 통해서 접속이 완료되었을 상대편 모뎀이 캐리어신호를 보내오며 신호를 검출하였음을 컴퓨터 또는 터미널에 알려주는 신호선이다.

RI - Ring Indicator
상대편 모뎀이 통신을 하기위해서 먼저 전화를 걸어오면 전화 벨이 울리게 된다. 이때 신호를 모뎀이 인식하여 컴퓨터 또는 터미널에 알려주는 신호선이며 일반적으로 컴퓨터가 이신호를 받게되면 전화벨 신호에 응답하는 프로그램을 인터럽터등을 통해서 호출하게 된다.


- 시작 비트, 정지비트 : RS232C 인터페이스 규격에서는 송수신 데이터의 신호는 부논리로 규정되어 있으므로 데이터의 비트는 1 -5~-15V 0 5~15V 대응한다. 실제 데이터 전송시 데이터의 앞에 Start Bit(논리 0) 1비트와 뒤에 Stop Bit(논리 1) 2비트와 패리티 비트를 부가해서 송신한다. 여기서 Start Bit Stop Bit 사용해서 수신측 통신소자가 1 캐릭터(8Bit)마다 동기를 맞출 있기 때문에 송수신 측의 동기신호 없이도 송수신이 가능한 것이다. Stop Bit 2비트 뿐만 아니라 1.5비트나 1비트도 설정가능하기 때문에 송수신 측의 Stop Bit 일치 시켜야한다.

- 패리티 비트 : 통신에 있어서 어떤 데이터를 전송할 데이터가 정확히 보내졌는가를 검사하는데 사용한다. Odd 패리티와 Even 패리티 체크의 가지 형태가 있는데 Odd 패리티는 2진수의 1 합이 홀수가 되게 지정하고 Even 패리티의 경우는 1 합이 홀수가 되게 지정한다.

- Baud rate : RS232C 인터페이스의 통신속도는 접속하는 쌍방의 장치의 성능과 통신선로의 특성에 따라 크게 좌우된다. 통신회선 상에 신호율을 측정하기 위한 기본단위를 보오레이트라고 불리며, 1초간에 송수 가능한 비트 수로 규정된다. 만약 110으로 설정된다면 패리티가 부가된 ASCII 코드 1캐릭터를 보내기 위해서

1(Start Bit) + 7(캐릭터의 비트수) + 1(패리티 비트) + 2(Stop Bit) = 11비트

따라서 1초간에 최대 110 / 11 = 10(캐릭터) 송수신 가능하다.

보통의 퍼스널 컴퓨터에서는 1문자 단위를 8비트(1바이트) 다루기 때문에 하나의 문자정보를 보내기 위해서 Stop Bit 최소 1비트로 하여

1(Start Bit) + 8(문자 데이터 비트수) + 1(Stop Bit) = 10비트

1문자 전송에 10비트를 사용한다.


  디버그 유닛(Debug Unit)

디버그 유닛은 USART 포트관련 레지스터에서 많이 사용하는 것만 뽑아 놓은 것이다.



   포함
포함 관계는 어떤 객체가 다른 객체를 포함하고 있는 관계를 의미한다. 즉 객체를 멤버로 갖는 클래스이다. 이러한 객체 멤버들은 생성자 초기화 리스트에서 초기화 해야 한다. 소멸자는 객체를 포함하고 있는 객체의 소멸자부터 호출되고, 그 다음 멤버 객체들의 소멸자들이 호출된다.

 Rect.h
#ifndef RECT_H
#define RECT_H

#include "Point.h"

class Rect
{
public:
  // 생성자
  Rect();

  // 각 점의 값 지정/얻기
  void SetTopLeft(const Point& topLeft);
  void SetBottomRight(const Point& bottomRight);
  void SetRect(int left, int top, int right, int bottom);
  Point GetTopLeft() const;
  Point GetBottomRight() const;
  void GetRect(int& left, int& top, int& right, int& bottom);

  // 넓이, 높이 계산
  int GetWidth() const;
  int GetHeight() const;

  // 내 용 출력
  void Print() const;

protected:
  Point _topLeft;
  Point _bottomRight;
};

#endif

 Rect.cpp
#include "Rect.h"
#include <iostream>
using namespace std;

Rect::Rect()
{

}

void Rect::SetTopLeft(const Point& topLeft)
{
  _topLeft = topLeft;
}

void Rect::SetBottomRight(const Point& bottomRight)
{
  _bottomRight = bottomRight;
}

void Rect::SetRect(int left, int top, int right, int bottom)
{
  _topLeft.SetX(left); 
  _topLeft.SetY(top); 
  _bottomRight.SetX(right); 
  _bottomRight.SetY(bottom); 
}

Point Rect::GetTopLeft() const
{
  return _topLeft;
}

Point Rect::GetBottomRight() const
{
  return _bottomRight;
}

void Rect::GetRect(int& left, int& top, int& right, int& bottom)
{
  left = _topLeft.GetX(); 
  top = _topLeft.GetY(); 
  right = _bottomRight.GetX(); 
  bottom = _bottomRight.GetY(); 
}

int Rect::GetWidth() const
{
  return (_bottomRight.GetX() - _topLeft.GetX() + 1);
}

int Rect::GetHeight() const
{
  return (_bottomRight.GetY() - _topLeft.GetY() + 1);
}

void Rect::Print() const
{
  cout << "{L=" << _topLeft.GetX() << ", T=" << _topLeft.GetY();
  cout << ", R=" << _bottomRight.GetX() << ", B=" <<
    _bottomRight.GetY() << "}\n";
}

 main.cpp
#include "Rect.h"
#include <iostream>
using namespace std;

int main()
{
  // Rect 객 체 생성
  Rect rc1;

  // 내용 출 력
  rc1.Print();
  cout << (*((int *)(&rc1)+1)) << endl;

  // 값을 바꿔본다.
  rc1.SetRect(10203040);

  // 내용 출력
  rc1.Print();
  
  // 값을 바꿔본다.
  rc1.SetTopLeft(Point(2020));

  // 내용 출력
  rc1.Print();
  cout << (*((int *)(&rc1)+1)) << endl;
  cout << (*((int *)(&rc1)+2)) << endl;
  cout << (*((int *)(&rc1)+3)) << endl;

  // 넓 이, 높이 출력
  cout << "rc1.GetWidth() = " << rc1.GetWidth() << endl;
  cout << "rc1.GetHeight() = " << rc1.GetHeight() << endl;

  cout << "rc1 size = " << sizeof(rc1) << endl;

  return 0;
}



   ARM 인터럽트 실습
ARM에서는 각 장치별로 인터럽트 설정이 가능하다. 즉 PIO핀들에 설정 가능한 인터럽트 루틴은 하나다. 그러므로 이 인터럽트 루틴에서 어떤 핀이 인터럽트가 걸렸는지 판별하여 처리하도록 작성해야 한다.(Atmega128에서는 인터럽트로 이용 가능한 핀들이 미리 정해져 있고, 이 핀 마다 각기 인터럽트 루틴을 작성할 수 있다) 또한 ARM에서는 인터럽트 처리의 신뢰성을 위하여 인터럽트 상태를 확인 할 수 있도록 설계되었다. ARM에서는 인터럽트가 걸린 후 핀 상태를 확인함으로써 인터럽트가 초기화 되고 다시 사용가능하게 된다.

//---------- 인터럽트 제어기 IO설정 

#define AIC_IECR (*(volatile unsigned int *) 0xFFFFF120)  // 인터럽트 허 용 레지스터
#define AIC_IDCR (*(volatile unsigned int *) 0xFFFFF124)  // 인터럽트 금지 레지스터
#define AIC_ICCR (*(volatile unsigned int *) 0xFFFFF128)  // 인터럽트 클리어 레지스터
#define AIC_ISCR (*(volatile unsigned int *) 0xFFFFF12C)  // 인터럽트 세트 레지스터
#define AIC_SMR  ((volatile unsigned int *) 0xFFFFF000)    // AIC 소스 모드 레지스터
#define AIC_SVR  ((volatile unsigned int *) 0xFFFFF080)    // AIC 소 스 벡터 레지스터


//---------- 입출 력 IO설정 


#define PIO_PUDR (*(volatile unsigned int *) 0xFFFFF460)  // PIO Pull-Up Disable Register
#define PIO_PUER (*(volatile unsigned int *) 0xFFFFF464)  // PIO Pull-Up Enable Register

#define PIO_IER  (*(volatile unsigned int *) 0xFFFFF440)  // PIO Interrupt Enable Register
#define PIO_IDR  (*(volatile unsigned int *) 0xFFFFF444)  // PIO Interrupt Disable Register
#define PIO_IMR  (*(volatile unsigned int *) 0xFFFFF448)  // PIO Interrupt Mask Register
#define PIO_ISR  (*(volatile unsigned int *) 0xFFFFF44C)  // PIO Interrupt Status Register

#define PIO_IFER (*(volatile unsigned int *) 0xFFFFF420)  // PIO Glitch Input Filter Enable Register
#define PIO_IFDR (*(volatile unsigned int *) 0xFFFFF424)  // PIO Glitch Input Filter Disable Register

#define PIO_SODR (*(volatile unsigned int *) 0xFFFFF430) //출력 
#define PIO_CODR (*(volatile unsigned int *) 0xFFFFF434) //출력 하지 않음
#define PIO_OER  (*(volatile unsigned int *) 0xFFFFF410) //출력 방향 설정
#define PIO_ODR  (*(volatile unsigned int *) 0xFFFFF414) //출력 방향 설정
#define PIO_PER  (*(volatile unsigned int *) 0xFFFFF400) //병렬 입출력 제 어 레지스터 병렬 입출력 포트로 사용한다.


//---------- 전 력 제어 설정

#define PMC_PCER (*(volatile unsigned int *) 0xFFFFFC10)

 
#define MASTERCLOCK 48000000
#define INTERR    0x00000100
#define LED      0x00000010 

//--------- 전역 변수

void Delayms(unsigned int ms) //딜레이 함 수..

  volatile unsigned int count, countmax = (MASTERCLOCK / 10000) * ms;

  for(count = 0; count < countmax; count++);
}

void ledToggle()
{  
  unsigned int clear;
  PIO_SODR = 0x00000010;
  Delayms(500);
  PIO_CODR = 0x00000010;
  Delayms(500);
  clear = PIO_ISR;    // 인터럽트 초 기화를 위해 PIO_ISR 값을 읽음
}

int main(void)
{
  // PMC setting
  PMC_PCER = 0x00000004;    // PMC_PCER = (unsigned int)1 << 2;
  
  // PIO setting
  PIO_PER = 0x00000110;    // PIO_PER = 0x00000FFF;
  PIO_IER = 0x00000100;    
  PIO_IFER = 0x00000100;  
  
  PIO_PUER = 0x00000100;    // PIO_PUER = 1 << 8;
  PIO_ODR = 0x100;
                // PIO_ODR = 0x100;
  PIO_OER = 0x00000010;

  // AIC setting
  AIC_IDCR = (unsigned int)1 << 2;
  AIC_SMR[2= 0x00000065;
  AIC_SVR[2= (unsigned int)ledToggle;
  AIC_ICCR = 0x00000004;
  AIC_IECR = 0x00000004;    // AIC_IECR = (unsigned int)1 << 2;
  
  while(1)
  {

  }
  
  return 0;
// End Main....
// 8번 핀으로 인터럽트를 입력받고, 인터럽트가 걸리면 4번핀에 연결된 LED가 한번 깜빡 거리는 코드이다. 인터럽트는 positive edge에서 발생하며, 인터럽트가 발생 했을 시 ledToggle 함수를 실행하게 된다. ledToggle 함수에서는 LED를 한번 깜빡 거리고, PIO_ISR 레지스터를 읽어서 인터럽트를 다시 사용할 수 있게 초기화 한다.

//---------- 인터럽트 제어기 IO설정 

#define AIC_IECR (*(volatile unsigned int *) 0xFFFFF120)  // 인터럽트 허 용 레지스터
#define AIC_IDCR (*(volatile unsigned int *) 0xFFFFF124)  // 인터럽트 금지 레지스터
#define AIC_ICCR (*(volatile unsigned int *) 0xFFFFF128)  // 인터럽트 클리어 레지스터
#define AIC_ISCR (*(volatile unsigned int *) 0xFFFFF12C)  // 인터럽트 세트 레지스터
#define AIC_SMR  ((volatile unsigned int *) 0xFFFFF000)    // AIC 소스 모드 레지스터
#define AIC_SVR  ((volatile unsigned int *) 0xFFFFF080)    // AIC 소 스 벡터 레지스터


//---------- 입출 력 IO설정 


#define PIO_PUDR (*(volatile unsigned int *) 0xFFFFF460)  // PIO Pull-Up Disable Register
#define PIO_PUER (*(volatile unsigned int *) 0xFFFFF464)  // PIO Pull-Up Enable Register

#define PIO_IER  (*(volatile unsigned int *) 0xFFFFF440)  // PIO Interrupt Enable Register
#define PIO_IDR  (*(volatile unsigned int *) 0xFFFFF444)  // PIO Interrupt Disable Register
#define PIO_IMR  (*(volatile unsigned int *) 0xFFFFF448)  // PIO Interrupt Mask Register
#define PIO_ISR  (*(volatile unsigned int *) 0xFFFFF44C)  // PIO Interrupt Status Register

#define PIO_IFER (*(volatile unsigned int *) 0xFFFFF420)  // PIO Glitch Input Filter Enable Register
#define PIO_IFDR (*(volatile unsigned int *) 0xFFFFF424)  // PIO Glitch Input Filter Disable Register

#define PIO_SODR (*(volatile unsigned int *) 0xFFFFF430) //출력 
#define PIO_CODR (*(volatile unsigned int *) 0xFFFFF434) //출력 하지 않음
#define PIO_OER  (*(volatile unsigned int *) 0xFFFFF410) //출력 방향 설정
#define PIO_ODR  (*(volatile unsigned int *) 0xFFFFF414) //출력 방향 설정
#define PIO_PER  (*(volatile unsigned int *) 0xFFFFF400) //병렬 입출력 제 어 레지스터 병렬 입출력 포트로 사용한다.


//---------- 전 력 제어 설정

#define PMC_PCER (*(volatile unsigned int *) 0xFFFFFC10)


#define MASTERCLOCK 48000000
#define INTERR    0x00000100
#define LED      0x00000010 

void PIO_Ext02(void);



void Delayms(unsigned int ms) //딜레이 함 수..

  volatile unsigned int count, countmax = (MASTERCLOCK / 10000) * ms;

  for(count = 0; count < countmax; count++);
}

void PIO_Ext01(void)
{
  static unsigned int i=0;
  unsigned int status;

  status=PIO_ISR;

  if(status == (unsigned int)0x00000100)
  {
    if(i==0)
      i =1;
    else if(i&0x80)
      return;
    else
      i|=i<<1;

    PIO_SODR=i; 
  }
  else if(status ==(unsigned int)0x00000200)
  {
    if(i==0)
      return;
    else
      i =i>>1;
    PIO_CODR=0xFF;
    PIO_SODR=i;
  }
}

void PIO_Ext02(void)

  unsigned int i;
  for(i=1; i<=3; i++)
  {
    PIO_SODR=0x000000ff;
    Delayms(300);
    PIO_CODR=0x000000ff;
    Delayms(300);
  }
}

int main(void){

  PMC_PCER = (unsigned int)1<<2;
  PIO_PER = 0x00000FFF;   //병렬 I/O포 트로 사용 설정 디폴트로 I/O포트로 사용 하도록 되어 있다.
  PIO_PUER=0x300;
  PIO_ODR = 0x300;     //출력 버퍼 금지 레지스터 설정
  Delayms(50);

  PIO_OER = 0x00000FFF;  


  PIO_IFER=0x00000300;  //글리치 필터 사용
  PIO_IER=0x00000300;   //인터럽트 인애이블 레지스터

  AIC_IDCR=(unsigned int)1<<2;    //마스크 설정
  AIC_SVR[2]=(unsigned int)PIO_Ext01;      //인터럼트 함수 설정
  AIC_SMR[2]=5;           //
  AIC_ICCR=(unsigned int)1<<2;    //
  AIC_IECR=(unsigned int)1<<2;    //마스크 해지



  while(1);

  return 0;

// End Main....
// 8번, 9번핀으로 인터럽트를 입력받아 각각 처리 해주는 예제이다. 8번핀에 인터럽트가 걸리면 0번부터 7번에 연결된 LED가 하나씩 켜지며, 반대로 9번핀에 인터럽트가 걸리면 하나씩 LED가 꺼진다. PIO_Ext01 함수에서 어느 핀의 인터럽트가 걸렸는지 판별하여 각각의 동작을 정의한다.



//---------- 인터럽트 제어기 IO설정 

#define AIC_IECR (*(volatile unsigned int *) 0xFFFFF120)  // 인터럽트 허용 레지스터
#define AIC_IDCR (*(volatile unsigned int *) 0xFFFFF124)  // 인터럽트 금지 레지스터
#define AIC_ICCR (*(volatile unsigned int *) 0xFFFFF128)  // 인터럽트 클리어 레지스터
#define AIC_ISCR (*(volatile unsigned int *) 0xFFFFF12C)  // 인터럽트 세트 레지스터
#define AIC_SMR  ((volatile unsigned int *) 0xFFFFF000)    // AIC 소스 모드 레지스터
#define AIC_SVR  ((volatile unsigned int *) 0xFFFFF080)    // AIC 소스 벡터 레지스터


//---------- 입출력 IO설정 


#define PIO_PUDR (*(volatile unsigned int *) 0xFFFFF460)  // PIO Pull-Up Disable Register
#define PIO_PUER (*(volatile unsigned int *) 0xFFFFF464)  // PIO Pull-Up Enable Register

#define PIO_IER  (*(volatile unsigned int *) 0xFFFFF440)  // PIO Interrupt Enable Register
#define PIO_IDR  (*(volatile unsigned int *) 0xFFFFF444)  // PIO Interrupt Disable Register
#define PIO_IMR  (*(volatile unsigned int *) 0xFFFFF448)  // PIO Interrupt Mask Register
#define PIO_ISR  (*(volatile unsigned int *) 0xFFFFF44C)  // PIO Interrupt Status Register

#define PIO_IFER (*(volatile unsigned int *) 0xFFFFF420)  // PIO Glitch Input Filter Enable Register
#define PIO_IFDR (*(volatile unsigned int *) 0xFFFFF424)  // PIO Glitch Input Filter Disable Register

#define PIO_SODR (*(volatile unsigned int *) 0xFFFFF430) //출력 
#define PIO_CODR (*(volatile unsigned int *) 0xFFFFF434) //출력 하지 않음
#define PIO_OER  (*(volatile unsigned int *) 0xFFFFF410) //출력 방향 설정
#define PIO_PER  (*(volatile unsigned int *) 0xFFFFF400) //병렬 입출력 제어 레지스터 병렬 입출력 포트로 사용한다.


//---------- 전력 제어 설정

#define PMC_PCER (*(volatile unsigned int *) 0xFFFFFC10)

 
#define MASTERCLOCK 48000000
#define INTERR    0x00000100
#define LED      0x00000010 

//--------- 전역 변수

void Delayms(unsigned int ms) //딜레이 함수..

  volatile unsigned int count, countmax = (MASTERCLOCK / 10000) * ms;

  for(count = 0; count < countmax; count++);
}

void ledToggle()
{  
  int clear;
  PIO_SODR = 0x00000010;
  Delayms(500);
  PIO_CODR = 0x00000010;
  Delayms(500);
  clear = PIO_ISR;
}

int main(void)
{
  // PMC setting
  PMC_PCER = 0x00000004;
  
  // PIO setting
  PIO_PER = 0x00000110;
  PIO_IER = 0x00000100;
  PIO_IFER = 0x00000100;
  PIO_PUER = 0x00000100;

  PIO_OER = 0x00000010;
//  PIO_SODR = 0x00000000;  

  // AIC setting
  AIC_IECR = 0x00000004;
  AIC_SMR[2= 0x00000065;
  AIC_SVR[2= (unsigned int)ledToggle;
  AIC_ICCR = 0x00000004;
  
  while(1)
  {

  }
  
  return 0;
// End Main....

   예제

1. 다음 배열을 가리킬 수 있는 포인터 변수를 선언 및 초기화 하세요.

#include <iostream>
using namespace std;

int main()
{
  int C[9];
  int *pC = C;

  char B[20];
  char *pB = B;

  double A[10][10];
  double (*pA)[10= A;

  int D[5][7];
  int (*pD)[7= D;

  char E[4][5];
  char (*pE)[5= E;

  return 0;
}

2. 함수의 선언은 다음과 같습니다. 이 함수를 가리킬 수 있는 포인터 변수를 선언 및 초기화 하세요.
#include <iostream>
using namespace std;

char IntToChar(int num)
{
  cout << "IntToChar " << num << endl;
  return 0;
}

void MaxNum(int N1, int N2)
{
  cout << "MaxNum " << N1 << N2 << endl;
}

int OutputIDnum(char *name, int pwd)
{
  cout << "OutputIDnum " << name << pwd << endl;
  return 0;
}

int main()
{
  // 함수 포인터 선언 및 초기화
  char (*fp1)(int= IntToChar;
  void (*fp2)(intint= MaxNum;
  int (*fp3)(char *, int= OutputIDnum;

  // 함수 포인터를 이용하여 호출
  (*fp1)(1);
  (*fp2)(11);
  (*fp3)("test"1);

  return 0;
}


3. 클래스 멤버 함수를 가리킬 수 있는 포인터 변수를 선언 및 초기화를 하세요.

(클래스 외부에서 포인터 선언한다고 가정)

 Point.h
#ifndef POINT_H
#define POINT_H

// Point 클래수를 정의한다
class Point
{
public:
  // 멤버 함수
  void Print() const;
  void Offset(int x_delta, int y_delta);
  void Offset(const Point& pt);

  // 생성자들
  Point();
  Point(int initialX, int initialY);
  Point(const Point& pt);

  // 소멸자
  ~Point();

  // 접근자
  void SetX(int value);
  void SetY(int value);
  int GetX() const
  {
    return x;
  }
  int GetY() const
  {
    return y;
  }
private:
  // 멤버 변수
  int x, y;
};

#endif

 main.cpp
#include "Point.h"
#include <iostream>
using namespace std;

int main()
{
  Point p(100100);

  // 멤버 함수 선언 및 초기화
  void (Point::*fp1)(int= &Point::SetX;
  void (Point::*fp2)(int= &Point::SetY;
  int (Point::*fp3)(voidconst = &Point::GetX;
  int (Point::*fp4)(voidconst = &Point::GetY;
  void (Point::*fp5)(voidconst = &Point::Print;

  // 멤버 함수 호출
  (p.*fp5)();

  (p.*fp1)(50);
  (p.*fp2)(50);

  (p.*fp5)();

  return 0;
}


4. 사용자에게 년도를 입력 받아 윤년인지 아닌지를 판단하는 판단하는 프로그램을 작성하시오.

윤년은 아래와 같다.

l  4로 나누어 떨어지는 해이다.

l  그 중에서 100으로 나누어 떨어지는 해는 평년

l  다만 400으로 나누어 떨어지는 해는 다시 윤년


 Year.h
#ifndef YEAR_H
#define YEAR_H

class Year
{
public:
  // 생성자
  Year();
  Year(int y);

  // 멤버 함수
  void setYear(int y);
  int getYear() const;
  bool isLeapYear() const;
protected:
  int years;
};

#endif

 Year.cpp
#include "Year.h"

void Year::setYear(int y)
{
  years = y;
}

int Year::getYear() const
{
  return years;
}

// 윤년인지 여부를 알려주는 함수(return value 1 이면 윤년)
bool Year::isLeapYear() const
{
  if(years % 4 == 0)
  {
    if(years % 400 == 0)
    {
      return 1;
    }
    else if(years % 100 != 0)
    {
      return 1;
    }
  }
  return 0;
}

// 디폴트 생성자
Year::Year()
{
  setYear(0);
}

// 인자가 있는 생성자
Year::Year(int y)
{
  setYear(y);
}

 main.cpp
#include "Year.h"
#include <iostream>
using namespace std;

int main()
{
  int y;

  cout << "Input Year: ";
  cin >> y;

  Year cy(y);
  
  if(cy.isLeapYear())
  {
    cout << "[" << cy.getYear() << "] is leap year" << endl;
  }
  else
  {
    cout << "[" << cy.getYear() << "] is common year" << endl;
  }

  return 0;
}



   인터럽트 제어기(AIC:Advanced Interrupt Controller)

인터럽트 제어기와 관련된 I/O 제어 레지스터



   멤버 함수
 
멤버 함수에 대한 포인터
기존에 멤버가 아닌 함수에 대한 포인터를 만들던 방식으로는 멤버 함수를 가리킬 수 없다. 멤버 함수에 대한 포인터를 만드는 문법은 조금 다르다.
typedef void (*fp)(int)   // 일반 함수에 대한 포인터
typedef void (Point::*fp)(int)   // 멤버 함수에 대한 포인터는 범위 지정자를 이용하여 어느 클래스의 멤버인지 지정 해야 한다.
* 정적 멤버 함수에 대한 포인터 : 정적 멤버 함수의 경우에는 멤버가 아닌 함수에 대한 포인터를 그대로 사용하면 된다.

 main.cpp
#include "Point.h"

// void XX() 형태의 함수에 대한 포인터
typedef void (*FP1)(int);

// void Point::XX() 형 태의 멤버 함수에 대한 포인터
typedef void (Point::*FP2)(int);

int main()
{
  // 객체 생성
  Point pt(5050);

//  FP1 fp1 = &Point::SetX;
  FP2 fp2 = &Point::SetX;

  // 함수 포인터를 사용해서 함수 호출
  (pt.*fp2)(100);

  // 내용 출력
  pt.Print();

  return 0;
}


 멤버 함수의 오버로딩
멤버 함수에서도 오버로딩이 가능하다. 시그니처만 다르게 하면 얼마든지 오버로딩 할 수 있다.

Point.h
#ifndef POINT_H
#define POINT_H

// Point 클래수를 정의한다
class Point
{
public:
  // 멤버 함수
  void Print();
  void Offset(int x_delta, int y_delta);
  void Offset(const Point& pt);

  // 생성자들
  Point();
  Point(int initialX, int initialY);
  Point(const Point& pt);

  // 접근자
  void SetX(int value);
  void SetY(int value);
  int GetX() const
  {
    return x;
  }
  int GetY() const
  {
    return y;
  }
private:
  // 멤버 변수
  int x, y;
};

#endif

Point.cpp
#include "Point.h"
#include <iostream>
using namespace std;

void Point::SetX(int value)
{
  if(value < 0)
  {
    x = 0;
  }
  else if(value > 100)
  {
    x = 100;
  }
  else
  {
    x = value;
  }
}

void Point::SetY(int value)
{
  if(value < 0)
  {
    y = 0;
  }
  else if(value > 100)
  {
    y = 100;
  }
  else
  {
    y = value;
  }
}

void Point::Offset(int x_delta, int y_delta)
{
  SetX(x + x_delta);
  SetY(y + y_delta);
}

void Point::Offset(const Point& pt)
{
  Offset(pt.x, pt.y);
}

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

Point::Point(int initialX, int initialY)
{
  SetX(initialX);
  SetY(initialY);
}

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

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

 main.cpp
#include "Point.h"

int main()
{
  // 객체를 생성
  Point pt(5050);
  Point delta(100100);

  // 점을 x축으로 10, y축으로 -10만큼 이동시킨다.
  pt.Offset(10, -10);

  // 현재 점의 위치 출력
  pt.Print();

  // 점을 범위 밖으로 이동시킨다.
  pt.Offset(delta);

  // 현재 점의 위치 출력
  pt.Print();

  return 0;
}


   객체의 배열
클래스도 타입의 한 종류기 때문에 배열을 만들 수 있다. 구조체의 배열과 큰 차이점은 없지만 생성자와 관련된 문제를 생각해보아야 한다.

main.cpp
#include "Point.h"

int main()
{
  Point arr[3];

  for(int i = 0 ; i < 3 ; ++i)
    arr[i].Print();

  return 0;
}

// 객체의 배열을 선언 하면 각 객체들은 디폴트 생성자로 초기화 된다.

 main.cpp
#include "Point.h"

int main()
{
  Point arr[3= {
    Point(100100), Point(50100), Point(1010)
  };

  for(int i = 0 ; i < 3 ; ++i)
    arr[i].Print();

  return 0;
}

// 따로 생성자를 지정해서 초기화 할 수 있다.

   객체의 동적인 생성
객체를 동적으로 생성할 때도 new 연산자를 사용한다. 객체의 경우에는 생성자의 호출이라는 문제가 있기 때문에 기존에 new 연산자를 사용하던 방법하고는 조금 다르다.
겍체를 동적 할당 할 때 생성자는 new 연산자를 사용해서 동적으로 객체를 생성할 때 호출된다. 마찬가지로 소멸자는 delete 연산자를 사용해서 해제할 때 호출된다

 main.cpp
#include "Point.h"

int main()
{
  // 초기화용으로 객체를 만든다.
  Point pt(5050);

  // 동적으로 객체를 생성한다.
  Point *p1 = new Point();
  Point *p2 = new Point(100100);
  Point *p3 = new Point(pt);

  // 객체들의 내용을 출력한다.
  p1->Print();
  p2->Print();
  p3->Print();

  // 동 적으로 생성한 객체들을 정리한다.
  delete p1;
  delete p2;
  delete p3;
  p1 = p2 = p3 = 0;

  return 0;
}


// 정적 멤버 함수를 사용한 객체의 생성
#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
  string name;  // 이름
  int sNo;    // 학번

  void Print();
private:
  // 생 성자
  Student(const string& name_arg, int stdNumber);

public:
  // 정 적 멤버
  static int studentNumber;
  static Student* CreateStudent(const string& name_arg);
};

int Student::studentNumber = 0;

Student* Student::CreateStudent(const string& name_arg)
{
  // 학생 객체를 생성한다
  Student *p = new Student(name_arg, studentNumber++);

  // 새로 생성된 학생 객체 반환
  return p;
}

Student::Student(const string& name_arg, int stdNumber)
{
  name = name_arg;
  sNo = stdNumber;
}

void Student::Print()
{
  cout << "{Name = " << name << ", std. Num. = " << sNo << "}\n";
}

int main()
{
  // 학생 객체를 세 개 생성한다.
  Student *p1, *p2, *p3;
  p1 = Student::CreateStudent(" 이계희");
  p2 = Student::CreateStudent("이춘훈");
  p3 = Student::CreateStudent("윤수연");

  // 생성 된 학생의 정보를 출력
  p1->Print();
  p2->Print();
  p3->Print();

  // 생성된 객체 해제
  delete p1;
  delete p2;
  delete p3;
  p1 = p2 = p3 = 0;

  return 0;
}


   PMC(Power Management Controller)
ARM 프로세서에서는 사용할 모듈에 각각 전력을 공급을 해주어야 한다. 이를 설정하는 레지스터로 PMC_PCER, PMC_PCDR, PMC_PCSR 등이 있다.

PMC_PCER : 각 비트를 Enable 하는데 사용한다.


PMC_PCDR : 각 비트를 Disable 하는데 사용한다.


PMC_PCSR : 각 비트의 상태를 확인한다.




   PIO(Parallel Input/Output Controller)
핀의 입출력을 제어하는 레지스터로 PIO_PER, PIO_PDR, PIO_PSR // PIO_OER, PIO_ODR, PIO_OSR // PIO_SODR, PIO_CODR, PIO_ODSR을 알아본다.

PIO_PER, PIO_PDR, PIO_PSR : 병렬 입출력 관련 설정
PIO_OER, PIO_ODR, PIO_OSR  : 출력 관련 설정
PIO_SODR, PIO_CODR, PIO_ODSR : 출력 데이터 설정
* AMR 프로세서에는 레지스터를 Set, Clear 시키는 레지스터가 각기 따로 존재 한다.

PIO_PER : 병력 입출력 허용


PIO_PDR : 병렬 입출력 금지


PIO_PSR : 병력 입출력 상태


PIO_OER : 출력 허용


PIO_ODR : 출력 금지


PIO_OSR : 출력 상태


PIO_SODR : 출력 데이터 허용


PIO_CODR : 출력 데이터 금지


PIO_ODSR : 출력 데이터 상태


1. 두 개의 정수를 입력받고 두 정수 사이에 5의 배수의 개수를 출력하는 프로그램을 작성하세요.(객체 생성)


 Count.h
#ifndef COUNT_H
#define COUNT_H

class Count
{
private:
  int num1;
  int num2;

public:
  // 생성자
  Count();
  Count(int x, int y);

  // 멤버 함수
  void setNum1(int num);
  void setNum2(int num);
  int fiveMultiple() const;  // 5의 배수 갯수를 구하는 멤버 함수
};

#endif

 Count.cpp
#include "Count.h"

void Count::setNum1(int num)
{
  num1 = num;
}

void Count::setNum2(int num)
{
  num2 = num;
}

// 2수 사이의 5의 배수 갯수를 구해주는 멤버 함수
int Count::fiveMultiple() const
{
  int iCnt = 0;

  for(int i = (num1 + 1) ; i < num2 ; ++i)
  {
    if(i % 5 == 0)
    {
      ++iCnt;
    }
  }
  return iCnt;
}

// 디폴트 생성자 정의
Count::Count()
{
  setNum1(0);
  setNum2(0);
}

// 인자가 있는 생성자 정의
Count::Count(int x, int y)
{
  // 큰 수를 Num1에 넣어준다
  if(x > y)
  {
    setNum1(y);
    setNum2(x);
  }
  else
  {
    setNum1(x);
    setNum2(y);
  }
}

 main.cpp
#include "Count.h"
#include <iostream>
using namespace std;

int main()
{
  int num1, num2;
  int result;

  cout << "정수(2개) 입력 : ";
  cin >> num1 >> num2;

  // Count 객체 생성
  Count c(num1, num2);
  
  result = c.fiveMultiple();
  cout << "출력 : " << result << endl;

  return 0;
}


2. 두 개의 정수를 입력받고 두 정수 사이의 부호를 다음과 같이 바꿔가면서 계산한 식과 결과를 출력하세요.

 CalcToggle.h
#ifndef CALCTOGGLE_H
#define CALCTOGGLE_H

class CalcToggle
{
private:  
  int num1;
  int num2;
public:
  // 생성자
  CalcToggle();
  CalcToggle(int x, int y);

  // 멤버 함수
  void setNum1(int num);
  void setNum2(int num);
  void toggleCalculate() const;
};

#endif

 CalcToggle.cpp
#include "CalcToggle.h"
#include <iostream>
using namespace std;

void CalcToggle::setNum1(int num)
{
  num1 = num;
}

void CalcToggle::setNum2(int num)
{
  num2 = num;
}

void CalcToggle::toggleCalculate() const
{
  int result = 0;
  int toggle = 1;

  cout << "X = ";
  for(int i = num1 ; i <= num2 ; ++i)
  {
    if(toggle)
    {
      result += i;
      toggle = 0;
      cout << " + " << i;
    }
    else
    {
      result -= i;
      toggle = 1;
      cout << " - " << i;
    }
  }
  cout << endl << "X = " << result << endl;
}

// 디폴트 생성자 정의
CalcToggle::CalcToggle()
{
  setNum1(0);  
  setNum2(0);  
}

// 인자가 있는 생성자 정의
CalcToggle::CalcToggle(int x, int y)
{
  // 큰수를 num1에 넣어준다
  if(x > y)
  {
    setNum1(y);
    setNum2(x);
  }
  else
  {
    setNum1(x);
    setNum2(y);
  }
}

 main.cpp
#include "CalcToggle.h"
#include <iostream>
using namespace std;

int main()
{
  int num1, num2;

  cout << "정수(2개) 입력: ";
  cin >> num1 >> num2;

  CalcToggle c(num1, num2);
  c.toggleCalculate();

  return 0;
}



3. 출현 빈도 그래프

    • 0에서부터 9까지의 60개의 수가 배열에 저장되어 있을 때, 배열 원소 60개의 합과 평균을 구하여 출력하고 각 수에 대한 빈도 수를 세어서 다음과 같이 막대 그래프 형식으로 출력하시오
    • 프로그램의 주요한 부분에 주석을 명시하시오.


 InfoArray.h
#ifndef INFOARRAY_H
#define INFOARRAY_H

class InfoArray
{
private:
  int array[60];
public:
  // 생성자
  InfoArray();

  // 멤버 함수
  void randArray();      // 멤버 변수에 랜덤수를 넣어준다
  void printArray() const;
  int sumArray() const;    // 멤버 변수에 저장되어 있는 각수의 합을 구해준다
  float averageArray() const;  // 멤버 변수에 저장되어 있는 각수의 평균을 구해준다
  void frequencyArray() const;  // 멤버 변수에 저장 되어 있는 수의 빈도 출력
};

#endif

 InfoArray.cpp
#include "InfoArray.h"
#include <iostream>
using namespace std;

// 멤버 변수에 0~9까지 난수 저장
void InfoArray::randArray()
{
  srand((unsigned int)time(NULL));

  for(int i = 0 ; i < 60 ; ++i)
  {
    array[i] = (rand() % 10);
  }
}

void InfoArray::printArray() const
{
  for(int i = 0 ; i < 60 ; ++i)
  {
    cout << array[i] << " ";
    if((i + 1) % 10 == 0)
    {
      cout << endl;
    }
  }
}

// 멤버 변수에 저장된 각 수의 합을 구한다.
int InfoArray::sumArray() const
{
  int sum = 0;

  for(int i = 0 ; i < 60 ; ++i)
  {
    sum += array[i];
  }

  return sum;
}

// 멤버 변수에 저장된 각 수의 평균을 구한다.
float InfoArray::averageArray() const
{
  float average;

  average = (float)sumArray() / 60;

  return average;
}

// 멤버 변수에 저장된 각 수의 빈도를 출력해 준다
void InfoArray::frequencyArray() const
{
  int fre[10= {};

  for(int i = 0 ; i < 60 ; ++i)
  {
    switch(array[i])
    {
      case 0:
        ++fre[0];
        break;
      case 1:
        ++fre[1];
        break;
      case 2:
        ++fre[2];
        break;
      case 3:
        ++fre[3];
        break;
      case 4:
        ++fre[4];
        break;
      case 5:
        ++fre[5];
        break;
      case 6:
        ++fre[6];
        break;
      case 7:
        ++fre[7];
        break;
      case 8:
        ++fre[8];
        break;
      default:
        ++fre[9];
    }
  }

  cout << "no\tcount\tbar graph" << endl;
  for(int i = 0 ; i < 10 ; ++i)
  {
    cout << i << "\t" << fre[i] << "\t";
    for(int j = 0 ; j < fre[i] ; ++j)
    {
      cout << "*";
    }
    cout << endl;
  }
}

// 디폴트 생성자
InfoArray::InfoArray()
{
  randArray();
}

 main.cpp
#include "InfoArray.h"
#include <iostream>
using namespace std;

int main()
{
  int sum;
  float average;

  InfoArray iA;

  iA.printArray();
  sum = iA.sumArray();
  average = iA.averageArray();  

  cout << "sum = " << sum << endl;
  cout << "average = " << average << endl;

  iA.frequencyArray();

  return 0;
}

   Atmega128 EEPROM
Atmega128에는 4K Bytes의 EEPROM이 있다. 이 EEPROM을 사용하기 위해서 3개의 레지스터를 사용한다.
 EECR(Control Register)
EEPROM에 읽거나 쓰는 동작을 제어하는 레지스터

EERE(Read Enable) : EEAR에 지정된 주소의 1바이트를 읽어 EEDR에 저장
EEWE(Write Enable) : 쓰기 기능 동작, 시스템에 의한 클리어
EEMWE(Master Write Enable) : EEWE 동작 전 1 지정해야 함, 시스템에 의한 클리어
EERIE(Ready Interrupt Enable) : EEWE가 클리어 될 때 인터럽트 발생 여부 // SREG 비트도 활성화 되어 있어야 함

 EEDR(Data Register)
EEPROM에 쓰거나 읽을 데이터를 저장하는  레지스터


 EEAR(Address Register)
EEPROM 어느 번지에 쓸껀지를 지정하는 레지스터

EEPROM 용량이 4K byte 이므로 12비트를 사용한다.

읽기 // 쓰기 동작


 // EEPROM Read&Write 실습1
#include "port.h"
#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;  // 시간 변수

// TCCR0 설 정
void setTCCR0(void)
{
  TCCR0 = (1 << CS02);
}

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

// TIMSK 설정
void setTIMSK(void)
{
  TIMSK = (1 << TOIE0);
}

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

  while(g_elapsed_time < elapsed_time);
}

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

  TCNT0 = 256 - (CPU_CLOCK / TICKS_PER_SEC / PRESCALER);
}

void eeprom_write(unsigned int uiAddress, unsigned char ucData)
{
  while(EECR & (1 << EEWE));  // 쓰기 작업 중 이면 대기

  EEAR = uiAddress;      // 쓸 주소값 입력
  EEDR = ucData;        // 쓸 데이 터 입력
  EECR |= (1 << EEMWE);    // EEWE 할 준비
  EECR |= (1 << EEWE);    // 쓰기 명령
  return;
}

unsigned char eeprom_read(unsigned int uiAddress)
{
  while(EECR & (1 << EEWE));  // 쓰기 작업 중 이면 대기

  EEAR = uiAddress;      // 읽을 주소값 입력
  EECR |= (1 << EERE);    // 읽기

  return EEDR;        // 데 이터 리턴
}

int main(void)
{
  char state = 0;
  unsigned char ch = 0;
  unsigned int i, j;

  DDRC = 0x00;  // 스위치 입력
  PORTC = 0xFF;

  DDRF = 0xFF;  // FND 출력
  PORTF = 0xFF;

  setTCCR0();
  initTCNT0();
  setTIMSK();
  sei();

  for(i = 0 ; ; i++)
  {
    ch = PINC;
    if(ch == 0xFE)  // 0번 스위치를 누르면
    {
      ch = 0;
      for(j = 0 ; j < 10 ; j++)
      {
        eeprom_write(j, ch++);
      }
      ch = 9;
      for(j = 0 ; j < 10 ; j++)
      {
        eeprom_write(10 + j, ch--);
      }
    }
    ch = eeprom_read(i);  // EEPROM 의 i 주소에 들어 있는 데이터를 읽어옴
    PORTF = ch;
    if(i == 19)
    {
      i = -1;
    }
    
    sleep(500);
  }
  return 0;
}
// EEPROM 0번지부터 19번지안에 숫자를 저장하고, 이를 읽어와서 FND에 보여주는 코드이다.

 // EEPROM Read&Write 실습2
#include "port.h"
#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;  // 시간 변수

// TCCR0 설 정
void setTCCR0(void)
{
  TCCR0 = (1 << CS02);
}

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

// TIMSK 설정
void setTIMSK(void)
{
  TIMSK = (1 << TOIE0);
}

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

  while(g_elapsed_time < elapsed_time);
}

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

  TCNT0 = 256 - (CPU_CLOCK / TICKS_PER_SEC / PRESCALER);
}

void eeprom_write(unsigned int uiAddress, unsigned char ucData)
{
  while(EECR & (1 << EEWE));  // 쓰기 작업 중 이면 대기

  EEAR = uiAddress;      // 쓸 주소값 입력
  EEDR = ucData;        // 쓸 데이 터 입력
  EECR |= (1 << EEMWE);    // EEWE 할 준비
  EECR |= (1 << EEWE);    // 쓰기 명령
  return;
}

unsigned char eeprom_read(unsigned int uiAddress)
{
  while(EECR & (1 << EEWE));  // 쓰기 작업 중 이면 대기

  EEAR = uiAddress;      // 읽을 주소값 입력
  EECR |= (1 << EERE);    // 읽기

  return EEDR;        // 데 이터 리턴
}

int main(void)
{
  unsigned char ch = 0;
  unsigned char data = 0;

  DDRC = 0x00;  // 스위치 입력
  PORTC = 0xFF;

  DDRF = 0xFF;  // LED 출력
  PORTF = 0xFF;

  setTCCR0();
  initTCNT0();
  setTIMSK();
  sei();

  while(1)
  {
    data = eeprom_read(1);
    PORTF = data;

    if(0 == data)  // LED 모두가 ON이 면 
    {
      data = 0xFF;
      eeprom_write(1, data);
    }

    ch = PINC;
    ch = ~ch;
    if(ch)    // 키 가 눌러 졌으면
    {
      data ^= ch;    // toggle
      eeprom_write(1, data);
      sleep(500);
    }
  }

  return 0;
}
// 8개의 스위치와 LED를 매칭 시켜, 스위치를 누를 때마다 해당 LED가 Toggle 된다. 이때 LED ON 정보를 EEPROM에 저장해서 전원을 껐다 켜도 다시 그대로 복원된다.

   정적 멤버
정적 멤버는 모든 객체가 공유하는 멤버다. 지금까지 배워온 멤버 변수와 멤버 함수들은 객체마다 하나씩 존재했었고 객체는 저마다 자신의 멤버를 사용했다. 하지만 정적 멤버 변수와 정적 멤버 함수는 클래스에 오직 하나만 존재하기 때문에 모든 객체들은 하나의 정적 멤버를 같이 사용하게 된다. 정적 멤버는 기존에 사용하던 멤버 변수나 함수 앞에 static 키워드를 붙여서 만든다.

 // 정적 멤버를 사용한 객체 수 세기
#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
  // 생 성자, 소멸자
  Student(const string& name_arg, int stdNumber);
  ~Student();

public:
  // 정 적 멤버들
  static int student_count;
  static void PrintStdCount();

private:
  string name;  // 이름
  int sNo;    // 학번
};

// 정 적 멤버 변수
int Student::student_count = 0;

// 정적 멤버 함수
void Student::PrintStdCount()
{
  cout << "Student 객체 수 = " << student_count << endl;
}

Student::Student(const string& name_arg, int stdNumber)
{
  // 학생 객체의 수를 증가시킨다.
  student_count++;

  name = name_arg;
  sNo = stdNumber;
}

Student::~Student()
{
  // 학생 객체의 수를 감소시킨다.
  student_count--;
}

void Func()
{
  // 객체 생성
  Student std1("Bill"342);
  Student std2("James"214);

  Student::PrintStdCount();
}

int main()
{
  Student::PrintStdCount();

  // 객체 생성
  Student std("Jeffrey"123);

  Student::PrintStdCount();

  // Func() 함수 호출
  Func();

  Student::PrintStdCount();

  return 0;
}


 // 위의 예제에서 클래스를 헤더 파일과 구현 파일로 나누어 보았다
 // Student.h
#ifndef STUDENT_H
#define STUDENT_H

#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
  // 생 성자, 소멸자
  Student(const string& name_arg, int stdNumber);
  ~Student();

public:
  // 정 적 멤버들
  static int student_count;
  static void PrintStdCount();

private:
  string name;  // 이름
  int sNo;    // 학번
};

#endif

 // Student.cpp
#include "Student.h"

// 정적 멤버 변수
int Student::student_count = 0;

// 정적 멤버 함수
void Student::PrintStdCount()
{
  cout << "Student 객체 수 = " << student_count << endl;
}

Student::Student(const string& name_arg, int stdNumber)
{
  // 학생 객체의 수를 증가시킨다.
  student_count++;

  name = name_arg;
  sNo = stdNumber;
}

Student::~Student()
{
  // 학생 객체의 수를 감소시킨다.
  student_count--;
}

 // main.cpp
#include "Student.h"

void Func()
{
  // 객체 생성
  Student std1("Bill"342);
  Student std2("James"214);

  Student::PrintStdCount();
}

int main()
{
  Student::PrintStdCount();

  // 객체 생성
  Student std("Jeffrey"123);

  Student::PrintStdCount();

  // Func() 함수 호출
  Func();

  Student::PrintStdCount();

  return 0;
}

   접근 제어
클래스에서 말하는 접근 제어는 어떤 멤버를 외부에 보이게 할 것인지 지정하는 작업을 말한다. 즉 정보 은닉을 달성할 수 있는 수단이 된다.

접근 권한을 설정하기 위한 키워드로는 public, protected, private이 있고 각 키워드가 의미하는 바는 다음과 같다.
publc :
   - 외부 접근 가능(모든 클래스에서 사용 가능)
   - 다른 패키지에서도 접근 가능
protected :
   - 외부 접근 불가능(상속 관계에 있는 자식 클래스만 쓸 수 있게 만든 것)
   - 같은 패키지의 서브 클래스와 다른 패키지의 서브 클래스에서 접근 할 수 있다.
private :
   - 외부 접근 불가능(같은 클래스 안에서만 접근할 수 있다)
   - 오로지 해당 클래스 내부의 메소드만으로 접근이 가능하다.
   - 상속된 하위 클래스도 접근 불가능, 제일 강력한 접근 제어로 '정보 은닉'을 위한 장치
어떤 접근권한을 가지든지 클래스 내부의 멤버 함수에서는 모든 멤버에 접근할 수 있다.

* c++에서 구조체는 클래스가 할 수 있는 모든 일을 할 수 있다. 하지만 c언어와의 호환성을 위해서 그렇게 하지 않는 것이 일반적이다. c++에서 클래스와 구조체가 다른 점은 딱 한 가지 밖에 없다. 접근 권한과 관련된 키워드를 아무것도 적어주지 않은 경우에 클래스는 기본적으로 private 접근 권한을 적용하고, 구조체는 public 접근 권한을 적용한다.

 // 접근 권한 지정
#include <iostream>
using namespace std;

class AccessControl
{
public:
  char publicData;
  void publicFunc() 
  {

  }
protected:
  int protectedData;
  void protectedFunc()
  {

  }
private:
  float privateData;
  void privateFunc()
  {

  }
};

int main()
{
  // 객체 생성
  AccessControl ac;

  ac.publicData = 'A';    // 성공
  ac.publicFunc();      // 성공
  ac.protectedData = 100;    // 실패
  ac.protectedFunc();      // 실패
  ac.privateData = 4.5f;    // 실패
  ac.privateFunc();      // 실패

  return 0;
}


 // 멤버 함수에서 멤버에 접근하기
#include <iostream>
using namespace std;

class AccessControl
{
public:
  char publicData;
  void publicFunc()
  {

  }
protected:
  int protectedData;
  void protectedFunc()
  {

  }
private:
  float privateData;
  void privateFunc()
  {

  }
public:
  void AccessAllMembers();
};

void AccessControl::AccessAllMembers()
{
  publicData = 'A';
  publicFunc();
  protectedData = 100;
  protectedFunc();
  privateData = 4.5f;
  privateFunc();
}

int main()
{
  // 객체 생성
  AccessControl ac;

  // AccessAllMembers() 함수 호출
  ac.AccessAllMembers();

  return 0;
}

// 클래스 내부의 멤버 함수에서는 접근 권한에 상관없이 모든 멤버에 접근할 수 있다.

// 접근자를 이용해서 멤버 변수를 접근하는 프로그램
#include <iostream>
using namespace std;

// Point 클래스 정의
class Point
{
public:
  // 멤버 함수
  void Print();

  // 생성자들
  Point();
  Point(int initialX, int initialY);
  Point(const Point& pt);

  // 접근자
  void SetX(int value)
  {
    if(value < 0)
    {
      x = 0;
    }
    else if(value > 100)
    {
      x = 100;
    }
    else
    {
      x = value;
    }
  }
  void SetY(int value)
  {
    if(value < 0)
    {
      y = 0;
    }
    else if(value > 100)
    {
      y = 100;
    }
    else
    {
      y = value;
    }
  }
  int GetX()
  {
    return x;
  }
  int GetY()
  {
    return y;
  }
private:
  // 멤버 변수
  int x, y;
};

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

Point::Point(int initialX, int initialY)
{
  SetX(initialX);
  SetY(initialY);
}

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

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

int main()
{
  // 객체 생성
  Point pt(-50200);

  // pt의 내용을 출력한다.
  pt.Print();

  return 0;
}


   타이머 카운터
오늘 실험에서는 Compare Match Count 모드로 delay를 구현해보았다.

 1초를 만드는 원리
1. Prescaler를 64로 주면 16Mhz 신호가 250000hz 신호로 바꾼다.(1초에 250000만번 카운트 할 수 있다는 의미다.)
2. OCRn 레지스터를 250으로 초기화 한다. TCNTx 레지스터와 OCRn 레지스터 값을 계속 비교해서 같아 지면 SIG_OUTPUT_COMPARE0 발생한다. 즉 1/1000(1ms)가 만들어 진다.
3.  인터럽트가 발생할 때마다 카운트를 해서 1000번을 하면 1초가 만들어진다.

Compare Match Count 모드에서는 TCNT 레지스터가 자동으로 초기화 된다.
#include <avr/io.h>
#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_OUTPUT_COMPARE0);          // 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 = 0x0C;  // Compare Match Count 모드, Prescaler 64로 설정
}

void initTCNT0(void)
{
  OCR0 = (CPU_CLOCK / TICKS_PER_SEC / PRESCALER);  // OCR0 초기화
}

void setTIMSK(void)            // TIMSK 설정
{
  TIMSK = 0x02;  //Compare Match 인터럽트 활성화
}

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_OUTPUT_COMPARE0)          // timer0의 오버플로우 함수
{
  g_elapsed_time++;
}


   생성자
반드시 생성자가 있어야 하는 경우
객체를 만들 때 반드시 생성자가 있어야 하는 경우가 있다. 예를 들어 멤버 변수 중에 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);
}



   객체지향 프로그래밍
객체지향 프로그래밍은 모든 데이터를 겍체로 취급하여 프로그래밍 하는 방법으로, 처리 요구를 받은 객체가 자기 자신의 안에 있는 내용을 가지고 처리하는 방식이다.

 클래스와 객체
클래스와 객체는 '붕어빵 틀'과 '붕어빵'의 관계다. 혹은 '제품의 설계도'와 '제품'의 관계라고 말할 수도 있다. 클래스는 구조체와 거의 같은 개념으로 함수까지 멤버로 넣어서 사용한다는 점이 다르다. 즉 변수와 함수들을 하나의 가방에 모아놓은 것이 클래스다. 우리가 어떤 클래스를 만들어서 정의하면, 그 클래스는 새로운 타입이 되는 것이다.

 // 간단한 클래스와 객체 예제
#include <iostream>
using namespace std;

// 클래스를 정의
class Point
{
public:
  int x, y;

  void Print();
};

// 멤버 함수 정의
void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

int main()
{
  // 객체를 생성
  Point pt1, pt2;

  // pt1, pt2를 초기화 한다.
  pt1.x = 100;
  pt1.y = 100;
  pt2.x = 200;
  pt2.y = 200;

  // pt1, pt2의 내용을 출력한다.
  pt1.Print();
  pt2.Print();

  return 0;
}


 // 멤버 함수 안에서의 이름 충돌
#include <iostream>
using namespace std;

// 클래스 정의
class Point
{
public:
  int x, y;

  void Print();
};

// 멤버 함수 정의
void Point::Print()
{
  int x = 333;
  cout << "(" << x << ", " << y << ")\n";
}

int main()
{
  // 객체 생성
  Point pt1, pt2;

  // 객체 초기화
  pt1.x = 100;
  pt1.y = 100;
  pt2.x = 200;
  pt2.y = 200;

  pt1.Print();
  pt2.Print();

  return 0;
}

// 멤버 변수와 같은 이름의 지역변수들을 사용할 경우 나타나는 문제점을 보여주는 예제이다.

 생성자와 소멸자
생성자와 소멸자는 특별한 기능을 가진 멤버 함수다. 생성자는 객체를 생성할 때 자동적으로 호출되는 함수고, 소멸자는 객체가 소멸될 때 자동으로 호출되는 함수다.

 // 디폴트 생성자
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자
  Point();  // 디폴트 생성자
};

// 멤버 함수 정의
void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

// 디폴트 생성자 정의
Point::Point()
{
  x = 0;
  y = 0;
}

int main()
{
  Point pt;

  pt.Print();

  return 0;
}

// 디폴트 생성자에 의해서 x, y 멤버 변수가 0으로 초기화 된걸 알 수 있다.

 // 인자가 있는 생성자
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자
  Point();  // 디폴트 생성자
  Point(int initialX, int initialY);  // 인자가 있는 생성자
};

// 멤버 함수 정의
void Point::Print()
{
  cout << "(" << x << ", " << y << ")\n";
}

// 디폴트 생성자 정의
Point::Point()
{
  x = 0;
  y = 0;
}

// 인자가 있는 생성자 정의
Point::Point(int initialX, int initialY)
{
  x = initialX;
  y = initialY;
}

int main()
{
  Point pt(35);

  pt.Print();

  return 0;
}


 // 복사 생성자
#include <iostream>
using namespace std;

class Point
{
public:
  int x, y;

  // 멤버 함수
  void Print();

  // 생성자
  Point();
  Point(int initialX, int initialY);

  // 복사 생성자
  Point(const Point& pt);
};

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

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

Point::Point(int initialX, int initialY)
{
  x = initialX;
  y = initialY;
}

// 복사 생성자 정의
Point::Point(const Point& pt)
{
  x = pt.x;
  y = pt.y;
}

int main()
{
  Point pt1(100100), pt2(200200);  // 인자가 있는 생성자 호출

  Point pt3 = pt1;  // 복사 생성자 호출
  pt3.Print();

  pt3 = pt2;
  pt3.Print();

  return 0;
}


    FND 실습
1. 앞의 예제를 수정하여, 버튼을 누르면 2개의 FND가 동일한 숫자를 표시하도록 하기
#include "port.h"

int main(void)
{
  DDRF = 0xff;
  PORTF = 0xff;

  volatile unsigned char i = 0;
  volatile unsigned char j = 0;
  volatile unsigned char a = 0;

  while(1)
  {
    i = 0;

    a = ~PINC;

    while(a >= 1)
    {
      a /= 2;
      i++;
    }
    j = i;
    j <<= 4;
    PORTF = j | i;
  }
  return 1;
}
// '<<' 연산과 '|' 연산을 활용하여 구현

2. 0~99까지 순차적으로 숫자를 1씩 증가하면서 FND에 표시 하기
#include "delay_m.h"
#include "port.h"

int main(void)
{
  unsigned char fnd1 = 0x00;
  unsigned char fnd2 = 0x00;

  DDRF = 0xff;
  PORTF = 0x00;

  while(1)
  {
    PORTF = fnd2 | fnd1;
    fnd1++;
    if(fnd1 >= 0x0A)
    {
      fnd1 = 0x00;
      if(fnd2 == 0x90)
      {
        fnd2 = 0x00;
      }
      else
      {
        fnd2 += 0x10;
      }
    }
    delay(600);
  }
  return 0;
}

3. 0~99까지 순차적으로 숫자를 1씩 증가시키다가 버튼을 누르면 0부터 다시 시작하기.
#include "delay_m.h"
#include "port.h"

int main(void)
{
  unsigned char fnd1 = 0x00;
  unsigned char fnd2 = 0x00;

  DDRF = 0xff;
  PORTF = 0x00;
  DDRC = 0x00;
  PORTC = 0x00;

  while(1)
  {
    if((PINC & 0x01== 0)
    {
      fnd1 = 0x00;
      fnd2 = 0x00;
      PORTF = 0x00;
    }
    else
    {
      PORTF = fnd2 | fnd1;
      fnd1++;
      if(fnd1 >= 0x0A)
      {
        fnd1 = 0x00;
        if(fnd2 == 0x90)
        {
          fnd2 = 0x00;
        }
        else
        {
          fnd2 += 0x10;
        }
      }
      delay(600);
    }
  }
  return 0;
}

+ Recent posts