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


#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/input/input.h>

#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(inst) \
    static const uint16_t ch455g_keymap_##inst[] = \
        DT_PROP(DT_INST_CHILD(inst, input_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) {
            bool pressed = (raw_code & 0x40) ? true : false;
            
            // Clear the 0x40 push bit, leaving a clean address from the table (0x04 - 0x1F)
            uint8_t clean_addr = raw_code & 0x3F; 

            // From the datasheet table: DIG columns are defined by the least significant bits 1-0
            uint8_t dig = clean_addr & 0x03; 
            
            // SEG lines are defined by bits 5-3 of the clear address
            uint8_t seg = (clean_addr >> 3) & 0x07;

            printf("%s(%d)seg=%d dig=%d\n",__FUNCTION__,__LINE__,seg,dig);
            if (seg < 4 && dig < 4) {
                uint32_t map_idx = (seg * 4) + dig;

                if (map_idx < data->keymap_size) {
                    uint16_t key_code = data->keymap[map_idx];
                    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(inst)                                            \
    static const struct ch455g_config ch455g_cfg_##inst = {                \
        .i2c = I2C_DT_SPEC_GET(DT_DRV_INST(inst)),                         \
        .int_gpio = GPIO_DT_SPEC_GET(DT_DRV_INST(inst), int_gpios),        \
    };                                                                     \
    static struct ch455g_data ch455g_data_##inst = {                       \
        .keymap = (uint16_t *)ch455g_keymap_##inst,                        \
        .keymap_size = ARRAY_SIZE(ch455g_keymap_##inst),                   \
    };                                                                     \
    DEVICE_DT_INST_DEFINE(inst, ch455g_kbd_init, NULL,                     \
                          &ch455g_data_##inst, &ch455g_cfg_##inst,         \
                          POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY, NULL);

DT_INST_FOREACH_STATUS_OKAY(CH455G_KBD_DEVICE)