Electronic – wrong with this PIC pin change detection

cmicrocontrollerpicspi

I have two PIC18F4620 connected via SPI + Slave Select + additional IRQ line. Both controllers are driven from the same crystal oscillator using the same clock settings. The master sends one byte and then waits until the slave toggles that additional IRQ line. The duration of the toggling is 4 instruction cycles. All the edges look good on the scope and SPI communication works properly, except for the detection of the toggling (while(!PORTBbits.RB1);).

This is my SPI sending code:

    while (spi_out_msg_buffer.write_cursor > spi_out_msg_buffer.read_cursor)
    {
        DisableInterrupts;
        SSPBUF = spi_out_msg_buffer.data[spi_out_msg_buffer.read_cursor];
        LATBbits.LATB0 = 1;
        while(!PORTBbits.RB1); // wait for toggle on IRQ line
        LATBbits.LATB0 = 0;
        EnableInterrupts;
        spi_out_msg_buffer.read_cursor++;
    }

The while(!PORTBbits.RB1); is translated into two instructions:

BTFSS PORTB, 1, ACCESS
BRA 0x188

I insterted that B0 line for debugging purposes, you can see it at the very bottom of this timing diagram:

timing

You can see the toggling of the IRQ line (second from the bottom) and how it goes undetected, because the B0 debugging line stays high. When I stop the execution via ICD it hangs inside the while.
It is worth to mention that it usually works for a few bytes and then stops, as you can see here:

whole timing

I measured that the pulse is actually 4 instruction cycles (= 16 PLL cycles = 4 clock cycles) long:

pulse length

I think that should be sufficient for the pulse to be detected. Even if the first BTFSS misses it because the port is sampled at the beginning of the instruction cycle, then the second one should get it:

even more timing

10 MHz -> PLL -> 40 MHz -> 10 M instructions per second -> 100 ns per instruction.

Shouldn't that be long enough to exit the while?

Best Answer

You're using a compiler, so you have no idea how many cycles that polling loop takes. In assembler you could get it down to 3 cycles, but you gave up the right to count cycles when you wrote the code in a high level language.

However, the real problem is the overall approach. Asking the code to catch a short glitch is not a good idea. Even if you could guarantee the loop time is less than the glitch time, you can now not turn on interrupts during the wait. This may present architectural problems later.

A much better idea is to use the hardware you already have to catch the glitch, then have the firmware check that hardware. The simplest would be to wire the glitch into one of the INTx lines, then look for the INTxIF flag. Change detect pins would also work, but keep in mind the flag gets set on both edges and you have to clear the mismatch to clear the condition.