Я Вам потому и давал общее описание решения, чтобы понимание сути появилось. А Вы просто пытаетесь куски кода без малейших правок утащить, даже в документацию не заглянув.
Нескольно простых вопросов о программировании AVR на Си.
Зачем тупо копипастить куски кода? Зачем Вам мой делитель 64? Почему не 1024, лучше подходящий для задачи? Для шестичасовых интервалов - чем больше делитель, тем реже прерывания, тем лучше для Вас. Откройте даташит на МК, почитайте про таймера, про их регистры. Сделайте осознанный выбор таймера - возможно, 16-битный Timer1 с соответственно подобранным делителем будет дучшим выбором.
Я Вам потому и давал общее описание решения, чтобы понимание сути появилось. А Вы просто пытаетесь куски кода без малейших правок утащить, даже в документацию не заглянув.
Я Вам потому и давал общее описание решения, чтобы понимание сути появилось. А Вы просто пытаетесь куски кода без малейших правок утащить, даже в документацию не заглянув.
- Реклама
Для осознанного выбора хорошо иметь понимание. Присматриваюсь, отсюда и куски кода, как обозначение проблемы, а не ее решение. Направление Вы указали, спасибо, но перейти с основательно подзабытых пятнадцатилетней давности "плюсов" на си для мк за несколько часов мне не удалось. Засим, мечтаю о подарке в виде рабочего актуального кода
.
Варкалось. Хливкие шорьки
Пырялись по нове,
И хрюкотали зелюки,
Как мюмзики в мове.

Пырялись по нове,
И хрюкотали зелюки,
Как мюмзики в мове.
- Сообщения: 2
- Зарегистрирован: Сб апр 27, 2019 10:02:19
Подскажите в чём косяк может быть? Если кнопка (включаем к питанию) на PC0, то отлично всё выполняется, но если на PC1 и старше хрена лысого ..
Компилирую с помощью Atmel Studio 7, и визуализирую на Proteus 8.6/8.8 один фикус.
Код: Выделить всё
/*
* GccApplication1.c
* ATMEGA48
#define F_CPU 8000000UL // 8 MHz
#include <avr/io.h>
#include <util/delay.h>
int main(void)
{
DDRB = 0xFF; // все выводы порта B сконфигурировать как выходы
DDRD = 0xFF; // все выводы порта D сконфигурировать как выходы
DDRC = 0x00; // все выводы порта C сконфигурировать как входы
while (1)
if ((PINC &(1<<PC1))==1) // Если нажата кнопка
{
while((PINC &(1<<PC1))==1){} // Ждем отпускания кнопки
PORTB =0xFF; PORTD =0xFF; _delay_ms(500); // Включение группы портов B и D
}
}
Неверное условие. PINC & (1<<PC1) никогда не будет равно 1. Либо 0, либо 2Prod писал(а):if ((PINC &(1<<PC1))==1) // Если нажата кнопка
Проверяйте либо:
Код: Выделить всё
if ((PINC & (1<<PC1)) == (1<<PC1))Код: Выделить всё
if ((PINC & (1<<PC1))- Реклама
Без разницы
Prod, надежно тогда, когда знаешь как работает код.
Инженер R@D
Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
- Сообщения: 93
- Зарегистрирован: Пн окт 31, 2016 06:23:19
[uquote="Prod",url="/forum/viewtopic.php?p=3624013#p3624013"]WiseLord
Спасибо, а как надёжней исходя из опыта?[/uquote]
Никакой разницы в "надежности" тут нет.
Проверка
применяется для того, чтобы проверить, установлен ли в 1 хотя бы один бит, описываемый маской `MASK`.
Проверка
применяется для того, чтобы проверить, установлены ли биты, описываемый маской `MASK`, именно в значения, указанные в наборе `BITS`.
Понятно, что когда речь идет о проверке одного-единственного бита, разница между этими вариантами стирается полностью. Используйте тот, который вам больше нравится.
Спасибо, а как надёжней исходя из опыта?[/uquote]
Никакой разницы в "надежности" тут нет.
Проверка
Код: Выделить всё
if ((data & MASK) != 0)Проверка
Код: Выделить всё
if ((data & MASK) == BITS)Понятно, что когда речь идет о проверке одного-единственного бита, разница между этими вариантами стирается полностью. Используйте тот, который вам больше нравится.
Доброго времени суток
Делаю преобразователь из 12-битнго параллельного интерфейса в аналоговый на Atmega8. Вроде, просто: собираю со входов число, выдаю его как задание скважности ШИМ. Использую 10-битный FastPWM в Таймер/Счетчеке 1. ШИМ работает, задание отрабатывается, число со входов собирается. Только вот Протеус выдает что-то очень странное.
Тестовая схема:

Фьюзы:

Код:
Собственно, что не так. При задании разных бит на входы порта D проблем нет - ШИМ на выходе выдает сообразную входам скважность. То самое с двумя младшими битами порта B (PB5 и PB4). А вот на изменение старших битов PB6 и PB7, ШИМ реагирует только после того, как я меняю значение на любом другом входе. Т.е. меняю на PB6 0 на 1 - на выходе ничего не изменятся. Потом меняю на PD3 0 на 1 - и тут же ШИМ на выходе становится таким, как и должно быть при выставленных в 1 PB6 и PD3.
Отчего такая муть может происходить? Два часа ковырял сегодня и ничего, идеи кончились.
Делаю преобразователь из 12-битнго параллельного интерфейса в аналоговый на Atmega8. Вроде, просто: собираю со входов число, выдаю его как задание скважности ШИМ. Использую 10-битный FastPWM в Таймер/Счетчеке 1. ШИМ работает, задание отрабатывается, число со входов собирается. Только вот Протеус выдает что-то очень странное.
Тестовая схема:
Фьюзы:
Код:
Спойлер
Код: Выделить всё
/*
МК ATMEGA8
Частота МК = 8 МГц
Fuse High Byte
Fuse Low Byte
*/
//Подключенные файлы
#include <avr/io.h> // Стандартные функции ввода/вывода для МК
#include <avr/interrupt.h> // Стандартные функции работы с прерываниями
//Подстановки
//Прототипы функций
//Процедура инициализации входов/выходов
void init_IO(void);
//Процедура инициализации ТС1
void init_TC1(void);
//Глобальные переменные
/********** Главная фунция **********/
void main(void)
{
//Инициализация входов/выходов
init_IO();
//Процедура инициализации ТС1
init_TC1();
//Инициализация локальных переменных
//Значение числа с 12-битного входа
unsigned int data_input = 0;
//Значения с соотв. входов
unsigned char data_in_D, data_in_B = 0;
//Разрешение прерываний
sei();
//Рабочий цикл
while(1)
{
//Читаем значения со входов
//Порт D без изменений
data_in_D = PIND;
//Порт B с очисткой неиспользуемых младших битов
data_in_B = PINB & ((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Преобразуем их в 10-битное число
data_input = ((data_in_B << 4) | data_in_D) >> 2;
//Выдаем заданную скважность ШИМ
OCR1A = data_input;
};
}
/********** Функции **********/
//Процедура инициализации входов/выходов
void init_IO(void)
{
//12-битный вход, собранный из входов портов D и B
//Младшие биты входа 7-0 (порт D)
DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0));
//Старшие биты входа 11-8 (порт B)
DDRB &= ~((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Подтягиваем входы к единице
PORTD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0);
PORTB |= (1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4);
//Тестовый выход
DDRB |= (1 << PB0);
}
//Процедура инициализации ТС1 (формирование выходного аналогового напряжения)
void init_TC1(void)
{
//Вывод PB1 (OC1A) - выход ШИМ
DDRB |= (1 << PB1);
//Сброс в ноль выхода OC1A при совпадении, режим 10-bit FastPWM
TCCR1A |= (1 << COM1A1) | (1 << WGM11) | (1 << WGM10);
//Предделитель 1
TCCR1B |= (1 << WGM12) | (1 << CS10);
//Начальное значение скважности ШИМ
OCR1A = 0;
//Вкл. прерывания по переполнению ТС
TIMSK |= (1 << TOIE1);
}
/********** Векторы прерываний **********/
//Вектор прерывания по переполнению ТС1
ISR(TIMER1_OVF_vect)
{
PORTB ^= (1 << PB0);
}Отчего такая муть может происходить? Два часа ковырял сегодня и ничего, идеи кончились.
We do what we must because we can (c) GLaDOS
Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.
И, кстати, не пробовали работать отдельно с OCR1AH и OCR1AL. AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает... В даташите на ATmega8A (17.3 - Accessing 16-bit Registers) про это немножко написано.
И, кстати, не пробовали работать отдельно с OCR1AH и OCR1AL. AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает... В даташите на ATmega8A (17.3 - Accessing 16-bit Registers) про это немножко написано.
а зачем 12 бит, если они потом обрезаются до 10 бит?
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Варнинги компилятор выдает?baron_P писал(а):Два часа ковырял сегодня и ничего, идеи кончились.
Вот это
Код: Выделить всё
//Преобразуем их в 10-битное число
data_input = ((data_in_B << 4) | data_in_D) >> 2Код: Выделить всё
data_input = (uint16_t) какая-то тестовая константа;Кстати в протеусе можно и точки останова использовать и смотреть значения переменных и по шагам шагать.
Естественно следует избегать неявного преобразования типов.
А протеус исполняет то, что скомпиллено.WiseLord писал(а):AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает...
Потому что исходный сигнал 12 бит, но точность, пока, особо не нужна - может сделаю и 12-бит ШИМ потом.Starichok51 писал(а):а зачем 12 бит, если они потом обрезаются до 10 бит?
[uquote="WiseLord",url="/forum/viewtopic.php?p=3645324#p3645324"]Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.
И, кстати, не пробовали работать отдельно с OCR1AH и OCR1AL. AVR-libc, конечно, позволяет сразу с 16-битным работать, но кто знает, как это протеус воспринимает... В даташите на ATmega8A (17.3 - Accessing 16-bit Registers) про это немножко написано.[/uquote]
Компилятор молчит. Проблема в том, что, если вместо PINB подставить константу, то все выводится как должно. Более того, и с PINB выводится в ШИМ как должно, но происходит это только если изменить какой-либо еще из битов, кроме PB6 и PB7. Т.е. считается то все правильно, а вот с обновлением значений этой пары битов какая-то странь. Завтра посмотрю по шагам.Z_h_e писал(а):Варнинги компилятор выдает?
Вот этоЗаменить например на этоКод: Выделить всё
//Преобразуем их в 10-битное число data_input = ((data_in_B << 4) | data_in_D) >> 2Поглядеть на реакцию системы. Сделать какие-то выводы.Код: Выделить всё
data_input = (uint16_t) какая-то тестовая константа;
Кстати в протеусе можно и точки останова использовать и смотреть значения переменных и по шагам шагать.
Естественно следует избегать неявного преобразования типов. А протеус исполняет то, что скомпиллено.
We do what we must because we can (c) GLaDOS
Я бы всё-таки попробовал, спокойствия ради:
Но всё-таки, скорее всего, очередной глюк Proteus.
Код: Выделить всё
OCR1AH = (data_input >> 8);
OCR1AL = (data_input & 0xFF);[uquote="WiseLord",url="/forum/viewtopic.php?p=3645413#p3645413"]Я бы всё-таки попробовал, спокойствия ради:
Но всё-таки, скорее всего, очередной глюк Proteus.[/uquote]
Ничего не изменилось. Взял процедуру из датащита, но она тоже не помогла:
Как посмотреть в Протеусе состояние переменных не разобрался сходу, но поигрался с константами и выяснил, что входы портов не причем. Описанный глюк проявляется при трех значениях в OCR1A:
0b0100000000 - 256
0b1000000000 - 512
0b1100000000 - 768
Но любые другие значения - без проблем ( 255 и 257, 511 и 513, 767 и 769). Не понимаю, как состояние младших битов может влиять на старшие.
UPD: Спаял на макетке - все работает без проблем. Видимо, это и впрямь баг Протеуса.
Код: Выделить всё
OCR1AH = (data_input >> 8);
OCR1AL = (data_input & 0xFF);Ничего не изменилось. Взял процедуру из датащита, но она тоже не помогла:
Код: Выделить всё
void TIM16_WriteOCRA1(unsigned int i)
{
unsigned char sreg;
/* Save Global Interrupt Flag*/
sreg = SREG;
/* Disable interrupts*/
cli();
/* Set OCR1A to i */
OCR1A= i;
/* Restore Global Interrupt Flag*/
SREG = sreg;
}0b0100000000 - 256
0b1000000000 - 512
0b1100000000 - 768
Но любые другие значения - без проблем ( 255 и 257, 511 и 513, 767 и 769). Не понимаю, как состояние младших битов может влиять на старшие.
UPD: Спаял на макетке - все работает без проблем. Видимо, это и впрямь баг Протеуса.
We do what we must because we can (c) GLaDOS
- Сообщения: 93
- Зарегистрирован: Пн окт 31, 2016 06:23:19
[uquote="WiseLord",url="/forum/viewtopic.php?p=3645324#p3645324"]Немножко смущает (data_in_B << 4). Хотя.. обнуляться не должно, ведь 4 - вроде как 16-битная переменная, так что и data_in_B должно привестись к 16бит перед сдвигом.[/uquote]
Нет, это так не работает. В операторах побитового сдвига тип второго операнда никак не влияет на преобразования первого операнда. Так что не имеет никакого значения, какой тип имеет `4`.
А вот обыкновенные integral promotions тут никто не отменял, то есть `data_in_B` будет неявно преобразовано к типу `int` само по себе.
Нет, это так не работает. В операторах побитового сдвига тип второго операнда никак не влияет на преобразования первого операнда. Так что не имеет никакого значения, какой тип имеет `4`.
А вот обыкновенные integral promotions тут никто не отменял, то есть `data_in_B` будет неявно преобразовано к типу `int` само по себе.
Нужно подгружать исполняемый код не hex, а elf. Тогда можно будет видеть переменные в отладке на паузе. И еще желательно кинуть исходники в папку с проектом в протеусе, тогда можно и по шагам шагать будет. Я правда очень давно в протеусе симуляцию не делал, может чего не точно сказал. А так да, модели МК для протеуса не без глюков, что-то там в режиме СТС было не так, например.baron_P писал(а):Как посмотреть в Протеусе состояние переменных не разобрался сходу,
[uquote="Z_h_e",url="/forum/viewtopic.php?p=3645877#p3645877"]Нужно подгружать исполняемый код не hex, а elf. Тогда можно будет видеть переменные в отладке на паузе. И еще желательно кинуть исходники в папку с проектом в протеусе, тогда можно и по шагам шагать будет. Я правда очень давно в протеусе симуляцию не делал, может чего не точно сказал. А так да, модели МК для протеуса не без глюков, что-то там в режиме СТС было не так, например.[/uquote]
Спасибо, попробую при случае, пока же необходимость отпала) Вот, что получилось в итоге (входы типа "открытый коллектор", 12-битный ШИМ на выходе):
Спасибо, попробую при случае, пока же необходимость отпала) Вот, что получилось в итоге (входы типа "открытый коллектор", 12-битный ШИМ на выходе):
Спойлер
Код: Выделить всё
/*
МК ATMEGA8
Частота МК = 8 МГц
Fuse High Byte
BODLEVEL = 0 - нижний порог напряжения питания 4 В
BODEN = 0 - слежение за напряжением питания вкл.
SUT1 = 0 \ время включения 6СК + 64 ms
SUT0 = 0 /
CKSEL3 = 0 \
CKSEL2 = 1 - встроенный источник тактовых импульсов
CKSEL1 = 0 - с частотой 8 МГц
CKSEL0 = 0 /
Fuse Low Byte
RSTDISBL = 1 - переназначение вывода ~RESET выкл.
WDTON = 1 - постоянная работа сторожевого таймера выкл.
SPIEN = 0 - внутрисхемное программирование вкл.
CKOPT = 1 - опции работы с внешним кварцевым резонатором выкл.
EESAVE = 1 - защита EEPROM от стирания выкл.
BOOTSZ1 = 0 \ размер загрузочного сектора
BOOTSZ0 = 0 / 1024 слова, начальный адрес $0C00
BOOTRST = 1 - перенос стартового сектора в область загрузчика выкл.
*/
//Подключенные файлы
#include <avr/io.h> // Стандартные функции ввода/вывода для МК
#include <avr/interrupt.h> // Стандартные функции работы с прерываниями
#include <avr/wdt.h> // Стандартные функции работы cо сторожевым таймером
//Подстановки
#define PWM_MAX 4095 // Максимальное значение скважности для 12-битиного ШИМ (PWM_MAX = 2^12 - 1)
#define N_MAX 3 // Максимальное количесво значений для получения средне арифметичесого (2^N_MAX = 8)
#define U_INT_MAX 65535 // Максимальное значение типа unsigned int
//Прототипы функций
//Процедура инициализации входов/выходов
void init_IO(void);
//Процедура инициализации ТС1
void init_TC1(void);
//Процедура записи в 16-битный регистр OCR1A
void WriteToOCRA1(unsigned int);
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int, unsigned int);
/********** Главная фунция **********/
void main(void)
{
//Инициализация входов/выходов
init_IO();
//Инициализация ТС1
init_TC1();
//Инициализация локальных переменных
//Значения с входов соотв. портов
unsigned char data_in_D = 0, data_in_B = 0;
//Значение числа с 12-битного входа
unsigned int data_input = 0;
//Вычисленное средне арифметическое из N_MAX полученных значений
unsigned int data_reference = 0;
//Разрешение прерываний
sei();
//Включение сторожевого таймера с периодом 1с
wdt_enable(WDTO_1S);
//Рабочий цикл
while(1)
{
//Чтение значений со входов
//Порт D без изменений
data_in_D = PIND;
//Порт B с очисткой неиспользуемых младших битов
data_in_B = PINB & ((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Преобразование полученных значений в 12-битное число (0b00 4бита PortB 8 битов PortD)
data_input = (data_in_B << 4) | data_in_D;
//Получение среднего арифметического из N_MAX значений
data_reference = data_conversation(data_input, data_reference);
//Выдача заданния скважности ШИМ в регистр сравнения
WriteToOCRA1(data_reference);
//Сброс сторожевого таймера
wdt_reset();
};
}
/********** Функции **********/
//Процедура инициализации входов/выходов
void init_IO(void)
{
//12-битный вход, собранный из входов портов D и B
//Младшие биты входа 7-0 (порт D)
DDRD &= ~((1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0));
//Старшие биты входа 11-8 (порт B)
DDRB &= ~((1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4));
//Подтяжка входов к единице
PORTD |= (1 << PD7) | (1 << PD6) | (1 << PD5) | (1 << PD4) | (1 << PD3) | (1 << PD2) | (1 << PD1) | (1 << PD0);
PORTB |= (1 << PB7) | (1 << PB6) | (1 << PB5) | (1 << PB4);
//Неиспользуемые выводы назначаются входами
DDRB &= ~((1 << PB3) | (1 << PB2) | (1 << PB0));
DDRC &= ~((1 << PC5) | (1 << PC4) | (1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0));
//Подтяжка неиспользуемых входов к единице
PORTB |= (1 << PB3) | (1 << PB2) | (1 << PB0);
PORTC |= (1 << PC5) | (1 << PC4) | (1 << PC3) | (1 << PC2) | (1 << PC1) | (1 << PC0);
}
//Процедура инициализации ТС1 (формирование выходного аналогового напряжения)
void init_TC1(void)
{
//Вывод PB1 (OC1A) - выход ШИМ
DDRB |= (1 << PB1);
//Сброс в ноль выхода OC1A при совпадении, режим FastPWM (ICR1)
TCCR1A |= (1 << COM1A1) | (1 << WGM11);
//Предделитель 1
TCCR1B |= (1 << WGM13) | (1 << WGM12) | (1 << CS10);
//Маскимальное значение скважности
ICR1 = PWM_MAX;
//Начальное (нулевое) значение скважности ШИМ
WriteToOCRA1(0);
}
//Процедура атомарной записи в 16-битный регистр OCR1A (пример из датащита на Atmega8)
void WriteToOCRA1(unsigned int i)
{
unsigned char sreg;
/* Save Global Interrupt Flag*/
sreg = SREG;
/* Disable interrupts*/
cli();
/* Set OCR1A to i */
OCR1A= i;
/* Restore Global Interrupt Flag*/
SREG = sreg;
}
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int data_in, unsigned int data_ref)
{
//Суммарное значение N_MAX полученных
static unsigned int data_sum;
//Счетчик количества просуммированых значений
static unsigned char data_n;
//Если количество просуммированных значений меньше заданного
if (data_n < (1 << N_MAX))
{
//Добавляем полученное значение к сумме предыдущих (предельное значени data_in = PWM_MAX*2^N_MAX = 32670)
data_sum += data_in;
//Инкрементируем счетчик полученных значений
data_n++;
}
else
{
//Иначе, обновляем предыдущее средне-арифметическое значение
data_ref = (data_sum >> N_MAX);
//Обнуляем сумму полученных значений
data_sum = 0;
//Обнуляем счетчик полученных значений
data_n = 0;
}
//Возвращаем среднее значение
return data_ref;
}We do what we must because we can (c) GLaDOS
- Сообщения: 93
- Зарегистрирован: Пн окт 31, 2016 06:23:19
[uquote="baron_P",url="/forum/viewtopic.php?p=3646369#p3646369"][/uquote]
Во-первых, среднее у вас вычисляется не из `N_MAX` значений, как написано в комментарии, а из `2^N_MAX` значений.
Во-вторых, вышеприведенная функция возвращает новое среднее только на каждой `2^N_MAX` итерации. На остальных итерациях возвращается последнее вычисленное среднее. Это так и задумано?
Код: Выделить всё
//Функция получения среднего значения из N_MAX полученных
unsigned int data_conversation(unsigned int data_in, unsigned int data_ref)Во-первых, среднее у вас вычисляется не из `N_MAX` значений, как написано в комментарии, а из `2^N_MAX` значений.
Во-вторых, вышеприведенная функция возвращает новое среднее только на каждой `2^N_MAX` итерации. На остальных итерациях возвращается последнее вычисленное среднее. Это так и задумано?
Последний раз редактировалось KorbenDallas Чт июн 06, 2019 20:02:46, всего редактировалось 1 раз.
атомарные операции в AVR-GCC делаются не такbaron_P писал(а)://Процедура атомарной записи в 16-битный регистр OCR1A (пример из датащита на Atmega8)
Код: Выделить всё
#include <util/atomic.h> /* вот в этом заголовке все нужное */
void WriteToOCRA1(unsigned int i){
ATOMIC_BLOCK(ATOMIC_RESTORSETATE){
OCR1A= i;
}
}если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!
при взгляде на многих сверху ничего не меняется...
Мой уютный бложик... заходите!



