programing

리눅스에서 직렬 포트 읽기 및 쓰기

css3 2023. 6. 14. 22:08

리눅스에서 직렬 포트 읽기 및 쓰기

FTDI를 사용하여 USB 포트를 통해 데이터를 송수신하려고 하므로 C/C++을 사용하여 직렬 통신을 처리해야 합니다.저는 리눅스(우분투)에서 일하고 있습니다.

기본적으로 수신 명령을 수신하는 장치에 연결되어 있습니다.저는 그 명령들을 보내고 장치의 응답을 읽어야 합니다.명령과 응답은 모두 ASCII 문자입니다.

GtkTerm을 사용하면 모든 것이 정상적으로 작동하지만 C 프로그래밍으로 전환하면 문제가 발생합니다.

내 코드는 다음과 같습니다.

#include <stdio.h>      // standard input / output functions
#include <stdlib.h>
#include <string.h>     // string function definitions
#include <unistd.h>     // UNIX standard function definitions
#include <fcntl.h>      // File control definitions
#include <errno.h>      // Error number definitions
#include <termios.h>    // POSIX terminal control definitions

/* Open File Descriptor */
int USB = open( "/dev/ttyUSB0", O_RDWR| O_NONBLOCK | O_NDELAY );

/* Error Handling */
if ( USB < 0 )
{
cout << "Error " << errno << " opening " << "/dev/ttyUSB0" << ": " << strerror (errno) << endl;
}

/* *** Configure Port *** */
struct termios tty;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 )
{
cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << endl;
}

/* Set Baud Rate */
cfsetospeed (&tty, B9600);
cfsetispeed (&tty, B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;        // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;
tty.c_cflag     &=  ~CRTSCTS;       // no flow control
tty.c_lflag     =   0;          // no signaling chars, no echo, no canonical processing
tty.c_oflag     =   0;                  // no remapping, no delays
tty.c_cc[VMIN]      =   0;                  // read doesn't block
tty.c_cc[VTIME]     =   5;                  // 0.5 seconds read timeout

tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines
tty.c_iflag     &=  ~(IXON | IXOFF | IXANY);// turn off s/w flow ctrl
tty.c_lflag     &=  ~(ICANON | ECHO | ECHOE | ISIG); // make raw
tty.c_oflag     &=  ~OPOST;              // make raw

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );

if ( tcsetattr ( USB, TCSANOW, &tty ) != 0)
{
cout << "Error " << errno << " from tcsetattr" << endl;
}

/* *** WRITE *** */

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};
int n_written = write( USB, cmd, sizeof(cmd) -1 );

/* Allocate memory for read buffer */
char buf [256];
memset (&buf, '\0', sizeof buf);

/* *** READ *** */
int n = read( USB, &buf , sizeof buf );

/* Error Handling */
if (n < 0)
{
     cout << "Error reading: " << strerror(errno) << endl;
}

/* Print what I read... */
cout << "Read: " << buf << endl;

close(USB);

무슨 일이 일어날까요?read()0(바이트를 전혀 읽지 않음) 또는 시간 초과될 때까지 블록을 반환합니다(VTIME) 제 생각에 이런 일이 일어나는 것 같습니다.write()아무 것도 보내지 않습니다.이 경우 장치가 명령을 수신하지 못하고 응답을 수신할 수 없습니다.실제로 프로그램 읽기가 차단된 상태에서 장치를 끄면 응답이 표시됩니다(장치가 종료되는 동안 무언가를 전송함).

이상한 것은 이것을 추가한다는 것입니다.

cout << "I've written: " << n_written << "bytes" << endl; 

바로 뒤에write()통화, 수신:

I've written 6 bytes

그게 바로 제가 기대하는 바입니다.장치가 포트에서 실제로 쓰고 있는 내용을 수신할 수 없는 것처럼 프로그램만 제대로 작동하지 않습니다.

데이터 유형과 관련하여 다양한 방법과 솔루션을 시도했습니다(std::string을 사용해 보았습니다.cmd = "INIT \r"또는const char하지만 아무 것도 효과가 없었습니다.

누가 내가 어디가 틀렸는지 말해줄 수 있나요?

잘 부탁드립니다.

EDIT: 사용된 이 코드의 이전 버전

unsigned char cmd[] = "INIT \n"

그리고 또한cmd[] = "INIT \r\n"장치에 대한 명령 sintax가 다음과 같이 보고되어 변경했습니다.

<command><SPACE><CR>.

저는 또한 그것을 피하려고 노력했습니다.O_NONBLOCK읽다가 플래그를 세우면 영원히 차단할 뿐입니다.사용해 보았습니다.select()하지만 아무 일도 일어나지 않습니다.데이터를 사용할 수 있을 때까지 대기 루프를 만들었지만 코드는 루프를 벗어나지 않습니다.BTW, 대기 또는usleep()제가 피해야 할 것입니다.보고된 것은 제 코드의 발췌본일 뿐입니다.완전한 코드는 실시간 환경(특히 OROCOS)에서 작동해야 하므로 저는 수면과 같은 기능을 별로 원하지 않습니다.

저는 제 문제를 해결했기 때문에 누군가 유사한 것이 필요할 경우를 대비하여 여기에 올바른 코드를 게시합니다.

개항장

int USB = open( "/dev/ttyUSB0", O_RDWR| O_NOCTTY );

매개 변수 설정

struct termios tty;
struct termios tty_old;
memset (&tty, 0, sizeof tty);

/* Error Handling */
if ( tcgetattr ( USB, &tty ) != 0 ) {
   std::cout << "Error " << errno << " from tcgetattr: " << strerror(errno) << std::endl;
}

/* Save old tty parameters */
tty_old = tty;

/* Set Baud Rate */
cfsetospeed (&tty, (speed_t)B9600);
cfsetispeed (&tty, (speed_t)B9600);

/* Setting other Port Stuff */
tty.c_cflag     &=  ~PARENB;            // Make 8n1
tty.c_cflag     &=  ~CSTOPB;
tty.c_cflag     &=  ~CSIZE;
tty.c_cflag     |=  CS8;

tty.c_cflag     &=  ~CRTSCTS;           // no flow control
tty.c_cc[VMIN]   =  1;                  // read doesn't block
tty.c_cc[VTIME]  =  5;                  // 0.5 seconds read timeout
tty.c_cflag     |=  CREAD | CLOCAL;     // turn on READ & ignore ctrl lines

/* Make raw */
cfmakeraw(&tty);

/* Flush Port, then applies attributes */
tcflush( USB, TCIFLUSH );
if ( tcsetattr ( USB, TCSANOW, &tty ) != 0) {
   std::cout << "Error " << errno << " from tcsetattr" << std::endl;
}

쓰기

unsigned char cmd[] = "INIT \r";
int n_written = 0,
    spot = 0;

do {
    n_written = write( USB, &cmd[spot], 1 );
    spot += n_written;
} while (cmd[spot-1] != '\r' && n_written > 0);

바이트당 바이트를 쓸 필요도 없었습니다.int n_written = write( USB, cmd, sizeof(cmd) -1)잘 작동했습니다.

마지막으로 다음을 읽습니다.

int n = 0,
    spot = 0;
char buf = '\0';

/* Whole response*/
char response[1024];
memset(response, '\0', sizeof response);

do {
    n = read( USB, &buf, 1 );
    sprintf( &response[spot], "%c", buf );
    spot += n;
} while( buf != '\r' && n > 0);

if (n < 0) {
    std::cout << "Error reading: " << strerror(errno) << std::endl;
}
else if (n == 0) {
    std::cout << "Read nothing!" << std::endl;
}
else {
    std::cout << "Response: " << response << std::endl;
}

이건 저한테 효과가 있었어요.여러분 감사합니다.

하며, 으로 두 문자인 EOL 시퀀스를 합니다.\r\n그러니 코드를 입력해 보세요 라인을 교체하세요.

unsigned char cmd[] = {'I', 'N', 'I', 'T', ' ', '\r', '\0'};

와 함께

unsigned char cmd[] = "INIT\r\n";

그나저나, 위의 방법이 아마도 더 효율적일 것입니다.모든 문자를 인용할 필요는 없습니다.

init 뒤에 /n을 추가합니다. 즉, write(USB, "init\n", 5);

직렬 포트 구성을 다시 확인합니다.아마 뭔가 잘못된 것 같습니다.^Q/^S 또는 하드웨어 흐름 제어를 사용하지 않는다고 해서 상대방이 기대하지 않는 것은 아닙니다.

가능성이 가장 높습니다.write() 뒤에 "ussleep(100000);"을 추가합니다.파일 설명자는 차단하거나 기다리지 않도록 설정되어 있죠?호출 읽기를 호출할 수 있을 때까지 응답을 받는 데 얼마나 걸립니까?(읽기 전에 시스템 하드웨어 인터럽트를 통해 커널에서 수신하고 버퍼링해야 합니다.)select()를 사용하여 읽을 내용을 기다리는 것을 고려해 보셨습니까?아마도 시간이 초과되면요?

추가하도록 편집됨:

DTR/RTS 라인이 필요합니까?다른 쪽에 컴퓨터 데이터를 전송하도록 지시하는 하드웨어 흐름 제어(예:

int tmp, serialLines;

cout << "Dropping Reading DTR and RTS\n";
ioctl ( readFd, TIOCMGET, & serialLines );
serialLines &= ~TIOCM_DTR;
serialLines &= ~TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
usleep(100000);
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;
sleep (2);

cout << "Setting Reading DTR and RTS\n";
serialLines |= TIOCM_DTR;
serialLines |= TIOCM_RTS;
ioctl ( readFd, TIOCMSET, & serialLines );
ioctl ( readFd, TIOCMGET, & tmp );
cout << "Reading DTR status: " << (tmp & TIOCM_DTR) << endl;

언급URL : https://stackoverflow.com/questions/18108932/reading-and-writing-to-serial-port-in-c-on-linux