Научите писать программы для МК

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Аватара пользователя
К@Т
Говорящий с текстолитом
Сообщения: 1573
Зарегистрирован: Сб окт 09, 2010 12:33:48
Откуда: Незалежная

Re: Научите писать программы для МК

Сообщение К@Т »

Вот неплохие самоучители по пикам http://www.labkit.ru/html/Assembler_for_PIC....
...http://www.labkit.ru/html/C_for_PIC
ИМХО лучше учитса на пиках,т.к. их легче понять,да и фьюзофренией пики не болеют в отличие от авр.
У кошки 4 ноги и хвост-плюс,минус,вход,выход,а хвост-земля.....Надо переходить с китайской бурды на канифоль.......[url=http://infobar.hsdn.org/][img]http://static.hsdn.org/infobar/350x60.gif[/img][/url]
krant
Встал на лапы
Сообщения: 135
Зарегистрирован: Чт окт 25, 2007 11:00:10
Откуда: Russland

Re: Научите писать программы для МК

Сообщение krant »

Правильно, учится надо надо на пиках. Да и тем более Микрочип себя чуствует намного лучше нежели АВР.

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

Вот путь которы я Вам советую:
Шпак Ю.А.

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

Если Вы продолжите изучать пик контроллеры, то следующими книгами у вас должны быть:

Барри Брэй
Лусио Ди Джасио.

Лучше не пожалеть денег, а покупать книги в бумажном варианте, а не в электронном. Книги очень толковые, всё хорошо расписано (Правда по Брэю экран у меня работать не стал, но это единственный его косяк с которым я столкнулся).

Я советую кстати учиться не на живом общении, а на чтении исходников, это более действенный вариант :beer:
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

доброго времени суток многоуважаемые знатоки, для меня тоже очень актуальна данная тема т.к. сам толком не знаю с чего начать и как закончить написание проги............., есть опыт :)) при написании проги которая мигает светодиодами.........., пишу на Си в CodeVision..............., на данный момент поставил перед собой цель написать прогу для часов на atmega8............., в качестве индикаторов ИН-4.................., что должно составлять костяк проги, какова должна быть ее структура.........., подскажите плиз :))
Tell Me The Truth
Аватара пользователя
korsaj
Потрогал лапой паяльник
Сообщения: 330
Зарегистрирован: Чт июн 23, 2011 07:55:51

Re: Научите писать программы для МК

Сообщение korsaj »

Костяк программы - часы (прерывания от таймера). В прерываниях (допустим каждые 50мс) подсчитываешь время (вначале милисикунды, до переполнения, далее секунды, далее единицы минут, далее десятки минут, до 6, далее единицы часов, далее десятки часов до 2 ), опрашиваешь кнопки и сравниваешь текущее время с будильником. В основном цикле можно ничего не размещать, а можно разместить будильник и опрос кнопок (не забываем о дребезге контактов).

ЗЫ Удачи!
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

это единственно возможное решение данной задачи ?
Tell Me The Truth
BVS
Потрогал лапой паяльник
Сообщения: 336
Зарегистрирован: Пн май 23, 2011 18:27:41

Re: Научите писать программы для МК

Сообщение BVS »

FreshMan писал(а):это единственно возможное решение данной задачи ?

Ну, решений может быть столько, сколько людей будет это делать.
Но не надо сразу идти своим путем, возьмите чужой проект, можно и не один, разберитесь в нем попробуйте изменить его, не бойтесь экспериментов.
Пока у вас одни вопросы и нет действий, все ответы есть в Интернете (Книги, даташиты, статьи)
Аватара пользователя
mazda
Потрогал лапой паяльник
Сообщения: 382
Зарегистрирован: Сб фев 18, 2012 14:50:22

Re: Научите писать программы для МК

Сообщение mazda »

HeLiO писал(а):полнаый бред. только запуешься если начинать с ассемблера. НУжно начинать на СИ писать и потом использовать только си, а ассемблер начать гораздо позже, и то достаточно на уровне понимания кода, самому писать вряд ли понадобится что либо

ой неправ, как неправ. это от мозга конкретного человека зависит, мы с товарищами с асма начинали и с основных процессов внутри процессора и работали с эмуляторами где пишешь хекс код и видишь из какого регистра в какой что идёт, и потом си пристроить к этому делу легко.
тем более на асме можно делать то чего нельзя на си, компилятор некоторые вещи не оптимизирует, бывают довольно оригинальные вычисления вобщето.
Главное научиться читать и писать, а там приложиться
я его в гугл на дрц прогнал, вы знаете, пи-када нет.
Аватара пользователя
DX168B
Друг Кота
Сообщения: 4468
Зарегистрирован: Вс янв 24, 2010 19:19:52
Откуда: Главный Улей России (Moscow)
Контактная информация:

Re: Научите писать программы для МК

Сообщение DX168B »

А я ведь тоже с ассемблера начинал, когда АВРки изучал. Но на Си так и не перешёл. За то, когда перешёл на ядра Cortex M3 \ A8, так там сразу с Си начал, ибо ассемблер там жуткий.
I am DX168B and this is my favourite forum on internet!
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

написал свою первую прогудля часов, но она почему-то не работает..........., подскажите пожалуйста в чем может быть причина ? :dont_know:
в ней выводятся только секунды и минуты на индикаторы ИН-4, аноды этих индикаторов подключены к 4 старшим разрядам порта D, а катоди к 155ид1, информационные входы которой подключены к 4 младшим разрядам порта D.

Код: Выделить всё

#include <mega8.h>
#include <delay.h>

#define kn_sec_reset PINC.0  // на 0 пин порта С подключена кнопка сброса секунд
#define kn_min_up PINC.1    //  на 1 пин порта С подключена кнопка инкремента минут

unsigned char sec, min, x, i;
unsigned char number[4];               // в этом массиве мы будем хранить десятки и единици секунд и минут
unsigned char znak[4]={16,32,64,128};  // в этом массиве мы храним номер анода индикатора определенного разряда             



interrupt [TIM1_COMPA] void timer1_compa_isr(void)   // Timer1 срабатывает каждые 5мс
{
if (++x==200) {sec++; x=0;};
}

void output(unsigned char sec, unsigned char min)
{
number[0]=sec%10;  //в массив записываем остаток от деления преременной sec
number[1]=sec/10; // в массив записываем целую часть от деления преременной sec
number[2]=min%10; // в массив записываем остаток от деления преременной min
number[3]=min/10; // в массив записываем целую часть от деления преременной min

for (i=0;i!=4;i++)  // по очереди выводим на индикаторы выше вычесленные данные
 {
  PORTD=0;
  PORTD=(number[i]|znak[i]);
 };
 
}


void main(void)
{
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=P State0=P
PORTC=0x03;
DDRC=0x00;

// Port D initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTD=0x00;
DDRD=0xFF;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: Timer 0 Stopped
TCCR0=0x00;
TCNT0=0x00;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x02;
OCR1AL=0x71;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x10;

// Global enable interrupts
#asm("sei")

while (1)
      {
       
       output(min,sec);
       if (sec==60) {sec=0; min++;};
       if (min==60) min=0;
       if (kn_sec_reset==0) {delay_ms(10);sec=0; while(kn_sec_reset==0){}; delay_ms(10);};
       if (kn_min_up==0) {delay_ms(10);min++; while(kn_min_up==0){}; delay_ms(10);};

      };
}
Tell Me The Truth
SII
Вымогатель припоя
Сообщения: 635
Зарегистрирован: Пт янв 30, 2009 14:50:35
Откуда: Солнечногорск

Re: Научите писать программы для МК

Сообщение SII »

DX168B писал(а):А я ведь тоже с ассемблера начинал, когда АВРки изучал. Но на Си так и не перешёл. За то, когда перешёл на ядра Cortex M3 \ A8, так там сразу с Си начал, ибо ассемблер там жуткий.


Ну, насчёт армовского ассемблера Вы неправы: он много удобней и мощнее, чем у AVR.

Изучать лучше, ИМХО, сначала Паскаль/Дельфи на ПК (написание консольных приложений), и лишь затем начинать работать с МК. Главное в программировании -- алгоритмистика, а отнюдь не конкретный язык программирования, и настоящим программистом делает не знание одного или нескольких языков, а умение превращать задачу в чёткий алгоритм её решения. Ну а на при работе с МК знание ассемблера абсолютно обязательно: есть масса вещей, которые ни на каком языке высокого уровня не сделаешь вообще или же сделаешь, но неэффективно. И если на ПК от таких вещей программиста-прикладника спасает ОС (не приходится напрямую работать с железом, обрабатывать прерывания и т.п.), то на МК всё надо делать самому.
Galizin
Мучитель микросхем
Сообщения: 477
Зарегистрирован: Ср окт 15, 2008 09:33:03
Откуда: Воронеж

Re: Научите писать программы для МК

Сообщение Galizin »

FreshMan писал(а):

Код: Выделить всё

for (i=0;i!=4;i++)  // по очереди выводим на индикаторы выше вычесленные данные
 {
  PORTD=0;
  PORTD=(number[i]|znak[i]);
 };
}

В комментарии написано, что выводите по очереди, а на самом деле очень быстро меняете числа. Настолько быстро (несколько микросекунд), что индикатор не успевает отобразить ничего кроме одной цифры. На нее тратится гораздо больше времени, потому что программе нужно выйти из функции, попасть в главный цикл, а потом снова вернуться. Нужно каким то образом сделать так, что бы показания изменялись равномерно во времени и помедленнее, например раз в 5мс. Для этого можно например убрать цикл и менять состояние порта только один раз во время вызова функции с соотв наращиванием счетчика, а в функции делать смену только если будет выставлен флаг в прерывании. Или перенести вывод в прерывание.
Это можно сравнить с объявлениями на столбе. Если Вы хотите что бы увидели все 4 объявления, то должны будете их менять например с интервалов в один час. Если же Вы наклеете сразу все четыре друг на друга, то шанса увидеть нижние под последним нет.
Аватара пользователя
ds1307
Грызет канифоль
Сообщения: 260
Зарегистрирован: Чт янв 13, 2011 18:54:29
Откуда: Минск

Re: Научите писать программы для МК

Сообщение ds1307 »

FreshMan писал(а):
for (i=0;i!=4;i++) // по очереди выводим на индикаторы выше вычесленные данные

Мне кажется надо for (i=0;i<4;i++)
Более глубоко не вникал
Учиться, учиться, ...
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

вот что получилось опосля переделки:

Код: Выделить всё

#include <mega8.h>
#include <delay.h>

#define kn_sec_reset PINC.0  // на 0 пин порта С подключена кнопка сброса секунд
#define kn_min_up PINC.1    //  на 1 пин порта С подключена кнопка инкремента минут

unsigned int x;
unsigned char sec, min,i;
unsigned char number[4];               // в этом массиве мы будем хранить десятки и единици секунд и минут
unsigned char znak[4]={16,32,64,128};  // в этом массиве мы храним номер анода индикатора определенного разряда             



interrupt [TIM0_OVF] void timer0_ovf_isr(void)// Timer 0 срабатывает каждую мс
{
// Reinitialize Timer 0 value
TCNT0=0x83;
// Place your code here
if(++x==1000) {sec++; x=0;};
}


interrupt [TIM1_COMPA] void timer1_compa_isr(void)// Timer 1 срабатывает каждые 5мс
{
PORTD=0;
switch (i)
    {
    case 0:PORTD=number[i]|znak[i];break;
    case 1:PORTD=number[i]|znak[i];break;
    case 2:PORTD=number[i]|znak[i];break;
    case 3:PORTD=number[i]|znak[i];break;
    };
  if (++i==4)i=0;
}



void main(void)
{
// Declare your local variables here

// Input/Output Ports initialization
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;

// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=P State0=P
PORTC=0x03;
DDRC=0x00;

// Port D initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTD=0x00;
DDRD=0xFF;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
TCCR0=0x02;
TCNT0=0x83;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x02;
OCR1AL=0x71;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x11;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts
#asm("sei")

while (1)
      {
       number[0]=sec%10;  //в массив записываем остаток от деления преременной sec
       number[1]=sec/10; // в массив записываем целую часть от деления преременной sec
       number[2]=min%10; // в массив записываем остаток от деления преременной min
       number[3]=min/10; // в массив записываем целую часть от деления преременной min
       if (sec==60) {sec=0; min++;};
       if (min==60) min=0;
       if (kn_sec_reset==0) {delay_ms(10);sec=0; while(kn_sec_reset==0){}; delay_ms(10);};
       if (kn_min_up==0) {delay_ms(10);min++; while(kn_min_up==0){}; delay_ms(10);};

      };
}



какие будут предложения в плане модернизации и усовершенствования ? :tea:
Tell Me The Truth
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

немного модернезировал прогу но она перестала работать..............., вроде все так, но они стали идти ОЧЕНЬ медленно..........., подскажите, плиз, где я ошибся ? :dont_know:

Код: Выделить всё

#include <mega8.h>
#include <delay.h>

#define kn_sec_reset PINC.0  // на 0 пин порта С подключена кнопка сброса секунд
#define kn_min_up PINC.1    //  на 1 пин порта С подключена кнопка инкремента минут

#define anod_sec_l (PORTD|=(1<<4))
#define anod_sec_h (PORTD|=(1<<5))
#define anod_min_l (PORTD|=(1<<6))
#define anod_min_h (PORTD|=(1<<7))

unsigned int var;
unsigned char sec, min,i;
unsigned char number[4];               // в этом массиве мы будем хранить десятки и единици секунд и минут
//unsigned char znak[4]={16,32,64,128};  // в этом массиве мы храним номер анода индикатора определенного разряда             



interrupt [TIM0_OVF] void timer0_ovf_isr(void)// Timer 0 срабатывает каждую мс
{
// Reinitialize Timer 0 value
TCNT0=0x83;
// Place your code here
if(++var==1000) {sec++; var=0;};
}


interrupt [TIM1_COMPA] void timer1_compa_isr(void)// Timer 1 срабатывает каждые 5мс
{
PORTD=0;
switch (i)
    {
    case 0:PORTD=number[i]; anod_sec_l; break;
    case 1:PORTD=number[i]; anod_sec_h; break;
    case 2:PORTD=number[i]; anod_min_l; break;
    case 3:PORTD=number[i]; anod_min_h; break;
    };
  if (++i==4)i=0;
}


void mathematics(unsigned char sec, min)
{
number[0]=sec%10;  //в массив записываем остаток от деления преременной sec
number[1]=sec/10; // в массив записываем целую часть от деления преременной sec
number[2]=min%10; // в массив записываем остаток от деления преременной min
number[3]=min/10; // в массив записываем целую часть от деления преременной min
}


void main(void)
{
// Port C initialization
// Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State6=T State5=T State4=T State3=T State2=T State1=P State0=P
PORTC=0x03;
DDRC=0x00;

// Port D initialization
// Func7=Out Func6=Out Func5=Out Func4=Out Func3=Out Func2=Out Func1=Out Func0=Out
// State7=0 State6=0 State5=0 State4=0 State3=0 State2=0 State1=0 State0=0
PORTD=0x00;
DDRD=0xFF;

// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
TCCR0=0x02;
TCNT0=0x83;

// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: 125,000 kHz
// Mode: CTC top=OCR1A
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: On
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x0A;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x02;
OCR1AL=0x71;
OCR1BH=0x00;
OCR1BL=0x00;

// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;

// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
MCUCR=0x00;

// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x11;

// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;

// Global enable interrupts
#asm("sei")

while (1)
      {
       mathematics (min,sec);
       if (sec==60) {sec=0; min++;};
       if (min==60) min=0;
       if (kn_sec_reset==0) {delay_ms(10);sec=0; while(kn_sec_reset==0){}; delay_ms(10);};
       if (kn_min_up==0) {delay_ms(10);min++; while(kn_min_up==0){}; delay_ms(10);};
      };
}
Tell Me The Truth
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

неуж то никто не знает :)
Tell Me The Truth
phanis
Вымогатель припоя
Сообщения: 513
Зарегистрирован: Сб фев 19, 2011 18:04:08
Откуда: Татарстан, пос. Актюбинский
Контактная информация:

Re: Научите писать программы для МК

Сообщение phanis »

в место прерывания по переполнению

Код: Выделить всё

interrupt [TIM0_OVF] void timer0_ovf_isr(void)// Timer 0 срабатывает каждую мс
{
// Reinitialize Timer 0 value
TCNT0=0x83;
// Place your code here
if(++var==1000) {sec++; var=0;};
}

используйте прерывание по компаратору с автоматическим сбросом регистра TCNT0 бит WGM01

Код: Выделить всё

 interrupt [TIM0_COMPA] void timer1_compa_isr(void)   // Timer1 срабатывает каждые 5мс
{
if (++x==200) {sec++; x=0;};
}


Код: Выделить всё

  TCNT0  = 0;                               
 OCR0 = ваше значение
  TCCR0  = (0<< FOC0)|(0<<WGM00)|           // разрешить работу и прерывание  таймер0
           (0<<COM01)|(0<<COM00)|
           (1<<WGM01)|(1<< CS02)|
           (0<< CS01)|(1<< CS00);
Аватара пользователя
ploop
Модератор
Сообщения: 13490
Зарегистрирован: Ср ноя 26, 2008 16:34:25
Откуда: Тамбовская обл.

Re: Научите писать программы для МК

Сообщение ploop »

FreshMan, всё замечательно, но или ты вложишь тексты во вложения, или придётся их снести. Смотри сам.
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

ploop, оформить их отдельным файлом ?
Tell Me The Truth
Аватара пользователя
FreshMan
Друг Кота
Сообщения: 6296
Зарегистрирован: Пн ноя 22, 2010 00:57:15
Откуда: Ukraine

Re: Научите писать программы для МК

Сообщение FreshMan »

phanis писал(а):используйте прерывание по компаратору с автоматическим сбросом регистра TCNT0 бит WGM01

а почему нужно делать так ? из каких соображений ? :write:
Tell Me The Truth
phanis
Вымогатель припоя
Сообщения: 513
Зарегистрирован: Сб фев 19, 2011 18:04:08
Откуда: Татарстан, пос. Актюбинский
Контактная информация:

Re: Научите писать программы для МК

Сообщение phanis »

Так у вас будут четкие интервалы времени по сравнению с прерыванием по переполнению. Т.е сам интервал времени между прерываниями будет реализован на аппаратном уровне.
Посмотрите на полученный ассемблер из си кода. Или просто в протеусе по экспериментируйте.
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»