Проблема совмещения UTFT_Buttons и расходомера (датчик Холла)

Dm_G
Offline
Зарегистрирован: 12.12.2014

Добрый вечер!

Задача: задавать на сенсорном ЖКД расход жидкости, получать на выходе системы заданный расход.

Имеем: Arduino Due, TFT Mega Shield 2.2, TFT 3.2'', расходомер (датчик Холла), пропорциональный клапан.

Проблема: Библиотека UTFT_Buttons живет в пространстве while(1) {...}. Если там находится запрос функции прерывания, то кнопки на экране срабатывают лишь 1 раз в 1 секунду (если успеть отжать кнопку в нужный момент) - не годится. Если выставить задержку в функции прерывания меньше 1 секунды - получаем рабочую ЖКД-панель, но плохой расходомер. Последний выдает 485 пульсов на 1 литр. При задержке 0,1 сек получаем разрешение расходомера 1,24 л/мин. (1*600/485=1,237, где 1 - минимальное число прерываний на датчике Холла).

На всякий случай сам код:

#include <UTFT.h>
#include <UTouch.h>
#include <UTFT_Buttons.h>
#include <PID_v1.h>

extern uint8_t BigFont[];
extern uint8_t SmallFont[];
extern uint8_t Ubuntubold[];
#define COLD_WATER_FLOW_SENSOR 8 // пин датчика расхода холодной воды
#define COLD_WATER_PROPORTIONAL_VALVE DAC0 // пин пропорционального клапана холодной воды

UTFT myGLCD(TFT01_32,38,39,40,41);
UTouch myTouch(6,5,4,3,2);
UTFT_Buttons MainScreenButtons(&myGLCD, &myTouch);

volatile int counter;
float Calc;                               
double Setpoint, Input, Output; //переменные ПИД
double SetpointVisual; // Видимая на экране
double consKp=1, consKi=0.05, consKd=0.25; // Консервативные константы
double aggKp=4*consKp, aggKi=4*consKi, aggKd=4*consKd; //Агрессивные константы

PID myPID(&Input, &Output, &Setpoint, 2, 0, 0, REVERSE); //Input, Output, Setpoint, Kp, Ki, Kd, ControllerDirection)



void setup()
{ 
  pinMode(COLD_WATER_FLOW_SENSOR, INPUT);
  Input = 0; 
  Setpoint = 0.00; // Предустановленное значение потока = 0 л/мин 
  myPID.SetMode(AUTOMATIC);
  attachInterrupt(COLD_WATER_FLOW_SENSOR, rpm, FALLING);
  
  myGLCD.InitLCD();
  myGLCD.clrScr();
  myGLCD.setFont(Ubuntubold);
  myGLCD.fillScr(0, 0, 0);
  myGLCD.setColor(255, 255, 255);
  myTouch.InitTouch();
  myTouch.setPrecision(PREC_MEDIUM);
  MainScreenButtons.setTextFont(Ubuntubold);
} 


void loop (){
  int butplus, butminus, butset, pressed_button;
  myGLCD.setFont(BigFont);
  butplus = MainScreenButtons.addButton( 150,  150, 60,  60, "+");
  butminus = MainScreenButtons.addButton( 10,  150, 60,  60, "-");
  butset = MainScreenButtons.addButton( 80, 150, 60,  60, "Set");
  MainScreenButtons.drawButtons();
 
while(1) 
{
  myGLCD.setFont(Ubuntubold);
  myGLCD.print("Flow, l/min:", 10, 10);
  Input = WaterFlowMeterFunction();
  //myGLCD.print("     ", 10, 50);
  myGLCD.printNumF(Input, 2, 10, 50);
  
    
  myGLCD.setFont(BigFont);
  myGLCD.print("Input:", 10, 90);
  //myGLCD.print("      ", 110, 90);
  myGLCD.printNumF(Input, 2, 110, 90);
  myGLCD.print("Setpoint:", 10, 110);
  //myGLCD.print("      ", 160, 110);
  //myGLCD.printNumF(Setpoint, 2, 160, 110);
  myGLCD.print("Output:", 10, 130);
  //myGLCD.print("      ", 130, 130);
  myGLCD.printNumF(Output, 2, 130, 130);
  
  if (myTouch.dataAvailable() == true)
  {
    pressed_button = MainScreenButtons.checkButtons();
    
      if (pressed_button==butplus)
        {
          myGLCD.setColor(0, 0, 0);
          myGLCD.print("     ", 160, 110); 
          SetpointVisual = SetpointVisual + 0.10;
            if (SetpointVisual > 6.0)
              SetpointVisual = 6.0;
          myGLCD.setColor(VGA_AQUA);
          myGLCD.printNumF(SetpointVisual, 2, 160, 110);
          myGLCD.setColor(VGA_WHITE);
        }
      if (pressed_button==butminus)
        {
          myGLCD.setColor(0, 0, 0);
          myGLCD.print("     ", 160, 110);
          SetpointVisual = SetpointVisual - 0.10;
            if (SetpointVisual < 0.0)
              SetpointVisual = 0.0;
          myGLCD.setColor(VGA_AQUA);
          myGLCD.printNumF(SetpointVisual, 2, 160, 110); 
          myGLCD.setColor(VGA_WHITE);        
        }
      if (pressed_button==butset)
        {
          myGLCD.setColor(0, 0, 0);
          myGLCD.print("     ", 160, 110);
          Setpoint = SetpointVisual;
          myGLCD.setColor(255, 255, 255);
          myGLCD.printNumF(Setpoint, 2, 160, 110);
        } 

  
  if ( Input < 0.9*Setpoint || Input > 1.1*Setpoint ) // если входящий сигнал больше чем на 10% отличается от заданного
    {myPID.SetTunings(aggKp, aggKi, aggKd);} // используем агрессивные константы
  else {myPID.SetTunings(consKp, consKi, consKd);} // иначе - консервативные
  
  myGLCD.setFont(SmallFont);
  myGLCD.print("Kp:", 220, 150);
  myGLCD.print("Ki:", 220, 160);
  myGLCD.print("Kd:", 220, 170);
  myGLCD.printNumF(myPID.GetKp(), 3, 245, 150);
  myGLCD.printNumF(myPID.GetKi(), 3, 245, 160);
  myGLCD.printNumF(myPID.GetKd(), 3, 245, 170);
    
  myPID.Compute();
  analogWrite(COLD_WATER_PROPORTIONAL_VALVE, Output); // COLD_WATER_PROPORTIONAL_VALVE - пин пропорц клапана холодной воды
  }
}
}

float WaterFlowMeterFunction(){ // функция подсчета расхода
  counter = 0;
  interrupts();
  delay (1000);
  noInterrupts();
  float currentQ = ( counter * 60.00 / 485.00 );  
  return currentQ;
}

void rpm ()     //This is the function that the interupt calls 
{ 
  counter++;  //This function measures the rising and falling edge of the hall effect sensors signal
} 

 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

Очень хочется посоветовать не использовать библиотеку, которая "живет в пространстве while(1) {...}.", если это возможно, конечно

Да и подсчет расхода у вас странно реализован. Следует повесить его на прерывание. Ну или накрайняк от delay() избавиться.

Dm_G
Offline
Зарегистрирован: 12.12.2014

Я бы тоже не стал использовать эту библиотеку, если бы были альтернативы. Пример ButtonTest из библиотеки UTouch живет абсолютно идентично: while (true) {...}. С радостью выслушаю варианты..

Подсчет расхода как раз и висит на прерывании. Как же еще опросить датчик Холла? И как избавиться от delay(), если нужно считать прерывания?

 

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

Dm_G пишет:

Я бы тоже не стал использовать эту библиотеку, если бы были альтернативы. Пример ButtonTest из библиотеки UTouch живет абсолютно идентично: while (true) {...}. С радостью выслушаю варианты..

Подсчет расхода как раз и висит на прерывании. Как же еще опросить датчик Холла? И как избавиться от delay(), если нужно считать прерывания?

 

Что нужно сделать?

Dm_G
Offline
Зарегистрирован: 12.12.2014

Нужно получить: кликабельный сенсор, без задержек; достаточно точный расходомер. Оба условия должны выполняться..

Datak
Offline
Зарегистрирован: 09.10.2014

Dm_G пишет:
Подсчет расхода как раз и висит на прерывании. Как же еще опросить датчик Холла? И как избавиться от delay(), если нужно считать прерывания?

Прерывания от датчика нужно считать в прерывании, где они и считаются - тут всё логично. :)

"Одну секунду" - в другом прерывании. Насколько я успел понять ардуиновскую идеологию - время для нас уже считается, и его можно в любой момент вычитать функцией millis( ).

Как по значению из millis() определить, что прошла секунда - это тут постоянно пишут, я уже раз пятнадцать читал. :)

Ну и когда она, секунда, прошла - регулируем пропорциональный клапан, и на дисплей выводим тоже.

 

Хотя, вообще-то, можно было не привязываться к секундным интервалам, а измерять и регулировать вообще непрерывно. Алгоритм, может быть, даже проще получится:

1. знаем сколько времени прошло с момента открывания клапана.
2. знаем, сколько за это время должно вылиться воды, а значит можем посчитать, сколько должно насчитаться импульсов с датчика.
3. сравниваем то что должно было насчитаться с тем что насчиталось реально. Ну и, понятно - по результатам сравнения регулируем клапан.

Всё, можете пользоваться. :)
Только осторожно. Счётчики - они не бесконечны, и при переполнении начнутся разные неожиданности при сравнении. Это надо предусмотреть.

Dm_G
Offline
Зарегистрирован: 12.12.2014

Удалил функцию float WaterFlowMeterFunction(), соотв удалил задержку. Дописал такие строки возле while:

....
interrupts();
LastCheckTime = millis();
   
while(1) 
{
  if (millis()-LastCheckTime >= 1000){
    noInterrupts();
    myGLCD.setFont(SmallFont);
    myGLCD.printNumI((millis()-LastCheckTime), 245, 180);
    currentQ = ( counter * 123.711 / ((millis()-LastCheckTime)*1.000) );  // 123.711
    counter = 0;
    interrupts();
    LastCheckTime = millis();}
....

Пока все работает. Если у кого-то будут еще какие идеи - пишите, обязательно рассмотрю.

Dm_G
Offline
Зарегистрирован: 12.12.2014

Datak пишет:

Только осторожно. Счётчики - они не бесконечны, и при переполнении начнутся разные неожиданности при сравнении. Это надо предусмотреть.

 

millis() - Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается на ноль, в следствие переполнения значения, приблизительно через 50 дней. (с)

Программа не расчитана на 50 дней непрерывной работы - вот и всё)

Всем спасибо за помощь!

Datak
Offline
Зарегистрирован: 09.10.2014

Dm_G пишет:
Цитата:
millis() - Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается на ноль, в следствие переполнения значения, приблизительно через 50 дней. (с)
Программа не расчитана на 50 дней непрерывной работы - вот и всё)

Сегодня не расчитана - а завтра кому-нибудь придётся голову ломать, почему у него полтора месяца нормально регулировалось, а потом вдруг весь огород затопило. Правильный программист должен предусмотреть всё. :)

К тому же, я не только про millis писал, но и про counter.

Ну да ладно, заработало - и хорошо. Поздравляю! :)

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

Dm_G пишет:

Нужно получить: кликабельный сенсор, без задержек; достаточно точный расходомер. Оба условия должны выполняться..

Я спросил об этом:

Цитата:
И как избавиться от delay(), если нужно считать прерывания?