Отправляет email-рассылки с помощью сервиса Sendsay

Цифровая схемотехника

  Все выпуски  

Цифровая схемотехника Выпуск 18


Информационный Канал Subscribe.Ru

Цифровая схемотехника
Почтовая рассылка
Выпуск №18 от 22.04.2005 г.


Добрый день, уважаемый подписчик!

Предлагаю вашему вниманию очередной выпуск рассылки. В этой рассылке мы начинаем рассматривать принципы построения различных узлов микропроцессорных устройств. Более подробнее вы можете узнать это все из моей книги "Разработка систем на микропроцессорах". А сейчас рассмотрим первый класс устройств, подключаемых к микропроцессору:

Кнопки и датчики

Ни одно микропроцессорное устройство обычно не обходится без кнопок и простей-ших датчиков, состоящих из простых контактов. При помощи этих элементов в микропро-цессорное устройство передается различная информация, которая «на ходу» должна изме-нять алгоритм работы микропроцессорной системы либо учитываться каким, либо другим образом. Примером может служить датчик поворота управляемого устройства. Допустим, наша микропроцессорная система должна управлять поворотом некоего поворотного уст-ройства. Для отслеживания реального угла поворота нам понадобится датчик. Самый про-стой способ построения такого датчика – это механические контакты, связанные с поворачи-ваемым устройством. Проще всего на валу устройства укрепить постоянный магнит, а на не-подвижной ее части расположить геркон. При вращении вала укрепленный на нем магнит будет проходить рядом с герконом, и вызывать срабатывание его контактов. Схема подклю-чения такого датчика к микроконтроллеру приведена на рисунке 1.2.
Рис 1.2.
Простая схема подключения датчика на основе геркона

Датчик представляет собой свободно разомкнутые контакты геркона. В приведенном примере датчик подключен к линии P1.0 порта P1 микроконтроллера. Именно через этот вход в данном случае микропроцессор производит считывание состояния датчика. Однако с таким же успехом такую схему включения можно было бы подключить к любой линии дан-ного порта, а так же к любой линии любого другого порта. В микросхеме AT89C2051 приве-денной в качестве примера, имеются два порта ввода вывода, которые обозначаются, как P1 и P3. Оба этих порта восьмиразрядные. Однако у порта P3 лишь семь линий выходят на внешние выводы микросхемы. Линия P3.6 наружу не выведена и поэтому не может быть ис-пользована для подключения внешних устройств.
Принцип работы схемы, приведенной на рис. 1.2 очень простой. В исходном состоянии контакты датчика разомкнуты. На вход микроконтроллера через резистор R1 подается на-пряжение от источника питания +5В. Микросхема воспринимает это напряжение как сигнал логической единицы. При срабатывании датчика контакты замыкаются и соединяют вывод микроконтроллера с общим проводом. В результате напряжение на входе P1.0 падает до ну-ля. Теперь микросхема воспринимает входной уровень сигнала, как логический ноль. Рези-стор R1 при этом служит токоограничивающим элементом, предотвращая короткое замыка-ние между шиной питания и общим проводом. Резистор R1 обязателен только в том случае, если датчик подключается к выводу P1.0 или к выводу P1.1. Только эти два вывода микро-схемы не имеют внутреннего нагрузочного резистора. Если для подключения датчика ис-пользовать любой другой вывод портов P1 или P3, кроме двух вышеперечисленных, то рези-стор R1 можно не устанавливать.
Теперь рассмотрим работу схемы с точки зрения микросхемы микроконтроллера. Как известно, каждый вывод любого порта практически любой микросхемы микроконтроллера является одновременно и входом и выходом. Причем выходная схема микроконтроллера представляет собой каскад с открытым коллектором. Если программно подать на такой вы-ход уровень логической единицы, то выходной транзистор закроется, и не будет мешать ра-боте схемы на ввод информации. Подробно этот вопрос освещается в [1].
Далее рассмотрим простейшую программу для обслуживания датчика изображенного на рис. 1.1 (см. Листинг 1.1).

Листинг 1.1

  ; Программа обработки сигнала с датчика
1   mov p1.0,#1 ; Записываем 1 в соответствующий разряд порта
2 m1: mov c,p1.0 ; Читаем состояние датчика в битовый аккумулятор
3   jc m2   ; Если контакты датчика разомкнуты, перейти к m2
4   call proc1 ; Вызов процедуры обработки нажатия контакта
5   jmp m1 ; Возврат к началу (следующий цикл считывания)
6 m2: call proc2 ; Вызов процедуры обработки размыкания контакта
7   jmp m1 ; Возврат к началу (следующий цикл считывания)

Как легко видеть из текста программы, первый оператор записывает в линию 0 порта P1 сигнал логической единицы. Как уже говорилось, это необходимо для правильной работы линии на ввод. Следующая команда считывает бит информации с этой же линии и помещает его в битовый регистр признака переноса. Этот регистр часто используется как аккумулятор для битовых операций. Если в момент считывания сигнала контакты датчика были разомк-нуты, то, как уже говорилось выше, на входе P1.0 микроконтроллера будет логическая еди-ница. Если контакты замкнуты, то этот сигнал будет равен логическому нулю. В результате тот же сигнал окажется в регистре c микроконтроллера. Оператор условного перехода jc осуществляет оценку состояния контактов. Если в регистре c логический ноль, то управле-ние передается на метку m2. то есть далее выполняется оператор call proc2. Если же там единица, то передачи управления не происходит и выполняется следующий по порядку опе-ратор call proc1. Операторы call – это вызов подпрограммы. Подпрограмма proc1 вы-полняет действия, которые должны происходить при срабатывании датчика. Например, под-счет количества срабатываний. Подпрограмма proc2 включена сюда как иллюстрация од-ного из возможных вариантов. Обработка момента размыкания контактов может понадо-биться при реализации антидребезговых алгоритмов и для некоторых других специальных целей. В простейших случаях ее можно просто исключить. Тексты подпрограмм в данном примере не показаны. Для каждого конкретного применения они различны.

Схема подключения контактов датчика, приведенная на рис. 1.2 универсальна и широ-ко применяется для подключения не только контактных датчиков, но и кнопок управления. Редкое микропроцессорное устройство обходится без кнопок управления. При помощи та-ких кнопок могут переключаться режимы работы вашего устройства. Они могут служить кнопками «Пуск» и «Стоп». И так далее. Если вам нужно иметь несколько кнопок управле-ния, вы можете подключить их к разным входам микроконтроллера. Если количество кнопок не слишком велико, то приведенный способ их включения – самый рациональный. Однако, если количество управляющих клавиш слишком велико, то вам просто может не хватить имеющихся выводов. В этом случае не обойтись без матрицы клавиш. На рисунке 1.3 приве-дена схема подключения клавиатуры из 32 клавиш путем составления из них матрицы.

Рис 1.3.
Схема подключение клавиатуры в виде матрицы клавиш

На всякий случай напоминаю, что цепи сброса и цепи, связанные с кварцевым резона-тором на этой схеме, как и на всех последующих, условно не показаны. Как видно из схемы, для подключения тридцати двух клавиш используется всего лишь 12 выводов. В данном конкретном случае порт P1 работает как порт ввода. Четыре младшие разряда порта P3 ра-ботают на вывод. Теоретически возможно и наоборот. Но с точки зрения удобства составле-ния обслуживающей программы, такое построение более рационально. Давайте это рассмот-рим подробнее.
В исходном состоянии на выводы P3.0 - P3.3 подается сигнал логической единицы. На все выводы порта P1 (P1.0 – P1.7) так же поданы единицы. Это обеспечивает возможность работы этих выводов в режиме ввода. Контроллер периодически опрашивает состояние кла-виш и в случае обнаружения замыкания контактов одной из кнопок, выполняет закреплен-ные за этой кнопкой действия. В том случае, если в момент опроса нажатыми окажутся не-сколько кнопок, никаких действий не выполняется. Такой алгоритм используется в боль-шинстве современных клавиатур.
Каким же образом осуществляется опрос клавиш? Процедура опроса клавиш пооче-редно переводит линии P3.0 – P3.3 в нулевое состояние. Сначала в нулевое состояние пере-водится линия P3.0. Сразу после этого контроллер производит чтение числа с порта P1. Если не одна из кнопок не нажата, то все разряды считанного числа будут равны единице. То есть оно будет равно 0FFH. Если одна из кнопок (K1 – K8) окажется нажатой, то число, прочи-танное из порта P1, будет отличаться от 0FFH. Предположим, что мы нажмем кнопку K1. Тогда сигнал логического нуля с выхода P3.0 поступит на вход P1.0. Младший разряд счи-танного числа в этом случае будет равен нулю. Считанное из порта число будет равно 0FEH. Нажатие других клавиш этой вертикали (K2 – K8) приведет к обнулению других разрядов считываемого числа. В результате для разных комбинаций клавиш мы получим разные счи-тываемые коды.
Аналогичным образом микроконтроллер опрашивает остальные вертикали клавиш. То есть, на следующем этапе, процессор выставляет нуль на линии P3.1, а на линию P3.0 воз-вращает единицу. Считанное после этого из порта P1 число, будет определять состояние кнопок K9 – K16. Затем ноль выставляется на линии P3.2 и проверяется состояние кнопок K17 – K24. И в заключении ноль выставляется на линии P3.3 и проверяется состояние кно-пок K25 – K32.

Теперь рассмотрим Листинг 1.2. Эта программа представляет собой один из вариантов реализации процедуры опроса клавиатуры. Эта процедура оформлена в виде отдельной под-программы, к которой при необходимости обращается основная программа микроконтрол-лера. При каждом обращении к подпрограмме, она производит однократное сканирование всей клавиатуры и завершает свою работу. После завершения работы подпрограммы регистр аккумулятора (то есть регистр A) содержит код состояния клавиатуры. Если в момент скани-рования клавиатуры не одна кнопка не была нажата, то возвращаемый код состояния будет равен нулю. Если нажато сразу несколько кнопок, это считается недопустимым и в этом случае программа так же возвращает ноль. И только в том случае, если нажата всего одна кнопка, то возвращаемый подпрограммой код состояния будет равен номеру нажатой кноп-ки. Описанный выше алгоритм является стандартным для клавиатуры, состоящей из матри-цы клавиш.
Итак, рассмотрим подробнее текст программы. На самом деле она состоит из основной подпрограммы и нескольких вспомогательных. Оператор в строке 1 служит для присоедине-ния файла описаний. Подробнее о действии этого оператора и о файле описаний смотрите в [1]. В строках 2…8 происходит определение необходимых констант и резервирование ячеек памяти. На самом деле в реальной программе и констант и ячеек памяти будет гораздо больше. В приведенном примере показаны лишь те операции, которые необходимы для ра-боты нашей подпрограммы. В строках 2…5 программы определяется четыре константы. В данном случае - это коды, соответствующие четырем банкам регистров РОН. В нашей под-программе используется лишь одна из них.
В строках 6…8 производится резервирование ячейки памяти. Эта ячейка будет служить буфером порта P3. На этом моменте хочу остановиться подробнее. Взгляните еще раз на схему подключения клавиатуры (рис. 1.2). Как видно из схемы, для подключения клавиату-ры мы использовали почти все информационные выводы микросхемы. Но микропроцессор-ное устройство, содержащее только одну клавиатуру, не имеет никакого смысла. Не доста-точно просто вводить информацию. Нужно, что бы наше устройство еще, чем ни будь, управляло. Иначе, зачем клавиатура? Но это не беда! У нас еще остается три старших разря-да: P3.4, P3.5 и P3.7. Не густо, но в некоторых случаях этого вполне достаточно. Однако нам придется обеспечить независимое управление старшими и младшими разрядами порта P3.
Есть несколько способов, как раздельно управлять старшими и младшими разрядами одного и того же порта. Можно применять битовые операции и управлять по очереди каж-дым разрядом. В данном случае удобнее применять метод буфера. Этот метод состоит в том, что код, предназначенный для вывода в порт храниться в специальной ячейке памяти, слу-жащей буфером. Перед тем, как вывести код непосредственно в порт, его значение вычисля-ется в буфере. Как это делается, мы увидим чуть позже.
А сейчас рассмотрим основную подпрограмму процедуры опроса клавиатуры (строки 12…29). Вызов процедуры производится путем обращения к подпрограмме по метке klav. В строке 12 происходит сохранения регистра psw в стеке. Сразу за этим (строка 13) в этот регистр записывается константа bank3, что приводит к переключению на третий банк РОН. В конце процедуры, непосредственно перед выходом из основной подпрограммы (строка 28) состояние регистра psw восстанавливается. Вместе с этим регистром восстанавливается тот банк регистров, который был установлен до вызова подпрограммы. Таким образом, наша процедура использует для своей работы третий банк регистров. При написании программ рекомендуется по возможности распределить все банки регистров между крупными проце-дурами. Например, банк 3 закрепить за процедурой ввода с клавиатуры. Банк 2 для процеду-ры вывода. Банк 1 – для процедуры обслуживания таймера. Банк 0 обычно используют в ос-новном теле программы. В результате снижается опасность того, что вызванная процедура случайно изменит содержимое ячейки, где уже хранится важная информация.

Листинг 1.2

1   $mod2051    
  ;--------------------- Определение констант
2 Bank0 EQU 00000000B ; Коды банков памяти
3 Bank1 EQU 00001000B  
4 Bank2 EQU 00010000B  
5 Bank3 EQU 00011000B  
 
;-------------------- Резервирование ячеек памяти
6   DSEG    
7   ORG 20H ; Начинаем резервирование с адреса 20H

8

p3buf: DS 1 ; Буфер порта p3
  ;----------- Начало программного кода
9   CSEG    
10   ORG 00H ; Начинаем программный код с адреса 00H
 
;-----------------------------------------------------------------
; В этом месте вы должны поместить основной текст вашей программы
;-----------------------------------------------------------------
11   ORG 20H ; Продолжаем программный код с адреса 20H
 

;#######################################
;## Подпрограмма опроса клавиатуры ##
;#######################################
12 klav: push psw ; Сохранение регистра флагов в стеке
13   mov psw,#bank3 ; Переключение на Банк 3 регистров РОН
14   mov p1,#0FFH ; Перевод P1.0-P1.7 в единичное состояние
15   call clrU ; Перевод P3.0–P3.3 в единичное состояние

16

  mov r0,#0 ; Очистка буфера кода клавиши
17   mov r1,#4 ; Инициализация счетчика столбцов
18   mov r2,#0FEH ; Код сканирование первого столбца
19 kl1: call setU ; Вывод кода в порт P3
20   mov a,p1 ; Считывание состояния клавиатуры
21   cjne a,#0FFH,kl3 ; Если клавиша нажата, переходим к kl3
 
;------------ Переход к следующему столбцу
22 kl2: mov a,r2 ; Извлечение кода
23   rl a ; Сдвиг
24   mov r2,a ; Записать назад в буфер
25   djnz r1,kl1 ; Команда цикла опроса столбцов
 
;------------ Окончание процедуры опроса клавиатуры
26 klfin: call clrU ; Перевод в исходное состояние порта P3
27   mov a,r0 ; Запись в аккумулятор кода клавиши
28   pop psw ; Восстановление регистра флагов
29   ret   ; Выход из программы
 
;------------ Нахождение номера клавиши в строке
30 kl3: mov r3,#1 ; Инициализация счетчика строк
31 kl4: setb c ; Обнуление признака переноса
32   rrc a ; Сдвиг входного кода
33   jnc kl5 ; Если нашли разряд, равный нулю перейти к kl5
34   inc r3 ; Итерация счетчика строк
35   jmp kl4 ; Переход к началу цикла счета строк
36 kl5: cjne a,#0FFH,kl6 ; Если еще есть хоть один ноль, перейти к kl6
37   mov a,r0 ; Не найдена ли уже другая нажатая кнопка
38   jnz kl6 ; Если да, то перейти к kl6
 
; ----------- Вычисление номера клавиши
39   mov a,#4 ; Запись константы в аккумулятор
40   clr c ; Очистка признака переноса
41   subb a,r1 ; Вычисление номера столбца
42   rl a ; Три оператора сдвига (умножение на 8)
43   rl a  
44   rl a  
45   add a,r3 ; Прибавление номера строки
46   mov r0,a ; Записываем в буфер
47   jmp kl2 ; Продолжаем поиск по остальным столбцам
48 kl6: mov r0,#0 ; Возвратить ноль
49   jmp klfin ; Перейти на конец процедуры
 
;----------- Сброс разрядов столбцов
50 clrU: mov a,p3buf ; Считывание содержимого буфера порта P3
51   orl a,#0FH ; Перевод разрядов P3.0–P3.3 в единицу
52   mov p3buf,a ; Запись результата назад в буфер
53   mov p3,p3buf ; Вывод содержимого буфера в порт P3
54   ret    
 
;------------ Вывод столбцов
55 setU: mov a,p3buf ; Считывание содержимого буфера порта P3
56   orl a,#0FH ; Перевод разрядов P3.0–P3.3 в единицу
57   anl a,r2 ; Вывод в P3.0-P3.3 кода сканирования
58   mov p3buf,a ; Запись результата назад в буфер
59   mov p3,p3buf ; Вывод содержимого буфера в порт P3
60   ret    
  ;-----------------------------------------------------------------
; Сюда вы можете поместить другие подпрограммы.
;-----------------------------------------------------------------
61   end    

Но продолжим описание подпрограммы опроса клавиатуры. В строках 14 и 15 происхо-дит инициализация всех выводов, связанных с клавиатурой. Сначала (строка 14), в порт P1 выводится код 0FFH, что переводит все выходы в единичное состояние. После этого потр P1 готов работать на ввод сигнала. В строке 15 производится вызов подпрограммы clrU, кото-рая устанавливает разряды P3.0-P3.3 в единичное состояние. Текст этой подпрограммы при-веден в строках 50…54. Вспомогательная подпрограмма clrU производит предварительное вычисление в буфере p3buf. Она устанавливает младшие разряды числа, хранящегося в буфере, в единичное состояние, а затем выводит это число в порт P3. Значения старших раз-рядов числа в буфере устанавливают другие программы. Подпрограмма clrU сделана так, что не изменяет их значения.
В строках 16…18 производится подготовка к циклу сканирования столбцов клавиатуры. Регистр r0 используется, как буфер кода состояния клавиатуры. Код состояния хранится в этом регистре во время всей процедуры и только в конце помещается в аккумулятор. В на-чале сканирования туда помещается ноль. Регистр r1 используется как счетчик количества циклов сканирования. У нас четыре ряда, а значит, нам нужно совершить четыре цикла ска-нирования. Поэтому в r1 помещается константа 4. В регистр r2 помещается тот самый код, который мы будем выводить в младшие разряды порта P3, в процессе сканирования. В пер-вом цикле опроса рядов нам, как уже говорилось, нужно подать ноль на линию P3.0 и еди-ницу на все остальные линии порта. В результате нам потребуется число 11111110B. В ше-стнадцатеричном виде это будет равно 0FEH.
Строки 19…25 – это, собственно, цикл опроса столбцов клавиатуры. Цикл начинается с вызова вспомогательной подпрограммы setU. Подпрограмма производит вывод младших разрядов числа из регистра r2 в порт P3. При этом производится предварительное вычисле-ние с использованием буфера p3buf. В процессе вычисления четыре младшие разряды бу-фера становятся равными четырем младшим разрядам регистра r2, а старшие разряды бу-фера остаются без изменения. Затем, так же, как и в подпрограмме clrU, содержимое буфе-ра выводится в порт P3. Таким образом, происходит раздельное управление старшими и младшими разрядами порта.
В строке 20 производится считывание состояния клавиатуры в аккумулятор. Если в дан-ном столбце ни одна клавиша не нажата, то считанное число будет равно 0FFH. Если хотя бы одна клавиша нажата, то число будет другое. В строке 21 происходит проверка считанно-го числа. Если оно не равно 0FFH, то происходит переход по метке kl3,то есть к началу процедуры определения номера строки, в которой нажата клавиша. Если клавиши не нажа-ты, то цикл сканирования продолжается. В строках 22…24 производится вычисление нового кода для вывода в разряды P3.0-P3.3. Для этого код вызывается в аккумулятор, там происхо-дит его сдвиг на разряд влево и полученный новый код записывается назад в регистр r2.
В строке 25 происходит завершение цикла. Оператор djnz уменьшает содержимое реги-стра r1 на единицу. Если содержимое регистра не достигло нуля, то управление передается на начало цикла (kl1). Если это последний цикл, программа переходит к командам оконча-ния процедуры: В строке 26 вызывается уже знакомая нам подпрограмма clrU, устанавли-вающая младшие разряды порта P3. В строке 27 происходит помещение кода клавиатуры в аккумулятор. И далее завершающие операторы восстановления psw и выхода из подпро-граммы (строки 28, 29).
Рассмотрим теперь подробнее процедуру нахождения строки нажатой клавиши. Она за-нимает строки с 30 по 49. Эта процедура вступает в действие, когда обнаружится, что код состояния очередного столбца, не равен 0FFH. Это означает, что в этом столбце нажата одна или несколько клавиш. Если нажата одна клавиша, то процедура вычисляет ее номер. Если нажато больше чем одна клавиша, то происходит переход к завершающим операторам (строки 26…29) с возвращением нулевого кода. Рассмотрим подробнее работу этой проце-дуры. Сначала определяется номер первой нажатой клавиши. При этом регистр r3 исполь-зуется в качестве счетчика. В начале процедуры (строка 30) счетчику присваивается началь-ное значение. В строках 31…35 организован цикл сдвига кода состояния столбца клавиату-ры. Оператор rrc производит сдвиг кода через ячейку признака переноса с. Вначале каждо-го цикла сдвига ячейка признака переноса устанавливается в единицу (строка 31). Это гаран-тирует заполнение освободившихся разрядов единицами. В строке 33 происходит проверка признака переноса. Если после очередного цикла сдвига там окажется ноль, то сдвиг пре-кращается и управление передается на строку 36 (метка kl5). В строке 34 происходит под-счет количества произведенных сдвигов. Это количество равно номеру разряда в считанном коде, содержащего ноль, а значит и номеру первой нажатой клавиши.
После всех этих сдвигов первый нулевой бит окажется в ячейке признака переноса. Если больше не было нажатых клавиш, то в остальных разрядах числа будут единицы. В строке 36 проверяется этот факт. Если это не так, то управление передается по метке kl6 (строки 48, 49), где происходит запись нулевого кода в буфер состояния клавиатуры и переход к окончанию всей процедуры (возврат нуля). Если не одна кнопка в столбце больше не нажа-та, то в строках 37, 38 выполняется проверка кода в регистре r0. Дело в том, что возможна ситуация, когда на клавиатуре нажаты две или более клавиши, но в разных столбцах. Имен-но это и проверяется в строках 37, 38. К этому моменту мы определили, что в очередном столбце нажата одна и только одна клавиша. Но, если при сканировании предыдущих столб-цов уже была обнаружена другая нажатая клавиша, то ее код будет находиться в регистре r0. Итак, для того, что бы считать процедуру успешной, нужно, что бы в регистре r0 был нулевой код. Для этого в строке 37 он извлекается в аккумулятор и в строке 38 оценивается. Если там не ноль, то происходит переход по уже известному адресу kl6, к командам выхода из подпрограммы с нулевым результатом.
И, наконец, если программа прошла все испытания, и определила, что мы имеем дела с единственной нажатой клавишей, программа переходит к вычислению кода нажатой клави-ши (строки 39…47). Вычисление производится по простой формуле: номер столбца умножа-ется на восемь и к полученному результату прибавляется номер строки. Номера столбца в чистом виде у нас нет. Однако в регистре r1 содержится счетчик цикла опроса столбцов. Вначале он равен четырем. После опроса каждого столбца содержимое счетчика уменьшает-ся на единицу. Очевидно, сто для определения номера столбца нужно произвести простую арифметическую операцию: из числа 4 вычесть содержимое регистра r1. Именно это и про-исходит в строках 39…41. Оператор subb (строка 41) производит вычитание с учетом при-знака переноса. Поэтому в строке 40 этот признак сбрасывается в ноль. В строках 42…44 происходит тройной сдвиг содержимого аккумулятора влево. Это равносильно умножению его содержимого на 8. В строке 45 к полученному результату прибавляется содержимое ре-гистра r3, где находится номер строки. В строке 46 номер нажатой клавиши записывается в буфер кода состояния (регистр r0). Команда jmp в строке 47 передает управление в точку kl2. В результате цикл опроса столбцов продолжается.

Надеюсь все вышесказанное вам было понятно и интересно. Если что то не так, извините. Дело в том, что данная статья - это выдержка из моей книги "Разработка устройств на микропроцесорах". Напишите пожалуйста мне о ваших впечатлениях, замечаниях и пожеланиях. Мой адрес   avbelov@ukr.net


Если у вас в этом письме не открываются картинки, и вы не знаете, что делать, перейдите по ссылке и прочитайте подробную инструкцию о том, как добиться появления картинок.

Автор рассылки Белов А.В.         avbelov@ukr.net


http://subscribe.ru/
http://subscribe.ru/feedback/
Подписан адрес:
Код этой рассылки: tech.digitchip01
Отписаться

В избранное