Подписка на новости

Опрос

Нужны ли комментарии к статьям? Комментировали бы вы?

Реклама

 

2008 №7

Управление несколькими периферийными устройствами по линиям шин SPI/I2C

Логан Стив


В статье сравниваются два распространенных последовательных цифровых интерфейса, используемых в большинстве аналоговых интегральных схем (ИС) — SPI (трехпроводной) и I2C (двухпроводной). Каждый из этих последовательных интерфейсов имеет свои преимущества и недостатки применительно к различным схемам, в зависимости от таких критериев, как необходимая скорость передачи данных, объем доступного пространства памяти и шумы. В настоящей статье обсуждаются различия между этими интерфейсами и приводятся примеры, подробно демонстрирующие возможности каждого из них.

Введение

Хотя сигналы реального мира всегда будут аналоговыми, сегодня все в большем количестве аналоговых ИС коммуникация осуществляется через цифровые интерфейсы. Последовательные интерфейсы обеспечивают связь между ведущим устройством, предоставляющим последовательный тактовый сигнал, и ведомым или периферийным устройством. Порты SPI (трехпроводной) и I2C (двухпроводной), имеющиеся сегодня в большинстве микроконтроллеров, представляют собой популярные средства приема и передачи данных. Таким образом, микроконтроллеры ведут обмен данными по нескольким линиям шины для управления периферийными устройствами, такими как аналого–цифровые преобразователи (АЦП), цифро–аналоговые преобразователи (АЦП), батареи со встроенной логикой, расширители портов, ЭСППЗУ и датчики температуры. В отличие от параллельного интерфейса, в последовательном интерфейсе биты данных передаются один за другим — обычно по двум, трем или четырем линиям данных и синхронизации. Параллельные интерфейсы обеспечивают высокую скорость, зато последовательные требуют меньшего числа линий управления и данных.

Основные сведения о последовательных интерфейсах

Последовательные интерфейсы выпускаются в трех разновидностях: трехпроводной, двухпроводной и однопроводной. Данная статья посвящена трех– и двухпроводным интерфейсам. В стандартах SPI, QSPI и MICROWIRE (или MICROWIRE PLUS) обмен данными осуществляется по трехпроводным интерфейсам. В стандартах I2C и SMBus обмен данными осуществляется по двухпроводным интерфейсам. Обоим типам последовательных интерфейсов свойственны как преимущества, так и недостатки (табл. 1).

Таблица 1. Преимущества и недостатки трех и двухпроводных интерфейсов
Интерфейс Преимущества Недостатки
Трехпроводной: SPI, QSPI и MICROWIRE PLUS
  1. Скорость.
  2. Не требуются нагрузочные резисторы.
  3. Полнодуплексный режим работы.
  4. Устойчивость к шумам.
  1. Большее число подключений к линиям шины.
  2. Для одновременного обмена данными более чем с одним ведомым устройством необходимы отдельные линии выбора кристалла.
  3. Отсутствует подтверждение приема данных.
Двухпроводной: I2C и SMBus
  1. Меньше подключений к линиям шины.
  2. Множество устройств делят между собой одну шину.
  3. Прием данных подтверждается.
  1. Скорость:
    • для SMBus — не выше 100 кбит/с;
    • для I2C — не выше 3,4 Мбит/с.
  2. Полудуплексный режим работы.
  3. Для линий с открытым стоком требуются нагрузочные резисторы.
  4. Сниженная устойчивость к шумам.

Трехпроводной интерфейс

В трехпроводных интерфейсах используется линия выбора кристалла (CS или SS с активным низким уровнем), линия синхронизации (SCLK) и линия ввода данных или главная линия вывода (называется DIN или MOSI). Поскольку эти интерфейсы содержат также линию вывода данных или главную линию ввода (называется DOUT или MISO), их иногда называют четырехпроводными интерфейсами. Для простоты в данной статье трех– и четырехпроводные интерфейсы называются трехпроводными.

Трехпроводные интерфейсы работают на более высоких тактовых частотах и не требуют нагрузочных резисторов. Интерфейсы SPI/QSPI и MICROWIRE также обеспечивают полнодуплексный режим работы (данные могут одновременно передаваться и приниматься), и они более устойчивы к шумам. В трехпроводных интерфейсах синхронизация производится по фронту, а не по уровню. Основной недостаток трехпроводного интерфейса — необходимость в отдельной линии CS с активным низким уровнем для каждого ведомого устройства на шине, если только ведомые устройства не подключены по шлейфовой схеме, как показано на рис. 1. (Шлейфовая схема подключения более подробно обсуждается ниже.) Трехпроводной интерфейс также не предусматривает подтверждения правильной передачи или приема данных. С точки зрения программирования трехпроводные интерфейсы проще и эффективнее двухпроводных при работе с одним ведущим и одним ведомым устройствами.

Рис. 1. В трехпроводных интерфейсах используются линии ввода данных, вывода данных, синхронизации и выбора кристалла
Рис. 1. В трехпроводных интерфейсах используются линии ввода данных, вывода данных, синхронизации и выбора кристалла

Двухпроводной интерфейс

В двухпроводных интерфейсах используется только линия данных (SDA или SMBDATA) и линия синхронизации (SCL или SMBCLK). Меньшее на одну или две количество линий — особенно актуальное преимущество для компактных устройств, таких как мобильные телефоны и волоконно–оптические системы. Двухпроводные интерфейсы также позволяют подключать множество ведомых устройств к одной шине без необходимости использования сигналов выбора кристалла. Это возможно благодаря тому, что каждое устройство имеет уникальный адрес. Двухпроводные интерфейсы также предусматривают передачу бита подтверждения после успешного чтения. Поскольку в двухпроводных интерфейсах имеется только одна линия данных, они способны работать только в полудуплексном режиме (в заданном такте возможны только передача или только прием данных, но не то и другое вместе). В двухпроводных интерфейсах синхронизация производится по уровню, что может создавать проблемы в условиях сильных шумов при неправильном определении бита данных.

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

Конструктивные особенности интерфейсов SPI, QSPI, MICROWIRE

Интерфейсом SPI, который разработан компанией Motorola, оборудованы популярные процессоры и микроконтроллеры, такие как MAXQ2000 и MAXQ7654. Как уже отмечалось выше, схемы с интерфейсом SPI требуют двух линий управления (CS с активным низким уровнем и SCLK) и двух линий данных (DIN/SDI и DOUT/SDO). В стандартах SPI/QSPI компании Motorola линия данных DIN/SDI называется MOSI (master–out, slave–in — выход ведущего, вход ведомого), линия DOUT/SDO — MISO (master–in, slave–out — вход ведущего, выход ведомого), а линия CS с активным низким уровнем — SS (slave–select — выбор ведомого). Для простоты и ясности в настоящей статье линии данных трехпроводного интерфейса будут рассматриваться с точки зрения ведомого устройства: DIN — это вход данных ведомого устройства, а DOUT — выход данных ведомого устройства. В этой статье линии трехпроводной шины обозначаются как CS с активным низким уровнем, SCLK, DIN и DOUT, поскольку именно такие названия используются в периферийных устройствах, выпускаемых компанией Maxim.

В большинстве интерфейсов SPI имеется два конфигурационных бита, определяющих момент выборки данных ведомым устройством — CPOL (полярность сигнала синхронизации) и CPHA (фаза сигнала синхронизации). Бит CPOL определяет состояние сигнала SCLK в режиме холостого хода (отсутствие переключения) — высокий уровень (CPOL = 1) или низкий уровень (CPOL = 0). Бит CPHA определяет, по какому фронту сигнала SCLK происходит сдвиг данных в том и другом направлении. Если CPOL = 0, то при CPHA = 0 данные будут сдвигаться на ведомое устройство по положительному фронту сигнала SCLK. При CPHA = 1 данные будут сдвигаться на ведомое устройство по отрицательному фронту сигнала SCLK. Два состояния битов CPOL и CPHA дают четыре возможных сочетания полярности и фазы сигнала синхронизации; каждое сочетание несовместимо с остальными тремя. Для успешного обмена данными между ведущим и ведомым устройствами на каждом из них должны быть установлены одинаковые значения CPOL и CPHA.

В простейшей форме интерфейс SPI передает за один прием восемь битов данных (один байт), хотя некоторые микроконтроллеры передают за один прием два или более байта. Например, микроконтроллеры MAXQ2000 и MAXQ7654 способны передавать за один прием 8 или 16 битов. При CPOL = 0 и CPHA = 0 переход от высокого уровня к низкому на линии CS с активным низким уровнем запускает передачу данных с ведущего устройства на ведомое. Сигнал на линии CS должен удерживаться на низком уровне, пока уровень сигнала SCLK будет меняться от низкого к высокому в течение восьми полных тактов. Данные DIN сдвигаются по положительному фронту сигнала SCLK. Байт данных загружается в ведомое устройство после того, как сигнал на линии CS с активным низким уровнем перейдет от низкого уровня к высокому. Данные будут доступны на линии DOUT ведомого устройства по отрицательному фронту сигнала SCLK на протяжении того же цикла из восьми битов. На рис. 2a приведена временная диаграмма работы трехпроводного интерфейса SPI при CPHA = 1 и CPOL = 1. Потактовая передача данных на периферийное устройство производится по положительному фронту сигнала синхронизации, а с ведомого устройства — по отрицательному фронту. На рис. 2б приведена временная диаграмма работы трехпроводного интерфейса SPI при CPHA = 0 и CPOL = 1. Потактовая передача данных на периферийное устройство производится по отрицательному фронту сигнала синхронизации, а с ведомого устройства — по положительному фронту.

Рис. 2. a) Временная диаграмма работы трехпроводного интерфейса при CPHA = 1; б) временная диаграмма работы трехпроводного интерфейса при CPHA = 0
Рис. 2. a) Временная диаграмма работы трехпроводного интерфейса при CPHA = 1; б) временная диаграмма работы трехпроводного интерфейса при CPHA = 0

Линия шины CS с активным низким уровнем используется в качестве разрешающего сигнала для каждого ведомого устройства, поскольку каждой ИС на шине требуется собственная линия выбора кристалла. Если к одной шине подключено четыре ведомых устройства, для выбора соответствующего ведомого устройства необходимо четыре линии выбора кристалла. Если на линии CS с активным низким уровнем присутствует высокий (неактивный) уровень, ведомое устройство игнорирует фронты сигнала SCLK и удерживает линию DOUT в состоянии с высоким импедансом.

Некоторые периферийные устройства с трехпроводным интерфейсом можно программировать методом, который носит название шлейфового подключения. Вместо того чтобы подключать по одной линии CS с активным низким уровнем на каждое периферийное устройство, при шлейфовом подключении можно использовать одну линию CS с активным низким уровнем и линию SCLK для управления несколькими последовательно соединенными периферийными устройствами. Для такого шлейфового подключения периферийных устройств в трехпроводном интерфейсе должна быть линия DOUT. Как показано на рис. 1, линия DOUT периферийного устройства № 1 служит линией DIN для периферийного устройства № 2 и т. д.

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

Стандарт QSPI почти идентичен SPI. Фактически периферийные устройства не могут отличить шину QSPI от шины SPI. В отличие от ведущих устройств с интерфейсом SPI, ведущие устройства с интерфейсом QSPI позволяют осуществлять передачу данных с программным выбором кристалла. Более того, ведущие устройства с интерфейсом QSPI могут передавать за один прием от 8 до 16 битов, а устройства с интерфейсом SPI обычно передают всего 8 битов. Устройства QSPI можно настроить для последовательной передачи до 16 слов данных (максимум 256 битов). Такой передачей целиком управляет интерфейс QSPI, и вмешательства микроконтроллера не требуется. Подобно SPI, стандарт QSPI не нормирует максимальную скорость передачи данных.

Более старый стандарт MICROWIRE, разработанный компанией National Semiconductor, весьма похож на SPI. Однако в MICROWIRE жестко заданы полярность и фаза сигнала синхронизации (CPOL = 0, CPHA = 0). Данные на линии DIN всегда сдвигаются на ведомое устройство по положительному фронту сигнала SCLK. Сдвиг данных с вывода DOUT происходит по отрицательному фронту сигнала SCLK. Подобно SPI, стандарт MICROWIRE не нормирует максимальную скорость передачи данных.

Интерфейс I2C

В отличие от трехпроводного полнодуплексного последовательного интерфейса, стандарт I2C, разработанный компанией Philips, предусматривает связь в полудуплексном режиме по одной линии данных (SDA) и одной линии управления (SCL). Стандарт I2C определяет простой двунаправленный интерфейс «ведущий–ведомый». В этой схеме микроконтроллер обозначает режим, в котором он будет работать — ведущий (режим передачи) или ведомый (режим приема). Каждое ведомое устройство имеет уникальный адрес, что позволяет ведущему устройству связываться с различными ведомыми устройствами по одной шине без использования сигналов выбора кристалла (рис. 3). Число ведомых устройств ограничено только максимально допустимой емкостью шины (400 пФ). В протоколе I2C используются 7– или 10–разрядные адреса, хотя 7–разрядные адреса более распространены. При 7–разрядном протоколе к шине можно подключать до 127 различных периферийных устройств. SCL и SDA — это линии с открытым стоком, для надлежащей работы которых необходимо, чтобы в режиме холостого хода на них был установлен высокий уровень. При работе от источника питания напряжением 3 В к этим линиям следует подсоединить нагрузочный резистор сопротивлением 1 кОм или выше, a при работе от источника питания напряжением 5 В — 1,6 кОм или выше.

Рис. 3. В двухпроводных интерфейсах имеется линия ввода/вывода данных и линия синхронизации
Рис. 3. В двухпроводных интерфейсах имеется линия ввода/вывода данных и линия синхронизации

Обмен данными по интерфейсу I2C начинается с команды запуска, которая соответствует переходу линии SDA с высокого на низкий уровень при высоком уровне на линии SCL (рис. 4a). В двухпроводном интерфейсе для передачи данных между ведущим и ведомым устройствами используются команды запуска, повторного запуска и останова. За каждый такт SCL передается один бит данных; для передачи байта на ведомое устройство или с него необходимо передать как минимум девять битов. Цикл записи содержит восемь битов данных (рис. 4б), за которыми следует сигнал подтверждения (ACK) или отсутствия подтверждения (NACK). Двухпроводной интерфейс устанавливает низкий уровень на линии SDA, когда передача данных подтверждается. Когда данные передаются по шине I2C, они сдвигаются на ведомое устройство по положительному фронту сигнала SCL и считываются по отрицательному фронту сигнала SCL. Данные на линии SDA должны быть устойчивы, пока тактовый импульс SCL имеет высокий уровень. Передача завершается при поступлении команды останова или повторной команды запуска; в этот момент линия SDA переходит с низкого уровня на высокий при высоком уровне на линии SCL. Как SDA, так и SCL сохраняют высокий уровень, когда шина свободна.

Рис. 4. a) Условия запуска и останова в двухпроводном интерфейсе; б) биты подтверждения I^2C
Рис. 4. a) Условия запуска и останова в двухпроводном интерфейсе; б) биты подтверждения I2C

Цикл записи I2C начинается с команды запуска, за которой следует 7–разрядный адрес ведомого устройства и восьмой бит, указывающий на команду записи или чтения. Для передачи команды записи установите низкий уровень в восьмом бите, для передачи команды чтения — высокий. Ведущее устройство освобождает линию шины после восьмого такта. На девятом такте ведомое устройство удерживает низкий уровень на линии SDA, если оно подтверждает правильную передачу данных. Если ведомое устройство не подтверждает правильность выполнения команды записи, оно освобождает линию SDA (которая затем удерживается на высоком уровне нагрузочным резистором).

После этого ведущее устройство записывает 8–битный командный байт, за которым следует второй бит ACK/NACK. Далее ведущее устройство записывает 8–битный байт данных, за которым следует третий бит ACK/NACK. Конечный бит подтверждения байта данных завершает цикл чтения–записи, и выходы периферийного устройства обновляются. На рис. 5a показан пример цикла записи.

Рис. 5. a) Пример цикла записи I^2C; б) примеры циклов чтения I^2C
Рис. 5. a) Пример цикла записи I2C; б) примеры циклов чтения I2C

Цикл чтения I2C начинается с команды запуска, за которой следует адрес ведомого устройства с восьмым битом, обозначающим команду записи. После поступления бита ACK/NACK ведущее устройство записывает командный байт для доступа к новому регистру ведомого устройства. За вторым битом ACK/NACK ведущее устройство переписывает адрес ведомого устройства. Затем после получения третьего бита ACK/NACK ведомое устройство берет на себя управление шиной и записывает восемь битов данных за один прием (рис. 5б). При чтении из того же регистра ведомого устройства, что и в предыдущих операциях чтения, ведущему устройству достаточно записать адрес ведомого устройства, прежде чем прочесть данные с этого ведомого устройства.

В двухпроводном интерфейсе передача данных происходит по восемь бит за один прием (рис. 5). Интерфейс I2C поддерживает медленные (до 100 кбит/с), быстрые (до 400 кбит/с) и высокоскоростные (до 3,4 Мбит/с) протоколы. Интерфейс I2C распознает сигналы высокого и низкого уровня по уровням напряжения КМОП–логики: напряжение низкого уровня составляет менее 0,3 от напряжения питания; сигнал высокого уровня составляет более 0,7 от напряжения питания.

Интерфейс SMBus

Стандарт SMBus был разработан корпорацией Intel для низкоскоростного обмена данными, и интерфейс SMBus похож на I2C. Подобно I2C, в SMBus используется двухпроводной интерфейс с линией данных (SMBDATA) и линией синхронизации (SMBCLK). Для линий SMBCLK и SMBDATA также требуются нагрузочные резисторы. При питании от источника напряжением 3 В следует использовать нагрузочный резистор сопротивлением 8,5 кОм и более, а при питании от источника напряжением 5 В — 14 кОм и более. Шина SMBus питается от источника напряжением 3 или 5 В и распознает как сигнал высокого уровня напряжение выше 2,1 В, а как сигнал низкого уровня — напряжение ниже 0,8 В.

Главное отличие между интерфейсами I2C and SMBus заключается в величине таймаута, а также в минимальной и максимальной тактовой частоте. Шина I2C работоспособна на частотах до 0 Гц и не имеет таймаута по бездействию. В интерфейсе SMBus таймаут возможен. Тайм–аут происходит, когда ведомое устройство сбрасывает свой интерфейс после того, как сигнал синхронизации сохраняет низкий уровень в течение временного интервала, превышающего время ожидания (максимум 35 мс). Время ожидания интерфейса SMBus обусловливает минимальную тактовую частоту, равную 19 кГц. Для надлежащего обмена данными необходимо установить частоту сигнала SMBCLK в диапазоне от 10 до 100 кГц. Однако ведущее или ведомое устройство, подключенное к шине I2C, может удерживать сигнал синхронизации на низком уровне так долго, как это необходимо для обработки данных.

Примеры работы с периферийными устройствами

Микроконтроллеры часто ведут обмен данными с периферийными устройствами по последовательному интерфейсу. Используя трех– или двухпроводной интерфейс, микроконтроллер считывает и записывает данные во внутренние регистры периферийных устройств. Периферийные устройства далее устанавливают смещение различных аналоговых и цифровых выходов и управляют ими. Например, периферийные устройства программируют ток и напряжение зарядки батареи, управляют вращением вентилятора с помощью датчика температуры, устанавливают выходное напряжение ЦАП и напряжение смещения различных цепей.

На рис. 6 показан микроконтроллер, обменивающийся данными с 8–разрядным ЦАП (MAX5115) по двухпроводному интерфейсу. Поскольку у этого ЦАП имеются четыре адресных контакта, дающие 16 уникальных адресов ведомых устройств, возможно параллельное подключение до 16 ЦАП. Те же две линии шины можно было бы использовать для установки смещения контроллера вентилятора на базе датчика температуры с интерфейсом SMBus (MAX6641), поскольку MAX6641 имеет другой адрес как ведомое устройство. Этот контроллер управляет напряжением на затворе полевого МОП–транзистора для включения и выключения вентилятора.

Рис. 6. Поскольку данный микроконтроллер оборудован интерфейсом I^2C, для обмена данными с различными периферийными устройствами, такими как параллельно соединенные ЦАП и датчик температуры, достаточно двух линий шины
Рис. 6. Поскольку данный микроконтроллер оборудован интерфейсом I2C, для обмена данными с различными периферийными устройствами, такими как параллельно соединенные ЦАП и датчик температуры, достаточно двух линий шины

В то время как трехпроводной интерфейс требует наличия отдельных линий выбора кристалла для обмена данными между микроконтроллером и множеством параллельно соединенных ИС, более простой двухпроводной интерфейс использует те же линии данных и синхронизации для связи с каждым устройством на шине. Можно соединить параллельно несколько ИС, установив для каждого периферийного устройства свой адрес как ведомого устройства.

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

В новых конструкциях обеспечивается возможность использовать больше адресов ведомых устройств при меньшем количестве адресных контактов. Например, расширитель портов ввода/вывода MAX7319 можно запрограммировать на 16 уникальных адресов ведомого устройства, используя всего лишь два адресных контакта (AD2 и AD0). Эти контакты могут быть привязаны к «земле» (GND), напряжению питания (Vcc), SDA или SCL. В таблице 2 показано 16 доступных адресов ведомого устройства. Хотя разряды A6, A5 и A4 должны быть установлены в значение 110, разряды A3–A0 могут программироваться четырьмя различными состояниями контактов AD2 и AD0.

Таблица 2. Дешифрация 16 уникальных адресов по двум адресным линиям (AD2 и AD0) в MAX7319
Подключение контакта Адрес устройства
AAD2 AD0 A6 A5 A4 A3 A2 A1 A0
SCL GND 1 1 0 0 0 0 0
SCL Vcc 1 1 0 0 0 0 1
SCL SCL 1 1 0 0 0 1 0
SCL SDA 1 1 0 0 0 1 1
SDA GND 1 1 0 0 1 0 0
SDA Vcc 1 1 0 0 1 0 1
SDA SCL 1 1 0 0 1 1 0
SDA SDA 1 1 0 0 1 1 1
GND GND 1 1 0 1 0 0 0
GND Vcc 1 1 0 1 0 0 1
GND SCL 1 1 0 1 0 1 0
GND SDA 1 1 0 1 0 1 1
Vcc GND 1 1 0 1 1 0 0
Vcc Vcc 1 1 0 1 1 0 1
Vcc SCL 1 1 0 1 1 1 0
Vcc SDA 1 1 0 1 1 1 1

Перспективы на будущее

Сегодняшние трехпроводные интерфейсы служат для решения иных задач, чем двухпроводные интерфейсы, и каждый из типов интерфейсов обладает конкретными преимуществами. Маловероятно, что какой–либо из этих типов в будущем полностью вытеснит другой. Устройства с интерфейсом I2C прогрессируют быстрее за счет того, что начинают перенимать такие функции интерфейса SMBus, как сброс по тайм–ауту, которые могут быть отключены по необходимости. Новые адреса ведомых устройств с интерфейсом I2C имеют десятиразрядный формат вместо семиразрядного, обеспечивая пользователям еще большую гибкость.

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

Для получения дополнительной информации обращайтесь к официальным дистрибьюторам компании Maxim в России: www.maxim-ic.ru/contact.

Литература

  1. Указания по применению «Выбор последовательной шины» www.maxim-ic.com/an3967
  2. Указания по применению «Последовательные цифровые сети данных» www.maxim-ic.com/an3438

Скачать статью в формате PDF  Скачать статью Компоненты и технологии PDF

 


Сообщить об ошибке