天天看點

STM32F7xx —— 輸入                              STM32F7xx —— 輸入

                              STM32F7xx —— 輸入

目錄

STM32F7xx —— 輸入

一、輸入配置

二、輸入掃描

三、輸入處理

一、輸入配置

為了靈活使用,我們将輸入的有效電平設定成可配置。同樣是清單表示所有IO口。

// 配置有效電平
typedef enum
{
  KEY_INIT_IS_ACTIVE = 0,
  KEY_LOW_IS_ACTIVE  = 1,
  KEY_HIGH_IS_ACTIVE = 2,
} key_active_t;


#define KEY_CONFIG(gpio, pin)     GPIOConfig(gpio, pin, GPIO_MODE_INPUT, GPIO_PULLUP)
#define KEY_READ(gpio, pin)       HAL_GPIO_ReadPin(gpio, pin)

#define KEY1_PORT                 GPIOH
#define KEY1_PIN                  GPIO_PIN_3

#define KEY2_PORT                 GPIOH
#define KEY2_PIN                  GPIO_PIN_2

#define KEY3_PORT                 GPIOC
#define KEY3_PIN                  GPIO_PIN_13
           
// demo代碼 隻傳遞思想

static void key_gpio_config(GPIO_TypeDef *gpio, uint16_t pin)
{
  KEY_CONFIG(gpio, pin);
}

typedef struct
{
  GPIO_TypeDef *gpio;
  uint16_t pin;
} key_port_t;

static key_port_t key_entries[] =
{
  {KEY1_PORT, KEY1_PIN},
  {KEY2_PORT, KEY2_PIN},
  {KEY3_PORT, KEY3_PIN},
};

void KeyInit(void)
{
  uint32_t i, mask = 1;

  for(i = 0; i < ARRAY_SIZE(key_entries); ++i)
  {
    if(0xFFFFFFFF & mask)
    {
      key_gpio_config(key_entries[i].gpio, key_entries[i].pin);

#if(CONFIG_KEY_TEST == 1) // 測試時使用      
      //config.key.total_switch  = KEY_MODE_OPEN;
      //config.key.sub_switch[i] = KEY_MODE_OPEN;
      config.key.active_tag[i] = KEY_LOW_IS_ACTIVE;
#endif
    }

    mask <<= 1;
  }
}

// 輸入是否可用
static uint8_t key_is_enable(uint8_t index)
{
  // 這裡可以寫成可配置 配置IO口可用或者不可用(類似一個總開關)
  return 1;
}

// 有輸入到來
static uint8_t key_is_pressed(uint8_t index)
{
  if(key_is_enable(index))
  {
    if(KEY_LOW_IS_ACTIVE == config.key.active_tag[index])
    {
      if(KEY_READ(key_entries[index].gpio, key_entries[index].pin) == 0)
      {
        return 1;
      }
    }
    else if(KEY_HIGH_IS_ACTIVE == config.key.active_tag[index])
    {
      if(KEY_READ(key_entries[index].gpio, key_entries[index].pin) == 1)
      {
        return 1;
      }
    }
  }

  return 0;
}

// 按鍵被按下
uint8_t Key1IsDown(void)
{
  return key_is_pressed(0);
}

uint8_t Key2IsDown(void)
{
  return key_is_pressed(1);
}

uint8_t Key3IsDown(void)
{
  return key_is_pressed(2);
}
           

二、輸入掃描

// 按鍵的狀态機結構定義
// 按鍵狀态
typedef enum
{
  KEY_STATE_INIT, // 預設按鍵狀态
  KEY_STATE_UP,   // 按鍵彈起狀态
  KEY_STATE_DOWN, // 按鍵按下狀态
  KEY_STATE_LONG, // 按鍵長按狀态
  KEY_STATE_AUTO, // 按鍵自動連發狀态
} key_state_t;

// 按鍵濾波時間20ms, 機關10ms。
// 隻有連續檢測到20ms狀态不變才認為有效,包括彈起和按下兩種事件
// 即使按鍵電路不做硬體濾波,該濾波機制也可以保證可靠地檢測到按鍵事件
#define KEY_FILTER_TIME 2   // 濾波消抖
#define KEY_LONG_TIME   100 // 機關10ms    持續1秒,認為長按事件
#define KEY_REPEAT_TIME 100 // 機關10ms    持續1秒,自動連發

typedef uint8_t (*key_cb)(void);
typedef struct
{
  uint8_t state;        // 按鍵目前狀态(按下還是彈起)
  uint8_t last;         // 上一次按鍵的狀态
  uint8_t count;        // 濾波消抖計數器
  uint16_t long_time;   // 按鍵按下持續時間, 0表示不檢測長按
  uint16_t long_count;  // 長按計數器
  uint8_t repeat_speed; // 連續按鍵周期
  uint8_t repeat_count; // 連續按鍵計數器
  key_cb is_down_func;  // 按鍵按下的判斷函數,1表示按下
} key_t;

static key_t key_items[] =
{
  {KEY_STATE_INIT, KEY_STATE_INIT, KEY_FILTER_TIME, KEY_LONG_TIME, 0, KEY_REPEAT_TIME, 0, Key1IsDown},
  {KEY_STATE_INIT, KEY_STATE_INIT, KEY_FILTER_TIME, KEY_LONG_TIME, 0, KEY_REPEAT_TIME, 0, Key2IsDown},
  {KEY_STATE_INIT, KEY_STATE_INIT, KEY_FILTER_TIME, KEY_LONG_TIME, 0, KEY_REPEAT_TIME, 0, Key3IsDown},
};
           
// 按鍵狀态機key_scan
// 配置限制條件在這個函數裡面加
static void key_scan_ext(key_t *entry, uint8_t i)
{
  switch(entry->state)
  {
  case KEY_STATE_INIT:
  case KEY_STATE_UP:
  {
    entry->state = KEY_STATE_DOWN; // 按鍵被按下
    break;
  }

  case KEY_STATE_DOWN:
  {
    if(entry->long_time > 0)
    {
      if(entry->long_count < entry->long_time)
      {
        if(++entry->long_count >= entry->long_time)
        {
          entry->state = KEY_STATE_LONG;
        }
      }
    }

    break;
  }

  case KEY_STATE_LONG:
  {
    if(entry->repeat_speed > 0) // 自動連發時間到  自動連發事件
    {
      if(++entry->repeat_count >= entry->repeat_speed)
      {
        entry->repeat_count = 0;
        // 長按觸發
      }
    }

    break;
  }
  }

  entry->last = entry->state; // 最新的按鍵狀态
}

static void key_scan(uint8_t i)
{
  key_t *entry = &key_items[i];
	uint8_t key;

  if(entry->is_down_func())
  {
    if(entry->count < KEY_FILTER_TIME) // 消抖
    {
      ++entry->count;
    }
    else
    {
      key_scan_ext(entry, i); // 按鍵掃描狀态機
    }
  }
  else
  {
    if(entry->count > KEY_FILTER_TIME)
    {
      entry->count = KEY_FILTER_TIME;
    }
    else if(entry->count > 0)
    {
      --entry->count;
    }
    else
    {
      if(KEY_STATE_DOWN == entry->last) // 一次完整的按鍵到這裡就彈起了
      {
       // 按鍵按下之後可以加入到隊列中,這裡的隊列可以自己寫;如果帶系統可以使用系統的消息隊列等方式。
       // key = i + 1;
       // xQueueSend(os_key_queue, &key, 10); 
      }
      entry->last  = KEY_STATE_UP;
      entry->state = KEY_STATE_UP; // 按鍵彈起狀态
    }

    entry->long_count = 0;
    entry->repeat_count = 0; // 清空計數器
  }
}
           

三、輸入處理

// 按鍵按下之後,會将值加入到隊列中,我們讀取隊列資料,然後掃描清單比對功能
static void key1_cb(void);
static void key2_cb(void);
static void key3_cb(void);

#define KEY1_CMD         1
#define KEY2_CMD         2
#define KEY3_CMD         3

typedef struct
{
  uint8_t cmd;
  void (* key_handle_cb)(void);
} key_handle_t;

static const key_handle_t key_entries[] =
{
  {KEY1_CMD, key1_cb},
  {KEY2_CMD, key2_cb},
  {KEY3_CMD, key3_cb},
  {0xFF, NULL  },
};

// 按鍵的通用功能
static void key_func(uint8_t func)
{

}

static void key1_cb(void)
{

}

static void key2_cb(void)
{

}

static void key3_cb(void)
{

}

static void key_process(uint8_t event)
{
  const key_handle_t *entry;

  for(entry = key_entries; entry->key_handle_cb; ++entry)
  {
    if(event == entry->cmd)
    {
      entry->key_handle_cb();
      break;
    }
  }
}
           

裸機:按鍵掃描20ms執行一次,按鍵處理直接丢在主函數中。(自己寫隊列)

系統:在任務中執行按鍵掃描和按鍵處理。(系統自帶隊列或者郵箱)

繼續閱讀