Handling button presses

To handle buttons a task needs to event when a pin changes value. This can be done using the select construct and the pinsneq predicate on the select case:

// This function is combinable - it can run on a logical core with other tasks.
[[combinable]]
void task1(port p_button)
{
  // The last read value off the port.
  int current_val = 0;
  while (1) {
   select {
   // event when the button changes value
   case p_button when pinsneq(current_val) :> int new_val:
     if (new_val == 1) {
       printf("Button up\n");
     } else {
       printf("Button down\n");
     }
     current_val = new_val;
     break;
   }
  }
}

This code will react when the I/O pins change value. However, due to the button bouncing up and down, after a button is pressed the I/O pin will change value many times, very quickly. To avoid reacting to each of these changes you can add a debouncing period.

To do this, add a guard to the select case. This guard says do not react to the button unless the variable is_stable evaluates to true (i.e. non-zero). When a button is pressed is_stable is set to 0 and a timeout is setup. A separate case handles this timeout expiring (using a timer) at which point is_stable is set back to 1.

[[combinable]]
void task1a(port p_button)
{
  int current_val = 0;
  int is_stable = 1;
  timer tmr;
  const unsigned debounce_delay_ms = 50;
  unsigned debounce_timeout;
  while (1) {
   select {
   // If the button is "stable", react when the I/O pin changes value
   case is_stable => p_button when pinsneq(current_val) :> current_val:
     if (current_val == 1) {
       printf("Button up\n");
     } else {
       printf("Button down\n");
     }
     is_stable = 0;
     int current_time;
     tmr :> current_time;
     // Calculate time to event after debounce period
     // note that XS1_TIMER_HZ is defined in timer.h
     debounce_timeout = current_time + (debounce_delay_ms * XS1_TIMER_HZ);
     break;

   // If the button is not stable (i.e. bouncing around) then select
   // when we the timer reaches the timeout to renter a stable period
   case !is_stable => tmr when timerafter(debounce_timeout) :> void:
     is_stable = 1;
     break;
   }
  }
}

See Also