Skip to content

File syn_keypad.c

File List > input > syn_keypad.c

Go to the documentation of this file

#include "syn_keypad.h"

#include "../util/syn_assert.h"

#include <string.h>

SYN_Status syn_keypad_init(SYN_Keypad *kp, const SYN_GPIO_Pin *rows, uint8_t num_rows,
                           const SYN_GPIO_Pin *cols, uint8_t num_cols, const char *keymap)
{
    SYN_ASSERT(kp != NULL);
    SYN_ASSERT(rows != NULL);
    SYN_ASSERT(cols != NULL);
    SYN_ASSERT(num_rows > 0 && num_rows <= SYN_KEYPAD_MAX_ROWS);
    SYN_ASSERT(num_cols > 0 && num_cols <= SYN_KEYPAD_MAX_COLS);
    SYN_ASSERT(keymap != NULL);

    memset(kp, 0, sizeof(*kp));
    kp->num_rows = num_rows;
    kp->num_cols = num_cols;

    memcpy(kp->rows, rows, sizeof(SYN_GPIO_Pin) * num_rows);
    memcpy(kp->cols, cols, sizeof(SYN_GPIO_Pin) * num_cols);
    memcpy(kp->keymap, keymap, (size_t)num_rows * num_cols);

    /* Rows as Outputs (Idle LOW) */
    for (uint8_t r = 0; r < num_rows; r++) {
        syn_port_gpio_init(kp->rows[r], SYN_GPIO_OUTPUT);
        syn_port_gpio_write(kp->rows[r], SYN_GPIO_LOW);
    }

    /* Cols as Inputs with PULL-DOWN */
    for (uint8_t c = 0; c < num_cols; c++) {
        syn_port_gpio_init(kp->cols[c], SYN_GPIO_INPUT_PULLDOWN);
    }

    return SYN_OK;
}

void syn_keypad_scan(SYN_Keypad *kp)
{
    if (kp == NULL)
        return;

    char found_key = 0;

    for (uint8_t r = 0; r < kp->num_rows; r++) {
        /* Drive row HIGH */
        syn_port_gpio_write(kp->rows[r], SYN_GPIO_HIGH);

        for (uint8_t c = 0; c < kp->num_cols; c++) {
            if (syn_port_gpio_read(kp->cols[c]) == SYN_GPIO_HIGH) {
                found_key = kp->keymap[r * kp->num_cols + c];
                break;
            }
        }

        /* Set row back to LOW */
        syn_port_gpio_write(kp->rows[r], SYN_GPIO_LOW);

        if (found_key != 0)
            break;
    }

    /* Process state changes */
    if (found_key != 0) {
        if (!kp->is_pressed || kp->active_key != found_key) {
            kp->active_key = found_key;
            kp->is_pressed = true;
            kp->press_count++;

            if (kp->on_event) {
                kp->on_event(kp, found_key, true, kp->user_ctx);
            }
        }
    } else {
        if (kp->is_pressed) {
            char released_key = kp->active_key;
            kp->is_pressed = false;
            kp->active_key = 0;

            if (kp->on_event) {
                kp->on_event(kp, released_key, false, kp->user_ctx);
            }
        }
    }
}

bool syn_keypad_get_key(const SYN_Keypad *kp, char *out_key)
{
    if (kp == NULL || !kp->is_pressed)
        return false;
    if (out_key != NULL) {
        *out_key = kp->active_key;
    }
    return true;
}

void syn_keypad_set_callback(SYN_Keypad *kp, SYN_KeypadCallback callback, void *user_ctx)
{
    if (kp == NULL)
        return;
    kp->on_event = callback;
    kp->user_ctx = user_ctx;
}