sudo apt-get install manpages-dev glibc-doc
맨페이지 설치

http://linux.die.net/man/
영문 manpage 사이트

FND count 99 with DK128

main.c
#include "port.h"
#include "delay_m.h"

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

  DDRF = 0xff;  // 
포트 초기화
  PORTF = 0x00;
  DDRC = 0x01;
  PORTC = 0x00;

  while(1)
  {
    if(PINC & 0x02)  // 
출력 신호를 받았을 
    {
      flag = 1;  // 
출력 flag set
    }

    if(flag)
    {
      PORTF = fnd2 | fnd1;
      fnd1++;
      
      if(fnd1 >= 0x0A)  // 
출력이 끝난 시점
      {
        fnd1 = 0x00;
        PORTC = 0x01;  // 
상대편 FND 출력 시작
        flag = 0;    // 
출력 flag off
        check = 1;    // C PORT 
초기화를 위한 check

        if(fnd2 >= 0x80)  // 10
자리 처리 
        {
          if(fnd2 == 0x80)  // 10
자리가 8 
          {
            fnd2 = 0x00;
          }
          else        // 10
자리가 9  
          {
            fnd2 = 0x10;
          }
        }
        else        // 10
자리를 2 증가
        {
          fnd2 += 0x20;
        }
      }
    }
    else        // 
출력 안할 
    {
      PORTF = 0x00;
    }
    
    delay(600);

    if(check)      // C PORT 
초기화
    {
      PORTC = 0x00;
      check = 0;
    }
  }
  return 0;
}

// DK128 두 대를 연결해서 99까지 카운트 하는 코드이다. 일단 A DK128에서 0에서 9까지 카운트하고, 그 다음 B DK128에서 10부터 19까지 카운트 하는 방식으로 99까지 카운트한다.
// C PORT에 핀 2개를 이용해서 서로 카운트 동기화를 한다.

fopen(), fclose(), fprinf(), fscanf()

main.c
#include <stdio.h>

int main()
{
  int i;

  fprintf(stdout, "Enter the number: ");
  fscanf(stdin, "%d"&i);
  fprintf(stdout, "i = %d\n", i);

  return 0;
}
// fprintf(), fscanf() 함수들을 사용법을 알아보았다. 이 함수들은 기본적으로 입출력 스트림을 지정 할 수 있다. 위 코드를 보면 fprintf는 stdout(모니터) 표준출력으로 출력하고 있고, fscanf는 stdin(키보드) 표준입력에서 입력받는다. 즉, 각각 printf, scanf와 같은 동작을 한다고 보면 된다.

main1.c
#include <stdio.h>

int main()
{
  fprintf(stdout, "one\n");
  fprintf(stderr, "two\n");
  fprintf(stdout, "three\n");

  return 0;
}
// stdout은 표준출력, stderr은 표준에러로 둘 다 모니터로 출력된다. 기본적으로 둘 다 모니터로 출력은 되지만 엄밀히 따지면 서로 다른 스트림이다.


// 리다이렉션(>)명령을 통해 이를 확인 할 수 있었다.

p5-9.c
#include <stdio.h>

int main()
{
  int score;
  char name[10];
  FILE *fpin;
  FILE *fpout;

  fpin = fopen("d0327.in""r");
  fpout = fopen("d0327.out""w");

  while(!(feof(fpin)))
  {
    fscanf(fpin, "%s %d", name, &score);
    fprintf(stdout, "%s %d\n", name, score);
    fprintf(fpout, "%s\t%d\n", name, score);
  }
  fclose(fpin);
  fclose(fpout);

  return 0;
}


// fopen(), fclose() 함수를 이용하여 직접 파일에서 데이터를 읽어오고 데이터를 써보는 코드이다. 기본적으로 파일 입출력을 위해서 FILE 구조체 포인터가 필요하며, 파일을 이 구조체에 연결시켜주는 함수가 fopen()이고 파일을 다 쓴 후 연결을 끈어주는 함수가 fclose()이다. 함수 원형들은 http://www.winapi.co.kr 사이트에 가보면 잘 알 수 있다.
// 이 프로그램은 d0327.in 파일에서 데이터를 읽어와서 모니터와 d0327.out으로 출력시키는 프로그램이다.

p5-10.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
  FILE *fp;
  char ch;
  int count;

  if((fp = fopen("d0328.dat""r")) == NULL)
  {
    printf("File open error!!\n");
    exit(1);
  }

  count = fscanf(fp, "%c"&ch);
  while(count != EOF)
  {
    putchar(ch);
    count = fscanf(fp, "%c"&ch);
  }
  fclose(fp);

  return 0;
}


// 이 프로그램은 d0328.dat에서 데이터를 문자 단위로 읽어와서 화면에 출력해주는 프로그램이다.

더블클릭

main.c
#include "port.h"

#define LED_ON(led)    PORTF &= (~(0x01 << led))
#define LED_OFF(led)  PORTF |= (0x01 << led)

int main(void)
{
  unsigned int us = 0;
  unsigned int ms = 0;
  char click_count = 0;  // 
클릭 횟수 카운트
  char start_flag = 0;  // 
더블 클릭 시간감지 플래그
  char action = 0;    // 
클릭에 대한 액션을 결정

  DDRF = 0xff;  // 
포트 최기화
  PORTF = 0xff;
  DDRC = 0x00;
  PORTC = 0x00;
  
  while(1)
  {
    if(ms >= 500)  // 
시간 카운트 종료
    {
      start_flag = 0;
      ms = 0;
      action = click_count;
      click_count = 0;
    }
    else if((PINC & 0x01== 0)  // 
버튼을 눌렀을 
    {
      if(click_count == 0 || (click_count % 2== 0)
      {
        start_flag = 1;
        click_count++;  // 
버튼을 눌렀을  카운트 
      }
    }
    else if((PINC & 0x01&& start_flag)  // 
버튼 땠을 
    {
      if((click_count % 2== 1)
      {
        click_count++;  // 
버튼을 땠을  카운트
      }
    }

    us++;
    if(us >= 1000)
    {
      if(start_flag)  // 
시간 카운트 시작
      {
        ms++;
      }
      us = 0;
    }

    // 
클릭에 대한 동작 코드
    if(action)
    {
      LED_OFF(0);
      LED_OFF(1);
      LED_OFF(2);

      switch(action) 
      {
        case 1:
        case 2:
          LED_ON(0);  // 
 클릭 했을 
          break;
        case 3:
        case 4:
          LED_ON(1);  // 
더블 클릭 했을 
          break;
        case 5:
        case 6:
          LED_ON(2);  // 
트리플 클릭 했을 
          break;
        defalut :
          break;
      }
      action = 0;
    }
  }
  
  return 0;
}

// DK128에서 스위치 1번을 한번 클릭 했을 때 첫 번째 LED를 ON시키고, 더블 클릭 했을 때 두 번째 LED를 ON, 트리플 클릭 했을 때 세 번째 LED를 ON 시키는 코드이다.
// 스위치를 눌렀을 때 카운트를 하나씩 올리고 땔 때 다시 카운트를 올린다. 즉 카운트 값이 1이나2이면 한번 클릭을 한것이다.

FND 제어

delay_m.h
void delay(unsigned int ms);

void delay(unsigned int ms)
{
  unsigned int u = 0;
  unsigned int m = 0;

  while(1)
  {
    u++;
    if(u >= 1000)
    {
      m++;
      u=0;
    }
    if(m >= ms)
    {
      break;
    }
  }
}


main.c
#include "port.h"
#include "delay_m.h"

int main(void)
{
  unsigned char fnd1 = 0x00;  // 1
자리
  unsigned char fnd2 = 0x00;  // 10
자리

  DDRF = 0xff;  // 
 초기화
  PORTF = 0x00;
  DDRC = 0x00;
  PORTC = 0x00;
  
  while(1)
  {
    if((PINC & 0x01== 0)  // 
스위치를 눌렀을 
    {
      PORTF = fnd2 | fnd1;
    }
    else          // 
스위치가 눌러지지 않았을 
    {
      PORTF = fnd2 | fnd1;
      fnd1++;
      if(fnd1 >= 0x0A)  // 1
자리가 9 되면
      {
        fnd1 = 0x00;
        if(fnd2 == 0x90)  // 10
자리가 9 되면
        {
          fnd2 = 0x00;
        }
        else
        {
          fnd2 += 0x10;
        }
      }
    }
    delay(600);
  }
  return 0;
}
// 7-segment를 1부터 99까지 카운트하는 코드이다. 스위치를 누르면 카운트를 중지한다.
// 각 자리 수를 변수 2개를 OR연산해서 구현하였다.

포인터 연산

P5-4.c
#include <stdio.h>

int main()
{
  int inum;
  int *ip;
  short *sp;
  unsigned char *c;
  int i;

  inum = 0x0F5A0B43;
  ip = &inum;
  c = (unsigned char *)ip;

  printf("inum: %x  *ip: %p \n", inum, ip);
  sp = (short *)ip;  
  printf("sp: %p  *sp: %x \n", sp, *sp);
  sp++;    // short
형이라 증감연산시 2 증가 한다
  printf("sp: %p  *sp: %x \n", sp, *sp);

  for(i = 0 ; i < 4 ; i++)  // Little Endian
 확인하는 코드
  {
    printf("addr:%p  *c: %02x \n", c, *c);
    c++;  // char
형이라 증감연산시 1 증가 한다
  }

  return 0;
}


// 포인터 연산은 덧셈과 뺄셈만 허용된다. 덧셈과 뺄셈 연산 시 가리키는 자료형의 크기만큼 변한다.
// 인텔 x86 cpu들은 Little Endian 방식을 사용한다. 이를 확인하기 위해서 char형 포인터를 이용하여 1바이트씩 출력하여 확인할 수 있었다.

SRCS    = $(wildcard *.c)
CC        = gcc

.SUFFIXES : .o
% : %.o
    $(CC) -o $@ $^

all : $(SRCS:.c=)
clean :
    rm -rf $(SRCS:.c=)

현재 폴더에 있는 *.c파일들을 확장자 앞의 각 파일이름으로 된 실행파일로 컴파일해서 만들어준다.



Debugger

Windows Visual Studio

Debugging 이란 프로그램의 논리적 오류를 찾아내서 수정하는 일이며, 이 작업을 도와 주는 툴을 Debugger라고 한다. 즉 프로그램의 논리적 오류를 검출하는데 사용하는 프로그램이 Debugger이다. 윈도우 환경에서 우리가 설치한 Visual Studio 6에는 Debugger가 포함되어 있는데, 간단한 사용법을 알아 보았다.
1. 일단 Debugging 해볼 간단한 코드를 작성해 보았다.


2. 코드를 작성하고 F7을 눌려 Build를 한다.
3. Debugging 할 부분에 F9를 눌러 Breakpoint를 건다. 밑의 그림과 같이 빨간색 점이 생긴다.


4. F5를 눌러 Debugging을 시작한다. 그러면 프로그램이 실행되고 Breakpoint에서 실행을 멈춘다.
위 그림에서 화살표는 현재 코드를 실행할 차례라는 것을 의미한다. 왼쪽 밑에 보면 Auto라는 탭이 있는데 여기는 Debugger가 알아서 변수를 등록시켜서 그 값들을 출력해준다. 현재 iNum변수가 초기화 되지 않아 쓰레기 값을 가지고 있는 것을 알 수 있다.
5. Debug 메뉴를 보면 step over, step into라는 명령이 있다. step over(F10)는 현재 행을 실행하되 블록 안으로 들어가지 않는 명령어이고, step into(F11)는 현재 행을 실행하고 블록 안으로 들어가는 명령어 이다. 여기선 iNum값들을 확인하기 위해서 F11(step into)를 눌러 진행한다.


위 그림을 보면 for문이 처음 실행되고 iNum값이 0으로 설정된 것을 볼 수 있다. 이런 식으로 변수 값들을 직접 눈으로 확인하면서 프로그램의 오류를 검출한다. 또한 오른쪽 밑의 watch에 따로 변수를 등록할 수도 있다.

오른쪽 밑 watch에 &iNum을 등록해서 주소값과 실제 가지고 있는 값을 다 알 수 있다.

Linux Debugger GDB

Linux에도 강력한 Debugger GDB가 있다. GDB는 텍스트 기반 Debugger로 많은 명령어들이 있다

l(list)

소스 보기

b

브레이크 포인트

info breakpoints

브레이크 포인트 정보(info b)

cl

브레이크 포인트 삭제

r(run)

프로그램 수행

s(step)

프로그램을 한 행씩 실행(함수 호출 시 내부로 들어감)

n(next)

프로그램을 한 행씩 실행(함수 호출 시 내부로 들어가지 않음)

c(continue)

브레이크 포인트를 만날 때까지 실행

u

현재 루프를 빠져 나감

finsh

현재 함수를 수행하고 빠져나감

return

현재 함수를 수행하지 않고 빠져나감

si

인스트럭션 단위로 s동작 실행

ni

인스트럭션 단위로 n동작 실행

watch

변수 값이 변할 때마다 알려줌

info locals

현재 상태에서 지역 변수들을 보여줌

info variable

현재 상태에서 전역 변수들을 보여줌

p(print)

해당 변수나 함수 값을 보여준다(밑에 표 참조)

p 변수=설정값

변수 값 설정

display

변수 값을 계속 보여줌(밑에 표 참조)

info frame

현재 함수의 스택 프레임을 보여줌

x

메모리 상태 검사(x/[범위][출력 형식] [범위의 단위])

disas

어셈블리 코드 보기

call

함수를 임의대로 호출 할 수 있다

signal

프로세스에게 시그널을 보낸다

set

메모리 특정 영역에 값을 설정(set {타입}[주소] = [값])

 

p [변수명]

변수 값을 출력

p [함수명]

함수의 주소를 출력

p/[출력 형식] [변수명]

변수 값을 출력 형식으로 출력

p '[파일명]'::[변수명]

파일명에 있는 전역 변수의 값을 출력

p [함수명]::[변수명]

함수에 있는 변수 값을 출력

p [변수명]@[배열 크기]

변수의 내용을 변수 배열의 크기 형태로 출력

 

display [변수명]

변수 값을 매번 화면에 디스플레이 한다

display/[출력 형식] [변수명]

변수 값을 출력 형식으로 디스플레이 한다

undisplay [디스플레이 번호]

디스플레이 설정을 없앤다

disable display [디스플레이 번호]

디스플레이를 일시 중단한다

enable display [디스플레이 번호]

디스플레이를 다시 활성화한다

 

t

2진수로 출력

o

8진수로 출력

d

부호가 있는 10진수로 출력(int)

u

부호가 없는 10진수로 출력(unsigned int)

x

16진수로 출력

c

최소 1바이트 값을 문자형으로 출력

f

부동 소수점 값 형식으로 출력

a

가장 가까운 심볼의 오프셋을 출력

기본적인 명령어는 다음과 같다. 위와 같은 코드를 리눅스에서 GDB를 이용하여 Debugging 해보았다.
1. GDB에서 Debugging을 하기 위해서는 일단 컴파일 할 때 옵션 -g를 주어야 한다. 이 옵션은 Debugging정보를 넣고, 모든 최적화 flag를 turn off 한다.
2. "$ gdb 실행파일명" 으로 GDB를 실행한다.


3. list 명령으로 소스코드를 볼 수 있다

4. b 명령으로 7번째 줄에 Breakpoint를 건다. 그리고 r명령을 눌러 실행하면 7번째 줄에서 실행을 멈춘다.

5. display명령으로 iNum의 상태를 계속 확인 할 수 있다. s 명령으로 계속 소스코드를 실행시켜 보면서 iNum값을 확인 할 수 있다.

일단 GDB를 이용하여 간단하게 Debugging을 해보았다. 윈도우 시스템에서와 달리 for문에 아무 명령문이 없었을 때, 리눅스에서는 i 값을 바로 5로 바꿔버려서 iNum값의 변화를 알 수가 없었다. 그래서 printf("test\n"); 명령문을 넣어 iNum값을 확인 할 수 있게 하였다.

 

LED ON/OFF 일반화

main.c
#include "port.h"

#define LED_ON(led)        PORTF &= (~(0x01 << led))    // led번째LED ON
#define LED_OFF(led)        PORTF |= (0x01 << led)    // led번째LED OFF
int main(void)
{
    unsigned int us = 0;
    unsigned int ms = 0;
    unsigned char led = 0x00;

    DDRF = 0xff;
    PORTF = 0x00;

    while(1)
    {
        us++;
        if(us >= 1000)
        {
            ms++;
            us = 0;
        }

        if(ms >= 500)
        {
            if(led == 8)
            {
                led = 0;
                PORTF = 0x00;
            }

            LED_OFF(led);
//            LED_ON(led);

            led++;
            ms = 0;
        }
    }
    return 0;
}

#define LED_ON(led)    PORTF &= (~(0x01 << led))
LED를 ON시키려면 Low 신호를 주면 된다. 어떤 수든 상관없이 Low를 만들려면 0을 AND연산하면 되므로 다음과 같이 원하는 비트만큼 0x01를 <<연산한 다음에 보수를 취해서 AND연산을 하면 ON시킬 수 있다.
#define LED_OFF(led)    PORTF |= (0x01 << led)
LED를 OFF 시키려면 High 신호를 주면 된다. 어떤 수든 상관없이 High를 만들려면 1을 OR연산하면 되므로 다음과 같이 원하는 비트만큼 0x01를 <<연산한 다음 OR연산을 하면 OFF 시킬 수 있다.

소수를 구하는 프로그램

main.c
#include <stdio.h>

int main()
{
    int i, j;

    printf("2\n");
    for(i = 3 ; i <= 100 ; i += 2)
    {
        for(j = 3 ; j <= i ; j += 2)
        {
            if(i % j == 0)
            {
                break;
            }
        }
        if(j == i)
        {
            printf("%d\n", i);
        }
    }
    
    return 0;
}


// 1부터 100까지 사이에 소수를 구하는 프로그램이다.
// 일단 2의 배수들은 2를 제외하고 소수가 될 수 없으므로 일단 제외하고, 나머지 수들을 각각 3부터 시작해서 2씩 증가시킨 값들로 자기 자신과 같은 값을 만날 때까지 나눠서 그 전에 나머지가 0인 값이 있으면 소수가 아니므로 제외하고, 자기 자신까지 도달하면 소수이므로 출력 시키는 방법으로 동작한다.

 

DK128간 통신으로 LED 켜기

main.c
#include "port.h"

int main(void)
{
    unsigned char led;
    
    DDRE = 0x00;
    PORTE = 0x00;

    DDRF = 0xFF;
    PORTF = 0xFF;

    DDRC = 0x00;
    PORTC = 0x00;

    while(1)
    {
        led = PINC;
        led = 0xFF - led;

        switch(led)
        {
            case 0x01 :
                DDRF = 0x01;
                break;
            case 0x02 :
                DDRF = 0x03;
                break;
            case 0x04 :
                DDRF = 0x07;
                break;
            case 0x08 :
                DDRF = 0x0F;
                break;
            case 0x10 :
                DDRF = 0x1F;
                break;
            case 0x20 :
                DDRF = 0x3F;

                break;
            case 0x40 :
                DDRF = 0x7F;
                break;
            case 0x80 :
                DDRF = 0xFF;
                break;
            default :
                DDRF = 0x00;
        }
        if(PINE & 0x01)
        {
                DDRF = 0xFF;
        }
        else
        {
                DDRF = 0x00;
        }

    }
    return 0;
}

// DK128보드 2개를 이용해서 스위치를 누를 때마다 각각 LED ON 개수를 늘려가는 코드이다. 예전 실습 시간에 작성 했던 코드에서 조금만 수정해서 작성 할 수 있었다. 일단 한쪽에선 스위치가 눌려 졌다는 신호를 받아서 LED 8개 모두를 ON 시키고, 다른 한쪽에서는 키가 눌려 졌을 때 눌러 졌다는 신호를 다른쪽 DK128한테 보내기만 하면 구현이 가능했다.
// 이 코드는 신호를 받아서 LED 모두를 ON 시키는 코드이다.

DK128간 통신으로 LED 켜기

DK128간 서로 통신하여 한 번씩 LED를 ON 시키는 코드를 작성해 보았다. 즉 A DK128에서 8개의 LED를 차례되로 ON 시킨 후 B DK128에 신호를 준다. 그러면 B DK128이 8개의 LED를 차례되로 ON 시킨 후 다시 A DK128에 신호를 주면 A DK128이 다시 LED ON 동작을 반복하는 코드이다.
main.c
#include "port.h"    // 각핀들에대한정의가선언되어있다


int
main()
{
    int flag = 1;        //입출력여부확인flag
    unsigned int us = 0;    //시간딜레이를위한변수
    unsigned int ms = 0;
    unsigned char led = 0x01;
    unsigned char check = 0;

    DDRF = 0xff;
    PORTF = 0xff - led;    //portf 초기값led 하나만켜지는상태

    DDRC = 0x01;        //0번은출력1번은입력으로사용
    PORTC = 0x00;

    while(1)
    {
        us++;
        if(us >= 1000)
        {
            ms++;
            us = 0;
        }
        if(PINC & 0x02)        // 1번핀으로입력이들어오면
        {
            flag = 1;
            if(led == 0x00)
            {
                led = 0x01;
                PORTF = 0xff - led;
            }
        }

        if(ms >= 500)
        {
            if(check)    // 출력을하고있을때
            {
                PORTC = 0x00;    // C핀출력을리셋
                check = 0;
            }
            if(flag)
            {
                led = led << 1;
                PORTF = 0xff - led;
                if(led == 0x00)    // led이동이끝난후
                {
                    flag = 0;
                    PORTC = 0x01;    // 0번핀으로High 신호출력
                    check = 1;
                }
            }
        ms = 0;
        }
    }

    return 0;
}
// F핀은 LED 출력으로 사용하고, C핀은 DK128간 통신을 하는데 사용하고 있다. A DK128은 C0핀을 입력으로 C1핀을 출력으로 B DK128은 C1핀을 입력으로 C0핀을 출력으로 사용한다.
// 코드를 보면 입력을 받으면 LED 출력 여부를 판별하는 flag를 1로 셋팅해주는 코드가 있는데 이 코드가 us 카운트 밑에 있다. 이는 두 DK128간 통신 동기화를 위해서 us 카운트하는 코드 존재한다.

 

C언어 for 문

for문은 반복문으로 while문과 비교해서 동작에는 차이가 없다. 다만 초기식, 조건식, 증감식 양식만 조금 다르다고 생각하면 된다
    for(초기식 ; 조건식 ; 증감식)
    {
        명령문
    }
p3-21.c
#include <stdio.h>

int main()
{
    int i;
//    i = 1;

//    while(i <= 5)
    for(i = 1; i <= 5 ; i++)
    {
        printf("%d\t", i);
//        i++;
    }
    putchar('\n');

    return 0;
}


// 1부터 5까지의 정수를 화면에 출력하는 프로그램이다. for문으로 작성된 이 코드는 while문으로 변경이 가능하다 위의 코드에서 현재 주석처리 된 부분을 해제하고 for문 한 줄만 주석처리 하면 완전한 동일한 동작을 하는 코드가 된다.

p3-23.c
#include <stdio.h>
#include <conio.h>

int main()
{
    char ch;
    int i;

    printf("Please enter any character: ");
    for(i = 0 ; (ch = getche()) != '$' ; i++)
    {
        printf("\nYou typed : %c\n", ch);
        printf("Please enter any character: ");
    }
    putchar('\n');

    return 0;
}


// for문 조건식 안에 함수를 사용하였다. '$'이 입력 될 때까지 계속해서 for문 블록 안의 명령이 계속 반복해서 실행된다

main.c
#include <stdio.h>

int main()
{
    int i, j;

    for(j = 1 ; 10 > j ; j++)
    {
        for(i = 3 ; 9 > i ; i++)
        {
            printf("%d * %d = %d\t", i, j, i * j);
        }
        putchar('\n');
    }

    return 0;
}


// 3단부터 8단까지 가로로 출력하는 프로그램
// 각 구구단을 가로로 출력하기 위해서는 각 단의 *1을부터 가로로 다 출력하고 그 다음 *2, *3... 차례되로 출력해 주면 된다.

전자회로실험

스위치를 누름에 따라 LED ON 개수를 하나씩 늘려가는 코드
main.c
#include "port.h"

int main(void)
{
    unsigned char led;

    DDRF = 0x00;        //F핀 초기화
    PORTF = 0x00;

    DDRC = 0x00;        //C핀 초기화
    PORTC = 0x00;

    while(1)
    {
        led = PINC;        // C핀으로 입력된 값을 보수를 취하기 위한 코드
        led = 0xFF - led;

        switch(led)
        {
            case 0x01 :        //첫번째 스위치가 눌려지면
                DDRF = 0x01;
                break;
            case 0x02 :        //두번째 스위치가 눌려지면
                DDRF = 0x03;
                break;
            case 0x04 :        //세번째 스위치가 눌려지면
                DDRF = 0x07;
                break;
            case 0x08 :         //네번째 스위치가 눌려지면
                DDRF = 0x0F;
                break;
            case 0x10 :         //다섯번째 스위치가 눌려지면
                DDRF = 0x1F;
                break;
            case 0x20 :         //여섯번째 스위치가 눌려지면
                DDRF = 0x3F;
                break;
            case 0x40 :         //일곱번째 스위치가 눌려지면
                DDRF = 0x7F;
                break;
            case 0x80 :         //여덟번째 스위치가 눌려지면
                DDRF = 0xFF;
                break;
            default :         //아무것도 눌러지지 않으면
                DDRF = 0x00;
        }

    }
    return 0;
}
// 회로도를 보면 스위치는 안눌러진 상태에서 High 신호를 보내다가 스위치를 누르게 되면 Low신호가 입력된다. 그러므로 이 값을 보수를 취해서 각각을 비교하면 쉽게 어느 스위치가 눌렀는지 알 수 있다.
// PORTF가 0x00으로 초기화 되어 있고 DDRF방향설정으로 LED ON 개수를 조절 하고 있다.

위의 코드를 간략화한 코드
main.c
#include "port.h"

int main(void)
{
    unsigned char in = 0x01;
    unsigned char out = 0xFE;
    unsigned int count = 0;

    DDRC = 0x00;    //switch input
    PORTC = 0x00;

    DDRF = 0xFF;    //LED output
    PORTF = 0xFF;

    while(1)
    {
        if((~PINC) & (in << count))        // 어느 스위치가 눌러졌는지 확인
        {
            PORTF = out << count;    // LED ON
        }
        else                    // 아무 스위치도 눌러 지지 않았을 때
        {
            PORTF = 0xFF;
        }

        count++;
        if(count >= 8)
        {
            count = 0;
        }
    }
    return 0;
}
// 시프트연산자를 이용하여 처음 작성한 코드를 간단히 만들어 보았다. 처음 코드를 보면 스위치를 확인 할 때의 숫자들을 자세히 살펴보면 결국 0x01로 시작해서 왼쪽으로 1개씩 시프트 연산한 것과 같다. 이를 변수로 만들어서 if문 하나에서 전부 확인하고 있다.
// LED ON 또한 Low 신호에 반응하기 때문에 시프트연산을 이용하면 쉽게 출력을 만들어 낼 수 있다.

 

C언어

swtich 문
p3-9.c
#include <stdio.h>

int main()
{
    char ch;

    printf("Please enter a number between 0 to 9 : ");
    scanf("%c", &ch);

    switch(ch)
    {
            case '0' :
                    printf("Input Number is Zero.\n");
                    break;
            case '1' :
                    printf("Input Number is One.\n");
                    break;
            case '2' :
                    printf("Input Number is Two.\n");
                    break;
            case '3' :
                    printf("Input Number is Three.\n");
                    break;
            case '4' :
                    printf("Input Number is Four.\n");
                    break;
            case '5' :
                    printf("Input Number is Five.\n");
                    break;
            case '6' :
                    printf("Input Number is Six.\n");
                    break;
            case '7' :
                    printf("Input Number is Seven.\n");
                    break;
            case '8' :
                    printf("Input Number is Eight.\n");
                    break;
            case '9' :
                    printf("Input Number is Nine.\n");
                    break;
            default :
                    printf("wrong number.\n");
    }

    return 0;
}

// switch문은 ch 안의 변수와 매칭되는 case 문이 실행 된다. 조건이 맞아 떨어지는 case문부터 그 밑으로 쭉다 실행하기 때문에 break;는 필수적으로 사용해야 한다.

문자열 입출력 함수
p3-10.c
#include <stdio.h>

int main()
{
    char ch;

    printf("Please enter any character: ");
    ch = getchar();
    putchar(ch);
    printf(" is a letter you typed.\n");
    //fflush(stdin);
    __fpurge(stdin);
    printf("Please enter any character: ");
    scanf("%c", &ch);
    printf("%c is a letter you typed.\n", ch);

    return 0;
}

// fflush(stdin)은 입출력 버퍼에 남아 있는 내용을 강제로 쓰는 명령이다. 이로 인해 입출력 버퍼가 비워지는데 윈도우 시스템에서는 이 코드가 원하는데로 잘 동작하나 리눅스시스템에서는 우리가 원하는데로 버퍼가 비워지지 않았다. 리눅스에서는 __fpurge(stdin)으로 입력버퍼의 내용을 지울 수 있었다. 이 함수들을 사용하는 이유는 앞선 입력에서 입력버퍼를 사용하고 버퍼에 불필요한 쓰레기 값을 남기게 되는데 이 값이 다음 입력에 영향을 주기 때문에 입력 버퍼를 비워주어야만 다음 입력이 제대로 동작한다.
//getchar(), putchar()함수는 각각 하나의 문자를 입력, 출력 하는 함수 이다. getchar()함수는 키보드로부터 하나의 문자를 입력받아서 이를 리턴해주고, putchar()함수는 아스키값을 넘겨주면 이를 문자로 화면에 출력해준다.

반복문
반복문은 같은 명령문을 여러 번 반복해서 수행해야 될 경우 사용한다. 반복문을 사용할 때는 다음의 3가지 조건이 필요하다.
1. 초기조건 // 2. 반복되는 일 // 3. 종료조건
p3-12.c
#include <stdio.h>

int main()
{
    int num;

    num = 0;        // 1.초기조건
    while(num <= 5)    // 3.종료조건
    {
            printf("%d\t", num);    // 2.반복되는 일
            num = num + 1;
    }
    printf("\n");

    return 0;
}


// while(논리식) { 명령문 } while문은 논리식이 참(0이아닌값)일 동안 계속해서 명령문을 실행한다.

전자회로실험

LED 속도 변화
어제 실험에서는 주기적인 시간에 따라 LED를 On/Off시키는 코드를 작성해 보았다. 오늘은 이 LED On/Off를 시간을 동적으로 바꾸어 보는 코드를 작성했다.

port.h파일

#define PINA  (*(volatile unsigned int *)0x39)
#define DDRA  (*(volatile unsigned int *)0x3A)
#define PORTA  (*(volatile unsigned int *)0x3B)

#define PINB  (*(volatile unsigned int *)0x36)
#define DDRB  (*(volatile unsigned int *)0x37)
#define PORTB  (*(volatile unsigned int *)0x38)

#define PINC  (*(volatile unsigned int *)0x33)
#define DDRC  (*(volatile unsigned int *)0x34)
#define PORTC  (*(volatile unsigned int *)0x35)

#define PIND  (*(volatile unsigned int *)0x30)
#define DDRD  (*(volatile unsigned int *)0x31)
#define PORTD  (*(volatile unsigned int *)0x32)

#define PINE  (*(volatile unsigned int *)0x21)
#define DDRE  (*(volatile unsigned int *)0x22)
#define PORTE  (*(volatile unsigned int *)0x23)

#define PINF  (*(volatile unsigned int *)0x20)
#define DDRF  (*(volatile unsigned int *)0x61)
#define PORTF  (*(volatile unsigned int *)0x62)

main.c파일

#include "port.h"

int main(void)
{
  unsigned int us = 0;    // 10^-6
  unsigned int ms = 0;  // 10^-3
  int flag = 0;
  int t = 2000;  // 
시간 변화를 주기 위한 변수

  DDRC = 0xFF;

  while(1)
  {
    us++;
    if(us >= 1000)
    {
      ms++;
      us = 0;
    }
    if(ms >= t)
    {
      if(flag)
      {
        PORTC = 0x00;
        flag = 0;
      }
      else
      {
        PORTC = 0xFF;
        flag = 1;
      }
      ms = 0;
      t -= 100;
      if(t == 0)
      {
        t = 2000;
      }
    }
  }

  return 0;
}
//
변수 t 이용하여 LED on/off 마다 t값을 줄여줌으로써 on/off 주기가 점점 짧아진다. 변수 t 0 되면 다시 2000으로 초기화하고 다시 반복한다.

PINx
PINx 레지스터는 I/O핀이 입력으로 사용될 경우 핀에 입력된 데이터를 읽어 오는 레지스터이다. 이 PINx를 직접 사용해보았다.

main.c 파일

#include "port.h"

int main(void)
{
  DDRC = 0x01;  // C
 01번을 출력으로 설정
  PORTC = 0x01;  // C
 01번에 High 출력

  DDRD = 0x00;  // D
핀을 입력으로 설정
  PORTD = 0x00;  // D
 초기화

  PORTF = 0x00;  // F
 초기화

  while(1)
  {
    if(PIND & 0x01)  // D
 01번에 High신호가 들어오면
    {
      DDRF = 0xff;  // F
핀에 High 신호 출력
    }
    else
    {
      DDRF = 0x00;  // F
핀에 Low 신호 출력
    }

  }
  return 0;
}
// F
핀에는 LED 연결시켜놓고, C 01번을 D 01번으로 연결시키면 F에서 출력이 나오므로 LED 불이 들어오는 것을 있다.

switch누름에 따라 LED on개수변화
#include
 "port.h"

int main(void)
{
  DDRC = 0x00;  //switch input
  PORTC = 0x00;

  DDRF = 0xFF;  //LED output
  PORTF = 0xFF;

  while(1)
  {
    if((~PINC) & 0x01)    // 1
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x01);
    }
    else if((~PINC) & 0x02)  // 2
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x03);
    }
    else if((~PINC) & 0x04)  // 3
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x07);
    }
    else if((~PINC) & 0x08)  // 4
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x0F);
    }
    else if((~PINC) & 0x10)  // 5
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x1F);
    }
    else if((~PINC) & 0x20)  // 6
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x3F);
    }
    else if((~PINC) & 0x40)  // 7
 스위치가 눌러 졌을 
    {
      PORTF = ~(0x7F);
    }
    else if((~PINC) & 0x80)  // 8
 스위치가 눌러 졌을 
    {
      PORTF = ~(0xFF);
    }
    else
    {
      PORTF = 0xFF;
    }
  }
  return 0;
}
// switch
입력 받은핀 : PORTC // LED 출력핀 : PORTF
// switch
회로도를 보면 스위치가 눌려 졌을 Low 신호가 입력 된다. 그러므로 C 입력을 보수 연산을해서 어느 핀이 눌러 졌는가 확인하고, 이에 따라 LED On 개수를 정하고 F핀으로 출력 하였다.

 

C언어

Big Endian & Little Endian


정수 0x12345678가 실제 메모리에 저장 되는 방식에는 Big-Endian과 Little-Endian방식이 있다. Big-Endian의 의미는 정수의 끝부분 메모리 주소가 크다는 의미이고, Little-Endian은 정수의 끝부분의 메모리 주소가 작다는 의미다.

일반적인 인텔x86계열 cpu들은 Little-Endian방식을 채택하고 있는데, Little-Endian 방식은 메모리 첫 부분에 바로 정수의 끝자리가 오기 때문에 산술연산이 간단해지는 장점이 있다. 반면 끝부분이 앞에 오기 때문에 대수비교에 약점이 있다.

main.c파일

#include <stdio.h>

int main()
{
  int iNum = 0x12345678;
  int *iP = &iNum;

  printf("iNum address: %08x\n"&iNum);
  printf("iP   address: %08x\n"&iP);
  printf("iNum   value: %08x\n", iNum);

  *iP = 0x77777777;
  printf("iNum   value: %08x\n", iNum);

  *((int *)0x12ff7c) = 0x99;
  printf("iNum   value: %08x\n", iNum);
  
  /* little endian
 확인하는 코드 */
  *((char *)0x12ff7c) = 0x01;
  *((char *)0x12ff7d) = 0x02;
  *((char *)0x12ff7e) = 0x03;
  *((char *)0x12ff7f) = 0x04;
  printf("iNum   value: %08x\n", iNum);

  return 0;
}


//
인텔 x86계열 Little-Endian 확인하는 코드로 결과 제일 밑을 보면 확인할 있다

Volatile 키워드

c컴파일러는 불필요한 코드를 자동적으로 최적화 시켜준다. 하지만 특수 역할을 하는 레지스터를 엑세스 해야 되는 경우 최적화가 문제를 일으킨다. 이를 방지하기 위해서 volatile키워드를 사용한다.

전자회로실험

Atmega128

Atmega128 칩은 설정에 따라 변경 가능한 8비트 I/O포트 6개(Port A~F)와 5비트 I/O 포트 1개(Port G)를 가지고 있다. 즉 설정에 따라 출력으로 혹은 입력으로 사용할 수 있는 I/O 라인이 53개이다.

각 I/O 핀들은 DDR(Data Direction Register), PORT(Data Register), PIN(Port Input Pins Register)과 같은 레지스터에 의해 제어된다.

DDRx은 핀이 입력용으로 사용할지 출력용으로 사용할지와 같은 핀의 신호 방향을 결정하는 레지스터 이고, PORTx는 I/O핀이 출력용으로 사용될 경우에 출력할 데이터를 저장하는 레지스터이며, PINx은 I/O핀이 입력용으로 사용될 경우에 핀을 입력된 데이터를 저장하는 레지스터이다.

오늘 실험에서는 C핀을 출력으로 설정하고, LED를 제어하는 실험을 해보았다.
기본 컴파일러의 라이브러리를 이용하지 않고, 각 핀 주소를 #define을 사용하여 제어하였다.
LED를 일정 시간 단위로 불을 켰다 끄는 것을 반복하는 코드를 작성하였는데 이를 위해 딜레이를 주기위한 코드를 작성하였다.

port.h파일

#define DDRA  (*(volatile unsigned int *)0x3A)
#define PORTA  (*(volatile unsigned int *)0x3B)

#define DDRB  (*(volatile unsigned int *)0x37)
#define PORTB  (*(volatile unsigned int *)0x38)

#define DDRC  (*(volatile unsigned int *)0x34)
#define PORTC  (*(volatile unsigned int *)0x35)

#define DDRD  (*(volatile unsigned int *)0x31)
#define PORTD  (*(volatile unsigned int *)0x32)

#define DDRE  (*(volatile unsigned int *)0x22)
#define PORTE  (*(volatile unsigned int *)0x23)

#define DDRF  (*(volatile unsigned int *)0x61)
#define PORTF  (*(volatile unsigned int *)0x62)
//
기존 컴파일러에서 제공하는 헤더 파일을 이용하지 않고, atmega128 datasheet 나와 있는 핀별 주소를 직접 #define 했다.

main.c파일

#include "port.h"

int main(void)
{
  unsigned int us = 0;  // 10^-6
  unsigned int ms = 0;  // 10^-3
  int flag = 0;

  DDRC = 0xFF;

  while(1)
  {
    us++;
    if(us >= 1000)
    {
      ms++;
      us = 0;
    }
    if(ms >= 500)
    {
      if(flag)
      {
        PORTC = 0x00;
        flag = 0;
      }
      else
      {
        PORTC = 0xFF;
        flag = 1;
      }
      ms = 0;
    }
  }

  return 0;
}

// atmega128의 동작 클럭은 16MHz로 1초에 16000000번의 마이크로연산이 가능하다. 그러므로 일반적인 사람의 눈으로 LED 불을 on/off 시키는 것을 보기 위해서는 불을 켠 후 어느정도 딜레이를 주고 다시 불을 끄는 동작을 해야 한다. 이 딜레이를 덧셈 연산의 반복을 이용하여 구현한다.

 

c언어 실습

다중 if 문

#include <stdio.h>

int
main()
{
    int score;
    char grade = 'X';

    printf("Please enter examination score: ");
    scanf("%d", &score);

    if((score <= 100) && (score >= 90))
    {
            grade = 'A';
    }
    else if((score < 90) && (score >= 80))
    {
            grade = 'B';
    }
    else if((score < 80) && (score >= 70))
    {
            grade = 'C';
    }
    else if((score < 70) && (score >= 60))
    {
            grade = 'D';
    }
    else if((score < 60) && (score >= 0))
    {
            grade = 'F';
    }
    else
    {
            printf("wrong score\n");
    }
    printf("The grade is %c\n", grade);

    return 0;
}


포인터

포인터는 메모리의 주소를 의미하고, 포인터 변수는 이 메모리 주소들을 저장할 수 있는 변수를 뜻한다.

#include <stdio.h>

int main()
{
    int a = 100;    // int형 변수 a선언 & 100으로 초기화
    int *p;    // int형 포인터 변수 p 선언
    p = &a;    // 포인터 변수p에 a변수의 주소를 대입한다
    *p = 0x99;    // 포인터 변수p에 저장된 메모리 주소의 메모리에 0x99를 대입(즉 변수a에 0x99를 대입)
    *((int *)0x18FF38) = 0x77;    // 변수a의 메모리 주소를 int형 포인터 변수로 캐스팅 연산한 후, 이 주소의 메모리에 0x77을 대입

    printf("a value : %x\n", a);
    printf("a address(16) : %x\n", &a);
    printf("p address(16) : %x\n", p);

    return 0;
}


변수 a에 77이 대입된 걸 알 수 있다.
포인터 변수 p에 int형 변수a의 주소값이 저장 되어 있는 것을 알 수 있다.

+ Recent posts