Funpack2-6 nRF7002-DK 蓝牙复合设备项目
nRF7002-DK 任务一蓝牙鼠标键盘复合设备项目,包含视频、文档、代码
标签
Funpack参赛
蓝牙
nRF7002
hids
Quarix
更新2023-10-07
北京邮电大学
336

项目描述:

  • 项目介绍:Funpack2-6 nRF7002-DK 蓝牙复合设备项目,基于VScode和nRF Connect SDK平台完成任务一,实现蓝牙鼠标键盘功能。按键1作为鼠标右键点击,按键2作为键盘输入按下时输入“eetree”字符,电脑开启大写锁定时,板卡的LED亮起。

 

  • 设计思路:板载的nRF5340 Soc作为主控,利用其内置的蓝牙支持,开发蓝牙HIDS设备。利用板卡外置的按钮和LED进行交互操作,完成蓝牙配对和按键模拟。

 

  • 硬件介绍:nRF7002-DK是用于nRF7002 Wi-Fi 6 协同IC的开发套件,该开发套件采用nRF5340多协议片上系统 (SoC) 作为nRF7002的主处理器,在单一的电路板上包含了开发工作所需的一切,可让开发人员轻松开启基于 nRF7002 的物联网项目。 其支持低功耗 Wi-Fi 应用开发,并实现了多项 Wi-Fi 6 功能,比如 OFDMA、波束成型和 TWT。该 DK 包括 Arduino 连接器、两个可编程按钮、一个 Wi-Fi 双频段天线和一个低功耗蓝牙天线,以及电流测量引脚。 nRF7002 Wi-Fi 6配套IC为另一个主机添加了低功耗Wi-Fi 6功能,具有无缝连接和基于Wi-Fi的定位(本地Wi-Fi集线器的SSID嗅探)功能。该IC设计用于搭配Nordic现有的nRF52®和nRF53®系列多协议片上系统 (SoC) 和nRF91®系列蜂窝物联网系统级封装 (SiP) 使用。nRF7002 IC还可与非nordic主机器件搭配使用。通过SPI或QSPI与主机通信,并带有额外的共存功能,可与其他协议如蓝牙、Thread或Zigbee无缝共存。nRF7002在Nordic的nRF Connect SDK中提供集成和支持。

 

板卡特性:

  • 用于nRF7002双频带Wi-Fi 6配套IC的开发套件
  • nRF5340 SoC主机器件
  • Wi-Fi 6 (IEEE 802.11 a/b/g/n/ac/ax)、蓝牙低功耗 (LE)、蓝牙网状网络、802.15.4、Thread、Zigbee®、ANT、2.4GHz专有和NFC无线协议支持
  • 2.4GHz、5GHz芯片和NFC天线 SWF射频连接器
  • SEGGER J-Link板载编程器/调试器
  • 用户可编程LED (2x) 和按钮 (2x)
  • 用于测量功耗的引脚
  • 来自USB、外部或锂聚合物电池的2.9V至5.0V电源
  • Arduino连接器

芯片应用场景:

  • 电池供电Wi-Fi产品
  • 智慧城市和智能农业
  • 智能家居
  • 工业传感器
  • 可穿戴设备和医疗设备

软件流程图:

FtOcsiUFsfPK7x2mDfh1zgQVFmTl

 

主要代码片段及说明:

头文件引入

#include <zephyr/types.h>
#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <zephyr/sys/printk.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <soc.h>
#include <assert.h>
#include <zephyr/spinlock.h>

#include <zephyr/settings/settings.h>

#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>

#include <zephyr/bluetooth/services/bas.h>
#include <bluetooth/services/hids.h>
#include <zephyr/bluetooth/services/dis.h>
#include <dk_buttons_and_leds.h>

定义hid报告,初始化

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

	static const uint8_t report_map[] = {
		0x05, 0x01,       /* Usage Page (Generic Desktop) */
		0x09, 0x06,       /* Usage (Keyboard) */
		0xA1, 0x01,       /* Collection (Application) */

		/* Keys */
#if INPUT_REP_KEYS_REF_ID
		0x85, INPUT_REP_KEYS_REF_ID,
#endif
		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 */
#if OUTPUT_REP_KEYS_REF_ID
		0x85, OUTPUT_REP_KEYS_REF_ID,
#endif
		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 report */
		0x95, 0x01,       /* Report Count (1) */
		0x75, 0x03,       /* Report Size (3) */
		0x91, 0x01,       /* Output (Data, Variable, Absolute), */
				  /* Led report padding */

		0xC0,              /* End Collection (Application) */

		0x05, 0x01, /* Usage Page (Generic Desktop Ctrls) */
		0x09, 0x02, /* Usage (Mouse) */
		0xA1, 0x01, /* Collection (Application) */
		0x85, 0x02, /*	 Report Id (2) */
		0x09, 0x01, /*   Usage (Pointer) */
		0xA1, 0x00, /*   Collection (Physical) */
		0x05, 0x09, /*     Usage Page (Button) */
		0x19, 0x01, /*     Usage Minimum (0x01) */
		0x29, 0x03, /*     Usage Maximum (0x03) */
		0x15, 0x00, /*     Logical Minimum (0) */
		0x25, 0x01, /*     Logical Maximum (1) */
		0x95, 0x03, /*     Report Count (3) */
		0x75, 0x01, /*     Report Size (1) */
		0x81, 0x02, /*     Input (Data,Var,Abs,No Wrap,Linear,...) */
		0x95, 0x01, /*     Report Count (1) */
		0x75, 0x05, /*     Report Size (5) */
		0x81, 0x03, /*     Input (Const,Var,Abs,No Wrap,Linear,...) */
		0x05, 0x01, /*     Usage Page (Generic Desktop Ctrls) */
		0x09, 0x30, /*     Usage (X) */
		0x09, 0x31, /*     Usage (Y) */
		0x15, 0x81, /*     Logical Minimum (129) */
		0x25, 0x7F, /*     Logical Maximum (127) */
		0x75, 0x08, /*     Report Size (8) */
		0x95, 0x02, /*     Report Count (2) */
		0x81, 0x06, /*     Input (Data,Var,Rel,No Wrap,Linear,...) */
		0xC0,       /*   End Collection */
		0xC0,       /* End Collection */
	};

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

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

	hids_inp_rep =
		&hids_init_obj.inp_rep_group_init.reports[INPUT_REP_KEYS_IDX];
	hids_inp_rep->size = INPUT_REPORT_KEYS_MAX_LEN;
	hids_inp_rep->id = INPUT_REP_KEYS_REF_ID;
	hids_init_obj.inp_rep_group_init.cnt++;

	hids_outp_rep =
		&hids_init_obj.outp_rep_group_init.reports[OUTPUT_REP_KEYS_IDX];
	hids_outp_rep->size = OUTPUT_REPORT_MAX_LEN;
	hids_outp_rep->id = OUTPUT_REP_KEYS_REF_ID;
	hids_outp_rep->handler = hids_outp_rep_handler;
	hids_init_obj.outp_rep_group_init.cnt++;

	hids_inp_rep = &hids_init_obj.inp_rep_group_init.reports[INPUT_REP_BUTTONS_IDX];
	hids_inp_rep->size = INPUT_REPORT_BUTTONS_MAX_LEN;
	hids_inp_rep->id = OUTPUT_REP_BUTTONS_REF_ID;
	hids_init_obj.inp_rep_group_init.cnt++;

	hids_init_obj.is_kb = true;
	hids_init_obj.is_mouse = true;
	hids_init_obj.boot_kb_outp_rep_handler = hids_boot_kb_outp_rep_handler;
	hids_init_obj.pm_evt_handler = hids_pm_evt_handler;

	err = bt_hids_init(&hids_obj, &hids_init_obj);
	__ASSERT(err == 0, "HIDS initialization failed\n");
}

初始化蓝牙对象:

static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_GAP_APPEARANCE,
		      (CONFIG_BT_DEVICE_APPEARANCE >> 0) & 0xff,
		      (CONFIG_BT_DEVICE_APPEARANCE >> 8) & 0xff),
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_HIDS_VAL),
					  BT_UUID_16_ENCODE(BT_UUID_BAS_VAL)),
};

static const struct bt_data sd[] = {
	BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};

static struct conn_mode {
	struct bt_conn *conn;
	bool in_boot_mode;
} conn_mode[CONFIG_BT_HIDS_MAX_CLIENT_COUNT];

 

开始蓝牙广播

static void advertising_start(void)
{
	int err;
	struct bt_le_adv_param *adv_param = BT_LE_ADV_PARAM(
						BT_LE_ADV_OPT_CONNECTABLE |
						BT_LE_ADV_OPT_ONE_TIME,
						BT_GAP_ADV_FAST_INT_MIN_2,
						BT_GAP_ADV_FAST_INT_MAX_2,
						NULL);

	err = bt_le_adv_start(adv_param, ad, ARRAY_SIZE(ad), sd,
			      ARRAY_SIZE(sd));
	if (err) {
		if (err == -EALREADY) {
			printk("Advertising continued\n");
		} else {
			printk("Advertising failed to start (err %d)\n", err);
		}

		return;
	}

	is_adv = true;
	printk("Advertising successfully started\n");
}

按钮按下以及信号发送


static void button_shift_changed(bool down)
{
	for (size_t i = 0; i < CONFIG_BT_HIDS_MAX_CLIENT_COUNT; i++) {
		if (conn_mode[i].conn) {
			int err;
			uint8_t buffer[INPUT_REPORT_BUTTONS_MAX_LEN];
			memset(buffer, 0, INPUT_REPORT_BUTTONS_MAX_LEN);
			/*if (down) {
				buffer[0] = 1;
			} else {
				buffer[0] = 0;
			}*/
			if (down) {
    			//buffer[0] |= (1 << 1); // 设置 buffer[0] 的第二位为 1 即为鼠标右键按下
				buffer[0] = 0b000010;
			} else {
    			//buffer[0] &= ~(1 << 1); // 设置 buffer[0] 的第二位为 0
				buffer[0] = 0b000000;
			}
			bt_hids_inp_rep_send(&hids_obj, conn_mode[i].conn,
					  INPUT_REP_BUTTONS_IDX,
					  buffer, sizeof(buffer), NULL);
			if (err) {
				printk("Key report send error: %d\n", err);
				return err;
			}
		}
	}
}
if (pairing_button_pressed &&
	    (has_changed & (KEY_PAIRING_ACCEPT | KEY_PAIRING_REJECT))) {
		pairing_button_pressed = false;

		return;
	}

	if (has_changed & KEY_TEXT_MASK) {
		button_text_changed((button_state & KEY_TEXT_MASK) != 0);
	}
	if (has_changed & KEY_SHIFT_MASK) {
		button_shift_changed((button_state & KEY_SHIFT_MASK) != 0);
	}
#if CONFIG_NFC_OOB_PAIRING
	if (has_changed & KEY_ADV_MASK) {
		size_t i;

		for (i = 0; i < CONFIG_BT_HIDS_MAX_CLIENT_COUNT; i++) {
			if (!conn_mode[i].conn) {
				advertising_start();
				return;
			}
		}

		printk("Cannot start advertising, all connections slots are"
		       " taken\n");
	}
#endif
}

 

功能展示及说明:

蓝牙配对:

Fnlle_h5aGBEABsXtDaBKbK5uu-5

 

FjLmeO8PbHJutdk_qkhQKIaPROdR

打字功能

FlpFrndrmhIgTjdliWD0j1QUqdma

大写指示灯:

FlGZZmEZl_S5Lslz-riw37tmU6xI

 

右键菜单:

Flr6qmerTeW6pOYRjdQIU5xgtr8I

 

项目心得:我学会了基本的nrf SDK开发,也是第一次了解蓝牙协议,第一次接触hid标准,这次活动增加了我对hid和蓝牙的理解认识,也熟悉了VScode的开发流程。

该项目已经成功实现了简易蓝牙鼠标键盘二合一设备的功能,并达到了预期指标。然而通过更换硬件,还有许多可以提升与扩展的地方。例如接上屏幕,利用丰富的io接口进行图文展示。或者学习利用IMCU进行动态调试开发等等。

附件下载
my_hids_keymouse.zip
代码文件
团队介绍
个人项目,个人报名
团队成员
Quarix
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号