Загрузка данных
#include <zephyr/kernel.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/gpio.h>
// Команды CH455G из даташита
#define CH455_CMD_SYSTEM 0x48 // Базовая команда настройки системы
#define CH455_CMD_READ_KEY 0x4F // Команда чтения кода кнопки (единственная операция чтения)
// Биты конфигурации для команды SYSTEM
#define CH455_BIT_7SEG 0x08 // 1 = Режим 7 сегментов (SEG7 становится выходом INT#)
#define CH455_BIT_ENA 0x01 // 1 = Включить дисплей и сканирование кнопок
// Объекты Zephyr для I2C и GPIO
static const struct i2c_dt_spec ch455 = I2C_DT_SPEC_GET(DT_NODELABEL(ch455g));
static const struct gpio_dt_spec int_pin = GPIO_DT_SPEC_GET(DT_NODELABEL(ch455g), int_gpios);
static struct gpio_callback keyboard_cb_data;
// Семафор для обработки прерываний в потоке
K_SEM_DEFINE(key_pressed_sem, 0, 1);
// Матрица перевода кодов клавиш 4х4 (DIG0-DIG3 на SEG0-SEG3)
// Смещение строки (SEG) задается младшими 3 битами (0..3)
// Смещение столбца (DIG) задается битами 3,4,5 (0..3)
static const char key_map[4][4] = {
// SEG0 SEG1 SEG2 SEG3
{ '1', '2', '3', 'A' }, // DIG0
{ '4', '5', '6', 'B' }, // DIG1
{ '7', '8', '9', 'C' }, // DIG2
{ '*', '0', '#', 'D' } // DIG3
};
// Функция декодирования байта от CH455G
char decode_ch455_key(uint8_t raw_code) {
// Из даташита: Бит 6 - статус (нажата/отпущена), Биты 5-3 - DIG, Биты 2-0 - SEG
uint8_t dig = (raw_code >> 3) & 0x07; // Индекс столбца (0..3)
uint8_t seg = raw_code & 0x07; // Индекс строки (0..3)
// Проверяем, что индексы лежат внутри нашей матрицы 4х4
if (dig < 4 && seg < 4) {
return key_map[dig][seg];
}
return '\0'; // Неизвестный код или кнопка вне матрицы 4х4
}
// Callback прерывания (Вызывается в контексте ISR)
void keyboard_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins) {
k_sem_give(&key_pressed_sem); // Передаем обработку в поток
}
// Поток обработки клавиатуры (Thread)
void keyboard_thread(void *p1, void *p2, void *p3) {
uint8_t raw_code = 0;
uint8_t cmd = CH455_CMD_READ_KEY;
while (1) {
// Ждем прерывания (когда SEG7 упадет в 0)
k_sem_take(&key_pressed_sem, K_FOREVER);
// Читаем 1 байт от CH455G через I2C4
int ret = i2c_write_read_dt(&ch455, &cmd, 1, &raw_code, 1);
if (ret == 0) {
// Если кнопка действительно нажата (код не равен 0)
if (raw_code != 0) {
// Бит 6 в даташите отвечает за статус: 1 = кнопка нажата, 0 = отпущена
bool is_pressed = (raw_code & 0x40) ? true : false;
char key = decode_ch455_key(raw_code);
if (key != '\0') {
printk("Кнопка [%c] -> %s (Сырой код: 0x%02X)\n",
key, is_pressed ? "НАЖАТА" : "ОТПУЩЕНА", raw_code);
}
}
} else {
printk("Ошибка чтения I2C4: %d\n", ret);
}
// Защита от дребезга контактов матрицы
k_msleep(30);
}
}
// Регистрация системного потока обработки клавиатуры
K_THREAD_DEFINE(kbd_thread_id, 1024, keyboard_thread, NULL, NULL, NULL, 7, 0, 0);
void main(void) {
printk("Запуск драйвера CH455G на I2C4...\n");
// Проверяем готовность периферии
if (!device_is_ready(ch455.bus)) {
printk("Ошибка: Шина I2C4 не готова!\n");
return;
}
if (!gpio_is_ready_dt(&int_pin)) {
printk("Ошибка: Порт GPIO прерывания не готов!\n");
return;
}
// 1. Инициализация CH455G в режим 7-сегментов для активации INT# на выводе SEG7
// Нам нужны флаги: CH455_BIT_7SEG (0x08) и CH455_BIT_ENA (0x01)
uint8_t init_cmd = CH455_CMD_SYSTEM | CH455_BIT_7SEG | CH455_BIT_ENA;
int ret = i2c_write_dt(&ch455, &init_cmd, 1);
if (ret != 0) {
printk("Ошибка инициализации CH455G по I2C: %d\n", ret);
return;
}
// 2. Конфигурируем пин GPIO_SD_B1_09 как вход
gpio_pin_configure_dt(&int_pin, GPIO_INPUT);
// 3. Настраиваем прерывание на низкий уровень (активное состояние)
// Благодаря флагу GPIO_ACTIVE_LOW в DTS, макрос настроит прерывание именно по низкому уровню
gpio_pin_interrupt_configure_dt(&int_pin, GPIO_INT_EDGE_TO_ACTIVE);
// 4. Регистрируем callback прерывания
gpio_init_callback(&keyboard_cb_data, keyboard_handler, BIT(int_pin.pin));
gpio_add_callback(int_pin.port, &keyboard_cb_data);
printk("CH455G успешно настроен. Ожидание нажатий...\n");
}