|
Работа с COM портом с++ builder, Есть небольшие проблемы при работе своего устройства с СОМ |
|
|
|
Nov 16 2016, 19:39
|

Частый гость
 
Группа: Свой
Сообщений: 114
Регистрация: 14-08-07
Из: Харьков, Украина
Пользователь №: 29 773

|
Здавствуйтке. Уже несколько дней, а то и недель занимаюсь тем, что изучаю, читаю, пробую, эксперементирую, но так положительного резутата и не получил, посему хочу обратиться к специалистам этого форума. Постановка задачи примерно следующая, есть устройство, мною сделанное, требуется вычитать из него данные в ПК для дальнейшей обработки. Устройство работает по RS-232. На стороне ПК стоит преобразователь USB-RS232TTL (какой именно, если это принципиально, могу посмотреть). Обмен реализован следующим образом: ПК посылает команду начала передачи данных. после приема команды на передачу устройство передаёт первый пакет . ПК принимает, анализирует его если всё ОК, то высылает байт подтверждения, ( так же может послать останов передачи, повтора пакета). устройство формирует новый пакет исходя из принятого байта. Вроде всё работает более менее нормально, за исключением следующего. При вычитке данных из буфера время от времени происходит какая-то ситуация при которой вычитываются данные со смещением и тогда происходит ошибка анализа пакета, посылается запрос на повтор пакета, и опять вычитка со смещением и так циклически. До останова передачи данных. Для раброты с КОМ-портом использую AsyncPro. Функции вычитки байта и пакета дынных привожу ниже Код unsigned char TReadBuffer::ReadByte(char *byte, short int TimeOut) { //Ожидаем ответа TimeCounter = TimeOut; TimeOutCounter->Enabled=true;
while (ComPort->CharReady()==false) { if (TimeCounter==0) { return erTimeOut;} Application->ProcessMessages(); } *byte = ComPort->GetChar(); return erNoError; }
//---------------------------------------------------------------------------
short int TReadBuffer::RecivePacket(unsigned char *buffer, unsigned char Count) { unsigned char b; for (short int i=0; i<Count; i++){ if (ReadByte(&b, ReadByteTimeOut) == 0) { *buffer=b; buffer++;} else return erTimeOut; } ComPort->FlushInBuffer(); return erNoError; } //--------------------------------------------------------------------------- ComPort->FlushInBuffer(); - поставил дабы при сбое очистить буфер и начать всё сначала. Пробовал ставить как в данный модуль или ставил при возникновении CRC-error... Результат приммерно одинаковый, раз в какое-т овремя происходит смещение чтения.... Не могу понять почему. Понимаю, что где-то неправильно организованы контроль наличия байта в буфере и чтение, но не могу понять где и что.... AsyncPro позволяет писать логи того что принимается и передаётся, согласно задумке. пакеты передаются правильно. после приёма правильного пакета, передаётся байт необходимости повтора пакета, получается точно такой же пакет! и уже отправляется байт что всё ОК, потом несколько пакетов передаются нормально, и опять .... Пакеты короткие по 7 байт. 57 32 0B 10 00 00 72 00 3A 02 32 01 00 6B 61 62 63 64 02 00 D4 65 66 67 68 03 00 66 B7 58 84 01 04 00 6E 03 B7 58 8E 05 00 EA 8E 05 00 EA 01 E4 B7 58 06 00 11 01 E4 B7 это то что получается в buffer. до "разыва" всё идёт хорошо, а вот следующий пакет уже содержит часть предыдущего 8E 05 00 EA . Пакет должен был выглядеть 01 E4 B7 58 06 00 11 Не знаю доступно ли я изложил свою проблему, но она есть и я не знаю как её побороть, т.к. опыта в программировании малова-то. Большое спасибо за помощь
--------------------
Жизнь сложна и не предсказуема, незачем её усложнять.
|
|
|
|
|
 |
Ответов
|
Nov 16 2016, 22:03
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(Diko @ Nov 16 2016, 23:39)  short int TReadBuffer::RecivePacket(unsigned char *buffer, unsigned char Count) { unsigned char b; for (short int i=0; i<Count; i++){ if (ReadByte(&b, ReadByteTimeOut) == 0) { *buffer=b; buffer++;} else return erTimeOut; } ComPort->FlushInBuffer(); return erNoError; } //---------------------------------------------------------------------------[/code] Закладываться на таймауты чтения по порту, даже не засекая реальное время - это вы слишком игриво работаете. поэтому и получаете что имеете. ваш протокол должен иметь логически детерминированную структуру позволяющую определить начало и конец или длинну фрейма. если вы закладываетесь на паузы - то их надо реально измерять и однозначно задавать на обоих концах интерфейса, вот как это делает модбас. но мадбас - сильно затратный по ресурсам процессора протокол, как раз изза контроля таймингов. дорабатывайте протокол - надо четко определять начало фрейма и его длинну. в вариантах что я встречал в качестве заголовка фрейма используется какойто маркер - скажем 1-2 байта констанных. это уже сильно вас выручит.
|
|
|
|
|
Nov 17 2016, 09:16
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(zltigo @ Nov 17 2016, 12:43)  . . . . Так что протокол менять однозначно, а не бороться до конца жизни с тем, что надежный фреймиг по небольшим таймаутам под Win нереализуем в принципе. (?) А если в Win таки реализовывать. Какой порядок (размер) таймаутов можно использовать ? (?) Мне это танцы с бубном надоели, думаю делать внешний "буферизатор" на контроллере, обеспечивающий требуемый реалтайм для фрейминга, а для "закачки" в PC задействовать CTS-RTS. Это правильное решение ?
|
|
|
|
|
Nov 17 2016, 17:05
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(k155la3 @ Nov 17 2016, 13:16)  А если в Win таки реализовывать. Какой порядок (размер) таймаутов можно использовать ? таймауты 5мс (банальным sleep ом) - там получаются вполне нормально, но нестабильно - будут время от времени вылезать 15-20мс паузы. если вы повысите приоритет задачи - то возможно удасться избавиться от таких выпадений. в этом плане линух оказался менее предсказуемым (ядро 2й версии) - там непредсказуеые лаги я получал по 50мс. если будете пользоваться настройками DCD как вам выше расписали - то они реализуются скорее всего ядром, и разрешение 1мс они вполне реализуют. для засекания времени нужны не таймауты а измерение времени - эта штука гораздо более проворная и малозатратная. для точного засекания времени у венды тоже есть стандартные средства, и вы найдете пачку библиотек и примеров типа Ultra High Precision Timing для венды. Цитата Мне это танцы с бубном надоели, думаю делать внешний "буферизатор" на контроллере, обеспечивающий требуемый реалтайм для фрейминга, а для "закачки" в PC задействовать CTS-RTS. Это правильное решение ? чем городить такое лучше поправить протокол - для этого не нужно городить железо, и оно будет надежно работать почти везде. неужели невозможно ввести маркер заголовка? CTS-RTS вам поможет мало. лучше посмотрите в сторону линии DTR-DSR - кажется они дают прерывание и можно повесить обработчик этого события. но вы покладете комп на обработку этих прерываний если они будут лететь интенсивно, а судя по размеру ваших пакетов - они будут свистеть интенсивно. - на вендовой (да и на линуховой) машине гораздо быстрее будет работать обмен с маркером чем с прерыванием.
|
|
|
|
|
Nov 18 2016, 10:22
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(AlexRayne @ Nov 17 2016, 20:05)  . . . . ( 1) чем городить такое лучше поправить протокол - для этого не нужно городить железо, и оно будет надежно работать почти везде. неужели невозможно ввести маркер заголовка?
(2) CTS-RTS вам поможет мало. лучше посмотрите в сторону линии DTR-DSR - кажется они дают прерывание и можно повесить обработчик этого события. . . . . (1) - протокол бинарный, перадаются в том числе числа int_32, double(64), соотв-но маркер получится слегка длинноватый. Очевидно (8+1) байт. Надо будет это осмыслить насчет целесообразности. Зато можно будет работать "потоком". (2) спасибо за инф. Я тестилку делал на базе CTS-RTS, но по опросу. До прерываний будет время доберемся. Цитата(Diko @ Nov 17 2016, 22:38)  . . . . Пакеты коротки не потому что они идут с большой скоростью, а просто так сложилось .... . . . . Если пакеты короткие (5-10-20 байт), можете использовать следующий изворот. 1. Все данные передаются как 7-битные (т.е. байт с нулевым старшим битом) 2. Все заголовки пакетов, концевики, и прочие служебные символы, требуемые для фрейминга, передаются с установленным старшим битом. 3. Как впихнуть 8-битные данные в 7 бит ? Очень просто. Сперва в виде байтов c очищенным D7 передаются разряды данных D0-D6. Потом - все "непереданные" D7-биты данных пакуются в требуемое кол-во байт последовательно, в биты D0...D6 Длина тела пакета (данных) получается Data_len+(data_len/7) байт. Недостаток - затраты времени на (рас)паковку и избыточность. Затраты RAM на всеэто. Достоинства - поточный разбор, 128 возможных "служебных" кодов, включая старт/стоп пакета STX ETX
|
|
|
|
|
Nov 18 2016, 10:48
|
Гуру
     
Группа: Модераторы
Сообщений: 4 011
Регистрация: 8-09-05
Из: спб
Пользователь №: 8 369

|
Цитата(k155la3 @ Nov 18 2016, 13:22)  1. Все данные передаются как 7-битные (т.е. байт с нулевым старшим битом) 2. Все заголовки пакетов, концевики, и прочие служебные символы, требуемые для фрейминга, передаются с установленным старшим битом. 3. Как впихнуть 8-битные данные в 7 бит ? Очень просто. Сперва в виде байтов c очищенным D7 передаются разряды данных D0-D6. Потом - все "непереданные" D7-биты данных пакуются в требуемое кол-во байт последовательно, в биты D0...D6 Длина тела пакета (данных) получается Data_len+(data_len/7) байт.
Недостаток - затраты времени на (рас)паковку и избыточность. Затраты RAM на всеэто. Достоинства - поточный разбор, 128 возможных "служебных" кодов, включая старт/стоп пакета STX ETX Это сложно и так делать не советую. Уж если передавать байт данных двумя посылками в линию, то передавайте его стандартными символьными кодами. А "возврат каретки + перевод строки" - это стандартный же разграничитель кадра. И можно работать с любой терминалкой...
--------------------
www.iosifk.narod.ru
|
|
|
|
Сообщений в этой теме
Diko Работа с COM портом с++ builder Nov 16 2016, 19:39 Diko Покопался в том что у меня происходит и вот что ув... Nov 16 2016, 21:29 AlexRayne Цитата(Diko @ Nov 17 2016, 01:29) B вот к... Nov 16 2016, 21:54 AlexandrY Функция ReadByte для AsyncPro написана идеологичес... Nov 16 2016, 22:02    Ruslan1 Цитата(k155la3 @ Nov 17 2016, 11:16) А ес... Nov 17 2016, 09:35       k155la3 Цитата(iosifk @ Nov 18 2016, 14:48) Это с... Nov 18 2016, 11:05        AlexandrY Цитата(k155la3 @ Nov 18 2016, 13:05) Если... Nov 18 2016, 11:10         k155la3 Цитата(AlexandrY @ Nov 18 2016, 15:10) Та... Nov 18 2016, 11:26   AlexandrY Цитата(zltigo @ Nov 17 2016, 10:43) В ого... Nov 17 2016, 10:19    k155la3 Цитата(AlexandrY @ Nov 17 2016, 14:19) . ... Nov 17 2016, 11:15    zltigo QUOTE (AlexandrY @ Nov 17 2016, 12:19) На... Nov 17 2016, 19:12 Diko Цитата(AlexRayne @ Nov 17 2016, 00:54) во... Nov 17 2016, 06:28 k155la3 Цитата(Diko @ Nov 16 2016, 23:39) Здавств... Nov 17 2016, 07:05 AlexandrY Цитата(k155la3 @ Nov 17 2016, 09:05) Если... Nov 17 2016, 07:20 Ruslan1 Вот кусок кода из работающей программы, лет много ... Nov 17 2016, 07:53 Diko Цитата(k155la3 @ Nov 17 2016, 10:05) без ... Nov 17 2016, 17:55 Diko Цитата(zltigo @ Nov 17 2016, 11:43) В ого... Nov 17 2016, 19:38 iosifk Цитата(Diko @ Nov 17 2016, 22:38) И да эт... Nov 17 2016, 19:50  zltigo QUOTE (iosifk @ Nov 17 2016, 21:50) Прото... Nov 17 2016, 20:36  Ruslan1 Ага. я тоже за SLIP RFC1055.
обрамил кодом команды... Nov 17 2016, 20:46 AlexandrY Цитата(Diko @ Nov 17 2016, 21:38) Как изм... Nov 18 2016, 07:55  Diko Цитата(AlexandrY @ Nov 18 2016, 10:55) В ... Nov 18 2016, 20:49 Diko Спасибо. Буду поизучать в этом направлении. Nov 17 2016, 20:15 Diko Всем откликнувшимся огромное спасибо.
Благодаря эт... Nov 19 2016, 15:43
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|