esp开发与应用(薄膜键盘的输入)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】

有一种输入是数字输入,在现实生活中用的特别多。如果还需要计算的话,一般要加上+-*/,这样就更好了。当然除了0~9,以及四位运算,剩下就是点和等于,这样基本上就把常用的计算都包含在内了。不过,如果16个按键全部用gpio表示,则需要16个gpio,看上去有一点浪费。因此,人们想出了薄膜键盘的方法,也就是矩阵键盘,一般按下去的时候,对应的行和列就会拉低,通过这个办法就可以知道哪个按键被按下去了。

1、8个输出

如果是4*4的薄膜键盘,一般就是4+4=8个输出信号。推广一下,如果是3*4,也就是12个按键的键盘,就是3+4=7个输出信号。

2、准备8个gpio

前面谈到了需要8个gpio,所以只需要找到8个引脚就可以了。不失一般性,不妨找到gpio12~gpio19这8个引脚。

3、连线

矩阵键盘本身没有电源线和地线,这和之前的传感器不太一样。所以,我们需要做的就是就是把esp32对应的引脚和矩阵键盘连接起来即可。一般连接的时候,采用公对母的连线,公口连接键盘,母口连接esp32。

4、开始ai编程

连接好线路之后,就可以准备ai编程了。比如告诉ai,用esp32的8个pin脚连接薄膜键盘,8个引脚是pin12到pin19。没什么大问题的话,ai就可以帮助我们生成的答案。

复制代码
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_rom_sys.h"

static const char *TAG = "KEYPAD_SCAN";

// ==================== Configuration ====================
#define ROWS 4
#define COLS 4

// GPIO pins for rows (outputs)
const gpio_num_t row_pins[ROWS] = {12, 13, 14, 15};

// GPIO pins for columns (inputs with pull-up)
const gpio_num_t col_pins[COLS] = {16, 17, 18, 19};

// Key mapping table (4x4 layout)
const char key_map[ROWS][COLS] = {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'}
};

// Debounce configuration
#define DEBOUNCE_THRESHOLD 3      // Number of consistent reads required
#define SCAN_INTERVAL_MS 10       // Scan interval in milliseconds
#define STABLE_DELAY_US 50        // Delay for signal stabilization
// ========================================================

// Debounce state variables
static uint8_t last_stable_state[ROWS][COLS] = {0};     // Last debounced key state (0=pressed, 1=released)
static uint8_t debounce_counter[ROWS][COLS] = {0};      // Counter for debounce stability

// Key event tracking
static char last_reported_key = 0;                       // Last key that was reported
static uint32_t last_key_press_time = 0;                 // Timestamp of last key press

/**
 * @brief Initialize GPIO pins for matrix keypad
 * 
 * Row pins: Configured as push-pull outputs, initial state HIGH
 * Column pins: Configured as inputs with internal pull-up resistors
 */
void keypad_gpio_init(void)
{
    // Initialize row pins as outputs, default HIGH
    for (int i = 0; i < ROWS; i++) {
        gpio_config_t io_conf = {
            .pin_bit_mask = (1ULL << row_pins[i]),
            .mode = GPIO_MODE_OUTPUT,
            .pull_up_en = GPIO_PULLUP_DISABLE,
            .pull_down_en = GPIO_PULLDOWN_DISABLE,
            .intr_type = GPIO_INTR_DISABLE,
        };
        gpio_config(&io_conf);
        gpio_set_level(row_pins[i], 1);  // Set initial state HIGH
    }
    
    // Initialize column pins as inputs with internal pull-up
    for (int i = 0; i < COLS; i++) {
        gpio_config_t io_conf = {
            .pin_bit_mask = (1ULL << col_pins[i]),
            .mode = GPIO_MODE_INPUT,
            .pull_up_en = GPIO_PULLUP_ENABLE,   // Enable internal pull-up
            .pull_down_en = GPIO_PULLDOWN_DISABLE,
            .intr_type = GPIO_INTR_DISABLE,
        };
        gpio_config(&io_conf);
    }
    
    ESP_LOGI(TAG, "GPIO initialized: %d rows, %d columns", ROWS, COLS);
}

/**
 * @brief Scan the matrix keypad and return the currently pressed key
 * 
 * Scanning principle:
 * 1. Drive each row LOW one at a time
 * 2. Read all column pins
 * 3. If a column reads LOW, the key at that row/column intersection is pressed
 * 4. Uses debouncing to filter out mechanical bouncing
 * 
 * @return char Pressed key character, or 0 if no key is pressed
 */
char keypad_scan(void)
{
    // Iterate through each row
    for (int row = 0; row < ROWS; row++) {
        // Drive current row LOW
        gpio_set_level(row_pins[row], 0);
        
        // Small delay to allow signal to stabilize
        esp_rom_delay_us(STABLE_DELAY_US);
        
        // Read all columns for this row
        for (int col = 0; col < COLS; col++) {
            // Read column state (0 = pressed, 1 = released due to pull-up)
            int current_state = gpio_get_level(col_pins[col]);
            
            // Debouncing logic: require multiple consistent reads
            if (current_state != last_stable_state[row][col]) {
                // State changed, increment counter
                debounce_counter[row][col]++;
                if (debounce_counter[row][col] >= DEBOUNCE_THRESHOLD) {
                    // State is stable, update last stable state
                    last_stable_state[row][col] = current_state;
                    debounce_counter[row][col] = 0;
                    
                    // Log state change for debugging
                    if (current_state == 0) {
                        ESP_LOGD(TAG, "Key %c stabilized (pressed)", key_map[row][col]);
                    } else {
                        ESP_LOGD(TAG, "Key %c stabilized (released)", key_map[row][col]);
                    }
                }
            } else {
                // State consistent, reset counter
                debounce_counter[row][col] = 0;
            }
            
            // Check if key is pressed (active LOW)
            if (last_stable_state[row][col] == 0) {
                // Restore row HIGH before returning
                gpio_set_level(row_pins[row], 1);
                return key_map[row][col];
            }
        }
        
        // Restore row HIGH before moving to next row
        gpio_set_level(row_pins[row], 1);
    }
    
    return 0;  // No key pressed
}

/**
 * @brief Task for continuous keypad scanning with event detection
 * 
 * This task continuously scans the keypad and reports key press/release events
 * It only reports when key state changes to avoid spamming the log
 */
void keypad_scan_task(void *pvParameters)
{
    char current_key;
    TickType_t last_scan_time = 0;
    
    ESP_LOGI(TAG, "Keypad scanning task started");
    
    while (1) {
        // Scan for currently pressed key
        current_key = keypad_scan();
        
        // Report events only when state changes
        if (current_key != last_reported_key) {
            if (current_key != 0) {
                // Key pressed event
                ESP_LOGI(TAG, "Key pressed: %c", current_key);
                last_key_press_time = xTaskGetTickCount();
            } else if (last_reported_key != 0) {
                // Key released event
                ESP_LOGI(TAG, "Key released: %c", last_reported_key);
            }
            last_reported_key = current_key;
        }
        
        // Maintain consistent scan interval
        vTaskDelay(pdMS_TO_TICKS(SCAN_INTERVAL_MS));
    }
}

/**
 * @brief Alternative: Simple blocking scan for single key detection
 * 
 * This function blocks until a key is pressed and returns it
 * Useful for menu navigation or PIN entry applications
 * 
 * @return char The key that was pressed
 */
char keypad_wait_for_key(void)
{
    char key;
    while (1) {
        key = keypad_scan();
        if (key != 0) {
            // Wait for key release to avoid multiple detections
            while (keypad_scan() != 0) {
                vTaskDelay(pdMS_TO_TICKS(SCAN_INTERVAL_MS));
            }
            return key;
        }
        vTaskDelay(pdMS_TO_TICKS(SCAN_INTERVAL_MS));
    }
}

/**
 * @brief Get the current key state without event logging
 * 
 * @return char Currently pressed key, or 0 if none
 */
char keypad_get_key(void)
{
    return keypad_scan();
}

/**
 * @brief Check if a specific key is currently pressed
 * 
 * @param key The key character to check
 * @return true if the key is pressed, false otherwise
 */
bool keypad_is_key_pressed(char key)
{
    char current_key = keypad_scan();
    return (current_key == key);
}

/**
 * @brief Reset debounce states (useful after deep sleep)
 */
void keypad_reset_debounce(void)
{
    for (int i = 0; i < ROWS; i++) {
        for (int j = 0; j < COLS; j++) {
            last_stable_state[i][j] = 1;  // Default to released state
            debounce_counter[i][j] = 0;
        }
    }
    last_reported_key = 0;
    ESP_LOGI(TAG, "Debounce states reset");
}

/**
 * @brief Main application entry point
 */
void app_main(void)
{
    // Initialize GPIO pins
    keypad_gpio_init();
    
    // Reset debounce states
    keypad_reset_debounce();
    
    ESP_LOGI(TAG, "4x4 Matrix Keypad Scanner Started");
    ESP_LOGI(TAG, "Scan interval: %d ms, Debounce threshold: %d", 
             SCAN_INTERVAL_MS, DEBOUNCE_THRESHOLD);
    
    // Option 1: Continuous scanning with event detection (recommended)
    xTaskCreate(keypad_scan_task, "keypad_scan", 4096, NULL, 5, NULL);
    
    // Option 2: Simple blocking scan (uncomment to use)

    while (1) {
        char key = keypad_wait_for_key();
        ESP_LOGI(TAG, "User entered: %c", key);
        
        // Example: Simple calculator or menu logic
        switch(key) {
            case 'A':
                ESP_LOGI(TAG, "Function A triggered");
                break;
            case 'B':
                ESP_LOGI(TAG, "Function B triggered");
                break;
            case '#':
                ESP_LOGI(TAG, "Enter key pressed");
                break;
            case '*':
                ESP_LOGI(TAG, "Back/Cancel pressed");
                break;
        }
    }
    
    // Main loop can do other tasks while keypad is scanned in background
    while (1) {
        // Your main application code here
        // Key events will be printed by the scanning task
        
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

5、验证确认

代码ok之后,就可以编译烧入看一下,确认下是不是我们想要的结果。等到结果ok之后,再回头看看代码的流程,做到心中有数即可。等到真正有需要的时候,再来详细分析也不迟。

相关推荐
羽翼安全1 天前
显示器泄密与屏幕防拍照系统如何选择?十大核心指标对比
计算机外设
俊哥工具1 天前
不用打开文件也能预览!支持压缩包、PDF、音视频
python·智能手机·django·pdf·计算机外设·virtualenv
鹿鸣天涯2 天前
kali 2026.1 vmware虚拟机内看不见鼠标处理方法
网络·计算机外设
CyL_Cly2 天前
罗技鼠标驱动下载最新版2026
计算机外设
weilaieqi13 天前
华硕发布创梦Pro 27 OLED SDI专业显示器:集成&nbsp;12G-SDI&nbsp;与内置色度计
计算机外设
兴通物联科技3 天前
条码防重防错防漏防呆:工业数据采集的全链路风控技术方案
大数据·物联网·计算机视觉·计算机外设·硬件架构
开开心心就好3 天前
小白友好的程序联网封锁实用工具
windows·eureka·计算机外设·rabbitmq·word·excel·csdn开发云
Dxy12393102164 天前
HTML如何写鼠标事件
前端·html·计算机外设