项目总结报告
一、简介
EFR32xG24 Explorer 套件是一个基于 EFR32MG24 片上系统的小封装开发和评估平台。EFR32xG24 Explorer 套件专注于快速原型化和概念创建 2.4 GHz 无线协议的 IoT 应用程序,包括蓝牙 LE、蓝牙网状网络、Zigbee、Thread 和 Matter。
该板的关键功能包括一个 USB 接口、一个板载 SEGGER J-Link 调试器、数据包追踪接口和按钮,并可通过 mikroBus 插座和 Qwiic® 连接器对硬件附加板提供支持。硬件附加支持允许开发人员使用来自 mikroE、Sparkfun、AdaFruit 和 Seeed Studios 的现成板进行各种组合,来创建和原型化应用程序。
二、开发环境:
Simplicity Studio Version: SV5.8.1.0
三、知识储备:
这次的任务是实现一个模拟键盘与鼠标的复合设备。首选需要学习如何对该设备进行描述。这里我找到了几个学习的资源:基于XG24-EK2703A的BLE HID蓝牙键盘+鼠标复合设备功能开发(BLE+HID+FreeRTOS+Gecko SDK) 还有就是USB鼠标、键盘数据格式_usb 枚举鼠标只能发4字节吗?-CSDN博客这些文章详细的介绍了如何新建工程 ,对蓝牙鼠标、健盘如何组装通协议等等。
三、实现方式:
1、新建一个基于freertos的空白工程:
生成工程 后,我们先添加三个按键分别为btn0、btn1、btn2。
在配置文件中我们可以看到系统给我配置了三个按键,以及按键的回调:
// the table of buttons and button count are generated as a
// convenience for the application
const sl_button_t *sl_simple_button_array[] = {
&sl_button_btn0,
&sl_button_btn1,
&sl_button_btn2
};
const uint8_t simple_button_count = 3;
void sl_simple_button_init_instances(void)
{
sl_button_init(&sl_button_btn0);
sl_button_init(&sl_button_btn1);
sl_button_init(&sl_button_btn2);
}
void sl_simple_button_poll_instances(void)
{
sl_button_poll_step(&sl_button_btn0);
sl_button_poll_step(&sl_button_btn1);
sl_button_poll_step(&sl_button_btn2);
}
2、程序的流程图:
3、根据以上流程图,我们创建鼠标描述符,其路径为: CONFIGURATION TOOLS 选项卡下找到 Bluetooth GATT Configurator——》Report Map,在vluesettins中填 入05010906a101850175019508050719e029e71500250175019508810295017508810195067508150025650507190029658100c005010902a1018502750195080901a1000509190129031500250195037501810295017505810305010930093109381581750895038106c0c0,具体意思,可以阅读提示链接。
接下来定义四个发送的函数,代码如下:
static void km_btn_task(void *p_arg)
{
(void)p_arg;
app_timer_t btn_press_timer;
sl_status_t sc;
bool is_running = false;
EventBits_t btn_events;
while (1) {
btn_events = xEventGroupGetBits(xbtn_events);
switch (btn_events) {
case (BTN_BOTH_PRESSED):
app_log_debug("BTN0 & BTN1 pressed\r\n");
if (!is_running) {
app_log_debug("Timer started\r\n");
sc = app_timer_start(&btn_press_timer, 2000, btn_press_timer_cb, NULL, false);
app_assert_status(sc);
is_running = true;
}
break;
case (BTN0_PRESSED):
app_log_debug("BTN0 pressed\r\n");
sc = app_timer_stop(&btn_press_timer);
app_assert_status(sc);
km_status = KM_SCROLL_UP; // scroll up
sl_bt_external_signal(1);
break;
case (BTN1_PRESSED):
app_log_debug("BTN1 pressed\r\n");
sc = app_timer_stop(&btn_press_timer);
app_assert_status(sc);
km_status = KM_SCROLL_DOWN; // scroll down
sl_bt_external_signal(1);
break;
case (BTN2_PRESSED):
app_log_debug("BTN2 pressed\r\n");
sc = app_timer_stop(&btn_press_timer);
app_assert_status(sc);
km_status = KM_MUOSE_RIGHT; // scroll down
sl_bt_external_signal(1);
break;
default: // BTN_NONE_PRESSED
sc = app_timer_stop(&btn_press_timer);
app_assert_status(sc);
is_running = false;
break;
}
void scroll_with_distance(uint8_t distance)
{
sl_status_t sc;
memset(mouse_report_data, 0, sizeof(mouse_report_data));
mouse_report_data[REPORT_ID_INDEX] = MOUSE_REPORT_ID;
mouse_report_data[WHEEL_INDEX] = distance;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
sizeof(mouse_report_data),
mouse_report_data);
app_assert_status(sc);
}
void mouse_with_distance(uint8_t distance)
{
sl_status_t sc;
memset(mouse_report_data, 0, sizeof(mouse_report_data));
mouse_report_data[REPORT_ID_INDEX] = MOUSE_REPORT_ID;
mouse_report_data[1] = distance;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
sizeof(mouse_report_data),
mouse_report_data);
app_assert_status(sc);
}
void send_keyboard(uint8_t caps_key, uint8_t c)
{
sl_status_t sc;
memset(kb_report_data, 0, sizeof(kb_report_data));
kb_report_data[REPORT_ID_INDEX] = KB_REPORT_ID;
kb_report_data[MODIFIER_INDEX] = caps_key;
kb_report_data[DATA_INDEX] = c;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
sizeof(kb_report_data),
kb_report_data);
app_assert_status(sc);
memset(kb_report_data, 0, sizeof(kb_report_data));
kb_report_data[REPORT_ID_INDEX] = KB_REPORT_ID;
sc = sl_bt_gatt_server_notify_all(gattdb_report,
sizeof(kb_report_data),
kb_report_data);
app_assert_status(sc);
sl_sleeptimer_delay_millisecond(20);
}
【鼠标右键的实现思路】
其实前面两个按键的使用,大家的实现方式都一样,我这里增加了模拟鼠标右键的思路如下:
1、自己在工程中添加IO为BTN2,为输入、中断模式。
2、配置好后,添加btn2的按键回调函数,
if (&sl_button_btn2 == handle) {
if (sl_button_get_state(handle) == SL_SIMPLE_BUTTON_PRESSED) {
xResult = xEventGroupSetBitsFromISR(xbtn_events, BTN2_PRESSED, &xHigherPriorityTaskWoken);
if (xResult == pdPASS) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
else {
app_log_error("Set BTN2_PRESSED event failed\r\n");
}
}
else {
xResult = xEventGroupClearBitsFromISR(xbtn_events, BTN2_PRESSED);
if (xResult == pdFAIL) {
app_log_error("Clear BTN2_PRESSED event failed\r\n");
}
}
}
如果是btn2按下,测发送事件freertos的事件BTN2_PRESSED。
3、接着在km_btn_task任务中实现对事件的处理:
case (BTN2_PRESSED):
app_log_debug("BTN2 pressed\r\n");
sc = app_timer_stop(&btn_press_timer);
app_assert_status(sc);
km_status = KM_MUOSE_RIGHT; // scroll down
sl_bt_external_signal(1);
其发送KM_MOUSE_RIGHT事件,然后在
case sl_bt_evt_system_external_signal_id:
if (notification_enabled == 1 && km_status != KM_IDLE) {
if (km_status == KM_SEND_STRING) {
send_eetree_string();
}
else if (km_status == KM_SCROLL_UP) {
scroll_with_distance(0x01);
}
else if(km_status == KM_MUOSE_RIGHT)
{
mouse_with_distance(0x0A);
mouse_with_distance(0x08);
}
else { // KM_SCROLL_DOWN
scroll_with_distance(0xFF);
}
app_log_info("Key report %d was sent\r\n", km_status);
km_status = KM_IDLE;
}
在这里我调用鼠标右键的发送函数:
mouse_with_distance(0x0A);//为按右键
mouse_with_distance(0x08);//为释放键
那么0x0A是如何得到的,我们就需要了解鼠标发送的组装:
根据上面的说明,我组装右键按为下:0b00001010 即为0x08;
我们需要在发送右键后再发送释放的事件。即发送0x00;
其余代码就不详细列举了。
【实现效果】
1、下载载后程,在电脑上可以看到键盘鼠标外设:
按下btn0实现鼠标上滚,按下btn1可实现下滚,两个按键按下后可以实现输出“EETRE.CN"。外接一个按键,可以实现鼠标右键的点击。
【心理体会】
经过两个月的学习,初始学会了使用Simplicity Studio开发XG24,然后也学习了蓝牙的回调,键盘鼠标的描述符等等。学会了自己用XG24来开发蓝牙设备。