Page 1 of 2

Button clicked, NEGEDGE interrupt fired multiple times

Posted: Wed Oct 30, 2019 8:31 pm
by Piscenois
Hi !

I try to detect a button click and have adapted code from gpio example :

Code: Select all

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

#define GPIO_BTN	GPIO_NUM_13

#define ESP_INTR_FLAG_DEFAULT 0

volatile uint32_t nbInt = 0;

static TaskHandle_t xHandlerTask = NULL;

static void IRAM_ATTR btn_isr_handler(void *arg) {
	BaseType_t xHigherPriorityTaskWoken;
	xHigherPriorityTaskWoken = pdFALSE;
	nbInt++;
	vTaskNotifyGiveFromISR(xHandlerTask, &xHigherPriorityTaskWoken);
	portYIELD_FROM_ISR();
}

static void btn_task(void *pvParameters) {
	const TickType_t xMaxExpectedBlockTime = pdMS_TO_TICKS(20);
	uint32_t ulEventsToProcess;
	for (;;) {
		ulEventsToProcess = ulTaskNotifyTake( pdTRUE, xMaxExpectedBlockTime);
		if (ulEventsToProcess != 0) {
			printf("GPIO intr, val: %d, nb notif: %d\n", gpio_get_level(GPIO_BTN), ulEventsToProcess);
		}
	}
}

void app_main() {
	gpio_config_t io_conf;
	io_conf.intr_type = GPIO_PIN_INTR_NEGEDGE;
	io_conf.pin_bit_mask = 1UL << GPIO_BTN;
	io_conf.mode = GPIO_MODE_INPUT;
	io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
	io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
	gpio_config(&io_conf);

	xTaskCreate(btn_task, "btn_task", 2048, NULL, 10, &xHandlerTask);

	gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
	gpio_isr_handler_add(GPIO_BTN, btn_isr_handler, (void*) GPIO_BTN);

	int cnt = 0;
	for (;;) {
		printf("cnt: %d, nb int: %d\n", cnt++, nbInt);
		vTaskDelay(1000 / portTICK_RATE_MS);
	}
}
When I push the button I get this trace :

Code: Select all

cnt: 0, nb int: 0
cnt: 1, nb int: 0
cnt: 2, nb int: 0
cnt: 3, nb int: 0
GPIO intr, val: 0, nb notif: 1 <-- button pressed
GPIO intr, val: 0, nb notif: 1 <-- button released immediatly
GPIO intr, val: 1, nb notif: 9
cnt: 4, nb int: 11
cnt: 5, nb int: 11
cnt: 6, nb int: 11
cnt: 7, nb int: 11
GPIO intr, val: 0, nb notif: 1 <-- button pressed
cnt: 8, nb int: 12
cnt: 9, nb int: 12
GPIO intr, val: 0, nb notif: 1 <-- button released after few millis
GPIO intr, val: 1, nb notif: 8
cnt: 10, nb int: 21
cnt: 11, nb int: 21
The interrupt is fired when I pressed the button and when I release it and several times again.

I use an RC filter (R = 1KHz and C = 0.1uF) to debounce the signal, liake this schematic (Rp1 of 10k is replaced by pull-up of ~45k).
It gives this signal :
button.png
button.png (27.8 KiB) Viewed 11272 times
It seems very clean.
I can't find what is my mistake.
If someone can help me ... :-)

Regards

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Sat Nov 02, 2019 3:25 pm
by Piscenois
Hi !

I have some precisions. I have logged times of each interrupt.
I press the button at 0 usec, 2 falling interrupts occurred 234031 and 234093 usec later (62 usec between the last).
Oscilloscope gives a normal shape, without any bounce.

I tried other buttons, even a rotary one. Interrupts always here !
I tried to disable internal pullup. Interrupts always here !
I tried to disable RTC GPIO. Interrupts always here !

My esp32 is a Wemos D32 Pro, it is a WROVER.

I hope someone will understand the problem. :mrgreen:

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Sat Nov 02, 2019 4:28 pm
by WiFive
Edge interrupt is very sensitive. 62usec probably means very small bounce exactly at threshold voltage. So you can decide to filter interrupts closer than 10ms or something.

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Sat Nov 02, 2019 7:38 pm
by Piscenois
Thank you WiFive !

You must be right. But 10msec filter seems too high, the problem already exists for a 1 millisecond one. After zooming I see very very small peaks with a width about 1-10 usec. With the current filter the rising slope is probably not enough vertical. The capacitor is too high. I'll try to use a 100 ohms resistor and test different capacitors. I fixed the CPU frequency to 80MHz to decrease the sampling and the risk of parasite interrupts.
I have some work to find the good settings :D

An other solution is to increase the CPU frequency and do the sampling with a FreeRTOS task (with software timer). But I prefer to find an hardware solution.

Regards

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Mon Nov 04, 2019 4:30 am
by Aussie Susan
I always do the debounce in firmware. Set up a timer interrupt for (say) between 1 and 5 mSec and sample the GPIO line(s). For each line (this technique can handle any number of switch/button inputs) zero a counter each time you detect that the state has changed from the last interrupt; or increment the timer if it is the same.
When a counter reaches a suitable value (say 10 for a 1mSec interrupt) then set a flag according to the state of the line.
In the main part of the code (perhaps in a RTOS task etc) you can simply test the state of the flag whenever you want to.
The interrupts generally run outside the RTOS environments and so this is can be considered a 'hardware' solution (at least as seen by an RTOS task) if you want to.
Susan

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Mon Nov 04, 2019 5:38 pm
by Piscenois
Thank you Susan !

I will try timer interrupt ... one more think I need to learn ;)
I tried a software timer (with FreeRTOS). With a 20ms period I don't need to debounce. The task check the GPIO level and always get an already stable value.
But ... this doesn't work with the rotary button. The 2 signals have an interval of 5ms (when rotating fast) and I need to know which is first, my check period must be inferior to 5ms. I 'm not sure it's a good idea to do it with FreeRTOS, because I would need to use à tick of 1000Hz (from menuconfig help this seems inadvisable). Your solution must be fine in this case too.

I tried a complete hardware solution with CD4093 combined with RC filter. It works but has 2 cons : need place on PCB and implies about 10ms offset (=> can't manage rotary button).

Regards
Gabe

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Mon Nov 04, 2019 6:20 pm
by vonnieda
For a rotary encoder, I recommend using a state machine such as the one described here: http://www.buxtronix.net/2011/10/rotary ... perly.html

It removes the need for debouncing and makes it much more reliable over all.

Jason

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Tue Nov 05, 2019 8:00 pm
by Piscenois
Wow! This solution seems extremely elegant! :o Thank you.

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Wed Nov 06, 2019 1:33 am
by Aussie Susan
The first post mentioned pressing and releasing a button.
Then we suddenly start talking about a rotary encoder.
No wonder the solution is different!
Susan

Re: Button clicked, NEGEDGE interrupt fired multiple times

Posted: Wed Nov 06, 2019 3:51 pm
by vonnieda
Aussie Susan wrote:
Wed Nov 06, 2019 1:33 am
The first post mentioned pressing and releasing a button.
Then we suddenly start talking about a rotary encoder.
No wonder the solution is different!
Susan
But ... this doesn't work with the rotary button. The 2 signals have an interval of 5ms (when rotating fast) and I need to know which is first, my check period must be inferior to 5ms. I 'm not sure it's a good idea to do it with FreeRTOS, because I would need to use à tick of 1000Hz (from menuconfig help this seems inadvisable). Your solution must be fine in this case too.
The fifth post in the thread, from the OP, mentioned a "rotary button". I took that to mean encoder. The other replies of course apply to normal button debouncing.

Jason