Например TDA7294

Форум РадиоКот • Просмотр темы - Опрос клавиатуры и энкодеров по SPI
Форум РадиоКот
Здесь можно немножко помяукать :)





Текущее время: Вт апр 16, 2024 12:56:21

Часовой пояс: UTC + 3 часа


ПРЯМО СЕЙЧАС:



Начать новую тему Ответить на тему  [ Сообщений: 8 ] 
Автор Сообщение
Не в сети
 Заголовок сообщения: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Чт янв 11, 2018 11:54:50 
Встал на лапы
Аватар пользователя

Карма: 11
Рейтинг сообщений: 68
Зарегистрирован: Чт окт 09, 2014 11:29:45
Сообщений: 98
Откуда: Рязань
Рейтинг сообщения: 0
При разработке самодельных устройств на микроконтроллере, часто возникает проблема подключения к нему большого количества элементов управления, таких как кнопки и инкрементальные энкодеры, и ограниченного количества свободных выводов контроллера. Существует много способов решения этой задачи, таких как: использование микроконтроллера с большим количеством выводов; матрицирование элементов управления; использование мультиплексоров; и т.д. Но у всех этих способов есть свои недостатки. В этой статье, я хочу предложить еще один способ решения задачи, лишенный, по моему мнению, многих из этих недостатков. Суть этого метода, заключается в использовании распространенного, последовательного интерфейса SPI микроконтроллера c применением микросхем десятичных счетчиков.
Ниже, приводится схема, успешно использованная в одной из моих поделок. Схему немного доработал и упростил, но должна работать:
Изображение
Основными элементами этой схемы являются недорогие микросхемы — десятичные счетчики: HEF4017B и такой-же, недорогой и доступный буферный элемент с тремя состояниями на выходе: SN74LVC1G125DBVR.

Линии SCK, KBD_CS, EXT_INT, MISO подключаются к выводам микроконтроллера. SCK и MISO – стандартные сигналы интерфейса SPI и должны быть подключены к соответствующим выводам МК. Линия EXT_INT должна быть подключена к выводу контроллера, способному генерировать внешнее прерывание на переход с логической 1 на 0. С помощью этого сигнала, контроллер узнает, что произошло нажатие какой либо кнопки или поворот ручки энкодера и запускает процедуру опроса элементов управления. Линия управления схемой KBD_CS, по назначению, аналогична стандартному сигналу CS (Chip Select), используемому во всех схемах подключаемых по SPI и подключается к любому управляемому выводу микроконтроллера.

При логической единице на линии управления KBD_CS, схема находится в режиме ожидания. Выход схемы MISO находится в высокоимпедансном состоянии и интерфейс SPI свободен для общения контроллера с другими функциональными блоками. В этом режиме все выходы десятичных счетчиков находятся в состоянии логического нуля, а выход EXT_INT, наоборот, притянут к логической единице на линии KBD_CS, через резистор R2. Таким образом, при нажатии любой кнопки или замыкании контактов любого энкодера, выход EXT_INT притянется к логическому нулю на соответствующем выводе счетчика, а в контроллере сработает прерывание на переход с логической 1 на 0.
При срабатывании внешнего прерывания вызванного изменением состояния одного из элементов управления, в обработчике вектора этого прерывания, контроллер выдает на линию KBD_CS логический ноль, тем самым переводя схему в рабочий режим и запускает процедуру опроса состояний элементов управления. По завершении опроса, контроллер должен вновь выдать логическую единицу на линию KBD_CS, тем самым переводя схему в режим ожидания.

В рабочем режиме схемы, когда на линии KBD_CS логический ноль, этот сигнал поступает на вход OE выходного буферного элемента, и он переводится в режим передачи состояния со входа A на выход Y подключенного к линии MISO. В этом режиме, логический ноль с линии KBD_CS поступает на управляющий вывод MR счетчика IC3 и он так-же переводится в рабочий режим. В этот момент, выход O0 счетчика IC3 переходит в состояние логической единицы, а на всех остальных выходах этого счетчика, как и на всех выходах счетчика IC2, остается логический ноль. Таким образом, счетчики приводятся в исходное состояние, и схема готова к опросу.

При обмене по SPI, контроллер посылает счетные импульсы по линии SCK и побитно передает данные из своего выходного регистра по линии MOSI и принимает данные во входной регистр по линии MISO. В нашем случае никакие данные из контроллера в схему не передаются, поэтому линия MOSI не используется. Передачу по SPI в контроллере необходимо настроить таким образом, чтобы прием и передача битов информации происходила по переднему нисходящему фронту счетного импульса, а сдвиг данных в регистрах — по заднему восходящему фронту.

Наши десятичные счетчики устроены таким образом, что при прохождении восходящего фронта счетного импульса по входу CP0, очередной по порядку выход On переводится в состояние логической 1, а предыдущий выход On-1 возвращается в состояние логического 0. Таким образом, при прохождении счетных ипульсов SCK интерфейса SPI, во время опроса состояния схемы контроллером, логическая единица, последовательно перепрыгивает с одного выхода счетчика на следующий. И если этот выход счетчика замкнут кнопкой или контактами энкодера со входом A выходного буфера, то логическая единица передается на выход Y буфера и на выход MISO схемы, где и забирается контроллером в момент прохождения нисходящего фронта следующего счетного импульса. Если-же контакты управляющего элемента связанного с очередным выходом счетчика разомкнуты, то на входе A выходного буфера остается логический ноль, т.к. он, через диод VD4 и резистор R3 притянут к нулю на KBD_CS, и этот ноль контроллер заберет на линии MISO на следующем шаге.

Теперь разберемся с остальными элементами схемы:
Конденсатор C1 служит для гашения паразитных импульсов вызванных дребезгом контактов управляющих элементов во время нажатия кнопок или поворота ручки энкодера.
Диод VD1 припятствует прохождению положительных импульсов напряжения на кондесатор C1 во время опроса, препятствуя его заряду и отдаче положительного уровня обратно в схему при разряде.
Резисторы R5 – R15 служат для гашения тока короткого замыкания на выводах счетчиков, во время опроса схемы, в случае замыкания контактов сразу нескольких управляющих элементов.
Практическое применение инкрементальных энкодеров показало, что в ряде случаев их контакты могут оставаться замкнутыми после поворота ручки и ее отпускания. Это приводит к тому, что при замыкании контактов другого управляющего элемента прерывание не срабатывает. Это происхдит потому, что линия прерывания остается прижатой к нулю и ее невозможно подтянуть к единице для следующего срабатывания. Для обхода этой неприятности служат цепи R16, C2 и R17, C3. Эти цепи прозрачны для импульсов тока протекающего через конденсаторы в момент замыкания контактов и при смене логического состояния выхода счетчика, но затрудняют протекание постоянного тока при долговременном эамыкании цепи что позволяет подтянуть линию EXT_INT к логической единице для срабатывания прерывания.
На элементах R4, VD2, VD3 собрана логическая схема монтажного И, служащая для передачи управления со счетчика IC3 на счетчик IC2 по завершении восьмого по счету импульса SCK. Таким образом реализуется возможность получения нескольких последовательных байтов данных с элементов управления связанных с разными счетчиками. Т.е. количество опрашиваемых элементов управления можно практически, неограниченно увеличивать, путем такого последовательного подключения десятичных счетчиков. Более подробное описание последовательного подключения см. в даташите на HEF4017B.

Теперь самый сложный в понимании момент:
Для понимания направления вращения ручки энкодера, нам необходимо знание состояниия замкнутости обеих цепей между его выводами 3-2 и 1-2. Но совсем не обязательно, и даже вредно, каждый раз вызывать прерывание, как при замыкании цепи выводов 3-2 так и при замыкании цепи выводов 1-2. Для того, чтобы знать, что ручку энкодера повернули в ту или иную сторону, достаточно срабатывания прерывания в момент замыкания одной из этих цепей. В нашем случае, прерывание вызывается только при замыкании цепи выводов 3-2. А цепи выводов 1-2 обоих энкодеров, когда схема находится в режиме ожидания, разорваны закрытым транзистором VT1. Но в рабочем режиме схемы, в исходном ее состоянии, когда вывод O0 счетчика IC3 находится в состоянии логической единицы, транзистор открыт за счет протекания тока через переход эмиттер-база и резистор R1. (Диод VD5 блокирует обратный ток через транзистор и R1 на KBD_CS). Таким образом, при опросе, контроллер сможет узнать, замкнута или разомкнута цепь выводов 1-2 одного из энкодеров. Т.к. цепи выводов 1-2 обоих энкодеров подключены к выходу O0 первого по порядку опроса счетчика, соответствующего нулевому биту входного регистра интерфейса SPI. То значение 1 в нем будет означать, что цепь выводов 1-2 одного из энкодеров замкнута, а значение 0, будет означать, что эта цепь разомкнута. Следовательно, по состоянию нулевого бита первого принимаемого по SPI байта, мы можем узнать в какую сторону повернут один из энкодеров. Но какой-же энкодер был повернут? Это уже проще. Т.к. цепи выводов 3-2 наших энкодеров не объединены в одну, как цепи выводов 1-2, и каждая из них подключена к собственному выходу счетчика, то по состоянию битов принятого байта, сопоставленным этим выходам, мы однозначно сможем определить, какой из энкодеров был повернут.

Для наглядности, рассмотрим рисунок ниже. На нем изображены временные диаграмы состояний цепей выводов 1-2 и 3-2 энкодера ENC1 при повороте его ручки в ту и другую стороны:
Изображение
Цепь выводов 3-2 энкодера ENC1 подключена к выходу O1 первого по порядку опроса счетчика. Следовательно, если первый бит первого принятого по SPI байта равен 1, то это значит, что была повернута ручка именно энкодера ENC1. А по состоянию нулевого бита можно узнать в какую сторону была повернута эта ручка. Если нулевой бит равен 1, то ручка была повернута по часовой стрелке. А если он равен 0, то ручку повернули против часовой стрелки.
Таким же образом можно узнать и состояние энкодера ENC2, приняв, что его линии выводов 3-2 подключены к выходу O2 счетчика и отображаются на второй бит первого принятого байта. Значение байта 0x05 (b00000101) говорит, что ручка ENC2 повернута по часовой стрелке. А значение — 0x04 (b00000100) говорит, что ручка ENC2 повернута против часовой стрелки.

Ниже приведу поясняющие куски исходного кода программы:
Спойлер
Код:
#define me_none         0x0000
#define me_btn_left     0x0400
#define me_btn_right    0x0200
#define me_btn_chn      0x0010
#define me_btn_enc2     0x0080
#define me_btn_enc1     0x0040
#define me_enc2_left    0x0004
#define me_enc2_right   0x0005
#define me_enc1_left    0x0002
#define me_enc1_right   0x0003
#define me_startstop    0x0008
#define me_mode         0x0020
#define me_pwr_off      0x0800
#define COUNTER_SPI_PREPARE SPCR = (0<<SPIE) | (1<<SPE) | (1<<DORD) | (1<<MSTR) | (0<<CPOL) | (0<<CPHA) | (0<<SPR1) | (1<<SPR0)

uint16_t menu_event = me_none; // двухбайтный код состояния клавиатуры и энкодеров

// обработчик прерывания от кнопок и энкодеров
// External Interrupt 6 service routine
interrupt [EXT_INT6] void ext_int6_isr(void)
{
    uint8_t h,l;
    if(!TSTBIT(BTN_INT_PIN, BTN_INT0_PIN) && !menu_event)
// если нога EXT_INT притянута к нулю и прежнее состояние обработано
    { 
        CLRBIT(PORTF,5);  // переводим схему в рабочее состояние
        delay_us(10);             // задержка для пропуска переходных процессов в схеме
        COUNTER_SPI_PREPARE;  // настраиваем SPI
        l  = spi(0x00);      // запрашивыем первый   
        h  = spi(0x00);       // и второй байты данных
        menu_event = (h << 8) | (l & 0x00FF); // формируем единый код состояния блока управления
        SETBIT(PORTF,5); // переводим схему в режим ожидания
    }

}


//главный модуль
void main(void)
{
  ...
  while (1)
  {
    if(menu_event != me_none) // если есть не обработанное состояние
    {   
        menu_handle(menu_event); // то обрабатываем его
        menu_event = me_none; // и сбрасываем. состояние обработано
    }
    ...
  }
}
Еще несколько замечаний:
Если опрашивать только кнопки, то все просто и логично как 3 копейки. Но если используются энкодеры, то это самые быстро меняющиеся управляющие элементы. И поэтому их надо опрашивать как можно быстрее, и подключать в самое начало опроса. Емкость конденсатора C1 напрямую зависит от качества управляющих элементов. При использовании энкодеров ее надо подбирать особенно тщательно, чтобы дребезг, как можно более эффективно гасился, и в то-же время не задерживался опрос энкодеров. Оптимальное значение емкости C1 лежит в диапазоне 100 — 1000 pf. Если используются только кнопки, время опроса особо экономить не требуется. В этом случае можно выбрать значение C1 = 0.1uf и не париться. Так-же, необходимо правильно выбрать скорость работы SPI (частоту SCK), чтобы она не превышала максимальную частоту срабатывания счетчиков и в то-же время не тормозила опрос энкодеров.

Теперь, как-бы, надо подвести итоги:
Считаю, что получилось вполне жизнеспособное, достаточно элегантное, универсальное и, тем не менее, очень недорогое решение проблемы кнопок и энкодеров. Не лишенное некоторых недостатков, но и обладающее неоспоримыми преимуществами, перед методами программного ногодрыга и неоправданного расточительства пинов. Главное достоинство этого решения — экстремально малое число необходимых пинов контроллера для опроса, теоретически, неограниченного количества управляющих элементов. Учитывая то, что итерфейс SPI довольно широко используется, и с ним обычно работает сразу несколько различных функциональных блоков устройства, и можно считать, что линии SCK и MISO, часто, достаются на халяву. То опрос кучи кнопок и энкодеров по двум оставшимся проводам (причем один из них — линия прерывания и в опросе не учавствует) — это совсем неплохо. Метод очень универсален в применении микроконтроллеров. Даже если угораздит нарваться на контроллер лишенный SPI, его легко реализовать программно. И очень легко расширяем.
Конечно, есть недостатки. Нельзя, например, одновременно, двумя руками крутить энкодеры, и нажимать одновременно несколько кнопок. Но это не такой-уж большой недостаток, не приводящий к каким нибудь фатальным последствиям и легко отсекающийся программно. Неправильная комбинация просто не примется и ничего не произойдет. Пока не придумал как можно сделать матрицу элементов с использованием счетчиков (типа, один счетчик на столбцы, другой на строки) без применения большого количества дополнительных деталей. Наверное не получится зацепить большое количество энкодеров. Наверное есть еще...

Наверно скажете "фу - какое-то любительство". Но зато, теперь я точно знаю, как буду зацеплять кнопки в своих будущих поделках.

На этом все. Простите, что так много букв и начинайте ругать! :)


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Чт янв 11, 2018 15:26:03 
Друг Кота

Зарегистрирован: Вт мар 13, 2012 12:16:13
Сообщений: 6865
Откуда: .ru
Рейтинг сообщения: 0
Я конечно извиняюсь..)) Но при современных ценах на контроллеры с кучей пинов... менее 50 рублей/штука... https://ru.aliexpress.com/item/ATMEGA12 ... autifyAB=0 делать отдельный блок на счётчиках... мне не кажется дешёвым... и тем более проще... ))


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Чт янв 11, 2018 15:41:41 
Ум, честь и совесть. И скромность.
Аватар пользователя

Карма: 97
Рейтинг сообщений: 2058
Зарегистрирован: Чт дек 28, 2006 08:19:56
Сообщений: 18030
Откуда: Новочеркасск
Рейтинг сообщения: 1
Медали: 2
Получил миской по аватаре (1) Мявтор 3-й степени (1)
А мне понравилось - свежо.
Но почему не в виде статьи?

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

Мой уютный бложик... заходите!


Вернуться наверх
 
PCBWay - всего $5 за 10 печатных плат, первый заказ для новых клиентов БЕСПЛАТЕН

Сборка печатных плат от $30 + БЕСПЛАТНАЯ доставка по всему миру + трафарет

Онлайн просмотровщик Gerber-файлов от PCBWay + Услуги 3D печати
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Чт янв 11, 2018 18:18:06 
Встал на лапы
Аватар пользователя

Карма: 11
Рейтинг сообщений: 68
Зарегистрирован: Чт окт 09, 2014 11:29:45
Сообщений: 98
Откуда: Рязань
Рейтинг сообщения: 0
при современных ценах на контроллеры с кучей пинов... менее 50 рублей/штука... делать отдельный блок на счётчиках... мне не кажется дешёвым... и тем более проще... ))
Каждый извращенец, извращается по своему. Кому-то может больше нравится паять чем программировать. :)))
А мне, как раз на 64 ногом контроллере, ног и не хватило. А 100 ногую, влом было разводить, Да и разогнать такую мегу до 30MHz не получится. А если Вы предлагаете отдельный контроллер клавиатуры на 128Меге сделать - слишком жирно. Я бы сделал на какой-нибудь 48й, была такая мысль. Но решил вот так извратиться. :solder:

Добавлено after 4 minutes 25 seconds:
Но почему не в виде статьи?
А я не нашел, как там новую статью запостить. Наверно этим админы занимаются.


Вернуться наверх
 
Организация питания на основе надежных литиевых аккумуляторов EVE и микросхем азиатского производства

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

Подробнее>>
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Пт янв 12, 2018 19:04:15 
Друг Кота

Карма: 38
Рейтинг сообщений: 618
Зарегистрирован: Пн апр 06, 2015 11:01:53
Сообщений: 3092
Откуда: москва, уфа
Рейтинг сообщения: 0
начинайте ругать!

ну так, стилистически: наборы возможных значений лучше пихать в enum, так оно более структурировано, нежели ворох define-ов (ну и константы традиционно БОЛЬШИМИ_БУКВАМИ):
Код:
typedef enum {
    ME_NONE = 0x0000,
    ME_BTN_LEFT = 0x0400,
    ME_BTN_RIGHT = 0x0200,
    ME_BTN_CHN = 0x0010,
    ME_BTN_ENC2 = 0x0080,
//  ....
    ME_PWR_OFF = 0x0800
} menu_event_t;

menu_event_t menu_event = ME_NONE;
...

Конечно, есть недостатки. Нельзя, например, одновременно, двумя руками крутить энкодеры, и нажимать одновременно несколько кнопок.

почему, кстати? я так понял из всего этого, что в конце концов мы получаем два байта, состояние бит в которых соответствует состояниям ног счетчиков. Если так, то чому бы не проверять состояние c масками событий
Код:
    if(menu_event & ME_BTN_LEFT){
        // FOOOO
    }
    if(menu_event & ME_BTN_RIGHT){
        // BAAAAR
    }


Вернуться наверх
 
Новый аккумулятор EVE серии PLM для GSM-трекеров, работающих в жёстких условиях (до -40°С)

Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре. Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.

Подробнее>>
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Сб янв 13, 2018 00:11:45 
Встал на лапы
Аватар пользователя

Карма: 11
Рейтинг сообщений: 68
Зарегистрирован: Чт окт 09, 2014 11:29:45
Сообщений: 98
Откуда: Рязань
Рейтинг сообщения: 0
ну так, стилистически: наборы возможных значений лучше пихать в enum, так оно более структурировано, нежели ворох define-ов (ну и константы традиционно БОЛЬШИМИ_БУКВАМИ):
Да, да. Все правильно. На Си не писал уже лет 25, и стилистически сваливаюсь в то, на чем пишу всю жизнь. А за напоминание спасибо, чтоб другим не повадно было. :)))

почему, кстати? я так понял из всего этого, что в конце концов мы получаем два байта, состояние бит в которых соответствует состояниям ног счетчиков. Если так, то чому бы не проверять состояние c масками событий
А здесь, не согласен. Было бы верно, если бы биты гарантированно заполнялись и маски не пересекались. А в этой схеме, при нажатии двух кнопок, в лучшем случае получим 0x0000 а в худшем неопределенность. А у энкодеров, в добавок и маски пересекаются b00000011 и b00000101 - тоже неопределенность. Поэтому тупо сравниваю с константами для максимизации ошибки.


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Сб янв 13, 2018 02:05:10 
Друг Кота

Карма: 20
Рейтинг сообщений: 228
Зарегистрирован: Пт сен 13, 2013 13:11:31
Сообщений: 6388
Рейтинг сообщения: 0
В наши дни решения на дискретной логике, они скорее для фана, чем для практического применения. Это, типа, как чуваки на HACKDAY.IO собрали RFID-считыватель на 74-ой серии.

А так, даже для хобби проще взять тот же народный STM32F030F4P6, который аппаратно умеет два энкодера с дебаунсом, имея цену при этом -- 25 руб за корпус. На него же все кнопки повесить, еще и ног останется. При этом вся вышеприведенная схема выродится в один-единственный TSSOP20, что даже резисторов не потребуется.

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


Вернуться наверх
 
Не в сети
 Заголовок сообщения: Re: Опрос клавиатуры и энкодеров по SPI
СообщениеДобавлено: Сб янв 13, 2018 05:22:56 
Друг Кота
Аватар пользователя

Карма: 123
Рейтинг сообщений: 7955
Зарегистрирован: Сб сен 13, 2014 16:27:32
Сообщений: 39199
Откуда: СпиртоГонск созвездия Омега
Рейтинг сообщения: 0
идея прриферии ка модуля лет 30ка юзается брендами ИЩЕ со времен видикоф потом двд потом dvb а вы открыли 19век....
есть куча чипов 1кристалок заране зашитых позволяюшая подключить индюк на шину SPI(I2C) и еще передать по ней нажатия кнопок и команды конфигурации девайсса(запаяными на плате перемычками) сть таки чипы в сериях Htxxxxx PTxxxx Mxxxx
коих ка грязи с разбора за рупь кучка

_________________
ZМудрость(Опыт и выдержка) приходит с годами.
Все Ваши беды и проблемы, от недостатка знаний.
Умный и у дурака научится, а дураку и ..
Алберт Ейнштейн не поможет и ВВП не спасет.и МЧС опаздает


Вернуться наверх
 
Показать сообщения за:  Сортировать по:  Вернуться наверх
Начать новую тему Ответить на тему  [ Сообщений: 8 ] 

Часовой пояс: UTC + 3 часа


Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 6


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Перейти:  


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
Русская поддержка phpBB
Extended by Karma MOD © 2007—2012 m157y
Extended by Topic Tags MOD © 2012 m157y