Where in datasheet is the warning about unreliability of reading PINxn?
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.
A test with an Arduino Uno.
I used the usb connector to power-on the arduino uno. The led is at PB5, so I have exchanged PB0 and PB5. My test is therefor not the same.
#define LED_PB PB5 // pin 13, onboard led
#define INP_PB PB0 // pin 8, antenna
void setup(void)
{
noInterrupts();
PORTB |= 1 << INP_PB; /* pullup */
DDRB |= 1 << LED_PB; /* LED */
if (!(PINB & 1 << INP_PB))
PORTB |= 1 << LED_PB; /* turn on LED */
while (1) ;
}
void loop()
{
}
Using it next to my computer and phone, sometimes the led stays on. I can even find a spot behind my computer monitor (with CCFL backlight) where the led always turns on.
Then I went outside, away from metals, wifi, and so on. The air humidity outside is 70% at the moment, which is high. I put it on a wooden table, so there is no charge by plastic. I used a battery to power the arduino uno. Then the led never turns on.
In such a clean environment, the internal pullup resistor was probably strong enough to pull the pin high during that very short time.
@IgorLiferenko, @sa_leinad gave the right answer. What you noticed is normal. It is totally normal. That behaviour can be expected. It will be worse when your circuit is not well designed.
A oscilloscope will not help. As soon as you connect a oscilloscope, you have changed something. It will also be very hard to detect that very short time (less than a microsecond).
In my opinion the influence by radio waves is only half of the problem. I think the electrical charge of the air, and the capacitive coupling of the wire with the air might have a larger effect.
You have made an antenna with the 10cm wire and are receiving radio waves which is translating to voltages on your input pin. This is why you don't have the same trouble when the wire is not connected.
According to www.csgnetwork.com/freqwavelengthcalc.html your 10cm wire is picking up frequencies of 750 MHz based on a quarter wavelength calculation.
The datasheet states that the internal pull up resistance is roughly between 20K - 50K ohms. As mentioned in the comments, this is quite weak.
There are two things that you can do that I can see:
- Add a stronger pull up resistor (a value between 1K and 5K ohms should do)
- Add a decoupling/bypass capacitor.
The reason that this is not in the datasheet is because this is a general electronics/physics knowledge, not a characteristic of the micro-controller itself.