Прерывания по таймеру и переменные.
- Войдите на сайт для отправки комментариев
Доброго времени суток, форумчане!
Если честно, то не понимаю, как правильно задать вопрос... Проблема следующая: Есть проект метеостанции. В качестве задающей временные интервалы функции(период измерений, обновления ЛСД и т.д.), использован таймер прерываний. Столкнулся с такой проблемой:
В алгоритме по факту нажатия кнопки: 1. выставляется два флага - флаг смены режима(flagUpdateMode), флаг таймера подсветки дисплея(flagTimerBacklight) 2. выставляется величина задержки подсветки дисплея (timerBacklight). Строки алгоритма 84 - 99.
Если в цикле loop() я вывожу хотя бы одну переменную(flagUpdateMode,flagTimerBacklight или mode) в серийный порт, то скетч функционирует исправно, если убираю строки вывода в порт, флажки перестают устанавливаться... Я вообще не понимаю, что происходит. Куда смотреть? Что читать и как бороться?
#include "DHT.h" //Подключаем библиотеку для датчиков типа DHT11/DHT22 #include "MsTimer2.h" //Подключаем библиотеку для аппаратных прерываний #include "Button.h" //Подключаем библиотеку для обработки кнопки #include <LiquidCrystal_I2C.h> //Подключение библиотеки для LCD #include <Adafruit_BMP085.h> //Подключаем библиотеку для датчика BMP-180 //------Объявляем имена и значения констант------// #define DHT_INDOOR_PIN A0 //Пин датчика температуры и влажности в помещении #define DHT_OUTDOOR_PIN A1 //Пин датчика температуры и влажности уличного #define BUTTON_1_PIN A2 //Пин 1 кнопки #define CYCLE_MEAS 2500 //Время цикла измерений температуры и влажности * период прерывания по таймеру #define CYCLE_BACKLIGHT 7500 //Время работы подсветки lcd //------Создаем объекты------// Button button1(BUTTON_1_PIN, 15); //Создали кнопку с периодом антидребезга 15х2мс DHT indoorSensor(DHT_INDOOR_PIN, DHT11); //Создали датчик температуры и влажности в помещении DHT outdoorSensor(DHT_OUTDOOR_PIN, DHT22); //Создали датчик температуры и влажности уличный LiquidCrystal_I2C lcd(0x27, 16, 2); //Создаем дисплей. Указываем I2C адрес (наиболее распространенное значение), а также параметры экрана (в случае LCD 1602 - 2 строки по 16 символов в каждой Adafruit_BMP085 bmp; //Создаем датчик атмосферного давления //------Объявляем переменные------// //переменные для работы с данными с датчиков byte hIndoor; //влажность в помещении int tIndoor; //температура в помещении byte hOutdoor; //влажность уличная int tOutdoor; //температура уличная float pressure; //атмосферное давление //переменные для работы с таймерами unsigned int timerMeas; //таймер измерений температуры и влажности boolean flagTimerMeas = false; //флаг таймера измерений температуры и влажности unsigned int timerBacklight = CYCLE_BACKLIGHT ; //таймер lcd подсветки boolean flagTimerBacklight = true; //флаг таймера lcd подсветки //переменные основного цикла byte mode; //перменная определяющая режим работы boolean flagUpdateMode; //флаг смены режима boolean flagUpdateLCD; //флаг обновления дисплея //------Создаем нужные символы для дисплея------// byte deg[8] = {B00110, B01001, B01001, B00110, B00000, B00000, B00000, B00000,}; // градус byte up[8] = {B00000, B00100, B01110, B10101, B00100, B00100, B00100, B00000,}; // вверх byte down[8] = {B00000, B00100, B00100, B00100, B10101, B01110, B00100, B00000,}; // вниз byte outdoor[8] = {B00011, B00001, B00000, B00100, B01010, B11111, B01010, B01110,}; // снаружи byte indoor[8] = {B00010, B00011, B00000, B00100, B01010, B11111, B01010, B01110,}; // внутри byte hum[8] = {B00100, B01010, B01010, B10001, B10001, B01110, B00000, B00000,}; // капля byte term[8] = {B01100, B01110, B01100, B01110, B01100, B10010, B10010, B01100,}; // термометр byte press[8] = {B00100, B00100, B01000, B01000, B00100, B10101, B01110, B00100,}; // давление byte mr[8] = {B10001, B11011, B10101, B10001, B01100, B01010, B01100, B01001,}; // мр byte ms[8] = {B10001, B11011, B10101, B10001, B01100, B10000, B10000, B01101,}; // мc //byte bukva_B[8] = {B11110, B10000, B10000, B11110, B10001, B10001, B11110, B00000,}; // Буква "Б" byte bukva_G[8] = {B11111, B10001, B10000, B10000, B10000, B10000, B10000, B00000,}; // Буква "Г" byte bukva_D[8] = {B01111, B00101, B00101, B01001, B10001, B11111, B10001, B00000,}; // Буква "Д" //byte bukva_ZH[8] = {B10101, B10101, B10101, B11111, B10101, B10101, B10101, B00000,}; // Буква "Ж" //byte bukva_Z[8] = {B01110, B10001, B00001, B00010, B00001, B10001, B01110, B00000,}; // Буква "З" byte bukva_I[8] = {B10001, B10011, B10011, B10101, B11001, B11001, B10001, B00000,}; // Буква "И" //byte bukva_IY[8] = {B01110, B00000, B10001, B10011, B10101, B11001, B10001, B00000,}; // Буква "Й" //byte bukva_L[8] = {B00011, B00111, B00101, B00101, B01101, B01001, B11001, B00000,}; // Буква "Л" byte bukva_P[8] = {B11111, B10001, B10001, B10001, B10001, B10001, B10001, B00000,}; // Буква "П" //byte bukva_Y[8] = {B10001, B10001, B10001, B01010, B00100, B01000, B10000, B00000,}; // Буква "У" //byte bukva_F[8] = {B00100, B11111, B10101, B10101, B11111, B00100, B00100, B00000,}; // Буква "Ф" byte bukva_TS[8] = {B10010, B10010, B10010, B10010, B10010, B10010, B11111, B00001,}; // Буква "Ц" //byte bukva_CH[8] = {B10001, B10001, B10001, B01111, B00001, B00001, B00001, B00000,}; // Буква "Ч" //byte bukva_Sh[8] = {B10101, B10101, B10101, B10101, B10101, B10101, B11111, B00000,}; // Буква "Ш" //byte bukva_Shch[8] = {B10101, B10101, B10101, B10101, B10101, B10101, B11111, B00001,}; // Буква "Щ" //byte bukva_Mz[8] = {B10000, B10000, B10000, B11110, B10001, B10001, B11110, B00000,}; // Буква "Ь" //byte bukva_IYI[8] = {B10001, B10001, B10001, B11001, B10101, B10101, B11001, B00000,}; // Буква "Ы" //byte bukva_Yu[8] = {B10010, B10101, B10101, B11101, B10101, B10101, B10010, B00000,}; // Буква "Ю" byte bukva_Ya[8] = {B01111, B10001, B10001, B01111, B00101, B01001, B10001, B00000,}; // Буква "Я" //------Установки и прочее------// void setup() { MsTimer2::set(2, timerInterupt); //задаем период прерывания по таймеру 2 мс MsTimer2::start(); //запускаем таймер indoorSensor.begin(); //запускаем датчик внутренний outdoorSensor.begin(); //запускаем датчик уличный Serial.begin(9600); //запускаем серийный порт lcd.begin(); //запускаем дисплей bmp.begin(); //запускаем датчик давления onOffBacklight(); //включаем подсветку дисплея hello(); //выводим приветствие } //------Основной цикл------// void loop() { if (button1.flagClick == true) { //был "клик" кнопкой timerBacklight = CYCLE_BACKLIGHT; //установка таймера подсветки lcd flagTimerBacklight = true; //установить флаг включения подсветки lcd // Serial.print("flagTimerBacklight = "); // Serial.println(flagTimerBacklight); flagUpdateMode = true; //установить флаг смены режима // Serial.print("flagUpdateMode = "); // Serial.println(flagUpdateMode); mode++; //смена режима if (mode > 1) { mode = 0; } // Serial.print("mode = "); // Serial.println(mode); button1.flagClick = false; //сброс флага "клик" кнопкой } if (flagTimerMeas == true) measureTHP(); //измеряем параметры окр. среды switch (mode) { //режим по умолчанию(показания уличные) case 0: if (flagUpdateMode == true) { //если была смена режима lcd.createChar(1, outdoor); //создаем значек снаружи clearLCD(); updateLcdOutdoorTHP(); //обновляем показания дисплея flagUpdateMode = false; //сбросываем флаг смены режима } if (flagUpdateLCD == true) { //если получили новые данные updateLcdOutdoorTHP(); //обновляем показания дисплея } break; //режим показаний в помещении case 1: if (flagUpdateMode == true) { //если была смена режима lcd.createChar(1, indoor); //создаем значек внутри clearLCD(); updateLcdIndoorTH(); //обновляем показания дисплея flagUpdateMode = false; //сбросываем флаг смены режима } if (flagUpdateLCD == true) { //если получили новые данные updateLcdIndoorTH(); //обновляем показания дисплея } break; } onOffBacklight(); //управление подсветкой } //------Обработчик прерываний------// void timerInterupt() { button1.scanState(); //проверяем нажатие кнопки //Таймер измерений и обновления lcd timerMeas++; //инкремент таймера измерений if (timerMeas >= CYCLE_MEAS) { //проверка таймера измерений timerMeas = 0; //сброс таймера измерений flagTimerMeas = true; //выставление флага таймера измерений flagUpdateLCD = true; //выставление флага обновить показания на дисплее // Serial.print("flagTimerMeas = "); // Serial.println(flagTimerMeas); } //Таймер подсветки lcd if (timerBacklight > 0) { //проверка таймера подсветки timerBacklight--; //декремент таймера подсветки if (timerBacklight == 0) { flagTimerBacklight = false; //выставление флага таймера подсветки } } } //------Измерение параметров окр.среды------// void measureTHP() { float temp; tIndoor = indoorSensor.readTemperature() * 100.0; //измеряем температуру в помещении hIndoor = indoorSensor.readHumidity(); //измеряем влажность в помещении tOutdoor = outdoorSensor.readTemperature() * 100.0; //измеряем температуру уличную hOutdoor = outdoorSensor.readHumidity(); //измеряем влажность уличную temp = bmp.readTemperature(); //необходимо по условиям расчета давления(см. datasheet) pressure = bmp.readPressure() * 0.0075; //измеряем давление и переводим в мм.рт.ст. temp = bmp.readAltitude(); temp = bmp.readSealevelPressure(); temp = bmp.readAltitude(101500); flagTimerMeas = false; //сбрасываем флаг таймера измерений // Serial.println("measureTHP() STOP"); } //------Управление подсветкой lcd------// void onOffBacklight() { if ( flagTimerBacklight == true) { lcd.backlight(); } else { lcd.noBacklight(); } } //------Вывод текущих уличных значений на дисплей------// void updateLcdOutdoorTHP() { char _tOutdoorStr[4]; char _hOutdoorStr[4]; char _pressureStr[4]; float _tOutdoor = tOutdoor / 100.0; dtostrf(_tOutdoor, 4, 1, _tOutdoorStr); lcd.setCursor(0, 0); lcd.print("\1 "); lcd.print("\6 "); lcd.print(_tOutdoorStr); lcd.setCursor(9, 0); lcd.print("\3C"); lcd.setCursor(12, 0); lcd.print("\5"); dtostrf(hOutdoor, 2, 0, _hOutdoorStr); lcd.print(_hOutdoorStr); lcd.print("%"); dtostrf(pressure, 4, 1, _pressureStr); lcd.setCursor(2, 1); lcd.print("\4\4 "); lcd.print(_pressureStr); lcd.print(" \2\7"); flagUpdateLCD = false; // Serial.println("updateLcdOutdoorTHP()"); } //------Вывод текущих значений в помещении на дисплей------// void updateLcdIndoorTH() { char _tIndoorStr[4]; char _hIndoorStr[4]; float _tIndoor = tIndoor / 100.0; dtostrf(_tIndoor, 4, 1, _tIndoorStr); lcd.setCursor(0, 0); lcd.print("\1 "); lcd.print("\6 "); lcd.print(_tIndoorStr); lcd.setCursor(9, 0); lcd.print("\3C"); lcd.setCursor(12, 0); lcd.print("\5"); dtostrf(hIndoor, 2, 0, _hIndoorStr); lcd.print(_hIndoorStr); lcd.print("%"); lcd.setCursor(0, 1); flagUpdateLCD = false; // Serial.println("updateLcdIndoorTH()"); } //------Очистка дисплея------// void clearLCD() { lcd.setCursor(0, 0); lcd.print(" "); lcd.setCursor(0, 1); lcd.print(" "); } //------Приветствие------// void hello() { // Serial.println("Погодная станция v.01.20"); lcd.createChar(1, bukva_P); lcd.createChar(2, bukva_G); lcd.createChar(3, bukva_D); lcd.createChar(4, bukva_Ya); lcd.createChar(5, bukva_TS); lcd.createChar(6, bukva_I); lcd.setCursor(4, 0); // Установка курсора в 5ю позицию первой строки lcd.print("\1O\2O\3HA\4"); // Набор текста на первой строке lcd.setCursor(0, 1); // Установка курсора в начало второй строки lcd.print("CTAH\5\6\4 v.01.20"); // Набор текста на второй строке delay(2000); clearLCD(); lcd.createChar(1, outdoor); // Создаем значек снаружи lcd.createChar(2, mr); // Создаем значек мр lcd.createChar(3, deg); // Создаем значек градуса lcd.createChar(4, press); // Создаем значек давления lcd.createChar(5, hum); // Создаем значек влажность lcd.createChar(6, term); // Создаем значек термометр lcd.createChar(7, ms); // Создаем значек мс }
Переменные, которые изменяются в прерывании, следует объявлять как volatile. Иначе компилятор их соптимизирует неочевидным для человека способом.
1. Работать с Serial в прерывании по таймеру - не айс;
2. Почитайте про модификатор volatile;
3. В loop, при работе с переменными, изменяемыми в прерывании по таймеру - надо атомарно копировать их в локальные переменные. Атомарно - значит, на время копирования отключать прерывания. Потому что прерывание, изменяющее переменные, может быть вызвано в любой точке исполнения loop, в том числе в той, где сравниваются переменные, одна из которых - изменяемая в прерывании;
4. Если не понимаете, как работать с прерываниями - для начала можно обойтись без них.
Хоть они и изменяются в основном цикле программы, я пробовал их объявить через volatile, не помогло. Хотя...
Хоть они и изменяются в основном цикле программы, я пробовал их объявить через volatile, не помогло. Хотя...
Не помогло потому, что вы и устанавливаете эти переменные вне прерывания - не атомарно. Ещё раз повторюсь: вашу задачу можно решить проще, и без прерываний. Потому как вы сами себя запутываете, не обладая общим пониманием принципов работы с прерываниями.
Спасибо! Будем вникать.
Таймеры, много: https://github.com/DetSimen/Arduino_TimerList
Таймеры, много: https://github.com/DetSimen/Arduino_TimerList
Отдаёшь в "хорошие руки"? ;)))) Привиты? К лотку приучены?
Отдаёшь в "хорошие руки"? ;)))) Привиты? К лотку приучены?
Да зря, наерна, самому такие нужны. Не отдам. :)
Хоть они и изменяются в основном цикле программы, я пробовал их объявить через volatile, не помогло.
А зачем Вы их "взад" разобъявили?
Вы мне напомнили одного деятеля, заходил сюда как-то, но решил, что мы ему неправильно помогаем, обозвал всех мудаками и исчез. Так вот, у него датчик был присоединён к третьему пину, а читал он его с пятого. Ему сказали об этом, а он ответил: "поменял в скетче пятый на третий - всё равно не заработало, вернул всё как было". Я помню, долго не мог дыхание восстановить после такого ответа.
Не стоит неудачный опыт переносить на всех нуждающихся в помощи.
Т.к. с ошибкой справится не могу, решил начать с упрощения обработчика прерываний.
А так же сборки всего алгоритма с начала. Все было замечательно пока в алгоритме не было конструкции switch-case. Вернее сказать, до того момента, пока в одном case не использовалось два оператора if :
Т.е. если обновлять дисплей только факту смены режима или только по факту новых данных с датчиков все работает. Но стоит использовать оба условия, работа останавливается...
Понимаю что вникать в мою портянку вряд ли кто-то станет, но все же:
Таймеры, много: https://github.com/DetSimen/Arduino_TimerList
Спасибо!
метод решения проблемы "на коленке" - обновляйте экран за один цикл ЛУП только или по режиму, или по новым показаниям. У вас же режим, наверно. будет меняться редко - ну в этих случаях можно смирится с тем. что показания на жкране обновятся с задержкой.
метод решения проблемы "на коленке" - обновляйте экран за один цикл ЛУП только или по режиму, или по новым показаниям. У вас же режим, наверно. будет меняться редко - ну в этих случаях можно смирится с тем. что показания на жкране обновятся с задержкой.
Конечно я бы мог так сделать. Но это надеюсь не последнее мое творение на ардуино. И хочется понять, в чем суть проблемы. Хотя суть ясна - знаний не достаточно... А вот где ошибка - не ясно...
А мне вот ясно где ошибка - слишком резкий старт.
Я, в таких случаях, оставляю в лупе только вывод в сериал переменных, которые в обработчике дёргаются и начинаю с ними воевать. А потом уже мясо в виде LCD и прочей хренотени наращиваю.
А мне вот ясно где ошибка - слишком резкий старт.
Я, в таких случаях, оставляю в лупе только вывод в сериал переменных, которые в обработчике дёргаются и начинаю с ними воевать. А потом уже мясо в виде LCD и прочей хренотени наращиваю.
Обработчик работает исправно. Дело видимо не в прерываниях, как я изначально думал.
В общем я вернулся к первоначальному своему посту...
Если в обработке факта нажатия кнопки в цикле ЛУП, выводить в Сериал значение flagUpdateMode:
То программа полностью функционирует без замечаний, т.е. обновление дисплея происходит как по факту новых данных с датчиков, так и по факту смены режима работы... Стоит убрать вывод переменной flagUpdateMode в сериал, все "ломается". Это 3,14ЗДЕЦ какой-то... Как так-то?! Пойду посплю, ну на хрен!
А чё такого - нормальная практика. Для начала, поставьте вместо вывода задержку (delay), скажем миллисекунд на 100-200. Посмотрите помогло ли (и мне скажите). Если помогло, то понятно, что-то не успевает отработать, в Ваша печать как раз даёт нужное время.
И ещё, Вы столько уже всего накуролесили (то есть у Вас volatile, то нету), поставьте volatile на место (вместе с задержкой, о которой я говорил выше), проверьте, напишите результат и выложите текущий скетч прямо копипастом из иде, чтобы не было такого - я полчаса разбираюсь, что-то Вам пишу про строку, а Вы такой - да я эту строку ещё вчера выбросил.
Давайте, делайте.
Работает, зараза, с delay()...
Ну, вот, значит проблема в задержке. Что-то не успевает. Сейчас посмотрим, что может не успевать.
Ну, примерно, понятно. Вам ведь давно говорили, что
Но Вы это игнорируете. Переменная button1 изменяется в обработчике прерывания, но при этом не volatile ни грамма. Я не знаю как устроен объект Button, потому не могу сказать, достаточно ли просто объявить её volatile в строке 15 (можете попробовать), но если не поможет, то надо будет смотреть на объект Button.
И ещё, Вам также говорили, что
но Вы и это игнорируете. Вы не понимаете этих слов? Или считаете их глупостью?
Попытайтесь объявить button1 как volatile. Хотя, на самом деле, уж лучше сделать правильно, примерно так:
А не "гуверовская" ли кнопка туда приделана?
Слов по поводу атомарного копирования реально не понимаю... И уж тем более глупостью не считаю. Про кнопку, просто не подумал, т.к. сам флаг button1.flagClick работает исправно. А ошибка возникает не в ходе обработки прерывания, а в ходе выполнения цикла loop().
Объявление Button через volatile не помогло.
Button.h :
Button.cpp :
Место ли такой кнопке в прерывании... Засадите-ка
button1.scanState();
в loop()Ну, не помогло, значит надо в класс лезть. Но это не самая систематическая практика. Посмотрите #19 я там дописал как это правильно делается.
Вставьте в свой код и, если не поможет, выкладывайте то, что получилось с комментариями о том, какие эксперименты пробовали.
Место ли такой кнопке в прерывании... Засадите-ка
button1.scanState();
в loop()Спасибо! Работает исправно.
Буду рад пинку в направлении полезной информации по поводу прерываний. В основном все заканчивается volatile. И все на этом. Хотя это не обязательно - в гугле на забанили пока.
Вечером поэкспериментирую - обед закончился...
Место ли такой кнопке в прерывании... Засадите-ка
button1.scanState();
в loop()Спасибо! Работает исправно.
Надеюсь, Вы его не тупо запихали в луп, а как я писал выше? Код бы показали.
Запихивание в loop() работает, но в контексте - разобраться в тонкостях меня не устраивает. По-этому буду вникать в Ваш метод.
Запихивание в loop() работае
Т.е., тупо - вообще без учёта времени (да, приведите ж Вы код, наконец). Если без времени, то боюсь, что работает криво, т.к. Вы убили этим свою библиотеку кнопки, у неё вроде анитдребезг забит на то, что scan вызывается с разумными интервалами, а не когда попало.
Да, именно тупо без учета времени и с неопределенным периодом антидребезга.
Что дает строка ? Почему timeToScan объявляем, как const?
const
bool
timeToScan = timerTickDetected ;
Что дает строка ?
const
bool
timeToScan = timerTickDetected ;
Запоминает текущее значение timerTickDetected, т.к. мы затрём его в следующей строке, а оно нам ещё пригодится для определения надо или не надо сканить кнопку и инкрементировать счётчик.
Почему timeToScan объявляем, как const?
Потому, что мы не собираемся её изменять.
Да, именно тупо без учета времени и с неопределенным периодом антидребезга.
Глянул код. timerMeas по-прежнему читается "неатомарно", т.е. грабли лежат и ждут незадачливого прохожего. Делайте, как я написал - так Вы себе замедленную мину закладываете.
Если я все правильно понял, то button.scanState() мы будем вызывать один раз за период loop? Т.е. также с не очень понятным периодом антидребезга?
Если я все правильно понял, ...
Неправильно понял. См. #19 - строки №№ 14 и 15 выполняются только тогда, когда сработал таймер. И не выполняются тогда, когда он не срабатывал.
И кстати, заметьте, что timerMeas больше не изменяется в обработчике прерывания, значит ему не нужно быть волатильным.
Если я все правильно понял, ...
Неправильно понял. См. #19 - строки №№ 14 и 15 выполняются только тогда, когда сработал таймер. И не выполняются тогда, когда он не срабатывал.
И кстати, заметьте, что timerMeas больше не изменяется в обработчике прерывания, значит ему не нужно быть волатильным.
Неправильно сформулировал вопрос - строки 14 и 15 выполняются только тогда, когда сработал таймер, но не чаще 1 раза за итерацию loop(). Т.о. если выполнение loop() займет более 2 мс, то и период вызова scanState() тоже изменится. А выполнятся loop() может дольше 2мс, например когда вызовем функцию measureTHP().
Предложенный вариант работает исправно, кроме периодически "подтупливающей" кнопки.
Ну, или возвращайтесь к исходному варианту и правьте класс Button, чтобы там было волатильным всё, чему нужно быть волатильным.
А можно, чуть подробнее про блокирующий/неблокирующий вызов функции?
А можно, чуть подробнее про блокирующий/неблокирующий вызов функции?
А можно, чуть подробнее про блокирующий/неблокирующий вызов функции?
Ну, чего тут подробнее. Большинство библиотек, которыми Вы пользуетесь, написаны не разработчиками автоматики, а разработчиками конкретных устройств. И цель их продемонстрировать работоспособность устройства и не более того.
Вот, например, Вы пользуетесь библиотекой DHT. Откройте файл DHT.cpp и найдите там функцию boolean DHT::read(bool force) (она вызывается из readTemperature и их readHumidity). Смотрим на неё и что видим (я вставил комментарий "!!!!!" в важных для нас местах:
Итого, эта функция в худшем случае может Вам дать до 350 мс задержки (это я ещё не учитывал копейки типа delayMicroseconds). В лучшем же случае она Вам даст 270мс с копейками.
Вы же у себя в measureTHP вызываете её 4 раза. Итого, в худшем случае задержки составят 1,4 секунды, а в самом лучшем - 1,08 секунды.
Далее, там же в measureTHP Вы вызываете несколько функций из библиотеки Adafruit_BMP085 (я не буду приводить текст библиотеки, сами смотрите, я только времена задержек приведу). Смотрим на неё. Функции, которые вызываете: readTemperature (delay - 5мс), readPressure (delay - до 26 мс), readAltitude два раза (delay 26 мс в каждой), readSealevelPressure (ещё 26 мс)
Итого, имеем, что вызов всех этих библиотечных функций обходится Вам в 1,1 - 1,5 секунды тупых delay'ев. Ну, и какие кнопки? Как они, по-вашему, должны работать?
Поймите, целью авторов этих библиотек было продемонстрировать работоспособность их девайсов, а не чтобы у Вас кнопки работали. Им нужен delay - они его ставят и плевать им на Ваши кнопки.
Что делать?
1. Обходиться без таких библиотек (искать другие или писать самому, что обычно и делают те, кто умеет писать)
2. Сажать кнопки на прерывания (но тут свои грабли - см. ниже)
3. Терпеть.
4. Придумать пользовательский интерфейс, в котором это некритично.
По поводу п 2. - не всё так просто. Ну, хорошо отловили Вы нажатие кнопки прерыванием прямо во время измерения. И что делать? Прерывать измерение и плевать на него? Так на это опять же библиотеки не заточены - не умеют они прерываться на полпути.
По п.4. мой внук как-о выдал такое решение. При нажатии на кнопку (он её через прерывание ловил) нужная команда исполнялась не сразу, а сначала начинала "разбег" дорожка из светодиодов. А команда исполнялась после этого светового шоу. Он подавал это как крутую фичу, но на деле замаскировал задержку :-)
В некоторую защиту авторов библиотек могу написать, что ряд сенсоров критичны ко временным задержкам и, если окажется так, что пользователь библиотеки не вернул назад управление в функцию, которая применяя метод конечных автоматов, работает с таковым сенсором, то сенсор отдаёт фигу с маслом.
Так что авторы оказываются меж двух огней - между теми, у кого delay в лупе и теми, кто хочет "многозадачности".
Большое спасибо, за подробные разъяснения!
Еще один вопрос:
Это и есть атомарное изменение переменной? Т.е. при запрете прерываний на время ее записи?
Упс, тоже умной каши хочу. Последнее время, ток и слышу атомарность, атомарность, слушал я Вашего Карузо, ой атомарность, вывел для себя, что проблема существует для данных превышающих разрядность МК, но в данном примере вижу булеан, который равен для Ардуины байту и его разрядности. Что и где не так, для чего сие нужно?
Рассматривая пример:
Мы можем увидеть, что прерывание потенциально может произойти между операциями, в момент Хэ и обработчик установит timerTickDetected в true. После возврата из обработчика выполнится следующая операция и timerTickDetected перевернётся в false. Таким образом - новое значение timerTickDetected будет утеряно.
В данном конкретном случае, ничего страшного произойти не должно (будет небольшой сбой в Матрице), однако если из прерывания в переменной будут прилетать разные цифири (например - кол-во кредитов 0...255 к зачислению на карту "Вселенная"), то существует неиллюзорный шанс немножечко нарушить финансовую дисциплину.
Упс, тоже умной каши хочу. Последнее время, ток и слышу атомарность, атомарность, слушал я Вашего Карузо, ой атомарность, вывел для себя, что проблема существует для данных превышающих разрядность МК, но в данном примере вижу булеан, который равен для Ардуины байту и его разрядности. Что и где не так, для чего сие нужно?
А ежели не булеан? А ежели несколько булеан? Фишка в том, что раз мы в loop проверяем чего-то там (необязательно тупые флаги), изменяющееся в прерывании, то мы должны гарантировать, что по центру наших проверок нас не прервёт прерывание и не изменит в этих переменных чего-либо. Отсюда - требование атомарности, т.е. гарантий, что мы в конкретной точке следования работаем с неизменяемыми данными. Проще всего это сделать - быренько выключив прерывания, скопировав данные в локальные переменные, и быренько включив прерывания.
Есть ещё atomic.h - там есть разные макросы. Короче - вот: http://microsin.net/programming/avr/avr-gcc-atomically-and-non-atomically-executed-code-blocks.html
ОК, благодарствую, осознал.
Спасибо за разъяснения! Ушел учиться.
В некоторую защиту авторов библиотек могу написать, что ряд сенсоров критичны ко временным задержкам
Ну, если бы мне это сказали авторы данной библиотеки, я бы ответил им словами Л.Н. Толстого из письма Ф.А. Желтову: "До Вас это не относится" :-)
Лев Николаевич - он же поручиком артиллерии был, а артиллеристский офицер - он зря не скажет, посмотрите на нашего Ворота :-)