这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » tmk_keyboard 代码学习1-按键宏的实现

共13条 1/2 1 2 跳转至

tmk_keyboard 代码学习1-按键宏的实现

菜鸟
2015-09-08 00:34:58     打赏

学习tmk代码的目标主要是理解自定义宏与函数的部分,但只是按键相关的方法,比如一键输入一串按键这种宏,对于与硬件比如led这种的就一窍不通了。

直奔主题,先看下tmk_keyboard_custom下的README.md,可以知道与具体键盘相关的代码在keyboard下面,咱们这次就只看gh60,但hhkb下面有一些可以学习的调用,可以结合着看。

首先看makefile,其中的


说明这几个文件是必需要的文件,而:

这个是在make的时候指定的,默认是keymap_poker.c, 也就是

make KEYMAP=xxxxx

这个命令里指定了配列相关的名字,但咱们先不从keymap_pocker.c开始看,而是从../hhkb/keymap_hasu.c这个代码结合从http://www.enjoyclick.org/tkg/# 从这里下载的代码来看(主要是想偷懒,借助自动生成的代码比较方便),因为这里的实现的方法多一些,可以从中学习。用来做示例的配列如下:

Layer 0: http://www.keyboard-layout-editor.com/#/gists/fa6c48d4975e6d64e361

Layer 1: http://www.keyboard-layout-editor.com/#/gists/652b6bb9da03a2781a59

Tmk生成代码时的配置为:

我们这次要关注的是  Fn 1和Fn2,下载下来的keymap.c是这样的,可见它的macro功能是还没有完善的:

// Generated by TKG at 2015-09-07 22:41:51

#include "keymap_common.h"

#define KEYMAP_TKG( \

K0A, K0B, K0C, K0D, K0E, K0F, K0G, K0H, K0I, K0J, K0K, K0L, K0M, K0N, \

K1A, K1B, K1C, K1D, K1E, K1F, K1G, K1H, K1I, K1J, K1K, K1L, K1M, K1N, \

K2A, K2B, K2C, K2D, K2E, K2F, K2G, K2H, K2I, K2J, K2K, K2L,      K2N, \

K3A,      K3C, K3D, K3E, K3F, K3G, K3H, K3I, K3J, K3K, K3L,      K3N, \

K4A, K4B, K4C,           K4F,                K4J, K4K, K4L, K4M, K4N  \

) { \

{ KC_##K0A, KC_##K0B, KC_##K0C, KC_##K0D, KC_##K0E, KC_##K0F, KC_##K0G, KC_##K0H, KC_##K0I, KC_##K0J, KC_##K0K, KC_##K0L, KC_##K0M, KC_##K0N }, \

{ KC_##K1A, KC_##K1B, KC_##K1C, KC_##K1D, KC_##K1E, KC_##K1F, KC_##K1G, KC_##K1H, KC_##K1I, KC_##K1J, KC_##K1K, KC_##K1L, KC_##K1M, KC_##K1N }, \

{ KC_##K2A, KC_##K2B, KC_##K2C, KC_##K2D, KC_##K2E, KC_##K2F, KC_##K2G, KC_##K2H, KC_##K2I, KC_##K2J, KC_##K2K, KC_##K2L, KC_NO,    KC_##K2N }, \

{ KC_##K3A, KC_NO,    KC_##K3C, KC_##K3D, KC_##K3E, KC_##K3F, KC_##K3G, KC_##K3H, KC_##K3I, KC_##K3J, KC_##K3K, KC_##K3L, KC_NO,    KC_##K3N }, \

{ KC_##K4A, KC_##K4B, KC_##K4C, KC_NO,    KC_NO,    KC_##K4F, KC_NO,    KC_NO,    KC_NO,    KC_##K4J, KC_##K4K, KC_##K4L, KC_##K4M, KC_##K4N }  \

}

#ifdef KEYMAP_SECTION_ENABLE

const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] __attribute__ ((section (".keymap.keymaps"))) = {

#else

const uint8_t keymaps[][MATRIX_ROWS][MATRIX_COLS] PROGMEM = {

#endif

[0] = KEYMAP_TKG(

ESC, 1,   2,   3,   4,   5,   6,   7,   8,   9,   0,   MINS,EQL, BSLS, \

TAB, Q,   W,   E,   R,   T,   Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC, \

LCTL,A,   S,   D,   F,   G,   H,   J,   K,   L,   SCLN,QUOT,     ENT,  \

LSFT,     Z,   X,   C,   V,   B,   N,   M,   COMM,DOT, SLSH,     RSFT, \

APP, LALT,LGUI,          SPC,                GRV, LALT,LGUI,FN0, RCTL),

[1] = KEYMAP_TKG(

ESC, F1,  F2,  F3,  F4,  F5,  F6,  F7,  F8,  F9,  F10, F11, F12, BSLS, \

CAPS,FN1, FN2, FN3, FN4, FN5, Y,   U,   I,   O,   P,   LBRC,RBRC,BSPC, \

LCTL,END, S,   D,   F,   G,   LEFT,DOWN,UP,  RGHT,SCLN,QUOT,     ENT,  \

LSFT,     Z,   DEL, C,   V,   B,   N,   M,   COMM,DOT, SLSH,     RSFT, \

APP, LALT,LGUI,          SPC,                GRV, LALT,LGUI,FN0, RCTL),

};

#ifdef KEYMAP_SECTION_ENABLE

const uint16_t fn_actions[] __attribute__ ((section (".keymap.fn_actions"))) = {

#else

const uint16_t fn_actions[] PROGMEM = {

#endif

[0] = ACTION_LAYER_TOGGLE(1),

[1] = ACTION_MACRO(),

[2] = ACTION_MACRO(),

[3] = ACTION_MACRO(),

[4] = ACTION_MACRO(),

[5] = ACTION_MACRO(),

};

标红的部分就是我们在tmk页面上设置的那几个Fn键,其中0就是作为 layer 0 --> layer 1的开关,剩下的没有实现的,我们这次就是来实现它的。

把keymap.c改名为keymap_patrick.c(你喜欢的名字,但要以keymap_ 为前缀), 然后运行 make KEYMAP=patrick  看一下,是不是会报错,因为ACTION_MACRO这几个宏咱们还没有补充完成,看看下  keymap_hhkb.c里面是怎么写的:

enum macro_id {

HELLO,

VOLUP,

ALT_TAB,

};

#ifdef KEYMAP_SECTION_ENABLE

const uint16_t fn_actions[] __attribute__ ((section (".keymap.fn_actions"))) = {

#else

const uint16_t fn_actions[] PROGMEM = {

#endif

[0] = ACTION_DEFAULT_LAYER_SET(0),                // Default layer(not used)

[1] = ACTION_MACRO(HELLO),

.......

[8] = ACTION_MACRO(ALT_TAB), // Application switching

[9] = ACTION_MODS_KEY(MOD_LALT, KC_LEFT),

[10] = ACTION_MODS_KEY(MOD_LALT, KC_RIGHT),

const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)

{

switch (id) {

case HELLO:

return (record->event.pressed ?

MACRO( I(0), T(H), T(E), T(L), T(L), W(255), T(O), END ) :

MACRO_NONE );

case VOLUP:

return (record->event.pressed ?

MACRO( D(VOLU), U(VOLU), END ) :

MACRO_NONE );

case ALT_TAB:

return (record->event.pressed ?

MACRO( D(LALT), D(TAB), END ) :

MACRO( U(TAB), END ));

}

return MACRO_NONE;

}

void action_function(keyrecord_t *record, uint8_t id, uint8_t opt)

{

if (record->event.pressed) dprint("P"); else dprint("R");

dprintf("%d", record->tap.count);

if (record->tap.interrupted) dprint("i");

dprint("\n");

switch (id) {

case LSHIFT_LPAREN:

.........

break;

}

}



1.自己写不会,咱山寨一个还不可以吗,可以看到原创的有这几步:

2.定义一个enum.

3.增加  [1] = ACTION_MACRO(HELLO)   的赋值。

4.实现 action_function 函数,通过 id这个参数来做一下case选择,而这个id就是我们上面定义enum时的值。

5.实现case里面的实际操作,比如要输出 HELLO, 则要进行

6.MACRO( I(0), T(H), T(E), T(L), T(L), W(255), T(O), END ) :MACRO_NONE );

这个是什么意思呢?从文档里面可以看到  I为间隔, T为点击, W为等待,D为按下, U为抬起, END 标识结束, 整个语句的意思也就是:间隔0,输出HELL,等待255(不知道是啥单位), 再接着输出O,然后结束。

我们照写,最终得到这样的代码:

enum macro_id {

HELLO,

ALT_TAB,

};

#ifdef KEYMAP_SECTION_ENABLE

const uint16_t fn_actions[] __attribute__ ((section (".keymap.fn_actions"))) = {

#else

const uint16_t fn_actions[] PROGMEM = {

#endif

[0] = ACTION_LAYER_TOGGLE(1),

[1] = ACTION_MACRO(HELLO),

[2] = ACTION_MACRO(ALT_TAB),//只定义这两个来试验一下,其它的fn大家可以补上

};

const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)

{

switch (id) {

case ALT_TAB:

return (record->event.pressed ?

MACRO( D(LALT), D(TAB), END ) :

MACRO( U(TAB), END ));

case HELLO:

return (record->event.pressed ?

MACRO(I(0), T(H), T(E), T(L), T(L), W(255), T(O), END ) :

MACRO_NONE );

}

return MACRO_NONE;

}

#ifdef KEYMAP_IN_EEPROM_ENABLE

uint16_t keys_count(void) {

return sizeof(keymaps) / sizeof(keymaps[0]) * MATRIX_ROWS * MATRIX_COLS;

}

uint16_t fn_actions_count(void) {

return sizeof(fn_actions) / sizeof(fn_actions[0]);

}

#endif   //红色部分的代码是要我们自己补上去的,不然链接的时候会有些符号找不到


完整的代码(会有些不影响功能的变动)见附件。

然后我们再来 make KEYMAP=patrick的时候可以编译通过了,它会生成

而红圈的那个就是我们可以用来刷进去的固件了,但有个地方要注意下,Makefile里面的  KEYMAP_IN_EEPROM_ENABLE = yes  要注释掉,对嵌入式的不熟,只是不去掉刷固件的时候会报错,在网上找了下找到这个方法就可以成功了,有懂的同学可以告知下是什么原因。在win下可以把这个hex文件拖到

这里就可以进行刷了。win下刷似乎有问题,,mac/linux下都可以直接 make dfu 

刷完成后可以试验下,先按fn0,把layer 切换到1,然后点击 Q(也就是fn1),和 W(Fn2)可以模拟 alt +tab 程序切换  、 输出   HELLO 这个字符串,成功实现了宏定义功能。

当然这里只说到了它的实现,至于原理或者整个调用流程,有时间再来写下吧。

这些都是我个人的理解,多有谬误,大家觉得有更便捷的方法或者看到哪里有错误麻烦指出,谢谢。

附:centos安装开发环境:

yum install avr-gcc  avr-libc


gh60_patrick.zip




关键词: gh60    

菜鸟
2015-09-08 08:58:45     打赏
2楼
好贴,分析的有点到位了!顶了!

专家
2015-09-08 11:30:28     打赏
3楼
好帖,分析够投入

菜鸟
2015-09-08 18:27:26     打赏
4楼
好贴,非常赞

工程师
2015-09-09 00:34:43     打赏
5楼
此贴精彩! 楼主是花了时间研究的,太晚了,先mark明天上班细看咱们讨论。

菜鸟
2015-09-09 11:37:07     打赏
6楼

好贴必须顶!!!


菜鸟
2015-12-13 22:20:58     打赏
7楼
博主, 最近刚玩gh60 但是碰到了几个问题, tmk_keyboard_custom项目和 tmk_keyboard之前有什么区别呀? 我make了一下 tmk_keyboard_custom 但缺少 keymap_in_eeprom.h

工程师
2015-12-22 15:22:41     打赏
8楼
学习了。。。。。。。。。。。。。

专家
2015-12-22 16:30:06     打赏
9楼
学习了!

工程师
2016-06-05 10:37:14     打赏
10楼
学习@!!!!

共13条 1/2 1 2 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]