Проблемы с аппаратным I2C на AVR ATmega32

В разделе рассматриваются вопросы информационного обмена в робототехнике.
Аппаратный уровень, протоколы.

Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 08 июн 2011, 11:11

Делаю аппаратный I2C по апноутам с сайта Атмела. Там есть i2c master(AVR315) и slave(AVR311) отдельно.
Переделал под CVAVR. Теоретически все работает, но с некоторыми багофичами неприятными. Может кто сталкивался?

1) В указаных апноутах функционал I2C реализован не полностью. Например на мастере можно только Послать N байт (старт, серия байт с подтверждением, стоп) или принять N байт (Старт, прием неск байт, стоп). А например для нормальной работы с ЕЕПРОМом нужно не делая стоп, выполнять повторный старт и т.д. Ну это ладно, я допишу, это несложно.
2) С учетом пункта 1 пока сделал функцию записи 1 байта в ЕЕПРОМ и функцию чтения 1 байта. Функции работают, проверял. Но при попытке записать много байт, вызывая эту функцию в цикле возникает проблема.
Код: Выделить всё
// Функция записи 1 байта
unsigned char twi_eeprom_write1b(unsigned int addr, unsigned char byte)
{
        unsigned int w;

        messageBuf[0] = 0b10101110;                     // Адрес Slave(ЕЕПРОМ) + БитЗапись
        messageBuf[1] = (unsigned char)(addr >> 8);     // Старший байт адреса
        messageBuf[2] = (unsigned char)(addr & 0xff);   // Младший байт адреса
        messageBuf[3] = byte;                           // 1 байт

        TWI_Start_Transceiver_With_Data( messageBuf, 3+1 );
        while(TWI_Transceiver_Busy()) continue;

        //delay_ms(3); - Про эту строку подробней дальше

        return(TWI_statusReg.lastTransOK); // Возвращается статус операции - удачно записано или нет.
}

Если эту функцию вызывать 1 раз - проблем нет, все работает.
Если в цикле, например 1 кБайт переслать, то функция возвращает true только примерно каждые 16 байт
Если раскомментировать строку с задержкой - то все ок, 100% возврат true.
Далее!!!
если вместо задержки написать следующее:
Код: Выделить всё
if(TWI_statusReg.lastTransOK==0) delay_ms(3);

Т.е. вставлять задержку только если операция неудачно завершилась, то опять только каждый 16й байт успешно записан. Пробовал играть со скоростью в настройках I2C. Менял от 32 кГц до 400 кГц - это ничего не меняет. А постаянная задержка в 3мс это очень медленно получается.

3) Пробовал соединить 2 МК, один мастер, другой слейв. Пересылка по 1 байту работает, по 2-4 работает, а вот начиная с 5 байт данные не передаются.
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 08 июн 2011, 11:38

Я читал, что есть какой-то прикол с ЕЕПРОМом - один в один - типа байт пишет, а в цикле - нет.
Еще посмотри внимательно на функцию busy - что с ней. И третье - возможно, у тебя в настройках отключен clock stretching. Это когда мастер что-то запрашивает у слейва, после принятия слейв прижимает к земле clock - дает возможность пользователю выполнить какое-то действие, например, пихнуть байт в буффер. Если этого нет, то clock продолжает дергаться, но слейв ничего не отвечает - он ждет действия пользователя. Вот и возникает проскок по времени.
А, еще одно - надо проверить, каждый 16-й байт - это один и тот же байт (если да - то камень ресеттится чем-то, может вотчдогом), а если идет по массиву, типа 0-й, 16-й и т.д., то скорее всего клок стретчинг отключен.

Добавлено спустя 5 минут 14 секунд:
Вот еще прикольчик атмег:
The TWINT Flag must be cleared by software by writing a logic one to it. Note that this flag is not
automatically cleared by hardware when executing the interrupt routine. Also note that clearing
this flag starts the operation of the TWI, so all accesses to the TWI Address Register (TWAR),
TWI Status Register (TWSR), and TWI Data Register (TWDR) must be complete before clearing
this flag.

т.е. флаг прерывания в самом конце обработчика обнулять, а не где заблагорассудится.

Добавлено спустя 1 минуту 10 секунд:
А, может еще память поддерживает только постраничную запись
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 08 июн 2011, 12:45

Память поддерживает любую запись и прямую тоже. Только прямая и работает. Байты разные, смотроваясобака отключена.
А вот где смотреть про слок стретчинг не нашел. В апноутах инициализация идет следующим образом:
Код: Выделить всё
void TWI_Master_Initialise(void)
{
  TWBR = TWI_TWBR;                                  // Set bit rate register (Baudrate). Defined in header file.
// TWSR = TWI_TWPS;                                  // Not used. Driver presumes prescaler to be 00.
  TWDR = 0xFF;                                      // Default content = SDA released.
  TWCR = (1<<TWEN)|                                 // Enable TWI-interface and release TWI pins.
         (0<<TWIE)|(0<<TWINT)|                      // Disable Interupt.
         (0<<TWEA)|(0<<TWSTA)|(0<<TWSTO)|           // No Signal requests.
         (0<<TWWC);                                 //
}   
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 08 июн 2011, 13:23

А че прерывания отключены?

Добавлено спустя 30 секунд:
какой еепром, кстати?
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 08 июн 2011, 15:35

Прерывания включаются при старте приема/передачи и выключаются после завершения. Это так в апноуте, я не менял там ничего еще пока.
еепром 24LC512-I/SM 512 Кбит (64К x 8 ) SEEPROM, I2C 400 КГц, 2.5- 5.5 V SOIC-8

Попробовал немного другой вариант проверки I2C
МК-1 мастер, МК-2 слейв
Мастер берет данные из звукового массива и по 1 пихает их в МК-2.
Слейв ловит байты и выводит их на динамик.

Пришлось вставлять задержки при передаче. система сохраняет работоспособность при задержке в 400 нс, при 350 нс уже негарантированная переча - звук искажается на слух. При 300 нс сильные искажения и иногда зависания мастера.
Т.о. производительность подобного канала получилась всего 2120 байт/сек при задержке в 400 нс на каждый байт.

Вроде как заявлено что 400 кГц I2C. Это 50 кбайт/с, ну у меня по 1 байту, там получается в 2 раза меньше, ну 25 кБайт/с, но никак не 2 должно быть. Или мои расчеты неверны?
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 08 июн 2011, 16:32

А как у тебя функция записи устроена? Ты заметил, что там сначала идет передача адреса, потом ДВА байта - адрес слова, и только потом данные?

Добавлено спустя 2 минуты 52 секунды:
Вот еще какая-то странная тема: After receiving another
Acknowledge signal from the 24XX512, the master
device will transmit the data word to be written into the
addressed memory location. The 24XX512 acknowledges
again and the master generates a Stop
condition. This initiates the internal write cycle and
during this time, the 24XX512 will not generate
Acknowledge signals (Figure 6-1). If an attempt is
made to write to the array with the WP pin held high, the
device will acknowledge the command, but no write
cycle will occur, no data will be written and the device
will immediately accept a new command. After a byte
Write command, the internal address counter will point
to the address location following the one that was just
written.

Добавлено спустя 2 минуты 27 секунд:
Вот, ты наверняка не тестируешь - заакноледжила ли память контрол байт. А это надо делать, прежде чем пихать адрес слова и данные
Since the device will not acknowledge during a write
cycle, this can be used to determine when the cycle is
complete (this feature can be used to maximize bus
throughput). Once the Stop condition for a Write
command has been issued from the master, the device
initiates the internally timed write cycle. ACK polling
can be initiated immediately. This involves the master
sending a Start condition, followed by the control byte
for a Write command (R/W = 0). If the device is still
busy with the write cycle, then no ACK will be returned.
If no ACK is returned, then the Start bit and control byte
must be resent. If the cycle is complete, then the device
will return the ACK and the master can then proceed
with the next Read or Write command. See Figure 7-1
for flow diagram.
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 08 июн 2011, 23:17

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

С тестированием я действительно облажался. Пример в апноуте очень не прозрачный, я не сразу понял.
После переделки с мк-1 на мк-2 скорость передачи данных возросла до 9 кбайт/с, т.к. ушла необходимость вставки задержки. Это уже на что-то похоже. Звук с оцифровкой в 8кГц передается замечательно.

Пытаюсь теперь с ЕЕПРОМом опять поработать. Запись делается теперь оч быстро. 16 кбайт записывается за 1.4 секунды, показывая скорость записи около 10.5 кбайт/с.
А вот с чтением все еще проблемы. Во первых оно явно медленнее, чем запись. Это в принципе понятно, т.к. для чтения надо записать в I2C адрес устройства, 2 байта адреса памяти, потом 2 байта прочитать и 2й байт будет как раз тем чем надо. Очень муторная процедура.

У меня скорость чтение показывает 5.5 кбайт/сек, что в принципе неплохо, если бы не одно НО! Первая половина данных считывается нормально, а 2я половина данных - мусор, в основном из FF. Т.е. получается я звук на 16 кбайт записываю, потом читаю и воспроизвожу только 8 кбайт и получаю свой звук. Сейчас пытаюсь выяснить где грабли, при чтении или при записи...
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 08 июн 2011, 23:20

Слуш, я не пойму. Ты вроде пишешь ОДИН байт и в цикле? Либо ты фигачешь за один сеанс все 16кб? Если второе, то там в даташите написано, что сколько бы ты туда не запихал, запишется все равно только одна страница. А остальное выделиться в виде тепловой энергии!
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 09 июн 2011, 08:18

Ненене. Яж писал что по 1 байту.
На данный момент мои функции (которые используют апн AVR315) выглядят так:
Код: Выделить всё
// Запись одного байта
unsigned char twi_eeprom_write1b(unsigned int addr, unsigned char byte)
{
        unsigned int w;

        messageBuf[0] = 0b10101110;                     // Slave + Запись
        messageBuf[1] = (unsigned char)(addr >> 8);     // Старший байт адреса
        messageBuf[2] = (unsigned char)(addr & 0xff);   // Младший байт адреса
        messageBuf[3] = byte;                           // 1й байт

        TWI_Start_Transceiver_With_Data( messageBuf, 3+1 );

        while(TWI_Transceiver_Busy()) continue; // Ожидаем пока линия освободится

        // Вот это условие теперь вместо задержки
        if ( TWI_statusReg.lastTransOK==0 ) // Проверяем статус последней операции
        {
                TWIerrorMsg = TWI_Get_State_Info( ); // Ожидаем линию + получаем статус
                // Если получили отбой, делаем перезапуск
                if ( (TWIerrorMsg == TWI_MTX_ADR_NACK) | (TWIerrorMsg == TWI_MRX_ADR_NACK) )
                        TWI_Start_Transceiver();
        }

        return(TWI_statusReg.lastTransOK); // Возвращаем статус операции
}

// Чтение 1 байта
unsigned char twi_eeprom_read1b(unsigned int addr)
{

        messageBuf[0] = 0b10101110;     // Slave + Запись
        messageBuf[1] = (unsigned char)(addr >> 8);     // Старший байт адреса
        messageBuf[2] = (unsigned char)(addr & 0xff);   // Младший байт адреса
        TWI_Start_Transceiver_With_Data( messageBuf, 3 ); // Посылаем адрес

        while(TWI_Transceiver_Busy()) continue; // Ждем освобождения линии

        if ( TWI_statusReg.lastTransOK==0 ) // Проверяем статус
        {
                TWIerrorMsg = TWI_Get_State_Info( ); // Ждем линию + получаем код ошибки
                // Если получили отбой, делаем перезапуск
                if ( (TWIerrorMsg == TWI_MTX_ADR_NACK) | (TWIerrorMsg == TWI_MRX_ADR_NACK) )
                        TWI_Start_Transceiver();
        }

        if ( TWI_statusReg.lastTransOK ) // Если все ОК, начинаем чтение
        {
                messageBuf[0] = 0b10101111;     // Slave + Чтение
                TWI_Start_Transceiver_With_Data( messageBuf, 1 ); // Стартуем посылаем адрес устройства
                while(TWI_Transceiver_Busy()) continue; // Ждем линию
                TWI_Get_Data_From_Transceiver( messageBuf, 2 ); // Стартуем на чтение 2х байт
                while(TWI_Transceiver_Busy()) continue; // Ждем линию
        }
        return(messageBuf[1]); // 2й байт - это то, что нам нужно
}


Сдается мне что-то не так с функцией чтения у меня. Например если чтение не удалось, функция все равно что-то возвращает. Как минимум это неправильно.

Добавлено через час:

Фиг. Все таки не то что-то с записью :(
Пишу 16 байт, читаю 16 байт. Пишется только 1 первый байт, остальные фиг.
Ставлю задержку в 3 мс, пишется и читается все на ура.
Как же это победить-то?
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 09 июн 2011, 11:35

Ну пихни ты туда не 4 байта, а 19 байт, в чем проблема-то? У тебя в функцию передается массив, сделай его 19-ти байтами, и суй туда - никакой рестарт не нужен при батч-записи :-)

Добавлено спустя 24 минуты 54 секунды:
(TWIerrorMsg == TWI_MTX_ADR_NACK) | (TWIerrorMsg == TWI_MRX_ADR_NACK)
каков результат операции?
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 09 июн 2011, 11:38

Да пока проблема не этом была, а в 100% надежности передаваемой информации.
Разобрался. В апноуте ошибка. Точнее там сделано допущение, что слейв очень быстро принимает данные. МК с такой нагрузкой справляется, а вот ЕЕПРОМ нет, в результате и были проблемы с недостоверностью данных.

На самом деле после посылки байта (или блока, неважно) нужно делать не однократную проверку, а циклическую, до достижения результата (либо до какого-то разумного таймаута). Я переделал у себя это место.

Вот результирующий код:
Код: Выделить всё
///////////////////////////////////////////////////////////////////////
// Запись одного байта в ЕЕПРОМ
unsigned char twi_eeprom_write1b(unsigned int addr, unsigned char byte)
{
        messageBuf[0] = 0b10101110;                     // ChipSlave + Запись
        messageBuf[1] = (unsigned char)(addr >> 8);     // Старший байт адреса
        messageBuf[2] = (unsigned char)(addr & 0xff);   // Младший байт адреса
        messageBuf[3] = byte;                           // 1й байт

        while(TWI_Transceiver_Busy()) continue;                 // Ожидание шины
        TWI_Start_Transceiver_With_Data( messageBuf, 3+1 );     // Старт с записью
        while(TWI_Transceiver_Busy()) continue;                 // Ожидание шины
       
        TWI_error_restart();    // Циклический перезапуск шины или нафиг через 0.0625 с

        return(TWI_statusReg.lastTransOK); // Возвращаем статус
}
//////////////////////////////////////////////////
// Чтение одного байта из ЕЕПРОМ
unsigned char twi_eeprom_read1b(unsigned int addr)
{
        messageBuf[0] = 0b10101110;                             // ChipSlave + Запись
        messageBuf[1] = (unsigned char)(addr >> 8);             // Старший байт адреса
        messageBuf[2] = (unsigned char)(addr & 0xff);           // Младший байт адреса
        TWI_Start_Transceiver_With_Data( messageBuf, 3 );       // Посылаем адрес читаемого байта
        while(TWI_Transceiver_Busy()) continue;                 // Ждем шину

        TWI_error_restart();    // Циклический перезапуск шины или нафиг через 0.0625 с

        if ( TWI_statusReg.lastTransOK )                // Если все ок, читаем
        {
                messageBuf[0] = 0b10101111;     // Slave + Чтение
                TWI_Start_Transceiver_With_Data( messageBuf, 1 );       // Повторный старт и запись адреса чипа
                while(TWI_Transceiver_Busy()) continue;                 // Ждем шину
                TWI_error_restart();    // Циклический перезапуск шины или нафиг через 0.0625 с
                TWI_Get_Data_From_Transceiver( messageBuf, 2 );         // Чтение 2х байт (первый мусор)
                while(TWI_Transceiver_Busy()) continue;                 // Ждем шину
                TWI_error_restart();    // Циклический перезапуск шины или нафиг через 0.0625 с
                return(messageBuf[1]);                                  // Возвращаем байт
        }

        return(0); // Для проверки ошибки надо анализировать TWI_statusReg.lastTransOK в вызывающей программе
}
/////////////////////////////////////////////////////////////////////////////////
// Циклическая проверка последней операции и перезапуск шины. Таймаут 0.0625 сек
void TWI_error_restart()
{
        unsigned int w=0;
        while( TWI_statusReg.lastTransOK==0 && w<1474)  // Ожидание успешного окончания операции или наф через 0.0625 с
        {
                TWIerrorMsg = TWI_Get_State_Info( );    // Ждм шину, получаем статус
                if ( (TWIerrorMsg == TWI_MTX_ADR_NACK) | (TWIerrorMsg == TWI_MRX_ADR_NACK) )
                        TWI_Start_Transceiver();        // Если НАСК, делаем рестарт, выставляем прерывания
                w++;
        }
}


Результирующие скорости для мучаемого мной ЕЕПРОМА:
Запись: 370 байт/сек
Чтение: 5.38 кбайт/сек

Эх, жаль, чтение до 8кб/сек не дотягивает, звук хотел там хранить.
Ща буду мучить блочную чтение/запись - надеюсь там скорости будут побольше. Напишу еще сюда.
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 09 июн 2011, 11:42

// Если получили отбой, делаем перезапуск
А если получили не отбой, то перезапуск не делаем....

Добавлено спустя 32 секунды:
Конечно побольше - раза в четыре!
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 10 июн 2011, 10:12

Сделал запись блоками. Пробовал по 2 байта, по 4, по 16.
Результат один - болт. Вместо данных записывается мусор(иногда частично совпадающий с тем что нужно).
Хотя по даташиту все делаю правильно. Разбираюсь...
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение dccharacter » 10 июн 2011, 11:14

А я вчера своим новеньким анализатором почитал/позаписывал счетчик из принтерного картриджа :-) Отличная вещь, если еще не фантазировать с заменой компонентов, то работы на два вечера, а счастье - бесценно!
Мой волшебник это я сам. Всю архитектуру программы придумал лично, а ребята помогли воплотить её. Я бы и сам мог написать, но лень учить язык и его конструкции.
Аватара пользователя
dccharacter
 
Сообщения: 4995
Зарегистрирован: 10 дек 2010, 13:16
Откуда: Красногорск МО
прог. языки: C, Python, wiring/processing
ФИО: Андрей

Re: Проблемы с аппаратным I2C на AVR ATmega32

Сообщение HarryStar » 18 июн 2011, 22:36

Продолжаю разбираться с протоколом I2C. Возникли еще вопросы...
Пробую с МК1 передать на МК2 определенную информацию. Есть проблемы.
Передаю посылку из 16-ти байт и пытаюсь на МК2 ее поймать. Лезет полная левизна, например некоторые байты могут повторяться или наоборот теряться.
Я правильно понял, что для гарантированной работы нужно делать как ЕЕПРОМ, т.е. от мастера принимать адрес регистра и его значение. Т.е. в моем случае адрес от 0 до 15 и байт-значение. Только в этом случае я избавлюсь от повторов?
Или нужно вводить контрольную сумму, подтверждение и перепосылку? Никак не добьюсь гарантированного результата между двумя МК. Хотя с ЕЕПРОМОМ и часами проблем нет.

Добавлено:

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

Результат: Для полной пересылки 16-ти байт нужно повторно перепосылать мастером всю серию около 30-40 раз. Что как то мне не нравится. Видимо проблема в реализации слейва, т.к. мастер с ЕЕПРОМом работает как часы.
Аватара пользователя
HarryStar
 
Сообщения: 995
Зарегистрирован: 15 ноя 2010, 13:56
Откуда: Нижний Новгород
прог. языки: С, С++, РНР

След.

Вернуться в Коммуникации

Кто сейчас на конференции

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