8
\$\begingroup\$

I used to work with C but I am trying to learn using C++ for embedded programming. I want to do a basic GPIO interrupt example to understand the structure of a class and its usage.

My microcontroller is an M031SD2AE which is Nuvoton made and ARM Cortex M0 based. This code does not work. It never goes into the GPIO interrupt. What seems to be a problem here?

This is my code:

main.cpp:

#include "M031Series.h"
#include "TSMBTN.h"

uint32_t u32count;

void GPABGH_IRQHandler(void)
{
    if(GPIO_GET_INT_FLAG(PB, BIT2))
    {
        u32count++;
        if(u32count == 5)
        {
            LED = 0;
        }
    }
    GPIO_CLR_INT_FLAG(PB,BIT2);
}

int main(void)
{
    TSMBTN.sys_init();
    TSMBTN.gpio_init();

    while(1);
}

TSMBTN.cpp:

#include "TSMBTN.h"

#define LED PB14

TsmBtn TSMBTN;

void TsmBtn::gpio_init()
{
    GPIO_SetMode(PB,BIT2,GPIO_MODE_INPUT); //PB2->input mode
    GPIO_EnableInt(PB,2,GPIO_INT_RISING);   //PB2->interrupt rising edge enabled
    NVIC_EnableIRQ(GPIO_PAPBPGPH_IRQn);
    
    GPIO_SetMode(PB,BIT14,GPIO_MODE_OUTPUT);
}

void TsmBtn::sys_init()
{
    /* Unlock protected registers */
    SYS_UnlockReg();

    /* Enable clock source */
    CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

    /* Waiting for clock source ready */
    CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

    /* Disable PLL first to avoid unstable when setting PLL */
    CLK_DisablePLL();

    /* Set PLL frequency */
    CLK->PLLCTL = (CLK->PLLCTL & ~(0x000FFFFFul)) | 0x0008C03Eul;

    /* Waiting for PLL ready */
    CLK_WaitClockReady(CLK_STATUS_PLLSTB_Msk);

    /* If the defines do not exist in your project, please refer to the related clk.h in the Header folder appended to the tool package. */
    /* Set HCLK clock */
    CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));

    /* Set PCLK-related clock */
    CLK->PCLKDIV = (CLK_PCLKDIV_APB0DIV_DIV1 | CLK_PCLKDIV_APB1DIV_DIV1);

    /* Enable IP clock */
    CLK_EnableModuleClock(ISP_MODULE);
    CLK_EnableSysTick(CLK_CLKSEL0_STCLKSEL_HCLK, 0);

    /* Update System Core Clock */
    /* User can use SystemCoreClockUpdate() to calculate SystemCoreClock. */
    SystemCoreClockUpdate();

    /* Lock protected registers */
    SYS_LockReg();
}

void TsmBtn::led_on()
{
    LED = 0;
}

TSMBTN.h:

#include "M031Series.h"

class TsmBtn 
{
    public: 
        void gpio_init();
        void led_on();
        void sys_init();
};

extern TsmBtn TSMBTN;
\$\endgroup\$
9
  • 4
    \$\begingroup\$ Could be c++ name mangling. Need to add extern “c” to your isr function. I’ve been bitten by this at least once. If you look at your linker listing you’ll find the isr vector is not touched. Google c++ name mangling \$\endgroup\$
    – Kartman
    Commented Aug 18, 2022 at 7:24
  • 1
    \$\begingroup\$ As written, this is a pure software question so it's on the wrong site. People here may enjoy answering it but future readers with the same software problem should have it somewhere they can find it. Can only VTC as the migrate options here don't give 'migrate to SE embedded/other'. \$\endgroup\$
    – TonyM
    Commented Aug 18, 2022 at 7:59
  • 6
    \$\begingroup\$ @TonyM - Understand your point, but firmware-related programming questions (including those related to bare-metal interrupt handlers) are on-topic here. This overlaps with SO's remit, but that doesn't make them inappropriate here, based on the remit of this site. (If it wasn't firmware-related programming, that would be different.) \$\endgroup\$
    – SamGibson
    Commented Aug 18, 2022 at 8:58
  • 4
    \$\begingroup\$ If the underlying root cause is confirmed, this seems a likely duplicate of "Bug in Keil ARM compiler with interrupt handlers and C++?" (and related). \$\endgroup\$
    – SamGibson
    Commented Aug 18, 2022 at 9:01
  • 1
    \$\begingroup\$ How does the chip know that GPABGH_IRQHandler is the IRQ handler? Is it because of the name? Googling it does not bring many results. \$\endgroup\$ Commented Aug 18, 2022 at 17:07

1 Answer 1

9
\$\begingroup\$

If you are 100% sure about the interrupt setup being correct, this is a generic how to make C++ code callable from C question which is much better explained elsewhere, but in short, you don't have a function with correct C callable name due to C++ name mangling.

The interrupt vector expects a function that has a specific C callable name.

And because you define your IRQ handler function in C++ compiled file without declaring C linkage, there is no C callable name for your IRQ hander.

And since it does not exist, the vector points to default handler due to weak linkage.

This is both nice and annoying, because weak linkage allows you to write your program and you don't need to add dozens of interrupt handlers you don't need, but only the ones you want to use. On the other hand, if you forget to write the interrupt handler, or have a typo in the name, or compile it as a C++ function, you don't get an error about missing interrupt handler because weak linkage uses the default handler.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This is only true for platforms where the vector table comes with default names and implement weak linkage. Typically the average Cortex M does this, but it's uncommon on other platforms in my experience. I only ever recall seeing it for ARM, but the OP appears to be using a Cortex M indeed. \$\endgroup\$
    – Lundin
    Commented Aug 18, 2022 at 13:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.