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

GPIO pin reading wrong value

There seems to be an issue when reading GPIO pins returning false readings. I am using two pins for I2C communication, 26 and 27, I am configuring them with the required configuration for using I2C and then setting them high. However, when I go to read the inputs of these pins (which are floating, the only thing attached to them is an oscilloscope) they are falsely read as high when they are low (verified on the scope). When I then manually read the pin status from gdb I correctly see that both pins are low. Even stranger is that on the first power up I do not see the issue and the pins are correctly read as low, but if run the code a second time without restarting then I start to see the issue.

NRF_GPIO->PIN_CNF[SCLPin] = 
    (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
  | (GPIO_PIN_CNF_DRIVE_S0D1     << GPIO_PIN_CNF_DRIVE_Pos)
  | (GPIO_PIN_CNF_PULL_Disabled  << GPIO_PIN_CNF_PULL_Pos)
  | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
  | (GPIO_PIN_CNF_DIR_Output     << GPIO_PIN_CNF_DIR_Pos);    

NRF_GPIO->PIN_CNF[SDAPin] = 
    (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
  | (GPIO_PIN_CNF_DRIVE_S0D1     << GPIO_PIN_CNF_DRIVE_Pos)
  | (GPIO_PIN_CNF_PULL_Disabled  << GPIO_PIN_CNF_PULL_Pos)
  | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
  | (GPIO_PIN_CNF_DIR_Output     << GPIO_PIN_CNF_DIR_Pos);    

nrf_gpio_pin_set(SDAPin);
nrf_gpio_pin_set(SCLPin);

unsigned int SDAVal = nrf_gpio_pin_read(SDAPin);
unsigned int SCLVal = nrf_gpio_pin_read(SCLPin);
SDAVal = nrf_gpio_pin_read(SDAPin);
SCLVal = nrf_gpio_pin_read(SCLPin);
SDAVal = nrf_gpio_pin_read(SDAPin);
SCLVal = nrf_gpio_pin_read(SCLPin);
SDAVal = nrf_gpio_pin_read(SDAPin);
SCLVal = nrf_gpio_pin_read(SCLPin);
SDAVal = nrf_gpio_pin_read(SDAPin);
SCLVal = nrf_gpio_pin_read(SCLPin);
SDAVal = nrf_gpio_pin_read(SDAPin);
SCLVal = nrf_gpio_pin_read(SCLPin);

if( SDAVal && SCLVal )
{
    //Continue with I2C initialisation here
}

I've added the pin reading multiple times to ensure it isn't related to errata 3.48 (using the Rev 1 sillicon). Here's output from debugging with GDB:

Breakpoint 1, ###
    at ###.c
:229
229         if( SDAVal && SCLVal )
(gdb) print SDAVal
$1 = 0 '\000'
(gdb) c
Continuing.

--- this is where I rerun the function ---

Breakpoint 1, ###
    at ###.c
:229
229         if( SDAVal && SCLVal )
(gdb) print SDAVal
$2 = 1 '\001'
(gdb) print nrf_gpio_pin_read(26)
$3 = 0
(gdb) print nrf_gpio_pin_read(27)
$4 = 0
(gdb) print nrf_gpio_pin_read(1)
$5 = 1

Any idea why this is happening? I'm using the S132 v5.0.0 softdevice on an nRF52832

  • Hi,

    Have you tested other pins? In your GDB log you read pin 1 for example. In my tests it seems to behave similarly on all pins.

  • I haven't tested over pins as we only allow I2C on one set of pins which isn't configurable. Will there be a fix for this issue in an updated SDK or is it not possible to fix via software?

  • Hi,

    After further investigation I'm not sure that I'm able to reproduce what your are seeing afterall, and all I can see is expected behaviour. Let us start with this:

    However, when I go to read the inputs of these pins (which are floating, the only thing attached to them is an oscilloscope) they are falsely read as high when they are low (verified on the scope)

    I used your pin configuration, tied the pin to GND with a wire, and toggled the pin in software like this:

    NRF_GPIO->OUTCLR = 1 << PIN;
    val = NRF_GPIO->IN & (1 << PIN);
    
    /* SET */ 
    NRF_GPIO->OUTSET = 1 << PIN;
    val = NRF_GPIO->IN & (1 << PIN);
    

    At no point did I read the pin as high.

    Furthermore, considering your configuration:

    NRF_GPIO->PIN_CNF[pin] = 
        (GPIO_PIN_CNF_SENSE_Disabled << GPIO_PIN_CNF_SENSE_Pos)
      | (GPIO_PIN_CNF_DRIVE_S0D1     << GPIO_PIN_CNF_DRIVE_Pos)
      | (GPIO_PIN_CNF_PULL_Disabled  << GPIO_PIN_CNF_PULL_Pos)
      | (GPIO_PIN_CNF_INPUT_Connect  << GPIO_PIN_CNF_INPUT_Pos)
      | (GPIO_PIN_CNF_DIR_Output     << GPIO_PIN_CNF_DIR_Pos);   
    

    It sounds like you may know this, but this configuration disables both the pull up and the pull down resistor. Equally important though, it also disconnects the GPIO output from the pad whenever the pin is set high (S0D1 = Standard drive low, and disconnect high). This means that if you try to set the pin high with e.g. nrf_gpio_pin_set() the pin is actually left floating, while if you set it low it is firmly tied down. As you probably know, a floating pin is left to the mercy of the elements and if you try to read the state of such a pin you will not get reliable results.

    If you want to get correct readings you can either:

    1. Enable the pull up resistor. This will pull the pin high even though it is disconnected when you set the pin with nrf_gpio_pin_set().
    2. Use S0S1 or S0H1. These settings will ensure that the GPIO output is not disconnected when you set the pin high.
Related