STM32におけるボタンのシングルクリック、ダブルクリック、ロングプレス実装

CubeMX設定

1.GPIO設定

GPIOをプルアップ入力モードに設定します。

2.タイマー設定

タイマーを10ms間隔で割り込みを有効に設定します。

変数定義

struct button_states
{
    bool pressed_flag;    // ボタン押下フラグ
    bool current_state;   // ボタン状態
    bool long_press_flag; // ロングプレスフラグ
    bool double_click_flag; // ダブルクリックフラグ
    unsigned char detection_state; // 検知状態
};

struct button_states btn[5] = {0, 0, 0, 0, 0};

int long_press_counter;  // ロングプレス時間カウント
int double_click_interval = 0; // ダブルクリック間隔
int press_count = 0;     // 押下回数

メインロジック

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM4)
    {
        double_click_interval++; // 10msごとにカウントアップ
        
        // ボタン状態の読み取り
        btn[0].current_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);
        btn[1].current_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);
        btn[2].current_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);
        btn[3].current_state = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);
        
        for(int i = 0; i < 4; i++) // ループによる判定
        {
            switch(btn[i].detection_state)
            {
                case 0:
                    if(btn[i].current_state == 0) // ボタン初回押下
                    {
                        long_press_counter = 0; // ロングプレスカウントリセット
                        btn[i].detection_state = 1; // 次回割り込みでcase1へ
                        if(double_click_interval > 30) double_click_interval = 0; // ダブルクリックカウントリセット
                    }
                    break;
                case 1:
                    if(btn[i].current_state == 0) // チャタリング除去
                    {
                        btn[i].detection_state = 2; // 次回割り込みでcase2へ
                    }
                    else 
                    {
                        btn[i].detection_state = 0; // 2回目押下なしの場合はチャタリングと判断しcase0へ戻る
                    }
                    break;
                case 2: 
                    if(btn[i].current_state == 1) // ボタン解放
                    {
                        double_click_interval = 0; // ダブルクリックカウントリセット
                        press_count++; // ボタン押下回数インクリメント
                        btn[i].detection_state = 0; // case0へ戻る
                    }
                    else // ボタン未解放の場合はロングプレスカウント
                    {
                        long_press_counter++;
                    }
                    break;
            }

            if(press_count == 1 && long_press_counter > 200) // 1回押下かつ2秒以上押下でロングプレスと判定
            {
                btn[i].long_press_flag = 1; // ロングプレスフラグをセット
                press_count = 0; // ボタン押下回数リセット
            }
            else if(press_count == 2 && double_click_interval < 30) // 2回押下かつ300ms以内でダブルクリックと判定
            {
                btn[i].double_click_flag = 1; // ダブルクリックフラグをセット
                press_count = 0;
            }
            else if(press_count == 1 && double_click_interval > 30 && long_press_counter < 200) // 1回押下かつ300ms以上かつ2秒未満でシングルクリックと判定
            {
                btn[i].pressed_flag = 1;
                double_click_interval = 0;
                press_count = 0;
            }
        }
    }
}

実装のポイント

ダブルクリック間隔カウントは割り込み発生時にインクリメントされます。

ボタン押下と解放が検知されると、ダブルクリック間隔カウントはリセットされ、押下回数がインクリメントされます。

ボタンが押下されたままの場合は、ロングプレス時間カウントがインクリメントされます。

スキャン処理の前にロングプレスを判定し、1回押下かつ2秒以上でロングプレスとします。

次にダブルクリックを判定し、2回押下かつ300ms以内でダブルクリックとします。

最後にシングルクリックを判定します。

課題:シングルクリック判定に300msの遅延が発生します。

タグ: STM32 CubeMX 嵌入式系统 按键处理 定时器中断

5月18日 11:54 投稿