This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

NRF_GPIO->IN reading wrong value/pin

Hello,

I have the following issue when trying to detect which GPIO triggered the interrupt:

For my low power application, I was trying to connect two switches to the nRF52DK, via the GPIOs (11 and 12).

The switches are connected between GND and VCC and therefore do not need a pull up. I configured the GPIOs in SW_PIN_CONFIG.

Using the nrf GPIOTE driver works, but I wanted a solution that consumes less power, as this is for a low power application.

Therefore, I was looking at the way the buttons of the DK are used in simple_hal.c

Implementing the switches in a similar way, they worked and toggled each the right if clause.

However they would both have to be high, before one switch could toggle an interrupt. If one switch was low it wouldn't work for some reason.

void GPIOTE_IRQHandler(void)
{
    NRF_GPIOTE->EVENTS_PORT = 0;
    
    if (TIMER_DIFF(m_last_button_press, NRF_RTC1->COUNTER) > HAL_BUTTON_PRESS_FREQUENCY)
    {
      // SW 1 Low
      if ((NRF_GPIO->IN & (1 << 11)) == 0)
      {
          m_button_handler_cb(0);
      }

      // SW 2 Low
      if ((NRF_GPIO->IN & (1 << 12)) == 0)
      {
          m_button_handler_cb(1);
      }

      m_last_button_press = NRF_RTC1->COUNTER;

    }
}

This was weird, but since I wanted the switches to trigger an interrupt on both edges anyways, I continued and added this functionality as well.

#define SW_PIN_CONFIG_LOW ((GPIO_PIN_CNF_SENSE_Low  << GPIO_PIN_CNF_SENSE_Pos)    | \
                           (GPIO_PIN_CNF_DRIVE_S0S1 << GPIO_PIN_CNF_DRIVE_Pos)    | \
                           (NRF_GPIO_PIN_NOPULL     << GPIO_PIN_CNF_PULL_Pos)     | \
                           (GPIO_PIN_CNF_INPUT_Connect << GPIO_PIN_CNF_INPUT_Pos) | \
                           (GPIO_PIN_CNF_DIR_Input  << GPIO_PIN_CNF_DIR_Pos))

#define SW_PIN_CONFIG_HIGH ((GPIO_PIN_CNF_SENSE_High    << GPIO_PIN_CNF_SENSE_Pos)    | \
                           (GPIO_PIN_CNF_DRIVE_S0S1     << GPIO_PIN_CNF_DRIVE_Pos)    | \
                           (NRF_GPIO_PIN_NOPULL         << GPIO_PIN_CNF_PULL_Pos)     | \
                           (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)    | \
                           (GPIO_PIN_CNF_DIR_Input      << GPIO_PIN_CNF_DIR_Pos))

void GPIOTE_IRQHandler(void)
{
    NRF_GPIOTE->EVENTS_PORT = 0;
    
    if (TIMER_DIFF(m_last_button_press, NRF_RTC1->COUNTER) > HAL_BUTTON_PRESS_FREQUENCY)
    {
      // SW 1 Low
      if ((NRF_GPIO->IN & (1 << 11)) == 0)
      {
          m_button_handler_cb(0);
          NRF_GPIO->PIN_CNF[11] = SW_PIN_CONFIG_HIGH;
      }

      // SW 1 return to High
      else if (NRF_GPIO->IN & (1 << 11))
      {
          m_button_handler_cb(0);
          NRF_GPIO->PIN_CNF[11] = SW_PIN_CONFIG_LOW;
      }

      // SW 2 Low
      if ((NRF_GPIO->IN & (1 << 12)) == 0)
      {
          m_button_handler_cb(1);
          NRF_GPIO->PIN_CNF[12] = SW_PIN_CONFIG_HIGH;
      }

      // SW 2 return to High
      else if (NRF_GPIO->IN & (1 << 12))
      {
          m_button_handler_cb(1);
          NRF_GPIO->PIN_CNF[12] = SW_PIN_CONFIG_LOW;
      }

      m_last_button_press = NRF_RTC1->COUNTER;

    } // End debounce if
}

This is where stuff got really confusing.

Looking at the LOG output (since NRF_GPIO->IN is restricted memory, that I cannot debug) it does seem like one pin is triggering both if clauses.

Which, if I'm not wrong, should be impossible since x & 1<11 is not the same as x & 1<<12.

So the interrupt basically calls m_button_handler_cb(0) and m_button_handler_cb(1) every time the status of one switch changes.

Is there something wrong with the way I'm checking for the PIN, that toggled the interrupt? Or is there a simpler approach (that is not increasing the power consumption).

Thank you for your help.

Sincerely,

Heiner

Parents Reply
  • Hi Vidar,

    configuring the driver to use PORT events

    could you please refer to an example or point me to an explanation on how this is achieved?


    In the GPOITE documentation it is only stated: "Turning off high accuracy [...] enables the low-power feature" and "The following code example shows how to use a pin to detect toggling using high accuracy".

    There's no example on how to use the low accuracy function of the driver, neither it's explained how to configure the driver to use a PORT event instead, at least not in that section.

    look up the addresses in the memory view

    That worked, thank you. 

    However I'm even more confused now, because the registers actually change as expected. I even changed my GPIOs to #30 and #31, to move them as far from the button GPIOs as possible, but the problem remains.

    I can not really understand, why a comparison with the NRF_GPIO->IN register would return TRUE, even if the wrong bit is toggled.

    From my understanding it should only return TRUE once, when for example Bit 31 is changing from 1 to 0 and I compare  ~NRF_GPIO->IN & 1<<31.

     ~NRF_GPIO->IN & 1<<30 should not return true in this case, if Bit 30 is still 1.

     

    Sincerely,

    Heiner

Children
  • Hi Heiner, 

    Heiner said:
    could you please refer to an example or point me to an explanation on how this is achieved?

    The driver will use PORT events if you set the .high_accuracy flag to 'false' when you configure the input. I.e., it should be sufficient to just change the accuracy flag in your original code in order to use PORT event. 

    Heiner said:
    I can not really understand, why a comparison with the NRF_GPIO->IN register would return TRUE, even if the wrong bit is toggled.

     IN is not latched and should always return the current pin state. To check if the DETECT signal has been raised for a particular pin you need to check the LATCH registers. Also, note that LATCH must be manually cleared to detect the next input transition. Recommend to use the GPIOTE/button handler library instead of creating your own code. 

    Best regards,

    Vidar

  • Hi Vidar,

    I tried it again with your recommendation

    set the .high_accuracy flag to 'false'

    and that worked. Thank you for the explanation.

    It might be useful for other people reading this thread to know, that you also have to modify GPIOTE_CONFIG_NUM_OF_LOW_POWER_EVENTS in sdk_config.h to the number of low power events, or else it won't work correctly.

    Tank you again for your help.

    Sincerely,

    Heiner

  • Thanks for the update, and for pointing out that you may need to adjust the number of events in sdk_config.

Related