digitalWrite(ledPin, LOW);
delay(period);
}
void stuffHapenned()
{
flashFast = ! flashFast;
}
В этом скетче функция loop использует глобальную переменную flashFast, чтобы определить период задержки. Подпрограмма обработки изменяет значение этой переменной между true и false.
Обратите внимание на то, что в объявление переменной flashFast включено слово volatile. Вы можете успешно разрабатывать скетч и без спецификатора volatile, но он совершенно необходим, потому что в отсутствие этого спецификатора компилятор C может генерировать машинный код, кэширующий значение переменной в регистре для увеличения производительности. Если, как в данном случае, кэширующий код будет прерван, он может не заметить изменения значения переменной.
В заключение о подпрограммах обработки прерываний
Когда будете писать подпрограммы обработки прерываний, помните следующие правила.
• Подпрограммы должны действовать быстро.
• Для передачи данных между подпрограммой обработки прерываний и остальной программой должны использоваться переменные, объявленные со спецификатором volatile.
• Не используйте delay, но можете использовать delayMicroseconds.
• Не ожидайте высокой надежности взаимодействий через последовательные порты.
• Не ожидайте, что значение, возвращаемое функцией millis, изменится.
Разрешение и запрет прерываний
По умолчанию прерывания в скетчах разрешены и, как упоминалось ранее, автоматически запрещаются на время работы подпрограммы обработки прерываний. Однако есть возможность явно запрещать и разрешать прерывания в программном коде, вызывая функции noInterrupts и interrupts. Эти функции не имеют параметров, и первая из них запрещает прерывания, а вторая — разрешает.
Явное управление может понадобиться, чтобы исключить возможность прерывания фрагмента кода, например, выводящего последовательность данных или генерирующего последовательность импульсов и точно выдерживающего временные интервалы с помощью функции delayMicroseconds.
Прерывания от таймера
Вызов подпрограмм обработки прерываний можно организовать не только по внешним событиям, но и по внутренним событиям изменения времени. Такая возможность особенно полезна, когда требуется выполнять некоторые операции через определенные интервалы времени.
Библиотека TimerOne упрощает настройку прерываний от таймера. Ее можно найти и загрузить по адресу http://playground.arduino.cc/Code/Timer1.
Следующий пример показывает, как с помощью TimerOne сгенерировать последовательность импульсов прямоугольной формы с частотой 1 кГц. Если в вашем распоряжении имеется осциллограф или мультиметр с возможностью измерения частоты, подключите его к контакту 12, чтобы увидеть сигнал (рис. 3.2).
Рис. 3.2. Последовательность прямоугольных импульсов, сгенерированная с помощью таймера
// sketch_03_03_1kHz
#include <TimerOne.h>
int outputPin = 12;
volatile int output = LOW;
void setup()
{
pinMode(12, OUTPUT);
Timer1.initialize(500);
Timer1.attachInterrupt(toggleOutput);
}
void loop()
{
}
void toggleOutput()
{
digitalWrite(outputPin, output);
output = ! output;
}
То же самое можно было бы реализовать с помощью delay, но применение прерываний от таймера позволяет организовать выполнение любых других операций внутри loop. Кроме того, использование функции delay не позволит добиться высокой точности, потому что время, необходимое на изменение уровня напряжения на контакте, не будет учитываться в величине задержки.
ПРИМЕЧАНИЕ
Все ограничения для подпрограмм обработки внешних прерываний, о которых рассказывалось ранее, распространяются также на подпрограммы обработки прерываний от таймера.
Представленным способом можно установить любой интервал между прерываниями в диапазоне от 1 до 8 388 480 мкс, то есть примерно до 8,4 с. Величина интервала передается функции initialize в микросекундах.
Библиотека TimerOne дает возможность также использовать таймер для генерирования сигналов с широтно-импульсной модуляцией (Pulse Width Modulation, PWM) на контактах 9 и 10 платы. Это может показаться излишеством, потому что то же самое делает функция analogWrite, но применение прерываний позволяет обеспечить более точное управление сигналом PWM. В частности, используя такой подход, можно организовать измерение протяженности положительного импульса в диапазоне 0…1023 вместо 0…255 в функции analogWrite. Кроме того, при использовании analogWrite частота следования импульсов в сигнале PWM составляет 500 Гц, а с помощью TimerOne можно эту частоту увеличить или уменьшить.
Чтобы сгенерировать сигнал PWM с применением библиотеки TimerOne, используйте функцию Timer1.pwm, как показано в следующем примере:
// sketch_03_04_pwm
#include <TimerOne.h>
void setup()
{
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);
Timer1.initialize(1000);
Timer1.pwm(9, 512);
Timer1.pwm(10, 255);
}
void loop()
{
}
Здесь выбран период следования импульсов, равный 1000 мкс, то есть частота сигнала PWM составляет 1 кГц. На рис. 3.3 показана форма сигналов на контактах 10 (вверху) и 9 (внизу).
Рис. 3.3. Широтно-импульсный сигнал с частотой 1 кГц, сгенерированный с помощью TimerOne
Ради интереса давайте посмотрим, до какой степени можно увеличить частоту сигнала PWM. Если уменьшить длительность периода до 10, частота сигнала PWM должна увеличиться до 100 кГц. Форма сигналов, полученных с этими параметрами, показана на рис. 3.4.
Несмотря на наличие существенных переходных искажений, что вполне ожидаемо, протяженность положительных импульсов все же остается довольно близкой к 25 и 50% соответственно.
Рис. 3.4. Широтно-импульсный сигнал с частотой 100 кГц, сгенерированный с помощью TimerOne
В заключение
Прерывания, которые иногда кажутся идеальным решением для непростых проектов, могут осложнить отладку кода и не всегда оказываются лучшим способом решения трудных задач. Тщательно обдумайте возможные решения, прежде чем переходить к их использованию. В главе 14 мы познакомимся с другим приемом преодоления сложностей, связанных с тем, что Arduino не может выполнять более одной задачи одновременно.
Мы еще вернемся к прерываниям в главе 5, где рассмотрим возможность их применения для уменьшения потребления электроэнергии платой Arduino за счет периодического перевода ее в режим энергосбережения, и в главе 13, где прерывания будут применяться для увеличения точности обработки цифровых сигналов.
В следующей главе мы познакомимся с приемами увеличения производительности Arduino до максимума.
4. Ускорение Arduino
В этой главе рассказывается, как определить производительность платы Arduino и как выжать из нее дополнительную мощность, когда это необходимо.
Как определить производительность Arduino?
Прежде чем заняться изучением приемов увеличения скорости работы скетчей, потратим немного времени на тестирование Arduino, чтобы просто понять, насколько ее производительность сопоставима с производительностью компьютеров, начав с таких понятий, как мегагерц и гигагерц.
Тактовый генератор на плате Arduino Uno имеет частоту 16 МГц. Большинство инструкций (сложения или сохранения значения в переменной) выполняется за один такт. То есть Uno может выполнять до 16 млн элементарных операций в секунду. Вроде бы неплохо, не так ли? Однако все не так просто, потому что инструкции на языке C, которые вы пишете в скетчах, разворачиваются в множество машинных инструкций.