Programming/8051

8051( 중급 8부 - RS485 기타 serial data 다루기 )

청솔1 2009. 11. 29. 14:36

8051( 중급 8부 - RS485 기타 serial data 다루기 )


이번 강좌는 RS-485 뿐만 아니라 RS-232, MODEM등의 serial 통신상의 데이터를 어떻게 다루는가에 대한 내용이다.

마이컴을 배우다 보면 제일 첫번째로 부딪히는 문제가 명령어에 대한 이해 부족이다.

그 다음 단계에서는 그 명령어들을 어떻게 조합해서 프로그램을 작성하느냐의 문제이지요.

그 단계를 넘어서서 간단한 프로그램을 작성하게 되었을때 부딪히는 문제가 serial 통신 문제이다.
나름대로의 protocol을 작성하여 통신 데이터를 송신하는것은 그런대로 어렵지 않는데

수신 데이터를 어떻게 요리를 해야 하는지에 난색을 표하시는 분덜이 많다.
일단 지난 강좌에서 제시한 프로토콜중 마지막 프로토콜에 의해 설명을 하겠슴다.
실행할수 있는 프로그램이 되려면 이 강좌의 루틴을 복사해서 자신의 프로그램에 연결 하십시오.

STX 수신아이디 송신아이디 command length data ... ETX chk1 chk2

이 프로토콜은 산적이 올 연초에 작업했던 프로젝트에서 나름대로 구성해서 사용했던 검다.
여기에서 STX, ETX는 각각 0x02, 0x03이다.
그리고 수신 아이디와 송신 아이디는 ASCII 3 바이트의 문자로 표현한다.
말하자면 "000" ~ "999"가 되는 셈이다.
command는 1 바이트의 ASCII 문자를 사용함다. 'A' ~ 'Z', 'a' ~ 'z'
length는 3 바이트의 ASCII 문자로 표현한다. "001" ~ "999"
data는 length에서 규정한 문자 갯수만큼 나열된다.
수신하는 요령은 다음과 같슴다.
1) 현재 수신된 데이터가 STX 인가?
이때는 COM2_rx_count를 0 으로 클리어 시켜 수신되는 문자열의 저장 장소인 COM2_rx_buf[ 0 ]에 STX를 저장한다.
2) 현재 수신된 데이터가 ETX 인가?
일단 COM2_rx_buf[ COM2_rx_count ] 에 ETX를 저장하고 COM2_etx_flag 를 세트 시키고 COM2_chk_count 를 0 으로 클리어 시킨다.
COM2_etx_flag 를 세트 시키는 이유는 STX,ETX 가 아닌 문자인 경우 switch 문의 default 에서 동시에 처리 하기 때문에 ETX 수신전이라면 아이디, command, length, data 에 해당되지만 ETX 수신후라면 그것은 chk1, chk2 이기 때문이다.
3) STX, ETX가 아닌 다른 문자일때
가. ETX 수신 전인가를 확인한다. 확인 방법은 COM2_etx_flag 값을 조회 하면 된다.
ETX 수신 전이라면 COM2_rx_buf[ COM2_rx_count ] 에 저장하고 COM2_rx_count를 증가 시킨다.
만일 회선상의 노이즈나 그외 영향에 의해 ETX를 검출 못한다면 COM2_rx_count 가 한없이 증가 하여 CPU를 폭주하게 만들 가능성이 있다.
그러므로 매번 COM2_rx_count 값을 조회하여 처음 설정한 버퍼 용량을 초과 하는지를 검사 해야 한다.
나. ETX 수신후 라면
일단 COM2_chk_sum에 수신된 데이터를 저장한다. 그리고 COM2_chk_count 값을 증가 시킨다.
COM2_chk_count 값이 2라면 chk1, chk2 모두 수신된 경우이므로 수신된 체크썸과 CPU가 연산한 체크썸이 일치 하는지 확인 한다.
체크썸이 맞으면 COM2_rx_buf 값을 COM2_rx_data 에 복사한 다음 Command_process() 를 호출한다.

<XMP>#define BUF_SIZE 512 xdata int COM2_rx_count;

xdata unsigned char COM2_rx_buf[ BUF_SIZE / 2 ];

xdata int COM2_etx_flag; xdata int COM2_chk_count;

xdata unsigned char COM2_chk_sum[ 2 ];

void COM2_process( void )

   {

      int i;

unsigned char c;

c = COM2_read();

switch( c )

  {

     case STX : COM2_rx_count = 0;

     COM2_rx_buf[ COM2_rx_count++ ] = c;

     break;

case ETX :

COM2_rx_buf[ COM2_rx_count++ ] = c;

COM2_etx_flag = 1;

COM2_chk_count = 0; break;

default :

if( COM2_etx_flag == 1 )

   {

      </XMP>  /* ETX가 이미 수신 된경우 */

      <XMP> COM2_chk_sum[ COM2_chk_count++ ] = c;

      if( COM2_chk_count == 2 )

         {

             </XMP>  /* check sum이 다 수신 된 경우 */

             <XMP> COM2_etx_flag = 0;

             c = 0;

             COM2_chk_count = 0;

             </XMP>

 /* 현재 COM2_rx_buf 에 저장된 STX ~ ETX 까지의 모든 데이터를 xor 취한다. */

<XMP> for(i=0;i /* chk1의 값이 맞는지 확인한다. */

/* HEX_ascii_high() 함수는 지난 강좌에 있다. */

<XMP>

if( HEX_ascii_high( c ) != COM2_chk_sum[ 0 ] )

   {

       COM2_rx_count = 0; return;

   }

   </XMP>  /* chk2의 값이 맞는지 확인한다. */

   <XMP>

if( HEX_ascii_low( c ) != COM2_chk_sum[ 1 ] )

  {

     COM2_rx_count = 0; return;

  }

</XMP>

 /* 다음 문자열 수신을 위해 COM2_rx_buf 값과 체크썸 값을 복사한다.*/

/*문자열의 마지막에는 null( 0 )을 넣어 준다. */

<XMP>

for(i=0;

i

/* protocol 대로 정리된 수신 문자열을 처리하기 */

/* 위해 처리 함수를 호출한다. */

<XMP> Command_process();

COM2_rx_count = 0;

COM2_etx_flag = 0;

COM2_chk_count = 0;

  }

  }

else

 {

     </XMP>

     /* 아직 ETX를 수신하기 전이면 버퍼의 현재 위치에 */

     /* 저장하고 COM2_rx_count 를 증가 시킨다. */

     <XMP>

     COM2_rx_buf[ COM2_rx_count++ ] = c;

     </XMP>

 /* 만일 회선상의 오류등으로 ETX를 검출 못했을 경우 */

/* 버퍼에 overflow가 발생하는 것을 방지 하기 위해 COM2_rx_count값이 미리 설정한 버퍼 용량을 초과하면 COM2_rx_count, COM2_etx_flag, COM2_chk_dount  들을 모두 0 으로 클리어 시킨다. */

/* 프로그램 작성시에는 이처럼 에러가 발생할수 있는 */

 /* 가능성에 대비해서 작성해야 한다. */

     <XMP>

     if( COM2_rx_count >= ( BUF_SIZE / 2 ) )

        {

            COM2_rx_count = 0;

            COM2_etx_flag = 0;

            COM2_chk_count = 0;

        }

    }

break;

}

}

</XMP>

아래의 루틴은 실제 프로젝트에서 활용한 방법을 예로 들어 놓은것 이다.
이 루틴을 분석하는데 알고 있어야 할 사항은 아이디가 "000"이면 최상급 기관이고 "100"이면 그다음,
"110","111"이면 차례로 그 하급 기관에 해당이 되도록 아이디 체계를 임의로 만든것이다.

<XMP>void Command_process( void )

{

</XMP>

 /* ----------------- protocol ------------------- */

/* 0 1 2 3 4 5 6 7 8 9 10 n-2 n-1 n */

/* STX 수신I.D. 송신I.D. command length data ETX chk1 chk2 */

/* ----------------------------------------------*/

<XMP> xdata unsigned char slave_id[ 4 ], host_id[ 4 ], command, chk;

xdata unsigned char tx_str[ 40 ];

int i, num, true, slave, host, my;

command = COM2_rx_data[ 7 ];

</XMP>

/* 수신 아이디를 확인하여 ASCII 값을 정수형으로 바꾼다. */

/* 예를 들어 수신 아이디가 "100"이라면 */

/* slave = ('1' - '0') * 100 */

/* + ('0' - '0') * 10 */

/* + ('0' - '0') */

/* = 100 */

<XMP> slave_id[ 0 ] = COM2_rx_data[ 1 ];

slave_id[ 1 ] = COM2_rx_data[ 2 ];

slave_id[ 2 ] = COM2_rx_data[ 3 ];

slave_id[ 3 ] = 0;

slave = ( slave_id[ 0 ] - '0' ) * 100 + ( slave_id[ 1 ] - '0' ) * 10 + ( slave_id[ 2 ] - '0' );

</XMP>

 /* 송신 아이디를 확인하여 ASCII 값을 정수형으로 바꾼다 */

<XMP> host_id[ 0 ] = COM2_rx_data[ 4 ];

host_id[ 1 ] = COM2_rx_data[ 5 ];

host_id[ 2 ] = COM2_rx_data[ 6 ];

host_id[ 3 ] = 0; host = ( host_id[ 0 ] - '0' ) * 100 + ( host_id[ 1 ] - '0' ) * 10 + ( host_id[ 2 ] - '0' ); my = ( My_id[ 0 ] - '0' ) * 100 + ( My_id[ 1 ] - '0' ) * 10 + ( My_id[ 2 ] - '0' );

if( slave != my )

{

if( host != my )

{

 /* 자신을 경유해서 상,하급으로 가는 경우 */ .......................

}

 else

{

/* 자신이 하급을 동작 시키는 경우 */

...........................................

}

return;

}

 true = 0;

switch( command )

{ case 'Q' : /* 하급에서 수신되어 올라온 경우 */

................

break;

case 'q' : ................ break;

default : ................ break;

}

if( true )

{

............

}

}

</XMP>

위에서와 같이 command를 분석하여 원하는 동작을 할수 있도록 프로그램을 작성하면 된다.
RS-485 강좌 처음에 말씀 드렸다 시피 RS-485는 하드웨어적인 규격이지 소프트웨어적인것이 아니다.
그러므로 기존의 장비에 연결하여 제어를 하고자 할때는 미리 정해진 프로토콜에 의해 serial 데이터를 분석해야 한다.

새로운 장비를 만들어 제어코자 할때는 나름대로의 프로토콜을 만들어야 한다.
본 강좌에서 제시한것은 산적이 사용해왔던 프로토콜을 보여드린것 뿐이다.
보다 효율적인 프로토콜을 위해서는 각자 연구를 많이 해야 할것으로 본다.
아래 회로및 사진은 RS-485 회선을 모니터링 하기 위해서 산적이 즐겨 만들어 쓰는 회로이다.
보드상의 SN75176 IC를 제거하고 이것을 꼽은 다음에 RS-232로 모니터링 하면 RS-485 라인상에 오고 가는 데이터를 볼수 있다.
전원을 8 PIN SOCKET로 부터 받는다.
함 맹글어 보세요. 요긴하게 쓰일거다.

 

 

 

<< 전자기기 개발 --- 건강기기, 통신기기,각종 전자회로 개발, PCB ARTWORK >>

 

 

 

'Programming > 8051' 카테고리의 다른 글

8051( 중급 10부 - A/D )   (0) 2009.11.29
8051( 중급 9부 - Graphic LCD )  (0) 2009.11.29
8051( 중급 7부 - RS485 )   (0) 2009.11.29
8051( 중급 6부 - RS485 )  (0) 2009.11.29
8051( 중급 5부 - pre-processor )  (0) 2009.11.29