CAN или не CAN? Или зачем мне сеть микроконтроллеров?

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


    Сейчас многие предпочтут щитам дисплейные панели. Но будут ли любители дисплеев в большинстве, зависит от многого, нам неведомого. Но сейчас речь не об этом.


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


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


    И вот, помнится, глянул за окно, а там 21-й век. Стало быть надо все делать заново и по-другому. Вместо лампочек — экономичные светодиоды. Вместо проводов — проводки. Вместо одного ящика — много-много маленьких ящичков, контроллеров, стало быть.


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


    К тому времени я уже знал, что такое CAN и уровень моего уважения к фирме Bosch был много выше, нежели у повара приличного ресторана или аккуратной домохозяйки. А производители автомобилей BMW, я уверен, даже ходили к инженерам Bosch в гости.


    Controller Area Network, как сказали бы иностранцы, на мой взгляд, как техническое решение, возникло из желания сделать что-то, наконец, хорошо. Не скрою, все прелести результатов работы инженеров почувствуешь не сразу, как осилишь два тома стандарта, а значительно позже. Когда пообщаешься с очевидцами, опросишь свидетелей. Сейчас томов прибавилось, но, может быть, можно сразу начинать с третьего, поскольку, теперь оно называется CAN_FD. Однако, позвольте продолжить.


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


    Теперь несколько тысяч слов для читателя, который терпимо относится к занудам и не считает их врагами.


    CAN можно установить там, где раньше работал RS485 на витой паре. Витая пара — это не непременное условие, просто удобно сравнивать. Используя витую пару, по CAN, как и по RS485, можно передать сообщения от управляющего контроллера к подчиненному и получить ответ. Сходство бросается в глаза, но, давайте лучше остановимся на отличиях. Кое-какие из отличий могут нести знак минус для некоторых из читателей. Но им бы я посоветовал не огорчаться, а вспомнить закон Ломоносова.


    Благодаря синхронной организации протокола разрешение коллизий на шине реализовано аппаратно, на лету, так сказать. Ниже отмечено, к чему это приводит и что это дает непоседливому инженеру.


    Можно получить сообщение и без запроса.
    Не надо ждать, когда ответ будет готов, можно спросить в это время еще кого-то.
    Подчиненный контроллер тоже может спросить и получить ответ.
    Из-за синхронной работы длина шины CAN обратно пропорциональна скорости передачи или типа того.
    Максимальная скорость составляет 1 Мбод (10 — на подходе).
    То, что сообщение не исказилось при передаче отправляющий знает сразу после последнего бита. Точнее, это знают все на шине.
    Если сообщение исказилось для одного, попытка не засчитывается всеми.
    Если сообщение удалось передать в шину, то абонент не получит его лишь при условии, что сломался.
    Количество контроллеров на шине не должно превышать 127.


    Сообщения ограничены по длине. Они состоят из идентификатора, указателя длины в байтах и блока данных, именно с таким количеством байт, как указано. Есть еще несколько служебных битов, но о них пока помолчим, поскольку сервис должен быть ненавязчивым. Идентификатор может быть размером 11 или 29 бит. Блок данных может содержать от 0 до 8 байт (64 — на подходе).


    Для конкретики приведу немного цифр. Если хочется работать на скорости 1Мбод, то длина шины не должна быть больше 35 метров (некоторые предпочитают 40, то есть, погорячее). Если необходимо передать что-то на расстояние до 8 км, то скорость не должна превышать 5 Кбод. Кстати, читатель вправе спросить, почему килобод, а не килобит? Потому, что не все боды становятся битами. Как-то так.


    Как можно распорядиться всеми этими совсем не секретными ингредиентами? Те, кто во всем видят игру в кубики, сразу вспомнят, что есть такая замечательная вещь, как CANopen и еще много красивых сочетаний и аббревиатур и нечего изобретать велосипед. Таким мне часто хочется ответить: «Разве не похожа на велосипед та яичница из двух яиц, которую многие готовят себе на завтрак? Почему бы не сходить в общепит и не взять себе омлет?». Но я лучше промолчу и продолжу, не отвлекаясь на выкрики из зала.


    В те времена, когда 29 битовый идентификатора еще не успели придумать, существовал только 11 битовый. Одни его стали использовать, чтобы запихнуть туда название (номер) нужного вида данных. Другие использовали как адрес контроллера, к которому обращаются. И то и другое имело смысл. Например, можно спросить так:


    • А подай-ка нам, милейший, шато тринадцатого года в литровой бумажной упаковке.

    Или так:


    • Заверните мне, пожалуйста, то, что спрятано у вас на самой нижней полке справа.
      Кстати, в CAN может сработать и такая конструкция:


    • Всем лежать! А ты быстро складывай все с полок мне в сумку.
      Но этой конструкцией часто не попользуешься, поскольку после придется какое-то время ждать.
      Ждать пока все ответы не выстроятся один за другим и не поступят в распоряжение запрашивающего контроллера. Мы уже ушли от кино, если что.
      Меня в моем случае устроил бы вариант идентификатора в качестве адреса. Из 11 бит требовалось 7 и еще 4 оставалось на то, чтобы сделать одни сообщения более срочными по сравнению с другими, а также пометить часть контроллеров как главные.
      Некоторое неудобство перекочевало сюда из RS485, а именно, адреса надо было устанавливать вручную на каждом контроллере. Затем проверять и переустанавливать. И, возможно, вернуться к предыдущему шагу и повторить.
      К счастью, к тому времени уже существовали два обстоятельства.



    Первое — уже появился 29 битный идентификатор. А второе то, что многие производители микроконтроллеров стали считать хорошим тоном условие, чтобы каждый чип имел свой уникальный и довольно длинный номер.


    Теперь в длинном идентификаторе можно было 24 бита смело отвести для уникального адреса. Еще 5 оставалось, для заботы о том, чтобы поезда различались срочностью, направлением (туда, обратно), наличием вагона-ресторана и вагонов с повышенным комфортом.


    Если перестать дурачиться и сделаться серьезным, назвать подчиненных контроллеров агентами, а остальных боссами, то можно составить таблицу. Она будет показана немного позже.


    Еще немного про адресацию. Уникальный номер чипа, как правило, занимает количество битов значительно превышающее 24, например, 96 у STM32FXXX. Поэтому необходимо как-то получить 24 из 96. Я выбрал операцию XOR. Вы можете выбрать что-то другое, но небольшая проблема останется. Это совпадения адресов после редуцирования.


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


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


    Теперь останется подсчитать сколько получено ответов и сколько их должно быть. Если эти два числа совпадают, значит все в порядке. Если ответов меньше чем контроллеров, то налицо совпадение адресов и наладчикам есть работа. А если ответов больше, нежели контроллеров, то надо подумать о диссертации, поскольку, вы — на пороге открытия.


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


    Еще из интересного, если использовать и короткие и длинные идентификаторы одновременно, то можно получить, например, адресацию групп или частично широковещательные запросы. Но не будем пока углубляться.


    Вернемся к кодированию идентификатора.


    Для целей адресации в расширенном идентификаторе отведено 24 бита, а в стандартном – шесть. Адрес со значением 0x000000 является широковещательным для расширенного идентификатора. Для стандартного идентификатора нулевой адрес (6 его бит) также считается широковещательным. Пять начальных (старших) битов в длинном и коротком идентификаторе, называются заголовком, влияют на смысл сообщения и обозначаются буквами NVADR:



    Конечно, для диспетчерского щита потребовалось реализовать только часть этой схемы. В первом проекте со щитом (или на щите, как правильно?) использовались чипы Cortex от NXP, а в следующих проектах (были и такие) уже применялись M0 от STMicroelectronics.


    Пару слов об использовании коротких идентификаторов. Те шесть бит, которые отводятся для адресации, адресуют не контроллер, а группу. Эта группа при старте сначала у всех нулевая. Далее производится конфигурирование агентов, после которого часть из них или все становятся принадлежащими своей группе. Теперь запросом к группе, мы получаем ответы тех агентов, которые мы собрали в эту группу.


    Теперь, немного о том, что добавляется, если по-разному трактовать сообщения с различной длиной данных. Например, запрос с нулевой длиной хорошо помогает при отладке, как уже упоминалось выше. Запрос с длиной 3 обслуживает пространство байтовых переменных размером 16384. Запрос с длиной 4 делает то же самое, но предназначен для агента-шлюза, который обслуживает CAN шину второго уровня. Эта шина может состоять из одного-двух агентов, зато удаленных на пару километров.


    Запрос с длиной 5 и 6, аналогично, предназначены для пространства двухбайтовых переменных размером 4194304. Два бита используются не для адресации. Один бит управляет записью-чтением. Другой сигнализирует об ошибке.


    Далее 7 и 8 обслуживают четырех байтовые слова. Их тоже 4194304.


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



    Соединяются контроллеры плоским шлейфом на 6 жил. На питание идут сдвоенные. Микросхема о двадцати ногах — это STM32F042.


    С обратной стороны присутствует MAX3051, формирователь CAN в корпусе SOT23-8.
    Ну вот, мама кушать зовет.

    Поддержать автора
    Поделиться публикацией

    Комментарии 49

      +3

      Мама кушать зовет — это хорошо. Жаль, что так рано) Только разогнался)
      Познавательно, с юморком, спасибо.

        0
        у кана только 1 достоінство — аппаратная реализация,
        хотя если есть дма, то и в уарт пакеты кидаются с одного пинка.

        а из недостатков — на загруженной шине время ответа самого низкоприоритетного устройства будет большим. также и высокие накладные расходы на передачу
        image
          0
          Ну что значит «аппаратная реализация», ведь и про UART можно сказать, что у него аппаратная реализация — ведь биты кадра (старт, данные, четность, стоп) формируются не программно.

          Аппаратный арбитраж же.
          Каковой и позволяет перейти от двухуровневой иерархии «один ведущий, остальные — ведомые» (причём ведомые без команды ведущего вякнуть не смеют) к многоуровневой по приоритетам сообщений (и все могут по своей инициативе начинать передачу).

          Ну а приоритет = срочность, да. Но вопрос задержки низкоприоритетных — это вопрос пропускной способности, это должно учитываться при проектировании сети. Если загруженность настолько высока, что задержки становятся критичными — стоит или повысить скорость передачи (если позволяет размер линии), или разбивать на отдельные домены с шлюзованием.
            0
            ага, сначала создадим универсальный протокол, чтобы все работало вместе. потом увидев результат, выделим критично важные устройства в отдельную сеть. чтобы другие не мешали работать.
              0
              Серебряной пули не существует, как и универсального протокола, применимого во всех мыслимых случаях. Всегда есть какие-то ограничения.
            0
            Дело не в ДМА, по сравнению с уартом (485), достоинство кана в разруливании коллизий на физическом уровне, благодаря несимметричным 0/1 на линии.
            Если два устройства начнут что-то слать одновременно, тот, у которого адрес менее приоритетный, может понять, что он пытается говорить одновременно с кем-то другим и перестать это делать, не мешая при этом более высокоприоритетному участнику, и его сообщение при этом не испортится.

            В этом же и недостаток, все должны быть на расстоянии распространения 1 бита туда/обратно. А 485 можно с правильным драйвером и десятки МБит на сотни метров запустить, но коллизии/арбитраж шины разруливать придётся самому на уровнях выше.

            А накладные расходы будут и у 485, если на шине больше чем 2 участника и нужна адресация и хоть какой-то протокол поверх.
              0
              ну если гонять большие пакеты, пусть 64 байта, то расходы на заголовок и контролльную сумму будет не так много занимать как в случае 8 байт данных.
                0
                ну и +20, а то и 30% оверхэда на старт/стоп биты у уарта.
              0
              Ну да, конечно.
              Арбитраж, контрольная сумма, наличие идентификатора, фильтрация, реализации с наличием нескольких буферов на прием/передачу с возможностью независимой работы — это все мелочь, правда?
                0
                UART это поток, где как-то прилется реализвывать определение начала и конца сообщений, наличие несколькоих устройств на шине итд. CAN все это делает за вас. А если учесть, что есть еще и CAN-FD с размером пакета 64 байта, и устройства с 8-ю а то и 16-ю канами то становится все очень неплохо
                  0
                  У CANa три достоинства: аппаратное разруливание коллизий, мультимастерность и встроенный CRC.
                    0
                    А фильтрация как же?
                      0
                      Фильтрация тоже. Подзабыл.
                  +1
                  Хорошо написано, требую продолжения!
                    +1
                    Вдруг кому пригодится (или кто-то улучшит или найдёт ошибки). Вот мои драйвера для плат CAN 527D и PCI7841 для QNX 6:
                    CAN527D
                    PCI7841
                      0
                      Ого! QNX жив еще. Рад за него
                      0

                      А мне лично CAN в промышленных ПЛК не особо понравился — начиная с оконечных терминаторов, которые выглядят «колхозом» заканчивая ограниченной длинной линии. Проще унифицироваться через industrial Ethernet в его модификациях.

                        0

                        Ethernet требует свитча, ну и топология звезда а не шина. Хотя сейчас появляется автомобильный по одной паре с полным дуплексом

                          0
                          100base-t1, он хоть и по одной паре, но всё равно звезда и свичи. csma/cd там не предусмотрен.

                          etherCAT, топология кольцо, провод условно один, от соседа к соседу, но вот количество приёмо-передатчиков удваивается.
                            0

                            Сейчас разрабатывается ещё 10base-t1s (несколько устройств на одной паре), и 10base-t1l — 1 км по одной паре

                              0
                              ещё бы и цену за карточку как у обычных… уух!
                                0
                                не прошло и 10 лет как 10base2 выкинули из 802.3 за ненадобностью и вот он — технологический прорыв — 10мбитный multidrop, теперь до 15м и до шести устройств на шине.
                                  0

                                  Тот был по дорогому коаксиалу, с дорогими разъёмами, т-коннекторами. А тут простая витая пара даже без экрана. То что нужно для машины

                                    0
                                    неубедительно.
                                    этого провода для машины надо 15м — о чём намекает максимальная длина нового 10base-t1s. Да и «без экрана» для машины тоже так себе идея.
                                    А первый попавшийся в интернете магазин предложил купить rg-58 за 50 европейских копеек/м. витая пара в розницу настолько дешевле?
                                    Коннекторы подороже немного, но всё равно копейки по сравнению с ценой самого сетевого оборудования.
                                    Да и сделать выход диффернциальным, а не single-ended на коаксиал, оставив всё остальное как есть, тоже конечно задача стоящая десятилетий разработок новых стандартов.

                                    Да, я понимаю что они вроде хотят скрестить этот новый езернет с кэном, чтобы как у него коллизии и арбитраж сделать на физическом уровне, а всё что выше оставить совместимым с обычным езернетом.
                                    Но кто мешает это делать уровнями повыше, например есть тот же 1588 для синхронизации, чтобы избегать коллизий и всевозможный QoS для резервирования полосы/арбитража.
                                    Можно было бы что-нибудь попроще по мотивам изобразить.
                              0
                              ethercat работает по топологии шины
                                0
                                а это на смотря каком уровне на это смотреть
                                  0
                                  наверно не правильно выразился, в том смысле что не звездой а последовательно соединяются блоки. картинка потрясающая.
                              0
                              И прокладывать кабели толщиной в ногу? вы только представьте себе 100 кабелей индастриал изернета.
                              Один из плюсов кана — 3 провода на ворох узлов, а в вариации с лампочками так сам бог велел, только я не совсем понял чего автор на каждый узел повесил всего 4 лампочки.
                              А если это не щит где места вагон, а автомобиль, где короткие сообщения уровня поверни зеркало, включи обогрев стекла?

                              А колхоз в виде терминаторов — один джампер на конечной и начальной плате, где там колхоз?

                              Но опять же, всё зависит от задачи, не надо пихать кан туда где ему не место.
                                0
                                У автора есть вариант и на 64 «лампочки». В тот раз разбросано шибко было.
                                  0

                                  Для лампочек лучше использовать LIN. Там тоже есть CRC. И есть чипы с питанием от батареи и встроенным трансивером, и выходами к которым можно прямо светодиод цеплять

                                    0
                                    Лампочки были давно. Сейчас уже до фотодиодов дошло. Может, соберусь написать.
                                      0

                                      Рекомендую взглянуть на чип MLX81108. У него и ADC есть. Есть в soic. И вроде аналог в qfn20

                                0
                                Так в чём проблема. Ставьте CAN на оптике. Там всё также, как и на проводе — наличие света — доминантный уровень, отсутствие — рецессивный. Будет на километр бить без вопросов.
                                  0

                                  Так длина линии ограничена не тем, как далеко проходит сигнал, а тем, как быстро. Если мне не изменяет память, по оптоволокну сигнал идёт медленнее, чем по меди — если выбирать её с учётом коэффициента укорочения.

                                0
                                В отличиях, отчего-то, нет цены драйвера, хотя разница — существенная, раз в пять.
                                  0
                                  теперь оно называется CAN_FD
                                  А почему через подчеркивание?
                                    0
                                    Дурная привычка. Не обращайте внимания.
                                    0
                                    Спасибо за статью. Как введение не плохо. К сожалению про CAN протокол мало информации, хотелось б spyfnm как обеспечить связь ПК:Ethernet — Контроллер:CAN. Есть ли библиотеки под С++/Delphi, для того чтобы мониторить, переменные состояния контролера из под ПК.
                                      0
                                      как обеспечить связь ПК:Ethernet — Контроллер:CAN.


                                      Без конвертера Ethernet<->CAN — никак. Но что мешает воткнуть в компьютер плату CAN типа PCI7841?
                                        0
                                        Дороговатое решение, но функциональное.
                                        Просто хочу влезть в тему разработки элементов промышленной автоматики (различные реле параметров электрической сети, терморегуляторы и т.д.). Беглый анализ рунета в этой сфере, наталкивает на мысль, что надо учить разработку на базе STM32, (как лучших по цене/функционал/производительность). Но что мне уже хочется чтобы мои устройства умели общатся мжду собой и по scada. С протоколом Modbus, уже более менее поработал (как пользователь устройств), и вот я увидел что не малая часть чипов STM32, аппаратно поддерживают CAN. Про этот протокол слышал лишь краем уха, да и встречал его в некоторых немецких станках кои приходилось налаживать. вот и начал собирать информацию на русском по этой теме, в частности попал на эту статью автора. Потому если делать поддерку протокла CAN, то интересует все сферы его использования, и главное общение с ПК. Особенно как отдавать и получать комманды из приложений написанных под windows. Также интересует в какой, среде лучше кодировать прошивку Stm32 с учетом поддержки RS485 и CAN?
                                          0

                                          Что значит «лучше»? Я как‐то писал прошивку как раз для общения компьютера с Windows с «чем угодно» через CAN, в качестве переходника CANUART (в планах как‐нибудь сделать CANUSB, но до этого пока не дошло). У меня прошивка создавалась в Neovim на языке Rust, ответная часть на LabVIEW. Neovim — потому что *vim мне нравится, а привыкать к какой‐либо IDE, даже если бы я имел такое желание, в нашей компании не имеет смысла. Rust — потому что интересно изучить. Более того, данная прошивка вообще была первым моим проектом на Rust (и даже не первым сколько‐либо серьёзным, а вообще первым). LabVIEW — потому что почти всю автоматизацию в компании мы пишем на нём.


                                          Замечу, что даже несмотря на высокую незрелость экосистемы Rust для микроконтроллеров (прошивка писалась вроде где‐то полтора года назад) и отсутствие опыта в написании программ на Rust, основные проблемы с прошивкой были только в плане ошибок при использовании регистров специального назначения — в общем, то, что вы получите, если будете работать без HAL. Уже тогда присутствовали крейты для создания асинхронных прошивок для stm32 на Rust, включавшие в себя reentrant очередь, раскладывание нужных переходов по векторам прерываний, критические секции, генерацию условно безопасных API низкого уровня для доступа к регистрам специального назначения по файлам описания микроконтроллеров. Сейчас со всем должно быть получше — как минимум, насколько мне известно, более не требуется ночная сборка компилятора Rust.

                                            0
                                            Я в процессе проектирования и внедрения систем автоматики с CAN сталкивался и работал только с ПЛК Воронежского производителя. Для отладки и заливки программ там идет RS-485, для общения с сервером, АРМ — коммуникационный модуль CAN-Ethernet своего производства.
                                              0
                                              CANUART (в планах как‐нибудь сделать CANUSB
                                              А UART прямо вот RS232, втыкаемый в аппаратный COM-порт на компе? Или с еще одним переходником USB-UART?

                                              Стоит заметить, цепочка CAN — UART- USB вполне пригодна для такой задачи, UART что в большинстве МК, что в микросхемах USB-UART может раскочегариваться до мегабода а то и двух. Соответственно, можно работать с CAN на скоростях до 500 К — 1 М.

                                              Я когда-то делал подобный адаптер на AT90CAN128 и FT245. Программа на компе взаимодействовала с драйвером через D2XX API вместо виртуального COM-порта.
                                                0

                                                С переходником USB⇔UART. COM порт на плате тоже есть, но у переходника, как и у USB, имеется одно преимущество: оба предоставляют питание. USB так и не сделал, поскольку быть первопроходцем нет желания, готового шаблона «как сделать виртуальный COM порт на Rust и STM32» я до сих пор не видел, а вариант с переходником уже нормально работает.

                                                  0
                                                  Ну вот следующим логичным шагом и является объединение двух нормально работающих переходников в одном устройстве, чтобы избавиться от лишнего разъёма.
                                                  Хотя если чисто для себя — этот шаг в достаточной степени лишний.

                                              0
                                              Потому если делать поддерку протокла CAN, то интересует все сферы его использования, и главное общение с ПК.


                                              У нас CAN используется для связи с устройствами комплекса в реальном времени. Когда надо — с PC (под ОС QNX 6.3), когда надо с центральной ЭВМ на базе миландровских микроконтроллеров. В отличие от Ethernet, CAN работает в реальном времени и позволяет организовать обмен с устройствами, разнесённый с точностью до долей миллисекунды. Для PC используются платы CAN 527 D (она ISA), PCI7841 (она, ожидаемо, PCI — мне больше всех нравится) и sysWORXX USB-CANmodul (этот в USB вставляется — его не рекомендую, проблемы с ним есть при долговременной работе в нагруженной сети).
                                                0
                                                неправда, время ответа в загруженной линии не гарантируется. особенно если общатся с покупным устройством.
                                                  0
                                                  А кто сказал, что у нас линия загружена? ;) У нас чёткая диаграмма обмена.

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое