Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spurious Interrupts #1250

Closed
stickbreaker opened this issue Mar 23, 2018 · 17 comments
Closed

Spurious Interrupts #1250

stickbreaker opened this issue Mar 23, 2018 · 17 comments

Comments

@stickbreaker
Copy link
Contributor

Hardware:

Board: ?WeMos BlueTooth with Battery?
Core Installation/update date: ?09MAR2018
IDE name: ?Arduino IDE 1.8.5
Flash Frequency: ?80Mhz?
Upload Speed: ?921600?

Description:

I am working on SLAVE mode I2C, my interrupt service routine (ISR) is receiving interrupts without any interrupt cause.

The following is a dump of a capture buffer that lists all arriving interrupts, the CCOUNT (cycle Count) when they were dispatched, and the pending hardware interrupt state.
The interesting columns are:

  • INTR: current value of i2c->dev->int_status.val
  • CCOUNT: current processor cycle count.
  • count: 0x8000 + number of consecutive interrupts
  • TX:RX Current value of i2c->dev->status_reg.val
[E][esp32-hal-i2c.c:999] i2cDumpInts(): 0 row  count   INTR    TX     RX    CCOUNT
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [01] 0x8001 0x0010 0x0014 0x0111 0xd268e314
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [02] 0x8001 0x0000 0x0014 0x0111 0xd268e3ce
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [03] 0x8001 0x0080 0x0014 0x0400 0xd269f950
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [04] 0x8001 0x0000 0x0014 0x0400 0xd269fa3b
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [05] 0x8001 0x0400 0x0014 0x0412 0xd26a8b15
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [06] 0x8001 0x0000 0x0014 0x0412 0xd26a8c00
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [07] 0x8001 0x0080 0x0014 0x0402 0xd26a9174
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [08] 0x8001 0x0000 0x0014 0x0402 0xd26a924f
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [09] 0x8001 0x0010 0x0010 0x0413 0xd26b7e10
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [10] 0x8001 0x0000 0x0010 0x0413 0xd26b7eca
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [11] 0x8001 0x0400 0x0008 0x0412 0xd26c2c10
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [12] 0x8001 0x0000 0x0008 0x0412 0xd26c2cca
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [13] 0x8001 0x0080 0x0008 0x0402 0xd26c3e23
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [14] 0x8001 0x0000 0x0008 0x0402 0xd26c3f0f
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [15] 0x8001 0x0400 0x0008 0x0410 0xd26ce189
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [16] 0x8001 0x0000 0x0008 0x0410 0xd26ce274
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [17] 0x8001 0x0080 0x0008 0x0400 0xd26ce829
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [18] 0x8001 0x0000 0x0008 0x0400 0xd26ce917
// code attaching interrupt

ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, 0, &i2c_isr_handler_default, i2c, &i2c->intr_handle);
      
i2c->dev->int_ena.val = 0x1FFF; // int 0 .. int 12

As you can see each interrupt is processed, and the ISR exits, immediately the ISR is re-entered without any interrupt source. I have coded around it by if(i2c->dev->int_status.val==0) return;, but there is something I am not understanding, or doing wrong. What is generating this wasted interrupt cycle?

There are multiple open issues around interrupts. #1111, #1229 I am wondering all of these have the same basis?

Am I not clearing the interrupt correctly? I use i2c->dev->int_clr.val = activeInterrupt;, to clear the interrupt status flag after I have responded to the underlying interrupt cause, so I only clear the interrupts I receive. I am not 'ignoring' any interrupt by just clearing the flag.

Chuck

@stickbreaker
Copy link
Contributor Author

stickbreaker commented Mar 23, 2018

Found IT

[E][esp32-hal-i2c.c:999] i2cDumpInts(): 0 row  count   INTR    TX     RX
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [01] 0x8001 0x0010 0x0010 0x0111 0xc05cb8e7
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [02] 0x8001 0x0080 0x0010 0x0400 0xc05dcf58
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [03] 0x8001 0x0400 0x0010 0x0412 0xc05e6180
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [04] 0x8001 0x0080 0x0010 0x0402 0xc05e65b4
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [05] 0x8001 0x0010 0x000c 0x0413 0xc05f527e
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [06] 0x8001 0x0400 0x0004 0x0412 0xc0600084
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [07] 0x8001 0x0080 0x0004 0x0402 0xc06012c7
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [08] 0x8001 0x0400 0x0004 0x0410 0xc060b6ee
[E][esp32-hal-i2c.c:1002] i2cDumpInts(): [09] 0x8001 0x0080 0x0004 0x0400 0xc060bb95

I was doing something Wrong, I looked through the esp-idf/esp_intr_alloc.h and found some configuration flags:

/** @brief Interrupt allocation flags
 *
 * These flags can be used to specify which interrupt qualities the
 * code calling esp_intr_alloc* needs.
 *
 */
//Keep the LEVELx values as they are here; they match up with (1<<level)
#define ESP_INTR_FLAG_LEVEL1		(1<<1)	///< Accept a Level 1 interrupt vector (lowest priority)
#define ESP_INTR_FLAG_LEVEL2		(1<<2)	///< Accept a Level 2 interrupt vector
#define ESP_INTR_FLAG_LEVEL3		(1<<3)	///< Accept a Level 3 interrupt vector
#define ESP_INTR_FLAG_LEVEL4		(1<<4)	///< Accept a Level 4 interrupt vector
#define ESP_INTR_FLAG_LEVEL5		(1<<5)	///< Accept a Level 5 interrupt vector
#define ESP_INTR_FLAG_LEVEL6		(1<<6)	///< Accept a Level 6 interrupt vector
#define ESP_INTR_FLAG_NMI			(1<<7)	///< Accept a Level 7 interrupt vector (highest priority)
#define ESP_INTR_FLAG_SHARED		(1<<8)	///< Interrupt can be shared between ISRs
#define ESP_INTR_FLAG_EDGE			(1<<9)	///< Edge-triggered interrupt
#define ESP_INTR_FLAG_IRAM			(1<<10)	///< ISR can be called if cache is disabled
#define ESP_INTR_FLAG_INTRDISABLED	(1<<11)	///< Return with this interrupt disabled

#define ESP_INTR_FLAG_LOWMED	(ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< Low and medium prio interrupts. These can be handled in C.
#define ESP_INTR_FLAG_HIGH		(ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< High level interrupts. Need to be handled in assembly.

#define ESP_INTR_FLAG_LEVELMASK	(ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \
								 ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \
								 ESP_INTR_FLAG_NMI) ///< Mask for all level flags

I changed the interrupt attached code to:

  uint32_t flags = ESP_INTR_FLAG_EDGE |//< Edge-triggered interrupt
    ESP_INTR_FLAG_IRAM; //< ISR can be called if cache is disabled
  switch(i2c->num){
    case 0:
      ret = esp_intr_alloc(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c_isr_handler_default, i2c, &i2c->intr_handle);
      break;
    case 1:
      ret = esp_intr_alloc(ETS_I2C_EXT1_INTR_SOURCE, flags, &i2c_isr_handler_default, i2c, &i2c->intr_handle);
      break;
    default :;
    }

No more Spurious interrupts, We need to check the attachInterrupt() source and see if there is a similar problem.

@me-no-dev do you know if I should be using more of these values? Like SHARED or one of the LEVEL values?

Chuck.

@stickbreaker
Copy link
Contributor Author

stickbreaker commented Mar 23, 2018

@me-no-dev Should I try to code my ISR to work in a 'Shared' interrupt mode using:

/**
 * @brief Allocate an interrupt with the given parameters.
 *
 *
 * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask
 * combo. For shared interrupts, the handler is only called if a read from the specified 
 * register, ANDed with the mask, returns non-zero. By passing an interrupt status register
 * address and a fitting mask, this can be used to accelerate interrupt handling in the case
 * a shared interrupt is triggered; by checking the interrupt statuses first, the code can
 * decide which ISRs can be skipped
 *
 * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
 *               sources, as defined in soc/soc.h, or one of the internal
 *               ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
 * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
 *               choice of interrupts that this routine can choose from. If this value
 *               is 0, it will default to allocating a non-shared interrupt of level
 *               1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
 *               interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return 
 *               from this function with the interrupt disabled.
 * @param intrstatusreg The address of an interrupt status register
 * @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits
 *               that are 1 in the mask set, the ISR will be called. If not, it will be
 *               skipped.
 * @param handler The interrupt handler. Must be NULL when an interrupt of level >3
 *               is requested, because these types of interrupts aren't C-callable.
 * @param arg    Optional argument for passed to the interrupt handler
 * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
 *               used to request details or free the interrupt. Can be NULL if no handle
 *               is required.
 *
 * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
 *         ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
 *         ESP_OK otherwise
 */
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
uint32_t flags =  ESP_INTR_FLAG_SHARED | //< Interrupt can be shared between ISRs
  ESP_INTR_FLAG_EDGE |  //< Edge-triggered interrupt
  ESP_INTR_FLAG_IRAM |  //< ISR can be called if cache is disabled
  ESP_INTR_FLAG_LOWMED;   //< Low and medium prio interrupts. These can be handled in C.

ret = esp_intr_alloc_intrstatus(ETS_I2C_EXT0_INTR_SOURCE, flags, &i2c->dev->int_status.val, 0x1FFF, &i2c_isr_handler_default,i2c, &i2c->intr_handle);

I don't know how scarce a resource these interrupts are, can you give me your perspective?

Chuck.

EDIT: 04/16/2018
SHARED and EDGE cannot both be used at the same time.

@404-baitnotfound
Copy link

yesterday I tryed to understand the attachInterrupt() funktion and there were serveral things I did not understand:

    static bool interrupt_initialized = false;
    
    if(!interrupt_initialized) {
        interrupt_initialized = true;
        esp_intr_alloc(ETS_GPIO_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __onPinInterrupt, NULL, &gpio_intr_handle);
    }

Why check ´if(!Interrupt_initialized)´ when it is set in the line above or is it because a `static bool' only gets initialised at the beginning?

esp_intr_alloc(ETS_GPIO_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __onPinInterrupt, NULL, &gpio_intr_handle);

the only used FLAG is ESP_INTR_FLAG_IRAM why not ESP_INTR_FLAG_EDGE

And the most important part. Where do I find the code that actually checkes the pin state and determents the trigger reason? GPIO.pin[pin].int_type = intr_type;
I am looking for this section of code because I allways get wrong Interrupts for my choosen intr_type

@perkint4
Copy link

Yes, the static part means the variable only gets defined/initialised on first execution so that code runs once on first execution and then set the flag so not run again.

Tim

@usmanshahid001
Copy link

i have check using esp-idf gpio code that is working fine for positive edge trigger....but not working in arduino............

@usmanshahid001
Copy link

negedge is working fine for esp-idf but not working in arduino core in arduino core just working for change

@404-baitnotfound
Copy link

This may not help you.
I personally have given up on using interrupts. Active polling gave me better results. And to save some energy I only activate "fast" polling when there is user activity.

@usmanshahid001
Copy link

usmanshahid001 commented Jun 5, 2018

we are designing pulse counter which change rate from 240 MHZ to 2 MHZ to save energy......i was using PCNT but debouncing is limited so it is catching noise i need isr on falling so i could perform some filtering but with change i am unable to do so,,,,i am unable to understand that esp-idf working fine it means issue is there in arduino core...is there any option to directly configure gpios using esp-idf while using arduino as we did for PCNT

@404-baitnotfound
Copy link

404-baitnotfound commented Jun 5, 2018

have you tried to use "deeper" methodes?

extern void __attachInterrupt(uint8_t pin, voidFuncPtr userFunc, int intr_type)
{
    static bool interrupt_initialized = false;

    if(!interrupt_initialized) {
        interrupt_initialized = true;
        esp_intr_alloc(ETS_GPIO_INTR_SOURCE, (int)ESP_INTR_FLAG_IRAM, __onPinInterrupt, NULL, &gpio_intr_handle);
    }
    __pinInterruptHandlers[pin] = userFunc;
    esp_intr_disable(gpio_intr_handle);
    if(esp_intr_get_cpu(gpio_intr_handle)) { //APP_CPU
        GPIO.pin[pin].int_ena = 1;
    } else { //PRO_CPU
        GPIO.pin[pin].int_ena = 4;
    }
    GPIO.pin[pin].int_type = intr_type;
    esp_intr_enable(gpio_intr_handle);
}

I guess you can write your own interrupt routine. maybe even outside the Arduino "loop", as a new task.

@usmanshahid001
Copy link

interesting did u tried that?

@usmanshahid001
Copy link

usmanshahid001 commented Jun 5, 2018

i tried this approach but there are compilation errors

void setup()
{
gpio_config_t pGPIOConfig
{
GPIO_SEL_21,
GPIO_MODE_INPUT,
GPIO_PULLUP_ENABLE,
GPIO_PULLDOWN_DISABLE,
GPIO_INTR_NEGEDGE,
};
pGPIOConfig.intr_type=GPIO_INTR_NEGEDGE;
gpio_config(&pGPIOConfig);
//gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_NEGEDGE);
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
//start gpio task
xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

//install gpio isr service
gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

//remove isr handler for gpio number.
gpio_isr_handler_remove(GPIO_INPUT_IO_0);
//hook isr handler for specific gpio pin again
gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

int cnt = 0;

}

@usmanshahid001
Copy link

esp_err_t gpio_set_intr_type(gpio_num_t gpio_num, gpio_int_type_t intr_type);

       ^

exit status 1
invalid conversion from 'int' to 'gpio_num_t' [-fpermissive]

@404-baitnotfound
Copy link

I only had limited succsess, and in the end it didn't work because I never got triggering on LOW to work.
Also "analog read" gave the best noise suppression but is slow and does not work in interrupt handlers.
I really can't help you further, since I am a totall noob.

@usmanshahid001
Copy link

YES PROBLEM SOLVED

@404-baitnotfound
Copy link

well well well. please share your solution, so me and others can learn from it.

@usmanshahid001
Copy link

usmanshahid001 commented Jun 6, 2018

#include "driver/gpio.h"

#define GPIO_OUTPUT_IO_0 18
#define GPIO_OUTPUT_IO_1 19
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0 4
#define GPIO_INPUT_IO_1 5
#define GPIO_INPUT_PIN_SEL ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0
extern "C" {
#include "soc/gpio_struct.h"
}

static xQueueHandle gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
Serial.println("yes");

}

void setup()
{

Serial.begin(115200);
gpio_config_t pGPIOConfig
{
GPIO_INPUT_PIN_SEL,
GPIO_MODE_INPUT,
GPIO_PULLUP_ENABLE,
GPIO_PULLDOWN_DISABLE,
GPIO_INTR_POSEDGE,
};
pGPIOConfig.intr_type=GPIO_INTR_POSEDGE;
gpio_config(&pGPIOConfig);
gpio_set_intr_type(GPIO_NUM_5, GPIO_INTR_POSEDGE);
gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));

gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
//hook isr handler for specific gpio pin
gpio_isr_handler_add(GPIO_NUM_5, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);
 gpio_isr_handler_remove(GPIO_NUM_4);
//hook isr handler for specific gpio pin again
gpio_isr_handler_add(GPIO_NUM_4, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

int cnt = 0;

}
void loop()
{

}

@usmanshahid001
Copy link

pin 4 and 5 configured for positive edge interrupt...Arduino IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants