应用说明

简介

本项目的编译环境是基于make来编译的。

蓝牙本身有各种各样的场景的业务,不同业务场景的功能需求各不相同,为了满足不同场景的需要,并确保资源有限的设备在特殊场景下使用足够少的Code Size和RAM Size,项目提供了丰富的配置参数,来实现相关功能代码的开关。

为了方便用户理解本项目,编译是以example中的例程为中心的,每个例程都有不同的配置参数和业务代码。通过学习这些例程,便于用户快速掌握如何使用本项目的API和Kconfig配置系统。

本文具体对应用设计和实现进行说明。

本项目所有的应用都在example目录中,通常的目录结构如下:

<home>/example/xxx
 ├── prj.conf(应用层Kconfig配置)
 ├── build.mk(编译系统所需)
 └── app_main.c(程序代码)

prj.conf:Kconfig持久化设计,关于Kconfig的设计可以看 配置系统(Kconfig),每个应用可以根据需要单独配置,修改这个会最终影响生成的autoconfig.h,也就是蓝牙协议栈编译出来的Code和RAM。

build.mk:根目录的makefile会引用这个文件,这里需要配置应用所需编译的文件和头文件路径。

app_main.c:应用层代码,文件名并没有特别指定,但是需要在build.mk中引用。

新增应用

按照下面的流程可以创建一个你自己的应用

创建应用目录

到example目录下,创建一个自己感兴趣的目录即可,可以通过GUI创建当然也可以通过命令行创建。

如:创建一个名为app_test的工程。后续已这个例程为例进行介绍。

mkdir app_test

创建app_main.c

app_test目录下,创建app_main.c即可,由于本项目主要实现蓝牙功能,所以所有例程都会开启蓝牙功能,项目的入口函数也不再是main函数,而是bt_ready函数。

bt_ready:是蓝牙开启结束的回调函数,在porting中的main.c调用。

app_polling_work:是平台提供给应用层的轮询函数,注意这里不能写while(1),只能写一些轮询业务,在porting中的main.c中有个while(1)函数周期调用。

printk:是平台提供的日志打印函数,所有的日志都需要使用本接口,不能使用printf,不然一些日志记录之类的功能可能不好用。

#include <logging/bt_log_impl.h>

void bt_ready(int err)
{
    if (err)
    {
        printk("Bluetooth init failed (err %d)\n", err);
        return;
    }

    printk("Bluetooth initialized\n");
}

void app_polling_work(void)
{
    return;
}

创建build.mk

这个比较简单,就是配置SRC代码路径、INCLUDE头文件路径以及LIB库路径(应用层一般不建议使用,由于平台异构,lib使用会比较麻烦。)

需要注意的是APP_PATH,这个是编译系统提供的当前应用的路径,也就是example/app_test,新加的路径必须加这个根路径参数。

# define source directory
SRC          += $(APP_PATH)

# define include directory
INCLUDE      += $(APP_PATH)

# define lib directory
LIB          +=

创建prj.conf

这个要理解需要去看懂 配置系统(Kconfig),默认情况下我们只需要开启日志功能和蓝牙功能即可,其他的可以通过make menuconfig,修改配置后,用D保存差异,生成在根目录的defconfig文件就是所需的prj.conf

CONFIG_BT=y
CONFIG_BT_DEBUG_LOG=y

编译应用

上述操作完成后,目录结构如下:

<home>/example/app_test
 ├── prj.conf
 ├── build.mk
 └── app_main.c

这时可以通过调整make传入的APP参数来使用本应用程序,键入该指令就可以完成对APP的编译。

make all APP=app_test

运行应用

生成的执行文件在output目录下,直接执行即可,Windows下的执行效果如下:

可以看到启动蓝牙后,就什么都没干了。

PS D:\worksplace\github\zephyr_polling> .\output\main.exe
[2022-12-07 11:06:12.194] [0xb8bc] display_devices(), idVendor: 0xbda, idProduct: 0x8771
[2022-12-07 11:06:12.196] [0xb8bc] display_devices(), idVendor: 0x10d7, idProduct: 0xb012
[2022-12-07 11:06:12.198] [0xb8bc] display_devices(), idVendor: 0xa12, idProduct: 0x1
[2022-12-07 11:06:12.200] [0xb8bc] success: set configuration #1
[2022-12-07 11:06:12.202] [0xb8bc] success: claim_interface #0
[2022-12-07 11:06:12.205] [0x77dc] tx_process_loop
[2022-12-07 11:06:12.205] [0xca8c] rx_evt_process_loop
[2022-12-07 11:06:12.205] [0xb8bc] hci_driver_open()
[2022-12-07 11:06:12.209] [0xb8bc] I: (bt_hci_core)hci_init():3220: work start.
[2022-12-07 11:06:12.211] [0xb8bc] CMD =>  00 FC 13 C2 02 00 09 00 02 00 03 70 00 00 F2 00 01 00 08 00 01 00
[2022-12-07 11:06:13.241] [0xb8bc] EVT <=  FF 13 C2 01 00 09 00 02 00 03 70 00 00 F2 00 01 00 08 00 01 00
[2022-12-07 11:06:13.247] [0xb8bc] CMD =>  00 FC 19 C2 02 00 0C 00 08 00 03 70 00 00 01 00 04 00 08 00 44 00 66 55 33 00 22 11
[2022-12-07 11:06:13.254] [0xb8bc] EVT <=  FF 19 C2 01 00 0C 00 08 00 03 70 00 00 01 00 04 00 08 00 44 00 66 55 33 00 22 11
[2022-12-07 11:06:13.258] [0xb8bc] CMD =>  00 FC 13 C2 02 00 09 00 09 00 02 40 00 00 00 00 00 00 00 00 00 00
[2022-12-07 11:06:13.258] [0xc71c] reset_driver_process, wait usb reboot.
[2022-12-07 11:06:13.264] [0x77dc] error tx:
libusb0-dll:err [control_msg] sending control message failed, win error: 连到系统上的设备没有发挥作用。


[2022-12-07 11:06:18.270] [0xc71c] reset_driver_process, usb reboot ready.
[2022-12-07 11:06:18.272] [0xca8c] error reading:
libusb0-dll:err [submit_async] submitting request failed, win error: 设备不识别此命令。

[2022-12-07 11:06:18.381] [0xca8c] rx_evt_process_loop end
[2022-12-07 11:06:18.384] [0xc71c] display_devices(), idVendor: 0xa12, idProduct: 0x1
[2022-12-07 11:06:18.386] [0xc71c] display_devices(), idVendor: 0xbda, idProduct: 0x8771
[2022-12-07 11:06:18.388] [0xc71c] display_devices(), idVendor: 0x10d7, idProduct: 0xb012
[2022-12-07 11:06:18.390] [0xc71c] success: set configuration #1
[2022-12-07 11:06:18.392] [0xc71c] success: claim_interface #0
[2022-12-07 11:06:18.394] [0xb8bc] CMD =>  03 0C 00
[2022-12-07 11:06:18.394] [0x130c] tx_process_loop
[2022-12-07 11:06:18.394] [0xbddc] rx_evt_process_loop
[2022-12-07 11:06:19.559] [0xb8bc] CMD =>  02 10 00
[2022-12-07 11:06:19.568] [0xb8bc] EVT <=  0E 44 01 02 10 00 FF FF FF 03 FE FF FF FF FF FF FF FF F3 0F E8 FE 3F F7 83 FF 1C 00 00 00 61 F7 FF FF 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[2022-12-07 11:06:19.578] [0xb8bc] CMD =>  03 20 00
[2022-12-07 11:06:19.581] [0xb8bc] EVT <=  0E 0C 01 03 20 00 01 00 00 00 00 00 00 00
[2022-12-07 11:06:19.583] [0xb8bc] CMD =>  6D 0C 02 01 00
[2022-12-07 11:06:19.587] [0xb8bc] EVT <=  0E 04 01 6D 0C 00[2022-12-07 11:06:19.590] [0xb8bc] CMD =>  01 20 08 02 00 00 00 00 00 00 00
[2022-12-07 11:06:19.595] [0xb8bc] EVT <=  0E 04 01 01 20 00
[2022-12-07 11:06:19.596] [0xb8bc] CMD =>  01 0C 08 00 80 00 02 00 00 00 20
[2022-12-07 11:06:19.600] [0xb8bc] EVT <=  0E 04 01 01 0C 00
[2022-12-07 11:06:19.602] [0xb8bc] CMD =>  09 10 00
[2022-12-07 11:06:19.608] [0xb8bc] EVT <=  0E 0A 01 09 10 00 66 55 44 33 22 11
[2022-12-07 11:06:19.610] [0xb8bc] I: (bt_hci_core)hci_init_end():3195: work end.
[2022-12-07 11:06:19.612] [0xb8bc] I: (bt_hci_core)bt_dev_show_info():2998: Identity: 11:22:33:44:55:66 (public)
[2022-12-07 11:06:19.614] [0xb8bc] I: (bt_hci_core)bt_dev_show_info():3030: HCI: version 4.0 (0x06) revision 0x22bb, manufacturer 0x000a
[2022-12-07 11:06:19.617] [0xb8bc] I: (bt_hci_core)bt_dev_show_info():3033: LMP: version 4.0 (0x06) subver 0x22bb
[2022-12-07 11:06:19.623] [0xb8bc] Bluetooth initialized

应用调试

编译系统除了生成执行文件外,还会生成反编译文件以及map文件,以便用户对代码进行分析。

生成的目录结构如下所示。

<home>/output
 ├── log
 │   ├── log.cfa
 │   └── log.txt
 ├── main.bin
 ├── main.exe
 ├── main.lst
 └── main.map

GDB调试

编译的工程都开启了-g选项,所以可以直接通过GDB进行调试,也可以自己根据具体需要使用VSCODE或者其他IDE来用GDB调试。

日志调试

在交互终端中,可以会打印核心的日志信息。

此外除了实时打印外,项目还会在output/log目录下保存日志文件。其中终端显示的交互日志保存为log.txt;交互的hci数据包会保存为btsnoop格式的文件log.cfa

默认打印的日志只是开启了INFO级别以上的日志,如果需要看所有日志,可以在prj.conf中加入,初期学习的时候可以通过开启CONFIG_BT_LOG_LEVEL_DBG=y来显示所有日志来学习代码。