How can I debounce a switch on both positive and negative-going edges?
To debounce either edge of a changing signal, use hysteresis. Many debouncing algorithms assume an active high or active low signal, but you need to detect both.
Here is the essence of the hysteresis algorithm:
    bool input_state = digitalRead(INPUT_PIN);
    unsigned long current_ms = millis();

    edge = rise = fall = false;

    // Hysteresis:
    //   If there is no change, reset the debounce timer.
    //   Else, compare the time difference with the debounce delay.
    if (input_state == output_state)
    {
      last_ms = current_ms;
    }
    else
    {
      if ((current_ms - last_ms) &gt;= DEBOUNCE_DELAY_ms)
      {
        // Successfully debounced, so update the outputs.
        is_debounced = true;
        rise = input_state &amp;&amp; !output_state;
        fall = !input_state &amp;&amp; output_state;
        edge = rise || fall;
        output_state = input_state;
      }
      else
      {
          is_debounced = false;
      }
    }

This could be called from an interrupt service routine something like this:
ISR(TIMER0_COMPA_vect)
{
  static Debouncer button1(BUTTON_PIN, DEBOUNCE_DELAY_ms);
  static bool led_state = false;
  
  button1.UpdateISR();
  
  // Toggle the LED on either edge of the debounced signal.
  if (button1.Edge())
  {
    led_state = !led_state;
    digitalWrite(LED_PIN, led_state);
  }
}

Here is a link the full code of the hysteresis debouncer on GitHub which uses the millis() timer, but you might need to change it to use the micros() timer and possibly increase the interrupt frequency for greater accuracy in your application. You also need to figure out the length of DEBOUNCE_DELAY. Use an oscilloscope to see what the switch bounce noise looks like.