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의 주소값이 저장 되어 있는 것을 알 수 있다.

전자회로실험

DK128


dk128 키트의 구성도. 기본적으로 2개의 보드로 이루어져 있는데, 한쪽엔 핵심칩인 Atmega128 칩이 장착되어 있고 한쪽엔 여러가지 IO들이 장착되어 있다.

Atmega128은 8비트 마이크로 컨트롤러로 Harvard architecture로 설계되어 있는 칩이다.
여기서 마이크로컨트롤러란 CPU코어 하나가 아니라 CPU와 메모리 및 주변 하드웨어 제어회로 모두를 뜻한다. 보통 MCU(Micro-controller Unix)이라 하며 1개의 칩으로 컴퓨터의 구현이 가능하다. Harvard architecture은 일반적인 컴퓨터 구조인 폰노이만 구조와 달리 프로그램 메모리와 데이터 메모리가 따로 분리되어 있는 구조이다. 즉, 어드레스 버스와 데이터 버스가 독립적으로 분리되어 있는데 이는 주소 접근과 데이터 접근을 동시에 할 수 있어서 CPU의 처리속도가 빠르다.

오늘은 DK128에 간단한 프로그램을 올려보는 과정을 실습하였다. 이 과정을 간단히 요약해보면 먼저 키트에 올릴 c소스코드를 작성하고 Atmega128용 컴파일러를 이용하여 컴파일한다. 컴파일 되서 나온 결과물을 Parallel port를 이용하여 Program flash에 write하면 이 과정이 끝나는 것이다.

1. 컴파일러 설치 : WinAVR이라는 Atmega128용 컴파일러를 설치 하였다. 이 자료는 http://winavr.sourceforge.net 에서 구할 수 있다.

2. Makefile 생성 : WinAVR을 설치하면 mfile이라는 프로그램이 같이 설치 된다. mfile을 이용하여 Atmega128에 맞는 Makefile를 생성한다.
- MCU type을 atmega128로 선택
- Enable Editing of Makefile 선택
- F_CPU 항목을 80000000 에서 16000000로 변경
- File > Save As로 Makefile을 저장)

3. 프로그램 작성 : Programmers Notepad 프로그램을 이용해서 프로그램을 작성한다.
- File > New로 Project를 생성
- 생성된 Project에 소스파일(ex.main.c)를 추가
- 생성된 소스파일이 존재하는 디렉토리에 2번과정에서 생성한 Makefile을 복사
- Tool > Make All 을 이용해 컴파일하면 .hex확장자 파일이 생성된다

4. PonyProg2000 설치 : 컴파일 된 .hex 파일을 키트에 올리기 위해서 설치한다 . 이 자료는 http://www.lancos.com에서 구할 수 있다.

5. 키트에 작성한 프로그램을 쓰기 : PonyProg2000을 이용하여 작성한 프로그램을 키트에 쓴다.
- Device > AVR micro에서 ATmega128선택
- Setup > Calibration > OK
- Setup > Interface Setup에서 Parallel // Avr ISP I/O // LPT1 을 선택
- Probe버튼을 눌려서 연결을 테스트 해본다.
- File > Open Program(Flash)File을 선택해서 생성한 .hex파일을 불러온다.
- Command > write program 을 선택하면 프로그램 Flash로 작성한 프로그램을 기록한다


오늘 직접 올려본 프로그램


실제 사용된 핀. C핀에 스위치 입력을 연결하고, F핀에 LED 출력을 연결했다.


실험결과 각 스위치들를 누르면 거기에 대응하는 LED에 불이 들어왔다.

 

 

vmware-tool 설치 & vi 사용법

오늘 오후 시간에는 저번 시간에 vmware에 우분투 리눅스를 설치한 곳에 vmware-tool을 설치하였다. 이 tool을 설치한 이유는 vmware상의 가상 virtual machine과 실제 Host 컴퓨터간의 데이터를 쉽게 이동시키기 위해서다. 이 머신 사이에 공유하는 폴더를 만듬으로써 쉽게 데이터를 이동시킬 수 있었다.

 

vi는 Visual editer의 약자로 리눅스 운영체제에서 보편적으로 가장 많이 사용하는 텍스트 편집기이다. vi는 일반 윈도우에서 사용하던 nodepad 같은 프로그램과 그 사용법이 완전히 다르기 때문에 vi를 처음부터 바로 사용하기는 어렵다.

vi는 세 가지 모드가 존재한다.
- 입력모드 : 문서를 편집하기 위한 모드
- 명령모드 : 커서 이동, 삭제, 복사, 붙이기 등의 작업이 가능
- ex 모드 : 라인 에디터인 ex 에디터 기능을 사용하는 모드

ex 모드 ->(esc키) 명령모드 ->(i, a, o, s) 입력모드
입력모드 ->(esc키) 명령모드 ->(:, /, ?) ex모드

vi에는 사용 가능한 무수히 많은 명령어들이 있는데, 최대한 키보드 자판에서 손을 놓지 않도록 설계되어 있기 때문에 익숙해지면 굉장히 강력한 편집기가 될 수 있다.

위의 표를 보면 vi의 명령어에 대해 자세히 나와 있다. 이 명령어들은 단독으로만 사용하는 것이 아니라 여러 조합으로 같이 사용할 수도 있다. 에를 들어 d(삭제)w(word)를 누르면 한 단어가 지워지고, d(삭제)G(파일 끝 줄로 이동)를 누르면 현재 위치에서 파일 끝까지의 내용을 지운다.

전자회로실험

Flip-Flop

Flip-Flop은 순차논리회로로 이전 정보를 기억하는 메모리 소자이다. 주로 컴퓨터의 레지스터에 사용된다. Flip-Flop의 종류에는 RS Flip-Flop, D Flip-Flop, T Flip-Flop, JK Flip-Flop이 있다

- RS Flip-Flop : R이 1이면 리셋, S가 1이면 셋

- D Flip-Flop : 입력을 그대로 출력

- T Flip-Flop : 입력이 들어올 때마다 출력의 상태가 바뀜

- JK Flip-Flop : RS Flip-Flop에 기능을 추가 JK 모두 1일 때 반전

 

오늘 실험에 사용한 칩은 74HC74N으로 Dual D-type Flip-Flop이다. 다음은 74HC74N의 데이터시트이다.


각 핀의 배열과 역할에 관한 데이터시트이다.


각 입력에 대한 출력 진리표이다. Table1을 보면 SD와 RD신호가 각각 LOW 일 때 출력이 셋, 리셋됨을 알 수 있다. 즉 low신호에 각각 셋, 리셋된다. 또한 위의 표에는 없지만 SD와RD에 동시에 High 신호를 입력해 보면 Q에서High신호가 출력되었다.


위 그림은 입력 신호 D가 인가 될 때, 어떤 타이밍에 Q신호가 출력이 되는지 잘 보여주고 있다. Flip-Flop은 기본적으로 클럭에 동기화 되어 동작하기 때문에 입력신호가 들어왔다고 해서 바로 출력이 나오는 것이 아니라 클럭에 맞춰서 출력이 나온다. 위의 칩은 positive-edge trigger이기때문에 클럭의 rising edge에서 출력값이 변하고 있다.

 

이번 실험은 D Flip-Flop의 동작을 직접 눈으로 확인하는 것이였다. 하지만 실험에서는 정확한 D Flip-Flop의 동작을 볼 수 없었다. Flip-Flop은 이진 데이터를 기억하는 소자이다. 실험에서 D에 Low값을 주고, 클럭핀에 신호를 한 번 주면 rising edge에서 출력 LED의 불이 꺼졌다가 falling edge를 만나면 바로 다시 불이 켜져버리는 것이였다. 이는 계속 Low값을 저장되어야 하는데 그렇지 못하다는 것을 뜻한다. 정확한 이유는 알 수 없었으나, 강사님 말씀으로는 정확한 클럭을 인가한게 아니라 임의로 우리가 스위치로 클럭을 만들어서 그럴것이라는 것이였다.

 

c언어

부가연산자 & 복합부가연산자

x = x + 5; 와 x += 5는 같은 의미를 가진다. 전자는 부가연산자(=)를 이용한 것이고, 후자는 복합부가연산자(+=)를 이용한 것이다.

예제소스

#include <stdio.h>

 

int main()

{

        int num = 17;

        printf("num += 5: %d\n", num += 5);

        printf("num -= 5: %d\n", num -= 5);

        printf("num *= 5: %d\n", num *= 5);

        printf("num /= 5: %d\n", num /= 5);

        printf("num %= 5: %d\n", num %= 5);

 

        return 0;

}


 

캐스트 연산자

산술연산이나 대입연산 등을 할 때 두 피연산자는 자료형이 같아야만 한다. 서로 다른 자료형을 산술연산이나 대입연산 등을 해야 되는 경우 캐스트 연산자를 이용하여 명시적으로 두 자료형을 맞춰줄 수 있다.

(자료형)수식

예제소스

#include <stdio.h>

 

int main()

{

        int intA = 14;

        int intB = 3;

        float floatC = 0;

 

        floatC = intA / intB;

        printf("%f\n", floatC);

 

        floatC = (float)intA / intB;

        printf("%f\n", floatC);

 

        return 0;

}


 

변수선언과 초기화

변수를 선언하면 cpu는 변수의 자료형에 맞는 메모리 공간을 확보한다. 이 메모리 공간에는 예전에 사용하던 아무 의미 없는 값들로 채워져 있는데 이를 쓰레기 값이라고 한다. 변수를 초기화 하지 않고 사용하게 되면 이 쓰레기 값들을 볼 수 있다.

예제소스

#include <stdio.h>

 

int main()

{

        int num1, num2;

 

        num2 = num1 + 10;      // num2를초기화하지않고바로사용

        printf("num1 : %d\nnum2 : %d\n", num1, num2);

 

        return 0;

}


 

if문

if문은 아래의 형식으로 사용한다. ~~~ 부분이 참이면(0이아니면) { }안의 명령문들을 실행하고, 거짓이면 { }안의 명령문들은 무시한다.

if(~~~)
{
}

예제소스

#include <stdio.h>

 

int main()

{

        int num1, num2, bigger;

 

        printf("Please enter two integer: ");

        scanf("%d %d", &num1, &num2);

 

        bigger = num1;

 

        if(num1 < num2)

        {

               bigger = num2; // 결국두수중큰수가bigger저장된다

        }

        printf("The larger number is : %3d\n", bigger);

 

        return 0;

}



 

  전자회로실습

 FND500 7-Segment LED Displays
 FND500 Datasheet

FND500은 7-Segment LED Displays로 Cathode Common 구조이다. 즉 common핀(3번or 8번핀)에 GND를 연결하고, 불을 켜고 싶은 곳의 전압을 주면 제대로 동작한다.


기본적으로 내부는 발광다이오드로 구성되어 있다. 다이오드는 한쪽 방향으로만 전류를 흐르게 해주는 특성이 있기때문에 구조에 따라 common핀에 어떤 극성을 연결시켜줘야 되는지 결정된다.


 74HC405 8-channel analog multiplexer/demultiplexer

74HC405 핀 배열 및 역할

입력에 따른 출력값
74HC405 멀티플렉서를 이용하여 입력신호에 따라 위의 FND500에 하나씩 신호가 들어오게 해보았다. 기본적으로 위의 표되로 제대로 된 출력을 보여주었으나, 다른 LED에도 약하게 불이 들어오는 현상이 있었다. 처음에는 출력의 Z의 의미를 몰라서 잠깐 혼란스러웠는데, 입력에 따라 출력 Y로 Z에 인가된 전압이 출력되는 것이었다. 이것이 멀티플렉스 동작이다.

반대로 디멀티플렉서 동작은 출력되는 Y쪽에 전압을 인가하면 역으로 Z로 Y에 인가된 전압이 나오는 것이다. 이를 이용하여 디멀티플렉서로 이용한다.


   C언어

 문자열은 하나 이상의 연속된 문자를 말하며, 큰따움표로써 표시한다. c컴파일러는 문자열 맨 마지막에 항상 '\0'(NULL문자)문자를 자동으로 넣어준다.
ex) "This is a string."

 c언어에는 많은 연산자가 있으며, 각각 우선순위가 정해져 있다.
ex) 산술연산자 : +, -, *, /, %
      비교연산자 : <, <=, >, >=, ==, != (비교연산자는 논리값인 참(1), 거짓(0)을 돌려주는 논리식을 만든다)
      논리연산자 : &&, ||, !
      증감연산자 : ++, -- (전치형, 후치형이 있으며 동작에는 미세한 차이가 있다)

 문자와 해당 아스키 코드를 출력하는 프로그램


A와 A의 아스키코드값 65가 출력으로 나왔다.

 8,10,16진수를 이용한 문자 출력 & escape sequence를 이용한 문자 출력



 문자열을 화면에 출력하는 프로그램


 비교 연산자를 사용하는 프로그램의 예

논리가 참이면 1 거짓이면 0으로 출력되고 있다.

 미성년자를 구분하는 프로그램

13세이상 19세미만이면 참이므로 1이 나온다. 그외는 거짓으로 0이 출력됨

 증감 연산자의 전치형과 후치형을 비교하는 프로그램

위의 결과를 보면 전치형과 후치형의 차이를 알 수 있다. 일단 전치형을 보면 증감연산부터 일어나고 그 다음 대입연산이 일어남을 알 수 있다. 반대로 후치형은 대입연산부터 일어나고 증감연산이 일어난다.
   전자회로실습

  사용한 TTL IC 칩
74HC08 2-INPUT-AND-GATE

74HC00 2-INPUT-NAND-GATE

  오늘 구성한 회로
오늘 구성해본 회로다. NAND 게이트를 이용하여 NOT를 구현한 회로다.
위 회로의 진리표

실제 회로 구성한 사진


   c언어 실습

  키보드로부터 숫자를 입력받아 화면에 출력하는 프로그램

  여기서는 scanf()함수의 사용법을 알아보았다. scanf()함수는 표준출력함수로 위 소스에서는 키보드로부터 정수를 입력받아서 name변수에 대입한다.

  자료형의 크기를 화면에 출력하는 프로그램

sizof() 연산자를 이용하여 자료형들의 크기를 알아보았다. sizeof()연산자는 자료형, 변수, 수식에 모두 사용가능하다.

 
 정수형 상수를 화면에 출력하는 프로그램



 실수를 화면에 출력하는 프로그램

실수를 printf()함수의 옵션에 따라 여러가지 형태로 출력한다.


 아스키코드를 확인하는 프로그램

컴퓨터는 내부적으로 이진수만 처리한다. 인간이 사용하는 문자를 컴퓨터에서 사용하기 위해서 각 문자마다 이진수로 정해놓고 이를 활용하는데, 이를 확인하는 코드(아스키 코드)를 작성해 보았다.





   논리식(부울대수)

1. 부울 대수의 개념

 ① 하나의 명제가 참 또는 거짓인가를 판단하 는데 이용되는 수학적인 방법 (수학자 불(G.Boole)에 의해 개발)

 ② 이진변수논리 동작을 취급하는 대수

 ③ 부울 대수를 이용하여 기본적인 연산은 NOT연산, AND연산, OR연산 등이 있음

 

2. 부울 대수의 목적

 ① 변수 사이의 진리표 관계를 대수 형식으로 표시

 ② 논리도의 입출력 관계를 대수 형식으로 표시

 ③ 같은 기능을 가진 더 간단한 회로를 발견할 수 있음

 

3. 부울 대수의 기본 법칙

 


 ⑴ 교환법칙

  ① A + B = B + A

  ② A · B = B · A

 

 ⑶ 배분법칙

   ① A · (B + C) = A · B + A · C

   ② A + (B · C) = (A + B) · (A + C)

 

 ⑸ 누승법칙

   ① A + A = A

   ② A · A = A

 

 ⑺ 항등법칙

   ① A + 0 = A     ② A + 1 = 1

   ③ A · 1 = A      ④ A · 0 = 0

 

 ⑼ 드모르간(De-Morgan)법칙

   ① (A + B)' = A' · B'

   ② (A · B)' = A' + B'


 ⑵ 결합법칙

   ① (A + B) + C = A + (B + C)

   ② (A · B) · C = A · (B · C)

 

 ⑷ 부정의 법칙

   ① (A')' = A

 

 

 ⑹ 흡수법칙

   ① A + A · B = (A + A) · (1 + B) = A · 1 = A

   ② A · (A + B) = (A · A) + (A · B) = A + (A · B)

                       = (A + A) · (1 + B) = A · 1 = A

 

 ⑻ 보간법칙

   ① A' + A = 1

   ② A' · A = 0


위의 논리식을 이용하여 복잡한 회로를 간단하게 만들수 있다. 하지만 너무  복잡한 수식은 계산하기 어려운데 이는 카르노맵을 이용하면 쉽게 간략화 할 수 있다.


   트랜지스터

 오늘 실습에서는 C3202를 이용하여 트랜지스터의 원리 및 사용법들을 알아보았다.

오늘 사용한 C3202의 데이터시트 이다.(NPN 트랜지스터)

NPN 트랜지스터의 구조와 기호

 트랜지스터에는 에미터 베이스 콜렉터가 존재한다. 기본적으로 콜렉터를 출력으로 사용하고 에미터 베이스는 npn pnp에 따라 역할이 달라지는데, 오늘 사용한 npn 트랜지스터는 에미터가 스위치 역할을 하고 베이스로 입력되는 전압을 인가하면 된다.

 즉, 정리를 해보면 이렇다.
 NPN 트랜지스터
- 에미터 : 전압을 인가하면 콜렉터로 출력이 나온다.(스위치역할)
- 베이스 : 입력
- 콜렉터 : 출력 (에미터에 인가된 전압 + 베이스에 인가된 전압 : 결론적으로 증폭된 전압이 출력)


   컴퓨터에서의 수의 표현(음수)

 일반적으로 컴퓨터 시스템에서의 수의체계는 '1','0'으로 이루어져 있는 2진수 체계이다. 그럼 2진수 체계에서 음수는 어떻게 표현될까? 현실세계에서는 숫자 앞에 '-'기호를 이용하여 음수를 표현하는데 컴퓨터를 저런 기호를 알지 못한다.

- magnitude 방식
아주 간단한 방식으로 제일 왼쪽비트 한 비트를 부호비트로 생각해서 0이면 양수, 1이면 음수를 나타낸다. 하지만 덧셈이 제대로 이루어 지지 않는다는 약점이 있어 컴퓨터 시스템에서 사용하지 않는다.

- 2의 보수
양수 값의 1의 보수를 구한 뒤 1을 더한 값으로 음수를 표현하는 방식으로 컴퓨터에서 실제로 사용하는 방식이다. 사칙연산이 잘 적용 된다.


수의 체계(2의보수)를 알아 보기 위한 c소스코드
결과출력 255가 -1로 254가 -2로 표현되는 것을 알 수 있다.




  코일(인덕터)

 교류를 충전/방전하는 소자. 교류성분을 제한한다. 단위 면적당 감은 횟수가 많아 질수록 용량은 커진다.

초크 코일

 
코일 기호도

  RELAY(PA1A-5V)

 코일을 이용한 스위치... 전자석 원리를 이용하여 코일에 전류를 흘려줬을 때, 딸칵 소리와 함께 접지하여 전류를 흐리게 해준다.
RELAY의 데이터 시트 중 핀의 역할을 나타내는 그림이다. Coil 부분에 전류를 흘러 주게 되면 딸칵 소리와 함께 접지가 된다. 그리되면 COM 입력이 N.O 출력으로 나오게 된다. 이를 직접 브레드보드판에서 LED를 이용하여 확인해 보았다.


  논리 게이트



  c언어 컴파일 과정

위 그림은 일반적인 c언어의 컴파일 과정을 대략적으로 보여준다. 보통 위의 like.i like.s 파일들은 컴파일 과정에서 보이지 않는데 이는 컴파일러가 필요 없다고 생각해서 알아서 지운 것이다. 이 파일들을 보고 싶으면 컴파일러에 따로 옵션을 줘서 지우지 않게 하면 된다.

* 리눅스에서 사용하는 gcc에서 저 파일들을 보려면 주어야 할 옵션
     gcc -save-temps -o like like.c
* MS사의 cl 컴파일러에서 저 파일들을 보려면 주어야 할 옵션
     cl /P like.c     : like.i 파일 생성
     cl /Fa like.c    : like.asm 파일 생성


  c언어 변수

 기본적으로 컴퓨터는 프로그램을 메모리상에 두고 이를 cpu로 가져와서 실행하게 된다. c언어에서 변수선언의 의미는 이 메모리 공간을 확보하는 것이다. 변수의 자료형(타입)에 따라서 얼마의 공간을 확보하는지 결정되며, 변수를 선언하면 내부적으로 심볼 테이블에 변수의 자료형, 변수명, 해당주소를 저장한다.

 실습한 코드
////////////////////////////////////////////////////////////////////////////////////
#include <stdio.h>

int main()
{
  char test;  // char형 변수 선언(1byte메모리할당)
  test = 'a';  // 변수 test에 문자'a' 대입

  printf("The Character is %c.\n", test);  // 변수 test 출력
  return 0;
}

////////////////////////////////////////////////////////////////////////////////////
 결과 출력

+ Recent posts