С 23 февраля
Форум самогонщиков Сайт Барахолка С 23 февраля

Учимся программировать контроллеры

Форум Оборудование Автоматика
Altair Научный сотрудник Омск 335 71
09 Марта 09, 20:59
Разговоров про контроллеры много, штука оно перспективная, однако "въехать" в тему с нуля достаточно сложно. И толковых описаний как это сделать "с нуля" до сих пор нет. Попытаюсь поделиться опытом.

   Основная суть программы в том, что она бегает по кольцу. Исполняется первая строчка кода, потом вторая, третья и так далее. После исполнения последней строчки опять выполняется первая. Это обязательно. Кольцо есть всегда. И при использовании ассемблера, и при использовании Си и вобще. Разница только в словах которыми описывается это кольцо. В АСМе я начало кольца обозначу меткой, а в конце напишу "goto метка". В Си создам функцию main(). Функция - это группа команд. Примерно как "книга это группа страниц с буквами". Можно функциями и не пользоваться, но быстро запутаешься. :-) В программе написанной на языке Си всегда обязательно есть функция main. Функция (группа команд, помним?) main - самое основное кольцо программы. Отличается эта функция от всех остальных тем, что после выполнения последней инструкции написанной внутри, автоматически все начинается сначала - с первой инструкции внутри main. Всякие вложенные функции можно уже называть как угодно (почти), а вот самое глобальное кольцо - обязательно main.
   Внутри этого колечка, я уже пропишу "ногодрыжество". Вот например так можно выдать меандр на ножку контроллера:
1)   main{
2)      PORTB.1=!PORTB.1;
3)   }
   Это вполне законченная программа. В первой строчке объявлена функция main.  Во второй строчке нужно прочитать так: "Биту 1 порта В (PORTB.1) присвоить (=) инвертированное значение (!) бита 1 порта В (PORTB.1)" В третьей строчке фигурная скобка закрывающая функцию main. Как только выполняемая программа дойдет до этой фигурной скобки, она начнет выполнять все снова с первой строчки внутри main. В нашем случае это будет выглядеть так: "Биту 1 порта В (PORTB.1) присвоить (=) инвертированное значение (!) бита 1 порта В (PORTB.1)", "Биту 1 порта В (PORTB.1) присвоить (=) инвертированное значение (!) бита 1 порта В (PORTB.1)", "Биту 1 порта В (PORTB.1) присвоить (=) инвертированное значение (!) бита 1 порта В (PORTB.1)", "Биту 1 порта В (PORTB.1) присвоить (=) инвертированное значение (!) бита 1 порта В (PORTB.1)" и т.д. На ножке контроллера соответствующей порту В мы будем иметь постоянно инвертирующееся значение. То 1, то 0. Т.е. меандр.
   Все остальные функции "по кольцу" не работают. Т.е. после выполнения последней команды внутри функции, программа выполняется дальше и дальше. А если дальше ничего нет - то там стоит фигурная скобка закрывающая main. И соответственно - на новый круг.
   Первая программа будет давать меандр с частотой в пределах 1/6 от тактовой. Примерно. Программа ведь бегает по кольцу? Команды исполняются с тактовой частотй контроллера? Никаких задержек для исполнения нет?
   Теперь сделаем меандр с частотй 1 Гц. Для этого применим функцию дающую задержку.
1)   #include "delay.h"
2)   main{
3)      PORTB.1=!PORTB.1;
4)      delay_ms(500);
5)   }
   Читается это так. Строка 1. Покажем компилятору что будем в проекте использовать функцию задержки. Т.е. есть где то файл в котором мы уже один раз написали программу реализующую задержку, потом его оформили в библиотеку и сложили в надежное место. Вот на него и покажем. Функцию написал не я. Узнал что она есть - из хелпа к компилятору. Там много интересного. Строка 2. Ну тут понятно. Объявлена main. Строка 3. Тоже ничего нового. Строка 4. Читаем так: "Вызываем функцию задержки в несколько миллисекунд с параметром 500". Параметр 500 показывает на сколько миллисекунд надо сделать задержку. Откуда узнал? Из описания функции задержки из хелпа. Строка 5. Конец функции main.
   Что имеем на выходе? Программа так же исполняется по кольцу. Однако после каждого инвертирования состояния порта "зависает" на 500 миллисекунд. В результате мы на выходе видим меандр имеющий 500 мс низкого уровня и 500 мс высокого уровня. Т.е. период 1 сек и частоту 1 Герц.
   
   З.Ы. Про порты и ноги - курим ДШ на контроллер. Там все подробно. Я конкретно, имею ввиду архитектуру AVR Atmel.
игорь223 Академик таганрог 25108 16701
Отв.1  09 Марта 09, 21:17, через 18 мин
Пока все совершенно понятно.
 Только, боюсь, чтобы вспомнить старое и дотянуться до активного частия в процессорских делах - таких лекций не один десяток нужно прочитать. А тебе, соответственно, написать...
Rudy Академик Питер 5741 986
Отв.2  09 Марта 09, 21:33, через 16 мин
Хи, десяток. Если бы.
Kotische Академик Саратов 7311 2182
Отв.3  09 Марта 09, 21:48, через 16 мин
Про порты и ноги - курим ДШ на контроллер. Там все подробно. Я конкретно, имею ввиду архитектуру AVR Atmel.Altair, 09 Марта 09, 20:59
Там конечно "всё" и "подробно", но с непревычки там чёрт ногу сломит, особенно учитывая что там всё на "буржуйском" написано...
Так что при всём уважении к "благому начинанию" отсылка в даташит малость "мимо кассы"...  Смеющийся
так что ты уж давай без "посылов в..." даташит... а как то больше на пальцах...  Строит глазки

З.Ы. Ты эта... не обижайся... Просто присутствующим здесь гуру эта ветка как бы мало полезна с точки зрения информации, а новичкам нужно нечто другое нежели посыл в даташит...  Подмигивающий
Rudy Академик Питер 5741 986
Отв.4  09 Марта 09, 21:55, через 7 мин
Для начинающих есть неплохая ссылочка http://avr123.nm.ru/01.htm
SerjNSK Научный сотрудник Новосибирск 2472 904
Отв.5  09 Марта 09, 22:12, через 17 мин
Фигня все это, начинающих надо отсылать сначала туда , где учат программировать на С,С++. Иначе все пособия про микроконтроллерам, это так, воду в ступе потолочь. Понято на собственом опыте. ИМХО.
Altair Научный сотрудник Омск 335 71
Отв.6  10 Марта 09, 11:00
Фигня все это, начинающих надо отсылать сначала туда , где учат программировать на С,С++. Иначе все пособия про микроконтроллерам, это так, воду в ступе потолочь. Понято на собственом опыте. ИМХО.
SerjNSK, 09 Марта 09, 22:12

Очень даже может быть. Поучишь?
Я вот сам помню как десять лет назад мыкался, пытался ПОНЯТЬ как же к этому подступиться. А когда понял с какой стороны грызть - проблем не стало. Читаешь и все понятно что откуда. Но начать - самое тяжелое.
Причем именно понять, как же происходит связка железа и программы?


Потому, продолжу.

      Архитектура, регистры и чтение ДШ
   
   Частенько, спрашивая что нибудь на спец форумах про контроллеры, видишь летящие в тебя тухлые помидоры в виде отсылов читать ДШ. Не нужно на это обижаться. Это правильно. Прежде чем что то спрашивать, нужно все же ДШ прочитать. Ведь это полное и подробное описание микросхемы. Конечно, могут встречаться места описаные не очень понятно, но это редкость. Спецы люди капризные и отвечать на вопросы типа какая буква идет после буквы А, не любят. Хотя на интересный вопрос ответят с удовольствием.   
   
   Контроллер - это настоящий компьютер, только маленький. Самое основное - это конечно АЛУ (арифметико-логическое устройство), и память, в которой хранится программа. В нашем случае, память в которой находится программа, внутри самой микросхемы. АЛУ умеет брать из памяти цифры, складывать их и в разные стороны рассылать.  Как только на контроллер подается питание, сразу начинает работать программный счетчик. Он с каждым тактом увеличивается на единичку и показывает из какого адреса памяти загрузить данные в АЛУ. Понятно, что при включении питания он автоматом устанавливается на то место, откуда в памяти должна начинаться программа. Итак, в памяти есть программа. Команды вперемешку с данными. Включили питание и пошло: загрузили данные раз, загрузили два, загрузили команду о том что сделать с данными и куда их потом отослать и так далее по очереди, по адресам на которые показывает программный счетчик, с каждым тактом увеличивающийся на единичку. Что бы перейти исполнять программу из некторого иного места в памяти, нужно выполнить команду загрузки в программный счетчик нужного нам адреса.
   Кроме памяти и АЛУ в контроллере есть "исполнительные устройства". Они отвечают за общение с внешним миром. Что бы обратиться к "внешнему миру", АЛУ должен послать команду "исполнительному устройству". Например: "Установить ножку контроллера в логический 1". Или: "Прочитать состояние ножки контроллера". У каждого "исполнительного устройства" есть две стороны: одна обращена в мир, вторая - к АЛУ. Снаружи это всегда просто вывод контроллера, а изнутри - набор регистров. Регистр с английского переводится как "регистр". Совершенно непонятно. Но достаточно близкую аналогию регистру можно представить как шкафчик с выключателями. Или лампочками. Или вперемешку В первом случае - регистр ввода, во втором регистр вывода, в третьем - ввода-вывода. Т.е. наборчик органов управления и/или отображения. В нашем случае регистр это ячейка памяти обычно в 1 байт. Регистр - байт. Биты - лампочки/выключатели. Тогда фраза "загрузить регистр" означала бы что нужно подойти к ящичку и выключатели включить в некоторой комбинации. А "прочитать регистр" - подойти и посмотреть какие лампочки светятся, а какие нет. Например: я есть АЛУ. Вижу что пришло время включить например поливалку. Иду к шкафу с выключателями и включаю нужный. После чего иду к нужному окну и вижу - поливалка включилась. Контроллерным лексиконом то же самое будет звучать так: В программе выполнилось условие "Время пришло", по которому в регистр вывода загрузилась 1, после чего был прочитан регистр ввода и там обнаружилась 1.   
   Каждое устройство ввода-вывода имеет свой набор управляющих регистров. Например порты общего назначения. Фактически, это ключ из двух полевиков, который может быть изнутри установлен в 1, 0 или Z. Например есть Порт С. Например у него в наличии 8 ножек контроллера. Значит снаружи мы видим 8 ножек контроллера, а изнутри видим 3 регистра порта С. Регистр обычно имеет размер в 1 байт = 8 бит. 1 байт это ящик в котором объединены 8 бит - выключателей. Или лампочек. У порта общего назначения это регистр направления (назначение ножки-ввод или вывод), регистр вывода и регистр ввода. Каждый бит в регистре соответствует своей ножке снаружи. Смотрим на схему контроллера и чтаем назначение ножек. Находим ножку которая работает как нулевой вывлд порта С. Если мы хотим использовать ее как выход, то должны в регистре направления указать, что она должна работать на выход. Как можно прочитать из ДШ, для этого нужно в бит 0 регистра выбора направления DDRC, записать логический 1. Вот так:

   1)   DDRC=0b00000001;   
   
   Так и прочитаем: "Регистру DDRC присвоить ДВОИЧНОЕ значение 0b00000001". Или же по другому: "Загрузить в регистр DDRC значение 1".
Префикс 0b обозначает что используется двоичная запись числа. Я предпочитаю именно так записывать данные для загрузки всяких регистров - нагляднее. Можно и в шестнадцатеричной. Тогда будет выглядеть так: DDRC=0x01; Непринципиально.   
   Если хотим использовать нулевой и четвертый выводы порта С как выход, соответственно в регистр отвечающий за направление работы порта запишем так:
   
   1)  DDRC=0b00010001;
   
   Теперь ножки порта С контроллера работают как входы кроме нулевой и четвертой.
   Этим действием мы настроили порт С. Теперь что бы управлять состоянием выхода нужно записывать нужные нам значения в регистр вывода PORTC, а что бы принимать данные из порта нужно читать Из порта ввода PINC.
   
   Вот что по этому поводу написано в ДШ (Только про порт В):

   Порт B

     Порт B 8-разрядный двунаправленный порт ввода/вывода.
     Для  обслуживания  порта  отведено  три  регистра: регистр данных
PORTB ($18, $38), регистр направления данных - DDRB ($17, $37) и ножки
порта B ($16, $36). Адрес  ножек порта B предназначен только  для чте-
ния, в то время как регистр данных и регистр направления данных -  для
чтения/записи.
     Все выводы порта имеют отдельно подключаемые подтягивающие резис-
торы. Выходы порта  B могут поглощать  ток до 20  mA и непосредственно
управлять светодиодными индикаторами. Если выводы PB0..PB7 используют-
ся как входы и замыкаются на землю, если включены внутренние  подтяги-
вающие резисторы, выводы являются источниками тока (Iil). Дополнитель-
ные функции выводов порта B приведены в таблице 8.

     Таблица 16. Альтернативные функции выводов порта B
---------T----------------------------------------------------------¬
¦Вывод   ¦  Альтернативная функция                                  ¦
+--------+----------------------------------------------------------+
¦PB0     ¦  AIN0 (Положительный вход аналогового компаратора)       ¦
+--------+----------------------------------------------------------+
¦PB1     ¦  AIN1 (Отрицательный вход аналогового компаратора)       ¦
+--------+----------------------------------------------------------+
¦PB3     ¦  OC1 (Выход совпадения таймера/счетчика1)                ¦
+--------+----------------------------------------------------------+
¦PB5     ¦  MOSI (Вход данных для загрузки памяти)                  ¦
+--------+----------------------------------------------------------+
¦PB6     ¦  MISO (Выход данных для чиенмя памяти)                   ¦
+--------+----------------------------------------------------------+
¦PB7     ¦  SCK  (Вход тактовых импульсов последовательного обмена) ¦
L--------+-----------------------------------------------------------
     При использовании альтернативных функций выводов. регистры DDRB и
PORTB должны быть установлены в соответствии с описанием  альтернатив-
ных функций.

                   РЕГИСТР ДАННЫХ ПОРТА B - PORTB

 Бит          7      6      5      4      3      2      1      0
           -------T------T------T------T------T------T------T------¬
 $18 ($38) ¦PORTB7¦PORTB6¦PORTB5¦PORTB4¦PORTB3¦PORTB2¦PORTB1¦PORTB0¦
           L------+------+------+------+------+------+------+-------
Чт./зап.      R/W    R/W    R/W    R/W    R/W    R/W    R/W    R/W
Нач.знач.      0      0      0      0      0      0      0      0


              РЕГИСТР НАПРАВЛЕНИЯ ДАННЫХ ПОРТА B - DDRB

 Бит
           -------T------T------T------T------T------T------T------¬
 $17 ($37) ¦ DDB7 ¦ DDB7 ¦ DDB5 ¦ DDB4 ¦ DDB3 ¦ DDB2 ¦ DDB1 ¦ DDB0 ¦
           L------+------+------+------+------+------+------+-------
Чт./зап.     R/W    R/W    R/W    R/W    R/W    R/W    R/W    R/W
Нач.знач.     0      0      0      0      0      0      0      0


                        ВЫВОДЫ ПОРТА B - PINB

 Бит
           -------T------T------T------T------T------T------T------¬
 $16 ($36) ¦PINB7 ¦PINB7 ¦PINB5 ¦PINB4 ¦PINB3 ¦PINB2 ¦PINB1 ¦PINB0 ¦
           L------+------+------+------+------+------+------+-------
Чт./зап.      R      R      R      R      R      R      R      R
Нач.знач.    Hi-Z   Hi-Z   Hi-Z   Hi-Z   Hi-Z   Hi-Z   Hi-Z   Hi-Z

     PINB не является регистром, по этому адресу осуществляется доступ
к физическим значениям  каждого из выводов  порта B. При чтении PORTB,
читаются данные из регистра-защелки, при чтении PINB читаются логичес-
кие значения присутствующие на выводах порта.


           ПОРТ B, КАК ПОРТ ВВОДА/ВЫВОДА ОБЩЕГО НАЗНАЧЕНИЯ

     Все 8 бит порта B при использовании для ввода/вывода одинаковы.
     Бит DDBn регистра DDRB выбирает направление передачи данных. Если
бит установлен (1), вывод сконфигурирован как выход. Если бит  сброшен

(0) - вывод сконфигурирован как  вход. Если PORTBn установлен и  вывод
сконфигурирован как вход, включается КМОП подтягивающий резистор.  Для
отключения резистора, PORTBn должен быть сброшен (0) или вывод  должен
быть сконфигурирован как выход.

              Таблица 17. Влияние DDBn на выводы порта B
-----T------T-------T-----------T-----------------------------------¬
¦DDBn¦PORTBn¦Вх/Вых ¦Подт.резист¦Комментарий                        ¦
+----+------+-------+-----------+-----------------------------------+
¦0   ¦0     ¦Вход   ¦Нет        ¦Третье состояние (Hi-Z)            ¦
+----+------+-------+-----------+-----------------------------------+
¦0   ¦1     ¦Вход   ¦Да         ¦PBn источник тока Iil, если извне  ¦
¦    ¦      ¦       ¦           ¦соединен с землей                  ¦
+----+------+-------+-----------+-----------------------------------+
¦1   ¦0     ¦Выход  ¦Нет        ¦Выход установлен в 0               ¦
+----+------+-------+-----------+-----------------------------------+
¦1   ¦1     ¦Выход  ¦Нет        ¦Выход установлен в 1               ¦
L----+------+-------+-----------+------------------------------------
 n = 7,6...0 - номер вывода
Су-27 Магистр Сантьяго-де-Чили 286 45
Отв.7  11 Марта 09, 20:19
Для понимания данной темы нужно было иметь в детстве программируемый микрокалькултор, шагов на сто. Прошу прощения за оффтоп.
steel.ne Научный сотрудник Киев 544 69
Отв.8  11 Марта 09, 21:01, через 43 мин
По поводу отсылок к даташитам. Даташит - это прежде всего справочник. Его нужно читать потом, когда уже научился мигать светодиодом. До этого он не только бесполезен, а и вреден.

В моей философии программирования контроллеров работа с портами стоит как бы не на последнем месте. На первом месте - архитектура решения в целом.

Приведу пример - надо подключить экранчик. Для этого надо использовать семь ножек  процессора. Любых из 32. И хоть обчитайся даташитом, никто этого за тебя не решит. А как только определишься, то и программирование идет легко  ненапряжно.

На самом деле толчок к применению микроконтроллеров в деле ректификации дало приниятие набора NM8036 как стандарта аппаратной части. И в принципе не важно, что для регулировки яркости экрана задействовали два аппаратных таймера. А на ножках трехпроводного интерфейса висит экранчик. Это все частности, главное что стандарт принят.
steel.ne Научный сотрудник Киев 544 69
Отв.9  12 Марта 09, 16:41
Утащено с форума, подпишусь под каждым словом Улыбающийся
http://adsh.ukrweb.net/radiohobby/viewtopic.php?id=1218

Это даже не грабли, а то, что нужно знать начинающему AVR-щику:

1. В командах sbr и cbr нужно применять не номер бита, а маску, нужно писать (1<<Bit) или exp2(Bit).

2. Для обращения к таблицам в памяти программ командой lpm указатель Z нужно загружать значением (метка * 2). Память программ организована как слова, а lpm работает с байтами. Нужно писать так: ldi ZL,low(Label*2) ldi ZH,high(Label*2).

3. Для сброса флага прерывания в него нужно записать "1".

4. Нет приоритетов прерываний. Прерывания автоматически запрещаются в начале обработки. Если требуются вложенные прерывания, в обработчик нужно добавить команду sei. Команда reti делает то же самое, что и ret, плюс разрешает прерывания.

5. Питание портов, на которых располложены входы АЦП, производится от AVCC/AGND. Если это питание не подать, порты работать не будут.

6. В EEPROM лучше не использовать ячейку с адресом 0, регистр адреса EEPROM лучше обнулять после операций с EEPROM. Для младших AVR, которые не имеют встроенного BOD, при использовании EEPROM обязательно нужен внешний супервизор. В современных AVR есть BOD, он должен быть включен фузом. Для таких AVR внешний супервизор лучше не использовать, может быть даже хуже - помехи на линии RESET могут вызвать даже порчу содержимого памяти программ. Вход RESET желательно подтянуть к питанию резистором порядка 4.7К и подключить конденсатор порядка 10 нФ на землю (программированию он обычно не мешает).

7. Команды с непосредственным операндом sbr, cbr, ldi, ori, andi, subi, sbci возможны только с регистрами R16 - R31.

8. Побитовая работа с портами sbi, cbi возможна только в диапазоне адресов 00 - 1F.

9. ADIW, SUBIW работают только с R24, 26, 28, 30.

10. movw работает с парами четный:нечетный регистр R0:R1 и т.д.

11. inc не устанавливает флаг C.

12. Команды с автоинкрементом/декрементом не изменяют старший байт указателя в устройствах с ОЗУ менее 256 байт. Его можно использовать для других целей. Команды ADIW, SUBIW - изменяют.

13. При определении таблиц в памяти программ за каждой директивой DB должно быть четное количество байт, иначе автоматически добавляется 0x00.

14. При переходе с AT90S1200 (без стека) на старшие AVR не забывать инициализировать стек.

15. Запись в EEPROM time-controlled, нужно запрещать прерывавания.

16. У некоторых AVR, например, mega8, нужно специальным битом разрешать не только выключение WDT, но и изменение его интервала.

17. Для 16-разрядных регистров периферии читать первым L, писать первым H.

18. Если есть JTAG, то его надо выключать фузом, иначе не работают соответствующие порты. Если есть фуз совместимости с Мегой103, его нужно не забывать убрать.

19. Чтение состояния внешнего сигнала, подключенного к порту, производится из PINx, а не из PORTx.

20. Mega64, 128 программируется через выводы RXD, TXD (а не MISO, MOSI).

21. Mega64, 128 OC2 и OC1C на одной ноге!

22. У новых мег UART имеет FIFO, в котором сохраняются данные и флаги ошибок (FE, DOR). Поэтому UDR нужно читать один раз на один принятый байт. Флаги ошибок нужно читать перед чтением UDR.

23. При использовании аппаратного SPI в режиме мастера вывод SS нельзя использовать в качестве входа.

24. У старших мег часть периферии находится не в адресном пространстве IO, а в адресном пространстве данных (например, порт F). В таких случаях доступ нужно осуществлять командами STS, LDS а не OUT, IN.

25. Внимательно изучите фузы, особенно типа тактового генератора и времени стартапа. Всегда желательно ставить Full Amplitude, иначе некачественные кварцы могут генерировать неустойчиво. Указать состояние фузов в исходном тексте программы нельзя, их нужно программировать отдельно (программаторы имеют такую возможность).

26. Некоторые программаторы (PonyProg, например) применяют инверсное обозначение состояния фузов. Будьте бдительны, распространенная ошибка - перевод AVR на внешнее тактирование. При этом он перестает отвечать программатору и выглядит совсем мёртвым. Выход из такой ситуации - подключение внешнего клока к XTAL1 на время программирования.

27. Не переключайте фузом вход RESET в режим обычного IO - контроллер невозможно будет дальше программировать последовательным программатором.

28. При чтении PINx нужно учитывать, что внешние сигналы синхронизируются в нутренней тактовой частотой, что приводит к задержке. Поэтому если мы что-то вывели на порт и хотим это же прочитать, нужно вставить команду NOP между OUT и IN. Хотя чтение собственного выходного сигнала требуется очень редко.

29. Линии, которые используются для внутрисхемного программирования (MISO, MOSI, SCK) можно использовать в проекте как порты ввода-вывода. Если используется вывод, то никаких дополнительных мер принимать не надо (только учтите, что в момент программирования будет "дергаться" подключенная периферия). Если используется ввод, то выход внешней схемы, подключенный к этому входу, надо развязать резистором порядка 470 ом.
blackdiamond Магистр Краснодар 247 237
Отв.10  09 Июня 16, 03:45
Класс! Я сразу всё понял, только не понял, а с кем это он разговаривал?
сообщение удалено
woddy Модератор Новосиб 1273 480
Отв.11  09 Июня 16, 07:11
мне интереснее, зачем вы топик 2009 (!!!) года откопали
SedoY Профессор Новосибирск 4685 1980
Отв.12  09 Июня 16, 10:37
Разговоров про контроллеры много, штука оно перспективная, однако "въехать" в тему с нуля достаточно сложно. И толковых описаний как это сделать "с нуля" до сих пор нет.Altair, 09 Марта 09, 23:59

?????????????? как это нет, а куда делось???

Добавлено через 4мин.:

да и темку бы назвать - ... контроллеры таких то семейств, а то я читаю и вижу неправильное, касательно моторолл )))))
Mnz Магистр Город 254 84
Отв.13  09 Июня 16, 11:02, через 25 мин
Есть сомнения по поводу работоспособности программы из первого сообщения. По умолчанию порты настроены на вход, а в программе не видно чтобы порт настраивался на выход.
blackdiamond Магистр Краснодар 247 237
Отв.14  10 Июня 16, 02:22
мне интереснее, зачем вы топик 2009 (!!!) года откопалиwoddy, 09 Июня 16, 07:11
Да не все ёще перековались в программисты с 2009 года, а вот первое сообщение очень информативно для небельмеса.
An-Drey Специалист Черкесск 151 40
Отв.15  26 Нояб. 19, 07:20
Раз уж это иногда читается, а оно читается, то обращайте внимание на ошибки в текстах...
¦ DDB7 ¦ DDB7 ¦Altair, 10 Марта 09, 11:00