项目介绍:
该项目是利用nRF Connect SDK平台,并运用C语言在VS Code环境下,对nRF7002-DK进行控制开发。我的目标是赋予nRF7002-DK基本的蓝牙键盘和鼠标功能,通过这个项目,我希望能够提升我的个人开发技能。
项目亮点和目标:
个人技术提升:在这个项目中,我将有机会深入了解嵌入式开发、C语言编程以及硬件控制。这将为我在嵌入式领域的技术水平提供显著的提升。
蓝牙键鼠功能:通过开发出nRF7002-DK的蓝牙键盘和鼠标功能,我将能够实际探索如何在物理设备上模拟这些输入行为。
独立项目管理:在这个项目中,我将自行管理开发的各个阶段,从需求分析到最终实现,这将锻炼我的独立项目管理能力。
技术实践:通过实际开发过程,我将能够将之前学到的理论知识转化为实际操作,加深对技术原理的理解。
项目带来的价值:
实际项目经验:通过亲自参与开发,我将获得宝贵的实际项目经验,对嵌入式系统和硬件控制有更深入的认识。
技术扩展:这个项目将使我涉足多个技术领域,包括嵌入式开发、蓝牙通信和硬件控制。这将拓展我的技术广度。
通过这个独立项目,我将能够在一个小而具体的任务中实践我的技能,提升自己的技术水平,并为未来的开发之路积累宝贵的经验。
设计思路:
按任务一的要求, 用板卡的蓝牙连接,设计了一个蓝牙鼠标+键盘复合设备,按键1作为鼠标点击,按键2作为键盘输入按下时输入“eetree”字符,电脑开启大写锁定时,板卡的LED亮起
硬件介绍:
此次借助nRF Connect SDK平台对nRF7002-DK进行控制开发
控制器:
nRF Connect SDK,简称NCS,是Nordic最新的SDK平台,该平台将支持Nordic所有产品线,包括低功耗蓝牙,蜂窝网,WiFi,GPS,2.4G,Homekit, FindMy等。
nRF Connect SDK内嵌Zephyr RTOS,并沿用了Zephyr project的编译系统。Zephyr Project是Linux基金会推出的一个Apache2.0开源项目,版权非常友好,适合用于商业项目开发。
nRF7002 DK 是 nRF7002 和 nRF7001 Wi-Fi 6 协同 IC 的开发套件。它包含在单个板上开始开发所需的一切。DK 采用 nRF5340 多协议系统级芯片(SoC) 作为 nRF7002 的主机处理器。
该DK 支持低功耗 Wi-Fi 应用的开发,并支持 Wi-Fi 6 功能,如 OFDMA、波束成形和目标唤醒时间。
nRF7002 是一款 Wi-Fi 6 协同 IC,提供无缝连接和基于 Wi-Fi 的定位(本地 Wi-Fi 集线器的 SSID 嗅探)。它旨在与Nordic现有的nRF52®和nRF53®系列蓝牙系统级芯片(SoC)以及nRF91®系列蜂窝物联网系统级封装(SiP)一起使用。nRF7002 还可以与非Nordic主机设备结合使用。
为了与主机通信,可以使用SPI或QSPI,并且额外的共存功能允许与其他协议(如蓝牙,线程或Zigbee)无缝共存。nRF7002已被Nordic nRF Connect SDK集成并支持,nRF7002 Dk也可用于模拟nRF7001。
nRF7002是Nordic独特Wi-Fi产品组合中的第一款设备,它将与Nordic现有的超低功耗技术无缝结合。Nordic 将其数十年的超低功耗无线物联网和硅设计专业知识带到 Wi-Fi 中。Wi-Fi 6 为物联网应用带来了更多优势,包括进一步提高效率,支持长寿命的电池供电 Wi-Fi 操作。
借助 Wi-Fi 6,Nordic 支持用于 Matter 的所有无线协议、用于调试的蓝牙 LE、用于低功耗网状网络的 Thread 以及用于高吞吐量的 Wi-Fi。Matter是苹果、亚马逊、谷歌、Nordic、三星和消费者物联网中数百家其他公司倡导的协议。
外设:
nRF7002-DK是用于nRF7002 Wi-Fi 6双频辅助IC开发的套件。它支持多协议蓝牙和Wi-Fi应用开发,以及Wi-Fi 6功能如OFDMA和波束成形。适用于智能家居、工业物联网和媒体设备等,提供稳定连接和高速性能。
软件流程图及主要代码片段的介绍:
初始化阶段:
导入所需的头文件。
定义一些常量、宏和数据结构。
初始化HID服务的报告结构。
定义广告数据和扫描响应数据。
初始化连接模式和状态变量。
主循环阶段:
初始化按钮和LED。
注册认证回调函数。
初始化HID服务并启用蓝牙。
启动广播。
按钮状态变化处理:
监听按钮状态的变化。
处理配对按钮按下的情况,确认或拒绝配对。
处理文本按钮状态的变化,模拟键盘按键发送。
处理Shift按钮状态的变化,模拟鼠标右键按下。
连接、断开连接和安全相关的回调处理。
键盘状态维护和发送:
根据按键的状态维护键盘状态。
发送键盘状态报告给连接的客户端。
输入、输出报告的处理:
处理HID输入报告的处理,模拟键盘按键和鼠标事件的发送。
处理HID输出报告的处理,如大写锁定的状态更新。
基础服务(Battery Service)的处理:
更新电池电量,并通知连接的客户端。
配对过程相关的处理:
在配对过程中,显示配对的Passkey。
处理用户确认或拒绝配对的操作。
NFC和广播相关的处理(根据条件是否编译)。
主要代码片段及说明:
以下是代码中与蓝牙连接相关的部分:
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];
/* HIDs instance. */
BT_HIDS_DEF(hids_obj,
OUTPUT_REPORT_MAX_LEN,
INPUT_REPORT_KEYS_MAX_LEN,
INPUT_REPORT_BUTTONS_MAX_LEN);
static volatile bool is_adv;
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 connected(struct bt_conn *conn, uint8_t err)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
if (err) {
printk("Failed to connect to %s (%u)\n", addr, err);
return;
}
printk("Connected %s\n", addr);
dk_set_led_on(CON_STATUS_LED);
err = bt_hids_connected(&hids_obj, conn);
if (err) {
printk("Failed to notify HID service about connection\n");
return;
}
for (size_t i = 0; i < CONFIG_BT_HIDS_MAX_CLIENT_COUNT; i++) {
if (!conn_mode[i].conn) {
conn_mode[i].conn = conn;
conn_mode[i].in_boot_mode = false;
break;
}
}
/* ... */
}
这些代码片段展示了蓝牙连接和HID服务的初始化、连接、断开等操作。ad 和 sd 是用于广播的数据,conn_mode 数组用于跟踪连接状态,hids_obj 是HID服务的实例。advertising_start() 用于启动蓝牙广播,connected() 函数在连接建立时调用。
以下是模拟键盘输入 eetree 部分:
static const uint8_t eetree_str[] = {
0x08, /* Key e */
0x08, /* Key e */
0x17, /* Key t */
0x15, /* Key r */
0x08, /* Key e */
0x08, /* Key e */
0x28, /* Key Return */
};
在上述代码片段中,eetree_str 数组中的每个元素都表示一个模拟键盘输入操作。例如,0x08 表示键盘上的 Backspace 键,0x17 表示键盘上的 "t" 键,0x15 表示键盘上的 "r" 键,0x28 表示键盘上的 Enter 键。这些值按顺序排列,以模拟输入字符串 "eetree"。
以下是模拟鼠标右键部分代码片段:
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] = 0b000010; // 设置 buffer[0] 的二进制的第 2 位为 1,表示鼠标右键按下
} else {
buffer[0] = 0b000000; // 设置 buffer[0] 的二进制的第 2 位为 0,表示鼠标右键释放
}
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;
}
}
}
}
这部分代码通过设置一个特定的字节来模拟鼠标右键的按下和释放。在函数button_shift_changed中,根据参数down的值,将对应的二进制位设置为1或者0,然后使用bt_hids_inp_rep_send函数发送鼠标按钮的状态报告。这样,当down为true时,表示鼠标右键按下,会发送一个对应的状态报告;当down为false时,表示鼠标右键释放,同样会发送一个对应的状态报告。
功能展示及说明:
使用板卡的蓝牙连接,设计一个蓝牙鼠标+键盘复合设备,按键1作为鼠标点击,按键2作为键盘输入按下时输入“eetree”字符,电脑开启大写锁定时,板卡的LED亮起
对本活动的心得体会:
通过这个独立项目,我将能够在一个小而具体的任务中实践我的技能,提升自己的技术水平,并为未来的开发之路积累宝贵的经验。