Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: COM-порт - МК
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Tiny
Вопрос к тем, кто работал по данной теме.
100 устройств. На каждом установлен по одному MEGA16, которые должны при поступлении команды от компьютера сбросить один из 13 пинов устройства на ноль.
Реализовал следующим образом. Компьютерную часть программы писал на Borland Builder C++. На удаленном компьютере нажимается кнопка, соответствующая команда (3 знака - номер устройства + 2 знака - номер пина, например 00101 - 1-е устройство, 1 пин) поступает на сервер. С сервера через СОМ-порт на UART МК.
Для МК писал в CodeVision AVR. При получении сигнала происходит прерывание UART, полученные данные перекодируются из кода ASCII в десятичные и сохраняются. Если номер устройства совпал с реальным номером данной платы (задается положением переключателей на потре B ), то анализируются полученные данные. Соответствующий пин порта А или С сбрасывается на 0, обратно для подтверждения отправляется полученный код, предварительно перекодировав его обратно в код ASCII из десятичных.
МК работает с кварцевой стабилизацией 14400кГц. (Реально стоит 14,31818, другого пока не нашел). Скорость UART 14400кГц.
С одним МК работает. Когда достану преобразователь RS232-RS485 - подключу в линию другие устройства.
Возникает вопрос. Когда будут подключены 100 устройств на 100МК и с Интернета на сервер будут одновременно обращаться десятки команд, будет ли все это работать?
При передаче команды вначале идет старт-бит, данные, стоп-бит. В эту процедуру никто посторонний не влезет. Так для передачи 00101 от 1-го пользователя эта процедура должна произойти 5 раз. А не получится ли так, что после передачи двух младших разрядов сюда вклинется другой пользователь передающий команду, допустим 05603?
Прохожий
Цитата(Tiny @ Aug 22 2010, 16:31) *
Вопрос к тем, кто работал по данной теме.

ПМСМ, придется все переделать.
Начать изучение этого дела лучше отсюда.
Не сочтите за снобизм, но в этой области "все уже украдено до нас"(С).
Вам надо просто выбрать необходимый протокол и выполнить все его требования, как физического уровня, так и всех остальных уровней.
Xenia
Цитата(Tiny @ Aug 22 2010, 15:31) *
А не получится ли так, что после передачи двух младших разрядов сюда вклинется другой пользователь передающий команду, допустим 05603?

Коллизия не возникнет, если вы правильно напишите алгоритм работы сервера. Выход сервера должен быть буферизирован. Т.е. он не должен отправлять команды аппаратам в тот же момент, как приходит команда от клиента, а должен складывать сформированные команды в (циклический) буфер FIFO. А физическая передача на аппаратную "шину" производиться уже из буфера, причем в порядке ОБЩЕЙ ОЧЕРЕДИ.
Поэтому, заказ клиента будет дешифроваться, превращаться в команды, а затем помещаться в буфер. Причем следует особо озаботиться тем, чтобы команда аппарату писалась в буфер не по частям, а сразу в один присест. Тогда если у вас приложение однопоточное, то коллизии с наложением байтов не возникнут, а если многопоточмное, что придется сделать какой-нибуть семафор, чтобы не позволял другому процессу писать в буфер, пока в него не окончит запись другой. Такая ситуция типична для многих многопроцессорных алгоритмов.
Короче говоря, вам надо создать очередь на вывод в виде буфера и организовать в него запись исключительно целыми командами. Сделать это всегда возможно, т.к. в момент формирования команды все ее байты полностью известны.

Можно поступить и по-другому, учитывая, что посылка от клиента занимает всего один байт, - класть в буфер однобайтные запросы от клиентов, а в содержимое буфера обрабатывать в порядке очереди, преобразуя байт-запрос в командную посылку, и тут же ее отправлять. Тогда коллизий тоже не возникнет, но какой-то арбитраж на буфере все равно понадобится, т.к. придется следить за тем, чтобы его счетчик очереди инкреметировался правильно. В конце концов то на то и выйдет.
Tiny
Цитата(Xenia @ Aug 22 2010, 16:43) *
Можно поступить и по-другому, учитывая, что посылка от клиента занимает всего один байт, - класть в буфер однобайтные запросы от клиентов, а в содержимое буфера обрабатывать в порядке очереди, преобразуя байт-запрос в командную посылку, и тут же ее отправлять.

Получить один байт и сразу его отправить не получится.
Посылка одного разряда кода занимает один байт. Но всего в коде таких разрядов 5 (00101). В коде информация об адресе назначения, но не отправителя. В момент между разрядами от одного клиента может попасть разряд от другого.
Я вношу полученные данные в переменные. Собираются все разряды, анализируется этому ли устройству адресовалось и только то устройство, которому адресовалось выполняет команду и отправляет код обратно.
Если я правильно понял, как вы писали в начале своего поста, нужно в программе сервера внести буферизацию полученных данных от клиентов, и не допускать попадания в UART данных от одних клиентов, пока не закончится процесс передачи- получения отклика от другого.
SSerge
В терминах OSI Вы должны над физ.уровнем надстроить второй - канальный, и рассматривать свои последовательности из нескольких байт как кадры канального уровня (хотя их чаще называют пакетами).
Этот уровень работает только с кадрами как с единым целым и не допускает перемешивания их в канале. Это в теории всё выглядит заумно, а на самом деле нужно просто передавать кадры не побайтно, а запросами типа WriteFile() или fwrite(), они всё это и обеспечат, и буферизацию внутри тоже.

Но с RS-485 встанет ещё один вопрос, до которого изобретателям OSI дела не было, необходимо обеспечить своевременное переключения направления передачи, по науке это называется управление доступом к среде передачи и считается частью канального уровня ( MAC, Media Access Control).
Сразу скажу, что на PC это реализовать с приемлемыми временами почти невозможно, поэтому это обычно делается аппаратно в преобразователе RS232-RS485. В любом случае устройства, подключенные к RS-485 должны выдерживать паузу перед тем, как передавать свой ответ.
defunct
Цитата(Tiny @ Aug 22 2010, 15:31) *
Так для передачи 00101 от 1-го пользователя эта процедура должна произойти 5 раз. А не получится ли так, что после передачи двух младших разрядов сюда вклинется другой пользователь передающий команду, допустим 05603?

Вы же пишете программу для сервера, получаете команды от пользователей.
Обеспечте атомарность выполнения каждой команды. - не слать другую команду устройствам до тех пор пока не получен ответ на текущую.


Цитата(Tiny @ Aug 22 2010, 18:25) *
Посылка одного разряда кода занимает один байт. Но всего в коде таких разрядов 5 (00101). В коде информация об адресе назначения, но не отправителя. В момент между разрядами от одного клиента может попасть разряд от другого.
Я вношу полученные данные в переменные. Собираются все разряды, анализируется этому ли устройству адресовалось и только то устройство, которому адресовалось выполняет команду и отправляет код обратно.


Есть 100 TCP сокетов - клиенты, и есть 100 исполнительных устройств.

Заводите как минимум два треда. Один обслуживает сокеты, второй - uart.
Создаете некую Cmd структуру, которая может быть например такой:

Код
typedef struct tagCMD
{
    PTCP_SOCKET  socket;
    U8  cmd[ MAX_CMD_LEN ];
    int  cmd_len;
    
    U8  resp[ MAX_RESP_LEN ];
    int  resp_len;
} CMD, *PCMD;


далее принимаете любую "хню" из некоего сокета-клиента, выделяете структурку и заполняете ее как-то так:

Код
OnSocketReadEvent( PTCP_SOCKET socket ...)
{
    pCmd  = (PCMD)Heap_Alloc( sizeof( CMD) );
    pCmd->socket = socket;  // <--- это клиент, который послал команду
    pCmd->cmd_len = socket->bytes_to_read;
    socket->read( pCmd->cmd, pCmd->cmd_len);
    ...


ставите ее в очередь на исполнение для другого треда:
Код
    enqueue( GlobalCmdQueue, pCmd);
}



в другом треде, делаете буквально так:

Код
while(TRUE)
{
    // спим пока нет события записи в очередь
    GlobalCmdQueueEvent->WaitFor( infinite );

    while ( (pCmd = dequeue( GlobalCmdQueue)) != NULL )
    {
        // отправка на исполнительный девайс
        Com->write( pCmd->cmd, pCmd->len);
        ...
        // чтение ответа от девайса
        Com->read( pCmd->resp, &pCmd->resp_len);

        // отправка результата клиенту
        pCmd->socket->write( pCmd->resp, pCmd->resp_len);

        // освобождение памяти
        Heap_Free( pCmd );
    }
}



Вот собсно и все...
Tiny
Спасибо большое всем за помощь. Буду переваривать всю полученную информацию.
demiurg_spb
Посмотрите что такое OPC-Server...
http://ru.wikipedia.org/wiki/OPC
http://www.opcfoundation.org
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.