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
2007. 2. 24. 17:03

RS485는 소프트웨어적인 특성에 대한 용어가 아니고 하드웨어적인 특성을 구분짓는 용어입니다. 우리가 serial통신에서 익히 알고 자주 사용하는 것은 RS232C입니다. 이는 Inactive 상태가 -12V, Active 상태가 +12V의 신호를 이용하여 RXD신호와 TXD신호를 서로 교차하여 통신이 이루어 질수 있도록 하는 규격입니다.

마찬가지로 RS422(평형 전송), RS423(불평형 전송), RS485(평형 전송)도 나타내는 규격인 것 입니다. 평형 전송이라 함은 하나의 신호를 두가닥의 신호선을 이용하여 서로 역상의 신호를 실어 보내는 방법 입니다. 그러므로 양단의 장비간의 GND는 공통이 아니어도 전송이 가능함다. 불평형 전송에 사용되는 RS423 의 경우에는 양단 장비간의 GND가 공통이 되어야 함다. 정확한 용어로는 TIA/EIA-422, TIA/EIA-485 standard라 합니다. RS485 driver/receiver로 사용되는 IC는 TI사의 SN75176이 주로 사용됩니다.

RS485 통신의 특징

1. HALF DUPLEX 방식으로 통신이 이루어 집니다.

RS232C, RS422의 경우에는 송신과 수신이 서로 분리 되어 있어서 FULL DUPLEX 양방향 동시 통신이 가능 합니다. 하지만 RS485의 경우에는 송신과 수신이 같은 회선을 사용합니다. 양단의 장비간에 연결되는 회선은 단 두가닥으로서 한쪽이 송신을 하면 다른쪽 은 수신을 해야 합니다. 그래서 HALF DUPLEX 방식으로 통신이 이루어 져야 합니다.

2. MULTI DROP에 의한 다자간 통신이 가능하다.

SN75176의 경우 최대 32 points간의 다자간 통신이 가능합니다. 요즈음 새로 나온 RS485 tranceiver IC중에는 256 points를 지원해주는 IC도 있습니다.

3. 종단 저항(terminator resistor)이 사용됩니다.

RS422에서와 같이 RS485에서도 종단 저항이 사용됩니다. 이 종단 저항이라 함은 start point와 end point 양쪽 끝에 저항을 삽입하여 라인간의 impedence를 조정하게 됩니다. 또한 RS485 통신에 사용되는 회선은 impedence가 50오옴의 twist pair를 사용해야 합니다. 이 종단 저항은 일반적으로 100 오옴 저항을 사용하지만 선로 상태에 따라서 가감을 해야 하는 경우가 발생합니다. 만일 100 오옴 종단 저항을 이용해서 통신이 원활히 이루어 지지 않을 경우에는 저항값을 임의로 조정을 해야할 필요가 있습니다.

4. 최고 1.2 Km(4,000 feet)까지 통신이 가능합니다.

다자간의 총 연장 거리로 따졌을때 최고 1.2 Km까지 통신이 가능합니다. 단, 거리에 반비례하여 통신 속도가 떨어진다는 점에 유념 해야 합니다. 통신 속도는 현장에 맞추어 결정해야 할겁니다.

5. MODEM등과 같은 전화 회선과는 달라서 두 가닥의 회선이 서로 바뀌면 안됨니다.

그러므로 A 회선은 A 회선끼리, B 회선은 B 회선끼리 연결 되어야 합니다. 만일 바뀌면 통신도 안될뿐만 아니라 SN75176이 과부하로 망가지게 됩니다.

RS485 통신 시 소프트 웨어적인 고찰

1. 위에서 설명한 바와 같이 RS485 규격은 하드웨어적인 규격입니다. 하지만 RS232나 RS422과 같은 FULL DUPLEX 방식에 의한 통신이 아니고 HALF DUPLEX 방식이므로 그에 따른 소프트웨어적인 기법이 사용되어야 합니다. 보통은 여러 대중의 하나를 HOST로 삼고 나머지는 SLAVE 컨트롤러로 정해놓고서 HOST가 폴링해서 각 SLAVE와 교신하는 방식을 취합니다.

2. 그러므로 HOST의 아이디, 각 SLAVE의 아이디를 서로 고유하게 보유 하고 있어야 원할한 통신망을 유지할수 있을것입니다.

3. RS485 통신을 위한 정해진 PROTOCOL( 통신 규약 )은 없습니다. 그래서 RS485를 사용한 장비 메이커의 PROTOCOL에 따라 프로그램을 작성해야 합니다.

'전기, 전자 > Commnication(통신)' 카테고리의 다른 글

CAN통신 이란?  (0) 2012.03.12
LAN CABLE(UTP 케이블) 제작 방법  (1) 2010.06.04
Posted by Paul Hwang