시리얼(rs-232c)통신프로그램 질문드립니다.

enermysong의 이미지

리눅스에서 시리얼 통신 예제 프로그램 만드는 중인데여

string을 주고 받는 프로그램입니다. 그런데 문제가 처음에 hello world !! 이런식으로 string을 보내면 정상적으로 수신측에서 받아지는데요
여러번 보내다보면 llo world !! h 이런식으로 글자순서가 바뀌거나 합니다. 글자 순서는 거의 랜덤으로 바뀌는것 같구요 .
옵션은 패리티 검사를 하지 않고 있습니다. 받은다음 시리얼의 버퍼를 비워주고 다시 보내야 하나요?
무엇이 문제인지 정확하게 모르겠습니다.

조언이 필요할것 같습니다. 꼭좀 도와 주세요!!

mach의 이미지

serial같은 point-to-point 미디어를 통한 전송에서 절대로 순서가 바뀔 수 없습니다.
데이터의 유실이나, 버퍼관리의 오류로 보입니다.
------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

klenui의 이미지

원래 시리얼통신은 USB나 Ethernet같이 무결성이 보장된다고 가정하시면 안됩니다.
물론 빈번하게 나온다면 이야기가 다릅니다. baudrate이 정확한지 봐야할것이고, 제어흐름이 양방향 모두 같은지 봐야 할것이고, 제어흐름이 같다면 필요한 라인이 다 연결되어 있는지 봐야할것이고..(보통은 GND, RX, TX만 연결할텐데 다른 제어흐름선이 필요한 프로토콜을 사용하는건 아닌지 확인) 케이블이 지나치게 길지 않나 확인해 봐야 할것이고... 윗분글처럼 데이터유실, 버퍼관리 오류도 검토해봐야하고...
하지만 1Mb전송했는데, 1,2 byte 잘못갔더라.. 이런건 시리얼 통신은 그럴수 있다고 봐야합니다.

보통은 패킷설계를 해서 CRC32 같은 체크섬을 넣고 체크섬이 다른경우 재전송을 하는식으로 많이 구현합니다.
그냥 로그 메세지 보는 경우라면 상관없지만, firmware다운로드같이 무결성 보장이 중요한경우는 체크섬확인이 필수적이지요..

klenui의 이미지

아래 drinkme님의 리플을 보고 제가 오해의 소지가 있도록 적었다는 생각이 듭니다.
시리얼 통신을 처음 접하시는 분들께는 패킷설계라는게 시리얼 통신이 원래 패킷단위 전송이 가능한것처럼 들릴수 있는데,
이 또한 구현해야 하는 부분입니다.
보통 End of Packet 값을 정해놓고 전송을 합니다. 패킷중에 정해놓은 값이 등장할때를 대비해서 bit stuffing을 하구요.
예를 들어 옛날 퀄컴 소스에는 0x7F가 packet reset신호입니다. 0x7F를 보내면 수신측에서는 이제껏 받은 버퍼를 하나의 패킷으로 간주합니다.
그안에서 체크섬 확인이나 뭐 그런걸 하구요
패킷안에 0x7F가 등장할때는 비트 스터핑 프리픽스인 0x5F 를 붙이고 값에 0x20을 XOR합니다.
즉 데이터 0x7F 는 0x5F 0x5F 가 되는 거죠.. 데이터 0x5F 는 0x5F 0x7F가 되겠네요.. 기억에 의존하는 거라 정확한지는 모르겠습니다만, 대강 이런식이었던 거 같습니다...
상당히 정밀한 전송이 필요할때 이렇게 합니다만(위에서 언급했듯, firmware다운로드 등일때...)
요즘은 다 USB를 쓰니까, 이렇게 까지는 하지 않는거 같습니다다만, 참고가 될까해서 적어봅니다.
소스부분은 다은 분들이 봐주실듯..

drinkme의 이미지

시리얼통신은 패킷통신이 아닙니다.
일종의 스트림 통신입니다.

가령,
"12345", "67890" 이렇게 두 문자열을 연달아 보내면,
받는쪽에서 어떤 상황에 읽어내느냐에 따라
"1" "2" "34567" "890" 이렇게 읽히기도 하고,
"1234567890" 으로 읽히기도 합니다.

뭐 가끔 전송단에서의 오류로 인해, 중간에 데이타가 날라가기도 합니다.

enermysong의 이미지

#include "serialtestwin.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <termios.h>
#include <sys/signal.h>
#include <fcntl.h>
#include <qsocketnotifier.h>
#include <qmessagebox.h>
 
#define BUFF_SIZE   1024
#define BAUDRATE B115200
 
QSocketNotifier *notRsRead;
int fd;
char buff[BUFF_SIZE+5];
 
void device_close()
{
    close(fd);
}
 
 
SerialTestWin::SerialTestWin(QWidget *parent) :
    QMainWindow(parent){
    setupUi(this);
    this->InitSerial();
}
 
void SerialTestWin::changeEvent(QEvent *e)
{
    QMainWindow::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        retranslateUi(this);
        break;
    default:
        break;
    }
}
 
void SerialTestWin::InitSerial()
{
    struct termios newtio;
    fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NONBLOCK);
 
    memset(&newtio,0,sizeof(newtio));
    switch(ratespeed)
    {
    newtio.c_cflag = BAUDRATE;
    newtio.c_cflag |= CS8;
    newtio.c_cflag |= CLOCAL | CREAD;
    newtio.c_iflag = IGNPAR;
    newtio.c_oflag = 0;
    newtio.c_lflag = 0;
    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 1;
 
    tcflush(fd, TCIFLUSH);
    tcsetattr(fd,TCSANOW, &newtio);
    notRsRead = new QSocketNotifier(fd, QSocketNotifier::Read, this);
    connect(notRsRead, SIGNAL(activated(int)), this, SLOT(ReceviceMessage()));
}
void SerialTestWin::ReceviceMessage()
{
    int szRead;
    QString buffer;
    szRead = read(fd, buff, BUFF_SIZE);
    buffer.fromAscii(buff,BUFF_SIZE);
 
    this->MessageLineEdit->setText(buff);
}
 
void SerialTestWin::on_SendMessageButton_clicked()
{
     char *messageBuf = this->MessageLineEdit->text().toAscii().data();
 
    if( 0 > write(fd, messageBuf , strlen(messageBuf)))
        QMessageBox::information(this, "rs232","Sending Error!!");
}
 
void SerialTestWin::on_ReturnMainButton_clicked()
{
    this->hide();
}
 
void SerialTestWin::on_ExitButton_clicked()
{
    QApplication::exit();
}

소스는 위와 같고요! PC와 타겟 시스템간에 시리얼 통신 테스트 프로그램입니다.

LINE_EDIT를 이용하여 글자를 적어서 SEND 버튼을 누르면 글자가 상대방 PC의 LINE_EDIT로 출력 되는건데요!

예를들어서 SERIAL TEST OK!! 라고 LINE EDIT 에 입력하고 SEND 버튼을 누르면 상대쪽 PC에 처음에는 정상적으로 출력이 됩니다. 그런데 계속 누르면 SERIAL TES OK T !! 이런식으로 문자가 변경 됩니다.

디버깅으로 돌려 봤는데 보내는 쪽에서는 정상적으로 문자열이 보내집니다.
중간 버퍼에서 문제가 생기는것 같은데.. 정확한 원인을 모르겠습니다.

gasiri의 이미지

제 생각에는 위의 drinkme 이 말씀하신 것처럼
스트림의 순서 뒤바뀜 현상은 일반적으로
나타나는 것이라 문제는 아닙니다.
특히 님이 사용하시는 환경에서도 나타날 수 있고요.
때문에 Serial 통신을 이용하여 사용자 정의 프로토콜을 정의
할때는 처음과 마지막 식별자 그리고 무결성을 보장할 CRC 나 checksum
을 프로토콜에 넣어 사용하고 있습니다.

그런데 위의 예시처럼 님께서 제시한 내용의 순서 뒤바뀜의 예는
다른 경우인 것 같습니다.

Quote:

예를들어서 SERIAL TEST OK!! 라고 LINE EDIT 에 입력하고 SEND 버튼을 누르면 상대쪽 PC에 처음에는 정상적으로 출력이 됩니다. 그런데 계속 누르면 SERIAL TES OK T !! 이런식으로 문자가 변경 됩니다.

확인 할 순 없지만 Serial 드라이버상의 버퍼 처리문제라기 보다는
Application 코드상의 오류쪽으로 접근 해보시는 것이 좋을 듯 해보입니다.
특히 Serial 프로그램에서 Read system call 을 이용시 read 의 결과 값은
꼭 확인 해보셔합니다.

mach의 이미지

void SerialTestWin::ReceviceMessage()
{
    int szRead;
    QString buffer;
    szRead = read(fd, buff, BUFF_SIZE);
    buffer.fromAscii(buff,BUFF_SIZE);
 
    this->MessageLineEdit->setText(buff);
}

을 다음과 같이 바꿔서 테스트해보세요.

void SerialTestWin::ReceviceMessage()
{
    int szRead;
    QString buffer;
<span>    memset(buff, 0, sizeof(buff));</span>
    szRead = read(fd, buff, BUFF_SIZE);
    buffer.fromAscii(buff,BUFF_SIZE);
 
    this->MessageLineEdit->setText(buff);
}

그리고, read(), write()에서 리턴값 검사가 없습니다.
* read()/write()에 대한 리턴값 검사와, 시그널 처리가 없는 유닉스/리눅스 프로그램은 신뢰할 수 없는 프로그램이랄 수 밖에 없습니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

지리즈의 이미지

this->MessageLineEdit->setText(buff);

가 아니라,

this->MessageLineEdit->setText(buffer);

네요.

여기도,
buffer.fromAscii(buff,BUFF_SIZE);
가 아니라,
buffer.fromAscii(buff,szRead);
가 되야 할 듯.

There is no spoon. Neo from the Matrix 1999.

There is no spoon. Neo from the Matrix 1999.

drinkme의 이미지

저도 null 처리 안한게 걸리는 군요.

void SerialTestWin::ReceviceMessage()
{
int szRead;
QString buffer;
szRead = read(fd, buff, BUFF_SIZE);
buff[szRead] = '\0';
buffer.fromAscii(buff,BUFF_SIZE);

this->MessageLineEdit->setText(buff);
}

지리즈의 이미지

void SerialTestWin::ReceviceMessage()
{
    int szRead;
    QString buffer;
    szRead = read(fd, buff, BUFF_SIZE);
    buffer.fromAscii(buff,BUFF_SIZE);
 
    this->MessageLineEdit->setText(buff);
}
 
void SerialTestWin::on_SendMessageButton_clicked()
{
     char *messageBuf = this->MessageLineEdit->text().toAscii().data();
 
    if( 0 > write(fd, messageBuf , strlen(messageBuf)))
        QMessageBox::information(this, "rs232","Sending Error!!");
}

이 부분을

void SerialTestWin::ReceviceMessage()
{
    int szRead;
    QString buffer;
    szRead = read(fd, buff, BUFF_SIZE);
    buffer.fromAscii(buff,<span>szRead</span>);
 
    this->MessageLineEdit->setText(<span>buffer</span>);
}
 
void SerialTestWin::on_SendMessageButton_clicked()
{
//     char *messageBuf = this->MessageLineEdit->text().toAscii().data();
 
//    if( 0 > write(fd, messageBuf , strlen(messageBuf)))
//      QMessageBox::information(this, "rs232","Sending Error!!");
    <span>if( 0 > write(fd, this->MessageLineEdit->text().toAscii().constData() , this->MessageLineEdit->text().toAscii().length()))</span>
        QMessageBox::information(this, "rs232","Sending Error!!");
}

로 하고 싶네요

There is no spoon. Neo from the Matrix 1999.

There is no spoon. Neo from the Matrix 1999.

Tony의 이미지

위에 언급된대로 s/w문제가 아니라면 flow control등의 세팅문제가 아닐까 하는 의심이 듭니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.