вторник, 3 декабря 2013 г.

Работа с COM портом в Linux

Инициализация COM порта

COM порт в Linux представляет собой файл устройства. Типичное расположение такого файла
\dev\ttySx для обычного COM порта или \dev\ttyUSBx для
USB-RS переходника, где x = 0,1,2...
Встречаются также \dev\ttyOSx и др.
Для начала работы COM порт необходимо открыть:
int F_ID = -1;
open("\dev\ttyUSB0", O_RDWR | O_NOCTTY);
if(F_ID == -1)
{
    std::cout << strerror(errno) << std::endl;
}
Если F_ID отличается от -1, значит COM порт открыт успешно
и можно переходить к его настройке.
Чтение текущих настроек:
struct termios options; /*структура для установки порта*/
tcgetattr(F_ID, &options); /*читает пораметры порта*/
Установка скорости:
cfsetispeed(&options, B57600); /*установка скорости порта*/
cfsetospeed(&options, B57600); /*установка скорости порта*/
Установка таймаутов чтения:
options.c_cc[VTIME]    = 20; /*Время ожидания байта 20*0.1 = 2 секунды */
options.c_cc[VMIN]     = 0; /*минимальное число байт для чтения*/
Установка других полезных (необходимых) параметров:
options.c_cflag &= ~PARENB; /*бит четности не используется*/
options.c_cflag &= ~CSTOPB; /*1 стоп бит */
options.c_cflag &= ~CSIZE;  /*Размер байта*/
options.c_cflag |= CS8;  /*8 бит*/
    
options.c_lflag = 0;
options.c_oflag &= ~OPOST; /*Обязательно отключить постобработку*/
 
options.c_iflag = 0;
options.c_iflag &= ~ (INLCR | IGNCR | ICRNL);
Сохранение параметров:
tcsetattr(F_ID, TCSANOW, &options);

Чтение и отправка данных

Прочитать данные из COM порта:

unsigned char buff[16] = {0};
int size = 8;
int n = read(F_ID, buff, size);
Отправить в COM порт:
unsigned char buff[4] = {1,2,3,4};
int size = 4;
int n = write(F_ID, buff, size);

Завершение работы

После того, как работа с COM портом будет завершена, его необходимо закрыть:
close(F_ID);
F_ID = -1;

Дополнительные возможности

Кроме сигналов TX и RX в COM порте присутствуют дополнительные сигналы,
например: RTS, CTS и т.д
иногда с ними необходимо работать.
Установка RTS:
int status;
ioctl(F_ID, TIOCMGET, &status);
status |= TIOCM_RTS;
ioctl(F_ID, TIOCMSET, &status);
Очистка RTS:
int status;
ioctl(F_ID, TIOCMGET, &status);
status &= ~TIOCM_RTS;
ioctl(F_ID, TIOCMSET, &status);

В итоге получаем набор функций

#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>  
#include <sys/ioctl.h>
#include <unistd.h>

int F_ID = -1;
/*----------------------------------------------------------------------------
 Открыть COM порт
 COM_name: путь к устройству, например: /dev/ttyS0 или  /dev/ttyUSB0 - для USB
 speed: скорость, например: B9600, B57600, B115200 
 ----------------------------------------------------------------------------*/
bool openPort(const char *COM_name,speed_t speed)
{
    F_ID = open(COM_name, O_RDWR | O_NOCTTY);
    if(F_ID == -1)
    {
        char *errmsg = strerror(errno);
        printf("%s\n",errmsg);
        return false;
    }
    struct termios options; /*структура для установки порта*/
    tcgetattr(F_ID, &options); /*читает пораметры порта*/

    cfsetispeed(&options, speed); /*установка скорости порта*/
    cfsetospeed(&options, speed); /*установка скорости порта*/

    options.c_cc[VTIME]    = 20; /*Время ожидания байта 20*0.1 = 2 секунды */
    options.c_cc[VMIN]     = 0; /*минимальное число байт для чтения*/

 
    options.c_cflag &= ~PARENB; /*бит четности не используется*/
    options.c_cflag &= ~CSTOPB; /*1 стоп бит */
    options.c_cflag &= ~CSIZE;  /*Размер байта*/
    options.c_cflag |= CS8;  /*8 бит*/
    
    options.c_lflag = 0;
    options.c_oflag &= ~OPOST; /*Обязательно отключить постобработку*/

    tcsetattr(F_ID, TCSANOW, &options); /*сохронения параметров порта*/
    
    return true;
}

/*----------------------------------------------------------------------------
 Прочитать данные из COM порта
 buff - буфер для принятых данных
 size - количество запрашиваемых байт
 ----------------------------------------------------------------------------*/
int readData(unsigned char *buff,int size)
{
    int n = read(F_ID, buff, size);
    if(n == -1)
    {
        char *errmsg = strerror(errno);
        printf("%s\n",errmsg);
    }
    return n;
}

/*----------------------------------------------------------------------------
 Отправить в COM порт данные
 buff - буфер данных для отправки
 size - количество отправляемых байт
 ----------------------------------------------------------------------------*/
int sendData(unsigned char* buff,int len)
{
    int n = write(F_ID, buff, len);
    if(n == -1)
    {
        char *errmsg = strerror(errno);
        printf("%s\n",errmsg);
    }
    return n;
}
/*----------------------------------------------------------------------------
 Закрыть COM порт
 ----------------------------------------------------------------------------*/
void closeCom(void)
{
    close(F_ID);
    F_ID = -1; 
    return;
}
/*----------------------------------------------------------------------------
 Установить RTS
 ----------------------------------------------------------------------------*/
void setRTS()
{
    int status;
    ioctl(F_ID, TIOCMGET, &status);
    status |= TIOCM_RTS;
    ioctl(F_ID, TIOCMSET, &status);
}
/*----------------------------------------------------------------------------
Очистить RTS
 ----------------------------------------------------------------------------*/
void clrRTS()
{
    int status;
    ioctl(F_ID, TIOCMGET, &status);
    status &= ~TIOCM_RTS;
    ioctl(F_ID, TIOCMSET, &status);
}
/*----------------------------------------------------------------------------
Пример использования
 ----------------------------------------------------------------------------*/
int main()
{
    bool res = openPort("\dev\ttyUSB0",B9600);
    if(!res)
    {
        printf("Невозможно открыть COM порт\n");
        return 0;
    }
    unsigned char buff[8] = {0,1,2,3,4,5,6,7};
    sendData(buff,8);
 
    int s = readData(rbuff,4);
    if(s < 4)
    {
        printf("Нет ответа\n");
        return 0;
    }
 /*
 Наш код
  */
    return 0;
}

4 комментария:

  1. А можно ли работать с портом не с помощью функций open(), close(), и т. д., а с помощью fopen(),fclose(), т. е. не через файловый дескриптор, а через указатель FILE*?

    ОтветитьУдалить
    Ответы
    1. Не знаю, не пробовал. Но в этом случае не понятно, что делать с настройками скорости и четности
      tcsetattr(F_ID, TCSANOW, &options);

      Удалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. Слэши обратные... Виндузятник детектед :D

    ОтветитьУдалить