Dylan Walter
Dylan Walter

Reputation: 11

Easiest Way to Implement a Tap Tempo functionality on STM32?

I'm using an STM32F411RE Nucleo board to develop some code for a digital guitar effects pedal, and I need to be able to implement a tap tempo.

I'm fairly inexperienced with coding in general. Does anybody know the easiest way to simply press a GPIO button twice, measuring the interval between the two presses, and assign that value to a parameter? Or at the very least just take that interval and flash an LED at that delay time? Assuming this is going to be utilizing some sort of interrupt.

Thanks!

Edit: I am using STM32CubeIDE with the HAL (Hardware Abstraction Layer) Library. I am able to easily read inputs and send outputs to various GPIO pins via the use of this library. I am also utilizing an external 8MHz clock.

Upvotes: 1

Views: 425

Answers (1)

Clifford
Clifford

Reputation: 93566

If you need help with reading the input, setting the output, and the timing then the question is too broad and you should post separate questions for each concept. However, assuming you have the following fundamental building blocks:

  • bool getTempoInput() ; - function that reads state of tempo setting input switch.
  • void setTempoOutput( bool state) - function that sets the state of the tempo indicator.
  • unsigned getMillisecTick() - function that returns a free-running millisecond count.

Clearly the names and signature of the functions need not be as above, but that allows the following pseudo-code (since we do not know what actual framework or library you might be using - if any).

#define TEMPO_OUT_PULSE_WIDTH_MS 100


unsigned tempo_set_start_timestamp = 0 ;
unsigned tempo_millisec = 1000 ;  // 1 second to start with (arbitrarily)
bool tempo_timing_in_progress = false ;

bool button_down = false ;
unsigned button_down_timestamp = 0 ;

unsigned tempo_start_timestamp = getMillisecTick() ;
setTempoOutput( true ) ; // LED on

for(;;)
{
   // Get current millsec count
    unsigned now = getMillisecTick() ;

    //--------------------------------
    // LED timing
    //--------------------------------
    unsigned time_since_start = tempo_start_timestamp - now ;

    // If past end of LED on period...
    if( time_since_start >= TEMPO_OUT_PULSE_WIDTH_MS )
    {
        // If past the endo the tempo period...
        if( time_since_start >= tempo_millisec )
        {
            // Restart tempo period
            tempo_start_timestamp = now ;
            setTempoOutput( true ) ; // LED on  
        }
        else
        {
            // LED off at end of pulse period
            setTempoOutput( false ) ; 
        }
     }
    //--------------------------------

    //--------------------------------
    // Tempo set timing
    //--------------------------------
    // Get current button state
    bool button_state = getTempoInput() ;

    // If button down and previously up...
    if( !button_down && button_state )
    {
       // Button pressed - timestamp it
        button_down_timestamp  = now ;
        button_down = true ;
    
        // If tempo set timing not in progress...
        if( !tempo_timing_in_progress )
        {
            // Start timing
            tempo_set_start_timestamp = now ;
            tempo_timing_in_progress = true ;
        }
        else
        {
            // Set tempo, stop timing
            tempo_millisec = tempo_set_start_timestamp - now ;
            tempo_timing_in_progress = false;
        }
    }
    else if( !button_state && button_down && 
             button_down_timestamp >= 2 * TEMPO_OUT_PULSE_WIDTH_MS )
    {
        // Button up after debounce period
        button_down = false ;
    }
    //--------------------------------
}

Treat the above as illustrative - I have no means of testing it, it may contain flaws. Important points are:

  • The loop contains no blocking/busy-wait states and will iterate significantly faster then the 1ms tick time.
  • Any input switch will "bounce" - the algorithm starts tempo timing on the initial switch close, and ignores subsequent activity for a period of two times the pulse width. That is longer than necessary for debounce, but also ensures that a tempo cannot be set so fast that the indicator does not perceivably flash. The maximum tempo here is 300 BPM.
  • The tempo indication code is largely independent of the tempo setting code, with only tempo_millisec shared. The code could be better structured with these blocks in separate functions - (an exercise for the reader).
  • This is just one solution. You could use the STM32 numerous timers to set the tempo using input-timer-capture and/or indicate the tempo using a PWM timer output. For the input however you would need to electronically debounce the switch input.

Upvotes: 1

Related Questions