레이블이 UART인 게시물을 표시합니다. 모든 게시물 표시
레이블이 UART인 게시물을 표시합니다. 모든 게시물 표시

2018년 7월 10일 화요일

아두이노 우노/메가 시리얼 포트에서 가능한 최대 통신속도는?

아두이노 우노/메가 하드웨어 시리얼 포트를 사용하는 경우 가장 빠른 속도는 얼마까지 가능할까?

ATmega 프로세서에서 사용하는 USART는 UBRRn 레지스터와 UCSRnA에 있는 U2Xn 플래그로 통신 속도가 결정된다. 프로세서 클럭이 분주되어 USART의 클럭으로 들어가는데 가장 빠르게 설정하면 (UBRRn = 0, U2Xn = 1 로 설정) 프로세서 클럭이 8분주 되어 USART에 들어가게 된다.



아두이노 우노/메가는 16MHz 클럭을 사용하므로 USART에는 2MHz의 클럭이 들어가게 되어 최대 2Mbps의 속도로 시리얼 통신이 가능하게 된다.

테스트를 위해 간단한 코드를 만들어 실행시켜 보았다.


위의 화면에서  볼 수 있듯이 깔끔하게 출력이 되고 있다.

확실하게 확인하기 위해 오실로 스코프로 신호를 잡아 보았다.


먼저 9600 bps일때의 파형이다.

2초 간격으로 'A'를 출력하고 있는 코드이다. 'A'의 아스키코드 값은 65(0x41)이므로 2진수로 하면 01000001 이 된다. 시리얼 출력의 경우 먼저 1비트 길이의 start bit (무조건 '0') + 8비트 데이터 + 1비트 길이의 stop bit(무조건 '1')가 전송되고 데이터의 경우 LSB부터 먼저 전송되게 된다. 그러므로 위의 화면과 같이 0(start bit) 1(LSB) 0 0 0 0 0 1 0(MSB) 1(stop bit) 이 전송된다. 한 비트의 길이는 약 105us, 즉 9.5238Khz의 신호가 된다.


2Mbps일때의 파형이다. 기본적으로 9600 bps일때와 동일한 모양이나 한 비트 길이가 500ns, 즉 2MHz의 신호가 된다.

2015년 3월 31일 화요일

라즈베리파이 시리얼포트 C 프로그래밍 (Raspberry Pi Serial port programming in C)

Raspberry Pi에서는 기본적으로 UART를 시리얼 콘솔로 사용하게 된다. 하지만 UART를 어플리케이션이 다른 용도로 사용하고자 하는 경우에는 콘솔로 동작하지 못하도록 해 줘야 한다.

먼저 이전 포스트(Raspberry Pi에서 UART를 console이 사용하지 않도록 설정하기)를 참조해 설정을 변경해준다.

C 프로그램에서 UART를 사용하기 위해서는 다음의 헤더 파일을 include 해 준다.

#include <stdio.h>
#include <unistd.h> //Used for UART
#include <fcntl.h> //Used for UART

#include <termios.h> //Used for UART

아래는 UART 설정 코드이다.

// -------------------------
// ----- SETUP USART 0 -----
// -------------------------
// At bootup, pins 8 and 10 are already set to UART0_TXD, UART0_RXD 
// (ie the alt0 function) respectively
int uart0_filestream = -1;

// OPEN THE UART
// The flags (defined in fcntl.h):
// Access modes (use 1 of these):
//   O_RDONLY - Open for reading only.
//   O_RDWR - Open for reading and writing.
//   O_WRONLY - Open for writing only.
//
// O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. 
//    When set read requests on the file can return immediately with a failure status
//    if there is no input immediately available (instead of blocking). 
//    Likewise, write requests can also return immediately with a failure status 
//    if the output can't be written immediately.
//
// O_NOCTTY - When set and path identifies a terminal device, 
//    open() shall not cause the terminal device to become 
//    the controlling terminal for the process.
uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode
if (uart0_filestream == -1) {     // ERROR - CAN'T OPEN SERIAL PORT
  printf("Error - Unable to open UART. Ensure it is not in use by another application\n");
}

// CONFIGURE THE UART
// The flags (defined in /usr/include/termios.h 
//     - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html):
// Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, 
//             B115200, B230400, B460800, B500000, B576000, B921600, 
//             B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, 
//             B3500000, B4000000
// CSIZE:- CS5, CS6, CS7, CS8
// CLOCAL - Ignore modem status lines
// CREAD - Enable receiver
// IGNPAR = Ignore characters with parity errors
// ICRNL - Map CR to NL on input 
//         (Use for ASCII comms where you want to auto correct 
//         end of line characters - don't use for bianry comms!)
// PARENB - Parity enable
// PARODD - Odd parity (else even)
struct termios options;
tcgetattr(uart0_filestream, &options);
options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; //<Set baud rate
options.c_iflag = IGNPAR;
options.c_oflag = 0;
options.c_lflag = 0;
tcflush(uart0_filestream, TCIFLUSH);

tcsetattr(uart0_filestream, TCSANOW, &options);

코드가 좀 길기는 해도 코멘트를 참고하면 이해하는데 문제는 없을 것이다. 위의 코드는 시리얼 포트를 9600bps, 8 bit data, no parity, non-blocking 모드로 오픈해준다.

시리얼 포트가 오픈되었으면 아래와 같이 데이터를 송수신 할 수 있다.

데이터 전송

//----- TX BYTES -----
unsigned char tx_buffer[20];
unsigned char *p_tx_buffer;

p_tx_buffer = &tx_buffer[0];
*p_tx_buffer++ = 'H';
*p_tx_buffer++ = 'e';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'l';
*p_tx_buffer++ = 'o';

if (uart0_filestream != -1) {
  int count = write(uart0_filestream, &tx_buffer[0], (p_tx_buffer - &tx_buffer[0])); //Filestream, bytes to write, number of bytes to write
  if (count < 0) {
    printf("UART TX error\n");
  }


}

데이터 수신

//----- CHECK FOR ANY RX BYTES -----
if (uart0_filestream != -1) {
  // Read up to 255 characters from the port if they are there
  unsigned char rx_buffer[256];
  int rx_length = read(uart0_filestream, (void*)rx_buffer, 255); //Filestream, buffer to store in, number of bytes to read (max)
  if (rx_length < 0) {
    //An error occured (will occur if there are no bytes)
  } else if (rx_length == 0) {
    //No data waiting
  } else {
    //Bytes received
    rx_buffer[rx_length] = '\0';
    printf("%i bytes read : %s\n", rx_length, rx_buffer);
  }

}

일반적인 경우 수신된 데이터가 없을 때 read()를 호출하면 데이터가 수신될 때 까지 실행이 멈추게 되지만 여기서는 시리얼포트를  O_NDELAY 플래그를 사용해 non-blocking 모드로 오픈했기 때문에 read()를 호출했을 때 수신된 데이터가 없어도 바로 리턴하기 때문에 코드 내에서 데이터가 수신되기를 기다려야 한다면 while 루프등을 사용해야 한다.

시리얼 포트를 다 사용했다면 포트를 close하면 된다.

// —— CLOSE THE UART —— 
close(uart0_filestream);

시리얼 포트가 동작하는지 확인할 수 있는 가장 쉬운 방법은 Tx와 Rx핀을 서로 연결해 준 다음 프로그램에서 송신한 데이터가 그대로 수신되는지 확인해 보면 된다. (Loopback Test)



Raspberry Pi에서 UART를 console이 사용하지 않도록 설정하기



Raspberry Pi는 기본적으로 UART를 콘솔이 사용하도록 설정되어 있다. 하지만 arduino등의 보드를 UART를 통해 연결해 제어하는 경우 제어 프로그램이 UART를 사용하려면 콘솔이 UART를 잡고 있지 않도록 설정해 줄 필요가 있다.



SD카드를 PC에 연결하고 오픈해보면 cmdline.txt 파일이 있을 것이다. 기본적으로 수정을 하지 않았다면 파일의 내용은 다음과 같을 것이다.


dwc_otg.lpm_enable=0 console=ttyAMA0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline rootwait

여기서 "ttyAMA0"에 관련된 모든 파라미터를 삭제해주면 된다.

즉 위의 경우 'console=ttyAMA0,115200' 부분을 삭제한 후 파일을 저장해 주면 된다.

또한 /etc/inittab 파일도 수정해 줘야 한다.

에디터로 파일을 열고 'ttyAMA0' 를 검색하면 시리얼 포트를 지정하는 라인을 찾을 수 있다.

해당 라인의 맨 앞에 '#'을 추가해 주고 저장해 주면 된다.

이제 리부팅 하고 나면 UART핀은 다른 프로세스가 사용하거나, 또는 그 핀을 일반 GPIO 용도로 사용할 수 있게 된다.