在上篇將有線鍵盤透過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
沒有留言:
張貼留言