ATmega162

AVR로 만들어서 이해하는 CPU

 

 

Atmega162 (DIP Type)

 

ATmega162의 핀 구조

 

 

프로그래밍의 설명

ATmega162 프로그램을 설계를 할 때 io.h 헤더파일을 include 하게 된다.

io.h 헤더파일 안에는 아래와 같은 부분이 있다.

Ex)

#elif defined (__AVR_ATmega162__)

# include <avr/iom162.h>

 

즉, 컴파일러에서 ATmega162에 맞게 컴파일 하도록 설정하면 iom162.h라는 또 다른 헤더파일을 include 하게 되어 있다.

그리고 그 iom162.h라는 헤더 파일 안에는 레지스터 이름들이 있는데 이 레지스터들은 아래와 같이 ATmega162의 메모리 주소에 맞게 연결 되어 있다.

Ex) #define PORTD _SER _IO8(0x12)
//PORTD 라는 변수를 번지수와 연결 시켜 놓았다.

(이제부터 PORTD를 조작하면 지정된 부분을 조작 할 수 있다.)

 

그러므로 iom162.h라는 헤더파일을 불러 옴으로써 직접 메모리 주소를 찾아가며 레지스터들을 설정하는 수고를 덜어주는 것이다.

 

이제 자주 사용하게 되는 몇 가지 레지스터 들을 살펴 보자

 

DDR(@) 레지스터

    @는 A, B, C, D 중 하나.

DDR(@) 는 PORT(@)의 입, 출력 모드를 결정한다.

8개의 포트를 8비트 데이터로 설정하며 1이면 출력 0이면 입력이다. (내림차순)

    Ex) DDRD = 0xFF
// PORTD의 모든 포트를 출력으로 사용

물론 2진수로 직접 입력 해줄 수도 있다.

    Ex) DDRD = 0b00111111
//PORTD7, PORTD6만 입력 모드 나머지는 출력 모드로 이용

 

PORT(@) 레지스터

PORT(@)에 해당하는 실질적인 신호를 다룬다.

 

TCCR0 레지스터

타이머 제어 레지스터로써 다름과 같은 구조로 되어 있고 역시 8비트로 셋팅한다.

FOC0

WGM0

COM1

COM0

WGM1

CS2

CS1

CS0

  1. FOC : Normal과 CTC모드에서 이 비트가 1로 셋팅 되는 순간 OC0에 출력값이 나오게 하는 것으로 그다지 많이 사용 되지 않는다.
  2. WGM0, WGM1 : 타이머의 동작 모드를 결정하는 비트로써 4가지 모드를 선택 한다.

WGM0

WGM1

MODE

0

0

Normal

1

0

PWM

0

1

CTC

1

1

Fast PWM

Normal 모드 : 0에서 255까지 도달하게 되면 인터럽트를 발생하고 다시 0으로 돌아가는 모드로 초기 값을 조절 하여 주기를 더 짧게 만들 수 있다.

 

CTC 모드 : 0에서 프로그래머가 셋팅해 둔 값에 도달하면 인터럽트를 발행 시키고 다시 0으로 돌아가는 모드

 

PWM 모드 : 디지털적으로 아날로그를 제어하는 방법 (전구의 밝기, 모터의 속도..)

 

  1. COM1, COM0 : OC0 핀의 기능을 정한다.

COM1

COM0

MODE

0

0

범용 I/O 포트로 동작(OC0 핀 차단)

0

1

OC0 핀 출력 토글

1

0

OC0 핀 출력을 0으로 클리어

1

1

OC0 핀 출력을 1로 설정

 

  1. CS0, CS1, CS2 : 클럭의 분주를 설정한다. CPU에 사용한 크리스탈의 주파수를 얼마만큼 분주 할 것인지 설정한다.

CS2

CS1

CS0

기능

0

0

0

클럭 입력 차단(타이머 사용 안함)

0

0

1

시스템클럭 / 1

0

1

0

시스템클럭 / 8

0

1

1

시스템클럭 / 64

1

0

0

시스템클럭 / 256

1

0

1

시스템클럭 / 1024

1

1

0

외부 입력 클럭 하강 엣지

1

1

1

외부 입력 클럭 상승 엣지

 

OCR0 레지스터

분주된 클럭이 몇 번 발생한 후 인터럽트를 발생하게 하는지를 설정하는 레지스터

Ex) OCR0 = 125;         // 클럭이 125번 발생하면 인터럽트를 발생한다.

(결국 분주의 기능을 가지고 있음)

 

TCNT0 레지스터

TCNT0 레지스터는 프로그램이 실행되는 동안 분주된 클럭의 주기로 0에서 부터 몇 번째 주기가 발행 했는지 나타낸다. 이 레지스터는 쓸 수도 있고 읽을 수도 있다. 값을 쓰면 주기를 더 빠르게 할 수 있다. 레지스터의 값은 인터럽트가 걸리면 자동으로 0으로 클리어 되고 다시 분주된 주기마다 한 개씩 값이 늘어난다.

 

TIMSK 레지스터

 

TOIE1

OCIE1A

OCIE1B

OCIE2

TICE1

TOIE2

TOIE0

OCIE0

 

TOIE0(Timer0 Overflow Interrupt Enable) 비트를 1로 셋팅하고 시스템 인터럽트를 인에이블 하면 Normal모드의 타이머 인터럽트가 작동.

OCIE0(Timer0 Output Compare Match Interrupt Enable) 비트를 1로 셋팅하고 시스템 인터럽트를 인에이블 하면 CTC모드의 타이머 인터럽트가 작동.

 

사용 함수

sei( ) : 인터럽트를 인에이블 시키는 함수

sli( ) : sei의 반대 되는 것으로 시스템 인터럽트를 디스에이블 시킨다.

SIGNAL( ) : 인터럽트가 발생 했을 때 이 함수로 점프한다.

SIG_OUTPUT_COMPARE0 : 내부, 외부 관계 없이 인터럽트가 발생하면 해당 번호에 따른 번지로 점프하게 된다.

 

 

AVR CPU로 FND(7segment)를 제어하기

 

모듈 하나에는 7개의 신호를 표현할 수 있는 led가 있다.

(FND507 모듈에는 dot가 없음)

 

Signal(sig_output_compare0)      //인터럽트에 반응하는 함수

{

If (timer_count == 125)    //125가 되면

{

     PORTD = count;    //PORTD 에 count값을 전달

     timer_count = 0;    //초기화 시킴(125까지 센다)

     count++;        //count를 증가 시킴

        if(count == 10)    //count는 10이 되면 초기화

        cout = 0;

}

timer_count++;            //인터럽트에 의해 카운트가 된다.

}

Int main( void )

{    timer_count = 0;        //전역 변수로 선언되었고 앞에서 카운트

    DDRD = 0xFF;        //PORTD를 출력 모드로 쓰겠다!

    PORTD = 0X00;        //8개의 핀 모드 0의 값을 출력 시키겠다

    cli( );            //시스템 인터럽트를 꺼놓고 아래의 셋팅

    TCCR0 = 0x0D;        //00001101

                //com1,com0=0,0범용 I/O포트 동작

                //WGM0,WGM1=0,1 타이머 모드 CTC

                //CS2,1,0 = 1,0,1 – 시스템 클럭 1024 분주

    OCR0 = 125;         //출력 비교 레지스터 (클럭 125번 발생후 인터럽트)

    TCNT0 = 0x00;        //0으로 리셋 해둔다.

    TIMSK = 0x01;        //CTC모드의 타이머 인터럽트를 작동 시킨다.

    sei( );            //인터럽트를 인에이블 시킨다.

    while(1);

    return 1;

}

시스템 클럭 16Mhz — 1/16M x 1024 x 125 x 125 = 1s

    count 변수는 1초마다 10까지 카운트 되고 PORTD에 값을 전한다.

    
 

 

 

 

입력 회로의 제작과 실험

PORTD 를 이용해 입력을 받는 실험을 한다.

  • PORTD 0~5까지는 출력 모드로 하고 6,7은 입력 모드로 한다.
  • 6,7 이 1일때 출력을 보내(스위치) led를 점등 한다.
  • Unsigned char switch_input = 0;

 

Int main(void){

    DDRD = 0b00111111;        //PORTD 를 7,6은 입력 모드, 나머지는 출력

    while (1)        

        {

        switch_input = PIND;    //입력 받는 변수

        If (switch_input == 0b11000000)    //7,6이 1이면

            PORTD = 1;    //출력 LED점등

        else        

            PORTD = 0;    //출력 안함

        }

            return 1;

    }

 

 

 

RS232 시리얼 통신

 

RS232의 통신 방식

 

 

    다음과 같이 설정한다.

 

보레이트 9600 데이터 비트는 8비트 패리티 비트 사용 안 함 STOP비트는 1로 설정한 모습

 

 

MAX 232

PC와 마이컴 시리얼 통신에는 MAX232 라는 IC가 필요 하다.

마이컴과 컴퓨터가 인식하는 0과 1의 값이 다르기 때문에..

 

마이컴

컴퓨터

0

0V

-10V

1

5V

10V

이 차이를 해결해주는 것이다.

IC 내부에는 VOLTAGE DOUBLER와 VOLTAGE INVERTER가 있다.

DOUBLER는 5V를 10V로 증폭 INVERTER는 증폭된 신호를 반전

내부에 5V를 충전했다 10V로 보내기 위한 콘덴서가 있다.

MAX232는 전압을 바꾸어 주는 역할만을 하며 통신을 하는것은 아님에 주의!

 

 

시리얼 통신을 위한 레지스터

 

ATmega162에는 시리얼 통신용 컨트롤러가 포함 되어 있다.

CPU 내부에서 CPU의 코어 부분과 시리얼 통신용 컨트롤러가 데이터를 주고 받고, 컨트롤러는 RxD, TxD의 핀을 통해 외부와 통신을 하고 있다.

시리얼 통신을 위한 레지스터에는 설정용 레지스터와 데이터 통신용 레지스터가 있고 설정용 레지스터를 이용해 설정을 하고 통신용 레지스터를 이용해 실제 통신을 한다

 

UCSR 레지스터

UCSR은 A,B,C세 개로 구성 되어 있고, 상태 설정과 제어의 기능을 한다.

 

UCSRA

7

6

5

4

3

2

1

0

RXC

TXC

UDRE

FE

DOR

PE

U2X

MPCM

7. RXC : 수신 완료 표시 비트

6. TXC : 송신 완료 표시 비트

5. UDRE : 송신 데이터 레지스터 준비 완료 표시 비트

4. FE : 프레임 에러 표시 비트

3. DOR : 데이터 오버런 에러 표시 비트

2. PE : 패리티 에러 표시 비트

1. USX : 송신 속도 2배 설정

0. MPCM : 멀티 프로세서 통신 모드

    *1번과 0번을 설정하고 나머지는 읽기 전용이다. (사용 할 때 1 아닐 때 0)

 

UCSRB

7

6

5

4

3

2

1

0

RXCIE

TXCIE

UDRIE

RXEN

TXEN

UCSZ2

RXB8

TXB8

7. RXCIE 수신 완료 인터럽트 인에이블 비트

6. TXCIE 송신 완료 인터럽트 인에이블 비트

5. UDRIE 송신 데이터 레지스터 준비 완료 인터럽트 인에이블 비트

4. RXEN 수신기 인에이블

3. TXEN 송신기 인에이블

2. UCSZ2 전송 데이터 길이 선택 비트

1. RXB8 수신 데이터 비트 8

0. TXB8 송신 데이터 비트 8

 

UCSRC

7

6

5

4

3

2

1

0

URSEL

UMSEL

UPM1

UPM0

USBS

UCSZ1

UCSZ0

UCPOL

7. UREL 레지스터 선택 비트

6. UMSEL 시리얼 통신 모드 설정 비트

5. UPM1 패리티 모드 설정 비트

4. UPM0 패리티 모드 설정 비트

3. USBS 정지 비트 선택

2. UCSZ1 전송 데이터 비트 수 설정

1. UCSZ0 전송 데이터 비트 수 설정

0. UCPOL 클럭 극성 선택

 

보레이트 속도 설정 관련 레지스터

 

보레이트 – 신호 하나의 속도

보레이트 속도를 조절 하는 레지스터는 UBRRH와 UBRRL 2개로 이루어져 있다. UBRRH의 4비트와 UBRRL의 8비트를 합친 12비트를 이용하여 속도를 설정한다.

 

UBBRH

7

6

5

4

3

2

1

0

URSEL

사용하지 않음 ( 0 )

UBRR의 값 (11 ~8 비트)

 

UBBRL

7

6

5

4

3

2

1

0

UBRR의 값 (7 ~0 비트)

 

UBRRH는 UCSRC레지스터와 같은 번지를 사용 하고 있다. 그렇기 때문에 UCSRC로 사용 할 것인지 UBRRH로 사용 할 것인지 선택을 해야 하는데, 이 기능을 하는 것이 7번 URSEL비트이다.

URSEL이 0일 때 UBRRH의 기능을 갖고 1일 때 UCSRC의 기능을 갖는다.

 

UBRR의 값은 보레이트와 시스템 클럭을 이용하여 계산 한다.

예를 들어 시스템 클럭이 16Mhz 이고 보레이트를 9600 bps로 잡았을 때

 

UBRR의 값은 103.1666…. 이 된다.

여기서 소수점 이하를 버리고 103을 2진수로 변환하면 1100111이 된다.

결국 UBRRH의 4 비트에는 모두 0이 들어가고 UBRRL에는 01100111이 들어가므로, 다음과 같이 입력 한다.

    UBRRH=0;

    UBRRH=103;    //물론 0b01100111으로도 입력 가능하다.

 

데이터 전송 레지스터

 

UDR

UDR레지스터는 데이터를 입력하면 설정한 값에 맞게 데이터를 전송하는 레지스터이다.

 

시리얼 통신 회로의 제작

  • 케이블 제작
  • CPU에 ‘Hello’라는 문자를 입력 하고 컴퓨터에서 수신

시리얼 케이블 커넥터

2번 과 3번이 Rx(수신), Tx(송신) 이다.

그러므로 케이블을 만들 때에는

반대쪽 커넥터에 연결하기 전에 한번 교차 시켜야 한다.

(Rx ->Tx , Tx->Rx)

 

시리얼 통신 프로그램

 

송신 프로그램

 

#include <avr/io.h>

 

Void Putch(char data) //글자를 보내는 부분

{    while(!(UCSR0A & 0x20));

/*0x20 = 00010000 5번 비트(UDRE) 가 1이 되기를 기다린다.

    UDR0 = data;        // 송신 레지스터, 전송 후 비게 되면 UDRE에 1를 전송

}

Int main(void)

{ char string[] = “Hellorn”;

char *pstr;

UCSR0A = 0x00;    //USX(송신 속도 2배) OFF MPCM(멀티프로세서 통신모드)OFF

UCSR0B = 0b00001000;    //TXEN(송신모드)만 ON

UBRR0L = 103;    //UBRR(보레이트 속도 조절)값 103

UBRR0H = 0;

pStr = string;

while (*pStr)     //pStr이 0이 아닌 동안

Putch(*pStr++);    //다음 문자를 가리킴

while(1);

}

 

레지스터 변수를 보면 뒤쪽에 숫자가 붙어 있다. 이것은 1번 시리얼 컨트롤러(PORTA, 핀 3, 4)를 사용할 것인지 0번 시리얼 컨트롤러(PORTD, 핀 10, 11)를 사용할 것인지 판단 하는 것 이다. (1페이지의 핀 구성을 참고 하자)

 

 

송수신 프로그램

 

#include <avr/io.h>

void Putch(char data)    //문자 송신 함수

{

    while(!(UCSR0A & 0x20));    //UDRE가 1일때

    UDR0 = data;        //UDR에 데이터를 넣어 전송

}

 

char Getch(void)        //문자 수신 함수

{

    while(!(UCSR0A & 0x80));    //RXC(수신완료 표시 비트)가 1일때

    return UDR0;        //UDR의 값을 리턴한다.

}

 

int main(void)

{

    char string[] = “Hellorn”;

    char *Pstr;

    

    UCSR0A = 0x00;

    UCSR0B = 0b00011000;    //TXEN, RXEN ON (송,수신 둘 다 사용)

    UBRR0L = 103;

    UBRR0H = 0;

 

    pStr = string;

    while(*pStr)    //pStr이 0이 아닌동안 다음 문자를 가리키고 0이면 넘어감

        Putch(*pstr++);

 

    while(1)        //무한 루프 시작

        Putch(Getch( ) + 1);    //Getch( )의 문자에 1을 더해서 Putch로 보냄

}

 

 

while(1)은 무한 루프를 의미한다. while(true) 일 때 아래의 포함된 명령이 실행 되고 false일때 나올수 있으므로 (true = 1 false = 0) 직접 1을 넣으면 전원을 차단 할 때까지 명령을 반복 한다.

 

 

인터럽트를 사용한 송수신 프로그램

 

#include<avr/io.h>

#include<avr/interrupt.h>

#include<avr/signal.h>

 

char rx = 0;    //rx라는 문자 변수 선언

 

void Putch (char data)     //문자 송신 함수

{

    while(!(UCSR0A & 0x20));

    UDR0 = data;

}

 

SIGNAL(SIG_USART0_RECV)    //데이터를 받으면 하던 일을 중단하고 이 함수를 실행한다.

{

    char string[ ] = “rn메세지 잘 받았습니다. –> “;    //문자 배열

    char *pStr;

 

    rx = UDR0;

 

    pStr = string;

    while(*pStr) Putch(*pStr++);     //string 의 내용을 송신

 

    Putch(rx);    //UDR0의 내용 송신

    Putch(‘r’);

    Putch(‘n’);

}

 

int main (void)

{

    char string[ ] = “rn안녕하세요rn”;

    char *pStr;

 

    DDRB = 0xFF;    //??

    PORTB = 0xFF;    //??

    

    UCSR0A = 0x00;

    UCSR0B = 0b10011000; //RXCIE(수신 완료 인터럽트 인에이블 비트)사용, 송수신사용

    UBRR0L = 103;

    UBRR0H = 0;

 

    sei( );     //인터럽트를 인에이블 시킨다.

    pStr = string;

    while (*pStr) Putch(*pStr++);    //”안녕하세요” 전송

 

    while(1);        //무한 루프

}

 

먼저 안녕하세요 문자 출력 후 무한 루프로 들어 가게 된다. 데이터를 받으면 그때 무한루프를 벗어나 SIGNAL(SIG_USART0_RECV)    함수가 작동하고 함수가 끝이 나면 다시 무한 루프로 들어가 다음 신호를 기다리는 상태가 된다. while( )문의 자리에 CPU가 다른 일을 할 수 있도록 프로그램을 작성할 수도 있다

hk71.pdf

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url=""> 

This site uses Akismet to reduce spam. Learn how your comment data is processed.