Автор Гілка: програмування СОМ-порта  (Прочитано 7663 раз)

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
щось я незнайду ніде документіції по програмування СОМ-порта. ну для відкриття використати fopen("/dev/ttyS0","rw") - це зрозуміло. далі для того щоб ганяти дані - fread/fwrite - це також зрозуміло, а як налаштувати швидкість, кількість біт датих, управління потоком (аналог WINAPI функції SetCommState) і таймаути (аналог функції SetCommTimeouts) я щось не знайду. І ще, може хто знає гарну доку по POSIX API, щось назразок MSDN, дайте буд-ласка посилання.

niii

  • Гість
Re: програмування СОМ-порта
« Відповідей #1 : 2007-12-03 12:03:12 »
От допоможе  8-)

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #2 : 2007-12-03 20:47:59 »
Дуже дякую  [smiley=wink2.gif]

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #3 : 2008-03-18 14:05:31 »
А в якій бібліотеці функція open лежить ? ось наприклад fopen є в libc.so відповідної версії, а от функції open там немає...

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #4 : 2008-05-02 20:37:19 »
Думаю що мені ніхто не відповість (хоч тут і добіса програмістів) але все ж таки запитаю.
У мене не получається зробити обмін по порту в режимі напівдуплексу. задача така - ганяти modbus через COM-порт. польова шина (це така мережа) працює на транспотрі RS-485 який, звісно, є напівдуплексним. комп до цієї шини підключений через адаптер RS-232 <-> RS-485 але він для програмного забезпечення прозорий, тобто все що з'явиться на виході TX COM-порту буде передана на лінії АВ RS-485 і навпаки, все що є на лініях AB попадає на вхід RX СОМ-порту. пріоритет має напрямок RS-458 ->RS-232, єха немає, тобто все що передавалось на TX назад на RX не повертається. Це така апаратна частина. тепер програмна. комп відправляє запит - послідовнисть байт. на тійс стороні пристрій (якийсь датчик чи інша гидота, яка знаходиться в режимі прийому)  приймає цей запит, декодує його, формує відповідь(теж послідовність байт), перемикається на передачу, передає відповідь і знову перемикається на прийом. дані - бінарні, можливі всі значення від 0 до 255, ніяких спецсимволів немає. це одина особливсіть, розділювачами пакунків є інтервали тиші довжиною в 4 байти, тобто якщо швидкість скажімо 19200 то пиримається що пакунок переданий  коли напротязі 2 мілісекунд із моменту отримання стопового біта немає передачі стартового (для посилки 8N1)
це знасить присказка, а тепер казка, власне в мене воно  працює, але я маю якийсь асинхрон. тобто після того як я пошлю запит я отримую не поточку відповідь, а попередню, хоча на осцилографі я чітко бачу картинку на виводах TX та RX СОМ-проту. маю тому підтвердження таке що коли я посилаю декілька одинакових запитів підряд то перших парку відповідей невірні, а далі нормально. а коли запити чергуються (кожен раз комп посилає пакунок іншого змісту) то я взагалі не можу нормальну відповідь отримати.
ось фрагмент проги, настройка порта
MgErr CINRun(LStrHandle portName, int16 *baudRate, int32 *timeout, int32 *fc,
        int32 *fport)
        {

        struct termios options;

        *fport = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); // відкриваємо порт

        if(*fport!=-1) // якщо вдалось відкрити
        { // налаштувати порт
            fcntl(*fport, F_SETFL, 0); // виконувати операцію читання із порта із блокуванням
            if(tcgetattr(*fport, &options)==0) // прочитати поточні параметри порут, якщо вдало, на
лаштувати
            {
                cfsetispeed(&options, B19200); // налаштувати шкидвість прийому
                cfsetospeed(&options, B19200); // та передачі

                // Enable the receiver and set local mode...
                options.c_cflag |= (CLOCAL | CREAD);

                // інші параметри порта (8N1 режим)
                options.c_cflag &= ~PARENB; // вимкнути перевірку паритету
                options.c_cflag &= ~CSTOPB; // 1 стоп біт
                options.c_cflag &= ~CSIZE;
                options.c_cflag |= CS8; // кількість біт даних
                //#ifdef CNEW_RTSCTS
                //    options.c_cflag &= ~CNEW_RTSCTS;
                //#endif
                // відключити обробку символа драйвером порта
                options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
                options.c_oflag &= ~OPOST;
                // відмінити програмне управління потоком передачі даних
                options.c_iflag &= ~(IXON | IXOFF | IXANY);

                // налаштувати таймаути
                options.c_cc[VMIN]=0;
                options.c_cc[VTIME]=*timeout;

                // встановити параметри порту

                if(tcsetattr(*fport, TCSANOW, &options)!=0) // якщо невдалося встановити тоді TCSAFLUSH
                {
                    close(*fport); //закрити порт
                    *fport=-1;
                }
            }
            else // якщо невдало прочитані параметри - закрити порт
            {
                close(*fport);
                *fport=-1;
            }

        }

        return noErr;
}
от така функція. ну а далі write та read.
Із читанням ситуація така: я наперед знаю довжину відповіді і хочу одним викликом її прочитати, але на тому кінці може статися якась поломкі і відмовіді може не  бути або зовсім, або частини. і з часом поломка буде усунута і прилад знову запрацює. так от, програма повинна чекати якийсь час а потім продовжити роботу, просигналізувавши про те що немає відповіді. тому я використав блокуюче читання із порта із завершенням по таймауту.
Ще одне питання: write просто закине дані у буфер передавача і завершить роботу не чекаючи на завершення передачі? можна якосьзмінити поведінку так, щоб ця функція чекала завершення передачі ?

ще в мене склалося враження що те що я написав

                options.c_cc[VTIME]=*timeout;
йому до спини. не реагуж воно на зміну значення змінної *timeout

Які будуть поради ?

Відсутній DalekiyObriy

  • Літератор
  • ******
  • дописів: 1929
  • Карма: +5/-0
Re: програмування СОМ-порта
« Відповідей #5 : 2008-05-02 23:09:54 »
у Вас досить складна ситуація, яку досить непросто відслідкувати не маючи повного коду та апаратних засобів, тому повну відповідь на всі питання Ви врядчи отримаєте, але можна спробувати по частинах:
ось тут є досить непоганий опис роботи з послідовним портом (англ.), ось там описана одна Ваша проблема:

Цитата
Two elements of the c_cc array are used for timeouts: VMIN and VTIME. Timeouts are ignored in canonical input mode or when the NDELAY option is set on the file via open or fcntl.
Fedora 35 (x86-64)

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #6 : 2008-05-02 23:48:42 »
З повим кодому тут проблемка. я звісно можу його виложити, але часина його зроблена на LabView, а частина - на сі. один фрагмент я вже навів. інші - це просто обгортки для викликів write, read та close. labview не вміє робити системних викликів (а може то я невмію його навчити їх робти) але може виконувати певним чином зкомпоновані шматки програми на сі.

на рахунок останнього, режим у мене звісно не канонічний. а от про NDELAY - незнаю, зараз покурю трохи h-файли.....

із станніх спостережень.
1) якщо відповідь вкладуєтьсяу 8 байтів, все працює.
2) якщо відповідь довше 8, наприклад 15, байтів то за перший раз читається 8, за другий 7 байтів, а далі по 15. але я думаю що ці байти це відповідь на попередній запит а не не поточний. Запити, звісно, однакові.

те що пан радив, тільки російською я вже читав.


зараз вирішив проблему просто вставивши затримку часу між викликами write та read, щоб напевне дочекатись всіх байт відповіді.  але це поганий варіант...
« Змінено: 2008-05-03 00:17:07 від vano_vvv »

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #7 : 2008-05-04 20:41:36 »
це все таки у лабв'ю, як казав мій колега, багатопотоковсіть із турбулентностями,
цей код
#include <stdio.h>
#include <unistd.h>  
#include <fcntl.h>
#include <termios.h>


int main(int argc,char **argv)
{
    int fp;
    struct termios options;
    char buff[10];

    fp = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY); // відкриваємо порт

    fcntl(fp, F_SETFL,0 ); // виконувати операцію читання із порта із блокуванням
    // якби було FNDELAY то було б читання без блокування. функція
    if(tcgetattr(fp, &options)==0) // прочитати поточні параметри порут, якщо вдало, налаштувати
    {
        cfmakeraw(&options);
        options.c_cflag |= (CLOCAL | CREAD);
        cfsetspeed(&options, B19200); // налаштувати шкидвість
                // налаштувати таймаути
        options.c_cc[VMIN]=0;
        options.c_cc[VTIME]=50; // чекати байта 5 секунд

        // встановити параметри порту
        if(tcsetattr(fp, TCSANOW, &options)!=0) // якщо невдалося встановити тоді TCSAFLUSH
        {
            close(fp); //закрити порт
            printf("Невдалося нашаштувати порт\n");
        }
    }
    printf("Записано  байт %d\n",write(fp,buff,10));
    printf("Прочитано байт %d\n",read(fp,buff,10));
    close(fp);

    return 0;
}

я просто написав для перевірки. як і можна очікувати, коли до порта нічого не підключено, він виконується за 5 секунд. У лабвю подібна послідовність викликиві виконується менше ніж за 1 секунду. різниця ось яка: у цій програмі виклик read повертає значення 0. у лабв'ю повертається значення -1.
маю такі думки із цього приводу. Лабв'ю воно із самого початку багатопотокове, причому потоки створюються автоматично, без будь-яких зусиль із боку розробника, у нього є свій диспечер потоків, незнаю чи базується він на системному диспечері чи ні, мабуть все-таки рано чи пізно воно всеодно викликає pthread_create. Є ймовірність що виклики open, read, write та close попадають у різні потоки або взагалі для них будуть створені окремі потоки які будуть існувати тільки під час виконання даних викликів. Ці потоки, вони звісно синхронізовані. тобто write не виконається раніше ніж open і точно отримає як свій перший параметер значення, яке поверне виклик open.  ну і після виконання виклику wire буде виконано виклик read але знову ж таки є ймовірність що той read буде в іншому потоці.
От такі пироги. Які взагалі будуть думки шановоного паньства? Чи спробувати і на сі рознести ці виклики по різних потоках і подивитить що з того получиться..

ПС Мабуть буду завтра писати мило на сапорт, благо за цю річ заплочені гроші.....
« Змінено: 2008-05-04 20:58:49 від vano_vvv »

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #8 : 2008-09-01 22:26:57 »
Я періодично повертаюся до цієї проблеми, останнім часом виключно використовуючи сі. Ось які маю спостереження.
Порт ініціалізується в raw режим із блокуванням читання. таймаути налаштовуються наступним чином
c_cc[VMIN]=0;
c_cc[VTIME]=1; // 100 мілісекунд.
ганяю пакунки в напівдуплексному режимі: запит - відповідь.
довжина запитів 8-10 байт, відповіді 10-50 байт. між запитом та відповіддю є інтервал тиші приблизно 2-5 мілісекунд. кількість байт відповіді я знаю (залежить від запиту)

в результаті маю наступне: якщо відповіді немає взагалі - фунція read завершує свою роботу за 100 мілісекунд  і повертає 0. Якщо приходить хоч декілька байт відповіді або вона є повністю, функція read здебільшого завершує свою роботу прочитавші тільки частину відповіді. Завершує свою роботу вона значно раніше аніж за 100 мілісекунд. Від чого залежить скільки байтів прочитає read я не знаю, таке враження таче нормальне розподілення. викликавши функцію read ще раз можна прочитати ще частину відповіді, у більшості випадків всю. інколи потрібно виклиати read втретє. таке буває десь приблизно у 20% випадків.
загалом я  зараз роблю так
int len - кількість байт яку необхідно прочитати char *b - буфер int i - просто змінна fPort- дескриптор порта
i=read(fPort,b,len);
if(i<len)
i+=read(fPort,b+i,len-i);
if(i<len)
i+=read(fPort,b+i,len-i);

загалом весь цикл обміну, разом із викликом wtire займає приблизно 30-35 мілісекунд.

ЛабВью із його "приколами" в даному випадку зовсім нідочого, абсолютно не винне.

сирці у причепі але без пристрою на другому кінці звісно ніяких відповідів не буде. але оскільки там три read'а підряд то цикл обміну в цьому випадку затягується на 300 мілічекунд. воно не критично але неприємно.

maranoid

  • Гість
Re: програмування СОМ-порта
« Відповідей #9 : 2008-09-02 09:21:19 »
Тут є корисна стаття:
http://linuxportal.ru/entry.php/2171_0_3_0_C/

Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #10 : 2008-09-02 12:04:34 »
2 maranoid
я це читаю вперше
Цитата
Если MIN = 0 и TIME > 0, то TIME является величиной таймаута. Возврат из read случится либо при появлении символа, либо при достижении таймаута (t = TIME * 0.1 с). При выходе по таймауту никакие символы не возвращаются.
але це впринципі все пояснює. будь-який прийнятий символ або декілька символів перериває виконання read навіть якщо отримано менше символів ніж запрошено. Я правильно зрозумів ?
« Змінено: 2008-09-02 12:10:15 від vano_vvv »

maranoid

  • Гість
Re: програмування СОМ-порта
« Відповідей #11 : 2008-09-02 13:07:43 »
2 vanessa
Так. Якщо, наприклад, MIN = 10, виконання read переривається після десятого символа, незалежно від кількості запрошених символів. Але, якщо трапиться міжсимвольний таймаут, виконання read переривається і частина символів може залишитися у буфері. У цьому випадку треба виконати очистку буферу, наприклад, так: read(fPort,NULL,255). У WINAPI для цього існує PurgeComm, який аналог є в Лінуксі - не знаю.


Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #12 : 2008-09-02 20:06:46 »
перевіряв як воно працює. отрже довжина пакету 33 байти. налаштування
........
c_cc[VMIN]=33;
c_cc[VTIME]=1;
....
у функції mb_getint прибрав три виклики read, залишив тільки один. поки на тому кінці відповідали все було добре, а от як  мовчок - програма блокується, цього мені ніяк допустити неможна.
ПС: аналог PurgeComm  - tcflush
« Змінено: 2008-09-02 20:12:38 від vano_vvv »

maranoid

  • Гість
Re: програмування СОМ-порта
« Відповідей #13 : 2008-09-03 10:00:14 »
А якщо спробувати fcntl(fd, F_SETFL, FNDELAY)?


Відсутній vanessa

  • Графоман
  • ****
  • дописів: 468
  • Карма: +0/-0
Re: програмування СОМ-порта
« Відповідей #14 : 2008-09-03 11:14:03 »
пробував. тоді воно працює в неблокованому режимі і на VMIN та VTIME йому начхати. якщо я сам витримую паузу 40мілісекунд після виклику write, а потім роблю read - все нормально, відповідь або вже є або немає. тільки мені чомусь такий підхід неподобається. може тому що втрачається зайвих 5-10 мілісекунд. Деколи цього часу може не вистасчити