在上篇將有線鍵盤透過Raspberry Pi Pico W轉換成無線藍芽鍵盤,
HID device, Convert a USB Cabled Keyboard to a Bluetooth Wireless Keyboard
本篇文章整合4x4 keypad pio程式,自訂一個藍芽鍵盤,如下圖。
- 4x4 keypad連接pico w pin 8 ~ pin 15,輸入如key pad上的內容。
- 左邊按鈕送出"F1" 功能鍵, key cod為0x3a。
- 右邊按鈕送出一串文字。
hid report包含modifier與scan code如下圖所示。
其他詳細文件說明,請參閱前一篇。
本實驗直接使用BTstack example程式hid_keyboard_demo.c。將363行註解。
另外製作送出keypad按鍵與特殊鍵功能。
成果影片
- Rebuild this project step by step:
- Generate the initial project structure:
Download pico_project.py from https://github.com/raspberrypi/pico-project-generator
$ pico_project.py --gui - Copy the 4x4 keypad library:
Copy (2)keypad.c (3)keypad.h (4)keypad.pio (5)CMakeLists.txt files to pico_keypad directory. - Copy the (1)BT_custom_keypad.c to project directory
- Copy (6)btstack_config.h to the project root directory
- Copy hid_keyboard_demo.c in pico-sdk/lib/btstack/example folder to the project root directory, and comment out line 363[//demo_text_timer_hanlder(NULL)].
- Add the required libraries into CMakeLists.txt in root directory.
- 程式碼:
(1) main code:BT_custom_keypad.c
#include <stdio.h> #include "pico/stdlib.h" #include "pico/cyw43_arch.h" #include "hid_keyboard_demo.c" #include "keypad.h" #define F1_BUTTON 16 #define TYPE_STRING_BUTTON 17 void send_hid_text(uint8_t* text) { btstack_ring_buffer_write(&send_buffer, text, strlen(text)); send_next(NULL); busy_wait_ms(100); } void send_hid_char(uint8_t c) { btstack_ring_buffer_write(&send_buffer, (uint8_t*)&c, 1); send_next(NULL); busy_wait_ms(100); } void send_hid_spec_code(uint8_t m, uint8_t c) { send_modifier = m; send_keycode = c; hid_device_request_can_send_now_event(hid_cid); } void key_button_callback(uint gpio, uint32_t events) { gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL, false); gpio_acknowledge_irq(gpio, events); if (gpio == F1_BUTTON ) { busy_wait_ms(100); send_hid_spec_code(0, 0x3a); //F1 key gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL, true); } if (gpio == TYPE_STRING_BUTTON) { busy_wait_ms(100); send_hid_text("messages...\n"); gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL, true); } } int main() { stdio_init_all(); if (cyw43_arch_init()) { printf("cyw43 arch init error\n"); return 0; } gpio_init(F1_BUTTON); gpio_init(TYPE_STRING_BUTTON); gpio_pull_up(F1_BUTTON); gpio_pull_up(TYPE_STRING_BUTTON); gpio_set_irq_enabled_with_callback(F1_BUTTON, GPIO_IRQ_EDGE_FALL, true, key_button_callback); gpio_set_irq_enabled_with_callback(TYPE_STRING_BUTTON, GPIO_IRQ_EDGE_FALL, true, key_button_callback); keypad_init(); btstack_main(0, NULL); gap_set_local_name("PicoW HID Keypad"); uint8_t c; while(1) { c=get_new_keypad_value(); if (c) { send_hid_char(c); } else { sleep_ms(10); } tight_loop_contents(); } return 0; }
(2) keypad.c
#include "keypad.pio.h" #include "keypad.h" #include "hardware/clocks.h" #include "stdio.h" #include "pico/stdlib.h" static uint8_t key_value=0; static const PIO keypad_pio=pio1; static const uint keypad_sm=0; static const uint8_t keys[4][4]={ {'1','2','3','A'}, {'4','5','6','B'}, {'7','8','9','C'}, {'*','0','#','D'} }; static void keypad_handle() { if (pio_interrupt_get(keypad_pio, 0)) { key_value=0; pio_interrupt_clear(keypad_pio, 0); uint32_t x, y; y=pio_sm_get_blocking(keypad_pio, keypad_sm); x=pio_sm_get_blocking(keypad_pio, keypad_sm); for(uint8_t i = 0 ; i < 4; i++){ if ((x >> i)==1) {x=i;break;} } for(uint8_t j = 0 ; j < 4; j++){ if ((y >> j)==1) {y=j;break;} } key_value = keys[x][y]; } } static void keypad_pio_init(PIO pio, uint sm, uint set_base, uint in_base, uint freq) { uint offset=0; pio_sm_config c; offset = pio_add_program(pio, &keypad_program); c = keypad_program_get_default_config(offset); for (int i=0; i < 4; i++) pio_gpio_init(pio, in_base+i); for (int i=0; i < 4; i++) pio_gpio_init(pio, set_base+i); pio_sm_set_consecutive_pindirs(pio, sm, in_base, 4, false); pio_sm_set_consecutive_pindirs(pio, sm, set_base, 4, true); sm_config_set_in_pins(&c, in_base); sm_config_set_set_pins(&c, set_base, 4); sm_config_set_in_shift(&c, false, false, 32); float div = clock_get_hz(clk_sys)/freq; sm_config_set_clkdiv(&c, div); uint pio_irq = pio_get_index(pio)? PIO1_IRQ_0:PIO0_IRQ_0; pio_set_irq0_source_enabled(pio, pis_interrupt0, true); irq_add_shared_handler(pio_irq, keypad_handle, PICO_SHARED_IRQ_HANDLER_DEFAULT_ORDER_PRIORITY); irq_set_enabled(pio_irq, true); pio_sm_init(pio, sm, offset, &c); pio_sm_set_enabled(pio, sm, true); } uint8_t get_new_keypad_value() { uint8_t ret_vale = key_value; key_value=0; return ret_vale; } void keypad_init() { keypad_pio_init(keypad_pio, keypad_sm, 8, 12, 2000); }
(3) keypad.h
#ifndef __KEYPAD_H #define __KEYPAD_H void keypad_init(); uint8_t get_new_keypad_value(); #endif
(4) keypad.pio
.program keypad .wrap_target set_row_1: set pins, 1 [31] ;set row_1 High and wait for button bounce set x,0x1 in pins,4 mov y, isr jmp !y set_row_2 jmp rx_fifo set_row_2: set pins, 2 [31] set x,0x2 in pins,4 mov y, isr jmp !y set_row_3 jmp rx_fifo set_row_3: set pins, 4 [31] set x,0x4 in pins,4 mov y, isr jmp !y set_row_4 jmp rx_fifo set_row_4: set pins, 8 [31] set x,0x8 in pins,4 mov y, isr jmp !y set_row_1 rx_fifo: push ;push y col in x,4 [2] push ;and then x row irq 0 rel wait 0 pin 0 ; check whether key is released wait 0 pin 1 wait 0 pin 2 wait 0 pin 3 .wrap
(5) CMakeLists.txt(keypad)
add_library(pico_keypad INTERFACE) pico_generate_pio_header(pico_keypad ${CMAKE_CURRENT_LIST_DIR}/keypad.pio) target_sources(pico_keypad INTERFACE ${CMAKE_CURRENT_LIST_DIR}/keypad.c ) target_include_directories(pico_keypad INTERFACE ${CMAKE_CURRENT_LIST_DIR} ) target_link_libraries(pico_keypad INTERFACE hardware_pio )
#ifndef _PICO_BTSTACK_BTSTACK_CONFIG_H #define _PICO_BTSTACK_BTSTACK_CONFIG_H // BTstack features that can be enabled #ifdef ENABLE_BLE #define ENABLE_LE_PERIPHERAL #define ENABLE_LE_CENTRAL #define ENABLE_L2CAP_LE_CREDIT_BASED_FLOW_CONTROL_MODE #endif #ifdef ENABLE_MESH // Mesh Config #define ENABLE_MESH_ADV_BEARER #define ENABLE_MESH_GATT_BEARER #define ENABLE_MESH_PB_ADV #define ENABLE_MESH_PB_GATT #define ENABLE_MESH_PROXY_SERVER #define ENABLE_MESH_RELAY #define ENABLE_MESH_PROVISIONER #define MAX_NR_MESH_SUBNETS 2 #define MAX_NR_MESH_TRANSPORT_KEYS 16 #define MAX_NR_MESH_VIRTUAL_ADDRESSES 16 // allow for one NetKey update #define MAX_NR_MESH_NETWORK_KEYS (MAX_NR_MESH_SUBNETS+1) #endif #define ENABLE_LOG_INFO #define ENABLE_LOG_ERROR #define ENABLE_PRINTF_HEXDUMP #define ENABLE_SCO_OVER_HCI // BTstack configuration. buffers, sizes, ... #define HCI_OUTGOING_PRE_BUFFER_SIZE 4 #define HCI_ACL_PAYLOAD_SIZE (1691 + 4) #define HCI_ACL_CHUNK_SIZE_ALIGNMENT 4 #define MAX_NR_AVDTP_CONNECTIONS 1 #define MAX_NR_AVDTP_STREAM_ENDPOINTS 1 #define MAX_NR_AVRCP_CONNECTIONS 2 #define MAX_NR_BNEP_CHANNELS 1 #define MAX_NR_BNEP_SERVICES 1 #define MAX_NR_BTSTACK_LINK_KEY_DB_MEMORY_ENTRIES 2 #define MAX_NR_GATT_CLIENTS 1 #define MAX_NR_HCI_CONNECTIONS 2 #define MAX_NR_HID_HOST_CONNECTIONS 1 #define MAX_NR_HIDS_CLIENTS 1 #define MAX_NR_HFP_CONNECTIONS 1 #define MAX_NR_L2CAP_CHANNELS 4 #define MAX_NR_L2CAP_SERVICES 3 #define MAX_NR_RFCOMM_CHANNELS 1 #define MAX_NR_RFCOMM_MULTIPLEXERS 1 #define MAX_NR_RFCOMM_SERVICES 1 #define MAX_NR_SERVICE_RECORD_ITEMS 4 #define MAX_NR_SM_LOOKUP_ENTRIES 3 #define MAX_NR_WHITELIST_ENTRIES 16 #define MAX_NR_LE_DEVICE_DB_ENTRIES 16 // Limit number of ACL/SCO Buffer to use by stack to avoid cyw43 shared bus overrun #define MAX_NR_CONTROLLER_ACL_BUFFERS 3 #define MAX_NR_CONTROLLER_SCO_PACKETS 3 // Enable and configure HCI Controller to Host Flow Control to avoid cyw43 shared bus overrun #define ENABLE_HCI_CONTROLLER_TO_HOST_FLOW_CONTROL #define HCI_HOST_ACL_PACKET_LEN 1024 #define HCI_HOST_ACL_PACKET_NUM 3 #define HCI_HOST_SCO_PACKET_LEN 120 #define HCI_HOST_SCO_PACKET_NUM 3 // Link Key DB and LE Device DB using TLV on top of Flash Sector interface #define NVM_NUM_DEVICE_DB_ENTRIES 16 #define NVM_NUM_LINK_KEYS 16 // We don't give btstack a malloc, so use a fixed-size ATT DB. #define MAX_ATT_DB_SIZE 512 // BTstack HAL configuration #define HAVE_EMBEDDED_TIME_MS // map btstack_assert onto Pico SDK assert() #define HAVE_ASSERT // Some USB dongles take longer to respond to HCI reset (e.g. BCM20702A). #define HCI_RESET_RESEND_TIMEOUT_MS 1000 #define ENABLE_SOFTWARE_AES128 #define ENABLE_MICRO_ECC_FOR_LE_SECURE_CONNECTIONS //#define HAVE_BTSTACK_STDIN // To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets //#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100 #ifdef ENABLE_CLASSIC #define ENABLE_L2CAP_ENHANCED_RETRANSMISSION_MODE #endif #define HAVE_MALLOC #endif // _PICO_BTSTACK_BTSTACK_CONFIG_H
沒有留言:
張貼留言