Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 3869

General • Re: How to set GPIO level change interrupt on both cores RP2040

$
0
0
UPDATE

It took me long time to understand that GPIO_BANK0 has two dedicated interrupt lines, one for each core. This is very different from all the other peripherals which have single shared line between the two.

GPIO_BANK0 interrupts have the option to route the pin interrupt to any one of the cores.
PICO.png
The other issue is on the pico SDK both cores share the same interrupt vector table. Due to this we can't really create separate interrupt handlers, let's say one for pin 15 on core 0 and the other for pin 16 on core 1.

Only one interrupt handler can be created which will be shared between the two cores and we would have find out in the interrupt routine itself which core the interrupt is running on. To do this we only have to do the `irq_set_exclusive_handler(IO_IRQ_BANK0,gpio_irq_handler)` on one core only. Doesn't matter which one because vector table is shared, setting for one also sets it for the other. But the `irq_set_enabled(IO_IRQ_BANK0,true)` have to be set on both the cores.

Following is the example of the above.

Code:

#include <stdio.h>#include "pico/stdlib.h"#include "pico/multicore.h"void gpio_irq_handler(){    uint32_t coreNum = get_core_num();        if (coreNum == 0)    {        if (io_bank0_hw->proc0_irq_ctrl.ints[1] & IO_BANK0_PROC0_INTS1_GPIO15_EDGE_HIGH_BITS)        {            hw_clear_bits(&io_bank0_hw->intr[1],IO_BANK0_INTR1_GPIO15_EDGE_HIGH_BITS);            printf("Pin 15 edge rise interrupt fired and cleared on core %d\n",coreNum);        }    }    else    {        if (io_bank0_hw->proc1_irq_ctrl.ints[2] & IO_BANK0_PROC1_INTS2_GPIO16_EDGE_HIGH_BITS)        {            hw_clear_bits(&io_bank0_hw->intr[2],IO_BANK0_INTR2_GPIO16_EDGE_HIGH_BITS);            printf("Pin 16 edge rise interrupt fired and cleared on core %d\n",coreNum);        }    }}void core_1_entry(){       gpio_init(16);    hw_set_bits(&io_bank0_hw->proc1_irq_ctrl.inte[2], IO_BANK0_PROC1_INTE2_GPIO16_EDGE_HIGH_BITS);        irq_set_enabled(IO_IRQ_BANK0,true);    while(true)    {        tight_loop_contents();    }}int main(){    stdio_init_all();    gpio_init(15);    hw_set_bits(&io_bank0_hw->proc0_irq_ctrl.inte[1], IO_BANK0_PROC0_INTE1_GPIO15_EDGE_HIGH_BITS);        irq_set_exclusive_handler(IO_IRQ_BANK0,gpio_irq_handler);    irq_set_enabled(IO_IRQ_BANK0,true);    multicore_launch_core1(core_1_entry);    while (true) {        tight_loop_contents();    }}
To setup dedicated interrupt handler for pin 15 on core 0 and pin 16 on core1 we have to make a separate interrupt vector table for core1. Then we will able to use `irq_set_exclusive_handler(IO_IRQ_BANK0,gpio_irq_handler)` on both the cores and it will not cause an error.

Code:

#include <stdio.h>#include "pico/stdlib.h"#include "pico/multicore.h"// Define the size of the vector table, typically 240 entries (for Cortex-M0+)#define VECTOR_TABLE_SIZE 240// Create a memory region for Core 1's vector table__attribute__((aligned(256))) uint32_t core1_vector_table[VECTOR_TABLE_SIZE];void gpio_irq_handler_core_0(){    if (io_bank0_hw->proc0_irq_ctrl.ints[1] & IO_BANK0_PROC0_INTS1_GPIO15_EDGE_HIGH_BITS)    {        hw_clear_bits(&io_bank0_hw->intr[1],IO_BANK0_INTR1_GPIO15_EDGE_HIGH_BITS);        printf("Pin 15 edge rise interrupt fired on core 0\n");    }}void gpio_irq_handler_core_1(){    if (io_bank0_hw->proc1_irq_ctrl.ints[2] & IO_BANK0_PROC1_INTS2_GPIO16_EDGE_HIGH_BITS)    {        hw_clear_bits(&io_bank0_hw->intr[2],IO_BANK0_INTR2_GPIO16_EDGE_HIGH_BITS);        printf("Pin 16 edge rise interrupt fired on core 1\n");    }}void core_1_entry(){       // Set Core 1's VTOR register to the new vector table    scb_hw->vtor = (uint32_t)core1_vector_table;        gpio_init(16);    hw_set_bits(&io_bank0_hw->proc1_irq_ctrl.inte[2], IO_BANK0_PROC1_INTE2_GPIO16_EDGE_HIGH_BITS);        irq_set_exclusive_handler(IO_IRQ_BANK0,gpio_irq_handler_core_1);    irq_set_enabled(IO_IRQ_BANK0,true);    while(true)    {        tight_loop_contents();    }}int main(){    stdio_init_all();    // Get the current vector table for Core 0    uint32_t *core0_vector_table = (uint32_t *)scb_hw->vtor;    // Copy Core 0 vector table to Core 1 vector table    for (int i = 0; i < VECTOR_TABLE_SIZE; i++) {        core1_vector_table[i] = core0_vector_table[i];    }    gpio_init(15);    hw_set_bits(&io_bank0_hw->proc0_irq_ctrl.inte[1], IO_BANK0_PROC0_INTE1_GPIO15_EDGE_HIGH_BITS);        irq_set_exclusive_handler(IO_IRQ_BANK0,gpio_irq_handler_core_0);    irq_set_enabled(IO_IRQ_BANK0,true);    multicore_launch_core1(core_1_entry);    while (true) {        tight_loop_contents();    }}
I suppose there should be makefile flag which enables dual vector table, one for each core. I understand why they didn't do this, because dual vector table doesn't make much sense when interrupts form one peripheral is going to both the cores, as we would only enable it on one core.

But the GPIO_BANK0 interrupts are a edge case which requires dual vector table to take advantage of it's double interrupts lines.

If my understanding of this is wrong, please feel free to correct.

Statistics: Posted by Heisen — Mon Sep 16, 2024 4:19 am



Viewing all articles
Browse latest Browse all 3869

Trending Articles