did somebody come across such case, or I'm the first in the world?
You are not the first. I recently got bitten by the very same issue.
However, unless you are close to an unusually strong radio source, I do
not think it has anything to do with electromagnetic interference. In my
experience, the internal pullup is perfectly reliable for reading
switches and push buttons, even on a breadboard with jumper wires acting
as antennas. If you use pinMode()
and digitalRead()
instead of
direct port access, you too will find the internal pullup is perfectly
fine.
Before I give you the answer (suspense...), let's look closely at what
your code does. The disassembly looks like this:
main:
sbi PORTB, PB5 ; turn on pullup on PB5
sbi DDRB, PB0 ; set LED pin as output
sbis PINB, PB5 ; read PB5, if it is LOW then:
sbi PORTB, PB0 ; turn on LED
0: rjmp 0b ; hang in an infinite loop
In the situation when the LED turns on, every instruction there executes
in two CPU cycles, save for sbis
, which takes a single cycle in this
case. Now, let's look at the timings, and specifically at the states of
the pullup and the input circuitry during each of the first seven CPU
cycles:
cycle instruction pullup latch PINB5
----------------------------------------
1 sbi PORTB, PB5 off X X
2 cont. off X X
3 sbi DDRB, PB0 on X X
4 cont. on LOW X
5 sbis PINB, PB5 on X LOW
6 sbi PORTB, PB0 on X X
7 cont. on X X
In the table above, “X” means “don't care”. Each instruction takes
effect at the beginning of the next CPU cycle, when the clock's rising
edge commits the result of the instruction to the affected flip-flops.
That's why the pullup turns on only at the beginning of cycle 3.
Now, since the pin did read LOW, that means the PB5 flip-flop was LOW
while the CPU was reading it, during cycle 5. This in turn means
that the synchronizing latch before that flip-flop was LOW during
cycle 4.
Your expectation was for the pin to read HIGH. So let's see: how fast
should the pin voltage have risen in order for the pin to indeed read
HIGH? Since the input latch is transparent during the first half of each
cycle, this means that the voltage should raise past the Schmitt
trigger's threshold during cycle 3 and the first half cycle 4.
So the rise time should be less than 1.5 cycles. Or rather
1.5 cycles minus the combined propagation delay of the PORTB5
flip-flop and the logic that controls the pullup from that flip-flop's
output. Probably around 90 ns. That's short!
By now you have probably guessed: the culprit is not electromagnetic
interference, it's stray capacitance. A simple calculation will show you
that about three picofarads is enough to produce the effect you are
seeing. This is the kind of stray capacitance you will find everywhere,
even between the PCB traces of your Arduino board. Note that
electromagnetic noise can add some unpredictability to what you see, as
the pin is susceptible to it when it's completely floating, i.e.
before you turn on the pullup, which makes the initial voltage (at the
beginning of cycle 3) unpredictable. But once the pullup is on and
the pin has settled, you won't care about noise anymore, unless it's
unusually strong.
The solution to your problem is simply to wait for the pullup to charge
the stray capacitance. A microsecond delay should be enough to fix the
issue. Alternatively, use the Arduino functions instead of direct port
access: they are so slow that you will not need any extra delay.
where in datasheet there is a warning about this gotcha?
In section 18.2.4 – Reading the Pin Value: “[The synchronizing latch] is
needed to avoid metastability if the physical pin changes value near the
edge of the internal clock, but it also introduces a delay.”
(emphasis mine).
Edit: In a previous version of this answer, I incorrectly stated
that sbi
is a single cycle instruction, which required the voltage to
rise in half a cycle instead of 1.5 cycles. The corrected timing is
still short enough for stray capacitance to be a valid explanation of
the observed behavior.
In summary:
- The stray capacitance attached to the pin, together with the pullup
resistor, for an RC circuit.
- As the pin is initially floating, it's initial voltage is
unpredictable.
- When the pullup is turned on, the voltage starts to rise from whatever
initial value it had to 5 V.
- After some time, which can be typically of the order of the RC time
constant, the input Schmitt trigger turns HIGH.
- After some extra delay introduced by the synchronizer, the PINxn
flip-flop turns HIGH.
- If the CPU reads the PINx register before that happens, it gets a LOW
reading.
I know that internal pullup is not useful for anything
... that is not true ... it works great for switches .... also you abused the intended usage of the chip .... you should not have an antenna connected to the input pin – jsotola Aug 1 '18 at 3:17