sparkfun / mbed-os-ambiq-apollo3

Arm Mbed OS is a platform operating system designed for the internet of things
https://mbed.com
Other
18 stars 13 forks source link

Unexpected behaviour of the InterruptIn API on the Artemis Thing Plus #29

Open idea--list opened 4 years ago

idea--list commented 4 years ago

Description of defect

The InterruptIn API does not behave as expected on the Artemis Thing Plus board, while on the MAX32630FTHR the same code results in the expected behaviour.

Target(s) affected by this defect ?

Sparkfun Artemis Thing Plus (may be all the other Artemis boards as well but i can not test them)

Toolchain(s) (name and version) displaying this defect ?

Both ARMC6 and GCC_ARM (9-2019-q4-major)

What version of Mbed-os are you using (tag or sha) ?

Branch of the Mbed-Os-Ambiq-Apollo3 with ARMC6 support.

What version(s) of tools are you using. List all that apply (E.g. mbed-cli)

Mbed Studio 1.2

How is this defect reproduced ?

Compile the following code for the Artemis board: #include "mbed.h" InterruptIn button(SW1, PullUp); DigitalOut led(LED_BLUE); void buttonFall() { led = 1; } void buttonRise() { led = 0; } int main() { button.fall(callback(buttonFall)); button.rise(callback(buttonRise)); while (1) { printf("Button state: %d\n", button.read()); } return 0; }

When run on the Artemis Thing Plus you will see the button state updating in the terminal window, however the led stays always on. It seems as if the interrupt handler functions would not have any effect, despite there is evidence about the state change in the instance of the InterruptIn object. This is unexpected.

If i compile the same code for my MAX32630FTHR then both the button state updates in the terminal window and also the led turns on and off as expected.

On the Artemis board i can use the following code to turn the led on and off, however by this way i am actually polling the state of the InterruptIn instance all the time and change the led's state from within the while loop in which case there is no sense in using any interrupts: #include "mbed.h" InterruptIn button(SW1, PullUp); DigitalOut led(LED_BLUE); void buttonFall() { led = 1; } void buttonRise() { led = 0; } int main() { button.fall(callback(buttonFall)); button.rise(callback(buttonRise)); while (1) { printf("Button state: %d\n", button.read()); if (button.read() == 0) { led = 1; } else { led = 0; } } return 0; }

idea--list commented 4 years ago

This issue forced me to begin learning AmbiqSDK (which at first seemed really alien to me). Found out i can even mix AmbiqSDK code right within an mbed-os project and it would still work (for example i can copy&paste the AmbiqSDK blinky code just under the very first #include "mbed.h" line of an mbed-project). I really love it as that makes learning the SDK much easier for me.

Next i compiled&downloaded the deepsleep_wake example found in the AmbiqSDK to the Artemis Thing Plus. As a native AmbiqSDK project it successfully runs on the board.

So next i began mixing AmbiqSDK code within an mbed-os project to try to find when things begin to break. This code does&behaves the same as the mbed only code in my opening comment: #include "mbed.h" DigitalOut led(LED_BLUE); uint32_t pin14status; void buttonFall() { am_util_delay_ms(200); AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); led = 1; } void buttonRise() { am_util_delay_ms(200); AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); led = 0; } const am_hal_gpio_pincfg_t g_deepsleep_button0 = { .uFuncSel = 3, .eIntDir = AM_HAL_GPIO_PIN_INTDIR_LO2HI, .eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE, }; int main() { am_hal_gpio_pinconfig(SW1, g_deepsleep_button0); am_hal_gpio_interrupt_register(SW1, buttonRise); AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); am_hal_gpio_interrupt_enable(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); //NVIC_EnableIRQ(GPIO_IRQn); am_hal_interrupt_master_enable(); while (1) { am_hal_gpio_state_read(SW1, AM_HAL_GPIO_INPUT_READ, &pin14status); printf("Button status: %d led value: %d\n", pin14status, led.read()); } return 0; } (So pin14status changes as i press the button, but the led state does not update as the interrupt handlers do not get executed at all. That also means it does not interrupt any running process).

If i uncomment //NVIC_EnableIRQ(GPIO_IRQn); to make it effective, mbed-os will begin to throw a hard fault error whenever i press the button configured as an interrupt.

To sum it up the NVIC_EnableIRQ() function of the AmbiqSDK just breaks in an mbed-os code.

EDIT: for those who would prefer writing AmbiqSDK code in an mbed-os project i found out that we need to call NVIC_SetVector before using NVIC_EnableIRQ. That way NVIC_EnableIRQ(GPIO_IRQn); will not cause hard faults anymore. So the above example should be: #include "mbed.h" DigitalOut led(LED_BLUE); uint32_t pin14status; void buttonFall() { am_util_delay_ms(200); AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); led = !led; } const am_hal_gpio_pincfg_t g_deepsleep_button0 = { .uFuncSel = 3, .eIntDir = AM_HAL_GPIO_PIN_INTDIR_LO2HI, .eGPInput = AM_HAL_GPIO_PIN_INPUT_ENABLE, }; int main() { am_hal_gpio_pinconfig(SW1, g_deepsleep_button0); am_hal_gpio_interrupt_register(SW1, buttonFall); AM_HAL_GPIO_MASKCREATE(GpioIntMask); am_hal_gpio_interrupt_clear(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); am_hal_gpio_interrupt_enable(AM_HAL_GPIO_MASKBIT(pGpioIntMask, SW1)); NVIC_SetVector(GPIO_IRQn, (uint32_t)buttonFall); NVIC_EnableIRQ(GPIO_IRQn); am_hal_interrupt_master_enable(); while (1) { am_hal_gpio_state_read(SW1, AM_HAL_GPIO_INPUT_READ, &pin14status); printf("Button status: %d led value: %d\n", pin14status, led.read()); } return 0; }

idea--list commented 4 years ago

Did some further testing, here is my ultimate finding:

On the MAX32630FTHR board this mbed code compiles and runs as expected (meaning i can attach 2 different ISRs to an interrupt pin: 1 ISR for falling and another 1 for rising edge): #include "mbed.h" DigitalOut led(LED_BLUE); InterruptIn button(SW1); int myVal = 111; void buttonFall() { led = 0; myVal = 0; } void buttonRise() { led = 1; myVal = 1; } int main() { button.fall(callback(buttonFall)); button.rise(callback(buttonRise)); while (1) { printf("Button status: %d myVal: %d\n", button.read(), myVal); } return 0; }

The same code fails on the Artemis Thing Plus board, however this mbed code runs also on the Artemis Thing Plus board: #include "mbed.h" DigitalOut led(LED_BLUE); InterruptIn button(SW1); int myVal = 111; void buttonFall() { led = !led; myVal = 0; } int main() { button.fall(callback(buttonFall)); while (1) { printf("Button status: %d myVal: %d\n", button.read(), myVal); } return 0; }

So it seems with the current dev branch we can attach only 1 ISR to an interrupt pin on the Artemis board. That way we can still toggle an LED, but we can not attach 2 ISRs doing different stuff on falling and rising edges.