Serial receiver (Arduino Micro ATmega32U4) in bad state
Ok, there are some issues in your code. First of all, are you sure that a baudrate  of 3.7Mbps is what you need? Can't you go a bit slower?

If you are dealing with mechanical parts (e.g. buttons), then a refresh rate of 100Hz cannot be noticed; to send 3 bytes you need 30 bits, and at 9600 bit/s this means 320 Hz of frequency. Reducing the speed increases the rejection to noise too, so I'd go with a lower speed (even a 38.4kbps is fine).

Then you are reading the PINx registers too many times, which can lead to inconsistency. What happens if the value changes after the if but before the updating of the value? It's better to store it in a variable.

These, however, are not the root cause of your problem. The main issue, as anticipated by Jot, is that you are not using a protocol to synchronize the two elements. This means that at the bootup the receiver does not know if it is at the beginning of the "frame" or in the middle. Moreover start conditions when the sender is not online are not ignored.

So a "protocol" must be agreed between the two. The simplest one I can think of revolves around the fact that you can reserve a bit for the flow control.

Looking at your port masks, I can see that if we split the PINB values in the two nibbles and then move the lowest one one bit to the left, we get that bit 3 is never used for transmitting a pin data:

PINB 76543210 -&gt; 7654x321 (pin 0 was not used)


consequently we use this as a control bit. It has value 1 for the first bit in the frame, and 0 otherwise. On the receiver side, you look for a byte with this bit set, then record also the other two, then check if the other two have a 0 in this position and, if confirmed, you have your frame.

This solution has the highest throughput, but may be a bit fragile when some noise is present (false start of frames, possibly).

For this reason I suggest you to add another byte to the packet, thus having to transmit four bytes. Again, the first one has the bit set in position 3, and the other have zero. But in this case the first byte has a predefined value, and consequently the receiver has just to look for this specific value (and the robustness is increased.

Ok people, I can hear your question. Where is the code? Well, here it is. For the sender, you have to perform a bit of manipulation on the bytes to be sent. I chose the hex value 0x5A because it has a lot of transitions (difficult to replicate from a noise) and it has a 1 bit in position 3. I also changed the things said at the beginning (baud rate and reading PINx only once); feel free to revert the changes if you have different needs. Moreover I initialized the old variables, and I set the direction of the pins based on the mask rather than having it done manually (easier to maintain).

EDIT: I added the possibility to get the current status by sending a char on the serial interface (useful when the receiver boots up)

byte PINBold;
byte PINDold;
byte PINFold; 

const byte PBmask = B11111110;
const byte PDmask = B11010011;
const byte PFmask = B11110011;

const byte StartOfFrame = 0x5A; // bit 3 is always '1'

void setup()
{
    Serial1.begin(38400); // Speed
    DDRB = DDRB &amp; (~PBmask); // Sets pins to digital input: 11,10,9,8,MI,MO,SCK 
    DDRD = DDRD &amp; (~PDmask); // Sets pins to digital input: 2,3,4,6,12                           
    DDRF = DDRF &amp; (~PFmask); // Sets pins to digital input: A0,A1,A2,A3,A4,A5

    PINBold = ~PBmask; // Force a 1 in a forbidden position -&gt; force a sending at first round
    PINDold = ~PDmask;
    PINFold = ~PFmask;
}

void loop() {
    byte currPINB = PINB &amp; PBmask;
    byte currPIND = PIND &amp; PDmask;
    byte currPINF = PINF &amp; PFmask;

    bool shouldSendData = false;

    if ((currPINB != PINBold) || (currPIND != PINDold) || (currPINF != PINFold))
    {
        PINBold = currPINB;
        PINDold = currPIND;
        PINFold = currPINF;
        shouldSendData = true;
    }

    while (Serial1.available())
    {
        Serial1.read();
        shouldSendData = true;
    }

    if (shouldSendData)
    {
        Serial1.write(StartOfFrame);
        Serial1.write((currPINB &amp; 0xF0) | ((currPINB &gt;&gt; 1) &amp; 0x07)); // send currPINB 7654_321, where _ is a '0'
        Serial1.write(currPIND); // bit 3 is always '0'
        Serial1.write(currPINF); // bit 3 is always '0'
    }
}


On the receiver side, what you need is to wait for the start of frame, then you have your data. I prefer to implement it as a very small state machine. I also added a bonus timeout feature to exit from the payload state after 10ms of inactivity.

EDIT: Added the sending of a char at the beginning, to trigger a resending of the status from the master (if available). Included also the functional code from the question, since the OP added the receiver

const byte StartOfFrame = 0x5A; // bit 3 is always '1'

const byte PBmask = B11111110;
const byte PDmask = B11010011;
const byte PFmask = B11110011;

// State machine states
const byte SM_WaitingForSOF = 1;
const byte SM_WaitingForPayload = 2;

byte SM_CurrentState;

bool receivedData;
byte receivedPortB;
byte receivedPortD;
byte receivedPortF;

uint8_t startOfTransmissionMillis;
const byte transmissionTimeout_ms = 10;

void setup()
{
    Serial1.begin(38400); // Speed

    DDRB = DDRB | PBmask; // Sets pins to digital output: 8,9,10,11,MI,MO,SCK
    DDRD = DDRD | PDmask; // Sets pins to digital output: 2,3,4,6,12
    DDRF = DDRF | PFmask; // Sets pins to digital output: A0,A1,A2,A3,A4,A5

    SM_CurrentState = SM_WaitingForSOF;
    receivedData = false;
    receivedPortB = 0;
    receivedPortD = 0;
    receivedPortF = 0;

    Serial1.write('H'); // Any byte is ok; it is used to trigger a sending of the status
}

void loop() {
    receivedData = false;

    switch (SM_CurrentState)
    {
        case SM_WaitingForSOF:
            while (Serial1.available() &gt; 0) // can also be a "if"
            {
                if (Serial1.read() == StartOfFrame)
                {
                    SM_CurrentState = SM_WaitingForPayload;
                    startOfTransmissionMillis = (uint8_t)millis();
                    break;
                }
            }
            break;
        case SM_WaitingForPayload:
            if (Serial1.available() &gt;= 3)
            {
                receivedPortB = Serial1.read();
                receivedPortD = Serial1.read();
                receivedPortF = Serial1.read();

                if (((receivedPortB &amp; 0x08) | (receivedPortD &amp; 0x08) | (receivedPortF &amp; 0x08)) == 0)
                { // All the payload bits have '0' as bit 3, so communication was ok
                    receivedData = true;                
                }

                SM_CurrentState = SM_WaitingForSOF;
            }
            else if ((((uint8_t)millis()) - startOfTransmissionMillis) &gt; transmissionTimeout_ms)
            {
                SM_CurrentState = SM_WaitingForSOF;
            }
            break;
        default:
            SM_CurrentState = SM_WaitingForSOF;
            break;
    }

    if (receivedData)
    {
        PORTB = (PORTB &amp; (~PBmask)) | (receivedPortB &amp; 0xF0) | ((receivedPortB &amp; 0x07) &lt;&lt; 1);
        PORTD = (PORTD &amp; (~PDmask)) | receivedPortD;
        PORTF = (PORTF &amp; (~PFmask)) | receivedPortF;
    }
  }
}


Beware: untested code. I'm confident that no major bugs are present, but some tweaking may be necessary