реклама на сайте
подробности

 
 
> Прошивка STM32F7 через другой STM32F7 (UART), Прошивка ведомых ведущим.
AVStech
сообщение May 23 2018, 14:58
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 59
Регистрация: 19-01-17
Пользователь №: 95 076



Добрый день! Повторюсь в этой ветке, так как в основном работа с ARM

Возникла необходимость прошивки микроконтроллера STM32F7 другим микроконтроллером STM32F7 подключенный к нему через UART, а также прошивки Atmega328P подключенного по SPI. Необходимость возникла по причине того, что SoM на Allwinere страдает кучей недостатков в том числе отсутствием необходимого количества UART.
Постараюсь описать кратко идею:
Есть несколько микроконтроллеров STM32F7 общающихся между собой по UART и один Atmega328P подключенный к STMке по SPI, из них:

а) 1-й Микроконтроллер STM32F7: Концентратор на базе STM32F7 он собирает данные с разных микроконтроллеров по UART пакует их в пакет и отправляет по Wi-Fi пользователю через модуль ESP-01 или ESP-06 (UART+2xGPIO). Также данный контроллер имеет слот под microSD подключенный по SDIO и FRAM по SPI.

б) 2-е...5-е Микроконтроллеры STM32F7 (4 штуки): собирают различную цифровую и аналоговую информацию, обрабатывают ее и отправляют на концентратор (см. пункт а)), который в свою очередь ее упаковывает и отправляет в сеть.

в) Микроконтроллер Atmega328P: Следит за уровнем заряда батареи, управляет вкл и выкл систем питания, и в теории программирует по i2c две микросхемы BQ25892 (устанавливает настройки).

Как все это должно работать в теории:

1. Вставляем в microSD слот карту памяти с папкой, например, Firmware, и контроллер (концентратор stm32 из пункта а)) просматривает файлы и в случае, если находит в ней прошивку старше загруженной шьется сам и шьет все остальные контроллеры включая Atmega328P. Затем удаляет все исходники из папки Firmware. Все папка пуста.

2. Подключается по wifi к сети интернет, заходит по ссылке в сети интернет смотрит версии прошивки и если есть прошивки старше загруженной качает ее на microSD карту в папку Firmware, перезагружается, и затем как в варианте с microSD шьется сам и шьет все остальные контроллеры включая Atmega328P. Затем удаляет все исходники из папки Firmware. Все папка пуста.

Теперь вопросы - это возможно реализовать на практике?

И "ДА" - Естественно все микроконтроллеры имеют необходимый bootloader и все прошивки скомпилированы в бин или хекс.

У меня одна проблема - я не могу учится по учебникам, то ли мозг так заточен, то ли уситчивости не хватает. Я не нашел ни одного примера. Я могу взять чужой пример и переделать его. С нуля не получается, тем более что нужно быстро.
Поэтому спрашиваю это физически возможно?
Если да то прошу помощи - пример как это сделать, включая схему подключения между контроллерами. Ведь голый uart не пойдет нужно с концентратора еще и gpio подводить к ногам boot0 и boot1 и RST ведомых микроконтроллеров (для avr также)

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

Спасибо.

Сообщение отредактировал AVStech - May 23 2018, 17:06
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
x893
сообщение May 24 2018, 08:18
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 333
Регистрация: 27-10-08
Из: Планета Земля
Пользователь №: 41 226



Можно и без ресета, но если ваша расчудесная программа (или загрузчик) завинет - будете бежать питание передергивать ?
Про WDG можно не писать.
Естественно в загрузчик можно и через команду попасть.
Go to the top of the page
 
+Quote Post
AVStech
сообщение May 24 2018, 09:39
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 59
Регистрация: 19-01-17
Пользователь №: 95 076



Цитата(x893 @ May 24 2018, 11:18) *
Можно и без ресета, но если ваша расчудесная программа (или загрузчик) завинет - будете бежать питание передергивать ?
Про WDG можно не писать.
Естественно в загрузчик можно и через команду попасть.


Да однозначно RST подтяну к ведущему. IWDG также присутствует в некоторых модулях.
Резюмируя, я так понимаю, что любые МК, например DSPIC33 в качестве субведомых (управляется ведомым) также можно шить по UART ведущим? Просто каскадный bootloadr?

Я нашел кучу примеров Bootloader'ов для прошивки по вайфай, USB и SD и т.п. Но не нашел ни одного примера кода как один STM32 шьет другой STM32 тем более Атмегу.
Если есть у кого пример - кусок кода или ссылка будут очень благодарен!
Самое главное что я хотел этой веткой для себя решить - это наличие такой возможности. Соответсвенно она есть и теперь я могу двигаться дальше.
Всем спасибо!
Go to the top of the page
 
+Quote Post
scifi
сообщение May 30 2018, 16:54
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136



Цитата(AVStech @ May 24 2018, 12:39) *
Я нашел кучу примеров Bootloader'ов для прошивки по вайфай, USB и SD и т.п. Но не нашел ни одного примера кода как один STM32 шьет другой STM32 тем более Атмегу.

У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее:
CODE
#include "stm32load.h"
#include "stm32f4xx.h"
#include "myassert.h"
#include "serial.h"
#include "systime.h"
#include "pt.h"
#include "uart.h"
#include "../../pilot/pilot.h"
#include "../../dfb/dfb.h"
#include "../../sbs/sbs.h"
#include <string.h>
#include <stdio.h>
#include <stdbool.h>

#define NSLAVES 3
#define ACK 0x79

struct mem_op_data
{
int addr, len;
uint8_t* ptr;
};

static uint8_t rxfifo0[256];
static uint8_t rxfifo1[256];
static uint8_t rxfifo2[64];
static uint8_t txfifo[NSLAVES][64];
static struct uart_idx idx[NSLAVES];
static struct pt boot_pt[NSLAVES];
static int slave = 0;
static bool burning;

const struct uart_config uart3 =
{
.rxfifo = rxfifo0,
.rxsz = sizeof rxfifo0,
.txfifo = txfifo[0],
.txsz = sizeof txfifo[0],
.idx = &idx[0],
.regs = USART3,
.rxport = GPIOB,
.rxpin = 11,
.rxaf = 7,
.txport = GPIOB,
.txpin = 10,
.txaf = 7,
.irq = USART3_IRQn,
.prio = 7,
.apb_clock = SYSTIME_TPS / 4,
.baudrate = 57600,
};
const struct uart_config uart6 =
{
.rxfifo = rxfifo1,
.rxsz = sizeof rxfifo1,
.txfifo = txfifo[1],
.txsz = sizeof txfifo[1],
.idx = &idx[1],
.regs = USART6,
.rxport = GPIOC,
.rxpin = 7,
.rxaf = 8,
.txport = GPIOC,
.txpin = 6,
.txaf = 8,
.irq = USART6_IRQn,
.prio = 7,
.apb_clock = SYSTIME_TPS / 2,
.baudrate = 115200,
};
const struct uart_config uart4 =
{
.rxfifo = rxfifo2,
.rxsz = sizeof rxfifo2,
.txfifo = txfifo[2],
.txsz = sizeof txfifo[2],
.idx = &idx[2],
.regs = UART4,
.rxport = GPIOC,
.rxpin = 11,
.rxaf = 8,
.txport = GPIOC,
.txpin = 10,
.txaf = 8,
.irq = UART4_IRQn,
.prio = 7,
.apb_clock = SYSTIME_TPS / 4,
.baudrate = 115200,
};
static const struct uart_config* const uart[NSLAVES] =
{
&uart3, &uart6, &uart4
};

void
usart3_handler(void)
{
uart_handler(&uart3);
}

void
usart6_handler(void)
{
uart_handler(&uart6);
}

void
uart4_handler(void)
{
uart_handler(&uart4);
}

void
stm32load_init(void)
{
/*
// reset line for the pilot MCU, enable pull-up, configure as open-drain
GPIOD->BSRR = 1 << 11;
GPIOD->OTYPER |= GPIO_OTYPER_OT_11;
GPIOD->PUPDR |= GPIO_PUPDR_PUPDR11_0;
GPIOD->MODER |= GPIO_MODER_MODER11_0;
// reset line for the DFB MCU, configure as GPO
GPIOG->BSRR = 1 << 7;
GPIOG->OTYPER |= GPIO_OTYPER_OT_7;
GPIOG->PUPDR |= GPIO_PUPDR_PUPDR7_0;
GPIOG->MODER |= GPIO_MODER_MODER7_0;
// reset line for the SBS MCU, configure as GPO
GPIOA->BSRR = 1 << 11;
GPIOA->OTYPER |= GPIO_OTYPER_OT_11;
GPIOA->PUPDR |= GPIO_PUPDR_PUPDR11_0;
GPIOA->MODER |= GPIO_MODER_MODER11_0;
*/
for (int i = 0; i < NSLAVES; i++)
{
uart_init(uart[i], true);
PT_INIT(&boot_pt[i]);
}
}

static
PT_THREAD(getbyte_thread(struct pt* pt, int* ret))
{
static unsigned int start[NSLAVES];
PT_BEGIN(pt);
start[slave] = systime_ticks();
do
{
PT_YIELD(pt);
if (uart_rxcount(uart[slave]) > 0)
{
*ret = uart_getbyte(uart[slave]);
PT_EXIT(pt);
}
}
while (systime_ticks() - start[slave] < SYSTIME_TPS / 10);
*ret = -1;
PT_END(pt);
}

static void
send_address(int addr)
{
int csum = 0;
for (int j = 24; j >= 0; j -= 8)
{
uart_putbyte(uart[slave], addr >> j);
csum ^= addr >> j;
}
uart_putbyte(uart[slave], csum);
}

static
PT_THREAD(readmem_thread(struct pt* pt, struct mem_op_data* md, bool* success))
{
static struct pt slave_pt[NSLAVES];
int byte;
*success = false;
PT_BEGIN(pt);
// send command code
uart_putbytes(uart[slave], "\x11\xEE", 2);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
send_address(md->addr);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
// send number of bytes
uart_putbyte(uart[slave], md->len - 1);
uart_putbyte(uart[slave], ~(md->len - 1));
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
// receive data
static int i[NSLAVES];
for (i[slave] = 0; i[slave] < md->len; i[slave]++)
{
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte < 0)
{
PT_EXIT(pt);
}
md->ptr[i[slave]] = byte;
}
*success = true;
PT_END(pt);
}

static
PT_THREAD(writemem_thread(struct pt* pt, struct mem_op_data* md, bool* success))
{
static struct pt slave_pt[NSLAVES];
int byte;
*success = false;
PT_BEGIN(pt);
// send command code
uart_putbytes(uart[slave], "\x31\xCE", 2);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
send_address(md->addr);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
// send number of bytes
uart_putbyte(uart[slave], md->len - 1);
static int i[NSLAVES], csum[NSLAVES];
csum[slave] = md->len - 1;
for (i[slave] = 0; i[slave] < md->len; i[slave]++)
{
PT_WAIT_WHILE(pt, uart_txcount(uart[slave]) == 0);
uart_putbyte(uart[slave], md->ptr[i[slave]]);
csum[slave] ^= md->ptr[i[slave]];
}
uart_putbyte(uart[slave], csum[slave]);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
*success = true;
PT_END(pt);
}

static
PT_THREAD(erase_thread(struct pt* pt, int sector, bool* success))
{
static struct pt slave_pt[NSLAVES];
static unsigned int start[NSLAVES];
int byte;
*success = false;
PT_BEGIN(pt);
// send command code
uart_putbytes(uart[slave], "\x44\xBB", 2);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
PT_EXIT(pt);
}
char buf[5] = "\0\0\0";
buf[3] = buf[4] = sector;
uart_putbytes(uart[slave], buf, 5);
start[slave] = systime_ticks();
do
{
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
}
while (byte < 0 && systime_ticks() - start[slave] < 5 * SYSTIME_TPS);
if (byte != ACK)
{
PT_EXIT(pt);
}
*success = true;
PT_END(pt);
}

static
PT_THREAD(boot_thread(struct pt* pt))
{
static unsigned int i[NSLAVES], attempt[NSLAVES];
static struct pt slave_pt[NSLAVES];
int byte;
bool success;
PT_BEGIN(pt);
static const int reset_mask[NSLAVES] = {
SHREG2_RSTPLT, SHREG2_RSTDFB, SHREG2_RSTSBS
};
reset:
// assert reset
shiftreg2_modify(reset_mask[slave], 0);
// deassert reset
shiftreg2_modify(0, reset_mask[slave]);
// initial delay, wait for bootloader to become ready
i[slave] = systime_ticks();
PT_WAIT_WHILE(pt, systime_ticks() - i[slave] < SYSTIME_TPS / 5);
uart_flush(uart[slave]);
// send 0x7F for automatic baud rate detection
uart_putbyte(uart[slave], 0x7F);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
goto error;
}
// synchronization successful
static uint8_t buf[0x24];
static bool buflock;
static struct mem_op_data md[NSLAVES];
// read first 0x24 bytes
md[slave].addr = 0x08000000;
md[slave].len = 0x24;
md[slave].ptr = buf;
PT_WAIT_WHILE(pt, buflock);
buflock = true;
PT_SPAWN(pt, &slave_pt[slave], readmem_thread(&slave_pt[slave], &md[slave], &success));
buflock = false;
if (!success)
{
goto error;
}
// compare with same in image
static const uint8_t* const image[NSLAVES] = { image_pilot, image_dfb, image_sbs };
static const int image_size[NSLAVES] = { sizeof image_pilot, sizeof image_dfb, sizeof image_sbs };
if (memcmp(buf, image[slave], 0x24) != 0)
{
goto burn;
}
// read last 16 bytes
md[slave].addr = 0x08000000 + image_size[slave] - 16;
md[slave].len = 16;
md[slave].ptr = buf;
PT_WAIT_WHILE(pt, buflock);
buflock = true;
PT_SPAWN(pt, &slave_pt[slave], readmem_thread(&slave_pt[slave], &md[slave], &success));
buflock = false;
if (!success)
{
goto error;
}
// compare with same in image
if (memcmp(buf, image[slave] + image_size[slave] - 16, 16) != 0)
{
goto burn;
}
else
{
goto done;
}
burn:
burning = true;
if (slave == 0)
{
// set voltage range to speed up flash erase/programming
md[slave].addr = 0xFFFF0000;
md[slave].len = 1;
md[slave].ptr = (void*)"\x03";
PT_SPAWN(pt, &slave_pt[slave], writemem_thread(&slave_pt[slave], &md[slave], &success));
if (!success)
{
goto error;
}
}
static const int sector_size[NSLAVES][16] = {
{ 0x4000, 0x4000, 0x4000, 0x4000, 0x10000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000, 0x20000 },
{ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024 },
{ 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024 }
};
static int sector[NSLAVES], upper_boundary[NSLAVES];
// start burning from address 0x24 up, save first 0x24 bytes for last
i[slave] = 0x24;
sector[slave] = 0;
upper_boundary[slave] = 0;
while (i[slave] < image_size[slave])
{
upper_boundary[slave] += sector_size[slave][sector[slave]];
upper_boundary[slave] = (upper_boundary[slave] > image_size[slave]) ?
image_size[slave] : upper_boundary[slave];
// erase sector
PT_SPAWN(pt, &slave_pt[slave], erase_thread(&slave_pt[slave], sector[slave], &success));
if (!success)
{
goto error;
}
// program firmware
while (i[slave] < upper_boundary[slave])
{
int len;
len = upper_boundary[slave] - i[slave];
len = (len > 256) ? 256 : len;
md[slave].addr = 0x08000000 | i[slave];
md[slave].len = len;
md[slave].ptr = (void*)&image[slave][i[slave]];
i[slave] += len;
PT_SPAWN(pt, &slave_pt[slave], writemem_thread(&slave_pt[slave], &md[slave], &success));
if (!success)
{
goto error;
}
}
sector[slave]++;
}
// program last chunk
md[slave].addr = 0x08000000;
md[slave].len = 0x24;
md[slave].ptr = (void*)image[slave];
PT_SPAWN(pt, &slave_pt[slave], writemem_thread(&slave_pt[slave], &md[slave], &success));
if (!success)
{
goto error;
}
// verify
i[slave] = 0;
while (i[slave] < image_size[slave])
{
static uint8_t buf[256];
int len = (i[slave] + 256 > image_size[slave]) ? image_size[slave] - i[slave] : 256;
md[slave].addr = 0x08000000 + i[slave];
md[slave].len = len;
md[slave].ptr = buf;
PT_SPAWN(pt, &slave_pt[slave], readmem_thread(&slave_pt[slave], &md[slave], &success));
if (memcmp(buf, image[slave] + i[slave], md[slave].len) != 0)
{
goto error;
}
i[slave] += md[slave].len;
}
done:
// issue Go command
uart_putbytes(uart[slave], "\x21\xDE", 2);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
goto error;
}
uart_putbytes(uart[slave], "\x08\x00\x00\x00\x08", 5);
PT_SPAWN(pt, &slave_pt[slave], getbyte_thread(&slave_pt[slave], &byte));
if (byte != ACK)
{
goto error;
}
uart_parityoff(uart[slave]);
uart_setrate(uart[slave], 115200);
PT_EXIT(pt);
error:
if (attempt[slave]++ < 3)
{
goto reset;
}
myassert(0);
PT_END(pt);
}

bool
stm32load_poll(void)
{
static bool done[NSLAVES];
static int done_count;
if (!done[slave])
{
if (PT_SCHEDULE(boot_thread(&boot_pt[slave])) == 0)
{
done[slave] = true;
done_count++;
}
}
slave = (slave + 1) % NSLAVES;
return done_count == NSLAVES;
}

bool
stm32load_burn(void)
{
return burning;
}
Go to the top of the page
 
+Quote Post
jcxz
сообщение May 30 2018, 19:35
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(scifi @ May 30 2018, 19:54) *
У меня есть система с ведущим и несколькими ведомыми STM32. Прошивка у ведомых обновляется через заводской загрузчик. Код для начинающих будет слишком мозголомным, надо думать, но тем не менее:

Ай-яй-яй! goto используете! не комильфо rolleyes.gif
Go to the top of the page
 
+Quote Post

Сообщений в этой теме
- AVStech   Прошивка STM32F7 через другой STM32F7 (UART)   May 23 2018, 14:58
- - zombi   ЦитатаПрошивка ведомых ведомым. Может прошивка вед...   May 23 2018, 16:44
|- - AVStech   Цитата(zombi @ May 23 2018, 19:44) Может ...   May 23 2018, 17:07
- - _pv   мелкий 8ми битный МК, за питанием следить и рубить...   May 23 2018, 17:10
|- - AVStech   Цитата(_pv @ May 23 2018, 20:10) вы что-т...   May 23 2018, 17:19
- - mantech   Цитата(AVStech @ May 23 2018, 17:58) Необ...   May 23 2018, 17:40
|- - AVStech   Цитата(mantech @ May 23 2018, 20:40) Не п...   May 23 2018, 18:17
- - Arlleex   ЦитатаТеперь вопросы - это возможно реализовать на...   May 23 2018, 17:50
- - x893   а посему нельзя использовать собственные загрузчик...   May 23 2018, 18:55
|- - AVStech   Цитата(x893 @ May 23 2018, 21:55) а почем...   May 24 2018, 05:22
- - kolobok0   Цитата(AVStech @ May 23 2018, 17:58) ...В...   May 23 2018, 19:04
- - x893   Когда загрузчики свои - вообще не проблема. UART +...   May 24 2018, 06:06
- - _pv   ресет-то зачем?   May 24 2018, 06:10
|- - AVStech   Цитата(_pv @ May 24 2018, 09:10) ресет-то...   May 24 2018, 06:43
- - _pv   Туда (в свой бутлоадер, а не встроенный) и програм...   May 24 2018, 07:24
|- - kolobok0   Цитата(_pv @ May 24 2018, 10:24) ...Можно...   May 24 2018, 17:10
- - Arlleex   Да ресет тут будет нужен для поддержки встроенного...   May 24 2018, 07:25
|- - HardEgor   https://electronix.ru/forum/index.php?showtopic=14...   May 24 2018, 09:57
||- - AVStech   Цитата(HardEgor @ May 24 2018, 12:57) htt...   May 24 2018, 10:12
- - Baser   Цитата(AVStech @ May 24 2018, 09:43) В пр...   May 24 2018, 09:57
- - Arlleex   Цитата(kolobok0 @ May 24 2018, 20:10) еди...   May 24 2018, 17:49
|- - leocat   Цитата(Arlleex @ May 24 2018, 18:49) О че...   May 25 2018, 12:10
|- - kolobok0   Цитата(Arlleex @ May 24 2018, 20:49) О че...   May 25 2018, 21:01
- - khach   Система с кучей STM32 в том числе F7 имеет право н...   May 26 2018, 13:25
|- - jcxz   Цитата(khach @ May 26 2018, 16:25) Это вс...   May 30 2018, 05:47
|- - Arlleex   Цитата(jcxz @ May 30 2018, 09:47) Соверше...   May 30 2018, 07:49
|- - jcxz   Цитата(Arlleex @ May 30 2018, 10:49) В ОЗ...   May 30 2018, 08:46
|- - Arlleex   Цитата(jcxz @ May 30 2018, 12:46) Не в ОЗ...   May 30 2018, 10:06
|- - jcxz   Цитата(Arlleex @ May 30 2018, 13:06) 1. П...   May 30 2018, 10:39
|- - Arlleex   Цитата(jcxz @ May 30 2018, 14:39) Нет, не...   May 30 2018, 10:57
|- - jcxz   Цитата(Arlleex @ May 30 2018, 13:57) Ох. ...   May 30 2018, 11:51
|- - Arlleex   Цитата(jcxz @ May 30 2018, 15:51) Но я пр...   May 30 2018, 13:06
- - Arlleex   КодPT_THREAD... А не тот ли это протопоток который...   May 30 2018, 19:57


Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th August 2025 - 23:54
Рейтинг@Mail.ru


Страница сгенерированна за 0.01446 секунд с 7
ELECTRONIX ©2004-2016