I am using the STM32 NUCLEO-F401RE microcontroller board.
I have a speaker programmed to change frequency by a set amount when the joystick is pushed up/down. My issue is that sometimes (more often than not) when the joystick is pushed up/down the frequency increases/decreases multiple times, implying that the ISR is executing multiple times. Also, the InterruptIn object is set to trigger on the rising edge however sometimes it also executes on the falling edge (when the joystick is returend to neutral after pushing up/down). Any help for getting over this?
void upISR()
{
if (greenLED.getStatus())
{
myTicker.detach();
frequency+=200;
myTicker.attach(callback(&spkr, &Speaker::toggle), 0.5/frequency);
}
}
'
int main()
{
InterruptIn up(A2);
InterruptIn down(A3);
InterruptIn fire(D4);
up.rise(&upISR);
down.rise(&downISR);
fire.rise(&toggleISR);
redLED.on();
while (1){}
}
Mechanical switch bounce is a feature of all mechanical switches to a lesser or greater extent. It is often necessary to implement "debouncing" in software especially if the switch is directly driving an interrupt as in this case.
A quick Google search for software denounce techniques yields some rather poor techniques IMO. I seen it done poorly more times than well unfortunately.
I suggest that in the switch ISR you start (or restart in the event of a "bounce") a hardware timer for a period of say 20ms or so (longer than the switch bounce time, but shorter than the time you could possibly to genuinely release the switch). Then in the timer ISR, you test the state of the switch and change the frequency accordingly:
Pseudocode:
void upISR()
{
debounceTimerRestart() ;
}
void downISR()
{
debounceTimerRestart() ;
}
void debounceTimerISR()
{
debounceTimerStop() ;
tDirection dir = getJoystickDir() ;
swithc( dir )
{
case UP :
{
increaseFrquency() ;
}
break ;
case DN :
{
decreaseFrquency() ;
}
break ;
}
}
What this does is trigger a timer interrupt shortly ("debounce time") after the switch stops bouncing. Note the timer is "single-shot" not periodic.
Below I present an enhancement at #BenVoigt's suggestion (in comments). I am keeping it separate to make it clear it was his work. The above will generally work, but if you have a particularly poor switch the following would resolve issues, and at little cost, so you may as well:
void debounceTimerISR()
{
debounceTimerStop() ;
static tDirection previous_dir = CENTRE ;
tDirection dir = getJoystickDir() ;
// If the state changed...
if( previous_dir != dir )
{
previous_dir = dir ;
switch( dir )
{
case UP :
{
increaseFrquency() ;
}
break ;
case DN :
{
decreaseFrquency() ;
}
break ;
}
}
}
Simple do not use EXTI for mechanical yousticks and buttons.
Use regular interrupt (for example systick) to poll the status of the pins.
We clearly believe this is the normal and expected bouncing of the switch. Mechanically a switch is some piece of metal that when acted on moves that metal from one pole to another, even if they do not resemble a wiper and two poles. The metal that moves will collide and bounce, the electrical connection will show that. The bouncing is often slow enough for a processor to get multiple interrupts, although that may be an under-sampling of all the bounces possibly seen electrically. If you try to look at it on a scope the scope itself may not-intentionally be filtering some of it (but so will your chip).
One way to see the problem is as with anything, research first then write the application later. This is not a solution but a way to characterize the problem for your system
switch_isr ( void )
{
...
some_global_variable <<= 1;
some_global_variable |= (pin_state_register>>pin_number)&1;
...
}
main ( void )
{
...
some_local_variable = 0;
while(1)
{
if(some_local_variable != some_global_variable)
{
some_local_variable = some_global_variable;
primitive_hex_print(some_local_variable);
}
}
}
No reason to expect to see every state change in the shifted variable, but you should see some and get a feel for the problem. Another way is to just have a counter increment on every interrupt, print periodically in the foreground and you will see one button press may result in multiple counts. And from the time it takes for the printouts to stop changing roughly in human time the settling time.
Filtering is all about state changes per unit time though and you have to have some flavor of time, be it a loop in the foreground that polls some information set by the interrupt (up/down counters, etc), or state changes relative to a timer/clock.
I do not know what the complete rules are for your assignment, if you can only have an interrupt for each switch and not a timer, or preferably a timer instead, I do not see a clean solution that will actually work. You would have to filter in the foreground but all that is doing is polling a copy of the pin state collected by the interrupt and is that any different than not using the interrupt? You cannot use Clifford's answer if you cannot set a timer interrupt, if you could use a timer and an interrupt then you could just periodically sample the switch states with that interrupt or a copy of the pin state collected by the pin state change interrupts and filter in the timer interrupt. Not the same as Clifford's but in all cases you need state change history relative to time to see when the thing settles.
Without a time reference and states not changing with respect to time (which a pin interrupt cannot show since the state has not changed) you cannot filter out the bounces. Instead work on your dexterity and how you flick the joystick up and down.
Related
I have built a simple coin sensor with two copper plates that detect when a coin hits them. Upon striking the two plates, I fire off an interrupt which looks like:
attachInterrupt( digitalPinToInterrupt(INPUT_PIN_COIN), Interrupt_CoinDeposit, FALLING );
This works fine and I am able to pick up when the coin strikes the two plates. In order to avoid the same coin being registered multiple times due to contact bounce, I detach the interrupt within the Interrupt_CoinDeposit() function as so:
void IRAM_ATTR Interrupt_CoinDeposit()
{
detachInterrupt(digitalPinToInterrupt(17));
g_crOSCore.EnqueueCoin();
}
EnqueueCoin simply increases a counter and returns back to where the interrupt left off. After which, I check if the counter has increased, and if it does, I reattach the interrupt. However, upon reattaching the interrupt, it fires off immediately. I learnt that reattaching the interrupt completes all the pending interrupts. I do not want this to happen. In the Arduino UNO R3, I believe you can solve this problem by resetting the EIFR. I'm wondering if there is something similar for the NodeMCU ESP32?
You could use a flag instead of disabling the interrupt. This way you also avoid the function call detachInterrupt() inside the ISR.
bool coinRegistered = false;
void IRAM_ATTR Interrupt_CoinDeposit()
{
if (!coinRegistered) {
coinRegistered = true;
g_crOSCore.EnqueueCoin();
}
}
/* ... somewhere else in the code ...*/
coinRegistered = false;
Either you can start a timer in the ISR, which resets the flag, or you reset it manually.
I have an animation shown on LEDs. When the button is pressed, the animation has to stop and then continue after the button is pressed again.
There is a method that processes working with the button:
void checkButton(){
GPIO_PinState state;
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_RESET) {
while(1){
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
if (state == GPIO_PIN_SET){
break;
}
}
//while (state == GPIO_PIN_RESET) {
//state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
//}
}
}
GPIO_PIN_SET is the default button position. GPIO_PIN_RESET is the condition when the button is pressed. The commented section is what I tried instead of the while(1){...} loop. The checkButton() method is called in the main loop from time to time to be run. The program runs on STM32 with an extension module (here the type of an extension module does not matter).
The fact is that this method stops animation just for a moment and does not work as I would like it to. Could you correct anything about this program to make it work properly?
Could you correct anything about this program to make it work
properly?
My guess is that you are trying to add a 'human interaction' aspect to your design. Your current approach relies on a single (button position) sample randomly timed by a) your application and b) a human finger. This timing is simply not reliable, but the correction is possibly not too difficult.
Note 1: A 'simple' mechanical button will 'bounce' during it's activation or release (yes, either way). This means that the value which the software 'sees' (in a few microseconds) is unpredictable for several (tbd) milliseconds(?) near the button push or release.
Note 2: Another way to look at this issue, is that your state value exists two places: in the physical button AND in the variable "GPIO_PinState state;". IMHO, a state value can only reside in one location. Two locations is always a mistake.
The solution, then (if you believe) is to decide to keep one state 'record', and eliminate the other. IMHO, I think you want to keep the button, which seems to be your human input. To be clear, you want to eliminate the variable "GPIO_PinState state;"
This line:
state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);
samples the switch state one time.
HOWEVER, you already know that this design can not rely on the one read being correct. After all, your user might have just pressed or released the button, and it is simply bouncing at the time of the sample.
Before we get to accumulating samples, you should be aware that the bouncing can last much more than a few microseconds. I've seen some switches bounce up to 10 milliseconds or more. If test equipment is available, I would hook it up and take a look at the characteristics of your button. If not, well, you can try the adjusting the controls of the following sample accumulator.
So, how do we 'accumulate' enough samples to feel confident we can know the state of the switch?
Consider multiple samples, spaced-in-time by short delays (2 controls?). I think you can simply accumulate them. The first count to reach tbr - 5 (or 10 or 100?) samples wins. So spin sample, delay, and increment one of two counters:
stateCount [2] = {0,0}; // state is either set or reset, init both to 0
// vvv-------max samples
for (int i=0; i<100; ++i) // worst case how long does your switch bounce
{
int sample = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15); // capture 1 sample
stateCount[sample] += 1; // increment based on sample
// if 'enough' samples are the same, kick out early
// v ---- how long does your switch bounce
if (stateCount[sample] > 5) break; // 5 or 10 or 100 ms
// to-be-determined --------vvv --- how long does switch bounce
std::this_thread::sleep_for(1ms); // 1, 3, 5 or 11 ms between samples
// C++ provides, but use what is available for your system
// and balanced with the needs of your app
}
FYI - The above scheme has 3 adjustments to handle different switch-bounce durations ... You have some experimenting to do. I would start with max samples at 20. I have no recommendation for sleep_for ... you provided no other info about your system.
Good luck.
It has been a long time, but I think I remember the push-buttons on a telecom infrastructure equipment bounced 5 to 15 ms.
I am trying to create a ping pong game and I need to say "If the user doesn't press space within 500 milliseconds, say 'You lost!' And if not, send the ball flying toward the other end."
The problem is that I can't use a function like Sleep() in if statements like:
if (Sleep(500)) {cout << "You lost!"}
Is there any time function that I could use in an if statement?
No.
You're programming at a lower level than a language with simple expressions to define rules like this.
Some ideas
Fundamentally, you're going to need to:
Set up a timer for 500ms
Set up a handler for keypresses
Have your timer expiry handler say "You lost" if a boolean flag is set, or otherwise "send the ball flying into the net".
Have your handler toggle that boolean flag if the keypress was Space
At a very basic level you could achieve this:
directly with two worker threads, or
crudely just by "waiting" for keypress activity (select, poll, epoll come to mind) with a timeout parameter (and ensure you don't reset the timeout if some other key were pressed instead), or
with help from your operating system, using e.g. a POSIX timer (though be cautious; this will send you into platform-specific, C-compatible land, which is probably not where you ultimately want to end up).
Usually, though, to do things "properly", we'd embed this logic into functionality provided by some engine implementing an "event loop" (particularly common in games, or in I/O-heavy applications).
Further information is going to take a book to explain adequately, and is thus not really appropriate in this medium. However, at least now you know which topics to explore in existing material.
Potentially interesting anecdote
I once wrote a little scripting language for my application, so that I could just define neat, high-level rules and the "compiler" would make use of my event loop to implement them. It made maintenance and expansion of my application really easy, which was important at the time because the rules became fairly complex and the application was broad in scope. It's not uncommon for computer game engines to embed scripting abilities of their own (say, using the "Lua" language), to achieve the same goal.
It's good to start thinking about your game design as a whole from the beginning as it helps solve problems like this in the design phase rather than further into development.
I'm not sure what library you're using but in sfml it would look something like this:
// Clock holds the current time.
// When we call Restart it returns the elapsed time since the last restart.
// With the current time and the start time we can find the elapsed time.
sf::Clock clock;
sf::Time maxTime = sf::milliseconds(500);
while(window.isOpen())
{
clock.Restart();
while(window.PollEvents(sf::Event e))
{
if(e.type == sf::Event::KeyPressed)
{
if(e.key.code == UP || e.key.code == DOWN || e.key.code == AnyOfOurOtherKeys)
{
maxTime = sf::milliseconds(500);
}
if(e.key.code == UP)
{
// Handle each individual key
}
if(e.key.code == DOWN)
{
// Handle each individual key
}
etc..
}
maxTime -= clock.Restart();
if(maxTime < sf::milliseconds(0))
{
std::cout << "Game Over, no input detected for 500 ms" << std::endl;
}
}
Regardless of library you can always use std::chrono::high_resolution_clock, std::chrono::milliseconds and std::chrono::duration to achieve the same results.
When you do your game loop you want to get the startLoopTime and then at the end of the game loop you want your endLoopTime. You can then do:
totalLoopTime = endLoopTime - startLoopTime;
maxTime -= totalLoopTime;
When you handle input you want a system where if a key that has a function is used, you set the maxTime back to 500ms. You could alternatively set a bool to true when a key is pressed and then run a check on the bool to restart maxTime.
I am trying to plot some serial data on my Qt Gui program using qcustomplot class. I had no trouble when I tried to plot low sampling frequency datas like 100 data/second. The graph was really cool and was plotting the data smoothly. But at high sampling rates such 1000data/second, plotter makes a bottleneck for serial read function. It slow downs serial there was a huge delay like 4-5 seconds apart from device. Straightforwardly, plotter could not reach the data stream speed. So, is there any common issue which i dont know about or any recommendation?
I thougth these scenarious,
1- to devide whole program to 2 or 3 thread. For example, serial part runs in one thread and plotting part runs in another thread and two thread communicates with a QSemaphore
2- fps of qcustom plot is limited. but there should be a solution because NI LABVIEW plots up to 2k of datas without any delay
3- to desing a new virtual serial device in usb protocol. Now, I am using ft232rl serial to usb convertor.
4- to change programming language. What is the situation and class support in C# or java for realtime plotting? (I know it is like a kid saying, but this is a pretex to be experienced in other languages)
My serial device send data funct(it is foo device for experiment there is no serious coding) is briefly that:
void progTask()
{
DelayMsec(1); //my delay function, milisecond
//read value from adc13
Adc13Read(adcValue.ui32Part);
sendData[0] = (char)'a';
sendData[1] = (char)'k';
sendData[2] = adcValue.bytes[0];
sendData[3] = (adcValue.bytes[1] & 15);
Qt Program read function is that:
//send test data
UARTSend(UART6_BASE,&sendData[0],4);
}
union{
unsigned char bytes[2];
unsigned int intPart;
unsigned char *ptr;
}serData;
void MedicalSoftware::serialReadData()
{
if(serial->bytesAvailable()<4)
{
//if the frame size is less than 4 bytes return and
//wait to full serial receive buffer
//note: serial->setReadBufferSize(4)!!!!
return;
}
QByteArray serialInData = serial->readAll();
//my algorithm
if(serialInData[0] == 'a' && serialInData[1] == 'k')
{
serData.bytes[0] = serialInData[2];
serData.bytes[1] = serialInData[3];
}else if(serialInData[2] == 'a' && serialInData[3] == 'k')
{
serData.bytes[0] = serialInData[0];
serData.bytes[1] = serialInData[1];
}
else if(serialInData[1] == 'a' && serialInData[2] == 'k')
{
serial->read(1);
return;
}else if(serialInData[0] == 'k' && serialInData[3] == 'a')
{
serData.bytes[0] = serialInData[1];
serData.bytes[1] = serialInData[2];
}
plotMainGraph(serData.intPart);
serData.intPart = 0;
}
And qcustom plot setting fuction is:
void MedicalSoftware::setGraphsProperties()
{
//MAIN PLOTTER
ui->mainPlotter->addGraph();
ui->mainPlotter->xAxis->setRange(0,2000);
ui->mainPlotter->yAxis->setRange(-0.1,3.5);
ui->mainPlotter->xAxis->setLabel("Time(s)");
ui->mainPlotter->yAxis->setLabel("Magnitude(mV)");
QSharedPointer<QCPAxisTickerTime> timeTicker(new QCPAxisTickerTime());
timeTicker->setTimeFormat("%h:%m:%s");
ui->mainPlotter->xAxis->setTicker(timeTicker);
ui->mainPlotter->axisRect()->setupFullAxesBox();
QPen pen;
pen.setColor(QColor("blue"));
ui->mainPlotter->graph(0)->setPen(pen);
dataTimer = new QTimer;
}
And the last is plot function:
void MedicalSoftware::plotMainGraph(const quint16 serData)
{
static QTime time(QTime::currentTime());
double key = time.elapsed()/1000.0;
static double lastPointKey = 0;
if(key-lastPointKey>0.005)
{
double value0 = serData*(3.3/4096);
ui->mainPlotter->graph(0)->addData(key,value0);
lastPointKey = key;
}
ui->mainPlotter->xAxis->setRange(key+0.25, 2, Qt::AlignRight);
counter++;
ui->mainPlotter->replot();
counter = 0;
}
Quick answer:
Have you tried:
ui->mainPlotter->replot(QCustomPlot::rpQueuedReplot);
according to the documentation it can improves performances when doing a lot of replots.
Longer answer:
My feeling on your code is that you are trying to replot as often as you can to get a "real time" plot. But if you are on a PC with a desktop OS there is no such thing as real time.
What you should care about is:
Ensure that the code that read/write to the serial port is not delayed too much. "Too much" is to be interpreted with respect to the connected hardware. If it gets really time critical (which seems to be your case) you have to optimize your read/write functions and eventually put them alone in a thread. This can go as far as reserving a full hardware CPU core for this thread.
Ensure that the graph plot is refreshed fast enough for the human eyes. You do not need to do a full repaint each time you receive a single data point.
In your case you receive 1000 data/s which make 1 data every ms. That is quite fast because that is beyond the default timer resolution of most desktop OS. That means you are likely to have more than a single point of data when calling your "serialReadData()" and that you could optimize it by calling it less often (e.g call it every 10ms and read 10 data points each time). Then you could call "replot()" every 30ms which would add 30 new data points each time, skip about 29 replot() calls every 30ms compared to your code and give you ~30fps.
1- to devide whole program to 2 or 3 thread. For example, serial part
runs in one thread and plotting part runs in another thread and two
thread communicates with a QSemaphore
Dividing the GUI from the serial part in 2 threads is good because you will prevent a bottleneck in GUI to block the serial communication. Also you could skip using semaphore and simply rely on Qt signal/slot connections (connected in Qt::QueuedConnection mode).
4- to change programming language. What is the situation and class
support in C# or java for realtime plotting? (I know it is like a kid
saying, but this is a pretex to be experienced in other languages)
Changing the programming language, in best case, won't change anything or could hurt your performances, especially if you go toward languages which are not compiled to native CPU instructions.
Changing the plotting library on the other hand could change the performances. You can look at Qt Charts and Qwt. I do not know how they compare to QCustomPlot though.
I'm working on an Arduino sketch where I created two traffic lights, one for normal traffic and one for pedestrians. I created a function for each of these two lights, which loops it through it's cycle (for example: turn yellow, wait 20 seconds, then turn red and wait again). These are called aTrafficlight() and pTrafficlight, where a is the normal light and p the one for the pedestrians. In my loop() function I set a digitalRead for a button. When this button is pressed the traffic lights should cycle through their loops one at a time (so the normal light turns red, pTrafficlight waits a bit, then turns green, blinks a few times, turns red, waits and ends the loop, so it goes to it's original state.)
This all works. But now I want to add a buzzer. This buzzer must beep once a second while the pTrafficlight is red, once a tenth second while it's green and twice per two seconds while it's flashing green.
Here I encountered a few problems:
- When waiting in original state (button's not pressed) it seems I can sometimes press the button without reaction because the loop is still going. I need to figure out how to avoid waiting in the loop when buttonState == LOW. (There's a function wait(int sec))
- When the button is pressed, it loops through the cycles. I could just write some kind of loop implementing the traffic light being red and beeping at the same time, but I'd rather keep these seperated.
- Same for the double beeps. I do not want the beeping and flashing of the light to be in the same for loop, as it's confusing and difficult to read and understand the code.
Here's my loop():
int buttonState = 0;
void loop(){
buttonState = digitalRead(pButton);
if(buttonState == LOW){
vSet("red");
pSet("green");
// This is where I tried to create the sound.
digitalWrite(pSound, HIGH);
delay(10);
digitalWrite(pSound, LOW);
wait(1);
} else {
aTrafficlight();
pTrafficlight();
}
}
Is there a way to solve my problems with multithreading? I tried to look it up at arduino.cc and google, but I can't find a way I understand enough to use it in my existing code.
If not, do you have any better suggestions?
You don't need multi-threading. You need to use timers, whether via interrupts (as Pawel wrote) or via another mechanism, such as the Metro library, that lets the code continue to loop through while the timer is running. See this question and answer: How Can I Create Interrupts in C for Arduino
Coincidentally, I recently posted some material both on state machines and the Arduino, as Hans Passant mentioned, and on alternatives to delay(), both with additional references you might find useful.
You could use a time-slice design. Let me just outline this in very general terms.
First code loop so that it always issues a delay(1) and set a mod 10 counter as:
int stopWhen = -1; // at startup
// etc.
x = (x+1)%10; // every time loop execs
Then when buttonState == LOW
if (stopwWhen = -1)
{
stopWhen = x;
// beep etc.
}
But on every loop:
// always exec this:
if (stopWhen == x)
{
stopWhen = -1;
// stop beeping.
}