Прерывания по таймеру и переменные.
- Войдите на сайт для отправки комментариев
Доброго времени суток, форумчане!
Если честно, то не понимаю, как правильно задать вопрос... Проблема следующая: Есть проект метеостанции. В качестве задающей временные интервалы функции(период измерений, обновления ЛСД и т.д.), использован таймер прерываний. Столкнулся с такой проблемой:
В алгоритме по факту нажатия кнопки: 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, не помогло.
А зачем Вы их "взад" разобъявили?
Вы мне напомнили одного деятеля, заходил сюда как-то, но решил, что мы ему неправильно помогаем, обозвал всех мудаками и исчез. Так вот, у него датчик был присоединён к третьему пину, а читал он его с пятого. Ему сказали об этом, а он ответил: "поменял в скетче пятый на третий - всё равно не заработало, вернул всё как было". Я помню, долго не мог дыхание восстановить после такого ответа.
Не стоит неудачный опыт переносить на всех нуждающихся в помощи.
Т.к. с ошибкой справится не могу, решил начать с упрощения обработчика прерываний.
//------Обработчик прерываний------// void timerInterupt() { button1.scanState(); //проверяем нажатие кнопки timerMeas++; //инкремент таймера измерений }А так же сборки всего алгоритма с начала. Все было замечательно пока в алгоритме не было конструкции switch-case. Вернее сказать, до того момента, пока в одном case не использовалось два оператора if :
//!!!!!!!Режим работы!!!!!!! switch (mode) { //режим по умолчанию(показания уличные) case 0: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdOutdoorTHP(); } // if (flagUpdateLCD == true) { //обновить дисплей по факту получения новых измерений // flagUpdateLCD = false; // updateLcdOutdoorTHP(); // } break; case 1: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdIndoorTH(); } // if (flagUpdateLCD == true) {//обновить дисплей по факту получения новых измерений // flagUpdateLCD = false; // updateLcdIndoorTH(); // } break; }Т.е. если обновлять дисплей только факту смены режима или только по факту новых данных с датчиков все работает. Но стоит использовать оба условия, работа останавливается...
Понимаю что вникать в мою портянку вряд ли кто-то станет, но все же:
#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; //атмосферное давление //переменные для работы с таймерами volatile unsigned int timerMeas; //таймер измерений температуры и влажности boolean flagTimerMeas = false; //флаг таймера измерений температуры и влажности volatile unsigned int timerBacklight; //таймер 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(); //запускаем датчик давления lcd.backlight(); hello(); //выводим приветствие } //------Основной цикл------// void loop() { //!!!!!!!Обработка счетчиков!!!!!!! if (timerMeas >= CYCLE_MEAS) { //проверка таймера измерений timerMeas = 0; //сброс таймера измерений measureTHP(); } //!!!!!!!Обработка кнопки!!!!!!! if (button1.flagClick == true) { //был "клик" кнопкой button1.flagClick = false; //сброс флага "клик" кнопкой flagUpdateMode = true; //установить флаг смены режима mode++; //смена режима if (mode > 1) { mode = 0; } } //!!!!!!!Режим работы!!!!!!! switch (mode) { //режим по умолчанию(показания уличные) case 0: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdOutdoorTHP(); } // if (flagUpdateLCD == true) { //обновить дисплей по факту получения новых измерений // flagUpdateLCD = false; // updateLcdOutdoorTHP(); // } break; case 1: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdIndoorTH(); } // if (flagUpdateLCD == true) {//обновить дисплей по факту получения новых измерений // flagUpdateLCD = false; // updateLcdIndoorTH(); // } break; } } //------Обработчик прерываний------// void timerInterupt() { button1.scanState(); //проверяем нажатие кнопки timerMeas++; //инкремент таймера измерений } //------Измерение параметров окр.среды------// 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); flagUpdateLCD = true; // Serial.print("flagUpdateLCD = "); // Serial.println(flagUpdateLCD); // Serial.println("measureTHP() STOP"); } //------Вывод текущих уличных значений на дисплей------// 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); // Создаем значек мс }Таймеры, много: https://github.com/DetSimen/Arduino_TimerList
Спасибо!
метод решения проблемы "на коленке" - обновляйте экран за один цикл ЛУП только или по режиму, или по новым показаниям. У вас же режим, наверно. будет меняться редко - ну в этих случаях можно смирится с тем. что показания на жкране обновятся с задержкой.
метод решения проблемы "на коленке" - обновляйте экран за один цикл ЛУП только или по режиму, или по новым показаниям. У вас же режим, наверно. будет меняться редко - ну в этих случаях можно смирится с тем. что показания на жкране обновятся с задержкой.
Конечно я бы мог так сделать. Но это надеюсь не последнее мое творение на ардуино. И хочется понять, в чем суть проблемы. Хотя суть ясна - знаний не достаточно... А вот где ошибка - не ясно...
А мне вот ясно где ошибка - слишком резкий старт.
Я, в таких случаях, оставляю в лупе только вывод в сериал переменных, которые в обработчике дёргаются и начинаю с ними воевать. А потом уже мясо в виде LCD и прочей хренотени наращиваю.
А мне вот ясно где ошибка - слишком резкий старт.
Я, в таких случаях, оставляю в лупе только вывод в сериал переменных, которые в обработчике дёргаются и начинаю с ними воевать. А потом уже мясо в виде LCD и прочей хренотени наращиваю.
Обработчик работает исправно. Дело видимо не в прерываниях, как я изначально думал.
В общем я вернулся к первоначальному своему посту...
Если в обработке факта нажатия кнопки в цикле ЛУП, выводить в Сериал значение flagUpdateMode:
//!!!!!!!Обработка кнопки!!!!!!! if (button1.flagClick == true) { //был "клик" кнопкой button1.flagClick = false; //сброс флага "клик" кнопкой flagUpdateMode = true; //установить флаг смены режима mode++; //смена режима Serial.print("flagUpdateMode = "); Serial.println(flagUpdateMode); if (mode > 1) { mode = 0; } }То программа полностью функционирует без замечаний, т.е. обновление дисплея происходит как по факту новых данных с датчиков, так и по факту смены режима работы... Стоит убрать вывод переменной flagUpdateMode в сериал, все "ломается". Это 3,14ЗДЕЦ какой-то... Как так-то?! Пойду посплю, ну на хрен!
А чё такого - нормальная практика. Для начала, поставьте вместо вывода задержку (delay), скажем миллисекунд на 100-200. Посмотрите помогло ли (и мне скажите). Если помогло, то понятно, что-то не успевает отработать, в Ваша печать как раз даёт нужное время.
И ещё, Вы столько уже всего накуролесили (то есть у Вас volatile, то нету), поставьте volatile на место (вместе с задержкой, о которой я говорил выше), проверьте, напишите результат и выложите текущий скетч прямо копипастом из иде, чтобы не было такого - я полчаса разбираюсь, что-то Вам пишу про строку, а Вы такой - да я эту строку ещё вчера выбросил.
Давайте, делайте.
Работает, зараза, с delay()...
#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; //атмосферное давление //переменные для работы с таймерами volatile unsigned int timerMeas; //таймер измерений температуры и влажности boolean flagTimerMeas = false; //флаг таймера измерений температуры и влажности volatile unsigned int timerBacklight; //таймер 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(); //запускаем датчик давления lcd.backlight(); hello(); //выводим приветствие } //------Основной цикл------// void loop() { //!!!!!!!Обработка счетчиков!!!!!!! if (timerMeas >= CYCLE_MEAS) { //проверка таймера измерений timerMeas = 0; //сброс таймера измерений measureTHP(); // Serial.println("timerMeas"); } //!!!!!!!Обработка кнопки!!!!!!! if (button1.flagClick == true) { //был "клик" кнопкой button1.flagClick = false; //сброс флага "клик" кнопкой flagUpdateMode = true; //установить флаг смены режима mode++; //смена режима // Serial.print("flagUpdateMode = "); // Serial.println(flagUpdateMode); delay(200); if (mode > 1) { mode = 0; } } //!!!!!!!Режим работы!!!!!!! switch (mode) { //режим по умолчанию(показания уличные) case 0: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdOutdoorTHP(); } if (flagUpdateLCD == true) { //обновить дисплей по факту получения новых измерений flagUpdateLCD = false; updateLcdOutdoorTHP(); } break; case 1: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdIndoorTH(); } if (flagUpdateLCD == true) {//обновить дисплей по факту получения новых измерений flagUpdateLCD = false; updateLcdIndoorTH(); } break; } } //------Обработчик прерываний------// void timerInterupt() { button1.scanState(); //проверяем нажатие кнопки timerMeas++; //инкремент таймера измерений } //------Измерение параметров окр.среды------// 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); flagUpdateLCD = true; // Serial.print("flagUpdateLCD = "); // Serial.println(flagUpdateLCD); // Serial.println("measureTHP() STOP"); } //------Вывод текущих уличных значений на дисплей------// 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); // Создаем значек мс }Ну, вот, значит проблема в задержке. Что-то не успевает. Сейчас посмотрим, что может не успевать.
Ну, примерно, понятно. Вам ведь давно говорили, что
Но Вы это игнорируете. Переменная button1 изменяется в обработчике прерывания, но при этом не volatile ни грамма. Я не знаю как устроен объект Button, потому не могу сказать, достаточно ли просто объявить её volatile в строке 15 (можете попробовать), но если не поможет, то надо будет смотреть на объект Button.
И ещё, Вам также говорили, что
но Вы и это игнорируете. Вы не понимаете этих слов? Или считаете их глупостью?
Попытайтесь объявить button1 как volatile. Хотя, на самом деле, уж лучше сделать правильно, примерно так:
... volatile bool timerTickDetected = false; ... // в обработчике прерывания ТОЛЬКО timerTickDetected = true; ... // в начале функции loop cli(); const bool timeToScan = timerTickDetected ; timerTickDetected = false; sei(); if (timeToScan) { button1.scanState(); //проверяем нажатие кнопки timerMeas++; //инкремент таймера измерений } ...А не "гуверовская" ли кнопка туда приделана?
Слов по поводу атомарного копирования реально не понимаю... И уж тем более глупостью не считаю. Про кнопку, просто не подумал, т.к. сам флаг button1.flagClick работает исправно. А ошибка возникает не в ходе обработки прерывания, а в ходе выполнения цикла loop().
Объявление Button через volatile не помогло.
Button.h :
// проверка, что библиотека еще не подключена #ifndef Button_h // если библиотека Button не подключена #define Button_h // тогда подключаем ее #include "Arduino.h" // класс обработки сигналов class Button { public: Button(byte pin, byte timeButton); // конструктор boolean flagPress; // признак кнопка нажата (сигнал в низком уровне) boolean flagClick; // признак клика кнопки (фронт) void scanState(); // метод проверки ожидание стабильного состояния сигнала void filterAvarage(); // метод фильтрации по среднему значению void setPinTime(byte pin, byte timeButton); // установка номера вывода и времени фильтрации private: byte _buttonCount; // счетчик времени фильтрации byte _timeButton; // время фильтрации byte _pin; // номер вывода }; #endifButton.cpp :
#include "Arduino.h" #include "Button.h" // метод фильтрации сигнала по среднему значению // при сигнале низкого уровня flagPress= true // при сигнале высокого уровня flagPress= false // при изменении состояния с высокого на низкий flagClick= true void Button::filterAvarage() { if ( flagPress != digitalRead(_pin) ) { // состояние кнопки осталось прежним if ( _buttonCount != 0 ) _buttonCount--; // счетчик подтверждений - 1 с ограничением на 0 } else { // состояние кнопки изменилось _buttonCount++; // +1 к счетчику подтверждений if ( _buttonCount >= _timeButton ) { // состояние сигнала достигло порога _timeButton flagPress= ! flagPress; // инверсия признака состояния _buttonCount= 0; // сброс счетчика подтверждений if ( flagPress == true ) flagClick= true; // признак клика кнопки } } } // метод проверки ожидание стабильного состояния сигнала // при нажатой кнопке flagPress= true // при отжатой кнопке flagPress= false // при нажатии на кнопку flagClick= true void Button::scanState() { if ( flagPress != digitalRead(_pin) ) { // признак flagPress = текущему состоянию кнопки // (инверсия т.к. активное состояние кнопки LOW) // т.е. состояние кнопки осталось прежним _buttonCount= 0; // сброс счетчика подтверждений состояния кнопки } else { // признак flagPress не = текущему состоянию кнопки // состояние кнопки изменилось _buttonCount++; // +1 к счетчику состояния кнопки if ( _buttonCount >= _timeButton ) { // состояние кнопки не мянялось в течение заданного времени // состояние кнопки стало устойчивым flagPress= ! flagPress; // инверсия признака состояния _buttonCount= 0; // сброс счетчика подтверждений состояния кнопки if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие } } } // метод установки номера вывода и времени подтверждения void Button::setPinTime(byte pin, byte timeButton) { _pin= pin; _timeButton= timeButton; pinMode(_pin, INPUT_PULLUP); // определяем вывод как вход } // описание конструктора класса Button Button::Button(byte pin, byte timeButton) { _pin= pin; _timeButton= timeButton; pinMode(_pin, INPUT_PULLUP); // определяем вывод как вход }Место ли такой кнопке в прерывании... Засадите-ка
button1.scanState();в loop()Ну, не помогло, значит надо в класс лезть. Но это не самая систематическая практика. Посмотрите #19 я там дописал как это правильно делается.
Вставьте в свой код и, если не поможет, выкладывайте то, что получилось с комментариями о том, какие эксперименты пробовали.
Место ли такой кнопке в прерывании... Засадите-ка
button1.scanState();в loop()Спасибо! Работает исправно.
Буду рад пинку в направлении полезной информации по поводу прерываний. В основном все заканчивается volatile. И все на этом. Хотя это не обязательно - в гугле на забанили пока.
Вечером поэкспериментирую - обед закончился...
Место ли такой кнопке в прерывании... Засадите-ка
button1.scanState();в loop()Спасибо! Работает исправно.
Надеюсь, Вы его не тупо запихали в луп, а как я писал выше? Код бы показали.
Запихивание в loop() работает, но в контексте - разобраться в тонкостях меня не устраивает. По-этому буду вникать в Ваш метод.
Запихивание в loop() работае
Т.е., тупо - вообще без учёта времени (да, приведите ж Вы код, наконец). Если без времени, то боюсь, что работает криво, т.к. Вы убили этим свою библиотеку кнопки, у неё вроде анитдребезг забит на то, что scan вызывается с разумными интервалами, а не когда попало.
Да, именно тупо без учета времени и с неопределенным периодом антидребезга.
#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; //атмосферное давление //переменные для работы с таймерами volatile unsigned int timerMeas; //таймер измерений температуры и влажности boolean flagTimerMeas = false; //флаг таймера измерений температуры и влажности volatile unsigned int timerBacklight; //таймер 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(); //запускаем датчик давления lcd.backlight(); hello(); //выводим приветствие } //------Основной цикл------// void loop() { button1.scanState(); //!!!!!!!Обработка счетчиков!!!!!!! if (timerMeas >= CYCLE_MEAS) { //проверка таймера измерений timerMeas = 0; //сброс таймера измерений measureTHP(); // Serial.println("timerMeas"); } //!!!!!!!Обработка кнопки!!!!!!! if (button1.flagClick == true) { //был "клик" кнопкой button1.flagClick = false; //сброс флага "клик" кнопкой flagUpdateMode = true; //установить флаг смены режима mode++; //смена режима // Serial.print("flagUpdateMode = "); // Serial.println(flagUpdateMode); // delay(200); if (mode > 1) { mode = 0; } } //!!!!!!!Режим работы!!!!!!! switch (mode) { //режим по умолчанию(показания уличные) case 0: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdOutdoorTHP(); } if (flagUpdateLCD == true) { //обновить дисплей по факту получения новых измерений flagUpdateLCD = false; updateLcdOutdoorTHP(); } break; case 1: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdIndoorTH(); } if (flagUpdateLCD == true) {//обновить дисплей по факту получения новых измерений flagUpdateLCD = false; updateLcdIndoorTH(); } break; } } //------Обработчик прерываний------// void timerInterupt() { // button1.scanState(); //проверяем нажатие кнопки timerMeas++; //инкремент таймера измерений } //------Измерение параметров окр.среды------// 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); flagUpdateLCD = true; // Serial.print("flagUpdateLCD = "); // Serial.println(flagUpdateLCD); // Serial.println("measureTHP() STOP"); } //------Вывод текущих уличных значений на дисплей------// 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); // Создаем значек мс }Что дает строка ? Почему timeToScan объявляем, как const?
constbooltimeToScan = timerTickDetected ;Что дает строка ?
constbooltimeToScan = 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().
Предложенный вариант работает исправно, кроме периодически "подтупливающей" кнопки.
#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; //таймер lcd подсветки boolean flagTimerBacklight = true; //флаг таймера lcd подсветки //переменные основного цикла byte mode; //перменная определяющая режим работы boolean flagUpdateMode; //флаг смены режима boolean flagUpdateLCD; //флаг обновления дисплея volatile bool timerTickDetected = false; //------Создаем нужные символы для дисплея------// 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(); //запускаем датчик давления lcd.backlight(); hello(); //выводим приветствие } //------Основной цикл------// void loop() { cli(); const bool timeToScan = timerTickDetected; timerTickDetected = false; sei(); if (timeToScan) { button1.scanState(); timerMeas++; } //!!!!!!!Обработка счетчиков!!!!!!! if (timerMeas >= CYCLE_MEAS) { //проверка таймера измерений timerMeas = 0; //сброс таймера измерений measureTHP(); // Serial.println("timerMeas"); } //!!!!!!!Обработка кнопки!!!!!!! if (button1.flagClick == true) { //был "клик" кнопкой button1.flagClick = false; //сброс флага "клик" кнопкой flagUpdateMode = true; //установить флаг смены режима mode++; //смена режима // Serial.print("flagUpdateMode = "); // Serial.println(flagUpdateMode); // delay(200); if (mode > 1) { mode = 0; } } //!!!!!!!Режим работы!!!!!!! switch (mode) { //режим по умолчанию(показания уличные) case 0: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdOutdoorTHP(); } if (flagUpdateLCD == true) { //обновить дисплей по факту получения новых измерений flagUpdateLCD = false; updateLcdOutdoorTHP(); } break; case 1: if (flagUpdateMode == true) { //обновить дисплей по факту смены режима flagUpdateMode = false; clearLCD(); updateLcdIndoorTH(); } if (flagUpdateLCD == true) {//обновить дисплей по факту получения новых измерений flagUpdateLCD = false; updateLcdIndoorTH(); } break; } } //------Обработчик прерываний------// void timerInterupt() { // button1.scanState(); //проверяем нажатие кнопки // timerMeas++; //инкремент таймера измерений timerTickDetected = true; } //------Измерение параметров окр.среды------// 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); flagUpdateLCD = true; // Serial.print("flagUpdateLCD = "); // Serial.println(flagUpdateLCD); // Serial.println("measureTHP() STOP"); } //------Вывод текущих уличных значений на дисплей------// 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); // Создаем значек мс }Ну, или возвращайтесь к исходному варианту и правьте класс Button, чтобы там было волатильным всё, чему нужно быть волатильным.
А можно, чуть подробнее про блокирующий/неблокирующий вызов функции?
А можно, чуть подробнее про блокирующий/неблокирующий вызов функции?
А можно, чуть подробнее про блокирующий/неблокирующий вызов функции?
Ну, чего тут подробнее. Большинство библиотек, которыми Вы пользуетесь, написаны не разработчиками автоматики, а разработчиками конкретных устройств. И цель их продемонстрировать работоспособность устройства и не более того.
Вот, например, Вы пользуетесь библиотекой DHT. Откройте файл DHT.cpp и найдите там функцию boolean DHT::read(bool force) (она вызывается из readTemperature и их readHumidity). Смотрим на неё и что видим (я вставил комментарий "!!!!!" в важных для нас местах:
boolean DHT::read(bool force) { // Check if sensor was read less than two seconds ago and return early // to use last reading. uint32_t currenttime = millis(); if (!force && ((currenttime - _lastreadtime) < 2000)) { return _lastresult; // return last correct measurement } _lastreadtime = currenttime; // Reset 40 bits of received data to zero. data[0] = data[1] = data[2] = data[3] = data[4] = 0; // Send start signal. See DHT datasheet for full signal diagram: // http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf // Go into high impedence state to let pull-up raise data line level and // start the reading process. digitalWrite(_pin, HIGH); delay(250); // 250 мс (!!!!!) // First set data line low for 20 milliseconds. pinMode(_pin, OUTPUT); digitalWrite(_pin, LOW); delay(20); // 20 мс (!!!!!) uint32_t cycles[80]; { // Turn off interrupts temporarily because the next sections are timing critical // and we don't want any interruptions. InterruptLock lock; // End the start signal by setting data line high for 40 microseconds. digitalWrite(_pin, HIGH); delayMicroseconds(40); // Now start reading the data line to get the value from the DHT sensor. pinMode(_pin, INPUT_PULLUP); delayMicroseconds(10); // Delay a bit to let sensor pull data line low. // First expect a low signal for ~80 microseconds followed by a high signal // for ~80 microseconds again. if (expectPulse(LOW) == 0) { DEBUG_PRINTLN(F("Timeout waiting for start signal low pulse.")); _lastresult = false; return _lastresult; } if (expectPulse(HIGH) == 0) { DEBUG_PRINTLN(F("Timeout waiting for start signal high pulse.")); _lastresult = false; return _lastresult; } // Now read the 40 bits sent by the sensor. Each bit is sent as a 50 // microsecond low pulse followed by a variable length high pulse. If the // high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds // then it's a 1. We measure the cycle count of the initial 50us low pulse // and use that to compare to the cycle count of the high pulse to determine // if the bit is a 0 (high state cycle count < low state cycle count), or a // 1 (high state cycle count > low state cycle count). Note that for speed all // the pulses are read into a array and then examined in a later step. for (int i=0; i<80; i+=2) { // Итого 40*2 - до 80 мс (!!!!!) cycles[i] = expectPulse(LOW); // До 1 мс (!!!!!) cycles[i+1] = expectPulse(HIGH); // До 1 мс (!!!!!) } } // Timing critical code is now complete. // Inspect pulses and determine which ones are 0 (high state cycle count < low // state cycle count), or 1 (high state cycle count > low state cycle count). for (int i=0; i<40; ++i) { uint32_t lowCycles = cycles[2*i]; uint32_t highCycles = cycles[2*i+1]; if ((lowCycles == 0) || (highCycles == 0)) { DEBUG_PRINTLN(F("Timeout waiting for pulse.")); _lastresult = false; return _lastresult; } data[i/8] <<= 1; // Now compare the low and high cycle times to see if the bit is a 0 or 1. if (highCycles > lowCycles) { // High cycles are greater than 50us low cycle count, must be a 1. data[i/8] |= 1; } // Else high cycles are less than (or equal to, a weird case) the 50us low // cycle count so this must be a zero. Nothing needs to be changed in the // stored data. } DEBUG_PRINTLN(F("Received:")); DEBUG_PRINT(data[0], HEX); DEBUG_PRINT(F(", ")); DEBUG_PRINT(data[1], HEX); DEBUG_PRINT(F(", ")); DEBUG_PRINT(data[2], HEX); DEBUG_PRINT(F(", ")); DEBUG_PRINT(data[3], HEX); DEBUG_PRINT(F(", ")); DEBUG_PRINT(data[4], HEX); DEBUG_PRINT(F(" =? ")); DEBUG_PRINTLN((data[0] + data[1] + data[2] + data[3]) & 0xFF, HEX); // Check we read 40 bits and that the checksum matches. if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) { _lastresult = true; return _lastresult; } else { DEBUG_PRINTLN(F("Checksum failure!")); _lastresult = false; return _lastresult; } }Итого, эта функция в худшем случае может Вам дать до 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
ОК, благодарствую, осознал.
Спасибо за разъяснения! Ушел учиться.
В некоторую защиту авторов библиотек могу написать, что ряд сенсоров критичны ко временным задержкам
Ну, если бы мне это сказали авторы данной библиотеки, я бы ответил им словами Л.Н. Толстого из письма Ф.А. Желтову: "До Вас это не относится" :-)
Лев Николаевич - он же поручиком артиллерии был, а артиллеристский офицер - он зря не скажет, посмотрите на нашего Ворота :-)