Hi, first a bit of background. I have a sensor that generates pulses when a shaft rotates; I use a Schmidt trigger to turn the sign wave to a square wave and feed the output to GPIO pin 13 on a ESP32 Devkit 1 module. I wrote an APP to count the pulses using a task that watched for a change of state from LOW to HIGH. I thought a much better way to do it would be to use an interrupt service routine which should detect a leading edge. I first tried adding entries to a queue and couldn’t get that to work the way I wanted so I then simply incremented a global unsigned integer still without luck. The real problem is I just don’t understand how the interrupt system works and have not found any useful guides. I have read the RTOS definitions but there is no overall description of how to use them.
Below is my test APP code:
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"
#define gpio_pin 13
#define GPIO_INPUT_PIN_SEL (1ULL<<gpio_pin)
xQueueHandle gpio_evt_queue = NULL;
gpio_num_t GPIO_pulsePin = GPIO_NUM_13; // Pulse pin
unsigned int iPulses = 0;
esp_err_t iResult = 0;
// GPIO Interrupt callback function
void IRAM_ATTR gpio_isr_handler(void* arg) {
uint32_t gpio_num = (uint32_t) arg;
if (gpio_num == 13) {
iPulses +=1;
//xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}
}
void setup() {
Serial.begin(115200);
while (!Serial) {delay(100);}
Serial.println("Initialising");
pinMode(GPIO_pulsePin, INPUT); // sets GPIO_pulsePin mode to input
Serial.print("State of GPIO_pulsePin ");
Serial.print(GPIO_pulsePin);
Serial.print(" = ");
Serial.println(digitalRead(GPIO_pulsePin));
Serial.println("Configuring pin 13 pulse handler");
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_POSEDGE; // Falling edge interrupt
io_conf.mode = GPIO_MODE_INPUT; // The input mode
io_conf.pin_bit_mask = GPIO_pulsePin; // Configure the pin to be set
io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; // Do not pull down
io_conf.pull_up_en = GPIO_PULLUP_DISABLE; // Do not pull-up
// configure gpio
iResult = gpio_config (&io_conf);
Serial.print("Configuring pin 13 result = ");
Serial.println(iResult);
iResult = gpio_set_intr_type(GPIO_pulsePin, GPIO_INTR_POSEDGE);
Serial.print("Set Interupt type GPIO_INTR_POSEDGE = ");
Serial.println(iResult);
Serial.println("Creating event queue");
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
Serial.print("Create event queue result = ");
Serial.println(iResult);
// Serial.println("Creating GPIO task");
// iResult = xTaskCreate(GPIO_Task, "GPIO_Task", 2048, NULL, 10, NULL);
// Serial.print("Created GPIO task result = ");
// Serial.println(iResult);
// Interrupt configuration
Serial.println("Configuring interupt");
/**
* Register to interrupt service
*/
Serial.println("Registering interupt service");
//iResult = gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1); // Register to interrupt service
iResult = gpio_install_isr_service(GPIO_INTR_POSEDGE); // Register to interrupt service
Serial.print("Registering interupt service result = ");
Serial.println(iResult);
Serial.println("Removing the interrupt handler for pin 13");
iResult = gpio_isr_handler_remove(GPIO_pulsePin);
Serial.print("Remove result = ");
Serial.println(iResult);
Serial.println("Adding my interrupt handler for pin 13");
iResult = gpio_isr_handler_add(GPIO_pulsePin, gpio_isr_handler, (void*) GPIO_pulsePin);
Serial.print("Add result = ");
Serial.println(iResult);
}
void loop() {
static bool bFirst = true;
static unsigned int iOldPulses = 0;
if (bFirst) {
bFirst = false;
Serial.println("Entered main loop");
}
if (iPulses != iOldPulses) {
Serial.print("new pulse count: ");
Serial.println(iPulses);
iOldPulses = iPulses;
}
delay(1000);
}
void GPIO_Task(void* arg) {
uint32_t io_num;
for(;;) {
if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY) == pdPASS) {
if (io_num = 13) {
//iPulses += 1;
Serial.print("new pulse count: ");
Serial.println(iPulses);
} else {
Serial.print("Unexpected io_num: ");
Serial.println(io_num);
}
}
}
}
(I left the code in for the queue handler, but commented out calls to it.)
When the code runs I get no interrupts as long as the pin stays HIGH, but as soon as the pin goes LOW I get interrupts incessantly and the counter increments continuously.
Does anyone have a good guide to interrupt service routines or can tall me what I’m doing wrong?
Dicky.
Do you see the mistake? The above line will assign the value 13 to io_num, and the if always takes the “true” path.
Many compilers will issue a warning for this.
EDIT
Sorry, later I read that:
(I left the code in for the queue handler, but commented out calls to it.)
Normally, it is necessary to acknowledge a hardware interrupt. Please look in the manual of the CPU, there will be a register like for instance GPIO Interrupt Status Register.
When you acknowledge the interrupt, it will stop triggering, and I think it will work as expected.
uint32_t gpio_num = (uint32_t) arg;
Are you sure that arg is actually a GPIO index ( 13 ), or is it a mask of indexes, such as 1 << 13?
Many thanks for pointing out that dumb mistake
That explains why my queue handler doesn’t work. This is the new result with the error corrected, it’s not what I want though. The counter increments all the time pin 13 is LOW. I want it to only increment on a positive edge, and then only once per edge:
18:47:00.439 -> Create event queue result = 0
18:47:00.439 -> Creating GPIO task
18:47:00.439 -> Created GPIO task result = 1
18:47:00.439 -> Configuring interupt
18:47:00.439 -> Registering interupt service
18:47:00.439 -> Registering interupt service result = 0
18:47:00.439 -> Removing the interrupt handler for pin 13
18:47:00.439 -> Remove result = 0
18:47:00.439 -> Adding my interrupt handler for pin 13
18:47:00.439 -> Add result = 0
18:47:00.439 -> Entered main loop
18:47:19.003 -> new pulse count: 1
18:47:19.003 -> new pulse count: 2
18:47:19.003 -> new pulse count: 3
18:47:19.003 -> new pulse count: 4
18:47:19.003 -> new pulse count: 5
18:47:19.003 -> new pulse count: 6
18:47:19.003 -> new pulse count: 7
18:47:19.003 -> new pulse count: 8
18:47:19.003 -> new pulse count: 9
18:47:19.003 -> new pulse count: 10
But I still don’t know why the simple interrupt routine which just increments the counter doesn’t work.
Oops I replied before I saw your second post.
I suspected something like that but don’t know how to do it. The documentation although comprehensive it doesn’t really help someone unfamiliar with the device to know how it all fits together.
A quick read suggests I should be using the PULSE_CNT feature.
I am not very experienced with the ESP32, but I looked in the reference and found the following 6 registers:
GPIO
GPIO_STATUS_REG 0-31 int status register 0x3FF44044 R/W
GPIO_STATUS_W1TS_REG 0-31 int status register_W1TS 0x3FF44048 W
GPIO_STATUS_W1TC_REG 0-31 int status register_W1TC 0x3FF4404C W
GPIO_STATUS1_REG 32-39 int status register1 0x3FF44050 R/W
GPIO_STATUS1_W1TS_REG 32-39 int status bit set register 0x3FF44054 W
GPIO_STATUS1_W1TC_REG 32-39 int status bit clear register 0x3FF44058 W
Could you read GPIO_STATUS_REG from within the interrupt? I think that bit 18 will be high.
What if you write the value (1U<<18) to register GPIO_STATUS_W1TC_REG at address 0x3FF4404C?
I think it will clear the GPIO interrupt status for line GPIO-18.
I’ve just found the Espressif ESP32 programming guide has a section on pulse counters. It does have code snippets examples which should be sufficient to give me a decent understanding of how it all works. The ESP32 pulse counter is limited to16bits, but I will ignore that (apart from resetting it every so often) and increment my own unsigned int counter (written to FRAM). I will try to write a test program tomorrow.
Once again thank you for your help - it has been greatly appreciated.
PS. Always read the small print! GPIO pins only support HIGH and LOW interurpts, not edge interrupts.
One way to convert a GPIO that only supports High or Low interrupts is when you get the low interrupt, change the GPIO to not interrupt till the pin is High, then when that happens, change it again to interrupt when low. That does require that the change GPIO pin function to be usable in an ISR, and one problem sometimes with environments like this is they don’t always document that sort of detail