|
Вопрос по АРМ LPC |
|
|
3 страниц
1 2 3 >
|
 |
Ответов
(1 - 35)
|
Jan 20 2006, 12:16
|

Частый гость
 
Группа: Свой
Сообщений: 144
Регистрация: 5-08-05
Пользователь №: 7 382

|
Цитата(vesago @ Jan 20 2006, 14:13)  Хочу сделать первый проект на арм. Выбрал в частности 2214. С инструментарием определился - кейл. Хочу спросить - программный мастер 1 варе хорошо будет работать на этом камне или лучьше взять мелкий сопроцессор типа 2313 и по уарту слать? Нормально lpc будет работать с max232? Знаю, что lpc толерантен к 5В, но спрошу на всякий случай. И вообще, действительно ли можно оперировать свободно с 5в цепями? Програаммный мастер - как напишите, есть огранияения на скорость махания ногами. лучше взять мах3232 , помнить что утилита писания флэша может сама вставать в режим ISP исли заведёте RTS и DTR иначе придётся делать перемычки. Схема сброса вязть нормальную микруху с внешним сбросом или на худой конец R-C + тактовая кнопка. Толерантен к выходам - входам 5V но зачем, сейчас 3.3 логики - как грязи. Да и мелкую логику - ну нах , Xelinx 9500XL ставте и будет счастье... Помнить что некотрые линии без пуллапа SDL SDA , TxD Если серьёзно думаете работать с ARM7TDMI возьмите МТ-ЛИнк , но есть инфа что проект закрыт. можно и виглер, но это гамадризм... Номинал кварц брать из даташита .. вроде всё
--------------------
Свет мой зеркальце, скажи, да всю правду расскажи я ль на свете всех тупее, бесполезней и пьянее? Ты - придурок. Спору нет! Но живет на белом свете вот ТАКИХ еще две трети!
|
|
|
|
|
Jan 20 2006, 14:00
|
Участник

Группа: Свой
Сообщений: 72
Регистрация: 8-11-04
Из: Томск
Пользователь №: 1 070

|
Цитата(Ken@t @ Jan 20 2006, 15:16)  Если серьёзно думаете работать с ARM7TDMI возьмите МТ-ЛИнк , но есть инфа что проект закрыт. Откуда такая информация и по какой причине?
|
|
|
|
|
Jan 22 2006, 10:00
|

Частый гость
 
Группа: Свой
Сообщений: 144
Регистрация: 5-08-05
Пользователь №: 7 382

|
Цитата(vesago @ Jan 22 2006, 12:22)  Спасибо, Kenat. Хотел еще спросить - существенно ли медленнее исполняется код из флеша? Стоит ли максимально грузить в рам или в большинстве случаев можно из флеши? И еще - можно как нибудь дистанционно запустить загрузку во флешь используя штатный загрузчик. Допустим прибор где нибудь в отдалении по 485 связан и надобно поменять прошивку? Я так понял 14 лапу сожают на землю только для того, чтоб после ресета камень перешел в область загрузчика. скорость выполнения программ зависит от ваших требований. Только учтите, что скорость махание лапками ограничено аппаратной реализацией . Для загрузки своего кода можно применить технологию IAP + преназначение области встроенной RAM на вектора прерыванией MEMMAP .. код IAP прилагаю с примером. iap.h Код #ifndef _IAP_H #define _IAP_H
// Results from flashInit() #define LPC256K 2 #define LPC128K 1 #define LPCUNKN -1
// (Philips) Status Codes #define CMD_SUCCESS 0 #define INVALID_CMD 1 #define SRC_ADDR_ERROR 2 #define DST_ADDR_ERROR 3 #define SRC_ADDR_NOT_MAPPED 4 #define DST_ADDR_NOT_MAPPED 5 #define COUNT_ERROR 6 #define INVALID_SECTOR 7 #define SECTOR_NOT_BLANK 8 #define SECTOR_NOT_PREPARED 9 #define COMPARE_ERROR 10 #define BUSY 11 #define PARAM_ERROR 12 #define ADDR_ERROR 13 #define ADDR_NOT_MAPPED 14 #define CMD_LOCKED 15 #define INVALID_CODE 16 #define INVALID_BAUD_RATE 17 #define INVALID_STOP_BIT 18 #define CODE_READ_PROT_ENABLED 19
int flashBlank(int startSector, int endSector); int flashErase(int startSector, int endSector); int flashInit(void); int flashWrite(void *dst, void *src, unsigned byteCount);
#endif iap.c Код #include <intrinsic.h> #include "string.h" #include "iap.h"
#define IAP_LOCATION 0x7ffffff1 typedef void (*IAP)(unsigned long *,unsigned long *); IAP iap_entry =(IAP) IAP_LOCATION;
static unsigned long flashParams[5], flashResult[2]; static int lastSector;
#ifndef MIN #define MIN(n,m) (((n) < (m)) ? (n) : (m)) #endif
int GetProcessorClockFreq(void); int GetPeripheralClockFreq(void);
int flashBlank(int startSector, int endSector) { flashParams[0] = 53; // blank check flashParams[1] = startSector; // start sector flashParams[2] = endSector; // end sector iap_entry(flashParams,flashResult); return (int)flashResult[0]; }
int flashErase(int startSector, int endSector) { // unsigned long cpsr; // unsigned char memmap;
__disable_interrupt(); // memmap = MEMMAP; // get current memory map // MEMMAP = MEMMAP_FLASH; // map User Flash into low 64 bytes
flashParams[0] = 50; // prepare sector(s) flashParams[1] = startSector; // start sector flashParams[2] = endSector; // end sector
iap_entry(flashParams,flashResult);
if (flashResult[0] == CMD_SUCCESS) { flashParams[0] = 52; // erase sector(s) flashParams[1] = startSector; // start sector flashParams[2] = endSector; // end sector flashParams[3] = GetProcessorClockFreq()/1000; // cpu clock freq in KHz iap_entry(flashParams,flashResult); }
//MEMMAP = memmap & 0x03; // restore the memory map __enable_interrupt(); return (int)flashResult[0]; }
int flashInit(void) { lastSector = 0; // clear last usable sector #
switch (flashBlank(16, 16)) // check last sector of 256K parts { case CMD_SUCCESS: case SECTOR_NOT_BLANK: lastSector = 16; // store last usable sector # return LPC256K;
case INVALID_SECTOR: break;
default: return LPCUNKN; }
switch (flashBlank(14, 14)) // check last sector of 128K parts { case CMD_SUCCESS: case SECTOR_NOT_BLANK: lastSector = 14; // store last usable sector # return LPC128K;
default: break; }
return LPCUNKN; }
int flashWrite(void *dst, void *src, unsigned byteCount) { // unsigned char memmap; unsigned long count; // cpsr,; unsigned long buffer[512/sizeof (long)];
if ((lastSector == 0) && ( flashInit() == LPCUNKN)) return BUSY;
while (byteCount) { // cpsr = disableINTs(); // disable all interrupts __disable_interrupt();
// memmap = MEMMAP; // get current memory map // MEMMAP = MEMMAP_FLASH; // map User Flash into low 64 bytes
// prepare all sectors for programing flashParams[0] = 50; // prepare sectors flashParams[1] = 0; // start sector flashParams[2] = lastSector; // last sector iap_entry(flashParams,flashResult);
if (flashResult[0] != CMD_SUCCESS) { // MEMMAP = memmap & 0x03; // restore the memory map // restoreINTs(cpsr); // restore all interrupts __enable_interrupt(); return flashResult[0]; }
// check if dst is on 512 byte boundary if ((unsigned long)dst & 0x1FF) { // no, copy 512 bytes from flash into buffer memcpy(buffer, (void *)((unsigned long)dst & ~0x1FF), 512);
// copy src into buffer count = MIN(byteCount, (512 - ((unsigned long)dst & 0x1FF))); memcpy((char *)buffer + ((unsigned long)dst & 0x1FF), src, count);
flashParams[0] = 51; // copy RAM to Flash flashParams[1] = (unsigned long)dst & ~0x1FF; // destination flashParams[2] = (unsigned long)buffer; // source flashParams[3] = 512; // byte count flashParams[4] = GetProcessorClockFreq()/1000; // CCLK in KHz iap_entry(flashParams,flashResult);
// MEMMAP = memmap & 0x03; // restore the memory map // restoreINTs(cpsr); // restore all interrupts __enable_interrupt();
if (flashResult[0] != CMD_SUCCESS) return (int)flashResult[0];
dst = (char *)dst + count; src = (char *)src + count; byteCount -= count; continue; // restart the loop to prepare sectors again }
// dst is now aligned on a 512 byte boundary // find the biggest chunk we can program at a time if (byteCount >= 8192) count = 8192; else if (byteCount >= 4096) count = 4096; else if (byteCount >= 1024) count = 1024; else if (byteCount >= 512) count = 512; else count = byteCount;
if (count < 512) { memcpy(buffer, dst, 512); // copy current flash contents into buffer memcpy(buffer, src, count); // update buffer with the new contents flashParams[2] = (unsigned long)buffer; // source flashParams[3] = 512; // byte count } else { flashParams[2] = (unsigned long)src; flashParams[3] = count; // byte count }
flashParams[0] = 51; // copy RAM to Flash flashParams[1] = (unsigned long)dst; // destination flashParams[4] = GetProcessorClockFreq()/1000; // CCLK in KHz
iap_entry(flashParams,flashResult);
// MEMMAP = memmap & 0x03; // restore the memory map // restoreINTs(cpsr); // restore all interrupts __enable_interrupt();
if (flashResult[0] != CMD_SUCCESS) return (int)flashResult[0];
dst = (char *)dst + count; src = (char *)src + count; byteCount -= count; }
return CMD_SUCCESS; } пример Код // 1 Erase 16 block printf ("Erase calibration\r\n"); rslt = flashErase(16, 16); printf ("Result = 0x%04X\r\n",rslt); printf ("Write calibration\r\n"); rslt = flashWrite((int *)pCalibration, &ZerroREDOX, sizeof(int)); printf ("Result = 0x%04X\r\n",rslt);
--------------------
Свет мой зеркальце, скажи, да всю правду расскажи я ль на свете всех тупее, бесполезней и пьянее? Ты - придурок. Спору нет! Но живет на белом свете вот ТАКИХ еще две трети!
|
|
|
|
|
Jun 6 2006, 05:54
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Да, мудрено вот тольок я не увидел код описания и инициализации переменных во флешь памяти никак не пойму как сделать это правильно. сейчас так: "globals.h" Код typedef union { int i; unsigned char b[4]; float r; } RealByte; "IAP.cpp" Код #include <inarm.h> #include <iolpc2214.h> #include "globals.h" #include "iap.h" #include "string.h"
const RealByte ParamInEEPROM[6] @ "EEPROM" = { 500, 2000, 10, 120, 0.1, 1.3 // Mic1 sensor };
RealByte const * pParamInEEPROM = ParamInEEPROM; EEPROM - один сектор flash, 16ый, адреса 0x0003 C000 - DFFF) в проце LPC2214 Код -DEEPROMSTART=0x0003C000 -DEEPROMEND=0003DFFF
-Z(DATA)EEPROM=EEPROMSTART-EEPROMEND pParamInEEPROM - используется в других модулях Если делать extern сам массив , то линкер выдает ошибку , типа undefine external symbol Сейчас просто Warning "Warning[w29]: Parts of segment EEPROM are initialized (as in module IAP), even though it is of type DATA (and thus not promable)" и что с ним делать? совсем не понятно да , кстати при инициализации 0.1 , 1.3 преобразовываются в int а мне это не нужно, может есть какие мысли по поводу как это исправить? --------------------------------- по второму вопросу разобрался надо делать так Код const RealByte ParamInEEPROM[6] @ "EEPROM" = { 500, 2000, 10, 120, {.r = 0.1}, {.r = 1.3} // Mic1 sensor };
|
|
|
|
|
Jun 6 2006, 07:41
|

Участник

Группа: Свой
Сообщений: 47
Регистрация: 28-07-05
Пользователь №: 7 162

|
Цитата(vesago @ Jan 22 2006, 14:18)  Большое спасибо! Буду переваривать. А какую максимальную частоту SPI можно выжать из LPC? Судя по юзер мануалу - не более 8 МГц. Или я не правильно понял? Реально удалось пропустить 1.6 МБита на такте 2.5 МГц (тактовая процессора-60 МГц). Там получаются большие дырки между байтами. Слишком простой SPI.
|
|
|
|
|
Jun 6 2006, 08:58
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(vesago @ Jan 22 2006, 13:18)  Большое спасибо! Буду переваривать. А какую максимальную частоту SPI можно выжать из LPC? Судя по юзер мануалу - не более 8 МГц. Или я не правильно понял? 7,5 - 1/8 тактовой. Для 213x/4x SSP мастером - на 1/2 тактовой, есть FIFO и SSEL аппаратный работает. Полную 30MHz скорость, естественно, не развить, ибо FIFO разгружается быстрее, чем его можно реально загружать.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jun 6 2006, 14:09
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Что то я туплю, уже целый день туплю. где то глюк есть.
Вообщем по UART запускаю процедуру IAP, зашиваю коэффициенты во флешь, результат выполнения этой процедуры запихиваю в буфер + устанавливаю счетчик байт на отправку следущим запросом хочу получить этот результат, но ! при этом запросе получаю 000 , т.е. буфер пуст и счетчик кем то сброшен. При пошаговой отладке место сброса буфера не обнаруживается, но стоит поставить breakpoint на прерывание от uarta, и я сразу вижу что буфер со счетчиком изменены
Поставив точку останова на доступ к ячейке памяти нашел, что трет переменную и буфер процедура __segment_init() - стандартная процедура выполняется еще до main() но кто вызывает ее и зачем мне выяснить не удалось Если закоментировать процедуру IAP то проблемы нет
RAM юзается до адреса 0x87F, т.е. 0x0400_0000 - 0x0400_087F В описании на IAP написано чт оюзаются последнии 16 байт RAM, вроде не пресекамся, даже близко
Ткните пальцем что я не правильно делаю.
------------------- поставил точку останова не адрес 0 и она сработала значит это ресет вот тольок какой то задерженный, почему дает выйди из процедуры и не проявлется при пошаговой отладке? всеравно не понятно PLL выключен
|
|
|
|
|
Jun 6 2006, 16:46
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(-=Space=- @ Jun 6 2006, 19:05)  Вопрос свелся не почему вызывается __segment_init() и зачем он нужен а почему после writeflash(), причем не сразу, а именно с задержкой следует reset? Вопрос звучал _совсем_ не так. По reset - вычитывайте исходники writeflash(). Прерывания, надеюсь, запрещены? Watchdog - как у Вас? Версия Bootloader какая? Без официальных багов с PLL?
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jun 7 2006, 05:50
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Цитата По reset - вычитывайте исходники writeflash(). Не совсем понял, это про что? Цитата Прерывания, надеюсь, запрещены? прерывания отключаются при входе в flashwrite() и вновь разрешаются при выходе из нее Цитата Watchdog - как у Вас? никогда не использовался в моем коде нет команд работы с WD, а после ресета он вроде выключен. может его IAP включать? Цитата Версия Bootloader какая? Без официальных багов с PLL? Команда 55 выдала 0x01 0x40 кстати PLL выключен , проц работает на частоте кварца 11,059МГц (это из соображений минимизации потребления)
|
|
|
|
|
Jun 7 2006, 06:09
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Цитата(GetSmart @ Jun 7 2006, 03:29)  Чую, прерывания разрешены и всё падает на ресет. Это если конечно writeflash() не кривая. А в конце RAM зарезервированно 32 байта. привожу свою процедуру flashWrite() немного соптимизировал ее 1. стирание сектора включено в процедуру записи 2. массив данных во flash всегда начинается с начала сектораи размером не больше сектора потом будет const RealByte ParamInEEPROM[2048] @ "EEPROM" = {.....} т.е. 8K - ровно 1 сектор Код int flashWrite(void) { RealByte buffer[512/sizeof (long)]; int i,result;
__disable_interrupt();
flashParams[0] = 50; // prepare sector flashParams[1] = 16; // start sector flashParams[2] = 16; // end sector
iap_entry(flashParams,flashResult);
if (flashResult[0] != CMD_SUCCESS) { __enable_interrupt(); return flashResult[0]; }
flashParams[0] = 52; // erase sector) flashParams[1] = 16; // start sector flashParams[2] = 16; // end sector flashParams[3] = GetProcessorClockFreq()/1000; // cpu clock freq in KHz iap_entry(flashParams,flashResult);
if (flashResult[0] != CMD_SUCCESS) { __enable_interrupt(); return flashResult[0]; }
// prepare all sectors for programing flashParams[0] = 50; // prepare sectors flashParams[1] = 16; // start sector flashParams[2] = 16; // last sector iap_entry(flashParams,flashResult);
if (flashResult[0] != CMD_SUCCESS) { __enable_interrupt(); return flashResult[0]; }
// загрузка буфера for(i=0;i<512/sizeof (long);i++) buffer[i].i = 0xffffffff;
buffer[0].i = SensorMic.MicP1; /* .......... */ /* здесь продолжается заполнятся массив */ /* .......... */
flashParams[0] = 51; // copy RAM to Flash flashParams[1] = (unsigned long)ParamInEEPROM; // destination flashParams[2] = (unsigned long)buffer; // source flashParams[3] = 512; // byte count flashParams[4] = GetProcessorClockFreq()/1000; // CCLK in KHz iap_entry(flashParams,flashResult);
if (flashResult[0] != CMD_SUCCESS) { __enable_interrupt(); return flashResult[0]; }
__enable_interrupt(); return CMD_SUCCESS; }
|
|
|
|
|
Jun 7 2006, 06:57
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Код RealByte buffer[512/sizeof (long)]; ....
// загрузка буфера for(i=0;i<512/sizeof (long);i++) buffer[i].i = 0xffffffff; ....... buffer[0].i = SensorMic.MicP1; ....... Что-то более чем мутное с буфером и адресацией в нем. 1. А в стеке место есть под буфер? 2. дабы все было непонятно используем sizeof(long); 3. Ну жуткая необходимость была называть элемент union 'i';
Сообщение отредактировал zltigo - Jun 7 2006, 08:06
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jun 7 2006, 07:25
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Цитата(zltigo @ Jun 7 2006, 10:57)  Что-то более чем мутное с буфером и адресацией в нем. RealByte это union из 4 байт которым может читаться как int как float или как 4 отдельных байта ну мне просто нужен не типизированный 4 байтовый буфер , и я так вышел из положения исходники RealByte были выше Кстати провел опыт: сразу после reseta посылаю стаус WD, там должен появиться бит если сброс произошел из-за таймаута WD. Получаю 00, т.е. Reset "внешний", не WDшный. Вот только происходит он не сразу, я успеваю отправить как минимум один байт - подтверждение что команда выполнена (просто байт 0x01) Бывает что успеваю и запросить ответ - результат выполнения функции flashWrite (всего по протоколу получается 9 байт) но вероятность этого мала, получилось всего 1- 2 раза. 0x01 принимается всегда.
|
|
|
|
|
Jun 7 2006, 09:40
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Вообщем выкладываю почти весь проект в основном модули которые интересны все остальные сенсорные модули имеют туже структуру что и sensor_radiation.cpp Цитата А в стеке место есть под буфер? 100 против 1, что нет. в стеке 1024 байта, помоему более чем достаточно а точно локальные переменные в стеке хранятся? надо проверить
Прикрепленные файлы
1.ZIP ( 32.6 килобайт )
Кол-во скачиваний: 54
|
|
|
|
|
Jun 7 2006, 11:30
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Цитата RealByte const * pParamInEEPROM; А Вы уверены, что хотели именно так написать? Это будет указатель на variable (!!!) (а не FLASH), который нельзя будет изменять. Это если конечно в С++ всё не запутали окончательно. Я пишу на Си. Думаю, надо так: const RealByte *pParamInEEPROM; Вызывать из прерываний IAP конечно можно. Однако. Для начала сделайте прерывание от UART с возможностью вложенных прерываний. Что-то типа этого: Цитата __arm __irq __nested void UART1Interrupt() { _enaIRQ(); switch(U1IIR & 0x0f) { ..... } _disIRQ(); VICVectAddr = 0; // Clear interrupt in VIC. } Это сделает такой фокус, что внутри прерывания стек будет использоваться глобальный. Поэтому стек прерываний уменьшите до 128..256 байт обратно. Цитата typedef void (*IAP)(unsigned long *,unsigned long *); IAP iap_entry =(IAP) IAP_LOCATION; исправьте на typedef void (__interwork *IAP)(unsigned long *,unsigned long *); IAP iap_entry =(IAP) IAP_LOCATION; Хотя прерывание не исправляйте. Чё-то у Вас оно как-то неуклюже написано. И лучше не напрягаться, а то долго объяснять мне придётся что к чему. Почему-то нашёл только один вектор для таймера.
Сообщение отредактировал GetSmart - Jun 7 2006, 11:33
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
|
Jun 7 2006, 11:34
|
Частый гость
 
Группа: Свой
Сообщений: 105
Регистрация: 14-01-05
Из: Москва Зеленоград
Пользователь №: 1 962

|
Цитата(GetSmart @ Jun 7 2006, 15:23)  Вызывать из прерываний IAP конечно можно. Однако. я вот сечас думаю а чт опроизойдет после выполнения __enable_interrupt(); в функции flashWrite();? прерывания должны разрешиться, а значит возможны вложенные прерывания нет ли здесь какой заковырки? Цитата(GetSmart @ Jun 7 2006, 15:23)  Цитата typedef void (*IAP)(unsigned long *,unsigned long *); IAP iap_entry =(IAP) IAP_LOCATION; исправьте на typedef void (__interwork *IAP)(unsigned long *,unsigned long *); IAP iap_entry =(IAP) IAP_LOCATION; а это точно нужно? у меня в general options IARа стоит generete interwork code + весь код у меня в режиме Trumb (тамже в general options - processor mode ) Цитата(GetSmart @ Jun 7 2006, 15:23)  Хотя прерывание не исправляйте. Чё-то у Вас оно как-то неуклюже написано. И лучше не напрягаться, а то долго объяснять мне придётся что к чему. Почему-то нашёл только один вектор для таймера. всмысле Interrupt.cpp это один вектор на все прерывания - от уратов, таймеров, i2c -------------------------добавлено перенес функцию writeflash() из прерывания в main и пререстало ресетить что бы это занчило?
|
|
|
|
|
Jun 7 2006, 12:00
|
.
     
Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753

|
Вы угадали. Ваши прерывания написаны так, что не позволяют вложенности. А когда на выходе writeflash() вы их разрешаете, то рушится стек IRQ. Таки дела. Я предпочитаю писать __interwork чтобы не зависеть от опций компилятора. Но не хотите, как хотите. В Вашем случае это не обязательно. Цитата всмысле Interrupt.cpp это один вектор на все прерывания - от уратов, таймеров, i2c Я говорил про векторизованные обработчики.
--------------------
Заблуждаться - Ваше законное право :-)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|