예제

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 : 출력 데이터 상태


source
#include <iostream>
using namespace std;

class DynamicArray
{
private:
  int **array;
  int x;    // x축
  int y;    // y축
public:
  // 생성자
  DynamicArray();
  DynamicArray(int x, int y);
  DynamicArray(const DynamicArray& da);
  
  // 소멸자
  ~DynamicArray();

  // 멤버 함수
  int getX() const;
  int getY() const;
  void allocate(int x, int y);
  void free();
  void initArray();
  void printArray();
};

// 멤버 함수
int DynamicArray::getX() const
{
  return x;
}

// 멤버 함수
int DynamicArray::getY() const
{
  return y;
}

// 멤버 함수
void DynamicArray::allocate(int x, int y)
{
  this->= x;
  this->= y;

  // 동적 메모리 할당
  array = new int* [x];
  for(int i = 0 ; i < x ; ++i)
  {
    array[i] = new int[y];
  }
}

// 멤버 함수
void DynamicArray::free()
{
  // 동적 메모리 해제
  for(int i = 0 ; i < x ; ++i)
  {
    delete[] array[i];
  }
  delete[] array;
}

// 멤버 함수
void DynamicArray::initArray()
{
  int cnt = 0;

  for(int i = 0 ; i < x ; ++i)
  {
    for(int j = 0 ; j < y ; ++j)
    {
      array[i][j] = cnt;
      ++cnt;
    }
  }
}

// 멤버 함수
void DynamicArray::printArray()
{
  for(int i = 0 ; i < x ; ++i)
  {
    for(int j = 0 ; j < y ; ++j)
    {
      cout << array[i][j] << endl;
    }
  }
}

// 디폴트 생성자
DynamicArray::DynamicArray()
{
  array = NULL;  
}

// 인자가 있는 생성자
DynamicArray::DynamicArray(int x, int y)
{
  allocate(x, y);
}

// 복사 생성자
DynamicArray::DynamicArray(const DynamicArray& da)
{
  x = da.getX();
  y = da.getY();
  allocate(x, y);

  for(int i = 0 ; i < x ; ++i)
  {
    for(int j = 0 ; j < y ; ++j)
    {
      array[i][j] = da.array[i][j];
    }
  }
}

// 소멸자
DynamicArray::~DynamicArray()
{
  free();
}

int main()
{
  DynamicArray da(57);

  da.initArray();
  da.printArray();

  return 0;
}

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;
}

출처 : http://blog.naver.com/radiskkk?Redirect=Log&logNo=70030108381


AVR-GCC

 

 SRAM에서의 변수 및 상수

 특별히 속성을 정하지 않고 변수를 정의하면 SRAM 영역에 저장된다. 이것들은 외부 메모리에 저장되는 경우에 비하여 빠르게 엑세스할 수 있다. 축약 표현은 <stdio.h>에 정의되어 있다. 그러나, <io.h>를 인클루드하면 내부적으로 <stdio.h> 파일을 자동 인클루드하도록 연결되어 있다.

 

- AVR-GCC에서 데이터의 형 및 이름 축약하여 표현 방법

일반적인 데이터형의 표현

AVR-GCC의 데이터형 축약 표현

바이트 수

표현 범위

signed char

int8_t

1

-128 ~ +127

unsigned char

uint8_t

1

0 ~ 255

signed int

int16_t

2

-32768 ~ +32767

unsigned int

uint16_t

2

0 ~ 65535

signed long

int32_t

4

-2147483648 ~ +21474783647

unsigned long

uint32_t

4

0 ~ 4294967295

signed long long

int64_t

8

-9.22×10^18 ~ +9.22×10^18

unsigned long long

uint64_t

8

0 ~ 1.844×10^19

 

intptr_t

2

-32768 ~ +32767

 

uintptr_t

2

0 ~ 65535

float

 

4

±8.4×10^-37 ~ ±3.4×10^38

double

 

8

±2.2×10^-308 ~ ±1.8×10^308

 

 

• 프로그램 메모리에서의 상수

 AVR의 프로그램 메모리인 플래시 메모리에 상수를 정의 가능하며 읽는 것만 가능하다. SRAM에 저장할 수도 있지만 플래시 메모리는 SRAM에 비하여 용량이 훨씬 크므로 유용하게 사용할 수 있다. 아래의 상수 데이터 표현을 사용하려면 <pgmspace.h>를 인클루드 하여야 한다.

 

- AVR-GCC에서 프로그램 메모리 상수를 정의하는 방법

데이터의 형

AVR-GCC에서 프로그램 메모리 상수

바이트 수

표현 범위

signed char

prog_char, prog_int8_t

1

-128 ~ +127

unsigned char

prog_uchar, prog_uint8_t

1

0 ~ 255

signed int

prog_int16_t

2

-32768 ~ +32767

unsigned int

prog_uint16_t

2

0 ~ 65535

signed long

prog_int32_t

4

-2147483648 ~ +21474783647

unsigned long

prog_uint32_t

4

0 ~ 4294967295

signed long long

prog_int64_t

8

-9.22×10^18 ~ +9.22×10^18

unsigned long long

prog_uint64_t

8

0 ~ 1.844×10^19

 

- AVR-GCC에서 프로그램 메모리 상수를 읽는 방법

프로그램 메모리 상수의 형

16비트 어드레스를 사용하여 읽기

32비트 어드레스를 사용하여 읽기

prog_char

prog_uchar

prog_int8_t

prog_uint8_t

pgm_read_byte(address_short)

pgm_read_byte_near(address_long)

pgm_read_byte_far(address_long)

prog_int16_t

prog_uint16_t

pgm_read_word(address_short)

pgm_read_word_near(address_long)

pgm_read_word_far(address_long)

prog_int32_t

prog_uint32_t

pgm_read_dword(address_short)

pgm_read_dword_near(address_long)

pgm_read_dword_far(address_long)

 

- 바이트 상수의 저장과 읽기

prog_int8_t aaa = {1};

int8_t value;

value = pgm_read_byte(&aaa);

 

- 바이트 상수 배열의 저장과 읽기

prog_int8_t array[] = {0, 1, 2, 3, 4};

int8_t value;

value = pgm_read_byte(&array[3]);

 

- 문자열의 저장과 읽기

int8_t *str1 = PSTR(“ABC DEF”);

int8_t letter;

letter = pgm_read_byte(str1 + 2);

 

다른 방법으로는

int8_t str2[] _attribute_ ((progmem)) = “ABC DEF”;

int8_t letter;

letter = pgm_read_byte(&str2[2]);

 

 

 EEPROM의 사용

 AVR에서 EEPROM을 엑세스하는 것은 I/O 레지스터를 사용하여야 하므로 AVR-GCC에서는 EEPROM을 엑세스하기 위한 특별한 함수들을 제공하는데, <eeprom.h>에 정의되어 있다.

데이터의 종류

EEPROM 읽기

EEPROM 쓰기

8비트 바이트

eeprom_read_byte()

_EEGET()

eeprom_write_byte()

_EEPUT()

16비트 워드

eeprom_read_word()

eeprom_write_word()

바이트 블록

eeprom_read_block()

eeprom_write_block()

 

- EEPROM의 상태 조사

eeprom_is_ready();

리턴 값이 1이면 ready, 0이면 busy 상태를 나타낸다.

 

- 8비트 바이트 또는 16비트 워드의 읽기, 쓰기

void eeprom_write_byte(uint8_t *addr, uint8_t value);

void eeprom_write_word(uint16_t *addr, uint16_t value);

 

uint8_t eeprom_read_byte(const uint8_t *addr);

uint16_t eeprom_read_word(const uint16_t *addr);

 

- SRAM n바이트의 *buf 버퍼값과 EEPROM의 지정된 어드레스 값과의 읽고 쓰기

void eeprom_write_block(const void *addr, void *buf, size_t n);

void eeprom_read_block(void *buf, const void *addr, size_t n);

 

- IAR C컴파일러의 호환

_EEGET(val, addr);

_EEPUT(addr, val);

 

 

 I/O 레지스터 및 병렬 I/O 포트의 액세스

 AVR-GCC에서는 I/O 레지스터 또는 확장 I/O 레지스터를 액세스하기 위한 여러가지 함수나 매크로를 지원하며, 헤더파일<sfr_defs,h>에 정의되어 있다. <io.h>를 인클루드하면 자동으로 인클루드가 된다.

 

- 8비트 I/O 레지스터 또는 확장 I/O 레지스터의 액세스

unsigned char a;

PORTB = 0x80;

a = PINB;

 

- 특정 비트의 조사

#define bit_is_set(sfr, bit)

#define bit_is_clear(sfr, bit)

 

- 레지스터의 특정 비트의 값을 기다리는 매크로

#define loop_until_bit_is_set(sfr, bit)

#define loop_until_bit_is_clear(sfr, bit)

 

 

• 외부 I/O나 메모리의 절대 번지 액세스

ex) #define keyword (*(volatile unsigned char *) 0x2000)

 

 

• 인터럽트 처리

 

- 인터럽트 서비스 루틴 함수

ISR(vector)

{

             // interrupt service routine

}

 

- 모든 사용하지 않는 인터럽트에 대한 인터럽트 처리 함수

ISR(_vector_default)

{

             // interrupt service routine

}

 

- 다른 인터럽트 서비스 루틴 함수

SIGNAL(signame)

{

             // interrupt service routine

}

이 방식은 나중에 폐지될 예정이므로 사용하지 않는 것이 좋다. 예전에 사용하던 INTERRUPT(signame) 형식은 폐지되어서 현재는 사용하지 않는다.

 

- 인터럽트 처리 함수를 정의하는 vector  signame

번호

vector (새로운 방식)

signame (옛 방식)

인터럽트 발생 조건

1

INT0_vect

SIG_INTERRUPT0

External Interrupt Request 0

2

INT1_vect

SIG_INTERRUPT1

External Interrupt Request 1

3

INT2_vect

SIG_INTERRUPT2

External Interrupt Request 2

4

INT3_vect

SIG_INTERRUPT3

External Interrupt Request 3

5

INT4_vect

SIG_INTERRUPT4

External Interrupt Request 4

6

INT5_vect

SIG_INTERRUPT5

External Interrupt Request 5

7

INT6_vect

SIG_INTERRUPT6

External Interrupt Request 6

8

INT7_vect

SIG_INTERRUPT7

External Interrupt Request 7

9

TIMER2_COMP_vect

SIG_OUTPUT_COMPARE2

Timer/Counter2 Compare Match

10

TIMER2_OVF_vect

SIG_OVERFLOW2

Timer/Counter2 Overflow

11

TIMER1_CAPT_vect

SIG_INPUT_CAPTURE1

Timer/Counter1 Capture Event

12

TIMER1_COMPA_vect

SIG_OUTPUT_COMPARE1A

Timer/Counter1 Compare Match A

13

TIMER1_COMPB_vect

SIG_OUTPUT_COMPARE1B

Timer/Counter1 Compare Match B

14

TIMER1_OVF_vect

SIG_OVERFLOW1

Timer/Counter1 Overflow

15

TIMER0_COMP_vect

SIG_OUTPUT_COMPARE0

Timer/Counter0 Compare Match

16

TIMER0_OVF_vect

SIG_OVERFLOW0

Timer/Counter0 Overflow

17

SPI_STC_vect

SIG_SPI

SPI Serial Transfer Complete

18

USART0_RX_vect

SIG_UART0_RECV

USART0 Rx Complete

19

USART0_UDRE_vect

SIG_UART0_DATA

USART0 Data Register Empty

20

USART0_TX_vect

SIG_UART0_TRANS

USART0 Tx Complete

21

ADC_vect

SIG_ADC

ADC Conversion Complete

22

EE_READY_vect

SIG_EEPROM_READY

EEPROM Ready

23

ANALOG_COMP_vect

SIG_COMPARATOR

Analog Comparator

24

TIMER1_COMPC_vect

SIG_OUTPUT_COMPARE1C

Timer/Counter1 Compare Match C

25

TIMER3_CAPT_vect

SIG_INPUT_CAPTURE3

Timer/Counter3 Capture Event

26

TIMER3_COMPA_vect

SIG_OUTPUT_COMPARE3A

Timer/Counter3 Compare Match A

27

TIMER3_COMPB_vect

SIG_OUTPUT_COMPARE3B

Timer/Counter3 Compare Match B

28

TIMER3_COMPC_vect

SIG_OUTPUT_COMPARE3C

Timer/Counter3 Compare Match C

29

TIMER3_OVF_vect

SIG_OVERFLOW3

Timer/Counter3 Overflow

30

USART1_RX_vect

SIG_UART1_RECV

USART1 Rx Complete

31

USART1_UDRE_vect

SIG_UART1_DATA

USART1 Data Register Empty

32

USART1_TX_vect

SIG_UART1_TRANS

USART1 Tx Complete

33

TWI_vect

SIG_2WIRE_SERIAL

Two-wire Serial Interface

34

SPM_READY_vect

SIG_SPM_READY

Store Program Memory Ready

 

 

• 기본 헤더 파일

1 → C:\WinAVR\avr\include\ 폴더에 위치

2 → C:\WinAVR\avr\include\avr\ 폴더에 위치

3 → C:\WinAVR\avr\include\compat\ 폴더에 위치

4 → C:\WinAVR\avr\include\util\ 폴더에 위치

 

- WinAVR에 제공하는 헤더 파일

 

헤더파일

파일의 내용

비고

assert.h

Diagnostics

1

boot.h

Bootloader support utilities

2

crc16.h

CRC-16 computations

24

ctype.h

Character operations

1

delay.h

Busy-wait delay loops

24

eeprom.h

EEPROM handling

2

errno.h

System errors

1

ina90.h

Compatibility header for IAR C compiler EWB 3.x

3

interrupt.h

Interrupt handling

2

inttypes.h

Integer type conversions

1

io.h

AVR device-specific I/O definitions

2

iom128.h

I/O definitions for various AVR microcontrollers

2

math.h

Mathemathics

1

parity.h

Parity bit generations

24

pgmspace.h

Program space string utilities

2

portpins.h

I/O port bit number definitions

2

setjmp.h

Non local goto

1

sfr-defs.h

Special function registers

2

signal.h

기능이 모두 interrupt.h 파일로 흡수되어 사용하지 않음

2

sleep.h

Power management and sleep modes

2

stdint.h

Standard integer types

1

stdio.h

Standard I/O facilities

1

stdlib.h

General utilities

1

string.h

More string manipulation routines

1

twi.h

TWI bit mask definitions

34

version.h

avr-libc version macros

2

wdt.h

Watchdog timer handling

2

 

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

접근 권한을 설정하기 위한 키워드로는 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++;
}


1. 1바이트는 8개의 비트로 이루어집니다. 255 8개 비트 전체가 1인 경우이고, 1은 최하위 1비트만 1인 경우입니다. 문자 ch의 켜진 비트 개수는 몇 개입니까? 켜진 비트는 1로 설정된 비트의 다른 표현입니다.

대문자 ‘A’의 값은 65이고 6번째와 1번째 비트가 켜져 있습니다.

#include <iostream>
using namespace std;

int main()
{
  char c;
  int cnt = 0;
  int tmp = 1;

  cout << "문자 입력: ";
  cin >> c;

  for(int i = 0 ; i < 8 ; ++i)
  {
    if(c & tmp)
    {
      ++cnt;
    }
    tmp <<= 1;
  }
  cout << "켜진 비트의 개수: " << cnt << endl;
  cout << "값: " << (int)c << endl;

  return 0;
}


2. 정수 N이 소수입니까?

#include <iostream>
using namespace std;

int main()
{
  int num;
  int cnt = 0;

  cout << "정수 입력: ";
  cin >> num;

  for(int i = 1 ; i <= num ; i++)
  {
    if(num % i == 0)
    {
      ++cnt;
    }
  }

  if(cnt == 2)
  {
    cout << "결과 : 소수" << endl;
  }
  else
  {
    cout << "결과 : 합성수[" << cnt << "]" << endl;
  }

  return 0;
}


3. 로또 번호 생성 프로그램

    • 사용자에게 로또 게임 수를 입력받아 게임의 횟수별로 임의로 6개의 수(1~45)를 자동 생성하는 프로그램을 작성하시오. , 생성된 난수는 모두 다른 값이어야 한다.
    •  로또 번호는 정수형 배열을 선언하여 설정한다.
    • 프로그램의 주요한 부분에 주석을 명시하시오.
#include <iostream>
using namespace std;

bool sameNum(const int *arr, int size, int num);

int main()
{
  int num[6];  // 생성된 난수 보관 하는 변수
  int cnt;
  int tmp;  // 난수 값을 임시로 받는 변수

  srand((unsigned int)time(NULL));

  cout << "Enter the game count: ";
  cin >> cnt;

  for(int i = 0 ; i < cnt ; ++i)
  {
    for(int j = 0 ; j < 6 ; ++j)
    {
      tmp = (rand() % 45) + 1;
      while(sameNum(num, j + 1, tmp))  // 같은 값이 있으면
      {
        tmp = (rand() % 45) + 1;  // 새로 값을 받음
      }
      num[j] = tmp;
    }

    // 생성된 난수 출력
    cout << "game" << i << " : ";
    for(int j = 0 ; j < 6 ; ++j)
    {
      cout.width(2);
      cout << num[j] << " ";
    }
    cout << endl;
  }

  return 0;
}

// 같은 값이 있는지 확인해 주는 함수
bool sameNum(const int *arr, int size, int num)
{
  for(int i = 0 ; i < size ; ++i)
  {
    if(arr[i] == num)
    {
      return 1;
    }
  }
  return 0;
}




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



1. 키보드로부터 최대 세 자리의 정수를 입력 받습니다. 자릿수들의 합계는 얼마입니까?

#include <iostream>
using namespace std;

int main()
{
  int num;
  int result;

  while(1)
  {
    cout << "입력(3자리): ";
    cin >> num;

    if(-1 == num)
    {
      break;
    }
    else if((0 > num) || (999 < num))
    {
      cout << "3자리이하의 수를 입력하시오" << endl;
    }
    else
    {
      result = num / 100;    // 100 자리 
      num %= 100;
      result = result + (num / 10);  // 100 자리 + 10자리
      num %= 10;
      result += num;    // 100 자리 + 10 자리 + 1자리
      cout << "자릿수 합계: " << result << endl;
    }
  }
  cout << "프로그램 종료" << endl;
  
  return 0;
}


2. 10진수 0부터 16까지의 정수를 8진수로 출력합니다. 출력에는 10진수와 8진수의 대응관계를 반드시 포함시킵니다. 10진수 8 8진수로 10입니다.

#include <iostream>
using namespace std;

int main()
{
  cout << "10진수\t8진수" << endl;

  for(int i = 0 ; 17 > i ; i++)
  {
    cout << dec << i << "\t";  // 10진수 출력
    cout << oct << i << endl;  // 8진수 출력
  }

  return 0;
}


3. 1바이트 범위의 정수를 입력 받은 다음, 각각의 비트가 켜져 있으면 1, 꺼져 있으면 0을 출력하세요.

#include <iostream>
using namespace std;

int main()
{
  int num;
  char tmp;
  int cnt = 0;

  while(1)
  {
    cout << "정수 입력(0~255): ";
    cin >> num;

    if(-1 == num)
    {
      cout << "프로그램 종료" << endl;
      break;
    }

    for(tmp = 1 ; 0 != tmp ; tmp <<= 1)
    {
      if(num & tmp)  // 각 자리 수가 1이면 
      {
        cout << cnt << " : 1" << endl;
      }
      else
      {
        cout << cnt << " : 0" << endl;
      }
      cnt++;
    }
    cnt = 0;
  }

  return 0;
}


4. 난수를 발생하는 rand 함수를 활용하여 주사위를 10번 던졌을 때 주사위 값을 출력하시오.

#include <iostream>
using namespace std;

int main()
{
  int num;

  srand((unsigned int)time(NULL));

  cout << "(주사위 값 : 1 ~ 6)" << endl;
  for(int i = 0 ; 10 > i ; i++)
  {
    num = (rand() % 6) + 1;
    cout << "dice value: " << num << endl;
  }
  return 0;
}


+ Recent posts