本篇文章介紹如何利用Raspberry Pi Pico W和LVGL Graphics library 與 Btstack bluetooth library,製做一個小型但又功能齊全的BLE藍芽鍵盤。
有關HID over GATT device的介紹,可參閱前篇:[Raspberry Pi Pico W] BTstack BLE Ep 4. HID over GATT -- Custom keypad HID device, 本篇文章著重在利用LVGL library製作完整功能的鍵盤。
本文章介紹的鍵盤分別有四縱模式: 小寫英文字、大寫英文字、數字與特殊符號和注音鍵盤。四種鍵盤分對應到LVGL keyboard widget: LV_KEYBOARD_MODE_TEXT_LOWER、LV_KEYBOARD_MODE_TEXT_UPPER、LV_KEYBOARD_MODE_SPECIAL and LV_KEYBOARD_MODE_USER_1。
小寫英文字、大寫英文字、數字與特殊符號鍵盤使用LVGL內定keyboard widgets。
注音輸入鍵盤則利用LV_KEYBOARD_MODE_USER_1模式,自製鍵盤時需要按鍵排例方式與每個按鍵控制模式:
- 按鍵排例方式
- 每個按鍵控制模式
lv_obj_set_style_text_font(kb, &jf_font, LV_PART_ITEMS);//設定字形
lv_keyboard_set_map(kb, LV_KEYBOARD_MODE_USER_1, font_map, font_ctl_map);//設定LV_KEYBOARD_MODE_USER_1鍵盤
- 注音鍵盤HID scan code對應表:
製作鍵盤按鍵對應到HID scan code, 其中modifier為0。
使用上述指令加入按鍵的event callback function,處理收到按鍵按下時查詢按鍵的modifier and key_code 再透過HID report由藍芽輸出。其HID report格式如下所定義:
hid keyboard descriptor定義input data為:modifier(1 byte), reserved byte(1 byte)與keycode(6 bytes)。output data為:5 bits+3 bits。
指令:
send_hid_spec_code(uint8_t modifier, uint8_t keycode);
直接送出HID report,
key_input(uint8_t character);
查詢輸入character的modifier與keycode後再送出HID Report。
static int keycode_and_modifer_us_for_character(uint8_t character, uint8_t * keycode, uint8_t * modifier); //查詢character的modifier and keycode
三種keycode表如下:
- static const uint8_t keytable_scan_codes[]:
小寫字母與沒有shift的特殊符號。 - static const uint8_t keytable_scan_codes_shift[]:
大寫字母與有shift的特殊符號。 - static const char* keyboard_phonetic[]:
注音符號keycode表,modifier為0。
詳細程式碼附於文章末尾。
成果影片:
程式碼:
CMakeLists.txt
# Generated Cmake Pico project file cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) # Initialise pico_sdk from installed location # (note this can come from environment, CMake cache etc) set(PICO_SDK_PATH "/home/duser/pico/pico-sdk") set(PICO_BOARD pico_w CACHE STRING "Board type") # Pull in Raspberry Pi Pico SDK (must be before project) include(pico_sdk_import.cmake) if (PICO_SDK_VERSION_STRING VERSION_LESS "1.4.0") message(FATAL_ERROR "Raspberry Pi Pico SDK version 1.4.0 (or later) required. Your version is ${PICO_SDK_VERSION_STRING}") endif() project(picow_lvgl_hog_keyboard C CXX ASM) # Initialise the Raspberry Pi Pico SDK pico_sdk_init() # Add executable. Default name is the project name, version 0.1 add_executable(picow_lvgl_hog_keyboard picow_lvgl_hog_keyboard.c lvgl_keyboard.c zh_TW_kb.c shortcuts.c ) pico_set_program_name(picow_lvgl_hog_keyboard "picow_lvgl_bt_kb") pico_set_program_version(picow_lvgl_hog_keyboard "0.1") pico_enable_stdio_uart(picow_lvgl_hog_keyboard 1) pico_enable_stdio_usb(picow_lvgl_hog_keyboard 0) # Add the standard library to the build target_link_libraries(picow_lvgl_hog_keyboard pico_stdlib) # Add the standard include files to the build target_include_directories(picow_lvgl_hog_keyboard PRIVATE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts or any other standard includes, if required ) # Add any user requested libraries target_link_libraries(picow_lvgl_hog_keyboard hardware_spi hardware_dma hardware_pio pico_util pico_cyw43_arch_none pico_btstack_cyw43 pico_btstack_ble ) add_subdirectory(pico_lvgl) target_link_libraries(picow_lvgl_hog_keyboard pico_lvgl ) pico_btstack_make_gatt_header(picow_lvgl_hog_keyboard PRIVATE "${CMAKE_CURRENT_LIST_DIR}/picow_lvgl_hog_keyboard.gatt") pico_add_extra_outputs(picow_lvgl_hog_keyboard)
lvgl_keyboard.c
#include "stdio.h" #include "pico/stdlib.h" #include "pico_lvgl.h" #include "usb_hid_scan_code.h" extern const lv_font_t jf_font; uint8_t lvgl_kb_left[4] = {239, 129, 147,0}; uint8_t lvgl_kb_right[4] = {239, 129, 148,0}; uint8_t lvgl_kb_ok[4] = {239, 128, 140,0}; uint8_t lvgl_kb_enter[4] = {239, 162, 162,0}; uint8_t lvgl_kb_bs[4] = {239, 149, 154,0}; uint8_t lvgl_kb_kb[4] = {239, 132, 156,0}; void set_zh_TW_keyboard(lv_obj_t *kb); void shortcuts_init(lv_obj_t* parent); void key_input(char character); void send_hid_spec_code(uint8_t modifier, uint8_t keycode); static uint8_t phonetic_selected=0; void toggle_sel_key(lv_obj_t* kb) { if (phonetic_selected==0) { phonetic_selected = 1; send_hid_spec_code(0, KEY_DOWN); for(int i=0; i < 10; i++) { lv_btnmatrix_clear_btn_ctrl(kb,i,LV_BTNMATRIX_CTRL_HIDDEN); lv_btnmatrix_set_btn_ctrl(kb,i, LV_KEYBOARD_CTRL_BTN_FLAGS|1); } } else{ if (phonetic_selected == 1) { send_hid_spec_code(0, KEY_UP); } for(int i=0; i < 10; i++) { lv_btnmatrix_clear_btn_ctrl(kb,i,LV_KEYBOARD_CTRL_BTN_FLAGS|1); lv_btnmatrix_set_btn_ctrl(kb,i, LV_BTNMATRIX_CTRL_HIDDEN); } phonetic_selected=0; } } static bool lookup_phonetic_keycode(lv_obj_t *kb, uint8_t* key, uint8_t * keycode){ uint16_t size = 57;//sizeof(keyboard_phonetic); int i; for (i=0;i<size;i++){ if (strcmp(keyboard_phonetic[i], key) !=0) continue; *keycode = i; return true; } if (key[0] >= '0' && key[0] <= '9'){ key_input(key[0]); phonetic_selected++; toggle_sel_key(kb); } return false; } uint8_t lvgl_keyboard_mode; void lvgl_keyboard_cb(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); lv_obj_t* obj = lv_event_get_target(e); lv_obj_t * kb = lv_event_get_user_data(e); char key[5]; if (code == LV_EVENT_CLICKED) { strcpy(key, lv_keyboard_get_btn_text(kb,lv_keyboard_get_selected_btn(kb))); if (strcmp(key, "ABC")==0) { lvgl_keyboard_mode = LV_KEYBOARD_MODE_TEXT_UPPER; return; } if (strcmp(key, "abc") == 0) { lvgl_keyboard_mode = LV_KEYBOARD_MODE_TEXT_LOWER; return; } if (lv_keyboard_get_mode(kb) != LV_KEYBOARD_MODE_USER_1) { if ((lvgl_keyboard_mode == LV_KEYBOARD_MODE_TEXT_LOWER || lvgl_keyboard_mode == LV_KEYBOARD_MODE_TEXT_UPPER ) && key[0] == '1') { lvgl_keyboard_mode = LV_KEYBOARD_MODE_SPECIAL; return; } if (strlen(key) == 1) { key_input(key[0]); return; } } else { static uint8_t keycode; if (lookup_phonetic_keycode(kb, key, &keycode)) { send_hid_spec_code(0, keycode); return; } } if (strcmp(key, lvgl_kb_enter)==0) { key_input(CHAR_RETURN); return; } if (strcmp(key, lvgl_kb_right)==0 || strcmp(key, "〉") == 0) { send_hid_spec_code(0, KEY_RIGHT); return; } if (strcmp(key, lvgl_kb_left)==0 || strcmp(key, "〈") == 0) { send_hid_spec_code(0, KEY_LEFT); return; } if (strcmp(key, lvgl_kb_bs)==0 || strcmp(key, "←")==0) { key_input(CHAR_BACKSPACE); return; } if (strcmp(key, "︿") == 0) { send_hid_spec_code(0, KEY_UP); return; } if (strcmp(key, "﹀") == 0) { send_hid_spec_code(0, KEY_DOWN); return; } if (strcmp(key, ",") == 0) { //for Windows //send_hid_spec_code(0, KEY_GRAVE);send_hid_spec_code(0, KEY_COMMA); send_hid_spec_code(KEY_MOD_LSHIFT, KEY_COMMA); return; } if (strcmp(key, "。") == 0) { ///for Windows //send_hid_spec_code(0, KEY_GRAVE);send_hid_spec_code(0, KEY_DOT); send_hid_spec_code(KEY_MOD_LSHIFT, KEY_DOT); return; } if (strcmp(key, lvgl_kb_ok)==0) { key_input(CHAR_RETURN); return; } if (strcmp(key, lvgl_kb_kb)==0){ lv_obj_set_style_text_font(kb, &jf_font, LV_PART_ITEMS); lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_USER_1); send_hid_spec_code(KEY_MOD_LCTRL, 0x2c); //0x2c space key scan code; return; } if (strcmp(key, "英")==0){ lv_obj_set_style_text_font(kb, lv_font_default(), LV_PART_ITEMS); lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_TEXT_LOWER); send_hid_spec_code(KEY_MOD_LCTRL, 0x2c); //0x2c space key scan code; return; } if (strcmp(key, "數")==0){ lv_obj_set_style_text_font(kb, lv_font_default(), LV_PART_ITEMS); lv_keyboard_set_mode(kb, LV_KEYBOARD_MODE_SPECIAL); send_hid_spec_code(KEY_MOD_LCTRL, 0x2c); //0x2c space key scan code; return; } if (strcmp(key, "選字")==0){ toggle_sel_key(kb); return; } } } void lvgl_keyboard_init(void) { /*Create a keyboard to use it with an of the text areas*/ lv_obj_t * lvgl_keyboard = lv_keyboard_create(lv_scr_act()); lv_obj_t *shortcuts_frame = lv_obj_create(lv_scr_act()); shortcuts_init(shortcuts_frame); lv_obj_set_size(shortcuts_frame, lv_pct(100), lv_pct(32)); lv_obj_set_style_pad_all(shortcuts_frame, 3,0); lv_obj_set_style_bg_color(shortcuts_frame, lv_palette_main(LV_PALETTE_LIGHT_BLUE),0); set_zh_TW_keyboard(lvgl_keyboard); lvgl_keyboard_mode = LV_KEYBOARD_MODE_TEXT_LOWER; lv_obj_set_size(lvgl_keyboard, lv_pct(100), lv_pct(67)); lv_obj_add_event_cb(lvgl_keyboard, lvgl_keyboard_cb, LV_EVENT_ALL, lvgl_keyboard); }
pico_lvgl_hog_keyboard.c
#include <stdio.h> #include "pico/stdlib.h" #include "pico/cyw43_arch.h" #include "btstack.h" #include "ble/gatt-service/battery_service_server.h" #include "ble/gatt-service/device_information_service_server.h" #include "ble/gatt-service/hids_device.h" #include "picow_lvgl_hog_keyboard.h" #include "inttypes.h" #include "pico_lvgl.h" #include "usb_hid_scan_code.h" extern const lv_font_t jf_font; PIO TFT_PIO = pio0; #define TFT_SM 0 #define TFT_SDI_GPIO 9 #define TFT_CSX_DCX_SCK_GPIO 6 // CSX=8, DCX=7, SCK=6, SIDE_SET void lvgl_keyboard_init(); static btstack_packet_callback_registration_t hci_event_callback_registration; static btstack_packet_callback_registration_t sm_event_callback_registration; static uint8_t battery = 100; static hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID; static uint8_t protocol_mode = HID_PROTOCOL_MODE_REPORT; const uint8_t adv_data[] = { // Flags general discoverable, BR/EDR not supported 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, // Name 0x0d, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'H', 'I', 'D', ' ', 'K', 'e', 'y', 'b', 'o', 'a', 'r', 'd', //0x0d // 16-bit Service UUIDs 0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE & 0xff, ORG_BLUETOOTH_SERVICE_HUMAN_INTERFACE_DEVICE >> 8, // Appearance HID - Keyboard (Category 15, Sub-Category 1) 0x03, BLUETOOTH_DATA_TYPE_APPEARANCE, 0xC1, 0x03, }; const uint8_t adv_data_len = sizeof(adv_data); // Buffer for 30 characters static uint8_t key_input_storage[30]; static btstack_ring_buffer_t key_input_buffer; // HID Keyboard lookup static int lookup_keycode(uint8_t character, const uint8_t * table, int size, uint8_t * keycode){ int i; for (i=0;i<size;i++){ if (table[i] != character) continue; *keycode = i; return 1; } return 0; } static int keycode_and_modifer_us_for_character(uint8_t character, uint8_t * keycode, uint8_t * modifier){ int found; found = lookup_keycode(character, keytable_scan_codes, sizeof(keytable_scan_codes), keycode); if (found) { *modifier = 0; // none return 1; } found = lookup_keycode(character, keytable_scan_codes_shift, sizeof(keytable_scan_codes_shift), keycode); if (found) { *modifier = 2; // shift return 1; } return 0; } // HID Report sending static void send_report(int modifier, int keycode){ uint8_t report[] = { modifier, 0, keycode, 0, 0, 0, 0, 0}; switch (protocol_mode){ case 0: hids_device_send_boot_keyboard_input_report(con_handle, report, sizeof(report)); break; case 1: hids_device_send_input_report(con_handle, report, sizeof(report)); break; default: break; } } static enum { W4_INPUT, W4_CAN_SEND_FROM_BUFFER, W4_CAN_SEND_KEY_UP, } state; static void typing_can_send_now(void){ switch (state){ case W4_CAN_SEND_FROM_BUFFER: while (1){ uint8_t c; uint32_t num_bytes_read; btstack_ring_buffer_read(&key_input_buffer, &c, 1, &num_bytes_read); if (num_bytes_read == 0){ state = W4_INPUT; break; } uint8_t modifier; uint8_t keycode; int found = keycode_and_modifer_us_for_character(c, &keycode, &modifier); if (!found) continue; printf("sending: %c\n", c); send_report(modifier, keycode); state = W4_CAN_SEND_KEY_UP; hids_device_request_can_send_now_event(con_handle); break; } break; case W4_CAN_SEND_KEY_UP: send_report(0, 0); if (btstack_ring_buffer_bytes_available(&key_input_buffer)){ state = W4_CAN_SEND_FROM_BUFFER; hids_device_request_can_send_now_event(con_handle); } else { state = W4_INPUT; } break; default: break; } } void send_hid_spec_code(uint8_t modifier, uint8_t keycode) { send_report(modifier, keycode); state = W4_CAN_SEND_KEY_UP; hids_device_request_can_send_now_event(con_handle); } void key_input(char character){ uint8_t c = character; btstack_ring_buffer_write(&key_input_buffer, &c, 1); // start sending if (state == W4_INPUT && con_handle != HCI_CON_HANDLE_INVALID){ state = W4_CAN_SEND_FROM_BUFFER; hids_device_request_can_send_now_event(con_handle); } } static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(channel); UNUSED(size); if (packet_type != HCI_EVENT_PACKET) return; switch (hci_event_packet_get_type(packet)) { case HCI_EVENT_DISCONNECTION_COMPLETE: con_handle = HCI_CON_HANDLE_INVALID; printf("Disconnected\n"); break; case SM_EVENT_JUST_WORKS_REQUEST: printf("Just Works requested\n"); sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); break; case SM_EVENT_NUMERIC_COMPARISON_REQUEST: printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet)); break; case SM_EVENT_PASSKEY_DISPLAY_NUMBER: printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); break; case HCI_EVENT_HIDS_META: switch (hci_event_hids_meta_get_subevent_code(packet)){ case HIDS_SUBEVENT_INPUT_REPORT_ENABLE: con_handle = hids_subevent_input_report_enable_get_con_handle(packet); printf("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet)); break; case HIDS_SUBEVENT_BOOT_KEYBOARD_INPUT_REPORT_ENABLE: con_handle = hids_subevent_boot_keyboard_input_report_enable_get_con_handle(packet); printf("Boot Keyboard Characteristic Subscribed %u\n", hids_subevent_boot_keyboard_input_report_enable_get_enable(packet)); break; case HIDS_SUBEVENT_PROTOCOL_MODE: protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet); printf("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot"); break; case HIDS_SUBEVENT_CAN_SEND_NOW: typing_can_send_now(); break; default: break; } break; default: break; } } void hog_keyboard_init() { //1. initialize ring buffer for key input btstack_ring_buffer_init(&key_input_buffer, key_input_storage, sizeof(key_input_storage)); //2. l2cap initialize l2cap_init(); //3. setup SM: Display only sm_init(); sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); sm_set_authentication_requirements(SM_AUTHREQ_SECURE_CONNECTION | SM_AUTHREQ_BONDING); //4. setup ATT server att_server_init(profile_data, NULL, NULL); //5. setup battery service battery_service_server_init(battery); //6. setup device information service device_information_service_server_init(); //7. setup HID Device service hids_device_init(0, hid_descriptor_keyboard_boot_mode, sizeof(hid_descriptor_keyboard_boot_mode)); //8. setup advertisements uint16_t adv_int_min = 0x0030; uint16_t adv_int_max = 0x0030; uint8_t adv_type = 0; bd_addr_t null_addr; memset(null_addr, 0, 6); gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); gap_advertisements_enable(1); //9. register for HCI events hci_event_callback_registration.callback = &packet_handler; hci_add_event_handler(&hci_event_callback_registration); //10. register for SM events sm_event_callback_registration.callback = &packet_handler; sm_add_event_handler(&sm_event_callback_registration); //11. register for HIDS hids_device_register_packet_handler(packet_handler); hci_power_control(HCI_POWER_ON); } int main() { stdio_init_all(); if (cyw43_arch_init()) { printf("cyw43_arch_init error\n"); return 0; } //0. initialize keyboard & lvgl TFT pico_lvgl_tft_init(TFT_PIO, TFT_SM, TFT_SDI_GPIO, TFT_CSX_DCX_SCK_GPIO); pico_lvgl_display_init(5); pico_lvgl_xpt2046_init(); //pico_lvgl_encoder_init(false); lvgl_keyboard_init(); hog_keyboard_init(); uint8_t c; while(1) { lv_timer_handler(); sleep_ms(1); if (con_handle == HCI_CON_HANDLE_INVALID) continue; } return 0; }
pico_lvgl_hog_keyboard.gatt
PRIMARY_SERVICE, GAP_SERVICE CHARACTERISTIC, GAP_DEVICE_NAME, READ, "PicoW BLE HID Keyboard" CHARACTERISTIC, GATT_DATABASE_HASH, READ, // add Battery Service #import <battery_service.gatt> // add Device ID Service #import <device_information_service.gatt> // add HID Service #import <hids.gatt>
shortcuts.c
#include "stdio.h" #include "pico/stdlib.h" #include "pico_lvgl.h" #define KEY_F1 0x3a // Keyboard F1 #define KEY_F2 0x3b // Keyboard F2 #define KEY_F3 0x3c // Keyboard F3 #define KEY_F4 0x3d // Keyboard F4 #define KEY_F5 0x3e // Keyboard F5 #define KEY_A 0x04 // Keyboard a and A #define KEY_B 0x05 // Keyboard b and B #define KEY_C 0x06 // Keyboard c and C #define KEY_D 0x07 // Keyboard d and D #define KEY_E 0x08 // Keyboard e and E #define KEY_MOD_LCTRL 0x01 #define KEY_MOD_LSHIFT 0x02 #define KEY_MOD_LALT 0x04 #define KEY_MOD_LMETA 0x08 #define KEY_MOD_RCTRL 0x10 #define KEY_MOD_RSHIFT 0x20 #define KEY_MOD_RALT 0x40 #define KEY_MOD_RMETA 0x80 #define KEY_ESC 0x29 // Keyboard ESCAPE #define KEY_BACKSPACE 0x2a // Keyboard DELETE (Backspace) #define KEY_TAB 0x2b // Keyboard Tab #define KEY_SPACE 0x2c // Keyboard Spacebar void send_hid_spec_code(uint8_t modifier, uint8_t keycode); static const char* shortcuts_map[] = { LV_SYMBOL_HOME "Home", "<Back", LV_SYMBOL_OK "Select", LV_SYMBOL_PREV "Prev",LV_SYMBOL_NEXT "Next", "\n", "ESC", "Tab", LV_SYMBOL_CLOSE "Close", "Ctrl+SP","" }; static const uint16_t KB_BTN = LV_KEYBOARD_CTRL_BTN_FLAGS; static const uint16_t KB_HIDDEN = LV_BTNMATRIX_CTRL_HIDDEN; static const uint16_t shortcuts_ctl_map[] = { KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN, KB_BTN,KB_BTN,KB_BTN,KB_BTN|2 }; static void shortcuts_btnmatrix_cb(lv_event_t* e) { lv_event_code_t code = lv_event_get_code(e); lv_obj_t* obj = lv_event_get_user_data(e); uint16_t select_btn; if (code == LV_EVENT_CLICKED) { select_btn = lv_btnmatrix_get_selected_btn(obj); switch(select_btn) { case 0: send_hid_spec_code(0, KEY_F1); break; case 1: send_hid_spec_code(0, KEY_F2); break; case 2: send_hid_spec_code(0, KEY_F3); break; case 3: send_hid_spec_code(0, KEY_F4); break; case 4: send_hid_spec_code(0, KEY_F5); break; case 5: send_hid_spec_code(0, KEY_ESC); break; case 6: send_hid_spec_code(0, KEY_TAB); break; case 7: send_hid_spec_code(KEY_MOD_LALT, KEY_F4); break; case 8: send_hid_spec_code(KEY_MOD_LCTRL, KEY_SPACE); break; } } } void shortcuts_init(lv_obj_t* parent) { lv_obj_t *shortcuts = lv_btnmatrix_create(parent); lv_btnmatrix_set_map(shortcuts, shortcuts_map); lv_btnmatrix_set_btn_ctrl_all(shortcuts, KB_BTN); lv_obj_set_size(shortcuts, lv_pct(100), lv_pct(100)); lv_obj_set_style_pad_all(shortcuts, 0,0); lv_obj_add_event_cb(shortcuts, shortcuts_btnmatrix_cb, LV_EVENT_ALL, shortcuts); }
usb_hid_scan_code.h
#ifndef __USB_HID_SCAN_CODE_H__ #define __USB_HID_SCAN_CODE_H__ #include "stdio.h" #include "pico/stdlib.h" /** * Modifier masks - used for the first byte in the HID report. * NOTE: The second byte in the report is reserved, 0x00 */ #define KEY_MOD_LCTRL 0x01 #define KEY_MOD_LSHIFT 0x02 #define KEY_MOD_LALT 0x04 #define KEY_MOD_LMETA 0x08 #define KEY_MOD_RCTRL 0x10 #define KEY_MOD_RSHIFT 0x20 #define KEY_MOD_RALT 0x40 #define KEY_MOD_RMETA 0x80 #define KEY_NONE 0x00 // No key pressed #define KEY_ERR_OVF 0x01 // Keyboard Error Roll Over - used for all slots if too many keys are pressed ("Phantom key") // 0x02 // Keyboard POST Fail // 0x03 // Keyboard Error Undefined #define SPECIAL_KEY 0xff #define CHAR_RETURN '\n' #define CHAR_ESCAPE 27 #define CHAR_TAB '\t' #define CHAR_BACKSPACE 0x7f #define KEY_DELETE 0x4c // Keyboard Delete Forward #define KEY_RIGHT 0x4f // Keyboard Right Arrow #define KEY_LEFT 0x50 // Keyboard Left Arrow #define KEY_DOWN 0x51 // Keyboard Down Arrow #define KEY_UP 0x52 // Keyboard Up Arrow // special keys #define KEY_ESC 0x29 // Keyboard ESCAPE #define KEY_F1 0x3a // Keyboard F1 #define KEY_F2 0x3b // Keyboard F2 #define KEY_F3 0x3c // Keyboard F3 #define KEY_F4 0x3d // Keyboard F4 #define KEY_F5 0x3e // Keyboard F5 #define KEY_F6 0x3f // Keyboard F6 #define KEY_F7 0x40 // Keyboard F7 #define KEY_F8 0x41 // Keyboard F8 #define KEY_F9 0x42 // Keyboard F9 #define KEY_F10 0x43 // Keyboard F10 #define KEY_F11 0x44 // Keyboard F11 #define KEY_F12 0x45 // Keyboard F12 #define KEY_GRAVE 0x35 // Keyboard ` and ~ #define KEY_COMMA 0x36 // Keyboard , and < #define KEY_DOT 0x37 // Keyboard . and > static const uint8_t keytable_scan_codes[] = { SPECIAL_KEY,SPECIAL_KEY,SPECIAL_KEY,SPECIAL_KEY,'a','b','c','d','e','f','g','h','i','j','k','l', //0x00~0x0F 'm','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2', //0x10~0x1F '3','4','5','6','7','8','9','0',CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB,' ','-','=','[', //0x20~0x2F ']','\\',SPECIAL_KEY,';','\'','`',',','.','/', }; static const uint8_t keytable_scan_codes_shift[] = { SPECIAL_KEY,SPECIAL_KEY,SPECIAL_KEY,SPECIAL_KEY,'A','B','C','D','E','F','G','H','I','J','K','L', //0x00~0x0F 'M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','!','@', //0x10~0x1F '#','$','%','^','&','*','(',')',CHAR_RETURN, CHAR_ESCAPE, CHAR_BACKSPACE, CHAR_TAB,' ','_','+','{', //0x20~0x2F '}','|',SPECIAL_KEY,':','"','~','<','>','?', }; static const char ILLEGAL_PHONEETIC[] = "000"; static const char* keyboard_phonetic[] = { ILLEGAL_PHONEETIC,ILLEGAL_PHONEETIC,ILLEGAL_PHONEETIC,ILLEGAL_PHONEETIC, "ㄇ","ㄖ","ㄏ","ㄎ","ㄍ","ㄑ","ㄕ","ㄘ","ㄛ","ㄨ","ㄜ","ㄠ", //0x00~0x0F "ㄩ","ㄙ","ㄟ","ㄣ","ㄆ","ㄐ","ㄋ","ㄔ","ㄧ","ㄒ","ㄊ","ㄌ","ㄗ","ㄈ","ㄅ","ㄉ", //0x10~0x1F "ˇ","ˋ","ㄓ","ˊ","˙","ㄚ","ㄞ","ㄢ","換行", ILLEGAL_PHONEETIC, "←", ILLEGAL_PHONEETIC, " ","ㄦ","+","{", //0x20~0x2F "}","|",ILLEGAL_PHONEETIC,"ㄤ","\"","~","ㄝ","ㄡ","ㄥ" //0x30~0x38 }; #define REPORT_ID 0x01 // from USB HID Specification 1.1, Appendix B.1 static const uint8_t hid_descriptor_keyboard_boot_mode[] = { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x06, // Usage (Keyboard) 0xa1, 0x01, // Collection (Application) 0x85, 0x01, // Report ID 1 // Modifier byte 0x75, 0x01, // Report Size (1) 0x95, 0x08, // Report Count (8) 0x05, 0x07, // Usage Page (Key codes) 0x19, 0xe0, // Usage Minimum (Keyboard LeftControl) 0x29, 0xe7, // Usage Maxium (Keyboard Right GUI) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x81, 0x02, // Input (Data, Variable, Absolute) // Reserved byte 0x75, 0x01, // Report Size (1) 0x95, 0x08, // Report Count (8) 0x81, 0x03, // Input (Constant, Variable, Absolute) // LED report + padding 0x95, 0x05, // Report Count (5) 0x75, 0x01, // Report Size (1) 0x05, 0x08, // Usage Page (LEDs) 0x19, 0x01, // Usage Minimum (Num Lock) 0x29, 0x05, // Usage Maxium (Kana) 0x91, 0x02, // Output (Data, Variable, Absolute) 0x95, 0x01, // Report Count (1) 0x75, 0x03, // Report Size (3) 0x91, 0x03, // Output (Constant, Variable, Absolute) // Keycodes 0x95, 0x06, // Report Count (6) 0x75, 0x08, // Report Size (8) 0x15, 0x00, // Logical Minimum (0) 0x25, 0xff, // Logical Maximum (1) 0x05, 0x07, // Usage Page (Key codes) 0x19, 0x00, // Usage Minimum (Reserved (no event indicated)) 0x29, 0xff, // Usage Maxium (Reserved) 0x81, 0x00, // Input (Data, Array) 0xc0, // End collection }; #endif
zh_TW.kb.c
#include "pico/stdlib.h" #include "stdio.h" #include "pico_lvgl.h" #include "jf_font.h" extern uint8_t lvgl_kb_left[4]; extern uint8_t lvgl_kb_right[4]; const char* font_map[] = { "1","2","3","4","5","6","7","8","9","0","選字","\n", "ㄅ","ㄉ","ˇ", "ˋ","ㄓ","ˊ", "˙","ㄚ","ㄞ","ㄢ","ㄦ","\n", "ㄆ","ㄊ","ㄍ","ㄐ","ㄔ","ㄗ","ㄧ","ㄛ","ㄟ","ㄣ","←","\n", "ㄇ","ㄋ","ㄎ","ㄑ","ㄕ","ㄘ","ㄨ","ㄜ","ㄠ","ㄤ","換行","\n", "ㄈ","ㄌ","ㄏ","ㄒ","ㄖ","ㄙ","ㄩ","ㄝ","ㄡ","ㄥ",",","\n", "英","數","〈"," ","〉","﹀","︿","。","" }; const uint16_t KB_BTN = LV_KEYBOARD_CTRL_BTN_FLAGS|1; const uint16_t KB_HIDDEN = LV_BTNMATRIX_CTRL_HIDDEN | 1; const uint16_t space_key_ctl = LV_BTNMATRIX_CTRL_CHECKABLE | LV_BTNMATRIX_CTRL_NO_REPEAT|2; lv_btnmatrix_ctrl_t font_ctl_map[] = { KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_HIDDEN,KB_BTN, KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN, KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,LV_BTNMATRIX_CTRL_CHECKED|2, KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,LV_BTNMATRIX_CTRL_CHECKED|2, KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN,KB_BTN, LV_BTNMATRIX_CTRL_CHECKED|2,LV_BTNMATRIX_CTRL_CHECKED|2,KB_BTN,KB_BTN|6,KB_BTN,KB_BTN,KB_BTN, KB_BTN }; void set_zh_TW_keyboard(lv_obj_t *kb) { lv_obj_set_style_text_font(kb, &jf_font, LV_PART_ITEMS); lv_keyboard_set_map(kb, LV_KEYBOARD_MODE_USER_1, font_map, font_ctl_map); lv_obj_set_style_text_font(kb, lv_font_default(), LV_PART_ITEMS); }
沒有留言:
張貼留言