QuickLogic Thing+ Button Interrupt Issue: Function Not Executing on Press

Hi everyone,

I’m trying to set up an interrupt that triggers a function when the user button is pressed, but I’m having trouble getting it to work properly.

I’ve configured the interrupt to detect the button press and link it to a specific function. However, when I press the button, it seems like the function isn’t being called, or the interrupt isn’t being registered at all. I’ve double-checked my wiring and reviewed the code multiple times, but I can’t figure out what’s going wrong.

Has anyone else encountered a similar issue? Or does anyone have suggestions on what I might be missing or how I can troubleshoot this further? Any help would be greatly appreciated!

I should also mention that I’m not entirely sure what type of interrupt the button generates when pressed. The documentation for the QuickLogic Thing+ board isn’t very extensive. This lack of information is making it harder to troubleshoot the problem, so if anyone has experience with this board or knows where to find more detailed documentation, that would be really helpful!

This is my GPIO configuration:
GPIOCfgTypeDef gpiocfg_table =
{
{
.usPadNum = PAD_6,
.ucGpioNum = GPIO_0,
.ucFunc = PAD6_FUNC_SEL_SENS_INT_1,
.intr_type = LEVEL_TRIGGERED,
.pol_type = FALL_LOW,
.ucPull = PAD_NOPULL,
}
};

And this is the handler I think I linked to the interruption:

void SensorGpio_Handler(void)
{

 INTR_CTRL->GPIO_INTR_EN_M4 &= ~(GPIO_0_INTR);

userbutton();

INTR_CTRL->GPIO_0_INTR = 0;
NVIC_ClearPendingIRQ(Gpio_IRQn);

INTR_CTRL->GPIO_INTR_EN_M4 = GPIO_0_INTR;

}

I’m a student so I’m a little bit confused about all of this sorry if I make some stupid questions.
Thanks in advance!

That board is beyond my expertise, however, Claude.ai has a few suggestions that might help so I’m going to paste them below:

First, let’s review your GPIO configuration:

GPIOCfgTypeDef gpiocfg_table = {
    {
        .usPadNum = PAD_6,
        .ucGpioNum = GPIO_0,
        .ucFunc = PAD6_FUNC_SEL_SENS_INT_1,
        .intr_type = LEVEL_TRIGGERED,
        .pol_type = FALL_LOW,
        .ucPull = PAD_NOPULL,
    }
};

And the interrupt handler:

void SensorGpio_Handler(void) {
    INTR_CTRL->GPIO_INTR_EN_M4 &= ~(GPIO_0_INTR);
    
    userbutton();
    
    INTR_CTRL->GPIO_0_INTR = 0;
    NVIC_ClearPendingIRQ(Gpio_IRQn);
    
    INTR_CTRL->GPIO_INTR_EN_M4 = GPIO_0_INTR;
}

Based on this information, here are some potential issues and suggestions:

  1. Interrupt Configuration:
  • Make sure that the interrupt is properly enabled in the NVIC (Nested Vectored Interrupt Controller).
  • Verify that the interrupt priority is set correctly.
  1. GPIO Configuration:
  • Double-check that PAD_6 and GPIO_0 are the correct pins for your button.
  • Ensure that PAD6_FUNC_SEL_SENS_INT_1 is the correct function selection for interrupt mode.
  1. Interrupt Trigger:
  • You’ve configured it as LEVEL_TRIGGERED with FALL_LOW. This means it will trigger when the input falls to a low level. Make sure this matches your button’s behavior (i.e., the button should connect the pin to ground when pressed).
  1. Debouncing:
  • Mechanical buttons often need debouncing. Without it, you might get multiple interrupts for a single press.
  1. Handler Registration:
  • Ensure that SensorGpio_Handler is properly registered as the interrupt handler for GPIO_0.
  1. userbutton() Function:
  • Make sure the userbutton() function is defined and does what you expect.

Here’s a step-by-step troubleshooting plan:

  1. Verify Basic GPIO Functionality: First, let’s make sure the GPIO is working correctly without interrupts. Try this simple polling approach:
while(1) {
    if(GPIO_READ(GPIO_0) == 0) {  // Assuming active low
        // Button is pressed
        // Add a small delay for debouncing
        delay_ms(50);
        while(GPIO_READ(GPIO_0) == 0);  // Wait for button release
        userbutton();
    }
}
  1. Enable Debugging Output: Add some debug print statements in your interrupt handler:
void SensorGpio_Handler(void) {
    printf("Interrupt triggered!\n");
    INTR_CTRL->GPIO_INTR_EN_M4 &= ~(GPIO_0_INTR);
    
    userbutton();
    printf("userbutton() called\n");
    
    INTR_CTRL->GPIO_0_INTR = 0;
    NVIC_ClearPendingIRQ(Gpio_IRQn);
    
    INTR_CTRL->GPIO_INTR_EN_M4 = GPIO_0_INTR;
}
  1. Check Interrupt Registration: Make sure you’re registering the interrupt handler correctly. It might look something like this:
NVIC_SetVector(Gpio_IRQn, (uint32_t)SensorGpio_Handler);
NVIC_EnableIRQ(Gpio_IRQn);
  1. Verify Interrupt Enable: Ensure that the GPIO interrupt is enabled:
INTR_CTRL->GPIO_INTR_EN_M4 |= GPIO_0_INTR;
  1. Check for Conflicting Configurations: Make sure no other part of your code is modifying the GPIO or interrupt settings.

If none of these steps resolve the issue, it would be helpful to see more of your initialization code, particularly how you’re setting up the GPIO and registering the interrupt handler.

Regarding the documentation for the QuickLogic Thing+ board, you might want to check QuickLogic’s official website or GitHub repositories for more detailed information. Remember, debugging embedded systems often requires patience and systematic checking. Don’t hesitate to use debug print statements, LED indicators, or even an oscilloscope if available to understand what’s happening at each step!

I’m currently working on a project involving the Sparkfun Thing+ and have been struggling with setting up interrupt routines. Despite following the examples provided in the repository, I am unable to get the blue LED to light up, and the button press interrupt isn’t being detected. I’m hoping someone here can help me identify what I’m doing wrong or missing. Any advice would be greatly appreciated!

Problem Overview

Based on the examples, it seems that the process should be straightforward. I need to configure the PAD/GPIO for the desired interrupt in the exceptions file and then set up the handler to clear the flags and execute the desired function. In my case, I want to detect button presses, so my GPIO configuration looks like this:
GPIOCfgTypeDef gpiocfg_table =
{
{
// Example setting for PAD_11 (sensor interrupt 5)
.usPadNum = PAD_11,
.ucGpioNum = GPIO_2,
.ucFunc = PAD11_FUNC_SEL_SENS_INT_5,
.intr_type = EDGE_TRIGGERED,
.pol_type = FALL_LOW,
.ucPull = PAD_PULLUP,
},
};

Handler Setup

I’ve set up the interrupt handler as follows:
void SensorGpio_Handler(void)
{
NVIC_DisableIRQ(Gpio_IRQn);
uint32_t intrCtrl;
intrCtrl = INTR_CTRL->GPIO_INTR;
if(intrCtrl & (1 << GPIO_2))
{
INTR_CTRL->GPIO_INTR |= (1 << GPIO_2);
buttontoggle();
}
NVIC_ClearPendingIRQ(Gpio_IRQn);
NVIC_EnableIRQ(Gpio_IRQn);
}

Main Code (main.c)

Here is the relevant section of my main.c file:
#include “Fw_global_config.h” // This defines application-specific characteristics

#include <stdio.h>
#include “FreeRTOS.h”
#include “task.h”
#include “semphr.h”
#include “timers.h”
#include “RtosTask.h”

/* Include the generic headers required for QORC */
#include “eoss3_hal_gpio.h”
#include “eoss3_hal_rtc.h”
#include “eoss3_hal_fpga_usbserial.h”
#include “s3x_clock_hal.h”
#include “s3x_clock.h”
#include “s3x_pi.h”
#include “dbg_uart.h”

#include “cli.h”

#include “fpga_loader.h” // API for loading FPGA
#include “gateware.h” // FPGA bitstream to load into FPGA

#include “Bootconfig.h”

#include <stdint.h>
#include <stdbool.h>

/*

  • Global variable definition
    */
    #define USER_BUTTON_GPIO_NUM (0) // PAD 6, GPIO is connected to User Button
    #define BLUE_LED_GPIO_NUM (4) // PAD 18, GPIO is connected to Blue LED
    #define GREEN_LED_GPIO_NUM (5) // PAD 21, GPIO is connected to Green LED
    #define RED_LED_GPIO_NUM (6) // PAD 22, GPIO is connected to Red LED

extern void qf_hardwareSetup();
static void nvic_init(void);
static void leds(void);
void buttontoggle(void);

uint8_t val = 1;

int main(void)
{
qf_hardwareSetup();
nvic_init();

load_fpga(axFPGABitStream_length, axFPGABitStream);
// Use 0x6141 as USB serial product ID (USB PID)
HAL_usbserial_init2(true, false, 0x6141);        // Start USB serial not using interrupts
for (int i = 0; i != 4000000; i++) ;   // Give it time to enumerate

dbg_str("\n\n");
dbg_str("##########################\n");
dbg_str("Sparkfun Thing+ LED / User Button Test\n");
dbg_str("##########################\n\n");

dbg_str("\n\nHello world!!\n\n");	// <<<<<<<<<<<<<<<<<<<<<  Change me!
    
/* Start the tasks and timer running. */
vTaskStartScheduler();
dbg_str("\n");

HAL_GPIO_Write(BLUE_LED_GPIO_NUM, 1);

while(1);

}

static void nvic_init(void)
{
// To initialize the system, this interrupt should be triggered at main.
// So, we will set its priority just before calling vTaskStartScheduler(), not at the time of enabling each IRQ.
NVIC_SetPriority(Ffe0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(SpiMs_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(CfgDma_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(Uart_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_SetPriority(FbMsg_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
}

// needed for startup_EOSS3b.s asm file
void SystemInit(void)
{

}

void buttontoggle(void)
{
uint8_t ucVal_boton = 0;

HAL_GPIO_Read(2, &ucVal_boton);
if (ucVal_boton != 1) { 
  for (uint32_t i = 0; i < 1152001; i++) {
      dbg_str(""); 
  }
  HAL_GPIO_Read(2, &ucVal_boton);
  if (ucVal_boton != 1) {
    
    if(val == 1){
      val = 0;
    }
    else{
      val = 1;
    }

    HAL_GPIO_Write(BLUE_LED_GPIO_NUM, val);    
  }

  for (int i = 0; i < 1152001; i++){
    dbg_str(""); 
  } 
}

}

qf_hardwaresetup.c

#include “Fw_global_config.h”
#include “eoss3_hal_gpio.h”
#include “eoss3_hal_pads.h”
#include “eoss3_hal_pad_config.h”
#include “eoss3_hal_spi.h”
#include “eoss3_hal_uart.h”
#include “s3x_clock.h”
#include “spi_flash.h”
#include “FreeRTOS.h”
#include “task.h”
#include “FreeRTOSConfig.h”
#include “string.h”

extern PadConfig pincfg_table;
extern GPIOCfgTypeDef gpiocfg_table;
extern int sizeof_pincfg_table ;
extern int sizeof_gpiocfg_table;

static void ldo_init(void);
static void system_init(void);
static void uart_setup(void);
static void SPIM_Setup(void);

void qf_hardwareSetup(void) {

SCnSCB->ACTLR |= SCnSCB_ACTLR_DISDEFWBUF_Msk;
S3x_pwrcfg_init();
system_init();
ldo_init();
configure_s3_gpio_interrupts(gpiocfg_table, sizeof_gpiocfg_table);
configure_s3_pads(pincfg_table, sizeof_pincfg_table);


uart_setup();
SPIM_Setup();

}

//Initialize all System Clocks,Gate settings and used Power Resources
static void system_init(void)
{
S3x_Clk_Enable(S3X_M4_S0_S3_CLK);
S3x_Clk_Enable(S3X_M4_S4_S7_CLK);
S3x_Clk_Enable(S3X_M4_S8_S11_CLK);
S3x_Clk_Enable(S3X_M4_S12_S15_CLK);

/* FPU settings ------------------------------------------------------------*/
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */

/* Configure Memory to support light sleep and retention mode */
PMU->M4SRAM_SSW_LPMF = 0xFFFF;
PMU->M4SRAM_SSW_LPMH_MASK_N = 0xFFFF;

S3x_Clk_Enable(S3X_FB_16_CLK);
S3x_Clk_Enable(S3X_FB_21_CLK);

S3x_Clk_Enable(S3X_A1_CLK);
S3x_Clk_Enable(S3X_CFG_DMA_A1_CLK);

INTR_CTRL->OTHER_INTR = 0xffffff;
DMA_SPI_MS->DMA_CTRL = DMA_CTRL_STOP_BIT;

}

static void ldo_init(void)
{
/* LDO Settings /
//AIP->LD0_30_CTRL_0 = 0x1ac; // LDO Enable /
0x1ac → Vo =1.01V, imax = 7.2mA, LDO enabled. /
//AIP->LD0_50_CTRL_0 = 0x1ac; // LDO Enable
AIP->LD0_30_CTRL_0 = 0x28c; // LDO Enable /
0x28c → Vo =1.15V, imax = 7.2mA, LDO enabled. /
AIP->LD0_50_CTRL_0 = 0x28c; // LDO Enable
}
/

  • Setup the SPI master for Flash chip
    */
    static void SPIM_Setup(void)
    {

    //SPI master init for SPI flash
    spiFlashHandle.Init.ucFreq = SPI_BAUDRATE_5MHZ; //above 5MHz does not work
    spiFlashHandle.Init.ucSPIInf = SPI_4_WIRE_MODE;
    spiFlashHandle.Init.ucSSn = SPI_SLAVE_1_SELECT;
    spiFlashHandle.Init.ulCLKPhase = SPI_PHASE_1EDGE;
    spiFlashHandle.Init.ulCLKPolarity = SPI_POLARITY_LOW;
    spiFlashHandle.Init.ulDataSize = SPI_DATASIZE_8BIT;
    spiFlashHandle.Init.ulFirstBit = SPI_FIRSTBIT_MSB;
    spiFlashHandle.ucSPIx = SPI1_MASTER_SEL;

    if(HAL_SPI_Init(&spiFlashHandle) != HAL_OK)
    {
    printf(“HAL_SPI1_Init failed\r\n”);
    configASSERT(0); //will hang here with out flash access
    return;
    }

    //make sure there is a Flash chip
    if(read_flash_id() == 0)
    {
    configASSERT(0); //will hang here if cannot read flash
    }

    return;
    }
    /*

  • Setup the UART for 115200bps
    */
    static void uart_setup(void)
    {
    int uart_id;
    UartBaudRateType brate;
    UartHandler uartObj;
    memset( (void *)&(uartObj), 0, sizeof(uartObj) );

    uart_id = UART_ID_HW;
    brate = BAUD_115200;

    uartObj.baud = brate;
    uartObj.wl = WORDLEN_8B;
    uartObj.parity = PARITY_NONE;
    uartObj.stop = STOPBITS_1;
    uartObj.mode = TX_RX_MODE;
    uartObj.hwCtrl = HW_FLOW_CTRL_DISABLE;
    uartObj.intrMode = UART_INTR_ENABLE;

    uart_init( uart_id, NULL, NULL, &uartObj);
    }

HAL_StatusTypeDef HAL_GPIO_IntrCfg(GPIOCfgTypeDef *hGpioCfg)
{
uint32_t *pRegOffset;
PadConfig xPadConf;

//configure the PAD as GPIO
xPadConf.ucPin = hGpioCfg->usPadNum;
xPadConf.ucFunc = hGpioCfg->ucFunc;
xPadConf.ucCtrl = PAD_CTRL_SRC_A0;
xPadConf.ucMode = PAD_MODE_INPUT_EN;
xPadConf.ucPull = hGpioCfg->ucPull;
xPadConf.ucDrv = PAD_DRV_STRENGHT_4MA;
xPadConf.ucSpeed = PAD_SLEW_RATE_SLOW;
xPadConf.ucSmtTrg = PAD_SMT_TRIG_DIS;

HAL_PAD_Config(&xPadConf);
NVIC_DisableIRQ(Gpio_IRQn);
pRegOffset = (uint32_t*)S_INTR_SEL_BASE;
pRegOffset += (((gpio_intr_cfg[hGpioCfg->usPadNum] & 0xF0) >> 4));
*pRegOffset = gpio_intr_cfg[hGpioCfg->usPadNum] & 0xF;
	
//configure GPIO     
INTR_CTRL->GPIO_INTR_TYPE &= (~((uint32_t) (0x1) << hGpioCfg->ucGpioNum)); //zero the bit position
INTR_CTRL->GPIO_INTR_TYPE |= (hGpioCfg->intr_type << hGpioCfg->ucGpioNum); //update the bit with desired value
    
INTR_CTRL->GPIO_INTR_POL &= (~((uint32_t) (0x1) << hGpioCfg->ucGpioNum)); //zero the bit position
INTR_CTRL->GPIO_INTR_POL |= (hGpioCfg->pol_type << hGpioCfg->ucGpioNum); //update the bit with desired value

// printf(“gpio_intr_type = %x, gpio_intr_pol = %x\r\n”,INTR_CTRL->GPIO_INTR_TYPE,INTR_CTRL->GPIO_INTR_POL);

//Clear the interrupt register
INTR_CTRL->GPIO_INTR |= (1 << hGpioCfg->ucGpioNum);
//configure GPIO interrupt
INTR_CTRL->GPIO_INTR_EN_M4 |= (1 << hGpioCfg->ucGpioNum);

NVIC_ClearPendingIRQ(Gpio_IRQn);
NVIC_SetPriority(Gpio_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
NVIC_EnableIRQ(Gpio_IRQn);

return HAL_OK;

}

void configure_s3_gpio_interrupts(GPIOCfgTypeDef *p_table, int nitems)
{
for (int k = 0; k < nitems; k++)
{
HAL_GPIO_IntrCfg(&p_table[k]);
}
}

Current Situation

Theoretically, this should be a very simple implementation, but for some reason, the blue LED on the board does not light up, and the button press interrupt is not detected. From what I have observed, the declaration and enabling of the interrupt are done in the qf_hardwaresetup.c file through the call to the function configure_s3_gpio_interrupts(gpiocfg_table, sizeof_gpiocfg_table).

I already know that the code is functional in the polling version but I can`t make it work with interruptions.

Request for Help

I am not using any RTOS or similar, and I cannot figure out what is wrong or what I might be missing. If anyone has any insights or suggestions on how to fix this, I would be incredibly grateful!

Thank you very much in advance for your time and help!