Загрузка данных
#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)