Временные задержки, таймеры, прерывания
Скачать 226.59 Kb.
|
Временные задержки, таймеры, прерывания. Задержки Вариант 1 – функция Delay(ms); void setup() { pinMode(13, OUTPUT); // 13 пин объявляется выходным } void loop() { digitalWrite(13, HIGH); // включаем светодиод, подключенный к 13 пину delay(1000); // задержка в 1с (1000 мс) digitalWrite(13, LOW); // выключаем светодиод, подключенный к 13 пину delay(1000); } Недостаток – пока идёт задержка по delay процессор ничего не выполняет (простаивает), не происходит опорос портов, т.е. процессор может пропустить сигнал, пришедший в порт. Вариант 2 – использование millis() (функция встроенная, работает по таймеру, сообщает количество миллисекунд, прошедших с начала работы процессора). long int timer = 0; // объявление глобальных переменных void setup() { // предустановка, задаются начальные параметры digitalWrite(13, 0); // сброс 13 пина в 0 } void loop() { // основной цикл программы if (millis() - timer >= 500) { //разница (500 мс) timer = millis(); digitalWrite(13,!digitalRead(13)); // 13 пин ставится в противоположное состояние } } Недостаток - если в основном цикле что-то вызовет задержку во времени, то период может «уйти». Использование таймеров Вариант 3 – использование написанных библиотек для работы с таймерами. #include "GyverTimer.h" // подключили библиотеку для работы с таймерами GTimer TIM1(MS); // объявили таймер, считающий в миллисиекундах void setup() { TIM1.setInterval(2000); // задали интервал отсчёта } void loop() { if (TIM1.isReady()) // проверка условия, если таймер отсичтал интервал, то выполнить действие digitalWrite(13, !digitalRead(13)); } Вариант 4 – работа со встроенными таймерами напрямую. #include #include #define LED 13 void setup() { // инициализация Timer1 cli(); // отключить глобальные прерывания TCCR1A = 0; // установить регистры в 0 TCCR1B = 0; OCR1A = 7812; // установка регистра совпадения TCCR1B |= (1 << WGM12); // включить CTC режим //TCCR1B |= (1 << CS10); // Установить биты на коэффициент деления 1024 TCCR1B |= (1 << CS12); TIMSK1 |= (1 << OCIE1A); // включить прерывание по совпадению таймера sei(); // включить глобальные прерывания pinMode(LED, OUTPUT); } ISR(TIMER1_COMPA_vect){ digitalWrite(LED, !digitalRead(LED)); } void loop() {} Так как наш микроконтроллер работает на частоте 16 МГц, а мы хотим управлять светодиодом, для увеличения времени счета нашего таймера, мы поделим его тактовую частоту на 1024: TCCR1B |= (1 << CS10); TCCR1B |= (1 << CS12); Таким образом мы получаем скорость счета (тактирования) 15625 Гц или 64,0е-6 секунды. Нам нужно, что бы счетчик менял свое состояние раз в 0,5 секунду, разделив 0,5/64,0е-6 мы получаем 7812,5. Таким образом в регистр OCR1A нам необходимо записать 7813. Но с учетом того, что сброс счетчика в ноль занимает один такт, нам необходимо записать в регистр OCR1A – 7812. Регистр TCCR1A : Регистр TCCR1В : Прерывания. Пример 1. void setup() { attachInterrupt(0, led1, RISING); // добавили нулевое прерывание (пин 2) , вызывающее функцию led1, срабатывает по восходящему фронту (RISING) attachInterrupt(1, led2, FALLING); // добавили первое прерывание (пин 3) , вызывающее функцию led2, срабатывает по восходящему фронту (FALLING) } void loop() { // в основном цикле программы ничего не происходит } void led1() { digitalWrite(12, !digitalRead(12)); } void led2() { digitalWrite(11, !digitalRead(11)); } Задача – написать код, который при нажатии одной кнопки выдаёт сигнал «бегущий огонь», с промежутком переключения между светодиодами 500 мс, при нажатии второй кнопки промежуток увеличивается. Добавить ограничение промежутка до 3000 мс. (Рекомендуется использовать прерывания). long int counter = 0; volatile int Period = 500; // переменная, использующаяся в функции прерывания имеет приставку volatile volatile bool flag = false; void setup() { attachInterrupt(0, runnig_fire, RISING); attachInterrupt(1, change_period, FALLING); } void loop() { if (flag) { digitalWrite(11, HIGH); delay(Period); digitalWrite(12, HIGH); digitalWrite(11, LOW); delay(Period); digitalWrite(10, HIGH); digitalWrite(12, LOW); delay(Period); flag = false; } else { digitalWrite(12, LOW); digitalWrite(11, LOW); digitalWrite(10, LOW); } } void runnig_fire() { flag = true; } void change_period() { Period += 1000; if (Period > 3000) Period = 500; } Второй вариант без delay. long int counter = 0; volatile int Period = 500; volatile bool flag = false; void setup() { Serial.begin(9600); attachInterrupt(0, runnig_fire, RISING); // прерывание по кнопке, которое вызывает функцию бегущего огня attachInterrupt(1, change_period, FALLING); // прерывание по кнопке, увеличивает период } void loop() { if (flag) { counter=millis(); while (millis() - counter < Period) { digitalWrite(11, HIGH); } digitalWrite(11, LOW); counter=millis(); while (millis() - counter < Period) { digitalWrite(12, HIGH); } digitalWrite(12, LOW); counter=millis(); while (millis() - counter < Period) { digitalWrite(10, HIGH); } flag = false; } else { digitalWrite(12, LOW); digitalWrite(11, LOW); digitalWrite(10, LOW); } } void runnig_fire() { flag = true; } void change_period() { Period += 1000; if (Period > 3000) Period = 500; } |