Cannot PULL_UP pins on 40-pin header on WioTerminal

I cannot pull-up any of the pins on the 40-pin header on the WioTerminal using ArduPy. I’ve been trying to hook-up tactile switches to a few pins and can’t get any of them to work.

I have tried all of the pins from 11-40 (except power/ground) pins using the simple program below. GND & 3V3 are all 0 and 3.3 as expected, but all of the other pins show up as ~0.5V, except the two I2C0 pins which are always 3V3 (regardless of whether you pull them up or not).

I have gotten some output LEDs working fine on pins 3,5,7,8, so at least some GPIO seems to work fine for me.

Does anyone know what the problem is here? Is there something that I’m missing in the code?

from machine import Pin
import time

pins = (11, 13, 15, 19, 21, 23, 29, 31, 33, 35, 37,
        10, 12, 16, 18, 22, 24, 26, 32, 36, 38, 40)
for p in pins:
    Pin(p, Pin.IN, Pin.PULL_UP)
print("DONE")
while True:
    time.sleep(1)
    pass
print("OOPS")

Just in case the Pin object was getting immediately de-allocated and reset to some particular state (since I wasn’t retaining its value in this test), I modified the code to retain a reference to the returned object, but that didn’t make any difference in behavior.

from machine import Pin
import time

pins = (11,12,13,15,16,18,19,21,22,23,24,26,27,28,31,32,33,35,36,37,38,40)

b=[]
for p in pins:
   print(p)
   b.append(Pin(p, Pin.IN, Pin.PULL_UP))
print("READY")

while True:
    time.sleep(0.25)
    pass

Hi @ericwertz

Just tested and experienced the same thing. Let me have a look at the sourcecode and findout whats wrong

I just looked at the schematic and saw that the three top buttons have hard pull-ups, so it’s possible that programmable pull-ups (and perhaps downs) don’t work at all in the current release.

Glad to hear that you can reproduce the problem. Thanks for checking into it.

I also had a quick test using Arduino IDE and seems input pullup is working fine, so there should be something error in the ArduPy core bit, will look into it. Thanks for spotting this out :smiley:

I’ve never looked at MicroPython code before, but it appears that there are multiple problems in mod_machine_pin.c. In particular, in machine_pin_obj_init_helper() where the pullups appear to need to be handled.

The first (and easiest) problem has to do with the comment “// set initial value (do this before configuring mode/pull)” has to be incorrect, because the code does the opposite of what this comment says to do. On the ATmega328, this order of setting the DDRx and PORTx bits was important because of the kludge of using PORTx as the pullup register control when you wanted to switch the pin direction from INPUT* to OUTPUT, because you might have to go through a bus cycle where the output could be driven momentarily with the wrong value before the correct one was established. Fortunately this problem was eliminated by giving pullups/downs their own register(s) in SAMD.

I believe that the correct fix for this is to move the if-statement for setting the value to be before the mode is set, as the comment states. The if-statement condition here seems like it could (or should) also be changed from “ARG_value != NULL” to add “&& ARG_mode==OUTPUT”, because no digitalWrite() needs to be done here if the mode isn’t OUTPUT.

The second (and originally discovered problem) is that turning on pull-ups/downs does not appear to be coded here anywhere in Pin…

It seems like the general flow of the initialization should be (sorry, but it doesn’t look like Preformatted Text is working below):
if (mode == none) {
return error
}
if (mode == OUTPUT) {
if (value != NULL) {
digitalWrite(value)
}
pinMode(OUTPUT)
}
else {
pinMode(mode) // either INPUT or INPUT_PULLUP
}

Lastly, it seems like there are a lot of possibilities for out of range input parameters – namely mode not OUTPUT, INPUT or INPUT_PULLUP, value not being 0 or 1, or a value being specified if mode != OUTPUT. However, I don’t know if these are all treated as “user beware” as a (conscious) matter of policy or not, or if this idiot-checking is done elsewhere, or…

By the way, I was able to find a combination of parameters that actually enables me to turn on the pullups using the existing buggy implementation, but this only works because the Arduino core has grandfathered the behavior of pinMode(INPUT) followed by a digitalWrite(HIGH) to turn on the pullups as was originally required for the ATmega328 before pinMode(INPUT_PULLUP) existed. Knowing this I was able to trick the current ArduPy code (which makes no sense as ArduPy/MicroPython code) to do what I wanted. That is, if you use:
Pin(p, Pin.IN, pull=Pin.PULL_UP, value=1)

So, until Pin.init gets fixed, at least I have a workaround…

Hope that helps,
-e