2007. 2. 27. 18:32

대부분의 경우 외부 크리스탈로 내부 클럭으로 사용하도록 설정해서 사용하거나, EMI같은 원인으로 퓨즈비트 세팅이 바뀌어 장치인식을 못하게 되는 경우입니다.

이 경우 강제로 외부 클럭(8MHz 전후)을 넣어주어 살릴 수가 있습니다. (일명 인공호흡)
발진소자(크리스탈)을 제거 후 오실레이터의 신호를 AVR칩의 XTAL1에 강제로 넣어주고 AVR칩에 전원을 넣으면 동작이 될 겁니다.

다시 퓨즈비트를 사용하고자 하는 것으로 write 하고 다시 읽어 확인한 후 전원을 끄고 원래 크리스탈을 꼽아서 사용하시면 됩니다.

Posted by Paul Hwang
2007. 2. 27. 18:15

아날로그 디지털 변환기(A/D 컨버터)는 센서와 같은 소자에서 들어오는 아날로그 값을 디지털 값으로 변경하는 기능을 가지고 있으며, 실제 제어기를 구성할 때 없어서는 안될 소자이다.
Atmega128의 경우 10비트 축차 근사형의 A/D 컨버터를 8개 내장하고 있다.
실질적으로 A/D 컨버터는 한개이며, 채널을 바꿔가며 아날로그 신호를 입력받을 수 있다.

A/D 컨버터를 제어하기 위한 레지스터로는
- 아날로그 디지털 멀티플렉서 선택 레지스터(ADMUX)와
- 아날로그 디지털 컨버터 제어 상태 레지스터(ADCSR)가 있다.

① ADMUX는 A/D 신호를 입력받을 채널을 0에서 7까지 선택한다.
② 변환 채널이 선택되면 ADCSR을 설정하여 컨버전 프리스케일러 설정, 컨버터 완료 인터럽트를 설정한다.
③ 모든 설정이 끝나면 전역 인터럽트 플래그를 '셋'하여 인터럽트를 활성화한다.
④ 아날로그 디지털 변환 완료 인터럽트 처리 루틴을 구성한 후,
⑤ ADCSR의 6번 비트를 셋시켜 A/D 변환을 시작하게 한다.

변환이 시작한 후 변환 완료 플래그를 주기적으로 점검하거나 아날로그 디지털 변환 완료 인터럽트를 이용하여 A/D 컨버전 이후의 데이터 처리 루틴을 구성할 수 있다. 변환이 종료되어 변환 데이터를 저장할 때, 반드시 하위 데이터를 먼저 읽어서 저장해야 한다. 왜냐하면, 상위 데이터를 먼저 읽으면, 하위 데이터에 쓰레기 값이 들어갈 수 있기 때문이다.


프로그램 예제
가변저항을 통해서 들어오는 전압의 변화(0~5V)를 측정하여, 포트A로 그 측정값을 출력하는 프로그램

#include <iom128.h>
#include <ina90.h>
#include <sig-avr.h>
#include <interrupt.h>
#define   SYSTEM_CLOCK  16000000   // CLOCK (X-tal frequency)
#define ADSC 0x40
typedef unsigned char BYTE;
BYTE ad_l, ad_h;
SIGNAL(SIG_ADC)    
{
ad_l = inp(ADCL);
ad_h = inp(ADCH);
outp(ad_l, PORTA);
outp(ADSC|inp(ADCSR), ADCSR);
}
void Port_Init(void)
{ //Port_A setting up output
outp(0xFF, DDRA);
}

void ADC_Init(int channel)
{ // Setting ADMUX, ADCSR
// Single Mode
ADMUX = channel;
ADCSR = 0xC9;                        // 0b11001001, Polling method
}

 

int main(void)
{ // External Memory Disable
outp(0x00, MCUCR);
Port_Init();
ADC_Init(0);
sei();

 

while(1)
                {
                }

 

return 0;

}

Posted by Paul Hwang
2007. 2. 27. 18:10

USART는 외부 디바이스와 직렬로 인터페이스를 통해 데이터를 송수신할 수 있게 해준다.

외부 디바이스는 다른 마이컴이 될 수 있고 PC도 될 수 있다. PC의 COM포트와 연결하기 위해서는 9핀 케이블과 데이터 전송의 레벨을 조절해 주는 전용 통신 칩이 추가로 필요하다.

범용 동기 수신 송신기(USART)로 데이터를 수신하거나 송신하는 방법에 대해 알아보자.

USART를 제어하기 위한 레지스터로는
USART를 통해 보낼 데이터나 받은 데이터를 두는 - USART I/O 데이터 레지스터(UDRn)  

USART 상태를 알 수 있는 - USART 제어 상태 레지스터(UCSRnA, UCSRnB, UCSRnC)
그리고 데이터 통신의 속도를 결정하는보레이트(Baud Rate)를 설정하는
- USART 보레이트 레지스터(UBRRnL, UBRRnH)가 있다.

통신 속도를 결정해 주는 보레이트가 얼마만큼 가능한지는 마이컴에 부착될 수 있는 크리스탈이나 오실레이터의 속도에 의해 좌우된다. 보레이트 설정하는 방법으로 사용 크리스탈의 주파수를 계산식에 넣어서 나온 값을 UBRR에 넣을 수도 있지만, 데이터 시트에 예로 나와있는 표를 참조할 수도 있다.

USART로 데이터 송수신을 위한 순서는
① UCSR에 송수신 인터럽트 및 방식을 설정하고,
② USART 보레이트 레지스터(UBRRnL, UBRRnH)를 설정하여 데이터 송수신 통신 속도를 선택한다.
③ 그리고, 전역 인터럽트 플래그를 '셋'하여 인터럽트를 활성한 후,
④ 그에 따른 USART 송수신 인터럽트 처리 루틴을 구성한다.


관련 자료
1172567451_lecture2.zip   테스트용 프로그램

 

아래의 예제 소스를 테스트 할 수 있는 PC용 시리얼통신 프로그램입니다.

프로그램 예제
시리얼 통신(RS232, 38400bps)을 통해서 들어오는 입력을 분석하여, 업카운트일 때, LED를 1씩 증가하며 출력하고 다운카운트일 때는 1씩 감소시키면서 출력하는 프로그램

#include <iom128.h>
#include <ina90.h>
#include <sig-avr.h>
#include <interrupt.h>

#define   SYSTEM_CLOCK  16000000   // CLOCK (X-tal frequency)

typedef unsigned char BYTE;

volatile int Count = 0;
volatile BYTE RS_Char=0x00;

SIGNAL(SIG_UART0_RECV)              // USART 수신용 인터럽트 함수
{
       RS_Char=inp(UDR0);
}

void UART_Init(unsigned long BaudRate)
{                                                    
       outp(0x00, UCSR0A);              // Not Double mode, Not multi_communication 
       outp(0x98, UCSR0B);              // 0b 1001 1000 RXCIE,TXCIE,UDRIE,RxEN,TxEN,xxx
       outp(0x06, UCSR0C);              // Setting BaudRate
       outp(0x00, UBRR0H);
       outp((SYSTEM_CLOCK/BaudRate/16 - 1),UBRR0L);
}

void TxData(BYTE data)
{
       while (!(inp(UCSR0A) & (1<<UDRE)));
       outp(data, UDR0);
}

void Port_Init(void)
{                                                   
       outp(0xFF, DDRA);                 //Port_A setting up output
}

int main(void)
{                                                  
       outp(0x00, MCUCR);               // External Memory Disable 
       Port_Init();
       UART_Init(38400);
       sei();

       while(1)
       {
              if (RS_Char == 0x01) Count++;

              else if (RS_Char == 0x02) Count--;

              if (Count > 255) Count=0;

              else if (Count < 0) Count=255;

              outp(Count, PORTA);
        }

        return 0;
}
Posted by Paul Hwang
2007. 2. 27. 17:41

ATmega128은 다음과 같은 4개의 범용 타이머/카운터가 있다.
Timer/Counter0(8비트), Timer/Counter1(16비트), Timer/Counter2(8비트), Timer/Counter3(16비트)가 있다.


타이머/카운터의 제어에 필요한 레지스터는
- 타이머/카운터 제어 레지스터(TCCRn),
- 타이머/카운터 레지스터(TCNTn),
- 출력 비교 레지스터(OCRn)가 있으며,

인터럽트 관련하여
-타이머/카운터 인터럽트 플래그 레지스터(TIFR),
-타이머/카운터 인터럽트 마스크 레지스터(TIMSK)가 있다.

타이머를 사용하기 위해서는 타이머에서 사용하는 클럭에 대해서 설정을 해야 하는데 이는 프리스케일러(Prescaler) 값으로 조절할 수 있다.
프리스케일러 값은 각 타이머의 컨트롤 레지스터(TCCRn)에서 설정할 수가 있다.

타이머 인터럽트는 각 타이머 관련 컨트롤 레지스터에서 적절한 프리스케일러 값을 설정한 후 , 각 타이머 레지스터(TCNTn)에 얼마마다 한번씩 인터럽트를 걸게 할 것인지와 관련된 값을 써주면 된다.

그리고 인터럽트를 사용해야 하므로 타이머 인터럽트 관련 레지스터들을 설정해야 한다. 타이머 인터럽트에서는 TIMSK 레지스터만 설정하면 된다.


프로그램 예제
1msec마다 발생하는 타이머 오버플로 인터럽트를 이용하여, 변수값이 1씩 증가하도록 설정하여 포트 A로 카운트된 값을 출력하는 프로그램

#include <iom128.h>
#include <ina90.h>
#include <sig-avr.h>
#include <interrupt.h>

#define   SYSTEM_CLOCK  16000000   // CLOCK (X-tal frequency)

volatile int Count = 0;

SIGNAL(SIG_OVERFLOW0)
{
     outp(0x83, TCNT0);
     outp(Count, PORTA);
     Count++;
     if (Count > 255)Count= 0;
}

void Port_Init(void)

     outp(0xFF, DDRA);    //Port_A setting up output
}

void Timer_Init(void)
{                // Timer0 setting, 1ms 
                 // system_clk/Presclae = divide_clk
                 // 1/divide_clk = divide_time
                 // 1ms/divide_time = n
                 // 256 - n = TCNT0

     outp(0x05, TCCR0);      // Clock Source = System clock/128
     outp(0x83, TCNT0);
     outp(0x01, TIMSK);      // Timer 0 overflow enable
}

int main(void)
{
     outp(0x00, MCUCR);    // External Memory Disable
     Port_Init();
     Timer_Init();
     sei();
     while(1)
        {
         }
     return 0;
}

Posted by Paul Hwang
2007. 2. 27. 17:13

AVR을 사용하여 할 수 있는 가장 기본적인 기능은 자체 내에 가진 입출력 포트에 신호를 제어하는 것이다.

포트를 제어하는 레지스터는
포트 방향 레지스터(DDRn), 포트 출력 레지스터(PORTn), 포트 입력핀 어드레스(PINn)로 세 개의 레지스터를 이용한다.

포트를 입력으로 이용할 때는
포트 방향 레지스터(DDRn)에 '0'을 넣어 입력으로 설정한 후,
포트 입력핀 어드레스(PINn)의 데이터를 읽어오면 된다.

포트를 출력으로 사용할 때는
포트 방향 레지스터에 '1'을 넣어 출력으로 설정한 후,
포트 출력 레지스터(PORTn)로 데이터를 써 넣으면 된다.

여기서 주의해야 할 부분은 데이터 입력시 조건이다.
포트 방향 레지스터를 입력으로 설정하고 데이터를 입력받을 때, 포트 출력 레지스터의 조건에 따라 포트 입력 레지스터의 상태가 달라지기 때문이다.

포트 출력 레지스터로 '1'을 출력하면, 포트 입력 레지스터가 내부 풀업이 연결된 형태로 구성되므로 외부에서 풀업저항을 추가할 필요가 없게 된다. 하지만, 다른 디바이스와 핀을 공유할 때는 해당핀을 입력이나 출력으로 사용하지 않을 때, 포트 출력 레지스터에 '0'을 출력시켜 해당핀을 하이 임피던스 상태로 만들어 주어야 한다.

Atmega128에서는 포트 A(8), B(8), C(8), D(8), E(8), F(8), G(5)에 해당되는 53개의 핀을 일반 입출력 핀으로 사용할 수 있다. 하지만, Atmega103 compatibility mode로 사용하게 되면, 포트 C는 입력전용, 포트 F는 출력전용, 포트 G는 입출력핀으로 사용할 수 없고, TOSC1, TOSC2, /WR, /RD, ALE의 기능으로만 사용가능하다.


프로그램 예제
포트 D로부터 외부 신호를 입력받아 포트 A로 카운트된 값을 출력하는 프로그램 portD.0의 입력은 내부 변수값을 1씩 증가시키며, portD.1의 입력은 내부 변수값을 1씩 감소시키는 역할을 한다.

#include <iom128.h>
#include <ina90.h>

void Port_Init(void)
     
    outp(0xFF, DDRA);    //Port_A setting up output
    outp(0x00, DDRD);     //Port_D setting up input
    outp(0xFF, PORTD);
}

int main(void)
{
     unsigned Tmp;
     int Count;
 
     outp(0x00, MCUCR);    // External Memory Disable

     Port_Init();
     Count = 0;
     while(1)
    {
      Tmp = inp(PIND)&0x03;
          if (Tmp == 0x02)
                {
                   outp(Count, PORTA);
                   Count++;
                    if (Count >=256) Count=0;
      }
      else if (Tmp == 0x01)
                {
                   outp(Count, PORTA);
                   Count--;
                    if (Count < 0) Count=255;
      }
     }
     return 0;
}

Posted by Paul Hwang