Qmk键盘生产公文翻译5-1-1部分Qmk应用导航功能定制

功能定制

如何自定义键盘功能

对于许多人来说,定制键盘所能做的不仅仅是向计算机发送按键代码信号。大多数用户希望完成比简单的键和宏更复杂的事情。Qmk附带hook,它允许您在不同情况下插入代码、重写函数或自定义键盘的性能。

一句话概述核心&键盘&键盘映射

一般来说,我们将QMK按照如下的一个结构构建:

·Core (_quantum)

      ·Keyboard/Revision (_kb)

            ·Keymap (_user)

上述的每个函数都可以用_kb()或_user()后缀定义。我们希望您在Keyboard/Revision级别时_kb()后缀,而_user()后缀应该在Keymap级别使用。

当定义Keyboard/Revision级别的函数时,注意你的_kb()在实现执行其他任何东西之前要调用_user()——否则keymap级别的函数将永远不会被调用。

自定义键码

到目前为止,最常见的操作是更改现有的键码或创建一个新的键码。从代码的角度来看,这个操作的每个机制都非常相似。

定义一个新的键码

创建您自己的自定义键码的第一步应该是去枚举它们。这意味着对它们进行命名并为该键码分配一个唯一的数字。在这里,QMK提供了名为SAFE_RANGE的宏,而不是将自定义键码限制为固定范围的数字。在枚举自定义键码时,可以使用SAFE_RANGE宏来确保获得唯一的数字。下面是一个包含2个键码的例子。在将这个块添加到你的keymap.c之后,你将能够在你的keymap中使用FOO和BAR:

enum my_keycodes {

  FOO = SAFE_RANGE,

  BAR

};

键码编程

当您想要覆盖现有键的行为,或为新键定义行为时,您应该使用process_record_kb()和process_record_user()函数。QMK将在键处理期间并在处理实际的键事件之前调用这些函数如果这些函数返回true, QMK将像往常一样处理键码。这可以方便地扩展键的功能,而不是替换它。如果这些函数返回false, QMK将跳过正常的键处理步骤,并由您发送任何可供使用的键的向上或向下事件。当您每次按下或释放键时,QMK都会调用这些函数。

例子:process_record_user()的实现

这个例子做了两件事。它定义了名为FOO的自定义键码的行为,并通过在按下Enter键时播放一种音调来提示Enter键的触发:

例子:process_record_user()的实现:

这个例子做了两件事。它定义了名为FOO的自定义键码的行为,并通过在按下Enter键时播放一种音调来补充Enter键:

bool process_record_user(uint16_t keycode, keyrecord_t *record) {

  switch (keycode) {

    case FOO:

      if (record->event.pressed) {

        // Do something when pressed

      } else {

        // Do something else when release

      }

      return false; // Skip all further processing of this key

    case KC_ENTER:

      // Play a tone when enter is pressed

      if (record->event.pressed) {

        PLAY_SONG(tone_qwerty);

      }

      return true; // Let QMK send the enter press/release events

    default:

      return true; // Process all other keycodes normally

  }

}


process_record_* 函数文档

·Keyboard/Revision:bool process_record_kb(uint16_t keycode, keyrecord_t *record)

·Keymap: bool process_record_user(uint16_t keycode, keyrecord_t *record)

键值参数是在你的keymap中定义的,例如MO(1), KC_L等。你应该用switch…case语句来处理这些事件。

record参数包含了关于实际按下键所记录的信息,如:

keyrecord_t record {

  keyevent_t event {

    keypos_t key {

      uint8_t col

      uint8_t row

    }

    bool     pressed

    uint16_t time

  }

}


键盘初始化代码

键盘初始化过程有几个步骤。这取决于您想要做什么,它将决定您应该使用哪个函数。

这是三个主要的初始化函数,按它们被调用的顺序列出:

·keyboard_pre_init_* -发生在大多数事情被启动之前。适合你想要尽早运行的硬件设置的情况。

·matrix_init_* -发生在固件启动过程的中途。硬件已经初始化,但特性可能还没有初始化。

·keyboard_post_init_* -发生在固件启动过程的末尾。在大多数情况下,这是您想要放置“定制”代码的地方。

对于大多数人来说,keyboard_post_init_user函数是最常被调用的。例如,在你想设置RGB背光的地方。


盘预初始化码

这运行在启动的很早之前,甚至在USB已经启动之前。

在这之后不久,矩阵将被初始化。

对于大多数用户来说并不常用,因为它主要用于面向硬件的初始化。

然而,如果你有需要初始化的硬件,这是最适合初始化的地方(如初始化LED引脚)。

例子:keyboard_pre_init_user()的实现

本例中,在键盘级中,将B0、B1、B2、B3和B4设置为LED引脚。

void keyboard_pre_init_user(void) {

  // Call the keyboard pre init code.

// Set our LED pins as output

  setPinOutput(B0);

  setPinOutput(B1);

  setPinOutput(B2);

  setPinOutput(B3);

  setPinOutput(B4);

}


keyboard_pre_init_ *函数文档

·Keyboard/Revision:void keyboard_pre_init_kb(void)

·Keymap:void keyboard_pre_init_user(void)

矩阵初始化代码

这是在矩阵初始化时调用的,在一些硬件已经建立之后,但在许多特性已经被初始化之前。

这对于您可能在其他地方的设置所需要的东西很有用,但与硬件无关,也不依赖于它的起始位置。

matrix_init_ *功能文档

·Keyboard/Revision:void matrix_init_kb(void)

·Keymap:void matrix_init_user(void)


键盘后初始化代码

这是作为键盘初始化过程中的最后一个任务运行的。如果您想对某些特性进行更改,这是非常有用的,因为它们应该在此时初始化。

例子:keyboard_post_init_user()实现

在这个例子中,在所有东西都在后初始化运行,此为设置rgb底灯的配置。

void keyboard_post_init_user(void) {

  // Call the post init code.

  rgblight_enable_noeeprom(); // enables Rgb, without saving settings

  rgblight_sethsv_noeeprom(180, 255, 255); // sets the color to teal/cyan without saving

  rgblight_mode_noeeprom(RGBLIGHT_MODE_BREATHING + 3); // sets mode to Fast breathing without saving

}

keyboard_post_init_ *函数文档

·Keyboard/Revision:void keyboard_post_init_kb(void)

·Keymap:void keyboard_post_init_user(void)


矩阵扫描代码

只要有可能,您就应该使用process_record_*()函数来客制化的键盘功能,并以这种方式连接到事件中,以确保您的代码不会对键盘产生负面的性能影响。然而,在少数情况下,功能有必要挂钩到矩阵扫描下。请特别注意这些函数中的代码性能,因为它每秒至少会被调用10次。

例子:matrix_scan_ *的实现

这个例子故意省略了。涉及这样一个性能敏感的领域之前,您需要对QMK内部结构有足够的了解,并能够在不用示例的情况下编写这些内容。如果您需要帮助,请联系作者Discord官方或者发布提问

matrix_scan_ *函数文档

·Keyboard/Revision:void matrix_scan_kb(void)

·Keymap:void matrix_scan_user(void)

这个函数在每次矩阵扫描时都会被调用,这基本上同步于MCU能够处理的频率。请务必确认你放在这里的东西足够安全,因为它会经常运行。

如果你需要自定义矩阵扫描代码,你应该使用这个函数。它还可以用于自定义状态输出(如led或显示)或其他您希望在用户不打字时定期触发的功能。


键盘管家

·Keyboard/Revision:void housekeeping_task_kb(void)

·Keymap:void housekeeping_task_user(void)

您可以在下一个迭代开始之前,在所有QMK处理结束时调用此函数。你可以假设QMK已经安全地处理了最后的矩阵扫描的时候,这些函数的被调用层的状态已经更新,USB报告已经发送,led已经更新,并且显示已经输出。

与matrix_scan_*类似,这些函数在MCU能够处理的情况下被频繁调用。为了保持您系统能够正确响应,建议在这些函数调用期间尽可能少做一些事情,如果您确实需要实现一些特殊的东西,可能会阻碍它们的行为。

键盘空转/唤醒代码

如果您的PCB支持这个功能,它可以“空闲”,表现为停止一些功能。一个很好的例子是RGB灯或背光。这可以节省电力消耗,或者减少你的键盘的寿命损耗。

这是由两个函数控制的:susend_power_down_ *和susend_wakeup_init_ *,它们分别在系统板空闲和唤醒时被调用。

例子:susend_power_down_user()和susend_wakeup_init_user()的实现

void suspend_power_down_user(void) {

    rgb_matrix_set_suspend_state(true);

}

void suspend_wakeup_init_user(void) {

    rgb_matrix_set_suspend_state(false);

}


键盘挂起/唤醒功能文档

·Keyboard/Revision:

    void suspend_power_down_kb(void)

    and 

    void suspend_wakeup_init_user(void)

·Keymap:void housekeeping_task_user(void)

    void suspend_power_down_kb(void)

    and 

    void suspend_wakeup_init_user(void)


换层代码

每当层被更改时,它都会运行此代码。这对于层指示或自定义层结构很有用。

例子:layer_state_set_ *的实现

这个例子展示了如何设置基于图层的RGB下位灯光,以Plank为例:

layer_state_t layer_state_set_user(layer_state_t state) {

    switch (get_highest_layer(state)) {

    case _RAISE:

        rgblight_setrgb (0x00,  0x00, 0xFF);

        break;

    case _LOWER:

        rgblight_setrgb (0xFF,  0x00, 0x00);

        break;

    case _PLOVER:

        rgblight_setrgb (0x00,  0xFF, 0x00);

        break;

    case _ADJUST:

        rgblight_setrgb (0x7A,  0x00, 0xFF);

        break;

    default: //  for any other layers, or the default layer

        rgblight_setrgb (0x00,  0xFF, 0xFF);

        break;

    }

  return state;

}

在此使用了使用IS_LAYER_ON_STATE(state, layer)和IS_LAYER_OFF_STATE(state, layer)宏来检查特定层的状态。

在layer_state_set_*函数之外,可以使用IS_LAYER_ON(layer)和IS_LAYER_OFF(layer)宏来检查全局层状态。

layer_state_set_ *功能文档

·Keyboard/Revision:layer_state_t layer_state_set_kb(layer_state_t state)

·Keymap:layer_state_t layer_state_set_user(layer_state_t state)

状态(state)是活动层的位掩码,在Keymap的概述中将会解释

长期设置(EEPROM  Electrically Erasable Programmable read only memory 带电可擦可编程只读存储器)

这允许您为键盘配置长期的设置。这些设置存储在您的控制器的EEPROM中,并在断电后仍然保留。您可以使用eeconfig_read_kb和eeconfig_read_user函数读取设置,也可以使用eeconfig_update_kb和eeconfig_update_user写入设置。这对于你想要切换的特性是很有用的(比如切换rgb层指示)。另外,您可以使用eeconfig_init_kb和eeconfig_init_user函数为EEPROM设置默认值。

这里复杂的部分是,有很多的方法可以实现这一点,比如说,你可以存储和访问数据通过EEPROM,但是并没有绝对“正确的”方法来做到这一点。但是共通的是,每个函数只有一个DWORD(4字节)。

请记住,EEPROM的写入次数是有限的。虽然这是一个非常高的数字,但此类设置不是唯一写入EEPROM的东西,如果您写入地太频繁,MCU的寿命可能会大幅度缩短。

如果您不理解这个示例,那么请尽可能地不去使用这个特性,因为它确实相当复杂。

例子:这是一个如何添加设置和读写设置的示例。我们在这里的例子中使用用户键的映射。这是一个复函数,它包含了很多内容。事实上,它使用了上面的很多函数来工作:

*在你的keymap.c文件中,请把这个添加到顶部:

typedef union {

  uint32_t raw;

  struct {

    bool     rgb_layer_change :1;

  };

} user_config_t;

user_config_t user_config;

这个代码块建立了一个32位的结构,我们可以在内存中存储设置,并写入EEPROM。使用这个方法就不需要定义变量,因为它们是在这个结构中定义的。记住bool (boolean)值使用1位,uint8_t使用8位,uint16_t使用16位。您可以混合和匹配它们,但更改顺序可能会导致问题,因为它将更改读取和写入的值。

对于layer_state_set_*函数,我们使用rgb_layer_change,并使用keyboard_post_init_user和process_record_user函数来配置所有内容。

现在,使用上面的keyboard_post_init_user代码,您希望将eeconfig_read_user()添加到其中,以填充刚才创建的结构。然后你可以立即使用这个结构来控制你的keymap中的功能。它应该是这样的:

void keyboard_post_init_user(void) {

  // Call the keymap level matrix init.

  // Read the user config from EEPROM

  user_config.raw = eeconfig_read_user();

  // Set default layer, if enabled

  if (user_config.rgb_layer_change) {

    rgblight_enable_noeeprom();

    rgblight_sethsv_noeeprom_cyan();

    rgblight_mode_noeeprom(1);

  }

}

上面的代码块会在读取后立即使用EEPROM配置,来设置默认层的RGB颜色。它的“原始”值会根据您上面创建的“联合”来转换为一个可用的结构。

layer_state_t layer_state_set_user(layer_state_t state) {

    switch (get_highest_layer(state)) {

    case _RAISE:

        if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_magenta(); rgblight_mode_noeeprom(1); }

        break;

    case _LOWER:

        if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_red(); rgblight_mode_noeeprom(1); }

        break;

    case _PLOVER:

        if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_green(); rgblight_mode_noeeprom(1); }

        break;

    case _ADJUST:

        if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_white(); rgblight_mode_noeeprom(1); }

        break;

    default: //  for any other layers, or the default layer

        if (user_config.rgb_layer_change) { rgblight_sethsv_noeeprom_cyan(); rgblight_mode_noeeprom(1); }

        break;

    }

  return state;

}

这个代码块将导致RGB underglow只在启用时改变。现在要配置这个值,请为process_record_user创建一个名为RGB_LYR的新键码。另外,我们想确保当你使用普通的RGB代码时,上面的操作会主动关闭它,让它看起来像这样:

bool process_record_user(uint16_t keycode, keyrecord_t *record) {

  switch (keycode) {

    case FOO:

      if (record->event.pressed) {

        // Do something when pressed

      } else {

        // Do something else when release

      }

      return false; // Skip all further processing of this key

    case KC_ENTER:

        // Play a tone when enter is pressed

        if (record->event.pressed) {

            PLAY_SONG(tone_qwerty);

        }

        return true; // Let QMK send the enter press/release events

    case RGB_LYR:  // This allows me to use underglow as layer indication, or as normal

        if (record->event.pressed) {

            user_config.rgb_layer_change ^= 1; // Toggles the status

            eeconfig_update_user(user_config.raw); // Writes the new status to EEPROM

            if (user_config.rgb_layer_change) { // if layer state indication is enabled,

                layer_state_set(layer_state);   // then immediately update the layer color

            }

        }

        return false;

    case RGB_MODE_FORWARD … RGB_MODE_GRADIENT: // For any of the RGB codes (see quantum_keycodes.h, L400 for reference)

        if (record->event.pressed) { //This disables layer indication, as it’s assumed that if you’re changing this … you want that disabled

            if (user_config.rgb_layer_change) {        // only if this is enabled

                user_config.rgb_layer_change = false;  // disable it, and

                eeconfig_update_user(user_config.raw); // write the setings to EEPROM

            }

        }

        return true; break;

    default:

      return true; // Process all other keycodes normally

  }

}

最后,您需要添加eeconfig_init_user函数,以便在重置EEPROM时,可以指定默认值,甚至自定义操作。为了强制EEPROM复位,请使用EEP_RST键码或Bootmagic功能。例如,如果要设置rgb层指示为默认值,并保存为默认值:

void eeconfig_init_user(void) {  // EEPROM is getting reset!

  user_config.raw = 0;

  user_config.rgb_layer_change = true; // We want this enabled by default

  eeconfig_update_user(user_config.raw); // Write default value to EEPROM now

  // use the non noeeprom versions, to write these values to EEPROM too

  rgblight_enable(); // Enable RGB by default

  rgblight_sethsv_cyan();  // Set it to CYAN by default

  rgblight_mode(1); // set to solid by default

}

OK,到这里你已经做完了所有操作。RGB图层指示将只在你想要的时候工作。而且即使拔下键盘,它也会被保存。如果你使用任何其他的RGB代码,它会禁用层指示,这样它就会存储在你设置的模式和颜色中。


“EECONFIG”功能的文档

·Keyboard/Revision:void eeconfig_init_kb(void), uint32_t eeconfig_read_kb(void)和void eeconfig_update_kb(uint32_t val)

·Keymap: void eeconfig_init_user(void), uint32_t eeconfig_read_user(void,void eeconfig_update_user(uint32_t val)

val值是要写入EEPROM的数据的值。eeconfig_read_*函数用来从EEPROM返回一个32位(DWORD)值。

翻译:AIALRA

资源下载: