Программа для микроконтроллера (2)
ScanJoyStick() – сканирование джойстика (русским языком написано). Так оно и есть:
byte ScanJoyStick()
{
byte Direction = JOYSTICK_RELEASED;
unsigned int X_Val = analogRead(JOYSTICK_X_PIN);
unsigned int Y_Val = analogRead(JOYSTICK_Y_PIN);
if ((Y_Val >= 900) && (X_Val >= 400))
{
Direction = JOYSTICK_UP_;
}
else if ((Y_Val <= 400) && (X_Val >= 400))
{
Direction = JOYSTICK_DOWN_;
}
else if ((Y_Val >= 400) && (X_Val >= 900))
{
Direction = JOYSTICK_RIGHT_;
}
else if ((Y_Val >= 400) && (X_Val <= 400))
{
Direction = JOYSTICK_LEFT_;
}
return Direction;
}
Функция определяет, куда наклонили джойстик. Делает она это нетипично просто для этого проекта. Просто считываются значения в портах (analogRead) и определяется направление по считанному. Видимо ноль джойстика это 650, раз так направления определяются:
В тексте остались две функции – два обработчика прерываний. Очень простые по тексту:
void JOYSTICK_Z_PIN_ISR()
{
JoyStickZPressFlag = true; //установка флага нажатия кнопки джойстика
}
//---------------------------------
void TimerOne_ISR()
{
UpdateScreenFlag = true; //установка флага обновления экрана
}
Они обе устанавливают внутренние программные флажки, по которым в других местах программы можно понять что была нажата кнопка джойстика (JOYSTICK_Z_PIN_ISR) или что прошло заданное количество секунд и пора обновлять нарисованное на экране (TimerOne_ISR)
В бесконечном цикле loop() выключается будильник если была нажата кнопка джойстика:
if ((JoyStickZPressFlag) && (digitalRead(RELAY_PIN))) //если нажата кнопка джойстика
{
delay(100);
if (!digitalRead(JOYSTICK_Z_PIN))
{
static byte JoyStickZPressCnt = 0;
if (JoyStickZPressCnt == 0) //если первое нажатие
{
//digitalWrite(BUZZER_PIN, LOW);
Buzzer.stop(); //выключение пищалки
JoyStickZPressCnt++;
Там же перерисовка экрана идёт только если пора его перерисовывать:
void loop()
{
static tmElements_t CurrentTime;
if (UpdateScreenFlag) //если обновление экрана
{
RTC.read(CurrentTime); //чтение времени с RTC
display.clearDisplay(); //очистка дисплея
UpdateTime(&CurrentTime); //обновление времени
UpdateDate(&CurrentTime); //обновление даты
UpdateTemp(); //обновление показаний температуры и влажности
… а не при каждом вызове функции loop(), что перегружало бы машину лишней работой.
Чтобы работало всё это сооружение надо не только написать функции – обработчики. Ещё надо объявить переменные (флажки) за пределами функций и привязать обработчики к событиям (нажатию кнопки джойстика и срабатыванию таймера):
На самом деле это всё можно писать и не понимая, что тут и как происходит. Надо только ещё не забыть подключить в #include <PinChangeInt.h> для джойстика.
Попробую описать происходящее. Слово «прерывание» - перевод английского «interrupt». Возможно «событием», как стало общепринято во времена распространения объектно – ориентированного программирования, это «прерывание» не стало по чистой случайности. Прерывание это именно событие изменения чего-то. На это событие надо (или не надо) как-то отреагировать. Слово interrupt состоит из inter (между, внутри) и rupt (ruption) – разрыв. Целиком может быть переведено ещё и как «перебивать», «вмешиваться», «преграждать».
Это какая-то преграда внутри, какое-то вмешательство, на которое надо отреагировать. Если просто это изменение, например, информации в каком-то порту (означающее, что туда пришла новая информация и что эту информацию надо как-то обработать).
Всё что делается, делается программами. Обработчик прерывания это тоже программа, постоянно загруженная в память чтобы её было быстрее выполнять. Чтобы выполнить эту программу компьютеру (или контроллеру) надо знать где она находится, т.е с какого места (адреса) оперативной памяти лежат те команды которые надо выполнять если произошло некое событие – прерывание.
Почему эти адреса начали называться «векторами прерываний» я не знаю. Наверное потому, что стандартный вид адреса как бы состоит из двух частей 1F00:891A (вектор это несколько цифр информации). Как бы то ни было, эти вектора, т.е адреса обработчиков хранятся в оперативной памяти начиная с нулевой ячейки. Т.е ссылка на обработчик 5-го прерывания лежит по адресу 5*4 байта = 20 байт.
Вся эта информация (и вектора и программы – обработчики) загружается в оперативную память при запуске компьютера. Можно сказать, что операционная система это именно эти обработчики. Когда включается компьютер какие-то аппаратные средства обходят все жёсткие диски, ищут так называемый загрузочный сектор. Если находит, что выполняет записанные в нем команды. Эти команды и копируют всё это в оперативную память. Потом работа едёт уже с помощью команд загруженных в оперативную память.
Но иногда тех реакций на события, которые предусмотрены в операционной системе недостаточно. Например в DOS-не не было ни стандартной мыши ни стандартного русификатора. И надо было написать свои обработчики. Например русификатор мог перехватывать событие нажатия кнопок на клавиатуре и переключать раскладку если нажата кнопка – правый Ctrl.
Программы, которые это делали обычно назывались резидентными. При запуске они записывали часть своего кода в оперативную память на постоянной основе (это и был новый обработчик прерывания). А ещё их загрузчик менял вектор прерывания (адрес в начале оперативной памяти) на адрес этого нового обработчика. Ещё обязательно надо было вставить в хвост нового обработчика запуск старого (точнее стандартного обработчика операционной системы). Ведь русификатор должен был только выполнить что-то по кнопке Ctrl. После этого надо было дать операционной системе обработать прочие кнопки так как она обычно обрабатывала их нажатия.
Ставить знак равенства между работой контроллера и чем-то компьютерным нельзя. Но кое-что наверное надо попытаться проассоциировать. Во-первых, Arduino это точно не Windows. Тут нет нескольких процессов, все ресурсы передаются одной программе, записанной в функции loop(). Назвать так называемый скетч резидентной программой под DOS нельзя. Формальное сходство – запись своих обработчиков событий. Других сходств нет. Остаток текста (loop()) это то единственное, что выполняет контроллер. Программы резиденты же это некий вспомогательный инструментарий обрабатывающий конкретное событие. Больше всего это, всё-таки, похоже на стандартную DOS-овскую программу, которой переданы все ресурсы компьютера, но которая при запуске подгрузила ещё и свои обработчики (потому что ей это надо).
А надо ей это потому что стандартные DOS-овские программы так тоже не работают. Они сидят и в таком же бесконечном цикле ждут нажатия кнопки или (что реже) щелчка мыши. Структура текста другая. С другой стороны, мне привычны в основном списки накладных и платёжных поручений. Там работа именно такая. А вот если бы мне потребовалось писать игру или вывод на экран видеоизображения, мне бы пришлось ориентироваться на время (т.е таймер) чтобы делать всё это со строго определенной скоростью. Похоже что метод работы взят с таких задач. А раз так, то простое нажатие кнопки на мышке-джойстике пришлось обрабатывать через прерывание.
Я это всё написала потому что с правилами подобной оболочки бороться сложно и лучше всё-таки не надо. Лучше понимать, почему тут это сделано так.
Никто не мешает засунуть в loop() ожидание нажатия кнопки, возможно бесконечное. Тогда этот loop() будет выполнен только один раз (всё будет в нем, т.е будет обрабатываться в циклах внутри него). Вот так, видимо, лучше не делать, хотя, наверное, можно. Если судить по данному проекту, это не рекомендуемый вариант.
|