С Новым годом! Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18

Опять про COM порт

26.04.2017, 14:38. Показов 1433. Ответов 18
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте. Наверняка данный вопрос уже неоднократно появлялся, но т.к. я к сожалению не программист, а скорей "железяньщик", то заранее прошу простить. Итак есть железка, которая принимает команды по последовательному порту, а потом по нему же передаёт некоторые данные, предварительно упакованные в пакет. Количество пакетов передаваемых между командами не ограничено. С портом я работаю с помощью ф-ций WINAPI (create file и т.д.). Всего в программе 3 потока (1 записи в порт, 1 чтения из порта и 1 активируется потоком чтения и осуществляет отображение данных в Memo и ListBox). Так вот мне понадобилось отображать содержимое принятых пакетов в строку (точнее 1пакет, 1 строка). Я сделал это следующим образом:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DWORD WINAPI Thread_ListBox(LPVOID) // поток вывода данных в Memo и ListBox
  {
    String str;
     while ( TRUE )
     {
        WaitForSingleObject( hEventListBox, INFINITE ); // Ожидаем пока поток чтения активизирует данный поток
        
        str = NULL;                 // Обнуляем промежуточный буфер для преобразования столбик -> строка    
        
        for(int i = 0; i < btr1; i++)
        {
            Form1 -> ListBox1 -> Items -> Add(IntToHex((int)BufRx[i], 2));  // В ListBox выводим данные в столбик 
            str += IntToHex(BufRx[i],2);                    // Заполняем наш временный буффер столбик -> строка
        }
        Form1 -> Memo1->Lines-> Add( str );                     // Выводим данные в строчку в Memo1
 
        
     }
  }
Но как известно COM очень рандомно определяет сколь ко же ему пришло данных и соответственно рандомно их и выплёвывает (скажем послал я ему 10 байт, а он выплюнул ,например, сначала 8 потом 2). Ну и естественно длинна строк ползёт (в какой то пол пакета а в какой то их 3 и т.д.). Количество байт в пакете я в принципе знаю и посчитать наверное смогу, но вот если вдруг чугуняка перестанет передавать данные (провод отвалится или ещё что то хитрое), то программка встанет пока ей не придёт необходимое количество байт. Так вот понятно, что нужно поставить какой то таймер, но вот как бы это получше сделать не совсем понятно. Может кто то сможет подсказать?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
26.04.2017, 14:38
Ответы с готовыми решениями:

Опять про связь таблиц
Как бы покороче. Есть две таблицы 1. Sector(Name, Code) сектор 2. Line(Name, Code, Sector) линия Вторая таблица связана с...

Опять COM-порт
Для чтения данных с компорта создаю вспомогательный поток, далее как обычно: OverlapPort.hEvent = CreateEvent( NULL, false, true, NULL );...

опять про ПР
Если у меня 1 страничный сайт и с index.php идут 12 локальных линков на форум, фурум я не яндексирую. Хочу удержать PR на морде, как мне...

18
Эксперт .NET
 Аватар для Rius
13069 / 7630 / 1669
Регистрация: 25.05.2015
Сообщений: 23,202
Записей в блоге: 14
26.04.2017, 15:58
Можно читать входящие данные фукцией ReadFile с overlapped и WaitForSingleObject. Тогда можно получить либо завершение операции (прочитано указанное число байт), либо таймаут. Таймаут можно задать больше, чем разбрасывает COM порт, но меньше, чем между посылками.

Добавлено через 3 минуты
Вот пример
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
BOOL read(BYTE *buffer, DWORD bytesToRead, DWORD *bytesReaded, DWORD timeout)
{
    BOOL result = FALSE;
 
    *bytesReaded = 0;
    DWORD dwRead, dwWait;
    OVERLAPPED sync = { 0 };
 
    // Создаём событие для контроля за асинхронным чтением
    sync.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
 
    // Если событие создано без ошибок
    if (sync.hEvent != NULL) {
        // Начинаем чтение…
        if (ReadFile(this->mPort, buffer, bytesToRead, &dwRead, &sync)) {
            // Операция чтения уже завершилась,
            // можно обрабатывать полученные данные
            *bytesReaded = dwRead;
            result = TRUE;
        } else {
            if (GetLastError() != ERROR_IO_PENDING) {
                // Ошибка чтения данных
            } else {
                // Ожидаем завершение операции чтения
                dwWait = WaitForSingleObject(sync.hEvent, timeout);
 
                switch (dwWait) {
                    case WAIT_OBJECT_0: { // Операция чтения закончена
                        if (GetOverlappedResult(this->mPort, &sync, &dwRead, FALSE)) {
                            // Обработка полученных данных
                            *bytesReaded = dwRead;
                            result = TRUE;
                        } else {
                            // Ошибка выполения операции
                        }
 
                        break;
                    }
 
                    case WAIT_TIMEOUT: {
                        // Операция чтения данных ещё не закончилась
                        // можно занятся чем-нибудь полезным
                        break;
                    }
 
                    default: {
                        // Ошибка выполнения WaitForSingleObject
                        break;
                    }
                }
            }
        }
 
        CloseHandle(sync.hEvent);
    }
 
    return (result);
}
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
26.04.2017, 17:54  [ТС]
Цитата Сообщение от Rius Посмотреть сообщение
Таймаут можно задать больше, чем разбрасывает COM порт, но меньше, чем между посылками.
Спасибо за ответ. Но скажите, а если пакеты идут практически один за другим...
0
Эксперт .NET
 Аватар для Rius
13069 / 7630 / 1669
Регистрация: 25.05.2015
Сообщений: 23,202
Записей в блоге: 14
26.04.2017, 17:55
Если это пакеты, они должны быть как-то разделены. Это либо время (таймаут), либо содержимое (типа CR LF).
Время вы можете по осциллографу посмотреть. Содержимое - в сниффере или протоколе обмена.
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
26.04.2017, 18:34  [ТС]
Ясно, спасибо!
0
Эксперт .NET
 Аватар для Rius
13069 / 7630 / 1669
Регистрация: 25.05.2015
Сообщений: 23,202
Записей в блоге: 14
26.04.2017, 18:38
В случае, если разделение в содержимом, надо считывать постоянно и пополнять некий самодельный буфер FIFO. Как только он заполнился одним пакетом, т.е. в нём есть и маркер начала, и маркер конца, то из буфера пакет вычитывается и используется.
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
26.04.2017, 21:36  [ТС]
А вот за мысль с fifo отдельное спасибо. Пакеты действительно отличаются содержимым а вот идут почти один за другим. Я прочитал про таймауиы для com порта, но не совсем понял как мне это может помочь, а вот с вашем кодиком всё стало намного яснее. Завтра с утра поэксперементирую.
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
28.04.2017, 10:59  [ТС]
Снова здравствуйте. Опять появилась проблема с портом. Уму не приложу в чём дело. В общем железяка работает нормально, поэтому пока с таймаутаи решил не заморачиваться. Но появилась другая напасть. Каждый пакет, который мне приходит состоит из 14 байт, при этом я знаю, что каждый пакет начинается с цифры 0х02. Т.к. в содержимом пакета такая цифра то же может появиться, решил сделать синхронизацию следующим образом: выделить 42-х байтный буфер (3х14) а в функции чтения порта указал 14 байт. Когда с порта приходят данные, я записываю их в старшие 14 байт выделенного буфера. Далее я проверяю равны ли 0 и 14 байты буфера 0х2. Если да, то делаю вывод, что за синхронизировался и выдаю данные. Если нет, то, сдвигаю весь 42-байтный блок на 1 в сторону младших разрядов и снова проверяю. Эта операция повтаряется 14 раз (пока младшие 14 байт полностью не выдвинутся из буфера). Вот мой код потока обработки:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
DWORD WINAPI Thread_ListBox(LPVOID)
{
    
    while ( TRUE )
    {
        WaitForSingleObject( hEventListBox, INFINITE ); // Ожидаем пока поток чтения активизирует данный поток
        String str;                 // Обнуляем промежуточный буфер для преобразования столбик -> строка    
        
        for (int i=0; i < 14; i++)          // Записываем принятые 14 байт в старшие регистры рабочего буффера 
        {WorkBufRx[i+28] = BufRx[i];}       
        
        bool sync = 0;          // флаг синхронизации
        
        for (int i=0; i<14; i++)    // сдвигаем 28-ми байтный блок рабочего буфера на 14 байт
        {
            if ((WorkBufRx[0] == 0x02) & (WorkBufRx[14] == WorkBufRx[0]))   // проверяем, что засинхронизировались
            {sync = 1;break;}                       // если засинхронизировались выдаём данные
            for(int i=0; i< 28; i++)                    // сдвигаем 28-ми байтный блок на 1 позицию               
            {WorkBufRx[i] = WorkBufRx[i+1];}
        }
        
        if (sync)               
        {
            for(int i = 0; i < 14; i++)
            {
                Form1 -> ListBox1 -> Items -> Add(IntToHex((int)BufRx[i], 2)); // В ListBox выводим данные в столбик  
                str += IntToHex(WorkBufRx[i],2);        // Заполняем наш временный буффер столбик -> строка 
            }
            
            
            for(int i=0; i < 28; i++)
            {WorkBufRx[i] = WorkBufRx[i+14];}         // Выдвигаем из буфера отработанный пакет
            Form1 -> Memo1 -> Lines-> Add( str );   // Выводим данные в строчку в Memo1
                    
        }
    }
}
Сначала всё работает хорошо, но вот затем... Затем поток отображаемый в Memo замедляется, а затем и вовсе прекращается. Я посмотрел, на то, что выводится, так вот железяка исправно шлёт пакеты, а вот COM порт с определённого момента начинает их рвать совсем не постижимым образом. В чём может быть дело. Может я не правильно настроил порт, куда лезть, на что внимание обратить? При этом, если порт закрыть, а потом опять открыть, работа возобновляется в прежнем режиме, но вскоре снова всё замерает
0
Эксперт .NET
 Аватар для Rius
13069 / 7630 / 1669
Регистрация: 25.05.2015
Сообщений: 23,202
Записей в блоге: 14
28.04.2017, 11:12
От сдвига избавьтесь. Есть такой способ как кольцевой буфер.
У вас отдельный поток вроде есть. Читайте в нём не однократно 14 байт, а постоянно: 14, 14, 14,... И всё принятое записывайте в хвост вышеуказанного буфера.
Добавьте такое понятие, которое назовём "окном проверки". В него будут попадать первые 14 байт от головы буфера.
После каждого завершения чтения, или по таймауту, проверяйте наличие в окне набора байт, подходящего под описание пакета.
Если не подходит, первый байт из головы выбрасываете (окно сдвигается далее), и проверяете снова.
Если подходит, забираете из буфера и обрабатываете.

А вообще вот такого:
Цитата Сообщение от Грендайзер Посмотреть сообщение
каждый пакет начинается с цифры 0х02. Т.к. в содержимом пакета такая цифра то же может появиться
не должно быть. Такие протоколы должны иметь способ разделения пакетов.
Приведите описание протокола.
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
28.04.2017, 11:39  [ТС]
Разобрался. В строке
C++
1
for(int i=0; i< 28; i++)    // сдвигаем 28-ми байтный блок на 1 позицию
необходимо заменить 28 на 42, иначе сдвигается лишь часть буфера.
От сдвига избавьтесь. Есть такой способ как кольцевой буфер.
Большое спасибо. Вот это очень полезно, а то конечно тасавать буферы не очень хорошо.
А вообще вот такого:
Цитата Сообщение от Грендайзер Посмотреть сообщение
каждый пакет начинается с цифры 0х02. Т.к. в содержимом пакета такая цифра то же может появиться
не должно быть.
Наверное Вы правы, но железяка не моя... Сама она на плисине, я лишь проверяю работоспособность схемы, которая в неё зашита (код то же не мой). Я лишь к коду который делает свою работу присобачил ответную часть, которая позволяет управлять основной схемой, по uart. Основной протокол, такой как есть, сначала 0x02 потом служебная информация (в ней правда есть адрес приёмника, то биш мой и можно ещё и его наверное проверять) но в целом, т.к. моя часть в основную схему не пойдёт, а нужна лишь для проверки, то и выдумывать я ничего не стал, тем более время поджимает, а в программировании под ОС я если честно не особо.

Добавлено через 11 минут
Rius, а Вы не могли бы поподробней объяснить, как читать голову буфера, если я пакеты записываю ему в хвост, так или иначе мне сдвигать то всё и так и так придётся...
0
Эксперт .NET
 Аватар для Rius
13069 / 7630 / 1669
Регистрация: 25.05.2015
Сообщений: 23,202
Записей в блоге: 14
28.04.2017, 11:49
Ссылку посмотрите.
Там ничего не сдвигается. Меняются только индексы головы (чтения) и хвоста (записи) .
1
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
28.04.2017, 11:51  [ТС]
Ясно, спасибо ещё раз.
0
Эксперт С++
 Аватар для Avazart
8484 / 6151 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
02.05.2017, 21:11
Цитата Сообщение от Грендайзер Посмотреть сообщение
Так вот понятно, что нужно поставить какой то таймер, но вот как бы это получше сделать не совсем понятно. Может кто то сможет подсказать?
1. Нельзя из потока обращаться к компонентам и их свойствам напрямую без синхронизации потоков.
2. Вместо WinApi лучше попробуйте использовать компонент(модуль) BComPort. (Или вообще другой фрейморк/ЯП)
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
02.05.2017, 23:03  [ТС]
А можете поподробней рассказать или какую нибудь ссылочку дать почитать... Или каким нибудь примерчиком угостить. А то про COM порт везде одно и тоже, открываем, передаём байтик, его же сразу же читаем, закрываем порт. Всё! Для начала, конечно не плохо, но практического применения никакого...
0
Эксперт С++
 Аватар для Avazart
8484 / 6151 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
03.05.2017, 10:58
Цитата Сообщение от Грендайзер Посмотреть сообщение
А то про COM порт везде одно и тоже, открываем, передаём байтик, его же сразу же читаем, закрываем порт. Всё!
Так а большего и не нужно.
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
03.05.2017, 15:24  [ТС]
Цитата Сообщение от Avazart Посмотреть сообщение
Так а большего и не нужно.
Для того что бы принять байтик наверное да...
0
Эксперт С++
 Аватар для Avazart
8484 / 6151 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
03.05.2017, 15:50
Так в других случаях все просто, алгоритм таков:

1. Включаем мозг.
2. Смотрим пример с принятием байтика.
3. Думаем головой (и не чем другим).
4. И реализуем код на его основе.
5. Можем выключать мозг )))
0
Практикантроп
 Аватар для nick42
4841 / 2726 / 534
Регистрация: 23.09.2011
Сообщений: 5,798
03.05.2017, 15:55
Цитата Сообщение от Грендайзер Посмотреть сообщение
поподробней рассказать или какую нибудь ссылочку дать почитать...
bitte_ Асинхронное чтение данных из COM порта
0
0 / 0 / 0
Регистрация: 10.12.2016
Сообщений: 18
03.05.2017, 17:36  [ТС]
Цитата Сообщение от nick42 Посмотреть сообщение
bitte_ Асинхронное чтение данных из COM порта
Ух ты... Спасибо, ознакомимся
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
03.05.2017, 17:36
Помогаю со студенческими работами здесь

Опять про ПР... :/
Вот у меня опять назрел вопрос, который пытаюсь уже давно выяснить... Для примера возьмем сайт на WP (все, думаю, знают, что это) ...

Опять про layout
Всех приветствую! Извиняюсь, но проведя 3 часа в попытках разобраться с layout я в конец запутался, поэтому вынужден обратится здесь...

Опять про камеру
Доброе время дня! На старости лет решил попробовать изучить Xamarin FORMS на C#. Передо мной стоит следующая задача и я ни как не могу с...

Опять про массивы
Всем привет. Подружка попросила помочь ее сыну решить пару лабораторных по информатике на vba, из 6 задач с четырьмя справилась, а вот...

Опять же про сапу
Ув сео. Я купила на сапе примарно 150 тематических сылок, с ключевыми словами новости, и новости сми. Эти сылки уже проиндексировались, и...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Новые блоги и статьи
сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и источниками (напряжения, ЭДС и тока). Найти токи и напряжения во всех элементах. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru