Програмирование pic на СИ.

Поклонники продукции Microchip Technology Inc тусуются тут.
Ответить
Друг Кота
Аватара пользователя
Сообщения: 25862
Зарегистрирован: Чт янв 10, 2008 22:01:02
Откуда: Московская область, Фрязино

Сообщение КРАМ »

[uquote="lin17",url="/forum/viewtopic.php?p=3867637#p3867637"]ARV - я думаю...[/uquote]
NStorm писал(а):...обычно мало пригодные для правки человеком. С тем же успехом можно пытаться.....
Даладна!!! :))) :))) :)))
Коллеги, это только мне кажется, что ARV ерничает? Вообще то это был сарказм. И тут можно только поржать, но никак не обсуждать на полном серьезе перспективы.
Реклама
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

КРАМ, а не ARV отвечал, там явный сарказм был. А lin17, который про транслятор из "делфи" писал.
Реклама
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

А как в MPLAB X посмотреть дизасм? Компилятор XC8. Версии MPLAB X и компилятора последние.
Вот только с Ассемблера перешел на СИ, написал работающую программу. Хотелось бы узнать мнение гуру о коде программы. Пойдет такое написание на СИ? Или что-то можно по другому скомпоновать в программе?
Спойлер

Код: Выделить всё

while (1)
{
PWM1CON = 0b00000000;           // отключаем ШИМ1
FVRCON = 0b00000000;            // отключаем ИОН, отключаем температурный индикатор
ADCON = 0b00000000;             // отключаем модуль АЦП
PORTA = 0;                      // все выходы порта переводим в низкое состояние
__delay_ms(500);
IOCIE = 1;                      // разрешаем прерывание по изменению уровня PORTA
IOCAF = 0;                      // сбрасываем флаг IOCAF (свидетельствующий о прерывании RA3)
SWDTEN = 0;                     // отключаем сторожевой таймер на период сна
SLEEP();                        // здоровый крепкий сон до пробуждения нажатием кнопки)
IOCIE = 0;                      // запрет прерывания по изменению уровня PORTA
SWDTEN = 1;                     // включаем сторожевой таймер 
    
     CLRWDT();
     LED = 1;                   // включаем питание
     PWM1CON = 0b11000000;      // вкл ШИМ для двухтонального сигнала включения
     TMR2 = 0;                  // сброс TMR2 для исключения пауз между тонами
     PR2 = 10;                  // первый тон сигнала включения ~ 700 Гц
     PWM1DCH = 5;               // -//-
     __delay_ms(111);
     TMR2 = 0;                  // сброс TMR2 для исключения пауз между тонами
     PR2 = 6;                   // второй тон сигнала включения ~ 1,1 кГц
     PWM1DCH = 3;               // -//-
     __delay_ms(111);
     PWM1CON = 0b01000000;      // выкл ШИМ
     FVRCON = 0b10000001;       // включаем ИОН 1,024V, отключаем температурный индикатор
     ADCON = 0b00011101;        // включаем модуль АЦП, FVR вход (фиксированное опорное напряжение), FOSC/2
 
       GO:
       for(n=0; n<=15000; n++)  // время работы основного цикла программы, где 1000n соответствует ~ 1 минуте работы (максимальный n=65535, ТЕ 65мин)
       {
       if (!TK1)                // если перемычка на 0, то бесконечный режим работы (без таймера))
         {
         n=0;
         }
       CLRWDT();
       __delay_ms(52);
       if (!TK)                 // если кнопка нажата - подаем короткий звуковой сигнал
          {
          PWM1EN = 1;           // вкл ШИМ звукового сигнала нажатия кнопки
          __delay_ms(55);
          PWM1EN = 0; 
          __delay_ms(999);  
          if (!TK)              // если кнопка нажата более 1 сек - отключаем питание
            {
            goto Power_OFF;
            }
            else                // если кнопка нажата менее 1 сек - обнуляем таймер ожидания
            {
            goto GO;
            }       
          }
       
       GO_nDONE = 1;            //начать преобразование АЦП
       while(GO_nDONE);         //ожидание окончания преобразования АЦП
       if(ADRES > 86)           //если U ниже 3.0В выключаем питание
                {
                goto Power_OFF;
                }
                else
                  {
                  if(ADRES > 80) //если U ниже 3.3В включим двойной предупредительный звуковой сигнал о низком заряде АКБ, который повторяется каждые 15с
                    {
                      if (i<2)
                      {
                      PWM1EN = 1;       // вкл ШИМ для предупредительного звукового сигнала
                       __delay_ms(55);
                      PWM1EN = 0;       // выкл ШИМ
                       __delay_ms(33);
                      }
                      i++;
                    }
                  }
       if(n==14000)                     // вкл предупредительный 4-хкратный звуковой сигнал, предупреждающий о отключении питания, где 1000n соответствует ~ 1 минуте работы (максимальный n=65535, ТЕ 65мин)
          {
           for(m=0; m<=3; m++) 
             {
             PWM1EN = 1;                // вкл ШИМ для предупредительного звукового сигнала
             __delay_ms(155);
             PWM1EN = 0;                // выкл ШИМ
             __delay_ms(77);   
             }
          }
        }
 
     Power_OFF:
     PWM1EN = 1;                // вкл ШИМ для двухтонального сигнала выключения
     TMR2 = 0;                  // сброс TMR2 для исключения пауз между тонами
     PR2 = 6;                   // первый тон ~ 1,1 кГц
     PWM1DCH = 3;               // -//-
     __delay_ms(111);
     TMR2 = 0;                  // сброс TMR2 для исключения пауз между тонами
     PR2 = 10;                  // второй тон ~ 700 Гц
     PWM1DCH = 5;               // -//-
     __delay_ms(111);
     PWM1EN = 0;                // выкл ШИМ
     LED = 0;                   // отключаем питание
}
     return;

}
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

[uquote="4uvak",url="/forum/viewtopic.php?p=3909568#p3909568"]А как в MPLAB X посмотреть дизасм? Компилятор XC8. Версии MPLAB X и компилятора последние.[/uquote]
Включить отладку (можно через симулятор), потом Window->Debugging->Disassembly

Добавлено after 25 minutes 22 seconds:
4uvak, напишите модель МК для кода, так проверить удобнее будет собрав исходник.

Добавлено after 2 minutes 56 seconds:
Вообще конечно видно, что переход был с асма ) Стилистика осталась в асм стиле. Вместо меток лучше в виде функций оформить. Да и вообще в Си метки почти никогда не нужны.

Добавлено after 23 minutes 7 seconds:
Чисто в плане избавления от ненужных меток:
Спойлер

Код: Выделить всё

void init() {
    PWM1CON = 0b00000000; // отключаем ШИМ1
    FVRCON = 0b00000000; // отключаем ИОН, отключаем температурный индикатор
    ADCON = 0b00000000; // отключаем модуль АЦП
    PORTA = 0; // все выходы порта переводим в низкое состояние
    __delay_ms(500);
    IOCIE = 1; // разрешаем прерывание по изменению уровня PORTA
    IOCAF = 0; // сбрасываем флаг IOCAF (свидетельствующий о прерывании RA3)
    SWDTEN = 0; // отключаем сторожевой таймер на период сна
    SLEEP(); // здоровый крепкий сон до пробуждения нажатием кнопки)
    IOCIE = 0; // запрет прерывания по изменению уровня PORTA
    SWDTEN = 1; // включаем сторожевой таймер
}

void power_on() {
    LED = 1; // включаем питание
    PWM1CON = 0b11000000; // вкл ШИМ для двухтонального сигнала включения
    TMR2 = 0; // сброс TMR2 для исключения пауз между тонами
    PR2 = 10; // первый тон сигнала включения ~ 700 Гц
    PWM1DCH = 5; // -//-
    __delay_ms(111);
    TMR2 = 0; // сброс TMR2 для исключения пауз между тонами
    PR2 = 6; // второй тон сигнала включения ~ 1,1 кГц
    PWM1DCH = 3; // -//-
    __delay_ms(111);
    PWM1CON = 0b01000000; // выкл ШИМ
    FVRCON = 0b10000001; // включаем ИОН 1,024V, отключаем температурный индикатор
    ADCON = 0b00011101; // включаем модуль АЦП, FVR вход (фиксированное опорное напряжение), FOSC/2

}

void power_off() {
    PWM1EN = 1; // вкл ШИМ для двухтонального сигнала выключения
    TMR2 = 0; // сброс TMR2 для исключения пауз между тонами
    PR2 = 6; // первый тон ~ 1,1 кГц
    PWM1DCH = 3; // -//-
    __delay_ms(111);
    TMR2 = 0; // сброс TMR2 для исключения пауз между тонами
    PR2 = 10; // второй тон ~ 700 Гц
    PWM1DCH = 5; // -//-
    __delay_ms(111);
    PWM1EN = 0; // выкл ШИМ
    LED = 0; // отключаем питание
}

void main(void) {
    init();
    power_on();
    CLRWDT();
    while (1) {
        for (n = 0; n <= 15000; n++) // время работы основного цикла программы, где 1000n соответствует ~ 1 минуте работы (максимальный n=65535, ТЕ 65мин)
        {
            if (!TK1) // если перемычка на 0, то бесконечный режим работы (без таймера))
            {
                n = 0;
            }
            CLRWDT();
            __delay_ms(52);
            if (!TK) // если кнопка нажата - подаем короткий звуковой сигнал
            {
                PWM1EN = 1; // вкл ШИМ звукового сигнала нажатия кнопки
                __delay_ms(55);
                PWM1EN = 0;
                __delay_ms(999);
                if (!TK) // если кнопка нажата более 1 сек - отключаем питание
                {
                    power_off();
                    return;
                } else // если кнопка нажата менее 1 сек - обнуляем таймер ожидания
                {
                    break;
                }
            }

            GO_nDONE = 1; //начать преобразование АЦП
            while (GO_nDONE); //ожидание окончания преобразования АЦП
            if (ADRES > 86) //если U ниже 3.0В выключаем питание
            {
                power_off();
                return;
            } else {
                if (ADRES > 80) //если U ниже 3.3В включим двойной предупредительный звуковой сигнал о низком заряде АКБ, который повторяется каждые 15с
                {
                    if (i < 2) {
                        PWM1EN = 1; // вкл ШИМ для предупредительного звукового сигнала
                        __delay_ms(55);
                        PWM1EN = 0; // выкл ШИМ
                        __delay_ms(33);
                    }
                    i++;
                }
            }
            if (n == 14000) // вкл предупредительный 4-хкратный звуковой сигнал, предупреждающий о отключении питания, где 1000n соответствует ~ 1 минуте работы (максимальный n=65535, ТЕ 65мин)
            {
                for (m = 0; m <= 3; m++) {
                    PWM1EN = 1; // вкл ШИМ для предупредительного звукового сигнала
                    __delay_ms(155);
                    PWM1EN = 0; // выкл ШИМ
                    __delay_ms(77);
                }
            }
        }
        return;
    }
}
Что еще я бы улучшил, но уже безотносительно перехода с асма:
1. Мне не нравится сброс n каждый раз в случае перемычки. Обычно перемычку на работающем ус-ве не меняют. Поэтому обычно значение перемычки считается только один раз. А дальше я бы просто сделал бы бесконечный цикл, где n приращивалось бы или нет.
2. Нажатие кнопки проверяется разово. При этом ожидание и замеры АЦП останавливаются. Дребезгом или дрочением кнопки можно тормознуть выполнение другого участка кода.
3. Вместо перехода на конец программы, где просто будет бесконечный цикл ничего не делания крутится, я бы в конце power_off() в спящий режим бы уходил бы постоянный.
Последний раз редактировалось NStorm Вт окт 20, 2020 13:47:05, всего редактировалось 1 раз.
Реклама
Эиком - электронные компоненты и радиодетали
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

NStorm, спасибо. Дизасму включил. Камень pic10f322, это я реализовал контроллер управления питанием от Li-ion, который включает/выключает нагрузку от нажатия кнопки на 15 минут (с автоотключением), или без автоотключения в зависимости от положения перемычки. Так же идет предупреждение звуковым сигналом, если напряжение на АКБ меньше 3,2В, или отключение при напряжении на АКБ менее 3В.
По поводу:
1. Так как это контроллер управления питанием, то он всегда подключен к банке АКБ, поэтому единоразовая проверка(во время инициализации регистров) не подходит поэтому проверка в коде программы, и режимы работы легко меняются в активном состоянии. Хотя раньше я делал в ассемблере, как Вы советуете.
2. А как кнопка может тормознуть программу? Опрос кнопки идет на определенном участке программы, а прерывания отключены в основном цикле программы, и вкл только на период сна
3. Так с конца программы после звукового сигнала отключения итак идет переход на начало (после Power_Off) на while (1) и контроллер уходит в спячку.

Насчет безGOTOвых переходов еще потренируюсь писать программы, чтобы обойтись без них.
Реклама
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

2. Ну вас в коде одна проверка: "if (!TK) // если кнопка нажата - подаем короткий звуковой сигнал" внутри сразу уже в любом случае происходит сигнал, после которого ожидание 999мс. Вот тут и тормозится.
3. return в c - выход из функции. Из main() значит завершение программы. Не уверен, как код генерит xc8, но в avr-gcc насколько я помню в этом случае проиходит переход ко встроенной функции exit(), где просто крутится бесконечный цикл. Надо будет глянуть что xc8 выдает при return'e из main().
Реклама
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

3. Глянул в дизасм, там нормальный переход GOTO 0x12B на начало программы, после чего сон)

Код: Выделить всё

143:                PWM1EN = 0;                // выкл ШИМ
01F6  1396     BCF PWM1CON, 0x7
144:                LED = 0;                   // отключаем питание
01F7  1105     BCF PORTA, 0x2
01F8  292B     GOTO 0x12B
145:           }
146:                return;
147:           
148:           }
2. Точно, 2 проверки состояния кнопки. Короткое нажатие просто сбрасывает таймер ожидания и подает звуковой сигнал (чтобы устройство не отключилось). Длинное нажатие более 1 сек подает сначала короткий звуковой сигнал, а потом двухтональный сигнал отключения и спячка
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

Глянул выхлоп xc8 - понятно, он инициализацию пихает в конец, а return из main() реально возвращается в конец инициализации этой, на последней адрес во флэш памяти. Тем самым прокручивая PC обратно до 0. Но как-то это неправильно с точки зрения Си. Это не на while(1) прыжок обратно, а всей программы сначала. Все переменные будут переинициализированы.
Кстати полный дизасм еще генерится в каталоге с проектом в подкаталоге dist/default/TARGET/PROJECT.lst
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

Да нет, именно на while(1) прыгает.

Код: Выделить всё

47:            while (1)
48:            {
49:            PWM1CON = 0b00000000;           // отключаем ШИМ1
012B  0196     CLRF PWM1CON
50:            FVRCON = 0b00000000;            // отключаем ИОН, отключаем температурный индикатор
012C  019D     CLRF FVRCON
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

3. В общем всё-равно на МК в общем принципе для портабельности лучше избегать return из main().
2. Ну если вам тут не важно, что нажатие кнопки даже короткое по сути откладывает измерение АЦП на секунду, то можно и так конечно оставить.
Да нет, именно на while(1) прыгает.
Это видимо оптимизатор постарался. Вообще в целом return из main() в embedded зависит от компилятора.
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

Вообщем СИ и МПЛАБ Х вещь, раньше сидел на Асме, и не знаю сколько бы у меня ушло времени на написание аналогичной программы, где куча программных таймеров связанных между собой. А вышеуказанную программу написал менее чем за день с учетом того, что раньше не имел опыта написания программ на СИ (мигание светодиода не в счет).
Еще конфигурируемые логические ячейки довольно забавная вещь. https://habr.com/ru/post/372509/ Я проверил - работает, но правда ног у 10f322 очень мало. Закажу МК, где ног побольше для экспериментов. Можно будет например аппаратные подавители дребезга контактов кнопки/энкодера делать...
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

4uvak, всё-таки это не программные таймеры, а просто задержки. Но в остальном вы правы, на Си куда проще многие штуки делаются. Но надо учиться абстрагироваться от асма, тут так в лоб не надо. Я даже не о сложных абстракциях, которые можно делать в Си, а просто хотя бы к базовым вещами привакать - не надо "переходить", надо вызывать функции. А потом еще привыкаем к тому, что у функций бывают аргументы и возвращаемое значение.
Друг Кота
Аватара пользователя
Сообщения: 25862
Зарегистрирован: Чт янв 10, 2008 22:01:02
Откуда: Московская область, Фрязино

Сообщение КРАМ »

[uquote="NStorm",url="/forum/viewtopic.php?p=3909698#p3909698"]у функций бывают аргументы и возвращаемое значение.[/uquote]
Прямого отношения к Си это не имеет. На АСМе совершенно так же делаются вызовы, в подпрограмму передаются значения и подпрограмма возвращает значение.
Тут дело не в языке, а в мышлении. В способности генерировать абстракции вообще, а не потому, что так требует язык.
Кстати, код написанный на АСМе в стиле языка высокого уровня не намного более объёмен нежели в ЯВУ.
Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

[uquote="КРАМ",url="/forum/viewtopic.php?p=3909720#p3909720"]Прямого отношения к Си это не имеет. На АСМе совершенно так же делаются вызовы, в подпрограмму передаются значения и подпрограмма возвращает значение.
Тут дело не в языке, а в мышлении. В способности генерировать абстракции вообще, а не потому, что так требует язык.[/uquote]
Без сомнения. Но обычно на асме более прямолинейно всегда пишут. ЯВУ всё-таки более располагает к большему уровню абстракции

[uquote="КРАМ",url="/forum/viewtopic.php?p=3909720#p3909720"]Кстати, код написанный на АСМе в стиле языка высокого уровня не намного более объёмен нежели в ЯВУ.[/uquote]
Смотря какой уровень сложности программы и владения ЯВУ. Структуры в Си, например, очень сильно могу сократить объем и наглядность кода. Да и портабельность с ЯВУ проще - перейти с одной архитектуры на другую с ЯВУ попроще - не надо набор инструкций на низком уровне изучать и "привыкать" к ним. Но я не хочу разводить холивар на этом месте. Везде свои преимущества. Оптимизация под архитектуру на асме ес-но возможна такая, которой ни один оптимизатор ЯВУ не сможет.
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

Подскажите как начать цикл заново не используя команду перехода по меткам GOTO? Компилятор XC8
Друг Кота
Аватара пользователя
Сообщения: 25862
Зарегистрирован: Чт янв 10, 2008 22:01:02
Откуда: Московская область, Фрязино

Сообщение КРАМ »

[uquote="4uvak",url="/forum/viewtopic.php?p=3910220#p3910220"]Подскажите как[/uquote]
Вы лучше сформулируйте задачу.
Вместо goto нужно оформить цикл while с условием или без него - бесконечный цикл:

Код: Выделить всё

while(1) {

// тут содержимое цикла

}
Встал на лапы
Аватара пользователя
Сообщения: 99
Зарегистрирован: Чт авг 16, 2012 10:41:42

Сообщение 4uvak »

Это начало основного цикла while (1), по условиям ложного срабатывания (контроллер чувствителен к помехам по цепям питания) и он ложно может выйти из сна через прерывание. Поэтому я добавил строку с проверкой срабатывания кнопки после сна. Если кнопка не нажата нужно повторно отправиться спать. Я организовал команду перехода через goto qwe. Есть другой способ начать цикл заново?

Код: Выделить всё

while (1)
{
qwe:
PWM1CON = 0b00000000;           // отключаем ШИМ1
FVRCON = 0b00000000;            // отключаем ИОН, отключаем температурный индикатор
ADCON = 0b00000000;             // отключаем модуль АЦП
PORTA = 0;                      // все выходы порта переводим в низкое состояние
__delay_ms(500);
IOCIE = 1;                      // разрешаем прерывание по изменению уровня PORTA
IOCAF = 0;                      // сбрасываем флаг IOCAF (свидетельствующий о прерывании RA3)
SWDTEN = 0;                     // отключаем сторожевой таймер на период сна
SLEEP();                        // здоровый крепкий сон до пробуждения нажатием кнопки)
IOCIE = 0;                      // запрет прерывания по изменению уровня PORTA
SWDTEN = 1;                     // включаем сторожевой таймер 
__delay_ms(50);
if (TK)                         // если кнопка отжата - было ложное страбатывание прерывания (из-за помех по цепям питания), отправляемся повторно спать
{
goto qwe;
}
     CLRWDT();
     LED = 1;                   // включаем питание
Друг Кота
Аватара пользователя
Сообщения: 25862
Зарегистрирован: Чт янв 10, 2008 22:01:02
Откуда: Московская область, Фрязино

Сообщение КРАМ »

Тогда так:

Код: Выделить всё

while(1){
// тут какой то код перед началом ожидания кнопки
   while(условие не нажатой кнопки){
   // тут код пока кнопка не нажата
   }
// тут код после нажатой кнопки
}

Поставщик валерьянки для Кота
Сообщения: 1978
Зарегистрирован: Ср июл 17, 2013 13:55:57

Сообщение NStorm »

4uvak, я вам дал на прошлой странице пример с функциями. Просто вынесите выключение и сон в отдельную функцию и вызывайте её в данном случае. А после неё можно как у вас сделать return (уже из main()), чтобы "перезапуститься".

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

Вот простенький относительно пример: https://github.com/N-Storm/flashlight/b ... ashlight.c
Это я делал для фонарика прошивку на замен штатному китайскому МК на базе PIC10F200 - самый простой и примитивный МК пожалуй в мире из ныне выпускающихся. У него нет прерываний. Поэтому с некоторыми вещами пришлось "извращаться" и очень мало памяти, чтобы сделать красивее. Но оно работает. ) Вот выключение сделано в функции enter_sleep(), которая всё выключает и отправляет МК в сон, как вам нужно.

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

Но если хотите "простыней", то что мешает вместо if (TK) goto qwe; написать:

Код: Выделить всё

IOCIE = 0;                      // запрет прерывания по изменению уровня PORTA
SWDTEN = 1;                     // включаем сторожевой таймер
__delay_ms(50);
if (!TK)                         // если кнопка нажата
{
     CLRWDT();
     LED = 1;                   // включаем питание
...
}
// qwe:
...
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18787
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

оператор continue немедленно осуществляет переход к проверке условия продолжения цикла (внутри которого находится этот оператор) - это 100% то самое, что вам нужно для замены goto
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Ответить

Вернуться в «PIC»