i2c display и прерывания.
- Войдите на сайт для отправки комментариев
Пт, 11/09/2015 - 20:06
Здравствуйте, уважаемые специалисты. Помогите разобраться с проблемой. Есть желание измерять мгновенное значение проходящей в трубе жидкости. Для этого приобретен прибор http://ru.aliexpress.com/item/1-x-1-30L-min-1-75MPa-Water-Flow-Sensor-Flowmeter-Hall-Flow-Water-Sensor-New/2045383642.html?spm=2114.03020208.3.29.9p7lIN&ws_ab_test=201407_2,201444_5,201409_3 К нем есть стандартный скетч с выводом данных через Serial порт.
[code]
// reading liquid flow rate using Arduino and Water Flow sensor.
volatile int Fan;//measuring the rising edges of the signal
int Calc;
int hallsensor = 2;//The pin location of the sensor
void rpm()//this is the function of that the interupt calls
{
Fan++;
}
void setup() {
pinMode(hallsensor, INPUT);
Serial.begin(9600);
attachInterrupt(0, rpm, RISING);
}
void loop() {
Fan=0;
sei();
delay(1000);
cli();
Calc = (Fan*60/5.5);//(puls frequency x 60)/5.5Q = flow rate in L/hour
Serial.print (Calc, DEC);
Serial.print (" L/hour\r\n"); //Prints "L/hour" and returnes a new line
}
[/code]
Все работает. Но хотелось бы вывести результат на lcd дисплей. Есть под рукой lcd 20 на 4 с интерфейсом i2c. Берем скетч из примеров, делаем микс из двух получаем:
[code]
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
LiquidCrystal_I2C lcd(0x27,20,4);
// reading liquid flow rate using Arduino and Water Flow sensor.
volatile int Fan;//measuring the rising edges of the signal
int Calc;
int hallsensor = 2;//The pin location of the sensor
void rpm()//this is the function of that the interupt calls
{
Fan++;
}
void setup() {
pinMode(hallsensor, INPUT);
attachInterrupt(0, rpm, RISING);
lcd.init();
lcd.backlight();
}
void loop() {
Fan=0;
sei();
delay(1000);
cli();
Calc = (Fan*60/5.5);
//lcd.clear;
lcd.print (Calc, DEC);
}
[/code]
Но данный код не работает. Если исключить из кода строки
sei();
delay(1000);
cli();
а переменной Fan присвоить значение (например 10) то на экране появляется значение, полученное при вычислении (при Fan=10 получим 109, что есть правильно).
Подскажите, в чем проблема.
Заранее благодарю.
BorisU, этот ваш скетч для замера просто как пример работы, никто в здравом уме такие программы не использует. Наиболее просто можно обойти delay по аналогии со светодиодом http://arduino.ru/tutorials/BlinkWithoutDelay
PS: вообще-то уберите cli() наверное и так заработает.
dimax, не заработало. Индикации как не было так и нет.
По поводу cli(). Если убрать это убрать, то не понятно, как программа будет работать. Логика простая. Есть датчик Холла. Нам нужно знать сколько импульсов пройдет в единицу времени. Для этого мы ставим sei() - enable interrupt, delay(1000) - считаем количество импульсов за секунду, cli() - disable interrupt. Переменная Fan больше не меняется и можно проводить вычисления. Если убрать cli(), то ничего посчитать будет нельзя, так как не будет не ясно за какое время прошло n импульсов. Как-то так.
BorisU, нет, разрешение и запрещение прерываний тут фактически не на что не влияют. Если ставить cli(), то с этого момента и до sei() переменная Fan не инкременируется. Если не стоит, то переменная конечно инкременируется, но перед паузой всё равно обнуляется. Проверьте. С LCD без cli работать должно, попробуйте после Lcd.print небольшую паузу поставить. А лучше конечно переделать всё, что б небыло секундной паузы.
PS: Вот, начертил, пробуйте:
#include <LiquidCrystal_I2C.h> #include <Wire.h> LiquidCrystal_I2C lcd(0x27,20,4); volatile uint16_t Fan;//measuring the rising edges of the signal uint16_t Calc; byte hallsensor=2;//The pin location of the sensor void rpm() { Fan++;} ISR (TIMER1_OVF_vect){Calc = (Fan*60/5.5); Fan=0; } void setup() { pinMode(hallsensor, INPUT); //Serial.begin(9600); attachInterrupt(0, rpm, RISING); TCCR1A=(1<<WGM11); //режим14 FAST PWM TCCR1B=(1<<CS12)|(1<<WGM13)|(1<<WGM12); //делить частоту CPU на 256 ICR1= (F_CPU/256)-1; // (16000000MHz /div256) -1 = 1 раз в секунду TIMSK1=(1<<TOIE1); //разрешить прерывание } void loop() { static uint16_t n; if (n!=Calc){n=Calc; lcd.print (Calc); // Serial.println (Calc); } }Здесь время считает таймер, каждую секунду выходит в прерывание, делает подсчёт, обнуляет Fan, и начинает сначала. В лупе выводит данные только если есть изменения. Никаких delay :-)
В первую мировую летчики научили обезьяну взлетать и садиться. Однажды она разбилась, потому, что в полете вспомнила, что забыла винт подергать....
Конкатенация не помогает, включайте понимание.
Dimax Вы издеваетесь? К Вам с букварём, а Вы с алгеброй :)
dimax, большое Вам спасибо за время, которое Вы уделили. Все, кто изучил матан, когда-то держал в руках букварь. В этом нет ничего особенного. Все зависит насколько дальше человек готов идти и кто ему в этом поможет. Спасибо Вам за помощь. Для меня это лишний повод заняться более глубоким изучением программирования.
По сути проблемы. Она, проблема, заключается в том, что изначально значение переменной Cacl выводится на монитор последовательного порта, но не выводится на lcd. Кусок кода с sei and cli я убирал, что бы убедиться, что проблем с lcd как таковым нет. Код выше, я уверен, более правильный с точки зрения науки, но он тоже не работает. Информация не выводиться ни на lcd, ни на монитор порта. К сожалению.
BorisU, а какая у вас плата? Скетч, который я дал -это готовая рабочая "шапка" для atmega328, я в него просто ваши исходные данные вставил. Не работать он не может)
Я тестил на меге 2560 r3. Вся проблема в выводе данных на дисплей. Дисплей девственно чист. Я, как уже ясно, еще собаку не съел на этом деле, и поэтому могу задавать глупые вопросы. Я подумал, нет ли связи между интерфейсом I2C и прерываниями?
BorisU, в обработчике I2C есть прерывание, но оно не используется в режиме мастер по-моему. Так что взаимосвязи нет, разбирайтесь, что делаете не так. Попробуйте в lcd.print вывести просто текст lcd.print("test"); например. Кстати посмотрел, мой скетч по идее совместим с мегой2560, так что должен работать. Разкомментируйте строчки, выведите в serial для проверки.
dimax, что бы все было ясно и понятно. Я взял "неправильный" скетч. Закомментил sei() и cli(). Присвоил переменной Calc=10. Загрузил в Мегу. Дисплей показал 109, что является правильным ответом. Удалил почти все, кроме библиотек. Вставил Ваш код. Загрузил в Мегу. Дисплей не показывает ничего. Закомментил все что касается lcd и раскомментил Serial. Загрузил в Мегу. Монитор порта не показывает ничего.
BorisU, когда пробывали в сериал выводить - то 3 и 23 строку закомментировали? Если lcd зависнет, то он заблокирует loop
dimax, да закомментировал все, что относиться к lcd включая библиотеки. Результат - показаний нет.
BorisU, не знаю что вам посоветовать. Попробуйте на уно. Я вчера проверил скетч, подключил вместо счётчика энкодер. Покрутил - как и следовало ожидать всё работает. Меги2560 у меня нет, но разницы по идее быть не должно.
PS: заметил ещё любопытную вещь: вы используете прерывание 0 attachInterrupt(0, rpm, RISING); а пин у вас указан 2. Но этот пин для Уно, у меги2560 INT0 на 21-ой ардуиновской ноге. Но тогда непонятно как у вас работал первый вариант без LCD.
dimax, большое спасибо за помощь. Я тестил на нано, но результат тоже отрицательный. Думаю нужно задать вопрос в аппаратной ветке. А за скетч спасибо еще раз. Я его разобрал уже процентов на 80. Там все не так сложно, но нужно еще некоторые вопросы синтаксиса разобрать.
Впервые вижу такое объявление экрана ( LiquidCrystal_I2C lcd(0x27,20,4); ).
Где lcd.begin?
dimax, есть новость. Я установил на другой компьютер среду версию 1.6.3 (на родном версия 1.5.7) и протестил. На мониторе порта все работает, на lcd нет. Результат - lcd не хочет работать ни при каких условиях. Значит нужно спрашивать о совместимости железа.
Это объявление работает. Все зависит от библиотеки, как я понимаю. Кстати, с этой библиотекой нужно lcd.init вместо lcd.begin. Экран работает с этой библиотекой.
А если экран цеплять не по i2c, то работает?
На мониторе порта все работает, на lcd нет. Результат - lcd не хочет работать ни при каких условиях. Значит нужно спрашивать о совместимости железа.
Попробуйте другую библиотеку LCD, возможно эта просто кривая или со свежими версиями IDE не совместима. У меня кстати помимо новой есть дежурная ардуино IDE 1.0.5 , и всегда проверяю на ней, если есть подозрения на несовместимость.
Да, я согласен, нужно другую библиотеку потестить. А экрана без I2C у меня нет, а выпаять - не так просто. Ног много можно перегреть, отслоиться плата. Напишу как найду подходящую библиотеку.
У меня все китайские так запускались. Либа
#include <Wire.h> #include <LCD.h> #include <LiquidCrystal_I2C.h> #define LCD_I2C_ADDR 0x27 #define BACKLIGHT 3 #define LCD_EN 2 #define LCD_RW 1 #define LCD_RS 0 #define LCD_D4 4 #define LCD_D5 5 #define LCD_D6 6 #define LCD_D7 7 LiquidCrystal_I2C lcd(LCD_I2C_ADDR,LCD_EN,LCD_RW,LCD_RS,LCD_D4,LCD_D5,LCD_D6,LCD_D7); void setup() { lcd.begin(20,4); lcd.setBacklightPin(BACKLIGHT,POSITIVE); lcd.setBacklight(HIGH); lcd.clear(); }Доброго аремени суток уважаемый ALL!
Есть проблема с работой дисплея 1602 подключенного к atmege 328 по i2c при обработке прерывания.
при попытке вывести сообщение на экран при обработке прерывания работа устройства замерзает.
вот текст проги
[code] #include <Wire.h>; #include <math.h>; #include <LiquidCrystal_I2C.h>; LiquidCrystal_I2C lcd(0x3F, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display //====================================================================================================================== // Переменные //====================================================================================================================== float davlenie = 0; float Hdav = 0.5; //Максимальное давление в системе, давление отключения насоса mPa float Ldav = 0.3; //Минимальное давление в системе, давление включения насоса mPa float Adav = 0.6; //Давление возникающее при аврии системы mPa int water; //Уровень воды в колонне int state = HIGH; int pumpState = 7; // флаг состояния насоса int tempState; // флаг состояния температуры //====================================================================================================================== // кодируем дополнительные символы //====================================================================================================================== byte degree[8] = // кодируем символ градуса { B11100, B10100, B11100, B00000, B00000, B00000, B00000, }; byte gradusnik[8] = // кодируем символ градусника { B00100, B01010, B01010, B01110, B11111, B11111, B01110, }; //====================================================================================================================== void setup() { lcd.init(); // initialize the lcd lcd.init(); lcd.backlight(); lcd.print(" WATER MAN 0.1 "); lcd.createChar(1, degree); // Создаем символ под номером 1 lcd.createChar(2, gradusnik); // Создаем символ под номером 2 lcd.setCursor(0, 1); for (int i = 0; i < 16; i++) { lcd.print("_"); delay(200); } pinMode(13, OUTPUT); //порт управления насосом pinMode(12, OUTPUT); //порт индикатора аварии pinMode(11, OUTPUT); //порт индикатора низкой температуры pinMode(10, OUTPUT); //порт управления электромагнитным кдапаном pinMode(9, OUTPUT); //порт управления водозависимым устройством pinMode(2, INPUT_PULLUP); //Порт аварийной кнопки pinMode(A0, INPUT) ; //инициализируем датчик давления pinMode(A1, INPUT) ; //инициализируем терморезистор в кесоне pinMode(A2, INPUT) ; //инициализируем терморезистор в бойлерной pinMode(A3, INPUT) ; //инициализируем датчик сухого хода delay (1000); lcd.clear(); //digitalWrite(9, HIGH); attachInterrupt(0, Hend_Stop, LOW); //Вызов прерывания по нажатию кнопки STOP } //====================================================================================================================== // создаем метод для перевода показаний сенсора в градусы Цельсия //====================================================================================================================== double Getterm(int RawADC) { double temp; temp = log(((10240000 / RawADC) - 10000)); temp = 1 / (0.001129148 + (0.000234125 * temp) + (0.0000000876741 * temp * temp * temp)); temp = temp - 273.15; return temp; } void loop() { water = analogRead(A3); // читаем датчик сухого хода davlenie = analogRead(A0); // читаем датчик давления в системе davlenie = map(davlenie, 531, 1023, 0, 85); // приводим значения давления к читаемым davlenie = davlenie / 100; //====================================================================================================================== // Модуль управления насосом //====================================================================================================================== if (water <= 100) { if ((Getterm(analogRead(A1))) >= 1 && (Getterm(analogRead(A2))) >= 1) { digitalWrite(10, LOW); digitalWrite(11, LOW); tempState = 1; //Флаг включения водозависимых устройств if (davlenie >= 0.6) { digitalWrite(13, LOW); //digitalWrite(9, LOW); pumpState = 1; //Флаг аварии digitalWrite(12, state); lcd.display(); state = !state; } if (davlenie >= 0.5) { digitalWrite(13, LOW); digitalWrite(12, LOW); pumpState = 2; //Флаг насос остановлен } if (davlenie <= 0.3) { digitalWrite(13, HIGH); digitalWrite(12, LOW); pumpState = 3; //Флаг набор давления насос включен } if (davlenie <= 0.1) { digitalWrite(13, HIGH); digitalWrite(12, HIGH); pumpState = 4; //Флаг насос не запустился digitalWrite(12, state); state = !state; } } else { digitalWrite(13, LOW); digitalWrite(12, state); digitalWrite(9, LOW); digitalWrite(11, HIGH); if (davlenie >= 0) { digitalWrite(10, HIGH); } else { digitalWrite(10, LOW); } state = !state; pumpState = 5; //Флаг низкая температура запуск насоса не возможен, сливной клапан открыт, риск размораживания системы tempState = 0; } } else { //digitalWrite(13, LOW); digitalWrite(12, HIGH); state = !state; digitalWrite(9, LOW); pumpState = 6; //Флаг cухой ход, запуск насоса не возможен риск поломки насоса } //===================================================================================================================== // Модуль управления дополнительным водозависимым оборудованием //===================================================================================================================== if (davlenie >= 0.25 && tempState == 1) { digitalWrite(9, HIGH); } else { digitalWrite(9, LOW); } pump_print(pumpState); delay(500); } void pump_print(byte pumpFlag) { //==================================================================================================================== // Модуль вывода на экран верхней строки //==================================================================================================================== if (pumpFlag != 5 || pumpFlag != 6) { lcd.setCursor(0, 0); lcd.print("\2"); // Выводим:Temperature: lcd.setCursor(1, 0); lcd.print (round(Getterm(analogRead(A1)))); // Выводим: температуру lcd.print("\1"); // Выводим: *C lcd.setCursor(5, 0); lcd.print("\2"); // Выводим:Temperature: lcd.setCursor(6, 0); lcd.print(" "); lcd.setCursor(6, 0); lcd.print (round(Getterm(analogRead(A2)))); // Выводим: температуру lcd.print("\1"); // Выводим: *C lcd.setCursor(10, 0); lcd.print("P"); lcd.print(davlenie * 10); // Выводим давление lcd.setCursor(14, 0); lcd.print(" "); lcd.setCursor(14, 0); lcd.print("aT"); } //==================================================================================================================== // Модуль вывода на экран нижней строки //==================================================================================================================== if (pumpFlag == 1) { lcd.setCursor(0, 1); lcd.print(" PUMP FAILURE "); lcd.noDisplay(); delay(500); lcd.display(); } if (pumpFlag == 2) { lcd.setCursor(0, 1); lcd.print("THE PUMP IS STOP"); } if (pumpFlag == 3) { lcd.setCursor(0, 1); lcd.print(" THE PUMP WORKS "); } if (pumpFlag == 4) { lcd.setCursor(0, 1); lcd.print(" PUMP NOT RUN "); lcd.noDisplay(); delay(500); lcd.display(); } if (pumpFlag == 7) { lcd.setCursor(0, 1); lcd.print(" SYSTEM TEST "); } //==================================================================================================================== // Модуль вывода на экран аварийного состояния //==================================================================================================================== if (pumpFlag == 5) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(" SYSTEM HALTED! "); lcd.setCursor(0, 1); lcd.print("TEMPERATURE LOW"); lcd.noDisplay(); delay(500); lcd.display(); } if (pumpFlag == 6) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(" SYSTEM HALTED! "); lcd.setCursor(0, 1); lcd.print("WATER LEWEL LOW"); lcd.noDisplay(); delay(500); lcd.display(); } pumpState = 0; pumpFlag = 0; } void Hend_Stop() { digitalWrite(13, LOW); digitalWrite(12, HIGH); digitalWrite(9, LOW); davlenie = analogRead(A0); // читаем датчик давления в системе davlenie = map(davlenie, 531, 1023, 0, 85); // приводим значения давления к читаемым davlenie = davlenie / 100; if (davlenie >= 0) { digitalWrite(10, HIGH); } else { digitalWrite(10, LOW); } lcd.clear(); lcd.setCursor(0, 0); lcd.write(" SYSTEM HALTED! "); lcd.setCursor(0, 1); lcd.write("PRESS STOP"); } [/code]подскажите как победить этот трабл.
сильно не пинайте ламера.
P.S. при коментировании строк обращения к экрану в обработчике прерывания все работает нормально.
Ну, значит какой вывод? Нефиг в ISR пихать все подряд. Выставили там флаг и в главном цикле написали на LCD, если флаг выставлен.
Так и должно быть. Эта библиотека слишком медленно работает с экраном. Тут есть относительно скоростной драйвер, но и он шустрее 95-125мксек на символ не работает. Примерно 1.58-2.0 миллисекунды на строку в 16 символов - это "самое шустрое" что можно предложить. Чтобы работать неблокирующе с экраном надо преобразовать ваш код и библиотеку (или "шустрый" драйвер) так, чтобы пока экран занят отрисовкой можно было бы что-нибудь делать полезное.
В общем, сейчас немного занят, но в планы себе поставил.
дело в том, что прога должна стоять в этом режиме пока не отпустится кнопка аварийной остановки.
вабрабоччике прерывания прерывания запрещены, а нутренний i2c работает на прерываниях. Дальше прадолжать?
и нафига вам здесь прерывания нужны....
Дело в том, что есть необходимость остановить выполнение, программы по нажатию аварийной кнопки, при этом должны сработать клапана аварийного сброса давления
Ujine, похоже программировать через цифровой автомат запретили на территории РФ для новичков и остальных.
Дело в том, что есть необходимость остановить выполнение, программы по нажатию аварийной кнопки, при этом должны сработать клапана аварийного сброса давления
и для этого без прерываний никак?
Ешшо и прерывание по кнопке. Мрак безпросветный. Исходник сжечь. Новый писать после изучения фундаментальных трудов классиков.
вабрабоччике прерывания прерывания запрещены, а нутренний i2c работает на прерываниях. Дальше прадолжать?
Уважаемые форумчане, подскажите, пожалуйста.
Работаю с несколькими устройствами по шине I2C (в частности lcd, eeprom, ds3231, mcp23017), плюс использую код для отправки сообщений в стороннее железо по протоколу wiegand26 :
//собираем пакет readerTmp[9] = bitRead(c3, 3); //.... readerTmp[24] = bitRead(c3, 0); //бит нечетности 1-12 byte bit1 = 0; for (byte b1 = 1; b1 <= 12; b1 ++) bit1 = bit1 + readerTmp[b1]; if (bit1 % 2) readerTmp[0] = 1; else readerTmp[0] = 0; //бит четности 13-24 byte bit2 = 0; for (byte b2 = 13; b2 <= 24; b2 ++) bit2 = bit2 + readerTmp[b2]; if (bit2 % 2) readerTmp[25] = 0; else readerTmp[25] = 1; //отправка for (byte i = 0; i <= 25; i ++) { if(readerTmp[i] == 0){ delay(2); digitalWrite(p_D0, LOW); delayMicroseconds(50); digitalWrite(p_D0, HIGH); }else{ delay(2); digitalWrite(p_D1, LOW); delayMicroseconds(50); digitalWrite(p_D1, HIGH); } }Появилась необходимость использовать светодиод для индикации работы программы, смена состояния которого будет происходить в обработчике прерывания. Могут ли появиться проблемы при таком подходе?
Появилась необходимость использовать светодиод для индикации работы программы, смена состояния которого будет происходить в обработчике прерывания. Могут ли появиться проблемы при таком подходе?
Что-то не понятно где обработчик прерывания в вышеприведенном коде и как там светодиод управляется.
В общем случае в прерывании вполне себе допустимо дергать пинами. Главное не вставлять туда всякие delay() и вообще не деалать дополнительных задержек.
Что-то не понятно где обработчик прерывания в вышеприведенном коде и как там светодиод управляется.
обработчик прерывания планирую такой:
volatile uint8_t blink_mode = 0; void timerIsr() { static byte blink_time = 0; static byte blink_loop = 0; blink_time++; if( blink_time > (4 * 5)){ if( blink_mode & 1<<(blink_loop&0x07) ){ mcp.digitalWrite(p_ledErr, HIGH); } else { mcp.digitalWrite(p_ledErr, LOW); } blink_loop++; } if( blink_time >= (4 * 7)){ blink_time = 0; blink_loop = 0; } } void setup() { //... Timer1.initialize(250000); Timer1.attachInterrupt( timerIsr ); //... }обработчик прерывания планирую такой:
volatile uint8_t blink_mode = 0; void timerIsr() { mcp.digitalWrite(p_ledErr, HIGH); }Так точно работать не будет. Безпроблемно в прерывании можно дергать только пины самой Ардуины.
В данном случае, скорее всего, достаточно будет разрешить глобальные прерывание в начачале timerIsr(). Но этро при условии что Timer1 нигде больше не используется.
Безпроблемно в прерывании можно дергать только пины самой Ардуины.