Загрузка данных


#define DT_DRV_COMPAT wch_ch455g_kbd

#define CH455_CMD_SYSTEM     0x48
#define CH455_CMD_READ_KEY   0x4F
#define CH455_BIT_7SEG       0x08
#define CH455_BIT_ENA        0x01

struct ch455g_config {
    struct i2c_dt_spec i2c;
    struct gpio_dt_spec int_gpio;
};

struct ch455g_data {
    const struct device *dev;
    struct gpio_callback gpio_cb;
    struct k_work work;
    uint16_t *keymap;
    uint32_t keymap_size;
};

#define CH455G_KEYMAP_INIT(node_id) \
    static const uint16_t ch455g_keymap_##node_id[] = \
        DT_PROP(DT_CHILD(node_id, keymap), keymap);

DT_INST_FOREACH_STATUS_OKAY(CH455G_KEYMAP_INIT)

//Deferred interrupt handling (I2C read enabled)
static void ch455g_work_handler(struct k_work *work)
{
    struct ch455g_data *data = CONTAINER_OF(work, struct ch455g_data, work);
    const struct ch455g_config *config = data->dev->config;
    uint8_t raw_code = 0;
    uint8_t cmd = CH455_CMD_READ_KEY;

    if (i2c_write_read_dt(&config->i2c, &cmd, 1, &raw_code, 1) == 0) {
        if (raw_code != 0) {
            uint8_t dig = (raw_code >> 3) & 0x07;
            uint8_t seg = raw_code & 0x07;
            bool pressed = (raw_code & 0x40) ? true : false;

            if (dig < 4 && seg < 4) {
                // Calculate the index in the one-dimensional keymap array: (row * col_size) + col
                // According to the CH455G datasheet: row is seg, col is dig. Our col_size is 4.
                uint32_t map_idx = (seg * 4) + dig;

                if (map_idx < data->keymap_size) {
                    uint16_t key_code = data->keymap[map_idx];

                    // Pass the event directly to the Zephyr Input subsystem
                    input_report_key(data->dev, key_code, pressed, true, K_FOREVER);
                }
            }
        }
    }
}

static void ch455g_gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
{
    struct ch455g_data *data = CONTAINER_OF(cb, struct ch455g_data, gpio_cb);
    k_work_submit(&data->work); // Send the task to the system queue
}

static int ch455g_kbd_init(const struct device *dev)
{
    const struct ch455g_config *config = dev->config;
    struct ch455g_data *data = dev->data;
    uint8_t init_cmd = CH455_CMD_SYSTEM | CH455_BIT_7SEG | CH455_BIT_ENA;

    data->dev = dev;
    k_work_init(&data->work, ch455g_work_handler);

    if (!device_is_ready(config->i2c.bus) || !gpio_is_ready_dt(&config->int_gpio)) {
        return -ENODEV;
    }

    // Initialize the chip in 7-segment mode
    if (i2c_write_dt(&config->i2c, &init_cmd, 1) != 0) {
        return -EIO;
    }

    // Setting up GPIO interrupts
    gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
    gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE);
    gpio_init_callback(&data->gpio_cb, ch455g_gpio_callback, BIT(config->int_gpio.pin));
    gpio_add_callback(config->int_gpio.port, &data->gpio_cb);

    return 0;
}

#define CH455G_KBD_DEVICE(node_id)                                         \
    static const struct ch455g_config ch455g_cfg_##node_id = {             \
        .i2c = I2C_DT_SPEC_GET(node_id),                                   \
        .int_gpio = GPIO_DT_SPEC_GET(node_id, int_gpios),                  \
    };                                                                     \
    static struct ch455g_data ch455g_data_##node_id = {                    \
        .keymap = (uint16_t *)ch455g_keymap_##node_id,                     \
        .keymap_size = ARRAY_SIZE(ch455g_keymap_##node_id),                \
    };                                                                     \
    DEVICE_DT_DEFINE(node_id, ch455g_kbd_init, NULL,                       \
                     &ch455g_data_##node_id, &ch455g_cfg_##node_id,        \
                     POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY, NULL);

DT_INST_FOREACH_STATUS_OKAY(CH455G_KBD_DEVICE)