All developer could you show me how to create a count down time with c++ Qt? If you can, you should to show me a source code.
You could use something like that. It calls timeOutSlot every second.
#define TIMEOUT 60
...
QTimer * timer = new QTimer();
connect(timer,SIGNAL(timeout()),this,SLOT(timeOutSlot()));
timer->start(1000);
...
void timeOutSlot()
{
static int time = TIMEOUT;
time--; // decrement counter
if (time==0) // countdown has finished
{
// timeout
}
else // re-start counter
{
time->start(1000);
}
}
Related
I want to increment a variable every second when an interrupt is triggered.
Code on esp32, esp-idf.
I have connected a button, when the button is pressed I want to count the number of seconds.
I did this using polling function, but I want to learn how to do it with interrupt, so counting and polling only when the button is pressed and not checking every second for a button pushed.
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#define ESP_INTR_FLAG_DEFAULT 0
#define BLINK_LED 13
#define GPIO_INPUT_IO_0 33
int buttonCount = 0;
int i = 0;
SemaphoreHandle_t xSemaphore = NULL;
TaskHandle_t printVariableTask = NULL;
void printVariable(void *pvParameter) {
int a = (int) pvParameter;
while (1) {
printf("A is a: %d \n", a++);
vTaskDelay(1000 / portTICK_RATE_MS);
}
}
// interrupt service routine, called when the button is pressed
void IRAM_ATTR button_isr_handler(void* arg) {
// notify the button task
xSemaphoreGiveFromISR(xSemaphore, NULL);
}
// task that will react to button clicks
void button_task(void* arg) {
// infinite loop
for(;;) {
// wait for the notification from the ISR
if(xSemaphoreTake(xSemaphore,portMAX_DELAY) == pdTRUE) {
int buttonState = gpio_get_level(GPIO_INPUT_IO_0);
while(buttonState == 1){ //code stucks here!!!!
buttonCount++;
printf("GPIO_INPUT_IO_0 %d\n", buttonState);
printf("Button pressed! %d \n", i++);
gpio_set_level(BLINK_LED, buttonState);
vTaskDelay(1000 / portTICK_RATE_MS);
}
}
}
}
void app_main()
{
// create the binary semaphore
xSemaphore = xSemaphoreCreateBinary();
// configure button and led pins as GPIO pins
gpio_pad_select_gpio(GPIO_INPUT_IO_0);
gpio_pad_select_gpio(BLINK_LED);
// set the correct direction
gpio_set_direction(GPIO_INPUT_IO_0, GPIO_MODE_INPUT);
gpio_set_direction(BLINK_LED, GPIO_MODE_OUTPUT);
// enable interrupt on falling (1->0) edge for button pin
gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_POSEDGE);
// start the task that will handle the button
xTaskCreate(button_task, "button_task", 2048, NULL, 10, NULL);
// install ISR service with default configuration
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
// attach the interrupt service routine
gpio_isr_handler_add(GPIO_INPUT_IO_0, button_isr_handler, NULL);
int pass = 25;
xTaskCreate(&printVariable, "printVariable", 2048, (void*) pass, 5, &printVariableTask);
}
It works, but when the code enter in the while(buttonState == 1) the loop never ends.
What am I doing wrong?
not sure if this is still and issue for you but try making a new global variable as a flag and set it to 1 in the interrupt routine when you want to start counting. In your loop look for that flag to be set to 1 and start incrementing. When you detect the button is no longer being pressed set the flag to 0.
Also you're never resetting your button state within the button task while loop. That's why your Button State is always coming back as 1.
Actually looking at it further I think you might just need to sample your input level level outside of your if statement within the for(;;) loop. I think this because (I believe) your if in the button task wont be called on a falling edge?
I interfaced a keypad to stm32f429i board and am able to display respective button of keypad on LCD screen of board. Short press is working fine but I have no idea how to implement long press key event. Can someone please help me?
Edit: As suggested by PeterJ_01, using a Timer interrupt is the most elegant way. Do not use the GPIO interrupt for buttons!
If you are using the Hardware Abstraction Layer (HAL) Library, you can do something like this inside your GPIO interrupt:
static int timestamp_pressed = -1; // -1 means not pressed
if (timestamp_pressed == -1) {
// user just pressed the button
timestamp_pressed = HAL_GetTick(); // milliseconds since startup
} else if (HAL_GetTick() - timestamp_pressed > 2000) {
// 2000 milliseconds = 2 seconds are over
// << your code
timestamp_pressed = -1;
}
If you are not using HAL and don't want to use a regular timer interrupt (from TIM1, TIM2, TIM3, ...) you can consider using the SysTick timer (with an interrupt or without). There is plenty information about it in the internet ;)
Yes - you need to use the timer interrupt. Here is the link and the library:
https://www.diymat.co.uk/arm-three-function-click-double-and-long-click-button-library-timer-interrupt-driven/
An implementation inspired from STM32F746G discovery board example.
if(HAL_GPIO_ReadPin(Push_GPIO_Port,Push_Pin) != RESET)
{
HAL_Delay(10);//debounce
timestamp_butpressed = HAL_GetTick();
while (HAL_GPIO_ReadPin(Push_GPIO_Port,Push_Pin) != RESET);
timestamp_butreleased = HAL_GetTick();
//Is button pressed enouph to run the program?
if(timestamp_butreleased>timestamp_butpressed?//This condition prevent max value to cause problem
((timestamp_butreleased-timestamp_butpressed)>2000)://normal condition
((4294967295-timestamp_butpressed)+timestamp_butreleased)>2000)//glitchy conditioin handle
{
//application runs here or flag handling heppens here
}
}
Another triky implementation (long press triggers as it reaches required time):
if(HAL_GPIO_ReadPin(Push_GPIO_Port,Push_Pin) != RESET)
{
HAL_Delay(10);
while(HAL_GPIO_ReadPin(Push_GPIO_Port,Push_Pin) == RESET)
{
HAL_Delay(5);
i++
if(i==100)break;
}
if(i<100)
{
short pressed
}
else
{
long pressed
}
}
A functional implementation:
uint8_t pushbot(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
if(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) != RESET)
{
uint32_t timestamp_butpressed=0;
uint32_t timestamp_butreleased=0;
HAL_Delay(10);//debounce
timestamp_butpressed = HAL_GetTick();
while (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) != RESET);
timestamp_butreleased = HAL_GetTick();
//Is button pressed enouph to run the program?
if(timestamp_butreleased>timestamp_butpressed?//This condition prevent max value to cause problem
((timestamp_butreleased-timestamp_butpressed)>1000)://normal condition
((4294967295-timestamp_butpressed)+timestamp_butreleased)>1000)//glitchy conditioin handle
{
return 2;// long pressed
}
else
{
return 1;// short pressed
}
}
else
{
return 0;// not pressed
}
}
I'm developing an OpenGL benchmark for a Qt application using VBOs and not glBegin/glEnd stuff.
I call the the class GLWidget (that inherits from public QGLWidget and protected QGLFunctions) from the MainWindow and I want to wait for the rendering to finish and get the value of the variable that stores the time elapsed. Problem is I don't find a suitable SIGNAL / method of waiting for the finish of a QGLWidget, so I get wrong variable values from MainWindow even if I use glFinish() at the end of the rendering.
void GLWidget::initializeGL() {
// VBO, glGenBuffers, glBindBuffer, glBufferData stuff
}
void GLWidget::paintGL()
{
QGLFunctions::glEnableVertexAttribArray(0);
QGLFunctions::glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
QGLFunctions::glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,0,(void*)0);
// Start Time
startTime = clock();
glDrawArrays(GL_TRIANGLES, 0, numVertices);
// Wait for the rendering
glFinish();
// Finish Time
finishTime = clock();
relativeTime = finishTime - startTime;
totalTime = (float) relativeTime / CLOCKS_PER_SEC;
QGLFunctions::glDisableVertexAttribArray(0);
}
You can create your own two signals
void startPaint() and void endPaint()
Than catch them somwhere(MainWindow maybe) and void startPaint() will start timer, and void endPaint() will stop timer and store value somewhere. Qt class QElapsedTimer should do the job.
I am developing for a touch screen and need to detect touch events to turn the screen back on. I am using Qt and sockets and have run into an interesting issue.
Whenever my QSocketNotifier detects the event it sends me infinite notices about it. Therefore I need to close and open the event file to cycle the notifier (inputNotifier in the below code). The problem usually arises several minutes after the device has been running and the file (inputDevice) suddenly changes it's handle from 24 to something else (usually 17).
I am not sure what to do, because the initial connect statement is linked to the initial Notifier pointer. If I create a new Notifier using the new handle, the connect is invalid. As far as I can tell there is no option to set a new socket value on a running QSocketNotifier. Suggestions? The relevant code is below:
#include "backlightcontroller.h"
#include <QTimer>
#include <QFile>
#include <syslog.h>
#include <QDebug>
#include <QSocketNotifier>
BacklightController::BacklightController(QObject *parent) :
QObject(parent)
{
backlightActive = true;
// setup timer
trigger = new QTimer;
trigger->setSingleShot(false);
connect(trigger, SIGNAL(timeout()), SLOT(deactivateBacklight()));
idleTimer = new QTimer;
idleTimer->setInterval(IDLE_TIME * 1000);
idleTimer->setSingleShot(false);
connect(idleTimer, SIGNAL(timeout()), SIGNAL(idled()));
idleTimer->start();
// setup socket notifier
inputDevice = new QFile(USERINPUT_DEVICE);
if (!inputDevice->open(QIODevice::ReadOnly))
{
syslog (LOG_ERR, "Input file for Backlight controller could not been opened.");
}
else
{
inputNotifier = new QSocketNotifier(inputDevice->handle(), QSocketNotifier::Read);
inputNotifier->setEnabled(true);
connect(inputNotifier, SIGNAL(activated(int)), SLOT(activateBacklight()));
}
qDebug()<<"backlight socket: "<<inputNotifier->socket();
// read out settings-file
QString intensity = Global::system_settings->getValue("BelatronUS_backlight_intensity");
if (intensity.length() == 0) intensity = "100";
QString duration = Global::system_settings->getValue("BelatronUS_backlight_duration");
if (duration.length() == 0) duration = "180";
QString always_on = Global::system_settings->getValue("BelatronUS_backlight_always_on");
if (always_on.length() == 0) always_on = "0";
setIntensity(intensity.toInt());
setDuration(duration.toInt());
if (always_on == "0")
setAlwaysOn(false);
else
setAlwaysOn(true);
}
BacklightController::~BacklightController()
{
trigger->stop();
inputNotifier->setEnabled(false);
inputDevice->close();
delete trigger;
delete inputDevice;
delete inputNotifier;
}
void BacklightController::setCurrentIntensity(int intensity)
{
// adapt backlight intensity
QFile backlightFile("/sys/class/backlight/atmel-pwm-bl/brightness");
if (!backlightFile.open(QIODevice::WriteOnly))
{
syslog (LOG_ERR, "Backlight intensity file could not been opened.");
}
else
{
QString intensityString = QString::number(TO_BRIGHTNESS(intensity));
if (backlightFile.write(
qPrintable(intensityString), intensityString.length()
) < intensityString.length())
{
syslog (LOG_ERR, "Backlight intensity could not been changed.");
}
backlightFile.close();
}
}
void BacklightController::resetNotifier()
{
inputDevice->close();
if (!inputDevice->open(QIODevice::ReadOnly))
{
syslog (LOG_ERR, "BacklightController::%s: Input file could not been opened.", __FUNCTION__);
}
qDebug()<<"reset, handle: "<<inputDevice->handle();
//inputNotifier=QSocketNotifier(inputDevice->handle(), QSocketNotifier::Read);
// restart timer after user input
idleTimer->start();
}
void BacklightController::activateBacklight()
{
// only activate backlight, if it's off (avoid to useless fileaccess)
if (!backlightActive)
{
setCurrentIntensity(_intensity);
backlightActive = true;
emit backlightActivated();
}
// restart backlight timeout, but only if we don't want the backlight to shine all the time
if (!_alwaysOn)
trigger->start();
// reset notifier to be able to catch the next userinput
resetNotifier();
}
void BacklightController::deactivateBacklight()
{
// don't turn it off, if it's forced on
if (!_alwaysOn)
{
if (backlightActive)
{
// only deactivate backlight, if it's on (avoid to useless fileaccess)
setCurrentIntensity(BACKLIGHT_INTENSITY_OFF);
backlightActive = false;
emit backlightDeactivated();
}
}
qDebug()<<"trigger stopping";
trigger->stop();
}
void BacklightController::setIntensity(int intensity)
{
if (intensity > 100)
intensity = 100;
else if (intensity < 0)
intensity = 0;
_intensity = intensity;
// write new intensity to file if it's active at the moment
if (backlightActive)
{
setCurrentIntensity(_intensity);
trigger->start();
}
}
void BacklightController::setDuration(int duration)
{
if (duration < 1)
duration = 1;
_duration = duration;
trigger->setInterval(_duration * MS_IN_SEC);
// reset trigger after changing duration
if (backlightActive)
{
trigger->start();
}
}
void BacklightController::setAlwaysOn(bool always_on)
{
_alwaysOn = always_on;
// tell the timer what to to now
if (_alwaysOn)
{
this->activateBacklight();
trigger->stop();
}
else
{
trigger->start();
}
}
I seem to have found a working solution for now. It's not the greatest so if there are better solutions I would be interested to hear them. The reason I did not think of this before is because I thought if I had a new connect statement in a function it would have limited scope as the function ended.
The solution was to simply check for an occurrence of the handle change in the file and then create a new pointer for the notifier using that handle. Then re-enable the notifier because it has likely been disabled by now and then create a new connect statement for the pointer. This is the code I used, added just below the closing and reopening of the event file:
if(inputDevice->handle()!=inputNotifier->socket()){
inputNotifier = new QSocketNotifier(inputDevice->handle(), QSocketNotifier::Read);
inputNotifier->setEnabled(true);
connect(inputNotifier, SIGNAL(activated(int)), SLOT(activateBacklight()));
}
I have a QTimeEdit widget on my dialog and I want to provide some kind of autochange - if the cursor is on minutes section and the time is 04:59, the next click on the arrow up would lead the time to change to 5:00.
How to do that?
I saw some mention of AutoAdvance property but I suppose it's obsolete because I cannot find it in Qt 4.7.
I noticed there is a signal called void timeChanged ( const QTime & time ). You can connect it to a slot and call function void QAbstractSpinBox::stepBy ( int steps ) in the slot function.
EDIT1:
Sorry for the misleading. In fact, we don't really need void timeChanged ( const QTime & time ).
See the code below:
class myTime : public QTimeEdit
{
Q_OBJECT
public:
virtual void stepBy(int steps)
{
if (this->time().minute()==59 && steps>0){
setTime(QTime(time().hour()+1,0,time().second(),time().msec()));
}else if(this->time().minute()==00 && steps<0){
setTime(QTime(time().hour()-1,59,time().second(),time().msec()));
}else{
QTimeEdit::stepBy(steps);
}
}
};
Keep in mind, you need to setWrapping(true) yourself.
I don't know whether it's still interesting, but I found another solution:
class myTime : public QTimeEdit {
Q_OBJECT public:
virtual void stepBy(int steps)
{
long lFactor=1;
if (currentSection()==QDateTimeEdit::MinuteSection)
lFactor=60;
else if (currentSection()==QDateTimeEdit::HourSection)
lFactor=3600;
long lDateTime = (dateTime().toMSecsSinceEpoch()/1000)+(steps*lFactor);
QDateTime dt = QDateTime::fromMSecsSinceEpoch(1000*(qint64)(lDateTime));
setDateTime(dt);
} };
If some one is wondering how to automatically change time (because I did), and needs easy, rather clean solution, here's what I came up with:
class TimeWidget : public QTimeEdit {
Q_OBJECT
public:
void stepBy(int steps) {
//Grab the current time
QTime t = time();
//So we edit the time as in raw milliseconds. Larger type would be better in this case.
//But 32bits is more than enough for day amount of milliseconds.
int raw = (t.hour()*360000 + t.minute()*60000 + t.second() * 1000 + t.msec());
//Let's find out what section time widget is changing
//And edit the raw millisecond data accordingly.
switch(currentSection()) {
case QDateTimeEdit::MSecSection:
raw += steps;
break;
case QDateTimeEdit::SecondSection:
raw += steps*1000;
break;
case QDateTimeEdit::MinuteSection:
raw+= steps*60000;
break;
case QDateTimeEdit::HourSection:
raw += steps*3600000;
break;
}
//Now we just split the "raw" milliseconds into
int hrs = (raw/ 36000000) % 24; //... hours
int mins = (raw/ 60000) % 60; //... minutes
int secs = (raw / 1000) % 60; //... seconds
int mills = raw % 1000; //... milliseconds
//Update & save the current time
t.setHMS(hrs, mins, secs, mills);
setTime(t);
}
};