Проблема совмещения UTFT_Buttons и расходомера (датчик Холла)
- Войдите на сайт для отправки комментариев
Добрый вечер!
Задача: задавать на сенсорном ЖКД расход жидкости, получать на выходе системы заданный расход.
Имеем: 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
}
Очень хочется посоветовать не использовать библиотеку, которая "живет в пространстве while(1) {...}.", если это возможно, конечно
Да и подсчет расхода у вас странно реализован. Следует повесить его на прерывание. Ну или накрайняк от delay() избавиться.
Я бы тоже не стал использовать эту библиотеку, если бы были альтернативы. Пример ButtonTest из библиотеки UTouch живет абсолютно идентично: while (true) {...}. С радостью выслушаю варианты..
Подсчет расхода как раз и висит на прерывании. Как же еще опросить датчик Холла? И как избавиться от delay(), если нужно считать прерывания?
Я бы тоже не стал использовать эту библиотеку, если бы были альтернативы. Пример ButtonTest из библиотеки UTouch живет абсолютно идентично: while (true) {...}. С радостью выслушаю варианты..
Подсчет расхода как раз и висит на прерывании. Как же еще опросить датчик Холла? И как избавиться от delay(), если нужно считать прерывания?
Что нужно сделать?
Нужно получить: кликабельный сенсор, без задержек; достаточно точный расходомер. Оба условия должны выполняться..
Прерывания от датчика нужно считать в прерывании, где они и считаются - тут всё логично. :)
"Одну секунду" - в другом прерывании. Насколько я успел понять ардуиновскую идеологию - время для нас уже считается, и его можно в любой момент вычитать функцией millis( ).
Как по значению из millis() определить, что прошла секунда - это тут постоянно пишут, я уже раз пятнадцать читал. :)
Ну и когда она, секунда, прошла - регулируем пропорциональный клапан, и на дисплей выводим тоже.
Хотя, вообще-то, можно было не привязываться к секундным интервалам, а измерять и регулировать вообще непрерывно. Алгоритм, может быть, даже проще получится:
1. знаем сколько времени прошло с момента открывания клапана.
2. знаем, сколько за это время должно вылиться воды, а значит можем посчитать, сколько должно насчитаться импульсов с датчика.
3. сравниваем то что должно было насчитаться с тем что насчиталось реально. Ну и, понятно - по результатам сравнения регулируем клапан.
Всё, можете пользоваться. :)
Только осторожно. Счётчики - они не бесконечны, и при переполнении начнутся разные неожиданности при сравнении. Это надо предусмотреть.
Удалил функцию 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();} ....Пока все работает. Если у кого-то будут еще какие идеи - пишите, обязательно рассмотрю.
Только осторожно. Счётчики - они не бесконечны, и при переполнении начнутся разные неожиданности при сравнении. Это надо предусмотреть.
millis() - Возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino. Это количество сбрасывается на ноль, в следствие переполнения значения, приблизительно через 50 дней. (с)
Программа не расчитана на 50 дней непрерывной работы - вот и всё)
Всем спасибо за помощь!
Сегодня не расчитана - а завтра кому-нибудь придётся голову ломать, почему у него полтора месяца нормально регулировалось, а потом вдруг весь огород затопило. Правильный программист должен предусмотреть всё. :)
К тому же, я не только про millis писал, но и про counter.
Ну да ладно, заработало - и хорошо. Поздравляю! :)
Нужно получить: кликабельный сенсор, без задержек; достаточно точный расходомер. Оба условия должны выполняться..
Я спросил об этом: