本篇文章介紹HID over Gatt(HOG) bluetooth mouse. 使用Btstack library. 使用者介面為觸控螢幕並使用LVGL graphic library。

| #include "stdio.h" |
| #include "stdlib.h" |
| #include "pico/stdlib.h" |
| #include "hardware/clocks.h" |
| #include "string.h" |
| |
| #include "registers.h" |
| #include "pico_tft.pio.h" |
| #include "pico_tft.h" |
| #include "hardware/dma.h" |
| |
| #define MAX_BYTE_TRANS (TFT_WIDTH*TFT_HEIGHT*2) |
| |
| #define PICO_TFT_SERIAL |
| #define PICO_TFT_DMA |
| |
| |
| static uint8_t TFT_MADCTL_PORTRAIT = 0b01001000; |
| static uint8_t TFT_MADCTL_LANDSCAPE = 0b00101000; |
| static uint8_t TFT_MADCTL_PORTRAIT_MIRROR = 0b10001000; |
| static uint8_t TFT_MADCTL_LANDSCAPE_MIRROR = 0b11101000; |
| static uint16_t tft_width; |
| static uint16_t tft_height; |
| static uint8_t tft_orientation; |
| |
| PIO tft_pio = pio1; |
| uint tft_sm=0; |
| uint in_out_base_pin=4; |
| uint set_base_pin=12; |
| uint sideset_base_pin=16; |
| uint s_in_out_base_pin=19; |
| |
| int tft_dma_channel; |
| |
| void tft_cmd(uint32_t cmd, uint32_t count, uint8_t *param) |
| { |
| pio_sm_restart(tft_pio, tft_sm); |
| #ifdef PICO_TFT_SERIAL |
| pio_sm_put_blocking(tft_pio, tft_sm, cmd << 24); |
| #endif |
| #ifdef PICO_TFT_PARALLEL |
| pio_sm_put_blocking(tft_pio, tft_sm, cmd); |
| #endif |
| |
| pio_sm_put_blocking(tft_pio, tft_sm, count); |
| for (int i = 0; i < count; i++) |
| { |
| #ifdef PICO_TFT_SERIAL |
| pio_sm_put_blocking(tft_pio, tft_sm, param[i]<<24); |
| #endif |
| #ifdef PICO_TFT_PARALLEL |
| pio_sm_put_blocking(tft_pio, tft_sm, param[i]); |
| #endif |
| } |
| } |
| |
| void tft_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param) |
| { |
| #ifdef PICO_TFT_SERIAL |
| tft_cmd(cmd, count, param); |
| return; |
| #endif |
| pio_sm_restart(tft_pio, tft_sm); |
| pio_sm_put_blocking(tft_pio, tft_sm, cmd); |
| pio_sm_put_blocking(tft_pio, tft_sm, count); |
| dma_channel_set_trans_count(tft_dma_channel, count >> DMA_SIZE_8, false); |
| dma_channel_set_read_addr(tft_dma_channel, param, false); |
| dma_channel_start(tft_dma_channel); |
| dma_channel_wait_for_finish_blocking(tft_dma_channel); |
| |
| } |
| |
| void tft_pio_cmd_init(PIO pio, uint sm, uint in_out_base, uint set_sideset, uint32_t freq) { |
| uint offset=0; |
| pio_sm_config c; |
| #ifdef PICO_TFT_PARALLEL |
| offset = pio_add_program(pio, &tft_pio_parallel_program); |
| c = tft_pio_parallel_program_get_default_config(offset); |
| for (int i=0; i < 8; i++) pio_gpio_init(pio, in_out_base+i); |
| for (int i=0; i < 4; i++) pio_gpio_init(pio, set_sideset+i); |
| pio_sm_set_consecutive_pindirs(pio, sm, in_out_base, 8, true); |
| pio_sm_set_consecutive_pindirs(pio, sm, set_base, 4, true); |
| sm_config_set_in_pins(&c, in_out_base); |
| sm_config_set_out_pins(&c, in_out_base, 8); |
| sm_config_set_set_pins(&c, set_sideset, 4); |
| sm_config_set_out_shift(&c, true, false, 8); |
| sm_config_set_in_shift(&c, false, false, 8); |
| #endif |
| #ifdef PICO_TFT_SERIAL |
| offset = pio_add_program(pio, &tft_pio_serial_program); |
| c = tft_pio_serial_program_get_default_config(offset); |
| pio_gpio_init(pio, in_out_base); |
| for (int i=0; i < 3; i++) pio_gpio_init(pio, set_sideset+i); |
| pio_sm_set_consecutive_pindirs(pio, sm, in_out_base, 1, true); |
| pio_sm_set_consecutive_pindirs(pio, sm, set_sideset, 3, true); |
| sm_config_set_in_pins(&c, in_out_base); |
| sm_config_set_out_pins(&c, in_out_base, 1); |
| sm_config_set_sideset_pins(&c, set_sideset); |
| sm_config_set_out_shift(&c, false, false, 8); |
| sm_config_set_in_shift(&c, true, false, 8); |
| #endif |
| |
| |
| |
| float div = clock_get_hz(clk_sys)/freq; |
| sm_config_set_clkdiv(&c, div); |
| |
| |
| #ifdef PICO_TFT_DMA |
| |
| tft_dma_channel = dma_claim_unused_channel(true); |
| dma_channel_config dc = dma_channel_get_default_config(tft_dma_channel); |
| channel_config_set_write_increment(&dc, false); |
| channel_config_set_read_increment(&dc, true); |
| channel_config_set_dreq(&dc, pio_get_dreq(pio, sm, true)); |
| channel_config_set_transfer_data_size(&dc, DMA_SIZE_8); |
| |
| dma_channel_configure(tft_dma_channel, &dc, (void*) (PIO1_BASE+PIO_TXF0_OFFSET), |
| NULL, MAX_BYTE_TRANS>> DMA_SIZE_8, false); |
| |
| #endif |
| pio_sm_init(pio, sm, offset, &c); |
| pio_sm_set_enabled(pio, sm, true); |
| } |
| |
| |
| uint16_t tft_color_565RGB(uint8_t R, uint8_t G, uint8_t B) { |
| uint16_t c; |
| c = (((uint16_t)R)>>3)<<11 | (((uint16_t)G)>>2) << 5 | ((uint16_t)B)>>3; |
| return c; |
| } |
| void tft_memory_write_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) |
| { |
| uint8_t addr[4]; |
| addr[0]=(uint8_t)(x1 >> 8); |
| addr[1]= (uint8_t)(x1 & 0xff); |
| addr[2]= (uint8_t)(x2 >> 8); |
| addr[3]= (uint8_t)(x2 & 0xff); |
| tft_cmd(TFT_COLADDRSET, 4, addr); |
| |
| addr[0]=(uint8_t)(y1 >> 8); |
| addr[1]= (uint8_t)(y1 & 0xff); |
| addr[2]= (uint8_t)(y2 >> 8); |
| addr[3]= (uint8_t)(y2 & 0xff); |
| tft_cmd(TFT_PAGEADDRSET, 4, addr ); |
| |
| tft_cmd(TFT_MEMORYWRITE, 0, NULL); |
| } |
| |
| void tft_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) |
| { |
| uint8_t addr[4]; |
| addr[0]=(uint8_t)(x1 >> 8); |
| addr[1]= (uint8_t)(x1 & 0xff); |
| addr[2]= (uint8_t)(x2 >> 8); |
| addr[3]= (uint8_t)(x2 & 0xff); |
| tft_cmd(TFT_COLADDRSET, 4, addr); |
| |
| addr[0]=(uint8_t)(y1 >> 8); |
| addr[1]= (uint8_t)(y1 & 0xff); |
| addr[2]= (uint8_t)(y2 >> 8); |
| addr[3]= (uint8_t)(y2 & 0xff); |
| tft_cmd(TFT_PAGEADDRSET, 4, addr ); |
| } |
| |
| |
| void tft_draw_pixel(uint16_t x, uint16_t y, uint16_t color) |
| { |
| if ( x < 0 || x > TFT_WIDTH-1 || y < 0 || y > TFT_HEIGHT-1) { |
| printf("over range,x,y\n"); |
| return; |
| } |
| tft_set_address_window(x,y,x,y); |
| tft_cmd(TFT_MEMORYWRITE, 2, (uint8_t[2]){(uint8_t)(color >> 8), (uint8_t)color}); |
| } |
| |
| uint16_t tft_get_width() { |
| return tft_width; |
| } |
| |
| uint16_t tft_get_height() { |
| return tft_height; |
| } |
| |
| uint8_t tft_get_orientation() { |
| return tft_orientation; |
| } |
| void tft_set_orientation(uint8_t orientation) { |
| tft_orientation = orientation; |
| switch (orientation) { |
| case TFT_ORIENTATION_PORTRAIT: |
| tft_cmd(TFT_MADCTL, 1, (uint8_t[1]){TFT_MADCTL_PORTRAIT}); |
| tft_width = TFT_WIDTH; |
| tft_height = TFT_HEIGHT; |
| break; |
| case TFT_ORIENTATION_PORTRAIT_MIRROR: |
| tft_cmd(TFT_MADCTL, 1, (uint8_t[1]){TFT_MADCTL_PORTRAIT_MIRROR}); |
| tft_width = TFT_WIDTH; |
| tft_height = TFT_HEIGHT; |
| break; |
| case TFT_ORIENTATION_LANDSCAPE: |
| tft_cmd(TFT_MADCTL, 1, (uint8_t[1]){TFT_MADCTL_LANDSCAPE}); |
| tft_width = TFT_HEIGHT; |
| tft_height = TFT_WIDTH; |
| break; |
| case TFT_ORIENTATION_LANDSCAPE_MIRROR: |
| tft_cmd(TFT_MADCTL, 1, (uint8_t[1]){TFT_MADCTL_LANDSCAPE_MIRROR}); |
| tft_width = TFT_HEIGHT; |
| tft_height = TFT_WIDTH; |
| break; |
| } |
| |
| } |
| void tft_init_config() { |
| tft_cmd(TFT_SOFTRESET, 1, NULL); |
| sleep_ms(120); |
| tft_cmd(TFT_SLEEPOUT, 0, NULL); |
| sleep_ms(120); |
| |
| tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0xC3}); |
| tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x96}); |
| |
| tft_set_orientation(TFT_ORIENTATION_PORTRAIT); |
| tft_cmd(TFT_PIXELFORMAT, 1, (uint8_t[1]){0x05}); |
| tft_cmd(TFT_DSIPLAY_INVER, 1, (uint8_t[1]){0x01}); |
| |
| tft_cmd(TFT_DISPLAYFUNC, 3, (uint8_t[3]){0x80, 0x02, 0x3B}); |
| tft_cmd(TFT_DISP_OUTPUT_CTRL_ADJUST, 8, (uint8_t[8]) |
| {0x40, |
| 0x8A, |
| 0x00, |
| 0x00, |
| 0x29, |
| 0x19, |
| 0xA5, |
| 0x33}); |
| |
| tft_cmd(TFT_POWERCONTROL2, 1, (uint8_t[1]){0x06}); |
| tft_cmd(TFT_POWERCONTROL3, 1, (uint8_t[1]){0xA7}); |
| tft_cmd(TFT_VCOMCONTROL1, 1, (uint8_t[1]){0x18}); |
| sleep_ms(120); |
| tft_cmd(TFT_PGAMCOR, 14, (uint8_t[14]){ 0xf0, 0x09, 0x0b, 0x06, 0x04, 0x15, 0x2f, 0x54, 0x42, 0x3c, 0x17, 0x14, 0x18, 0x1b}); |
| tft_cmd(TFT_NGAMCOR, 14, (uint8_t[14]){ 0xe0, 0x09, 0x0b, 0x06, 0x04, 0x03, 0x2b, 0x43, 0x42, 0x3b, 0x16, 0x14, 0x17, 0x1b}); |
| sleep_ms(120); |
| tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x3C}); |
| tft_cmd(TFT_COMMANDSET, 1, (uint8_t[1]){0x69}); |
| |
| tft_cmd(TFT_DISPLAYOFF, 0, NULL); |
| sleep_ms(120); |
| tft_cmd(TFT_DISPLAYON, 0, NULL); |
| sleep_ms(500); |
| |
| } |
| void tft_init(PIO pio, uint sm, uint din_base, uint csx_dcx_sck_side_base_pin) { |
| tft_pio = pio; |
| tft_sm = sm; |
| #ifdef PICO_TFT_PARALLEL |
| in_out_base_pin = din_base; |
| set_base_pin = csx_dcx_sck_side_base_pin; |
| tft_pio_cmd_init(tft_pio, tft_sm, in_out_base_pin, set_base_pin, 70000000); |
| #endif |
| #ifdef PICO_TFT_SERIAL |
| s_in_out_base_pin = din_base; |
| sideset_base_pin = csx_dcx_sck_side_base_pin; |
| tft_pio_cmd_init(tft_pio, tft_sm, s_in_out_base_pin, sideset_base_pin, 90000000); |
| #endif |
| tft_init_config(); |
| |
| } |
| |
| #ifndef _TFT_H_ |
| #define _TFT_H_ |
| |
| #define TFT_WIDTH 320 |
| #define TFT_HEIGHT 480 |
| |
| enum { |
| TFT_ORIENTATION_PORTRAIT=0, |
| TFT_ORIENTATION_LANDSCAPE, |
| TFT_ORIENTATION_PORTRAIT_MIRROR, |
| TFT_ORIENTATION_LANDSCAPE_MIRROR, |
| }; |
| |
| #include "pico/stdlib.h" |
| |
| #include "hardware/pio.h" |
| #include "pico_tft_lvgl.h" |
| |
| |
| void tft_init(PIO pio, uint sm, uint din_base, uint csx_dcx_sck_side_base_pin); |
| void tft_init_config(); |
| void tft_draw_pixel(uint16_t x, uint16_t y, uint16_t color); |
| void tft_set_address_window(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2); |
| void tft_cmd(uint32_t cmd, uint32_t count, uint8_t *param); |
| void tft_cmd_dma(uint32_t cmd, uint32_t count, uint8_t *param); |
| uint16_t tft_color_565RGB(uint8_t R, uint8_t G, uint8_t B); |
| void tft_lv_draw_bitmap(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t *bitmap); |
| void tft_pio_cmd_init(PIO pio, uint sm, uint out_base, uint set_base, uint32_t freq); |
| |
| uint16_t tft_get_width(); |
| uint16_t tft_get_height(); |
| uint8_t tft_get_orientation(); |
| void tft_set_orientation(uint8_t orientation); |
| |
| #endif |
| #include "stdio.h" |
| #include "stdlib.h" |
| #include "xpt2046.h" |
| #include "hardware/spi.h" |
| #include "hardware/gpio.h" |
| #include "pico/stdlib.h" |
| #include "pico_tft.h" |
| |
| uint32_t xpt2046_event=0; |
| uint8_t READ_X = 0xD0; |
| uint8_t READ_Y = 0x90; |
| #define XPT_WIDTH 320 |
| #define XPT_HEIGHT 480 |
| |
| bool xpt2046_getXY(uint16_t *x, uint16_t *y) { |
| |
| uint8_t temp[2]; |
| uint16_t raw_x, raw_y; |
| uint16_t est_raw_x, est_raw_y; |
| uint32_t avg_x = 0; |
| uint32_t avg_y = 0; |
| uint8_t nsamples = 0; |
| uint8_t SAMPLES=10; |
| |
| gpio_put(XPT2046_CS, false); |
| if(gpio_get(XPT2046_IRQ_GPIO)) return false; |
| busy_wait_ms(10); |
| |
| SAMPLES=20; |
| for(uint8_t i = 0; i < SAMPLES; i++, nsamples++) |
| { |
| if(gpio_get(XPT2046_IRQ_GPIO)) { |
| break; |
| } |
| spi_write_blocking(XPT2046_SPI, &READ_X, 1); |
| spi_read_blocking(XPT2046_SPI, 0x00, temp, 2); |
| raw_x = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1]; |
| |
| spi_write_blocking(XPT2046_SPI, &READ_Y, 1); |
| spi_read_blocking(XPT2046_SPI, 0x00, temp, 2); |
| raw_y = ((uint16_t)temp[0]) << 8 | (uint16_t)temp[1]; |
| |
| avg_x += raw_x; |
| avg_y += raw_y; |
| } |
| |
| if(nsamples < SAMPLES) |
| return false; |
| |
| gpio_put(XPT2046_CS, true); |
| raw_x = (avg_x / SAMPLES); |
| raw_y = (avg_y / SAMPLES); |
| |
| if(raw_x < XPT2046_MIN_RAW_X || raw_x > XPT2046_MAX_RAW_X) return false; |
| if(raw_y < XPT2046_MIN_RAW_Y || raw_y > XPT2046_MAX_RAW_Y) return false; |
| |
| uint16_t tx,ty; |
| tx = (raw_x - XPT2046_MIN_RAW_X) * XPT_WIDTH / (XPT2046_MAX_RAW_X - XPT2046_MIN_RAW_X); |
| ty = (raw_y - XPT2046_MIN_RAW_Y) * XPT_HEIGHT / (XPT2046_MAX_RAW_Y - XPT2046_MIN_RAW_Y); |
| |
| |
| uint8_t lot = tft_get_orientation(); |
| switch (lot) |
| { |
| case TFT_ORIENTATION_PORTRAIT: |
| *x=tx; |
| *y=TFT_HEIGHT-ty; |
| break; |
| case TFT_ORIENTATION_LANDSCAPE: |
| *x=TFT_HEIGHT-ty; |
| *y=TFT_WIDTH-tx; |
| break; |
| case TFT_ORIENTATION_PORTRAIT_MIRROR: |
| *x=TFT_WIDTH-tx; |
| *y=ty; |
| break; |
| case TFT_ORIENTATION_LANDSCAPE_MIRROR: |
| *x=ty; |
| *y=tx; |
| break; |
| } |
| return true; |
| } |
| |
| |
| bool xpt2046_TouchPressed() |
| { |
| return !gpio_get(XPT2046_IRQ_GPIO); |
| } |
| |
| void xpt2046_init() { |
| gpio_init(XPT2046_IRQ_GPIO); |
| gpio_init(XPT2046_MISO); |
| gpio_init(XPT2046_MOSI); |
| gpio_init(XPT2046_CLK); |
| gpio_init(XPT2046_CS); |
| gpio_set_dir(XPT2046_CS, GPIO_OUT); |
| gpio_set_dir(XPT2046_IRQ_GPIO, GPIO_OUT); |
| gpio_set_function(XPT2046_CLK,GPIO_FUNC_SPI); |
| gpio_set_function(XPT2046_CS,GPIO_FUNC_SIO); |
| gpio_set_function(XPT2046_MOSI,GPIO_FUNC_SPI); |
| gpio_set_function(XPT2046_MISO,GPIO_FUNC_SPI); |
| |
| spi_init(XPT2046_SPI,3125000); |
| |
| } |
| |
| extern lv_obj_t *mouse_cursor; |
| void xpt2046_lvgl_read_cb(struct _lv_indev_drv_t * indev, lv_indev_data_t* data) { |
| uint16_t x,y; |
| |
| if (xpt2046_TouchPressed()) { |
| if (xpt2046_getXY(&x,&y)) { |
| data->point.x = x; |
| data->point.y = y; |
| data->state = LV_INDEV_STATE_PRESSED; |
| } else { |
| data->state = LV_INDEV_STATE_RELEASED; |
| } |
| } else { |
| data->state = LV_INDEV_STATE_RELEASED; |
| } |
| |
| } |
| #ifndef _XPT2046_H_ |
| #define _XPT2046_H_ |
| #include "pico/stdlib.h" |
| #include "lvgl.h" |
| |
| #define XPT2046_IRQ_GPIO 11 |
| #define XPT2046_MOSI 15 |
| #define XPT2046_MISO 12 |
| #define XPT2046_CS 13 |
| #define XPT2046_CLK 14 |
| #define XPT2046_SPI spi1 |
| |
| #define XPT2046_MIN_RAW_X 1350 |
| #define XPT2046_MAX_RAW_X 31000 |
| #define XPT2046_MIN_RAW_Y 2050 |
| #define XPT2046_MAX_RAW_Y 31500 |
| |
| |
| void xpt2046_init(); |
| bool xpt2046_getXY(uint16_t *x, uint16_t *y); |
| bool xpt2046_TouchPressed(); |
| void xpt2046_lvgl_read_cb(struct _lv_indev_drv_t * indev, lv_indev_data_t* data); |
| |
| #endif |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #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 "inttypes.h" |
| |
| static struct { |
| int dx; |
| int dy; |
| int wheel; |
| uint8_t buttons; |
| } mouse_point; |
| |
| |
| static btstack_packet_callback_registration_t hci_event_callback_registration; |
| static btstack_packet_callback_registration_t l2cap_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 = 1; |
| static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); |
| |
| const uint8_t adv_data[] = { |
| |
| 0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, |
| |
| 0x11, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'P','i','c','o',' ','W',' ', 'H', 'I', 'D', ' ', 'M', 'o', 'u', 's', 'e', |
| |
| 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, |
| |
| 0x03, BLUETOOTH_DATA_TYPE_APPEARANCE, 0xC2, 0x03, |
| }; |
| const uint8_t adv_data_len = sizeof(adv_data); |
| |
| |
| const uint8_t hid_descriptor_mouse_boot_mode[] = { |
| 0x05, 0x01, |
| 0x09, 0x02, |
| 0xa1, 0x01, |
| |
| 0x85, 0x01, |
| |
| 0x09, 0x01, |
| |
| 0xa1, 0x00, |
| |
| 0x05, 0x09, |
| 0x19, 0x01, |
| 0x29, 0x03, |
| 0x15, 0x00, |
| 0x25, 0x01, |
| 0x95, 0x03, |
| 0x75, 0x01, |
| 0x81, 0x02, |
| 0x95, 0x01, |
| 0x75, 0x05, |
| 0x81, 0x03, |
| |
| 0x05, 0x01, |
| 0x09, 0x30, |
| 0x09, 0x31, |
| 0x09, 0x38, |
| 0x15, 0x81, |
| 0x25, 0x7f, |
| 0x75, 0x08, |
| 0x95, 0x03, |
| 0x81, 0x06, |
| |
| 0xc0, |
| 0xc0 |
| }; |
| |
| static void hog_mouse_setup(void){ |
| |
| |
| l2cap_init(); |
| |
| |
| sm_init(); |
| sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY); |
| |
| sm_set_authentication_requirements(SM_AUTHREQ_BONDING); |
| |
| |
| att_server_init(profile_data, NULL, NULL); |
| |
| |
| battery_service_server_init(battery); |
| |
| |
| device_information_service_server_init(); |
| |
| |
| hids_device_init(0, hid_descriptor_mouse_boot_mode, sizeof(hid_descriptor_mouse_boot_mode)); |
| |
| |
| 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); |
| |
| |
| hci_event_callback_registration.callback = &packet_handler; |
| hci_add_event_handler(&hci_event_callback_registration); |
| |
| |
| l2cap_event_callback_registration.callback = &packet_handler; |
| l2cap_add_event_handler(&l2cap_event_callback_registration); |
| |
| sm_event_callback_registration.callback = &packet_handler; |
| sm_add_event_handler(&sm_event_callback_registration); |
| |
| hids_device_register_packet_handler(packet_handler); |
| |
| hci_power_control(HCI_POWER_ON); |
| } |
| |
| |
| static void send_report(uint8_t buttons, int8_t dx, int8_t dy, int8_t wheel){ |
| uint8_t report[] = { buttons, (uint8_t) dx, (uint8_t) dy, (uint8_t) wheel}; |
| switch (protocol_mode){ |
| case 0: |
| hids_device_send_boot_mouse_input_report(con_handle, report, sizeof(report)); |
| break; |
| case 1: |
| hids_device_send_input_report(con_handle, report, sizeof(report)); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void mousing_can_send_now(void){ |
| send_report(mouse_point.buttons, mouse_point.dx, mouse_point.dy, mouse_point.wheel); |
| |
| mouse_point.dx = 0; |
| mouse_point.dy = 0; |
| mouse_point.wheel=0; |
| if (mouse_point.buttons){ |
| mouse_point.buttons = 0; |
| 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); |
| uint16_t conn_interval; |
| |
| 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; |
| break; |
| case SM_EVENT_JUST_WORKS_REQUEST: |
| 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 L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: |
| printf("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet)); |
| break; |
| case HCI_EVENT_LE_META: |
| switch (hci_event_le_meta_get_subevent_code(packet)) { |
| case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: |
| |
| conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet); |
| printf("LE Connection Complete:\n"); |
| printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); |
| printf("- Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet)); |
| break; |
| case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: |
| |
| conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); |
| printf("LE Connection Update:\n"); |
| printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); |
| printf("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet)); |
| break; |
| default: |
| break; |
| } |
| 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_BOOT_MOUSE_INPUT_REPORT_ENABLE: |
| con_handle = hids_subevent_boot_mouse_input_report_enable_get_con_handle(packet); |
| printf("Boot Mouse Characteristic Subscribed %u\n", hids_subevent_boot_mouse_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: |
| mousing_can_send_now(); |
| break; |
| default: |
| break; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| PRIMARY_SERVICE, GAP_SERVICE |
| CHARACTERISTIC, GAP_DEVICE_NAME, READ, "Pico W HID Mouse" |
| |
| |
| #import <battery_service.gatt> |
| |
| |
| #import <device_information_service.gatt> |
| |
| |
| #import <hids.gatt> |
| |
| PRIMARY_SERVICE, GATT_SERVICE |
| CHARACTERISTIC, GATT_DATABASE_HASH, READ, |
| |
| #include <stdio.h> |
| #include "pico/stdlib.h" |
| #include "pico/cyw43_arch.h" |
| |
| #include "picow_lvgl_ble_mouse.h" |
| |
| #include "pico_lvgl.h" |
| #include "hog_mouse.h" |
| |
| |
| PIO TFT_PIO = pio0; |
| #define TFT_SM 0 |
| #define TFT_SDI_GPIO 9 |
| #define TFT_CSX_DCX_SCK_GPIO 6 |
| |
| |
| |
| static lv_obj_t *mouse_cursor; |
| static lv_obj_t *right_button; |
| static lv_obj_t *left_button; |
| static lv_obj_t *wheel_up; |
| static lv_obj_t *wheel_down; |
| |
| void button_press_cb(lv_event_t *e) { |
| lv_event_code_t code =lv_event_get_code(e); |
| lv_obj_t* obj = lv_event_get_target(e); |
| if (code == LV_EVENT_PRESSED) { |
| if (obj == left_button) { |
| mouse_point.buttons |= 1; |
| } |
| if (obj == right_button) { |
| mouse_point.buttons |= 2; |
| } |
| hids_device_request_can_send_now_event(con_handle); |
| } |
| } |
| |
| void rect_pad_cb(lv_event_t *e) { |
| lv_event_code_t code =lv_event_get_code(e); |
| static uint16_t x, y; |
| static int16_t ox, oy; |
| if (code == LV_EVENT_PRESSED) { |
| if (!xpt2046_getXY(&ox, &oy)) { |
| ox=-1; oy=-1; |
| } |
| mouse_point.dx=0; |
| mouse_point.dy=0; |
| |
| } |
| |
| if (code == LV_EVENT_PRESSING && xpt2046_getXY(&x, &y)) { |
| lv_obj_set_x(mouse_cursor, x-lv_obj_get_style_pad_left(mouse_cursor,0)-15); |
| lv_obj_set_y(mouse_cursor, y-lv_obj_get_style_pad_top(mouse_cursor,0)-15); |
| if (ox > 0 && oy > 0) { |
| mouse_point.dx = (x-ox)*2; |
| mouse_point.dy = (y-oy)*2; |
| ox=x; |
| oy=y; |
| hids_device_request_can_send_now_event(con_handle); |
| } else { |
| mouse_point.dx=0; |
| mouse_point.dy=0; |
| } |
| } |
| } |
| |
| void mouse_wheel_cb(lv_event_t *e) { |
| lv_event_code_t code =lv_event_get_code(e); |
| lv_obj_t* obj = lv_event_get_target(e); |
| if (code == LV_EVENT_PRESSED || code == LV_EVENT_RELEASED) { |
| mouse_point.wheel=0; |
| hids_device_request_can_send_now_event(con_handle); |
| } |
| |
| if (code == LV_EVENT_PRESSING) { |
| if (obj == wheel_up) { |
| mouse_point.wheel = 1; |
| } |
| if (obj == wheel_down) { |
| mouse_point.wheel = -1; |
| } |
| hids_device_request_can_send_now_event(con_handle); |
| } |
| } |
| |
| void mouse_pad_init() { |
| left_button = lv_btn_create(lv_scr_act()); |
| right_button = lv_btn_create(lv_scr_act()); |
| lv_obj_t* rect_pad = lv_obj_create(lv_scr_act()); |
| lv_obj_t* wheel = lv_obj_create(lv_scr_act()); |
| |
| lv_obj_set_style_pad_all(wheel, 1, 0); |
| wheel_up = lv_btn_create(wheel); |
| wheel_down = lv_btn_create(wheel); |
| |
| lv_obj_set_size(left_button, lv_pct(42)-5,lv_pct(15)); |
| lv_obj_set_size(right_button, lv_pct(42)-5,lv_pct(15)); |
| lv_obj_set_size(wheel, lv_pct(10),lv_pct(15)); |
| |
| lv_obj_set_size(wheel_up, lv_pct(80),lv_pct(45)); |
| lv_obj_t *label = lv_label_create(wheel_up); |
| lv_label_set_text(label, LV_SYMBOL_UP); |
| lv_obj_center(label); |
| lv_obj_set_size(wheel_down, lv_pct(80),lv_pct(45)); |
| label = lv_label_create(wheel_down); |
| lv_obj_center(label); |
| lv_label_set_text(label, LV_SYMBOL_DOWN); |
| |
| lv_obj_set_size(rect_pad, lv_pct(100), lv_pct(85)-4); |
| |
| lv_obj_add_event_cb(rect_pad, rect_pad_cb, LV_EVENT_ALL, NULL); |
| lv_obj_set_scrollbar_mode(rect_pad, LV_SCROLLBAR_MODE_OFF); |
| |
| lv_obj_add_event_cb(left_button, button_press_cb, LV_EVENT_PRESSED, NULL); |
| lv_obj_add_event_cb(right_button, button_press_cb, LV_EVENT_PRESSED, NULL); |
| |
| lv_obj_add_event_cb(wheel_up, mouse_wheel_cb, LV_EVENT_ALL, NULL); |
| lv_obj_add_event_cb(wheel_down, mouse_wheel_cb, LV_EVENT_ALL, NULL); |
| |
| lv_obj_align(rect_pad, LV_ALIGN_TOP_MID, 0,0); |
| |
| lv_obj_align(wheel, LV_ALIGN_BOTTOM_MID, 0, -10); |
| lv_obj_align_to(left_button, wheel, LV_ALIGN_OUT_LEFT_TOP, -10, 0); |
| lv_obj_align_to(right_button, wheel, LV_ALIGN_OUT_RIGHT_TOP, 10, 0); |
| |
| static lv_style_t button_style; |
| lv_style_init(&button_style); |
| lv_style_set_bg_color(&button_style, lv_color_hex(0xaaffff)); |
| lv_style_set_border_color(&button_style, lv_color_hex(0x0000ff)); |
| lv_style_set_border_width(&button_style, 2); |
| lv_style_set_shadow_color(&button_style, lv_color_black()); |
| lv_style_set_shadow_ofs_x(&button_style, 2); |
| lv_style_set_shadow_ofs_y(&button_style, 2); |
| |
| lv_obj_add_style(left_button, &button_style,0); |
| lv_obj_add_style(right_button, &button_style,0); |
| |
| lv_obj_align(wheel_up, LV_ALIGN_TOP_MID, 0, 0); |
| lv_obj_align(wheel_down, LV_ALIGN_BOTTOM_MID, 0, -2); |
| |
| mouse_cursor = lv_obj_create(rect_pad); |
| lv_obj_set_style_bg_color(mouse_cursor, lv_color_hex(0xFF0000), 0); |
| lv_obj_set_style_radius(mouse_cursor, LV_RADIUS_CIRCLE, 0); |
| lv_obj_set_style_border_color(mouse_cursor, lv_color_hex(0xE08080), 0); |
| lv_obj_set_style_border_width(mouse_cursor, 8, 0); |
| lv_obj_set_size(mouse_cursor,30,30); |
| lv_obj_set_pos(mouse_cursor, lv_pct(50),lv_pct(50)); |
| |
| } |
| |
| int main() |
| { |
| stdio_init_all(); |
| |
| if (cyw43_arch_init()) { |
| printf("cyw43_arch_init error\n"); |
| return 0; |
| } |
| |
| pico_lvgl_tft_init(TFT_PIO, TFT_SM, TFT_SDI_GPIO, TFT_CSX_DCX_SCK_GPIO); |
| tft_set_orientation(TFT_ORIENTATION_LANDSCAPE); |
| |
| pico_lvgl_display_init(5); |
| pico_lvgl_xpt2046_init(); |
| |
| mouse_pad_init(); |
| |
| hog_mouse_setup(); |
| while (true) { |
| lv_timer_handler(); |
| sleep_ms(5); |
| } |
| } |
| |
| |
| if(WIN32) |
| set(USERHOME $ENV{USERPROFILE}) |
| else() |
| set(USERHOME $ENV{HOME}) |
| endif() |
| set(sdkVersion 2.0.0) |
| set(toolchainVersion 13_2_Rel1) |
| set(picotoolVersion 2.0.0) |
| include(${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) |
| |
| |
| |
| cmake_minimum_required(VERSION 3.13) |
| |
| set(CMAKE_C_STANDARD 11) |
| set(CMAKE_CXX_STANDARD 17) |
| set(CMAKE_EXPORT_COMPILE_COMMANDS ON) |
| |
| |
| |
| |
| |
| if(WIN32) |
| set(USERHOME $ENV{USERPROFILE}) |
| else() |
| set(USERHOME $ENV{HOME}) |
| endif() |
| set(sdkVersion 2.0.0) |
| set(toolchainVersion 13_2_Rel1) |
| set(picotoolVersion 2.0.0) |
| include(${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake) |
| |
| set(PICO_BOARD pico_w CACHE STRING "Board type") |
| |
| |
| include(pico_sdk_import.cmake) |
| |
| project(picow_lvgl_ble_mouse C CXX ASM) |
| |
| |
| pico_sdk_init() |
| |
| |
| |
| add_executable(picow_lvgl_ble_mouse picow_lvgl_ble_mouse.c ) |
| |
| pico_set_program_name(picow_lvgl_ble_mouse "picow_lvgl_ble_mouse") |
| pico_set_program_version(picow_lvgl_ble_mouse "0.1") |
| |
| |
| pico_enable_stdio_uart(picow_lvgl_ble_mouse 1) |
| pico_enable_stdio_usb(picow_lvgl_ble_mouse 0) |
| |
| |
| target_link_libraries(picow_lvgl_ble_mouse |
| pico_stdlib |
| pico_cyw43_arch_none |
| pico_btstack_cyw43 |
| pico_btstack_ble |
| ) |
| |
| add_subdirectory(pico_lvgl) |
| |
| target_link_libraries(picow_lvgl_ble_mouse |
| pico_lvgl |
| ) |
| pico_btstack_make_gatt_header(picow_lvgl_ble_mouse PRIVATE "${CMAKE_CURRENT_LIST_DIR}/picow_lvgl_ble_mouse.gatt") |
| |
| |
| target_include_directories(picow_lvgl_ble_mouse PRIVATE |
| ${CMAKE_CURRENT_LIST_DIR} |
| ${CMAKE_CURRENT_LIST_DIR}/.. |
| ) |
| |
| pico_add_extra_outputs(picow_lvgl_ble_mouse) |
| |
| |