Funpack2-6:在nRF7002开发板上使用nRF5340实现蓝牙键鼠套装
参加硬禾学堂Funpack2-6活动,使用Nordic的nRF Connect SDK开发套件和VSCode插件,在nRF7002DK开发板上,使用上面的蓝牙主控nRF5340实现了一个能够被PC和手机识别的蓝牙通用人机交互设备。
标签
嵌入式系统
Funpack活动
开发板
蓝牙
MCU
Nordic
nRF7002-DK
大波丁
更新2023-10-17
南京航空航天大学
1442

0. 项目背景

参加硬禾学堂Funpack2-6活动,使用Nordic的nRF Connect SDK开发套件和VSCode插件,在nRF7002DK开发板上,使用上面的蓝牙主控nRF5340实现了一个能够被PC和手机识别的蓝牙通用人机交互设备,完成模拟鼠标点击事件、键盘的连续多键值发送、接受来自蓝牙主设备的CapsLock信号等功能。

这块nRF7002DK开发板上,拥有2颗nRF5340带蓝牙功能的主控芯片,其中1颗用来作为JTAG通信,为另外一个核心主控提供JTAG烧录功能,还拥有1颗nRF7002无线WIFI6的协处理器,可以用来学习开发使用WIFI相关的场景。本次完成硬禾学堂布置的任务1,只使用了其中的核心主控nRF5340完成一个蓝牙场景任务,即一个通用的蓝牙键鼠套装。

对比两年前我曾使用Keil + nRF5 SDK在nRF52840进行开发的经历,现在Nordic推出了全新的nRF Connect SDK开发套件,以及与之适配的nRF Connect for Desktop桌面工具和VSCode插件,同时为MCU的开发集成了zephyr内核的RTOS,这些都极大地降低了使用Nordic MCU的难度,提高了开发体验。

Fh3_AnGI_TTWAp-SxCMG1-YfF8yY

 

FsZGIjZMF1dHndwcGzprSB4omGOV

下面就笔者进行该项目的开发历程做一个简要的介绍:

1. 开发环境的搭建

第一个阶段就是开发环境的搭建,正如上面所说,Nordic推出了全新的nRF Connect SDK,可以通过图形化管理工具nRF Connect for Desktop进行自动安装和维护。可以通过下面这个链接进行下载:

https://www.nordicsemi.com/Products/Development-tools/nrf-connect-for-desktop/download#infotabs

无脑安装后,就可以使用自动配置工具进行SDK和VSCode插件的下载,SDK当前最新版本为2.4.2,值得注意的是,国内的网络环境很容易导致SDK下载失败,具体表现就是在安装路径下的v2.4.2文件夹里面没有任何文件,此时可以参考下面这个教程解决网络环境的问题:

https://www.bilibili.com/read/cv26507574/

2. GPIO点灯

开发环境配置完成后,即可打开VSCode正式进行NCS(nRF Connect SDK)项目的开发,根据向导,创建第一个application,选择根据sample来创建,选择blinky这个示例程序。

FjGwPIT0lmAC6YYXbW_rV1hJ-63h

此时你会看到一个令你一脸懵逼的点灯程序,它看起来是这样的:

FgQifNgKdvkE_wyyQl1qoazrpdY0

在这段代码里,我们看到了一些与以往不同的代码形式,比如,在第10行那个宏定义,参数部分莫名其妙出现的那个“led0”.

不过还是先等等吧,我们先来打通编译和烧录功能,刚创建完application后,我们尚无法对项目进行编译使用,需要先指定一下build configuration,这里我们选择nRF7002DK开发板的编译配置,如图所示:

FjPYER_QDrkmZtOKaVrNLuv9XDjB

配置好编译选项后,我们可以直接在VSCode中方便地进行编译和烧录,同时在串口终端中还能观察到运行日志,这是由另一颗nRF5340帮我们代理实现的串口透传,没错,这颗辅助的nRF5340不光帮我们搭起了JTAG的桥梁,还帮我们实现了串口透传,这几乎已经成为当今各种开发板的常规操作了。

编译烧录成功后,我们就可以看到开发板的LED交替闪烁,至此开发环境和工具链我们就打通了。

3. 设备树

还记得在blinky示例中让我们摸不着头脑,莫名奇妙出现的那个“led0”吗,我们该如何理解它,这就要好好理解一下NCS机构中的设备树的概念了。这可真不是只言片语能把它讲明白的,我这里只能就我的理解,以极其不专业的方式简单描述一下设备树的概念:

NCS为每个bsp开发板建立了一种脱离于C语言的配置文件,在这种配置文件中,描述了这个开发板拥有的板上外设及相应的参数和引脚分配,比如它描述了这个开发板上有两颗LED,它们的引脚分配分别是P1.06和P1.07,此外还有两个buttons,它们的引脚分别是P1.08和P1.09,将这些信息以一种树形的结构化代码描述起来,每个外设设计成树形结构中的一个node,外设的相关配置形成这种结构化语言中的节点属性,于是设备树的配置文件就应运而生了,这种文件的后缀是dts。一个开发板只有一套dts,如果自己实际工程中需要有所调整,则可以针对需要修改的部分,自行创建针对当前工程的设备树覆盖配置文件,名为overlay机制。

Fs758FX331sg7FUfOBCLk4zwYvjY

以上,就是我理解的NCS中的设备树,关于这个概念的专业教程可以参考这里:

https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/lessons/lesson-2-reading-buttons-and-controlling-leds/topic/devicetree/

在我们写好程序,点击编译按钮之后,构建系统便开始解析设备树的配置文件,将他们翻译成.h和.c等能被编译器识别的代码,然后在NCS的zephyr操作系统进入main函数之前,加载这些动态生成的代码,从而实现在用户业务逻辑开始之前,这些关于硬件的配置就全都搞定了。

4. 多线程

如果有RTT或者FreeRTOS等RTOS的开发经验,那么熟悉和了解NCS中的zephyr操作系统将是一件轻松加愉快的事情,因为你会同样接触到以下这些概念:

  • 线程的创建:静态,动态
  • 线程的优先级
  • 同等优先级下的线程时间片让渡
  • 线程间同步:信号量,互斥量
  • 线程间通信:消息队列,邮箱
  • 工作队列workqueue

以上这些概念,在基本的RTOS中都会存在,仅需要在语法层面和API层面注意一些差异即可。如果你需要关于这些知识的详细文档,可以参考以下链接:

https://academy.nordicsemi.com/courses/nrf-connect-sdk-fundamentals/lessons/lesson-7-multithreaded-applications/

5. 实战:蓝牙鼠标

好了,知识储备准备得差不多了,我们可以着手实现本次任务1中所提及的一部分功能了,即首先来设计一个蓝牙的纯鼠标设备。

Fk4S8Vlh_MLHyB5XK4sJwBwIQ0-W

如上图所示,根据sample创建一个蓝牙USB鼠标工程。

跟之前的blinky示例不同,这个示例官方尚未为其配置适用于nRF7002DK开发板的设备树,因此我们在进行构建配置时,无法选择nRF7002DK开发板,退而求其次,我们只能选择nRF5340DK开发板进行构建配置。

FiJwT17y0QBMkMU9yV9o5dF9H6zn

这意味着,我们将要在nRF7002DK开发板上跑一个为nRF5340开发的程序,进一步的,这意味着,我们需要调整一些设备树的配置以适配这颗本不属于自己的开发板,否则将无法正确运行。之前讲过,我们可以使用设备树的overlay机制,仅对需要覆盖的部分进行少量微调,而这种微调代码,是针对当前工程的,而不会污染整个SDK中其他的BSP例程。

关于设备树的overlay机制,我们可以阅读以下官方文档:

https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/build/dts/howtos.html#set-devicetree-overlays

得益于强大的抽象设计,我们的overlay文件并不需要多么复杂,只需要将几个按钮的pin引脚重新映射即可。

&button0{
    gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    label = "Push button 1";
};

&button1{
    gpios = <&gpio1 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
    label = "Push button 2";
};

5.1 编译和运行

 

编译、烧录,并打开串口调试。此时打开PC上的蓝牙,扫描蓝牙设备,可以发现一个名为`Nordic_HIDS_mouse`的蓝牙鼠标,点击连接设备,此时串口中会有提示,按下开发板上的button1按键确认连接。

FjQZUwSc1E-RTJ-7KD7JDkEhH8Fx

5.2 实现鼠标点击事件

该示例没有实现鼠标的点击功能,只对鼠标位移进行了处理,要处理鼠标点击功能,需要对USB报文和HID有所熟悉。

参考USB中文网中关于HID设备描述的讲解:https://www.usbzh.com/article/detail-830.html

配合示例代码中的 report_map 数组中的设备描述符定义,我们不难推断出该蓝牙鼠标的三种报文格式:

  1. Report ID 1: Mouse buttons + scroll/pan
  2. Report ID 2: Mouse motion
  3. Report ID 3: Advanced buttons

每种报文的比特序都可以通过注释阅读和理解。

5.3 鼠标点击事件的报文发送

根据上面的`report_map`和鼠标移动报文的对比,可根据第一段报文规则,编写出类似的鼠标点击事件报文:

static void mouse_buttons_send(uint8_t btnStatus) {
    for (size_t i = 0; i < CONFIG_BT_HIDS_MAX_CLIENT_COUNT; i++) {
        if (!conn_mode[i].conn) {
            continue;
        }

        if (conn_mode[i].in_boot_mode) {
            bt_hids_boot_mouse_inp_rep_send(&hids_obj,
                                            conn_mode[i].conn,
                                            &btnStatus,
                                            (int8_t)0,
                                            (int8_t)0,
                                            NULL);
        } else {
            uint8_t buffer[3] = {btnStatus & 0x07, 0x00, 0x00};

            bt_hids_inp_rep_send(&hids_obj, conn_mode[i].conn,
                                 INPUT_REP_BUTTONS_INDEX,
                                 buffer, sizeof(buffer), NULL);
        }
    }
}

以上代码,实现了USB设备描述符中的1号报文的发送,发送格式为:

  • 一共3个字节
  • 第一个字节有5个bit的掩码,最低位既是鼠标左键按下的有效位
  • 后面两个字节是滚轮相关,不用在意,填入两个0x00即可

为了在物理层面实现按键状态的感知,将信号通过消息队列向上层汇报,我们还需要略微改造一下相关的消息队列数据结构,以及物理层的按键事件回调函数button_changed(),此处不再赘述。

6. 实战:蓝牙键鼠套装

为了弄清楚键盘相关的蓝牙和USB逻辑,我们需要新建一个蓝牙纯键盘示例,研究一下它的代码。在VSCode开发环境下,新建项目,使用sample,筛选keyboard即可创建蓝牙键盘的示例程序:

FvYdQaNNYAcVCssVIEBxcQAYMTAJ

 

6.1 运行DEMO

该示例程序使用了NFC读卡功能,进行设备配对时的验证,可以观察main函数中的代码:

#if CONFIG_NFC_OOB_PAIRING
    k_work_init(&adv_work, delayed_advertising_start);
    app_nfc_init();
#else
    advertising_start();
#endif

建议在prj.conf项目配置中酌情将CONFIG_NFC_OOB_PAIRING配置为n,以打开自动广告和配对功能,绕开NFC配对。

6.2 基于蓝牙鼠标示例引入蓝牙键盘特性

6.2.1 修改蓝牙外观类型

根据蓝牙协议官方出版的BLE_Assigned_Numbers.pdf,第2.6章Appearance Values内容,修改蓝牙外观类型为Generic Human Interface Device,其ID为0x03C0,十进制数为960。

因此,修改prj.conf文件,做以下调整:

CONFIG_BT_DEVICE_NAME="Nordic_HIDS_KIT"
CONFIG_BT_DEVICE_APPEARANCE=960

这个设置会让蓝牙设备显示成这样:

FgKX_rHTRzKPhwj5YcWISOLDL3ZA

6.2.2 扩大HID特性参数容量

因本实例在[[07-蓝牙鼠标]]项目基础上进行修改的,且这个鼠标本身的设备描述功能就比较多,因此加入蓝牙键盘的特性后,会继续加大其特性容量,NCS的默认工程,支持30个蓝牙特性容量,其配置项中CONFIG_BT_HIDS_ATTR_MAX的默认值为30, 因此,为了防止特性值超出后,报No space left on given svc异常,导致MCU陷入循环复位,考虑对其进行修改,该值最大为50,设置超出后会提示警告。

CONFIG_BT_HIDS_ATTR_MAX=50

6.2.3 蓝牙键盘报文分析

根据USB中文网给出的蓝牙键盘报文描述 https://www.usbzh.com/article/detail-13.html

配合蓝牙键盘示例中设备描述符的代码,不难分析出蓝牙键盘的输入输出报文协议:

6.2.3.1 输入信号

  • BYTE0:该字节的各位表示特殊按键是否按下,这些特殊的按键包括Shift,Alt,Ctrl等。
  • BYTE1:该字节保留,值固定为00.
  • BYTE2-BYTE7:这6个字节表为一个6字节的数组,其中数组中的每一项可以表示按键的键值。当有多个普通的按键按下时,则应同时返回这些键的值。数组中各键的无先后顺序。
  • 具体的键值可参考标准USB键盘键值规范,比如键盘上的A,其键值为0x04

6.2.3.2 输出信号

  • 输出为1个字节。其中低5位表示了相关指示灯的状态,高5位保留值为0。
  • 可通过手动调试判断出键盘上CapsLock,NumberLock,ScroolLock的信号点位
  • CapsLock的实际点位在BIT1上,因此其掩码为0x02
  • 另外2个输出信号位为工作错误信号和Kana信号,均不常用,其中Kana信号用来指示日语输入的模式

6.3 修改蓝牙鼠标项目

6.3.1 追加相关宏定义

#define INPUT_REP_REF_KEYBOARD_ID 3
#define OUTPUT_REP_REF_KEYSLED_ID 4
#define KEY_PRESS_MAX 6 /* Maximum number of non-control keys*/
#define INPUT_REP_KEYS_LEN (1 + 1 + KEY_PRESS_MAX)
#define OUTPUT_REP_LED_LEN 1
#define OUTPUT_REPORT_BIT_MASK_CAPS_LOCK 0x02

6.3.2 配置HID设备对象

使用以下宏定义,配置HID对象,传入所有可能的报文长度,该宏会取它们的最大值配置HID对象。

BT_HIDS_DEF(hids_obj,
            INPUT_REP_BUTTONS_LEN,
            INPUT_REP_MOVEMENT_LEN,
            INPUT_REP_KEYS_LEN,
            OUTPUT_REP_LED_LEN);

6.3.3 追加键盘的设备描述符

static void hid_init(void) {
    int err;
    struct bt_hids_init_param hids_init_param = {0};
    struct bt_hids_inp_rep *hids_inp_rep;
    struct bt_hids_outp_feat_rep *hids_outp_rep;

    static const uint8_t mouse_movement_mask[DIV_ROUND_UP(INPUT_REP_MOVEMENT_LEN, 8)] = {0};

    static const uint8_t report_map[] = {
        0x05, 0x01, /* Usage Page (Generic Desktop) */
        0x09, 0x02, /* Usage (Mouse) */
        0xA1, 0x01, /* Collection (Application) */
        /* Report ID 1: Mouse buttons + scroll/pan */
        0x85, INPUT_REP_REF_BUTTONS_ID, /* Report Id 1 */
        0x05, 0x09,                     /* Usage Page (Buttons) */
        0x09, 0x01,                     /* Usage (Pointer) */
        0xA1, 0x00,                     /* Collection (Physical) */
        0x19, 0x01,                     // USAGE_MINIMUM (Button 1)
        0x29, 0x03,                     // USAGE_MAXIMUM (Button 3)
        0x15, 0x00,                     /* Logical Minimum (0) */
        0x25, 0x01,                     /* Logical Maximum (1) */
        0x95, 0x03,                     /* Report Count (3) */
        0x75, 0x01,                     /* Report Size (1) */
        0x81, 0x02,                     /* Input (Data, Variable, Absolute) */

        0x95, 0x01, /* Report Count (1) */
        0x75, 13,   /* Report Size (13) */
        0x81, 0x01, /* Input (Constant) for padding */

        0x95, 0x01, /* Report Count (1) */
        0x75, 0x08, /* Report Size (8) */
        0x05, 0x01, /* Usage Page (Generic Desktop) */
        0x09, 0x38, /* Usage (Wheel) */
        0x15, 0x81, /* Logical Minimum (-127) */
        0x25, 0x7F, /* Logical Maximum (127) */
        0x81, 0x06, /* Input (Data, Variable, Relative) */
        0xC0,       /* End Collection (Physical) */

        /* Report ID 2: Mouse motion */
        0x85, INPUT_REP_REF_MOVEMENT_ID, /* Report Id 2 */
        0x09, 0x01,                      /* Usage (Pointer) */
        0xA1, 0x00,                      /* Collection (Physical) */
        0x75, 0x0C,                      /* Report Size (12) */
        0x95, 0x02,                      /* Report Count (2) */
        0x05, 0x01,                      /* Usage Page (Generic Desktop) */
        0x09, 0x30,                      /* Usage (X) */
        0x09, 0x31,                      /* Usage (Y) */
        0x16, 0x01, 0xF8,                /* Logical maximum (2047) */
        0x26, 0xFF, 0x07,                /* Logical minimum (-2047) */
        0x81, 0x06,                      /* Input (Data, Variable, Relative) */
        0xC0,                            /* End Collection (Physical) */
        0xC0,                            /* End Collection (Application) */

        0x05, 0x01, /* Usage Page (Generic Desktop) */
        0x09, 0x06, /* Usage (Keyboard) */
        0xA1, 0x01, /* Collection (Application) */
        /* Keys */
        0x85, INPUT_REP_REF_KEYBOARD_ID, /* Report Id (3) */
        0x05, 0x07,                      /* Usage Page (Key Codes) */
        0x19, 0xe0,                      /* Usage Minimum (224) */
        0x29, 0xe7,                      /* Usage Maximum (231) */
        0x15, 0x00,                      /* Logical Minimum (0) */
        0x25, 0x01,                      /* Logical Maximum (1) */
        0x75, 0x01,                      /* Report Size (1) */
        0x95, 0x08,                      /* Report Count (8) */
        0x81, 0x02,                      /* Input (Data, Variable, Absolute) */

        0x95, 0x01, /* Report Count (1) */
        0x75, 0x08, /* Report Size (8) */
        0x81, 0x01, /* Input (Constant) reserved byte(1) */

        0x95, 0x06, /* Report Count (6) */
        0x75, 0x08, /* Report Size (8) */
        0x15, 0x00, /* Logical Minimum (0) */
        0x25, 0x65, /* Logical Maximum (101) */
        0x05, 0x07, /* Usage Page (Key codes) */
        0x19, 0x00, /* Usage Minimum (0) */
        0x29, 0x65, /* Usage Maximum (101) */
        0x81, 0x00, /* Input (Data, Array) Key array(6 bytes) */
        /* LED */
        0x85, OUTPUT_REP_REF_KEYSLED_ID, /* Report Id (1) */
        0x95, 0x05,                      /* Report Count (5) */
        0x75, 0x01,                      /* Report Size (1) */
        0x05, 0x08,                      /* Usage Page (Page# for LEDs) */
        0x19, 0x01,                      /* Usage Minimum (1) */
        0x29, 0x05,                      /* Usage Maximum (5) */
        0x91, 0x02,                      /* Output (Data, Variable, Absolute), LED */
        0x95, 0x01,                      /* Report Count (1) */
        0x75, 0x03,                      /* Report Size (3) */
        0x91, 0x01,                      /* Output (Data, Variable, Absolute), padding*/
        0xC0,                            /* End Collection (Application) */
    };

    hids_init_param.rep_map.data = report_map;
    hids_init_param.rep_map.size = sizeof(report_map);

    hids_init_param.info.bcd_hid = BASE_USB_HID_SPEC_VERSION;
    hids_init_param.info.b_country_code = 0x00;
    hids_init_param.info.flags = (BT_HIDS_REMOTE_WAKE | BT_HIDS_NORMALLY_CONNECTABLE);

    hids_inp_rep = &hids_init_param.inp_rep_group_init.reports[0];
    hids_inp_rep->size = INPUT_REP_BUTTONS_LEN;
    hids_inp_rep->id = INPUT_REP_REF_BUTTONS_ID;
    hids_init_param.inp_rep_group_init.cnt++;

    hids_inp_rep++;
    hids_inp_rep->size = INPUT_REP_MOVEMENT_LEN;
    hids_inp_rep->id = INPUT_REP_REF_MOVEMENT_ID;
    hids_inp_rep->rep_mask = mouse_movement_mask;
    hids_init_param.inp_rep_group_init.cnt++;

    // hids_inp_rep++;
    // hids_inp_rep->size = INPUT_REP_MEDIA_PLAYER_LEN;
    // hids_inp_rep->id = INPUT_REP_REF_MPLAYER_ID;
    // hids_init_param.inp_rep_group_init.cnt++;

    hids_inp_rep++;
    hids_inp_rep->size = INPUT_REP_KEYS_LEN;
    hids_inp_rep->id = INPUT_REP_REF_KEYBOARD_ID;
    hids_init_param.inp_rep_group_init.cnt++;

    hids_outp_rep = &hids_init_param.outp_rep_group_init.reports[0];
    hids_outp_rep->size = OUTPUT_REP_LED_LEN;
    hids_outp_rep->id = OUTPUT_REP_REF_KEYSLED_ID;
    hids_outp_rep->handler = hids_outp_rep_handler;
    hids_init_param.outp_rep_group_init.cnt++;
    hids_init_param.boot_kb_outp_rep_handler = hids_boot_kb_outp_rep_handler;

    hids_init_param.is_mouse = true;
    hids_init_param.is_kb = true;
    hids_init_param.pm_evt_handler = hids_pm_evt_handler;

    printk("HID Service init\n");
    err = bt_hids_init(&hids_obj, &hids_init_param);
    printk("HID Service inited\n");
    __ASSERT(err == 0, "HIDS initialization failed\n");
}
 

6.3.3 实现键盘相关的回调函数

输入回调:通过递归循环报告eetree键值事件

static uint8_t jobKeyIndex = 0;
static void sendJobKeyValues(struct bt_conn *conn, void *user_data){
	static const uint8_t jobKeyValues[] = { 0x08, 0x00, 0x08, 0x00, 0x17, 0x00, 0x15, 0x00, 0x08, 0x00, 0x08, 0x00 };	// "eetree"
    uint8_t buffer[INPUT_REP_KEYS_LEN] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
	if(jobKeyIndex < sizeof(jobKeyValues)){
		buffer[2] = jobKeyValues[jobKeyIndex];
		jobKeyIndex++;
		bt_hids_inp_rep_send(&hids_obj, conn, INPUT_REP_REF_KEYBOARD_ID - 1, buffer, sizeof(buffer), sendJobKeyValues);
	}
}

static void keyboard_key_send(uint8_t ctrlStatus, uint8_t *values) {
	uint8_t buffer[INPUT_REP_KEYS_LEN] = {0x00, 0x00, values[0], 0x00, 0x00, 0x00, 0x00, 0x00};
    for (size_t i = 0; i < CONFIG_BT_HIDS_MAX_CLIENT_COUNT; i++) {
        if (conn_mode[i].conn) {
            if (conn_mode[i].in_boot_mode) {
                bt_hids_boot_kb_inp_rep_send(&hids_obj, conn_mode[i].conn, buffer, sizeof(buffer), NULL);
            } else {				
				if(values[0] != 0x00){
					jobKeyIndex = 0;
					sendJobKeyValues(conn_mode[i].conn, NULL);					
				}
            }
			break;
        }
    }
}
 

输出回调:CapsLock信号灯等控制

static void caps_lock_handler(const struct bt_hids_rep *rep){
	uint8_t report_val = ((*rep->data) & OUTPUT_REPORT_BIT_MASK_CAPS_LOCK) ?  1 : 0;
	// 需要提前配置设备树overlay,并在main中使用 gpio_pin_configure_dt(&led0, GPIO_OUTPUT); 配置输出
	gpio_pin_set_dt(&led0, report_val);
}

static void hids_outp_rep_handler(struct bt_hids_rep *rep, struct bt_conn *conn, bool write){
	char addr[BT_ADDR_LE_STR_LEN];

	if (!write) {
		printk("Output report read\n");
		return;
	};

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	printk("Output report has been received %s\n", addr);
	caps_lock_handler(rep);
}


static void hids_boot_kb_outp_rep_handler(struct bt_hids_rep *rep, struct bt_conn *conn, bool write){
	char addr[BT_ADDR_LE_STR_LEN];

	if (!write) {
		printk("Output report read\n");
		return;
	};

	bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
	printk("Boot Keyboard Output report has been received %s\n", addr);
	caps_lock_handler(rep);
}

同样的,要实现上面的业务逻辑,也需要对消息队列的数据结构和物理层的代码进行相应的修改,此处限于篇幅,便不再赘述。

至此,终于完成本次项目所需要实现的任务1的所有功能,即使用nRF5340实现了一个标准的蓝牙键鼠套装,按下开发板上btn1,能够向蓝牙主设备发送鼠标左键的点击事件;按下btn2,能够上报连续的“eetree”键盘键值;并且蓝牙主设备可以发送CapsLock信号,控制开发板上的led0。

最后,感谢电子森林和硬禾学堂组织的本次活动,感谢Funpack2-6:nRF7002-DK 109人的微信群里所有热心帮助的小伙伴,期待下次与大家参与更多的硬禾学堂的活动,并分享更多的嵌入式开发的知识。

附件下载
peripheral_hids_mouse.7z
团队介绍
丁丁,江苏赛洋机电科技有限公司
团队成员
大波丁
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号