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)



댓글 없음:

댓글 쓰기