Теория
Базовые принципы
Говоря упрощенно, все, что умеет делать микроконтроллер:
- Выполнять программный код
- Подавать логическое значение (высокий или низкий уровень напряжения) на некоторые свои ноги (пины).
- Считывать логическое значение с некоторых своих пинов.
- Считывать (с некоторой точностью) напряжение (некоторого допустимого диапазона) на своем пине через механизм АЦП - Аналого-Цифровой Преобразователь (ADC - Analog-to-Digital Converter)
- Подавать напряжение на свои пины через механизм ЦАП - Цифро-Аналоговый Преобразователь (DAC - Digital-to-Analog Converter), или притворяться, что подает определенное напряжение через механизм ШИМ - Широтно-Импульсная Модуляция (PWM - Pulse-Width Modulation). Микроконтроллеры в нашем курсе (atmega 328p и esp8266) не имеют DAC на борту, поэтому вывод аналогового значения будет происходит генерацией ШИМ-сигнала.
Работая с микроконтроллером, программист пишет платформозависимый код на ассемблере или, гораздо чаще на языке Си. Микроконтроллер помимо знакомых вам регистров общего назначения и системных имеет регистры перефирии - с их помощью происходит управление внутренними цепями микроконтроллера через триггеры, что позволяет подключать или отключать необходимую функциональность. Примерно, это выглядит так:
// #include some_headers
main()
{
DDRA |=1<<PA0; /* PA0 will now be the output pin */
while(1)
{
PORTA &= ~(1<<PA0);/* PA0 LOW */
sleep(1000);/* 1000 ms delay */
PORTA |=(1<<PA0); /* PA0 HIGH */
sleep(1000);/* 1000 ms delay */
}
}
Первое, что приходит на ум, видя такой код - закрыть его и больше не открывать. Помимо того, что регистры названы неинтуитивно, у нас есть масса способов невнимательно прочитать документацию или промахнуться одним битом, что-нибудь при этом спалив. Конечно, производители микроконтроллеров это понимают и предоставляют ряд фреймворков, но они все равно остаются платформозависимыми.
В нашем курсе мы будем использовать мультиплатформенный фреймворк Arduino. Он имеет достаточно скромный API. Рассмотрим hello-world платформы:
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // Назначение пина 13 в качестве выходного
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // Установка пина 13 в состояние "1", светодиод загорается
delay(1000); // Задержка на 1000 миллисекунд
digitalWrite(LED_BUILTIN, LOW); // Установка пина 13 в состояние "0", светодиод гаснет
delay(1000);
}
- Функция
setup()
выполнится однократно при запуске микроконтроллера. - Функция
loop()
- бесконечный цикл pinMode(pinNum,INPUT|INPUT_PULLUP|OUTPUT)
- заставляет микроконтроллер сконфигурировать свой пин на ввод или вывод.digitalWrite(pinNum, HIGH|LOW)
- пишем логический 0 или 1 в сконфигурированный пин.LED_BUILTIN
- это констана, на плате разработчика часто присутствует светодиод подпаянный к некоторому пину, для Arduino nano это 13.HIGH
иLOW
- это 1 и 0 соответственно. Все, что неdelay(1000)
- задержка в 1000 миллисекунд или 1 секунду.
Как видно, такой код более читаемый, хотя и менее оптимальный. Однако, для большинства бытовых задач это не критично.
Основным преимуществом платформы является ее распространенность - она поддерживается практически всеми семействами микроконтроллеров, вы без труда найдете библиотеки под модули и множество примеров проектов.
Пины
На Arduino nano пины, с которыми мы будем работать обозначены как:
D2
-D13
- в программе мы обращаемся к ним просто по номеру:digitalWrite(2, HIGH)
A0
-A7
- обращаемся к ним по соответсвующим константам:digitalWrite(A2, HIGH)
В свою очередь они делятся на:
- Те, что поддерживают
GPIO
- то есть для них можно вызыватьdigitalWrite
и прочие - Те, на которые выведен АЦП(ADC) - для них можно вызвать
analogRead()
- Умеющие выдавать ШИМ(PWM) сигнал - - для них можно вызвать
analogWrite()
- Те, на которые выведены хардварные интерфейсы (i2c, SPI)Arduino nano pinout
Светодиоды (LED)
Светодиод, это в первую очередь диод - он пропускает ток только в одном направлении, как бонус еще и светится. Светодиод нелинейный элемент и имеет ряд характеристик, которые различаются в том числе в зависимости от цвета. В рамках курса характеристики нам не интересны, просто договоримся не путать полярность и всегда подключать через токоограничивающий резистор (чтобы ничего не сгорело).


Резисторы
Резисторы это проводники с большим сопротивлением. В нашем курсе они будут применяться как токоограничивающие для светодиодов (номиналом 220Om) и резисторы подтяжки для кнопок (номиналом 10kOm). На корпусе у них есть цветные колечки, они там не для красоты - если под рукой нет мультиметра, по ним определяется номинал - не иначе как издевательство над людьми. Повезло, что в наборе они несколько отличаются оттенком (тот что на 10к немного с зелена)

Подключаем кнопки
Кнопка это нормально разомкнутый проводник. Пока мы держим на ней палец, она проводит ток. На кнопке в нашем наборе 4 ноги - они просто продублированы.

Для считывания логических значений с пинов, Arduino предоставляет следующий функционал:
pinMode(btnPin, INPUT);
...
int val = digitalRead(btnPin);
...
digitalWrite(LED_BUILTIN, val);
Попробуйте подсоединить провод к
btnPin
и потрогать его руками - микроконтроллер будет считывать нестабильное значение - наводоки с пальца или вообще из воздуха.
Понятно, что такое поведение нежелательно. Чтобы его избежать выполняется подтяжка (pullup, pulldown) - добавим резистор номиналом 10kOm между input пином, и противоположным уровнем относительно считываемого (если второй конец кнопки подсоединен к низкому уровню GND
то подтягиваем к 5v
и наоборот)

Микроконтроллер может выполнить подтяжку за нас, в случае AVR только pullup:
pinMode(btnPin, INPUT_PULLUP);
Тогда подключаем кнопку вторым пином к GND и обходимся без резистора. Учитывайте, что в этом случае ненажатая кнопка выдаст единицу, а нажатая ноль.
Breadboard
Нужен для создания макетов электронных схем. Внутри отверстия соединены следующим образом:

Задачи
1.1 - Светофор
Основываясь на blink-example, собрать схему на трех светодиодах - красном, желтом и зеленом и заставить их работать по принципу автомобильного светофора (только с меньшими задержками):

Не забываем токоограничивающие регистры
1.2 - Переключаем светодиоды
Подключите к микроконтроллеру две кнопки и горстку светодиодов. Выбирайте кнопками какой из светодиодов сейчас горит (переключаемся влево-вправо).