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


#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");
}