/** @file uart.c @date 2009-03-19 @author 오재경 freefrug@falinux.com @brief uart 를 사용한 통신을 담당한다. @modify 2010-08-18 (장길석) mingw와 함께 사용할 수 있는 코드 추가 @todo @bug @remark @warning */ // // 저작권 에프에이리눅스(주) // 외부공개 금지 // //---------------------------------------------------------------------------- #define EMBEDDED_LINUX // 이렇게 처리하지 않으면 EClipse에서 C 영역이 회색 바탕이 됨 #ifdef MS_WIN32 #undef EMBEDDED_LINUX #endif #include #include #include #include #include #include #ifdef EMBEDDED_LINUX #include #include #include #else #include #endif #include #include char desc_uart[] = "falinux uart ver 0.2.1"; /// uart 개별 구조체 typedef struct { // 버퍼관리 char recv_fifo[UART_RECV_FIFO_MAX]; int fifo_rcnt; char port[256]; } uart_priv_t; //------------------------------------------------------------------------------ /** @brief uart 을 open 한다. @param fname 문자열 파일이름 @return poll_obj_t 형태의 포인터 *///---------------------------------------------------------------------------- poll_obj_t *uart_open( char *dev_name, int baud, char parity, int stop_bit) { poll_obj_t *obj; fd_t fd; uart_priv_t *uart; #ifdef EMBEDDED_LINUX struct termios newtio; // 시리얼포트를 연다. // parity 'T' 이면 터미널형식으로 연다. if ( 'T' == parity ) fd = open( dev_name, O_RDWR ); else fd = open( dev_name, O_RDWR | O_NOCTTY ); if ( fd < 0 ) { // 화일 열기 실패 printf( "device open fail %s : ", dev_name ); perror(""); return NULL; } // 시리얼 포트 환경을 설정한다. memset(&newtio, 0, sizeof(newtio) ); // data 8bit newtio.c_cflag = CS8 | CLOCAL | CREAD; // NO-rts/cts if ( 2 == stop_bit){ newtio.c_cflag |= CSTOPB; // CSTOPB: 2stop bit } // baud switch( baud ) { case 2400 : newtio.c_cflag |= B2400 ; break; case 4800 : newtio.c_cflag |= B4800 ; break; case 9600 : newtio.c_cflag |= B9600 ; break; case 19200 : newtio.c_cflag |= B19200 ; break; case 38400 : newtio.c_cflag |= B38400 ; break; case 57600 : newtio.c_cflag |= B57600 ; break; default : newtio.c_cflag |= B115200; break; } // parity switch( parity | 0x20 ) // 소문자 처리 { case 'o' : newtio.c_cflag |= (PARENB |PARODD ); break; case 'e' : newtio.c_cflag |= PARENB ; break; default : ; break; } newtio.c_iflag = 0; newtio.c_oflag = 0; newtio.c_lflag = 0; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; tcflush ( fd, TCIFLUSH ); tcsetattr( fd, TCSANOW, &newtio ); #else char str_rs_config[1024]; sprintf( str_rs_config, "baud=%d data=8 parity=%c stop=1", baud, parity); fd = CreateFile( dev_name, GENERIC_READ|GENERIC_WRITE, 0, /* no share */ NULL, /* no security */ OPEN_EXISTING, 0, /* no threads */ NULL); /* no templates */ if ( INVALID_HANDLE_VALUE == fd){ printf("unable to open %s port\n", dev_name); return NULL; } DCB dcb; memset(&dcb, 0, sizeof(dcb)); /* clear the new struct */ dcb.DCBlength = sizeof(dcb); // if( !BuildCommDCBA( str_rs_config, &dcb)) // { // printf("unable to set comport dcb settings\n"); // CloseHandle( fd); // return NULL; // } dcb.BaudRate = baud; dcb.ByteSize = 8; switch( parity | 0x20 ){ case 'o' : dcb.Parity = ODDPARITY ; break; case 'e' : dcb.Parity = EVENPARITY ; break; default : dcb.Parity = NOPARITY ; break; } dcb.StopBits = ONESTOPBIT; dcb.fBinary = TRUE; dcb.fOutxCtsFlow = FALSE; dcb.fOutxDsrFlow = FALSE; dcb.fDtrControl = DTR_CONTROL_DISABLE; dcb.fDsrSensitivity = FALSE; dcb.fOutX = FALSE; dcb.fInX = FALSE; dcb.fErrorChar = FALSE; dcb.fNull = FALSE; dcb.fRtsControl = RTS_CONTROL_DISABLE; if(!SetCommState( fd, &dcb)) { printf("unable to set comport cfg settings\n"); CloseHandle( fd); return NULL; } COMMTIMEOUTS cto; cto.ReadIntervalTimeout = MAXDWORD; // 이 값을 주어야 1 개 바이트가 들어 와도 이벤트 발생 cto.ReadTotalTimeoutMultiplier = 0; cto.ReadTotalTimeoutConstant = 0; cto.WriteTotalTimeoutConstant = 0; cto.WriteTotalTimeoutMultiplier = 0; if( !SetCommTimeouts( fd, &cto)) { printf("unable to set comport time-out settings\n"); CloseHandle( fd); return NULL; } // 버퍼 비우기 PurgeComm( fd,PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); // 이벤트 설정 SetCommMask( fd,EV_RXCHAR); #endif // uart 만의 정보를 설정한다. uart = (uart_priv_t *)malloc( sizeof(uart_priv_t) ); memset( (void *)uart, 0, sizeof(uart_priv_t) ); strcpy( uart->port, dev_name ); obj = poll_add( fd ); obj->type = STYP_UART; obj->priv = (void *)uart; #ifdef MS_WIN32 obj->is_serial = TRUE; #endif return obj; } //------------------------------------------------------------------------------ /** @brief uart 를 모뎀 제어용으로 open 한다. @param fname 문자열 파일이름 @param fname 문자열 파일이름 @param fname 문자열 파일이름 @return poll_obj_t 형태의 포인터 *///---------------------------------------------------------------------------- poll_obj_t *uart_open_modem( char *dev_name, int baud, char parity ) { poll_obj_t *obj = NULL; #ifdef EMBEDDED_LINUX int fd; uart_priv_t *uart; struct termios newtio; printf( "[%s:%d] \n",__FILE__,__LINE__); // 시리얼포트를 연다. fd = open( dev_name, O_RDWR | O_NOCTTY ); if ( fd < 0 ) { // 화일 열기 실패 printf( "device open fail %s : ", dev_name ); perror(""); return NULL; } printf( "[%s:%d] \n",__FILE__,__LINE__); // 시리얼 포트 환경을 설정한다. memset(&newtio, 0, sizeof(newtio) ); // data 8bit // newtio.c_cflag = CS8 | CLOCAL | CREAD | CRTSCTS ; newtio.c_cflag = CRTSCTS | CS8 | CLOCAL | CREAD ; // newtio.c_cflag = CRTSCTS | CS8 | CREAD ; // baud switch( baud ) { case 2400 : newtio.c_cflag |= B2400 ; break; case 4800 : newtio.c_cflag |= B4800 ; break; case 9600 : newtio.c_cflag |= B9600 ; break; case 19200 : newtio.c_cflag |= B19200 ; break; case 38400 : newtio.c_cflag |= B38400 ; break; case 57600 : newtio.c_cflag |= B57600 ; break; default : newtio.c_cflag |= B115200; break; } // parity switch( parity | 0x20 ) // 소문자 처리 { case 'o' : newtio.c_cflag |= (PARENB |PARODD ); break; case 'e' : newtio.c_cflag |= PARENB ; break; default : ; break; } printf( "[%s:%d] \n",__FILE__,__LINE__); // newtio.c_iflag = 0; newtio.c_iflag = IGNPAR | ICRNL; newtio.c_oflag = 0; // newtio.c_lflag = 0; newtio.c_lflag = ICANON; newtio.c_cc[VTIME] = 0; newtio.c_cc[VMIN] = 0; printf( "[%s:%d] \n",__FILE__,__LINE__); tcflush ( fd, TCIFLUSH ); tcsetattr( fd, TCSANOW, &newtio ); // uart modem 만의 정보를 설정한다. uart = (uart_priv_t *)malloc( sizeof(uart_priv_t) ); memset( (void *)uart, 0, sizeof(uart_priv_t) ); strcpy( uart->port, dev_name ); printf( "[%s:%d] \n",__FILE__,__LINE__); obj = poll_add( fd ); obj->type = STYP_UART; obj->priv = (void *)uart; #else #endif return obj; } //------------------------------------------------------------------------------ /** @brief uart 를 close 한다. @param obj 폴객체 포인터 *///---------------------------------------------------------------------------- void uart_close( poll_obj_t *obj ) { #ifdef EMBEDDED_LINUX close( obj->fd ); #else CloseHandle( obj->fd); #endif if ( obj->priv ) { free( obj->priv ); } poll_delete( obj ); } //------------------------------------------------------------------------------ /** @brief uart 폴객체를 파일이름으로 찾는다. @param fname 문자열 파일이름 @return obj 폴객체 포인터 *///---------------------------------------------------------------------------- poll_obj_t *uart_get_byport( char *fname ) { poll_obj_t *obj; uart_priv_t *uart; int idx, count; count = poll_count(); for(idx=0; idxtype == STYP_UART ) { uart = (uart_priv_t *)obj->priv; if ( uart ) { if ( 0 == strcmp( uart->port, fname ) ) { return obj; } } } } return NULL; } //------------------------------------------------------------------------------ /** @brief uart 를 통해 데이타를 전송한다. @param obj 폴객체 포인터 @param buf 전송버퍼 @param len 버퍼의 길이 @return 전송한 데이타 개수 *///---------------------------------------------------------------------------- int uart_write( poll_obj_t *obj, char *buf, int len ) { int wrcnt; // 전송한다. #ifdef EMBEDDED_LINUX wrcnt = write( obj->fd, buf, len ); #else WriteFile( obj->fd, buf, len, (LPDWORD)((void *)&wrcnt), NULL); #endif if ( 0 > wrcnt ) { perror( "uart send error:" ); } return wrcnt; } //------------------------------------------------------------------------------ /** @brief uart 를 통해 데이타를 읽는다. @param obj 폴객체 포인터 @param buf 일기버퍼 @param len 버퍼의 길이 @return 읽은 데이타 개수 *///---------------------------------------------------------------------------- int uart_read( poll_obj_t *obj, char *buf, int len ) { int rdcnt; // 데이타를 읽는다. #ifdef EMBEDDED_LINUX rdcnt = read( obj->fd, buf, len ); #else ReadFile( obj->fd, buf, len, (LPDWORD)((void *)&rdcnt), NULL); #endif if ( 0 > rdcnt ) { perror( "uart recv error:" ); } return rdcnt; } //------------------------------------------------------------------------------ /** @brief uart 내부 수신버퍼에 데이타를 저장한다. @param obj 폴객체 포인터 @return 내부수신버퍼의 총 데이타 길이 *///---------------------------------------------------------------------------- int uart_read_into_fifo( poll_obj_t *obj ) { uart_priv_t *uart; char *buf; int rdcnt, len; uart = (uart_priv_t *)obj->priv; buf = uart->recv_fifo + uart->fifo_rcnt; len = UART_RECV_FIFO_MAX - uart->fifo_rcnt; if ( 0 >= len ) { printf( "uart recv buffer full\n" ); } else { #ifdef EMBEDDED_LINUX rdcnt = read( obj->fd, buf, len ); #else ReadFile( obj->fd, buf, len, (LPDWORD)((void *)&rdcnt), NULL); #endif if ( 0 < rdcnt ) { uart->fifo_rcnt += rdcnt; } } return uart->fifo_rcnt; } //------------------------------------------------------------------------------ /** @brief uart 내부 수신버퍼에서 데이타를 읽어온다. @param obj 폴객체 포인터 @param buf 데이타를 담아올 버퍼 @param len 버퍼의 길이 @return 복사된 데이타 길이 @remark 데이타를 복사한 후 복사한 크기만큼 버퍼의 앞쪽으로 데이타를 이동한다. *///---------------------------------------------------------------------------- int uart_copy_recv_fifo( poll_obj_t *obj, char *buf, int len ) { uart_priv_t *uart; uart = (uart_priv_t *)obj->priv; if ( len > uart->fifo_rcnt ) len = uart->fifo_rcnt; memcpy( buf, uart->recv_fifo, len ); uart->fifo_rcnt -= len; if ( 0 < uart->fifo_rcnt ) { memmove( uart->recv_fifo, uart->recv_fifo+len, uart->fifo_rcnt ); } return len; } //------------------------------------------------------------------------------ /** @brief uart 내부 수신버퍼의 포인터를 얻는다. @param obj 폴객체 포인터 @return uart 내부 수신버퍼의 포인터 *///---------------------------------------------------------------------------- char *uart_get_recv_fifo( poll_obj_t *obj ) { uart_priv_t *uart; uart = (uart_priv_t *)obj->priv; return uart->recv_fifo; } //------------------------------------------------------------------------------ /** @brief uart 내부 수신버퍼에 저장된 데이타의 갯수를 얻는다. @param obj 폴객체 포인터 @return 수신버퍼에 저장된 데이타의 갯수 *///---------------------------------------------------------------------------- int uart_get_recv_fifo_count( poll_obj_t *obj ) { uart_priv_t *uart; uart = (uart_priv_t *)obj->priv; return uart->fifo_rcnt; } //------------------------------------------------------------------------------ /** @brief uart 내부 수신버퍼에서 앞쪽의 데이타를 제거한 후 뒤쪽의 데이타를 이동한다. @param obj 폴객체 포인터 @param len 제거될 데이타 개수 @return 수신버퍼에 남아있는 데이타의 갯수 *///---------------------------------------------------------------------------- int uart_checkout_recv_fifo( poll_obj_t *obj, int len ) { uart_priv_t *uart; uart = (uart_priv_t *)obj->priv; uart->fifo_rcnt -= len; if ( 0 > uart->fifo_rcnt ) uart->fifo_rcnt = 0; if ( 0 < uart->fifo_rcnt ) { memmove( uart->recv_fifo, uart->recv_fifo+len, uart->fifo_rcnt ); } return uart->fifo_rcnt; } //------------------------------------------------------------------------------ /** @brief uart 내부 수신버퍼를 비운다. @param obj 폴객체 포인터 *///---------------------------------------------------------------------------- void uart_clear_recv_fifo( poll_obj_t *obj ) { uart_checkout_recv_fifo( obj, uart_get_recv_fifo_count( obj ) ); } //------------------------------------------------------------------------------ /** @brief uart에서 비교데이타가 수신될때까지 감시한다. @param obj 폴객체 포인터 @param match 비교할 버퍼 @param match_len 비교할 버퍼의 크기 @param tmout_msec 타임아웃 시간 @return 성공 0, 실패 -1 *///---------------------------------------------------------------------------- int uart_recv_wait_fifo( poll_obj_t *obj, unsigned char *match, int match_len, int tmout_msec ) { int rdcnt, poll_ret, deep_wait; char *rbuf; deep_wait = 0; // 과거의 데이타를 모두 없앤다. // 2011-05-25 제거 // uart_checkout_recv_fifo( obj, uart_get_recv_fifo_count( obj ) ); while( 0 < tmout_msec ) { // 수신데이타를 감시한다. poll_ret = poll_do_one( obj->fd, POLLIN, 100 ); // 이벤트가 없었다면 시간을 감소시킨다. if ( poll_ret != POLL_EVENTED ) { tmout_msec -= 100; // 1초 동안 데이타가 없었다면 수신버퍼 clear deep_wait ++; if ( 10 < deep_wait ) { deep_wait = 0; uart_checkout_recv_fifo( obj, uart_get_recv_fifo_count( obj ) );\ } continue; } // 통신이 들어왔다면 1mse 만 감소시킨다. tmout_msec --; deep_wait = 0; // 데이타를 버퍼에 넣는다. rdcnt = uart_read_into_fifo( obj ); rbuf = uart_get_recv_fifo ( obj ); while( match_len <= rdcnt ) { // 아래 문장은 아직 테스트를 못했다. // if ( 0 == memcmp( rbuf, match, match_len ) ) { uart_checkout_recv_fifo( obj, uart_get_recv_fifo_count(obj)-rdcnt ); return 0; } rbuf ++; rdcnt --; } if ( 0 < ( uart_get_recv_fifo_count(obj)-match_len ) ) { uart_checkout_recv_fifo( obj, uart_get_recv_fifo_count(obj)-match_len ); } } return -1; }