'UART echo server'에 해당되는 글 1건

  1. 2009.10.08 ATmega128 UART 에코 서버 만들기 (echo server) 4
embeded/AVR (ATmega,ATtiny)2009. 10. 8. 23:13
에코 서버라고 하니 먼가 거창한데,
간단하게 입력하면 그걸 그대로 돌려줘서 화면에 나타나게 하는 프로그램이다.
시리얼로 전송하면, 받는쪽에서 그 값을 돌려주지 않으면 터미널에서 그 값이 출력되지 않는다.
가장 간단하고, 확실한 테스트 방법이라서 일단 echo 하도록 하는데 먼가 험난했다 ㄱ-

아무튼 개인적으로 선호하는 115200bps - N - 8 - 1 로 설정하고, UART0를 통해 UART echo server를 만들어보자





오늘 필요한 녀석은
#include <avr/interrupt.h>

ISR(USART0_RX_vect)
{
    UDR0 = UDR0;
}
요렇게 두 부분이다.

위의 헤더는 ISR() 이라는 매크로를 사용하게 하는 것이고,
ISR은 Interrupt Serive Routine의 약자이다.

예전에는 SIGNAL(SIG_UART0_RECV) 로 사용했을 것이지만,
winAVR 버전이 올라가면서 ISR()로 바뀐 것으로 알고 있다. (물론 사용중인 버전이 20080610 버전으로 좀 오래됐다 ㅠ.ㅠ)

아무튼 USART0_RX_vect 라는 것은 iom128.h의 432 라인에 기술되어있다.
(iom128.h는 <avr/io.h>와 makefile의 cpu 선언에 의해서 자동으로 불려지는 파일이다)
/* USART0, Rx Complete */
#define USART0_RX_vect            _VECTOR(18)
#define SIG_USART0_RECV            _VECTOR(18)
#define SIG_UART0_RECV            _VECTOR(18)

시리얼포트 초기화시에는 당연히 UART RX 인터럽트를 사용하도록 설정을 해야하고,
전역 인터럽트를 사용하도록 해주어야 한다.

UART 인터럽트는 UCSR0A/B/C 레지스터로 조작을 해준다.
    /* for UART */
    /* "BaudRate" related setting */
    UBRR0H = 0;
    UBRR0L = 8; // 115k with U2X = 0
    UCSR0A = 0x00; // U2X = 0;

   /* "Interrupt" related setting */
    UCSR0B = 0x98;

   /* "Sync or Async mode - Parity - stop bits - data bits" related setting*/
    UCSR0C = 0x06; //Asyncronous - no parity - 1bits(stop) - 8bits(data)

UCSR0A  레지스터는 U2X0 를 제외하면 전부 Status Flag 이므로 U2X0를 사용하지 않는다면 0x00으로 설정한다.
UBBR0H 와 UBBR0L 은 BaudRate와 관련된 것으로, 클럭과 원하는 BaudRate에 따라 변하지만,
귀찮으면, 데이터 시트에 나와있는 값으로 입력을 하면된다. (위의 값은 계산하지 않고 그냥 데이터시트 값을 사용한 것이다)

그리고 U2X는 UCSR0A의 비트로, 에러율을 낮추기 위해 2배 속도로 샘플링하는 것이다.
(디바이더(Divider)의 값을 반으로 줄여, 샘플링을 자주해서 값을 놓치지 않도록 한다)

• Bit 1 – U2Xn: Double the USART Transmission Speed
This bit only has effect for the asynchronous operation. Write this bit to zero when using synchronous operation.
Writing this bit to one will reduce the divisor of the baud rate divider from 16 to 8 effectively doubling the transfer rate for asynchronous communication.

UCSR0B  레지스터의 98 값은 인터럽트 관련 설정값이다.
일단 Bit 5를 set 하게 되면, 원하는대로 echo 되지 않으니
    UCSR0B = 0xD8 이나
    UCSR0B = 0x98 로 설정해주면 된다.
(Bit 7 에서 Bit 4까지 1001(2) 이므로 0x90 이고 (RX enable / Receiver Enable)
 Bit 3 으로 인해 0x08, 합쳐서 0x98이 된다.)


UCSR0C  레지스터는 Asynchronous / Synchronous mode 를 고르고,
                어떤 종류의 패리티를 쓸지, 스탑 비트는 몇 비트를 할지, 데이터는 몇 비트로 사용할지 결정한다.
                친숙하게 보는 115200-N-8-1 이러한 설정값에 대한것 중 BaudRate를 제외한 거의 모든것을 결정한다.

그리고 sei() 라는 매크로를 통해, 전역 인터럽트를 사용가능하도록 설정해주어야 한다.
#define sei ()      
#include <avr/interrupt.h>

Enables interrupts by setting the global interrupt mask.
This function actually compiles into a single line of assembly, so there is no function call overhead.

[링크: http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html]

머.. sei() 친구로 cli()도 있지만, 어셈블리 명령어인데 명확한 약자로 보기에는 조금 애매함이 있다.
SEtting global Interrupt
CLearing global Interrupt
more <avr/interrupt.h>

#if defined(__DOXYGEN__)
/** \def sei()
    \ingroup avr_interrupts

    \code #include <avr/interrupt.h> \endcode

    Enables interrupts by setting the global interrupt mask. This function
    actually compiles into a single line of assembly, so there is no function
    call overhead. */
#define sei()
#else  /* !DOXYGEN */
# define sei()  __asm__ __volatile__ ("sei" ::)
#endif /* !DOXYGEN */

#if defined(__DOXYGEN__)
/** \def cli()
    \ingroup avr_interrupts

    \code #include <avr/interrupt.h> \endcode

    Disables all interrupts by clearing the global interrupt mask. This function
    actually compiles into a single line of assembly, so there is no function
    call overhead. */
#define cli()
#else  /* !DOXYGEN */
# define cli()  __asm__ __volatile__ ("cli" ::)
#endif /* !DOXYGEN */



[링크: http://blog.yurihan.net/]

Posted by 구차니