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