Передача порта avr как параметра в функцию
Перейти к содержимому

Передача порта avr как параметра в функцию

  • автор:

Ненастоящая работа с портом через указатель

Любое устройство на микроконтроллере AVR использует порты ввода вывода. Для работы с портами у AVR`ок есть три регистра: PORTx, PINx и DDRx, где x — буква порта, например A, B, C и т.д.
Регистр DDRx — определяет направление выводов микроконтроллера, PINx позволяет читать их состояние, «осязать» внешний мир, а PORTx в зависимости от направления вывода или задает его логический уровень, или подключает подтягивающий резистор.
Выводы микроконтроллера в проекте обычно задают с помощью макроопределений — define`ов. Мы получаем некую «отвязку» от железа и в дальнейшем это позволяет нам переназначать выводы на другие порты. Например, это может выглядеть так.


#define BUT_PIN 3
#define BUT_PORTX PORTB
#
define BUT_DDRX DDRB
#define BUT_PINX PINB

Неудобство такого подхода состоит в том, что для каждого вывода нужно определять три регистра. Бывает, что два (только PORTx и DDRx), но это тоже неудобно, если выводов много. Существует другой подход, позволяющий сократить число макроопределений. Разберемся в чем он заключается.

В микроконтроллер atmega16 регистр PORTB имеет адрес 0x18, а регистры DDRB и PINB — 0x17 и 0x16 соответственно. Тоже самое и с регистрами остальных портов, они тоже расположены друг за другом. Мы можем определить в проекте только один регистр, а к остальным обращаться вычисляя их адрес. За основу можно взять любой из них, главное ничего не напутать. Лучше всего для этих целей использовать макросы. Если отталкиваться от регистров PORTx, то макросы будут выглядеть так.


//это макросы для доступа к регистрам порта
#define PortReg(port) (*(port))
#define DirReg(port) (*((port) - 1))
#define PinReg(port) (*((port) - 2))

Макросы принимают в качестве параметра адрес регистра PORTx. Для взятия адреса регистра используется оператор &. Посмотрим, как можно использовать эти макросы.


//определили вывод мк
#define BUT_PIN 3
#define BUT_PORT PORTB
.
// конфигурируем вывод как вход
DirReg(&BUT_PORT) &= ~(1<//включаем подтягивающий резистор
PortReg(&BUT_PORT) |= (1<
.
//проверяем нажата ли кнопка
if(! (PinReg(&BUT_PORT)&(1 <.
>

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

Компилятор преобразует эти макросы в очень компактный код. Точно такой же, как если бы мы обращались к регистрам используя их имена. И дело в том, что с точки зрения ассемблера это так и есть. Если вы посмотрите ассемблерный код этих примеров, то увидите, что обращение к регистрам осуществляется методом прямой адресации с помощью команд IN, OUT. Поэтому я и озаглавил этот раздел «ненастоящая работа с портом через указатели». Указатели вроде как используются, но на самом деле нет.
Такой подход можно использовать не со всеми микроконтроллерами AVR, потому что в некоторых моделях регистры порта располагаются не по соседним адресам. Как, например, регистры порта F в микроконтроллере ATmega128.

Настоящая работа с портом через указатель

Иногда приходится прибегать к работе с портом, используя настоящий указатель. Для этого создается переменная указатель, которая инициализируется адресом какого-нибудь регистра порта. Делается это следующим образом.


//объявляем указатель на регистр
//обязательно должно присутствовать volatile
volatile uint8_t *portReg;

//инициализация
//передаем адрес регистра PORTB
portReg = &PORTB;

//вывод в порт через указатель
//перед указателем ставиться оператор *
(*portReg) = 0xff;

Также этот указатель можно передавать в функцию.


void OutPort(volatile uint8_t *pReg, uint8_t data)
*pReg = data;
>

.
//записываем в PORTB число 0xff
OutPort(&PORTB, 0xff);

//а здесь уже не нужен оператор &
//так как мы передаем переменную с адресом порта
OutPort(portReg, 0xff);

Работа с портом через указатель открывает большие возможности. Например, мы можем определить структуру, которая будет хранить все настройки пина микроконтроллера и обращаться к выводу, используя эту структуру. Или можем определить структуру виртуального порта содержащую выводы микроконтроллера из разных физических портов.
Все это так, но есть ложка дегтя. Работа с регистрами порта через указатель «тяжеловесна» с точки зрения размера кода и его быстродействия. Чтобы в этом убедиться, достаточно взглянуть на получаемый ассемблерный код. Если эти два фактора не критичны, то такой подход можно использовать, если нет, то придется работать по старинке.
Также п ри работе с портом через указатели, даже операция установки/сброса разряда будет неатомарна. Атомарность операций в этом случае нужно обеспечивать самостоятельно.

Вот небольшой пример, как можно использовать указатели при работе с портом.


//струтура для хранения настроек вывода - номера и порта
typedef struct outputs uint8_t pin;
volatile uint8_t *portReg;
>outputs_t;


//функция инициализации
void OUT_Init(outputs_t *out, uint8_t pin, volatile uint8_t *port, uint8_t level)
//сохраняем настройки в структуру
out->pin = pin;
out->portReg = port;

//конфигурируем вывод на выход
(*(port-1)) |= (1<
//задаем логический уровень
if (level) (*port) |= (1 <>
else (*port) &= ~(1 <>
>

//установить на выходе 1
void OUT_Set(outputs_t *out)
(*(out->portReg)) |= (1 pin);
>

//установить на выходе 0
void OUT_Clear(outputs_t *out)
(*(out->portReg)) &= ~(1 pin);
>

//определили вывод мк
#define OUT1_PIN 4
#define OUT1_PORT PORTB
.
//объявляем переменную для хранения
//настроек пина
outputs_t out1;

//инициализируем ее
OUT_Init(&out1, OUT1_PIN, OUT1_PORT, 0);

//устанавливаем 1 на выводе OUT1_PIN
OUT_Set(&out1);

Еще один пример работы с портом через указатели есть в коде к статье » сенсорная кнопка на микроконтроллере «.

Устройство и работа портов ввода-вывода микроконтроллеров AVR. Часть 2

Изучив данный материал, в котором все очень детально и подробно описано с большим количеством примеров, вы сможете легко овладеть и программировать порты ввода/вывода микроконтроллеров AVR.

  • Часть 1. Работа портов ввода/вывода
  • Часть 2. Подключение светодиода к линии порта ввода/вывода
  • Часть 3. Подключение транзистора к линии порта ввода/вывода
  • Часть 4. Подключение кнопки к линии порта ввода/вывода

Программу писать будем в Atmel Studio 6.0.

Эмулировать схему будем в Proteus 7 Professional.

Первым примером в изучении микроконтроллеров является подключение и управление светодиодом, это самый простой и наглядный пример. Этот пример стал классическим при изучении микроконтроллеров, как программа «Hello World!» при изучении прочих языков программирования.
Максимальный ток, который способен пропустить каждый порт ввода/вывода составляет 40 mA.
Максимальный ток, который способна пропускать каждая линия порта ввода/вывода составляет 20 mA.
Прежде чем подключать нагрузку, в том числе и светодиод к линиям порта ввода/вывода нужно знать, что можно спалить микроконтроллер превысив допустимую нагрузку на линию порта ввода/вывода.
Что бы ограничить ток, который протекает через линии порта ввода/вывода микроконтроллера нужно рассчитать и подключить резистор.

Рис: Рапиновка светодиода.

Рис: Подключение светодиода анодом к микроконтроллеру.

Рис: Подключение светодиода катодом к микроконтроллеру.

Сопротивление токоограничивающего резистора подключаемого к линиям портов ввода/вывода при подключении светодиода рассчитывается по формуле:

где:
Vs — напряжение источника питания;
Vsp — падение напряжения на линии порта ввода/вывода;
Vd — прямое падения напряжения на светодиоде;
Id — прямой ток на светодиоде;
Кn — коэффициент надежности роботы светодиода;

Пример:
— напряжение источника питания – ;
— прямое падения напряжения на светодиоде – (Берётся с datasheet на светодиод);
— прямой ток на светодиоде – 10мА (Берётся с datasheet на светодиод);
— коэффициент надежности роботы светодиода – 75% (Берётся с datasheet на светодиод);
— падение напряжения на линии порта ввода/вывода – 0,5В (Берётся с datasheet на микроконтроллер: Vol(output low voltage) – если ток втекает, и Voh (output high voltage) – если ток вытекает);

Таким образом номинал резистора R = 166,66 Om, подбирается ближайшее большее значение сопротивления.

Если не известно прямое напряжение светодиода, сопротивление можно рассчитать по закону Ома.

где:
U — напряжение, приложенное к участку цепи;
I — номинальный ток линии порта ввода/вывода.

Пример:
— напряжение, приложенное к участку цепи – 4,5В;
— номинальный ток линии порта ввода/вывода – 20мА.

Определив номинал резистора R, необходимо рассчитать мощность P, измеряемая в ваттах, которая будет выделяться в резисторе, в виде тепла при протекании тока в цепи.

где:
U – напряжение, приложенное к участку цепи;
I — номинальный ток линии порта ввода/вывода.

Пример:
— напряжение, приложенное к участку цепи – 4,5В;
— прямой ток на светодиоде – 20мА.

Рассчитав выделяемую мощность на резисторе, выбираем ближайшее большее значение мощности резистора. Если рассеиваемой мощности резистора будет недостаточной, то он может выйти из строя.

— подключения маломощного светодиода анодом к линии порта ввода/вывода:

// Подключаем внешние библиотеки #include #include // Основная программа int main(void) < // Настраиваем порты ввода/вывода DDRC = 0b11111111; //Настраиваем все разрады порта С на режим "Выход" PORTC = 0b11111111; //Устанавливаем все разряды порта C в лог.«1» (Навыходе порта напряжение равное Vcc) // Вечный цикл while (1) < >> 

— подключения маломощного светодиода катодом к линии порта ввода/вывода:

// Подключаем внешние библиотеки #include #include // Основная программа int main(void) < // Настраиваем порты ввода/вывода DDRC = 0b11111111; //Настраиваем все разряды порта С на режим "Выход" PORTC = 0b00000000; //Устанавливаем все разряды порта C в лог.«0» (На выходе порта напряжение равное GND) // Вечный цикл while (1) < >> 

— подключения маломощного светодиода анодом и катодом к линии порта ввода/вывода:

// Подключаем внешние библиотеки #include #include // Основная программа int main(void) < // Настраиваем порты ввода/вывода DDRD = 0b11111111; //Настраиваем все разряды порта D на режим "Выход" PORTD = 0b11111111; //Устанавливаем все разряды порта D в лог.«1» (На выходе порта напряжение равное Vcc) DDRC = 0b11111111; //Настраиваем все разряды порта C на режим "Выход" PORTC = 0b00000000; //Устанавливаем все разряды порта C в лог.«0» (На выходе порта напряжение равное GND) // Вечный цикл while (1) < >> 
  • программирование
  • программирование микроконтроллеров
  • программирование avr
  • микроконтроллеры avr
  • подключение кнопки к avr
  • подключение светодиода к avr

Микроконтроллеры AVR

Можно ли в IAR для AVR передать в качестве параметра адрес порта ввода-вывода? Сразу предупреждаю – задача не выдуманная, но описание сильно упрощено. Просьба не предлагать менять схему или сделать принципиально иначе. Хочется именно так. Просьба также не привязываться к терминологии. Итак. Есть интерфейсная микросхема, подключаемая к ножкам порта(ов) ввода-вывода. И есть функция, реализующая обмен с этой микросхемой, для чего дергающая программно ножками порта(ов). Для того, чтобы было просто менять ножки, к которым подключена микросхема, нужно обозвать их мнемониками через #define, и при необходимости переобозвать. Проблема появляется, если таких микросхем больше одной, и каждая из них подключена к своему набору ножек МК. Как изменить функцию так, чтобы можно было одной функцией связываться (последовательно, не одновременно) с несколькими микросхемами? То есть функция должна принимать в качестве параметра адрес порта (собственно, проблема) и номер бита (это просто). Для 24-х ПИКов такое написалось очень просто – там вполне понятная адресация, и с портом можно прекрасно работать через указатели. А вот с AVR…

    • Что-то опять не выходит. Подробнее.-=John=- (673 знак., 16.11.2008 16:30 )
      • Снимаю вопрос. Все работает вот так.-=John=- (237 знак., 16.11.2008 16:38 )
        • «AD5300_Write ( &PORTC, 2, 128 );» Что говорить, очень информативно получилось 🙂 — Vladimir Ljaschko (17.11.2008 14:44 )
          • Да это просто пример — к реальной программе не имеет вообще никакого отношения. — -=John=- (17.11.2008 16:15 )
          • Я что-то такое писал — не жреть. Порт — он sfr, написать-=John=- (67 знак., 11.11.2008 19:05 )
            • Что-то вы меня запутали. Не поленился, расчехлил IAR:Сергей Борщ (1797 знак., 11.11.2008 23:12 )
            • ну пусть будет void func (uint8_t volatile __io *port, uint8_t mask). А В пиках с понятной адресацией — как? — Сергей Борщ (11.11.2008 22:01 )
              • 4.20A не хочет — ругаетсяVit (103 знак., 11.11.2008 22:08 )
                • Хм, а привести __io к простому указателю он дает? Ведь все порты отображаются в АП данных, тока адреса на 0x20 больше. — she (11.11.2008 22:14 )
                  • Ну дык даёт. Мне пофиг 0x20 там или сколько — компилер это умеет разгребать — нафига ему это ещё раз объяснять? — Vit (11.11.2008 22:36 , ссылка)
                    • Сам численное значение модифицирует? Ух ты. Умный, зараза. — she (11.11.2008 22:43 )
                    • тут ничего не напутано? Какой смысл в приведении типа внутри функции? Скорее оно нужно при вызове. — Сергей Борщ (11.11.2008 22:14 )
                      • Из боязни;), так как через __generic не получилось. Я не знаю даже, работает оно или нет.Vit (76 знак., 11.11.2008 22:22 )
                        • если внутри функции приведение не нужно, то на что же ругался компилятор у -=John=-? __generic — вообще неоправданная трата ресурсов в этом случае. Тип параметра известен заранее. — Сергей Борщ (11.11.2008 22:55 )
                          • Понимаю так, что __io это атрибут, порождающий нечто похожее на #define PORTn (*(unsigned char*)(_ADDR+IO_BASE)), где IO_BASE равно известному 0x20, потому у него нет явной типизации, но некоторые действия как с указателями всё-таки применимы.Vit (729 знак., 12.11.2008 01:27 )
                            • Атрибут __io еще меняет порядок чтения/записи байт в 2-х байтовых регистрах. Например позволяет работать с таймером как с int. — Михаил Е. (12.11.2008 11:44 )
                              • Спасибо, интересно — Vit (12.11.2008 12:09 )
                              • Да это вообще ужас.-=John=- (107 знак., 11.11.2008 19:08 )
                                • В этом случае идеально подходит С++ и шаблоны. А на С можно вручную написать 4 функции uint8_t SPI_exchange(uint8_t) и передавать указатель на нужную функцию. Будет и быстрее и меньше по объему. — Сергей Борщ (11.11.2008 22:08 )
                                  • Ну, про 4 функции понятно — сам так с двумя делал. Но это экстенсивный путь развития. И некрасиво. — -=John=- (12.11.2008 08:36 )
                                  • Как объявлен I2CClockA? — Сергей Борщ (11.11.2008 22:09 )
                                    • static volatile unsigned int * I2CClockA; — -=John=- (12.11.2008 08:47 )
                                      • И у IAR также. Можете смело использовать свои макросы. — Сергей Борщ (12.11.2008 11:43 , ссылка)
                                        • Спасибо, посмотрю внимательней на адреса. — -=John=- (12.11.2008 12:39 )
                                        • Да поехать-то оно поехало. Хочется красиво.-=John=- (319 знак., 11.11.2008 19:14 )
                                          • Да понятно. Но обычно с AVR приходится экономить время, возлагая это дело на препроцессор и оптимизацию, ессно. — Vladimir Ljaschko (11.11.2008 19:20 )
                                            • Это неверный способ экономии времени, ИМХО.-=John=- (307 знак., 11.11.2008 19:31 )
                                              • Вопрос спорный. В новом проекте и я переписываю макросы, так и вы переписываете. Кроме того, убойный аргумент — скорость контроллера. Я на STM32 вообще библиотеку использую со структурами, описывающими порты. Там времени вагон. — Vladimir Ljaschko (11.11.2008 19:47 )
                                                • Да, спор беспредметный. Каждый пишет как привык — так по любому быстрее получается. — -=John=- (11.11.2008 19:53 )

                                                Лето 7532 от сотворения мира. При использовании материалов сайта ссылка на caxapу обязательна. Вебмастер
                                                MMI © MMXXIV

                                                Устройство и работа портов ввода-вывода микроконтроллеров AVR. Часть 1

                                                Изучив данный материал, в котором все очень детально и подробно описано с большим количеством примеров, вы сможете легко овладеть и программировать порты ввода/вывода микроконтроллеров AVR.

                                                • Часть 1. Работа портов ввода/вывода
                                                • Часть 2. Подключение светодиода к линии порта ввода/вывода
                                                • Часть 3. Подключение транзистора к линии порта ввода/вывода
                                                • Часть 4. Подключение кнопки к линии порта ввода/вывода

                                                Пример будем рассматривать на микроконтроллере ATMega8.

                                                Программу писать будем в Atmel Studio 6.0.

                                                Эмулировать схему будем в Proteus 7 Professional.

                                                С внешним миром микроконтроллер общается через порты ввода вывода. Схема порта ввода вывода указана в даташите:

                                                image

                                                Но новичку разобраться довольно со схемой довольно сложно. Поэтому схему упростим:

                                                image

                                                Pxn – имя ножки порта микроконтроллера, где x буква порта (A, B, C или D), n номер разряда порта (7… 0).
                                                Cpin — паразитная емкость порта.
                                                VCC — напряжение питания.
                                                Rpu — отключаемый нагрузочный верхний резистор (pull-up).
                                                PORTxn — бит n регистра PORTx.
                                                PINxn — бит n регистра PINx.
                                                DDRxn — бит n регистра DDRx.

                                                Рассмотрим, что же представляет собой вывод микроконтроллера. На входе микроконтроллера стоит небольшая защита из двух диодов (см.1), она предназначенная для защиты ввода микроконтроллера от кратковременных импульсов напряжения, превышающих напряжение питания. Если напряжение будет выше питания, то верхний диод откроется и это напряжение будет стравлено на шину питания, где с ним будет уже бороться источник питания и его фильтры. Если на ввод попадет отрицательное (ниже нулевого уровня) напряжение, то оно будет нейтрализовано через нижний диод и погасится на землю. Впрочем, диоды там хилые и защита эта помогает только от микроскопических импульсов и помех. Если же на ножку микроконтроллера подать вольт 6-7 при 5 вольтах питания, то внутренние диоды его не спасут.

                                                Конденсатор (см.2) — это паразитная емкость вывода. Хоть она и крошечная, но присутствует. Обычно ее не учитывают, но она есть. Не забивай голову, просто знай это.

                                                Дальше идут ключи управления (см.3,4). Каждый ключ подчинен логическому условию, которые нарисованы на рисунке. Когда условие выполняется — ключ замыкается.

                                                Каждый порт микроконтроллера AVR (обычно имеют имена A, B и иногда C или даже D) имеет 8 разрядов, каждый из которых привязан к определенной ножке корпуса. Каждый порт имеет три специальных регистра DDRx, PORTx и PINx (где x соответствует букве порта A, B, C или D). Назначение регистров:

                                                DDRx – Настройка разрядов порта x на вход или выход.

                                                PORTx – Управление состоянием выходов порта x (если соответствующий разряд настроен как выход), или подключением внутреннего pull-up резистора (если соответствующий разряд настроен как вход).

                                                PINx –Чтение логических уровней разрядов порта x.

                                                PINхn – это регистр чтения. Из него можно только читать. В регистре PINxn содержится информация о реальном текущем логическом уровне на выводах порта. Вне зависимости от настроек порта. Так что если хотим узнать что у нас на входе — читаем соответствующий бит регистра PINxn. Причем существует две границы: граница гарантированного нуля и граница гарантированной единицы — пороги за которыми мы можем однозначно четко определить текущий логический уровень. Для пятивольтового питания это 1.4 и 1.8 вольт соответственно. То есть при снижении напряжения от максимума до минимума бит в регистре PINx переключится с 1 на 0 только при снижении напряжение ниже 1.4 вольт, а вот когда напряжение нарастает от минимума до максимума переключение бита с 0 на 1 будет только по достижении напряжения в 1.8 вольта. То есть возникает гистерезис переключения с 0 на 1, что исключает хаотичные переключения под действием помех и наводок, а также исключает ошибочное считывание логического уровня между порогами переключения.

                                                При снижении напряжения питания разумеется эти пороги также снижаются.

                                                DDRxn – это регистр направления порта. Порт в конкретный момент времени может быть либо входом либо выходом (но для состояния битов PINxn это значения не имеет. Читать из PINxn реальное значение можно всегда).

                                                DDRxy = 0 – вывод работает как ВХОД.

                                                DDRxy = 1 – вывод работает на ВЫХОД.

                                                PORTxn – режим управления состоянием вывода. Когда мы настраиваем вывод на вход, то от PORTх зависит тип входа (Hi-Z или PullUp, об этом чуть ниже).

                                                Когда ножка настроена на выход, то значение соответствующего бита в регистре PORTx определяет состояние вывода. Если PORTxn=1 то на выводе лог.1, если PORTxn=0 то на выводе лог.0.

                                                Когда ножка настроена на вход, то если PORTxn=0, то вывод в режиме Hi-Z. Если PORTxn=1 то вывод в режиме PullUpс подтяжкой резистором в 100к до питания.

                                                Таблица. Конфигурация выводов портов.

                                                DDRxn PORTxn I/O Comment
                                                0 0 I (Input) Вход Высокоимпендансный вход. (Не рекомендую использовать, так как могут наводится наводки от питания)
                                                0 1 I (Input) Вход Подтянуто внутренне сопротивление.
                                                1 0 O (Output) Выход На выходе низкий уровень.
                                                1 1 O (Output) Выход На выходе высокий уровень.

                                                Общая картина работы порта показана на рисунках:

                                                image

                                                Рис. DDRxn=0 PORTxn=0 – Режим: HI-Z – высоко импендансный вход.

                                                image

                                                Рис. DDRxn=0 PORTxn=1 – Режим: PullUp – вход с подтяжкой до лог.1.

                                                image

                                                Рис. DDRxn=1 PORTxn=0 – Режим: Выход – на выходе лог.0. (почти GND)

                                                image

                                                Рис. DDRxn=1 PORTxn=1 – Режим: Выход – на выходе лог.1. (почти VCC)

                                                Вход Hi-Z — режим высокоимпендансного входа.
                                                Этот режим включен по умолчанию. Все ключи разомкнуты, а сопротивление порта очень велико. В принципе, по сравнению с другими режимами, можно его считать бесконечностью. То есть электрически вывод как бы вообще никуда не подключен и ни на что не влияет. Но! При этом он постоянно считывает свое состояние в регистр PINn и мы всегда можем узнать что у нас на входе — единица или ноль. Этот режим хорош для прослушивания какой либо шины данных, т.к. он не оказывает на шину никакого влияния. А что будет если вход висит в воздухе? А в этом случае напряжение будет на нем скакать в зависимости от внешних наводок, электромагнитных помех и вообще от фазы луны и погоды на Марсе (идеальный способ нарубить случайных чисел!). Очень часто на порту в этом случае нестабильный синус 50Гц — наводка от сети 220В, а в регистре PINn будет меняться 0 и 1 с частотой около 50Гц

                                                Вход PullUp — вход с подтяжкой.
                                                При DDRxn=0 и PORTxn=1 замыкается ключ подтяжки и к линии подключается резистор в 100кОм, что моментально приводит не подключенную никуда линию в состояние лог.1. Цель подтяжки очевидна — не допустить хаотичного изменения состояния на входе под действием наводок. Но если на входе появится логический ноль (замыкание линии на землю кнопкой или другим микроконтроллером/микросхемой), то слабый 100кОмный резистор не сможет удерживать напряжение на линии на уровне лог.1 и на входе будет лог.0.

                                                Режим выхода.
                                                Тут, думаю, все понятно — если нам надо выдать в порт лог.1, мы включаем порт на выход (DDRxn=1) и выдаем лог.1 (PORTxn=1) — при этом замыкается верхний ключ и на выводе появляется напряжение, близкое к питанию. А если надо лог.0, то включаем порт на выход (DDRxn=1) и выдаем лог.0 (PORTxn=1) — при этом открывается уже нижний вентиль, что дает на выводе около нуля вольт.

                                                • программирование
                                                • программирование микроконтроллеров
                                                • программирование avr
                                                • микроконтроллеры avr
                                                • подключение кнопки к avr
                                                • подключение светодиода к avr

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *