Модераторы: PILOT, ManiaK, Mazzi
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> RFID. Чтение PSK кода 
V
    Опции темы
supercelt
Дата 12.9.2015, 21:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 658
Регистрация: 25.8.2005

Репутация: нет
Всего: 1



В продолжении темы - обнаружение фазового сдвига.
Я почти написал код на си для мк atmega16. Устройство rfid читалка стандарта emmarine(em4100) (Все 5 видов модуляции)
В протеусе всё работает превосходно. За исключением одного НО.
Вкрадце если рассказать, то алгоритм такой.

user posted image

На вход мк (захват таймера1) поступает выделенная огибающая с приёмной катушки. 
Сначала пробуем распознать формат манчестер 64 периода на 1 бит, потом манчестер 32, далее бифаза 64, потом бифаза 32. И наконец PSK 16, который не работает.
В первых 4 вариантах я ловил прерывания,  отсчитывал временные интервалы и если они совпадали с условием, то я ставил флаг, который 
пропускается в основном цикле программы. Я старался в прерывании сделать как можно меньше действий, а саму обработку - в основном цикле.
Так вот, когда в блоке прерываний было ясно что принята либо 1 либо 0, далее проваливаемся в основной цикл программы, где идёт обработка. То есть
сначала считаем заголовок (9 единиц), потом когда пошли данные считаем CRC, потом высчитываем СRC. И если биты чётности совападают - идём дальше. А если нет, то программа задействует ф-ию another_format и пробует заново считать код уже в другом формате.
Фишка в том, что в первых 4 случаях 1 бит равнялся 64 или 32 периода таймера. И поэтому обработка в основном цикле спокойно успевала закончится.
А вот когда я пробую также сделать с PSK 16, то обработка просто не успевает пройти, потому как через 2 тика таймера уже следующее прерывание.
Для наглядности что такое PSK 16 (на вход мк поступает Modulator control)

user posted image

. Так вот подскажите пожалуйста. Сейчас я по мере поступления данных делаю обработку (вычисляю и сравниваю CRC). Может сделать так: Сначала просто и быстро набить массив data[64] данными (в стандарте em4100 - 64 бита данных), а потом уже спокойно расковыривать этот массив и вычислять CRC?
Хотя тогда замедлится принятие решения о том что это другой формат например...

Весь код на данный момент

Код

#define F_CPU 16000000

#include <avr/io.h>
#include <string.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include <stdio.h>
#include <util/delay.h>

volatile unsigned char edge_counter;
volatile unsigned char interrupt_counter;
unsigned char bit_counter;
unsigned char byte_counter;
unsigned char crc_counter;
unsigned char RFID_buffer[6];
volatile unsigned char edge_min_false = 62;
volatile unsigned char edge_min_true = 126;
volatile unsigned char edge_max_true = 126;
volatile unsigned char system;
//system//
//bit 3 - 0=заход в прерывание 1-й раз 1=заход в прерывание последующие разы
//bit 1 - если 0, то принимаем заголовок, если 1, то заголовок приняли и далее идут данные
//bit 0 - 1-разрешаем заход в основной цикл программы для обработки
//bit 2 - установка этого бита означает, что приняты все биты всключая стоповый и далее следует сравнить чётность столбов и принять решение о распозновании метки.
//bit 4 - бит для проверки чётности CRC. 1 - нечётно, 0 - чётно.
//bit 5 - бит распознования метки. Если 1 - метка распознана. Если 0 - нет. Распознование - сравнение CRC по столбам.
//bit 6 - Данные
volatile unsigned char format = 0x1;
//format//
//bit 0 - EM4100A6
//bit 1 - EM4100A5
//bit 2 - EM4100B6
//bit 3 - EM4100B5
//bit 4 - EM4100C4

//Macros######################################################################
#define SetBit(reg, bit)        reg |= (1 << bit)
#define ClearBit(reg, bit)        reg &= ~(1 << bit)
#define BitIsSet(reg, bit)        ((reg & (1 << bit)) != 0)
#define BitIsClear(reg, bit)    ((reg & (1 << bit)) == 0)
#define ShiftBitRight(reg)        reg >>= 1
#define CalculateCRC(reg, bit)    reg ^= (1 << bit)
//############################################################################

//Functions###################################################################
void USART_SendChar(unsigned char sym){
    while(!(UCSRA & (1 << UDRE)));
    UDR = sym;
}

void Another_Format(){
    if(TCCR1B == 0xC3){TCCR1B = 0x83;}//Если последний принятый бит перед вкидыванием в anotherformat был 0, то надо перенастроить прерывание сначала по спаду, потом по росту. иначе не будет работать
    interrupt_counter = 0;
    bit_counter = 0;
    byte_counter = 0;
    crc_counter = 0;
    RFID_buffer[6] = 0;
    system = 0;
    edge_counter = 0;
    format <<= 1;
    if(format == 0x2){
        edge_min_false = 30;
        edge_min_true = 62;
    }
    if(format == 0x4){
        edge_min_true = 62;
        edge_max_true = 126;
    }
    if(format == 0x8){
        edge_min_true = 30;
        edge_max_true = 62;
    }
    if(format == 0x20){
        format = 0x1;
        edge_min_false = 62;
        edge_min_true = 126;
    }
    //-------------test in proteus-------------
    PORTD ^= (1 << 5);
    TCCR2 &= ~(1 << COM20);
    _delay_ms(1);
    TCCR2 |= (1 << COM20);
    if(format == 0x2 || format == 0x1){TCCR1B = 0xC3;} //По наростающему фронту.
    if(format == 0x4 || format == 0x8 || format == 0x10){TCCR1B = 0x83;} //По спадающему фронту.    
    //-----------------------------------------
}
//###########################################################################

ISR(TIMER1_CAPT_vect){
    if((format == 0x1) || (format == 0x2)){
        if(BitIsClear(system, 3)){//заходим в прерывание первый раз, следующий уже не заходим сюда
            PORTC |= (1 << 0);////////////////
            asm("nop");///////////////////////
            PORTC &= ~(1 << 0);///////////////
            TCNT1 = 0x00;
            TIFR |= 0x20;
            if(TCCR1B == 0xC3){//Если прерывание настроено на активный фронт
                TCCR1B = 0x83;//то перенастраиваем его на активный спад, т.к. в следующий раз будет полюбасу спад
                SetBit(system, 0);
                SetBit(system, 6);
                }else{
                TCCR1B = 0xC3;
                SetBit(system, 0);
                ClearBit(system, 6);
            }
            SetBit(system, 3);
        }else{    
            edge_counter = ICR1;//записываем в переменную сколько оттикал таймер.
            if(edge_counter >= edge_min_false && edge_counter <= (edge_min_false + 4)){//если таймер оттикал 62-66 тиков, то это на границе битов и в расчёт не берётся
                //TIFR |= 0x20;//принудительно сбрасываем флаг прерываний
                if(TCCR1B == 0xC3){TCCR1B = 0x83;}else{TCCR1B = 0xC3;}//меняем условие прерывания для следующего фронта
                }else if(edge_counter >= edge_min_true && edge_counter <= (edge_min_true + 4)){//если таймер оттикал 126-130 тиков, то это середина бита. Это и есть нужный фронт
                TCNT1 = 0x00;//Обнуляем счётчик
                TIFR |= 0x20;
                if(TCCR1B == 0xC3){//если прерывание было настроено по активному фронту, то
                    TCCR1B = 0x83;
                    SetBit(system, 0);
                    SetBit(system, 6); //приняли 1
                    }else{
                    TCCR1B = 0xC3;
                    SetBit(system, 0);
                    ClearBit(system, 6); //приняли 0
                }
                PORTC |= (1 << 0);////////////////
                asm("nop");///////////////////////
                PORTC &= ~(1 << 0);///////////////
            }
        }    
    }
    if(format == 0x4 || format == 0x8){ //Если формат biphase 64 или 32
        if(BitIsClear(system, 3)){ //Если 0 то это первый заход в прерывание. Тут данные не собираем, а начинаем отсчёт
            TCNT1 = 0x00;
            TIFR |= 0x20;
            if(TCCR1B == 0x83){
                TCCR1B = 0xC3;
            }else{
                TCCR1B = 0x83;
            }
            SetBit(system, 3);
        }else{ //Последующие заходы в прерывание
            edge_counter = ICR1;//записываем в переменную сколько оттикал таймер.
            TCNT1 = 0x00;
            TIFR = 0x20;
            if(edge_counter >= edge_max_true && edge_counter <= (edge_max_true + 4)){//Если длина бита = 64 цикла. прерывание сработало вконце бита(количество тиков между прерываниями) то это 1
                if(TCCR1B == 0xC3){TCCR1B = 0x83;}else{TCCR1B = 0xC3;}
                SetBit(system, 0);
                SetBit(system, 6); //приняли 1
                PORTC |= (1 << 0);////////////////
                asm("nop");///////////////////////
                PORTC &= ~(1 << 0);///////////////
            }else if(edge_counter >= (edge_min_true) && edge_counter <= (edge_min_true + 4)){//Если в середине бита сработало прерывание, то это 0 и следующее прерывание игнорим
                if(TCCR1B == 0xC3){TCCR1B = 0x83;}else{TCCR1B = 0xC3;}
                SetBit(system, 0);
                ClearBit(system, 6); //приняли 0
                ClearBit(system, 3);//Игнорим следующее прерывание
                PORTC |= (1 << 0);////////////////
                asm("nop");///////////////////////
                PORTC &= ~(1 << 0);///////////////
            }
        }
    }
    if(format == 0x10){//Если формат PSK 16
        if(BitIsClear(system, 3)){ //Если 0 то это первый заход в прерывание. Тут данные не собираем, а начинаем отсчёт
            TCNT1 = 0x00;
            TIFR |= 0x20;
            if(TCCR1B == 0x83){TCCR1B = 0xC3;}else{TCCR1B = 0x83;}
            SetBit(system, 3);
            SetBit(system, 0);
            SetBit(system, 6);
        }else{    
            TCNT1 = 0x00;
            TIFR = 0x20;
            if(TCCR1B == 0x83){TCCR1B = 0xC3;}else{TCCR1B = 0x83;}
            if(interrupt_counter == 16){
                edge_counter = ICR1;//записываем в переменную сколько оттикал таймер.
                if(edge_counter <= 3){
                    SetBit(system, 0);
                    SetBit(system, 6);
                }else if(edge_counter > 3){
                    SetBit(system, 0);
                    ClearBit(system, 6);
                }
            }
        }
        interrupt_counter++;
    }
}

int main(void){
    SetBit(DDRC, 0);////////////////////////////
    ClearBit(PORTC, 0);/////////////////////////
    SetBit(DDRD, 5);////////////////////////////
    ClearBit(PORTD, 5);/////////////////////////
    
    SetBit(DDRD, 7);
    ClearBit(DDRD, 6);
    SetBit(PORTD, 6);
    
    OCR2 = 63;
    TCCR2 = 0x19;
    TCCR1A = 0x00;
    TCCR1B = 0xC3;
    TCNT1 = 0x00;
    ICR1 = 0x00;
    TIMSK = 0x20;
    
    UCSRA = 0x00;
    UCSRB = 0x18;
    UCSRC = 0x86;
    UBRRH = 0x00;
    UBRRL = 0x8;
    
    asm("sei");
    while(1){
        if(BitIsSet(system, 0)){
            if(BitIsSet(system, 1)){ //Приём данных
                if(BitIsSet(system, 6)){ //Если приняли 1 (Когда на порту ноль, значит идёт приём лог 1)
                    USART_SendChar('1');
                    if(byte_counter == 5){ //Если принимаем 5-й байт - это уже не данные, а биты чётности по столбам
                        if(bit_counter == 4){ //Принимаем последний стоповый бит //4
                            Another_Format(); //Стоповый 64 бит не ноль ошибка
                            }else{
                            SetBit(RFID_buffer[byte_counter], 4); //Набиваем регистр битов чётности по столбам
                            ShiftBitRight(RFID_buffer[byte_counter]); //Сдвигаем вправо
                            bit_counter++;
                        }
                        }else{
                        if(crc_counter == 4){ //Если принимаем бит чётности
                            USART_SendChar('S');
                            if(BitIsClear(system, 4)){ //Если бит чётности - 0
                                Another_Format(); //Ошибка, бит должен быть 1
                            }
                            crc_counter = 0;
                            ClearBit(system, 4); //Обнуляем бит чётности в регистре, что бы проверить следующие 4 бита
                            if(bit_counter == 8){ //Если приняли 8 бит, значит это 1 байт
                                bit_counter = 0;
                                byte_counter++; // Увеличиваем счётчик принятых байтов
                            }
                            }else{
                            SetBit(RFID_buffer[byte_counter], 7); //Набиваем регистр
                            if(bit_counter < 7){ShiftBitRight(RFID_buffer[byte_counter]);} //Сдвигаем вправо, если бит не последний
                            CalculateCRC(system, 4); //Расчёт чётности. Просто делаем исключающее или 4 бита регистра system и лог единицы. Получится в цикле если будет 1,3 единицы, бит будет - 1, если 2, 4 - бит будет 0
                            bit_counter++;
                            crc_counter++;
                        }
                    }
                    }else{ //Если приняли 0 (Когда напорту один, значит идёт приём лог. 0)
                        USART_SendChar('0');
                        if(byte_counter == 5){
                            if(bit_counter == 4){ //Принимаем последний стоповый бит //4
                                SetBit(system, 2); //Так как приняли 0, то это правильный стоп-бит и устанавливаем флаг, что бы разрешить далее проверку чётности столбов
                            }else{
                                ShiftBitRight(RFID_buffer[byte_counter]); // Просто сдвигаем регистр, так как 0 в этом случае запишется автоматически
                                bit_counter++;
                            }
                        }else{
                            if(crc_counter == 4){ //Если принимаем бит чётности
                                USART_SendChar('S');
                                if(BitIsSet(system, 4)){ //Если бит чётности - 1
                                    Another_Format(); //Ошибка, бит должен быть 0
                                }
                                crc_counter = 0;
                                ClearBit(system, 4); //Обнуляем бит чётности в регистре, что бы проверить следующие 4 бита
                                if(bit_counter == 8){
                                    bit_counter = 0;
                                    byte_counter++;
                                }
                            }else{
                                ClearBit(RFID_buffer[byte_counter], 7); //Набиваем ноль, так как при 7 бите сдвигать не будем. А если не сдвинуть, 0 не запишется
                                if(bit_counter < 7){ShiftBitRight(RFID_buffer[byte_counter]);}
                                bit_counter++;
                                crc_counter++;
                            }
                        }
                    }
            }else{ //Приём заголовка
                if(BitIsSet(system, 6)){
                    USART_SendChar('1');
                    bit_counter++;
                    }else{
                    Another_Format();
                }
                if(bit_counter == 9){
                    SetBit(system, 1);
                    bit_counter = 0;
                }
            }
            ClearBit(system, 0);
        }
        if(BitIsSet(system, 2)){//вычисление чётности столбов
            for(bit_counter = 0; bit_counter < 4; bit_counter++){ //Прогон для битов
                for(byte_counter = 0; byte_counter < 5; byte_counter++){ //Прогон для байтов
                    if(BitIsSet(RFID_buffer[byte_counter], bit_counter)){
                        CalculateCRC(system, 4);
                    }
                    if(BitIsSet(RFID_buffer[byte_counter], (bit_counter + 4))){
                        CalculateCRC(system, 4);
                    }
                }
                if(BitIsSet(system, 4)){
                    if(!(RFID_buffer[5] & (1 << bit_counter))){
                        Another_Format();
                        break;
                        }else{
                        SetBit(system, 5);
                    }
                    }else{
                    if(RFID_buffer[5] & (1 << bit_counter)){
                        Another_Format();
                        break;
                        }else{
                        SetBit(system, 5);
                    }
                }
                ClearBit(system, 4);
            }
            if(BitIsSet(system, 5)){
                char id[1];
                sprintf(id, "%hhX", RFID_buffer[0]);
                USART_SendChar('[');
                for(unsigned char ii = 0; ii < strlen(id); ii++){
                    USART_SendChar(id[ii]);
                }
                USART_SendChar(']');
                id[1] = 0;
                sprintf(id, "%hhX%hhX%hhX%hhX", RFID_buffer[1], RFID_buffer[2], RFID_buffer[3], RFID_buffer[4]);
                for(unsigned char ii = 0; ii < strlen(id); ii++){
                    USART_SendChar(id[ii]);
                }
                ClearBit(system, 2);
                break;
            }
        }
    }
}

PM   Вверх
bass
Дата 15.9.2015, 02:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 5.8.2007

Репутация: нет
Всего: 2



Ну не может того быть, что мега не справиться с сигналом PSK . Частота тактирования 125кГц. 250кГц Если использовать полупериуд. У вас на сколько помню кварц 16мГц. 56 тактиков не так уж и много, но и не так мало. По корректней подойти к программе.
PM MAIL   Вверх
supercelt
Дата 15.9.2015, 08:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 658
Регистрация: 25.8.2005

Репутация: нет
Всего: 1



Куда уж корректней). Итак всё ужал)
PM   Вверх
xvr
Дата 15.9.2015, 12:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



В программе как минимум 2 ошибки:
Код

unsigned char RFID_buffer[6];

...
void Another_Format(){
...
    RFID_buffer[6] = 0; // Выход за границу RFID_buffer
...
}


Код


                char id[1];
...
                sprintf(id, "%hhX", RFID_buffer[0]); // Выход за границу id
...
                id[1] = 0; // Выход за границу id
                sprintf(id, "%hhX%hhX%hhX%hhX", RFID_buffer[1], RFID_buffer[2], RFID_buffer[3], RFID_buffer[4]); // Выход за границу id



Есть некоторое количество неоптимального кода -
  •  ShiftBitRight(RFID_buffer[byte_counter]); - сдвиг в массиве по индексу лучше заменить на сдвиг в отдельной переменной и перекладыванием конечного результата в массив
  •  if(TCCR1B == 0xC3){TCCR1B = 0x83;}else{TCCR1B = 0xC3;} заменить на TCCR1B ^= 0x83^0xC3;
  •  if((format == 0x1) || (format == 0x2)) заменить на if(format & 0x3) (в других местах аналогично)
  •  Есть дубликаты кода и взаимоисключающие if / else if конструкции

Использование USART_SendChar внутри обработки бита (даже если это и не в прерывании) черевато пропуском битов.

И вообще - в прерывании надо делать всю критичную по времени работу. Включая сборку битов в байты (заполнение RFID_buffer) в вашем случае. Цикл в main должен обрабатывать уже готовый накопленный буфер. Кстати, их следует сделать 2 штуки - один накапливается в прерывании, второй обрабатывается в main (потом они меняются местами).

PM MAIL   Вверх
supercelt
Дата 15.9.2015, 21:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 658
Регистрация: 25.8.2005

Репутация: нет
Всего: 1



Спасибо большое! Буду делать ) Не могли бы вы ещё подробнее рассказать про выход за границу id

Это сообщение отредактировал(а) supercelt - 15.9.2015, 21:03
PM   Вверх
bass
Дата 15.9.2015, 21:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 5.8.2007

Репутация: нет
Всего: 2



Цитата(supercelt @ 15.9.2015,  21:02)
Спасибо большое! Буду делать ) Не могли бы вы ещё подробнее рассказать про выход за границу id

char id[1];
id[1] = 0; // Выход за границу id

вот он выход.
Как вам объяснить.....
char id[1]; //объявление буфера размером в свойство переменной(1 байт свойство char).
id[0] = 0; //Здесь мы обратимся к буферу
id[1] = 0; //Здесь мы вылезем за границу буфера. 

Если объявили так бы:

char id1[1];
char id2[1];
id1[0] = 1; 
id1[1] = 2; // Выход за границу id указывает на адрес следующий после буфера. В данной ситуации id2[0].
 
Буфер id2[0] будет равен 2-ум....

Если не понятно то спросите. )))) 











Это сообщение отредактировал(а) bass - 15.9.2015, 21:15
PM MAIL   Вверх
supercelt
Дата 15.9.2015, 22:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 658
Регистрация: 25.8.2005

Репутация: нет
Всего: 1



А ну понятно. Я спутал размерность массива и элемент массива. То есть если я хочу объявить массив, размером 1 элемент, то 
char id[1];
А если я хочу его обнулить, то id[0] - 0; ??
PM   Вверх
bass
Дата 15.9.2015, 23:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 5.8.2007

Репутация: нет
Всего: 2



Цитата(supercelt @ 15.9.2015,  22:07)
А ну понятно. Я спутал размерность массива и элемент массива. То есть если я хочу объявить массив, размером 1 элемент, то 
char id[1];
А если я хочу его обнулить, то id[0] - 0; ??

Да все верно по поводу буфера. А по поводу кода действительно xvr прав . Отправка по Usart подразумевает помещение байта в передатчик, сама операция помещения занимает несколько команд процессора. Но есть одно НО !!!!! Ваша функция отправки ожидает пока буфер передатчика не освободиться
Код

while(!(UCSRA & (1 << UDRE)));

А это примерно время отправки байта по USART время зависит от скорости.

В одной программе делал кольцевой буфер, а по таймеру проверял свободен ли передатчик далее смотрел если что то в кольцевом буфере. Ну и соответственно если передатчик свободен и есть данные в кольцевом буфере то отправлял. Плюс использовал символы разделения пакетов /r/n . 


 

PM MAIL   Вверх
xvr
Дата 16.9.2015, 15:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 8
Всего: 223



Цитата(supercelt @  15.9.2015,  22:07 Найти цитируемый пост)
А если я хочу его обнулить, то id[0] - 0; ?? 

Если он размером в 1 элемент, то да. Если больше - то поэлементно присвоить во все позиции нули (ну или memset позвать)

PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Микроконтроллеры (MCU) и микропроцессоры (MPU)"
PILOT ManiaK
UniBomb Mazzi

На данный раздел помимо Правил форума распространяются текже следующие правила:


  • Прежде чем создать тему воспользуйтесь поиском или посмотрите в faq. Возможно на форуме уже есть ответ на ваш или близкий к вашему вопрос.
  • В заголовке темы в квадратных скобках обозначьте используемое семейство микроконтроллера: [avr],[pic],[arm].
  • При создании темы с вопросом указывайте участок кода с ошибкой, версию компилятора, схемы подключения, fuse биты и прочие данные, которые помогут найти правильный ответ. Для форматирования текста программ используйте кнопку код.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Для флуда, просьб выполнить задание, поиска партнёров или исполнителей существуют свои разделы.
  • Если вы заметили несовместимое с правилами сообщение, то можете уведомить об этом модератора раздела нажав кнопку Репорт у соответствующего сообщения.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, PILOT, ManiaK, UniBomb, Mazzi.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Микроконтроллеры (MCU) и микропроцессоры (MPU) | Следующая тема »


 




[ Время генерации скрипта: 0.1628 ]   [ Использовано запросов: 20 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.