Keyboard code won't work as intended
Replace
for (int rows = 0; rows &lt; NUM_ROWS; rows++)
      {
          if (HAL_GPIO_ReadPin(GPIOD, keyscan_row_io[rows].pin))
              key_state [rows * NUM_ROWS + cols] = KEY_DOWN;
          else
              key_state [rows * NUM_ROWS + cols] = KEY_UP;
      }

with
for (int rows = 0; rows &lt; NUM_ROWS; rows++)
          {
              if (HAL_GPIO_ReadPin(GPIOD, keyscan_row_io[rows].pin))
                  key_state [rows * NUM_COLS + cols] = KEY_DOWN;
              else
                  key_state [rows * NUM_COLS + cols] = KEY_UP;
          }

So multiply by NUM_COLS instead of NUM_ROWS.
Or it may be also cols * NUM_ROWS + rows depending on your mapping. The way it is now is incorrect.