Matrix - inpudiy/zmk-guide GitHub Wiki

В этой части рассматривается процесс описания физической матрицы для разных вариантов распиновки.

Matrix transform

Начнём с того, что создадим файл с форматом имяплаты.dtsi в директории /board/shield/имяплаты. Вставим эту строку в начало для импорта нужного файла:

#include <dt-bindings/zmk/matrix_transform.h>

Следующим шагом зададим имя трансформации — можно указать несколько, если есть разные варианты количества клавиш. В нашем случае вариант один, поэтому просто напишем &default_transform;.

#include <dt-bindings/zmk/matrix_transform.h>

 / {
     chosen {
         zmk,kscan            = &kscan0;
         zmk,matrix-transform = &default_transform;
     };

Опускаемся ниже и описываем эту трансформацию. Обращаемся к ней по имени, указываем метод трансформации, а именно "zmk,matrix-transform", и задаём количество колонок и рядов:

     default_transform: keymap_transform_0 {
         compatible = "zmk,matrix-transform";
         columns    = <12>;
         rows       = <4>;
     };

Почему 12 колонок и 4 ряда? Потому что на каждой половинке, в нашем случае, по 6 колонок, а рядов и там, и там по 4 штуки. Описав первые 6 колонок в левой половинке, мы затем укажем, на сколько выполнить смещение для правой половинки. Станет понятнее, когда мы начнем их описывать. Пока для удобства можем в этом же блоке с помощью комментариев указать, как примерно будет выглядеть матрица с номерами клавиш:

         // | SW1  | SW2  | SW3  | SW4  | SW5  | SW6  |   | SW6  | SW5  | SW4  | SW3  | SW2  | SW1  |
         // | SW7  | SW8  | SW9  | SW10 | SW11 | SW12 |   | SW12 | SW11 | SW10 | SW9  | SW8  | SW7  |
         // | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 |   | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 |
         //                      | SW21 | SW20 | SW19 |   | SW19 | SW20 | SW21 |

Как видно, номера клавиш доходят до 21 на левой половинке и снова начинаются с 1 на другой половинке.

Следующий шаг — создание map, чтобы указать, в какой колонке и ряду находится каждая клавиша. Здесь используется конструкция вида RC(0,5), где первое число — номер ряда (row), а второе — номер колонки (col). Вот пример для такой матрицы:

image

         map = <
             RC(0,5)  RC(0,4)  RC(0,3)  RC(0,2)  RC(0,1)  RC(0,0)   RC(0,6)  RC(0,7)  RC(0,8)  RC(0,9)  RC(0,10) RC(0,11)
             RC(1,5)  RC(1,4)  RC(1,3)  RC(1,2)  RC(1,1)  RC(1,0)   RC(1,6)  RC(1,7)  RC(1,8)  RC(1,9)  RC(1,10) RC(1,11)
             RC(2,5)  RC(2,4)  RC(2,3)  RC(2,2)  RC(2,1)  RC(2,0)   RC(2,6)  RC(2,7)  RC(2,8)  RC(2,9)  RC(2,10) RC(2,11)
                               RC(3,2)  RC(3,1)  RC(3,0)  RC(3,6)  RC(3,7)  RC(3,8)
         >;

Теперь наш файл целиком выглядит так:

#include <dt-bindings/zmk/matrix_transform.h>

 / {
     chosen {
         zmk,kscan            = &kscan0;
         zmk,matrix-transform = &default_transform;
     };

     default_transform: keymap_transform_0 {
         compatible = "zmk,matrix-transform";
         columns    = <12>;
         rows       = <4>;
         // | SW1  | SW2  | SW3  | SW4  | SW5  | SW6  |   | SW6  | SW5  | SW4  | SW3  | SW2  | SW1  |
         // | SW7  | SW8  | SW9  | SW10 | SW11 | SW12 |   | SW12 | SW11 | SW10 | SW9  | SW8  | SW7  |
         // | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 |   | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 |
         //                      | SW21 | SW20 | SW19 |   | SW19 | SW20 | SW21 |
         map = <
             RC(0,5)  RC(0,4)  RC(0,3)  RC(0,2)  RC(0,1)  RC(0,0)   RC(0,6)  RC(0,7)  RC(0,8)  RC(0,9)  RC(0,10) RC(0,11)
             RC(1,5)  RC(1,4)  RC(1,3)  RC(1,2)  RC(1,1)  RC(1,0)   RC(1,6)  RC(1,7)  RC(1,8)  RC(1,9)  RC(1,10) RC(1,11)
             RC(2,5)  RC(2,4)  RC(2,3)  RC(2,2)  RC(2,1)  RC(2,0)   RC(2,6)  RC(2,7)  RC(2,8)  RC(2,9)  RC(2,10) RC(2,11)
                               RC(3,2)  RC(3,1)  RC(3,0)  RC(3,6)  RC(3,7)  RC(3,8)
         >;
     };
 };

Gpio-matrix

После создания матрицы нам необходимо описать, куда подключаются ряды и колонки. Для этого нужно посмотреть на номера пинов. Рассмотрим пример отладочной платы формата Pro Micro.

image

Нас интересуют именно синие цифры — их мы и будем указывать. Для понимания давайте разберём несколько примеров.

Одинаковая распиновка

image image

В первом примере рассмотрим вариант с col2row (ток подаётся на колонки и считывается с рядов), где на каждой половинке ряды и колонки подключены к одинаковым пинам.

Создаём блок для gpio-matrix и указываем направление диодов:

     kscan0: kscan {
         compatible      = "zmk,kscan-gpio-matrix";
         diode-direction = "col2row";
         wakeup-source;
     };

Следующим шагом ниже описываем подключение рядов, поскольку для них не требуется смещение, и их номера совпадают на обеих половинках:

         row-gpios =
               <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row1
             , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row2
             , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row3
             , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row4
             ;

В конечном счёте с такой распиновкой основной файл выглядит вот так:

#include <dt-bindings/zmk/matrix_transform.h>

 / {
     chosen {
         zmk,kscan            = &kscan0;
         zmk,matrix-transform = &default_transform;
     };

     default_transform: keymap_transform_0 {
         compatible = "zmk,matrix-transform";
         columns    = <12>;
         rows       = <4>;
         // | SW1  | SW2  | SW3  | SW4  | SW5  | SW6  |   | SW6  | SW5  | SW4  | SW3  | SW2  | SW1  |
         // | SW7  | SW8  | SW9  | SW10 | SW11 | SW12 |   | SW12 | SW11 | SW10 | SW9  | SW8  | SW7  |
         // | SW13 | SW14 | SW15 | SW16 | SW17 | SW18 |   | SW18 | SW17 | SW16 | SW15 | SW14 | SW13 |
         //                      | SW21 | SW20 | SW19 |   | SW19 | SW20 | SW21 |
         map = <
             RC(0,5)  RC(0,4)  RC(0,3)  RC(0,2)  RC(0,1)  RC(0,0)   RC(0,6)  RC(0,7)  RC(0,8)  RC(0,9)  RC(0,10) RC(0,11)
             RC(1,5)  RC(1,4)  RC(1,3)  RC(1,2)  RC(1,1)  RC(1,0)   RC(1,6)  RC(1,7)  RC(1,8)  RC(1,9)  RC(1,10) RC(1,11)
             RC(2,5)  RC(2,4)  RC(2,3)  RC(2,2)  RC(2,1)  RC(2,0)   RC(2,6)  RC(2,7)  RC(2,8)  RC(2,9)  RC(2,10) RC(2,11)
                               RC(3,2)  RC(3,1)  RC(3,0)  RC(3,6)  RC(3,7)  RC(3,8)
         >;
     };

     kscan0: kscan {
         compatible      = "zmk,kscan-gpio-matrix";
         diode-direction = "col2row";
         wakeup-source;

         row-gpios =
               <&pro_micro 5 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row1
             , <&pro_micro 6 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row2
             , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row3
             , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)> // row4
             ;
     };
 };

Теперь нам нужно в этой же директории создать файлы, в которых будут указаны пины для каждой половинки.

Начнём с левой и создадим файл имяплаты_left.overlay. В нём укажем распиновку для левой половинки:

#include "имяплаты.dtsi"

&kscan0 {
    col-gpios
        = <&pro_micro 19 GPIO_ACTIVE_HIGH> // col1
        , <&pro_micro 18 GPIO_ACTIVE_HIGH> // col2
        , <&pro_micro 15 GPIO_ACTIVE_HIGH> // col3
        , <&pro_micro 14 GPIO_ACTIVE_HIGH> // col4
        , <&pro_micro 16 GPIO_ACTIVE_HIGH> // col5
        , <&pro_micro 10 GPIO_ACTIVE_HIGH> // col6
        ;
};

Теперь создадим такой же файл только для правой половинки.

#include "имяплаты.dtsi"

&kscan0 {
    col-gpios
        = <&pro_micro 19 GPIO_ACTIVE_HIGH> // col7
        , <&pro_micro 18 GPIO_ACTIVE_HIGH> // col8
        , <&pro_micro 15 GPIO_ACTIVE_HIGH> // col9
        , <&pro_micro 14 GPIO_ACTIVE_HIGH> // col10
        , <&pro_micro 16 GPIO_ACTIVE_HIGH> // col11
        , <&pro_micro 10 GPIO_ACTIVE_HIGH> // col12
        ;
};

Поскольку мы указали 12 колонок, нужно обозначить, на сколько мы сместили колонки, чтобы начать с 7-й. Для этого между #include "имяплаты.dtsi" и &kscan0 { вставляем вот этот блок:

&default_transform {
    col-offset = <6>;
};

И теперь файл для правой половинки выглядит вот так:

#include "имяплаты.dtsi"

&default_transform {
    col-offset = <6>;
};

&kscan0 {
    col-gpios
        = <&pro_micro 19 GPIO_ACTIVE_HIGH> // col7
        , <&pro_micro 18 GPIO_ACTIVE_HIGH> // col8
        , <&pro_micro 15 GPIO_ACTIVE_HIGH> // col9
        , <&pro_micro 14 GPIO_ACTIVE_HIGH> // col10
        , <&pro_micro 16 GPIO_ACTIVE_HIGH> // col11
        , <&pro_micro 10 GPIO_ACTIVE_HIGH> // col12
        ;
};

Разная распиновка

image image

Во втором случае у нас на каждой половинке колонки и ряды подключены к разным пинам. Теперь нам нужно указывать и ряды, и колонки для каждой из них в .overlay файлах. Поэтому основной .dtsi файл теперь выглядит так:

#include <dt-bindings/zmk/matrix_transform.h>

/ {
    kscan0: kscan0 {
        compatible = "zmk,kscan-gpio-matrix";
        diode-direction = "col2row";
        wakeup-source;
    };

    chosen {
        zmk,kscan = &kscan0;
        zmk,matrix-transform = &default_transform;
    };

    default_transform: keymap_transform_0 {
        compatible = "zmk,matrix-transform";
        columns = <12>;
        rows = <4>;

        map = <
            RC(0,0)  RC(0,1)  RC(0,2)  RC(0,3)  RC(0,4)  RC(0,5)    RC(0,6)  RC(0,7)  RC(0,8)  RC(0,9)  RC(0,10) RC(0,11)
            RC(1,0)  RC(1,1)  RC(1,2)  RC(1,3)  RC(1,4)  RC(1,5)    RC(1,6)  RC(1,7)  RC(1,8)  RC(1,9)  RC(1,10) RC(1,11)
            RC(2,0)  RC(2,1)  RC(2,2)  RC(2,3)  RC(2,4)  RC(2,5)    RC(2,6)  RC(2,7)  RC(2,8)  RC(2,9)  RC(2,10) RC(2,11)
                                       RC(3,3)  RC(3,4)  RC(3,5)    RC(3,6)  RC(3,7)  RC(3,8)
        >;
    };

};

В overlay-файлах теперь всё указывается индивидуально. Вот пример для левой половинки:

#include "имяплаты.dtsi"

&kscan0 {
    col-gpios 
        = <&pro_micro 4 GPIO_ACTIVE_HIGH>   // col0
        , <&pro_micro 5 GPIO_ACTIVE_HIGH>   // col1
        , <&pro_micro 6 GPIO_ACTIVE_HIGH>   // col2
        , <&pro_micro 8 GPIO_ACTIVE_HIGH>   // col3
        , <&pro_micro 9 GPIO_ACTIVE_HIGH>   // col4
        , <&pro_micro 2 GPIO_ACTIVE_HIGH>   // col5
        ;

    row-gpios 
        = <&pro_micro 3 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>    // row0
        , <&pro_micro 7 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>    // row1
        , <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>   // row2
        , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>   // row3
        ;
};

И пример для правой половинки:

#include "имяплаты.dtsi"

&default_transform {
    col-offset = <6>;
};

&kscan0 {
    col-gpios 
        = <&pro_micro 21 GPIO_ACTIVE_HIGH>   // col6
        , <&pro_micro 10 GPIO_ACTIVE_HIGH>   // col7
        , <&pro_micro 16 GPIO_ACTIVE_HIGH>   // col8
        , <&pro_micro 15 GPIO_ACTIVE_HIGH>   // col9
        , <&pro_micro 18 GPIO_ACTIVE_HIGH>   // col10
        , <&pro_micro 19 GPIO_ACTIVE_HIGH>   // col11
        ;

    row-gpios 
        = <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>   // row0
        , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>   // row1
        , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>    // row2
        , <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>    // row3
        ;
};

Разница между col2row и row2col

В случае с col2row катод диода подключён к строке, а в случае с row2col — к колонке. Поэтому необходимо немного изменить тип пинов. Подробнее это описано здесь.

Вот как описывать пины в случае col2row:

&kscan0 {
    col-gpios 
        = <&pro_micro 21 GPIO_ACTIVE_HIGH>   // col6
        , <&pro_micro 10 GPIO_ACTIVE_HIGH>   // col7
        , <&pro_micro 16 GPIO_ACTIVE_HIGH>   // col8
        , <&pro_micro 15 GPIO_ACTIVE_HIGH>   // col9
        , <&pro_micro 18 GPIO_ACTIVE_HIGH>   // col10
        , <&pro_micro 19 GPIO_ACTIVE_HIGH>   // col11
        ;

    row-gpios 
        = <&pro_micro 20 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>   // row0
        , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>   // row1
        , <&pro_micro 8 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>    // row2
        , <&pro_micro 9 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>    // row3
        ;
};

А вот как это выглядит для row2col — здесь GPIO_PULL_DOWN указывается для колонок:

&kscan0 {
    col-gpios =
          <&pro_micro 19 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
        , <&pro_micro 18 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
        , <&pro_micro 15 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
        , <&pro_micro 14 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
        , <&pro_micro 16 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
        , <&pro_micro 10 (GPIO_ACTIVE_HIGH | GPIO_PULL_DOWN)>
        ;

    row-gpios =
          <&pro_micro 5 GPIO_ACTIVE_HIGH>
        , <&pro_micro 6 GPIO_ACTIVE_HIGH>
        , <&pro_micro 7 GPIO_ACTIVE_HIGH>
        , <&pro_micro 8 GPIO_ACTIVE_HIGH>
        ;
};