HiSpark嵌入式第一课

打印 上一主题 下一主题

主题 929|帖子 929|积分 2802

海思

海思简介

海思 - 使能万物互联的智能终端
华为芯片相干产品矩阵



  • 开辟工具

    • 仓颉:编程语言
    • HiSpark Studio:面向智能装备开辟的一站式集成开辟情况
    • DevEco Studio:开辟 HarmonyOS 应用及元服务的集成开辟情况
    • CodeArts:一站式软件开辟平台

  • 相干芯片

    • 麒麟:手机及手表SoC芯片
    • 巴龙:基带芯片
    • 昇腾:AI处理器
    • 鲲鹏:PC及服务器芯片
    • 天罡:通信基站
    • 凌霄:路由器芯片
    • 鸿鹄:电视芯片

  • 体系

    • 鸿蒙操纵体系
    • 欧拉操纵体系

  • 大模型

    • 盘古


海思社区

海思社区
提供丰富的技术资源和开辟支持,通过技术论坛可以学习其他开辟者的开辟经验。

HiSparkStudio

下载与安装



  • tools/HiSparkStudio工具下载及安装
fbb_ws63-master仓库



  • 参考资料: fbb_ws63: fbb_ws63代码仓为支持ws63和ws63e解决方案SDK
  • 下载


  • 认识fbb_ws63-master仓库

    • 总体认识:

      • fbb_ws63代码仓为支持ws63和ws63e解决方案SDK,该SDK包从统一开辟平台FBB(Family Big Box,统一开辟框架,统一API)构建而来,在该平台上开辟的应用很容易被移植到其他星闪解决方案上,有效低落开辟者门槛,收缩开辟周期,支持开辟者快速开辟星闪产品。

    • docs文件夹

      • 用户指南手册
      • board文件夹:

        • 主要是各种官方资料,包罗资料手册和IO复用关系表

      • hardware文件夹:

        • BearPi-Pico_H3863(本次实验所使用的开辟板)、HiHope_NearLink_DK_WS63E_V03两块开辟板的硬件原理图

      • pic文件夹:

        • 存放实验阐明的截图(可忽略)


    • src文件夹

      • SDK源码目录

        • 本处只介绍几个常用的文件/文件夹,具体介绍请参考:“fbb_ws63-master\fbb_ws63-master\docs\board\WS63V100 SDK开辟情况搭建 用户指南_03.pdf”
        • application:应用层代码,其中包含的demo是常用的参考文件

          • samples:存放demo

            • bt:蓝牙、星闪demo
            • peripheral:常见外设demo
            • radar:雷达demo(ws63E才有雷达功能)
            • WiFi:WiFi demo

          • ws63:整个代码的主函数入口

        • include:API头文件存放目录
        • build.log:编译的日志,方便检察报错信息


    • tools文件夹

      • 开辟工具及情况搭建指南

    • vendor文件夹

      • 存放各类开辟板以及开辟者贡献的demo


Hello HiSpark! demo介绍及实验



  • 认识HiSparkStudio工作情况

  • 新建工程

    • 新建工程

    • 配置工程信息

    • 开始编译(编译过程较慢,需耐心等候)

    • 编译完成

    • 若编译失败,可能是情况配置未完成或CMakeLists.txt配置有题目,请重新检查,或参考下列毗连

      • 星闪 Windows情况无法编译及题目解决FAQ


  • Hello HiSpark编程

    • 新建文件夹

      • 在application/samples文件夹下新建一个文件夹,命名为demo(可自行更改命名,但后续的命名需要同步跟进),在demo文件夹中新建一个文件,命名为Hello_HiSpark,在Hello_HiSpark新建两个文件夹,分别命名为inc、src,并在src中新建文件Hello_HiSpark.c


    • 构建代码框架

      • 介绍

        • 我们编写代码使用的体系是LiteOS,其是华为面向物联网(IoT)范畴开辟的轻量级实时操纵体系,在构建代码的时候需要遵循其框架。
        • 其代码基本框架是:函数任务 --> 为函数任务创建线程并注册优先级 --> 体系初始化和启动
        • 以代码application\samples\peripheral\blinky\blinky_demo.c为例进行介绍

          • static int blinky_task(const char *arg):函数主任务(可以理解为我们学习C语言时写的小工程中的main函数的作用)
          • static void blinky_entry(void):在该函数中为上述函数static int blinky_task(const char *arg)注册线程,并设置其线程优先级
          • app_run:调用该函数初始化并运行blinky_entry函数,完成对blinky_task函数的运行


      • 编写代码(以移植代码为方式)

        • 复制blinky_demo.c到我们新建的文件Hello_HiSpark.c中

        • 修改代码

          • 将所有blinky相干的代码替换成HiSpark(红方框中的代码均需要进行修改)

          • 在LiteOS中,变量界说之后没有使用是会报错的,故变量界说之后若没有使用需要使用unused()对其进行处理

        • 最终代码:
          1.         #include "pinctrl.h"
          2.         #include "gpio.h"
          3.         #include "soc_osal.h"
          4.         #include "app_init.h"
          5.         #define HISPARK_TASK_PRIO          24
          6.         #define HISPARK_TASK_STACK_SIZE    0x1000
          7.         static int HiSpark_task(const char *arg)
          8.         {
          9.                 unused(arg);
          10.             while (1) {
          11.         osal_msleep(500);
          12.         osal_printk("Hello HiSpark.\r\n");
          13.         }
          14.     return 0;
          15.         }
          16.         static void HiSpark_entry(void)
          17.         {
          18.             osal_task *task_handle = NULL;
          19.             osal_kthread_lock();
          20.             task_handle = osal_kthread_create((osal_kthread_handler)HiSpark_task, 0, "HiSparkTask", HISPARK_TASK_STACK_SIZE);
          21.             if (task_handle != NULL) {
          22.         osal_kthread_set_priority(task_handle, HISPARK_TASK_PRIO);
          23.         osal_kfree(task_handle);
          24.     }
          25.     osal_kthread_unlock();
          26.         }
          27.        
          28.         /* Run the HiSpark_entry. */
          29.         app_run(HiSpark_entry);
          30.         ```
          复制代码

      • 配置编译路径

        • CMake(简朴介绍)

          • CMake(Cross-Platform Make)是一个跨平台的构建体系生成器,用于管理和主动化软件的构建过程。它通过编写配置文件(通常是 CMakeLists.txt),生成适合不同编译器和开辟情况的构建文件(如 Makefile、Visual Studio 解决方案、Xcode 项目等)。CMake 广泛用于 C、C++、Fortran 等语言的项目,但也可以用于其他语言的构建管理。
          • CmakeLists.txt 中的变量寄义

            • COMPONENT_NAME:当前组件名称,如“Hello_HiSpark”
            • SOURCES:当前组件的 C 文件列表,其中CMAKE_CURRENT_SOURCE_DIR变量标识当前CMakeLists.txt地点的路径
            • PUBLIC_HEADER:当前组件需要对外提供的头文件的路径
            • PRIVATE_HEADER:当前组件内部的头文件搜索路径
            • PRIVATE_DEFINES:当前组件内部生效的宏界说
            • PUBLIC_DEFINES:当前组件需要对外提供的宏界说
            • COMPONENT_PUBLIC_CCFLAGS:当前组件需要对外提供的编译选项
            • COMPONENT_CCFLAGS:当前组件内部生效的编译选项

          • CMake构建的是一个编译链,需要顺着这条链路不断进行设置,对于在HiSparkStudi中,我们可以通过一下步骤将编译链指向我们想要编译的文件,我们将通过指向blinky_demo.c这个代码为案例进行分析

            • 在最外层的CMakeLists.txt中,可以找到下列内容
              1. add_subdirectory_if_exist(application)
              2. add_subdirectory_if_exist(bt)
              3. add_subdirectory_if_exist(bootloader)
              4. add_subdirectory_if_exist(kernel)
              5. add_subdirectory_if_exist(drivers)
              6. add_subdirectory_if_exist(middleware)
              7. add_subdirectory_if_exist(open_source)
              8. add_subdirectory_if_exist(protocol)
              9. add_subdirectory_if_exist(test)
              10. add_subdirectory_if_exist(include)
              11. add_subdirectory_if_exist(vendor)
              复制代码

              • 该内容主要是检查每个子目录(括号中的内容)是否存在,存在则将其作为子项包含到当前的构建体系中,其中就包含了application文件夹

            • 进入到application文件夹的CMakeLists.txt中,可以找到add_subdirectory_if_exist(samples),此时编译链会指向samples文件夹
            • 进入到samples文件夹中的CMakeLists.txt中,可以找到下列内容
              1. if(DEFINED CONFIG_ENABLE_PERIPHERAL_SAMPLE)
              2. add_subdirectory_if_exist(peripheral)
              3. endif()
              4. install_sdk("${CMAKE_CURRENT_SOURCE_DIR}/peripheral" "*")
              复制代码

              • 判断语句的作用是如果界说了该配置选项,则调用add_subdirectory_if_exist(peripheral)加载其子模块
              • install_sdk:是将该目录中的文件安装到SDK中,方便编译时调用

            • 进入到peripheral文件夹中,对于其中的CMakeLists.txt文件分析如下

              • 代码:set(SOURCES "${SOURCES}" "${CMAKE_CURRENT_SOURCE_DIR}/blinky_demo.c" PARENT_SCOPE)
              • "${SOURCES}":表现当前 SOURCES 变量的值
              • "${CMAKE_CURRENT_SOURCE_DIR}/blinky_demo.c":表现将文件路径 ${CMAKE_CURRENT_SOURCE_DIR}/blinky_demo.c 添加到 SOURCES 列表中
              • PARENT_SCOPE:set 下令的一个选项,表现将变量 SOURCES 的值设置到父作用域

            • 到此为止,对于CMake中的配置已经完成,但是其中另有一个关键点我们并没有完成配置,上述中提到了在判断语句中只有界说了配置选项才可以或许加载相对应的模块,怎样界说与选择这个配置选项,就需要用到宏编译了


        • 宏编译(简朴介绍)

          • 在打开的文件夹中,除了CMakeLists.txt文件,我们还可以发现存在另一个文件Kconfig,该文件对于主要是辅助CMakeLists.txt配置编译路径,同时提供交互式的配置界面即体系配置界面,按照配置blinky为例进行阐明

          • 下面将按照CMakeLists.txt的顺序来认识相应的Kconfig内容

            • 在最外层的Kconfig中,找到关于application的内容
              1. menu "Application"
              2.      comment "Config the application."
              3.      osource "application/Kconfig"
              4. endmenu
              复制代码

              • 该内容直接界说了Application这个选项,并将其指向"application/Kconfig"中


            • 按照其指引,打开application文件夹,找到Kconfig文件,找到关于samples的内容
              1. config SAMPLE_ENABLE
              2.      bool
              3.      prompt "Enable Sample."
              4.      default n
              5.      help
              6.          This option means support Samples.
              7.        
              8.         if SAMPLE_ENABLE
              9.         osource "application/samples/Kconfig"
              10.         endif
              复制代码

              • config SAMPLE_ENABLE:界说一个名为SAMPLE_ENABLE的配置选项
              • osource "application/samples/Kconfig":如果SAMPLE_ENABLE被启用,则加载路径application/samples/Kconfig中的配置选项
              • 点击application


            • 打开samples文件夹,找到Kconfig文件,找到关于peripheral的内容
              1. config ENABLE_PERIPHERAL_SAMPLE
              2.      bool
              3.      prompt "Enable the Sample of peripheral."
              4.      default n
              5.      depends on SAMPLE_ENABLE
              6. help
              7.      This option means enable the sample of peripheral.
              8.      
              9. if ENABLE_PERIPHERAL_SAMPLE
              10. osource "application/samples/peripheral/Kconfig"
              11. endif
              复制代码

              • 理解方式同上
              • depends on SAMPLE_ENABLE:依赖关系,depends on SAMPLE_ENABLE被选择才会显示本内容
              • 点击samples
                - 打开peripheral文件夹,打开Kconfig文件,找到blinky内容如下:
              1. config SAMPLE_SUPPORT_BLINKY
              2.      bool
              3.         prompt "Support BLINKY Sample."
              4.      default n
              5.      depends on ENABLE_PERIPHERAL_SAMPLE
              6.      help
              7.          This option means support BLINKY Sample.
              8. if SAMPLE_SUPPORT_BLINKY
              9. menu "Blinky Sample Configuration"
              10.      osource "application/samples/peripheral/blinky/Kconfig"
              11. endmenu
              12. endif
              复制代码
                          

              • 理解方式同上
              • 点击peripheral


            • 在blinky文件夹中的Kconfig内容如下:
              1. config BLINKY_PIN
              2. int
              3. prompt "Choose blinky pin."
              4. default 2
              复制代码

              • 上述代码的功能主要是提供体系配置中提供Choose blinky pin的功能






        • 构建Hello_HiSpark.c的编译链

          • 采用一层一层配置编译链的方式,因为我们创建的demo文件夹是在samples文件夹中,所以我们只需要配置samples–>demo–>hello_HiSpark这三层的CMakeLists与Kconfig文件即可,但又因我们的工程较小,可以不使用宏编译的方式,故我们只在samples中配置demo的宏编译,今后不配置宏编译,直接去掉判断语句,使用add_subdirectory_if_exist()将对应的文件添加到编译路径中即可
          • 配置samples文件夹中的CMakeLists.txt和Kconfig

            • 在CMakeLists.txt中添加下列内容
              1. if(DEFINED CONFIG_ENABLE_DEMO_SAMPLE)
              2. add_subdirectory_if_exist(demo)
              3. endif()
              4. install_sdk("${CMAKE_CURRENT_SOURCE_DIR}/demo" "*")
              复制代码

              • 位置如图


            • 在Kconfig中添加下列内容
              1. config ENABLE_DEMO_SAMPLE
              2.      bool
              3.      prompt "Enable the Sample of DEMO."
              4.      default n
              5.      depends on SAMPLE_ENABLE
              6. help
              7.      This option means enable the sample of DEMO.
              复制代码

              • 位置如图所示

              • 因为demo中不需要通过Kconfig进行配置,故在此不需要通过osource加载demo中的内容
              • 打开体系配置,可以看到已经有了demo的选项,但是无法继续展开,点击左上角save生存,这样编译路径已经指向demo文件夹来了



          • 配置demo中的CMakeLists.txt

            • 在demo文件夹中新建一个文件,将其命名为CMakeLists.txt,在其中编写下列内容
              1. add_subdirectory_if_exist(hello_HiSpark)
              2. set(SOURCES "${SOURCES}" PARENT_SCOPE)    # C 文件链接
              3. set(PUBLIC_HEADER "${PUBLIC_HEADER}" PARENT_SCOPE)  # 头文件链接
              复制代码

              • 如需更改其他文件夹,只需要修改add_subdirectory_if_exist()括号中的文件夹名称即可


          • 配置Hello_HiSpark中的CMakeLists.txt

            • 在Hello_HiSpark文件夹中新建一个文件,将其命名为CMakeLists.txt,在其中编写下列内容
              1. add_subdirectory_if_exist(src)
              2. set(SOURCES "${SOURCES}" PARENT_SCOPE)
              3. set(PUBLIC_HEADER "${PUBLIC_HEADER}" "${CMAKE_CURRENT_SOURCE_DIR}/inc" PARENT_SCOPE)  #将整个inc包含进编译路径中
              复制代码

          • 配置src中的CMakeLists.txt

            • 在src文件夹中新建一个文件,将其命名为CMakeLists.txt,在其中编写下列内容
              1. set(SOURCES "${SOURCES}"
              2.      "${CMAKE_CURRENT_SOURCE_DIR}/Hello_HiSpark.c"  #指向对应的 .c 文件
              3.      PARENT_SCOPE)
              复制代码
            • 如需添加其他.c文件,只需要复制第二行的内容到第三行,修改文件名即可




    • 编译代码

      • 点击编译,出现如图所示信息则表明编译乐成


    • 检查CH340驱动

      • 用Type-C线毗连电脑与开辟板,开辟板上红灯亮起则表明已毗连乐成

      • 打开电脑装备管理器

      • 打开端口,检察是否辨认出开辟板

      • 安装CH340驱动

        • 下载CH340驱动

          • 下载链接:CH340驱动
          • 下载驱动

          • 点击安装


          • 安装乐成

          • 重新回到装备管理器中检察是否可以或许显示出带有CH340的装备



    • 烧录

      • 点击工程配置,找到程序加载板块,修改端口为在装备管理器中看的带有CH340字样的端口

      • 点击程序加载,根据指示按下开辟板上的RST按键,烧录完成出现下列信息


    • 实验现象

      • 打开监视器,修改端口与波特率,开始监视,可以看到每隔500ms监视器就会打印一个Hello HiSpark,实验现象符合代码逻辑



  • 训练

    • 点灯各人:参考下列函数接口,复制Hello_HiSpark文件夹,在Hello_HiSpark程序基础上进行修改,配置好相干参数,点亮一个LED灯(杜邦线毗连LED灯与开辟板引脚)
    • 参考头文件:"pinctrl.h"、"gpio.h"

    • 参考接口:
      1. /*
      2.   * @brief  设置引脚复用模式。
      3.   * @param  [in] pin io,参考 @ref pin_t 。
      4.   * @param  [in] mode 复用模式,参考 @ref pin_mode_t 。
      5.   * @retval ERRCODE_SUCC 成功。
      6.   * @retval Other        失败,参考 @ref errcode_t 。
      7.   * @endif
      8.   */
      9. errcode_t uapi_pin_set_mode(pin_t pin, pin_mode_t mode);
      10. /*
      11.   * @brief  设置GPIO的输入输出方向函数。
      12.   * @param  [in] pin IO, 参考 @ref pin_t 。
      13.   * @param  [in] dir 输入输出方向, 参考 @ref gpio_direction_t 。
      14.   * @retval ERRCODE_SUCC 成功。
      15.   * @retval Other        失败,参考 @ref errcode_t 。
      16.   * @endif
      17.   */
      18. errcode_t uapi_gpio_set_dir(pin_t pin, gpio_direction_t dir);
      19. /*
      20.   * @brief  设置GPIO的输出状态。
      21.   * @param  [in]  pin IO, 参考 @ref pin_t 。
      22.   * @param  [in]  level GPIO 输出设置为高或低, 参考 @ref gpio_level_t 。
      23.   * @retval ERRCODE_SUCC 成功。
      24.   * @retval Other        失败,参考 @ref errcode_t 。
      25.   * @endif
      26.   */
      27. errcode_t uapi_gpio_set_val(pin_t pin, gpio_level_t level);
      复制代码

星闪

星闪技术介绍



  • 参考资料:星闪技术 | 海思官网
  • 什么是星闪?

    • 星闪(NearLink),中国原生的新一代无线短距通信技术。面向万物互联期间,星闪引入关键技术和创新理念,赋予智能终端新的毗连方式。与传统短距传输技术方案相比,星闪在功耗、速度、覆盖范围和毗连性能全面领先,可以在智能终端、智能家居、智能汽车、智能制造等各类细分场景下实现更极致的用户体验。2023年,星闪商用元年全面开启。

  • 星闪的优势

    • 低时延:星闪传输时延是传统无线技术的1/30,同等时间提供30倍的交互信息,由毫秒级迈进微秒级。星闪技术加持下,无线鼠标刷新率由传统的125-1000Hz提升至4000Hz、手写笔刷新率由传统的120Hz提升至360Hz,丝般流通,跬步不离,极大进步使用体验。
    • 高吞吐:星闪在装备间信息传输最高速率是传统无线技术(2Mbps)的6倍(12Mbps),能耗只有之前的60%,不但工作效率进步,质量还大幅提升。星闪高速率完美支持在无线耳机上的无损音频传输,让灌音室级风致音频体验成为现实。
    • 高并发:与传统技术直连装备只可毗连数个相比,星闪最大可到达百量级,避免了传统技术组网本领弱、同步实现难度高导致的跑马灯现象,深度赋能智能家居IOT等范畴。
    • 高可靠:初次将“Polar码”等前沿技术应用于短距无线通信,信息传输可靠性由传统技术的90%提升至99.99%。
    • 抗干扰:星闪初次将5G Polar码技术用于短距通信,共同干扰避让机制,抗干扰本领比传统无线技术提升7dB。
    • 精定位:星闪将定位精度由传统无线技术的米级提升到分米级,依托领先的测距算法,有效降服人体遮挡、情况吸收和反射等因素叠加,解决测距结果不稳固、反复解闭锁的痛点。

  • 原理分析

    • 星闪技术上手体验:万物互联的根基?_哔哩哔哩_bilibili

ws63


星闪测距

通信基本知识



  • 参考资料:

    • https://kdkce.edu.in/writereaddata/fckimagefile/ADBMS%20Unit%202%20(1).pdf
    • 计算机网络之应用层(客户/服务器(C/S)模型)_体系内的服务端和客户端的层级布局模态化配置-CSDN博客

  • C/S模型

    • 客户端/服务端模型,是一种集中式的网络架构,客户端(Client)通常是终端装备(如个人计算机、智能手机等),它通过网络协议向服务端发起请求来获取资源或服务;服务端(Server)则提供相应的服务,通常具有较强的计算本领和存储本领,根据客户端的请求提供所需的服务或资源(如文件、数据、计算本领等)
    • client:发起请求、提供用户界面、本地数据处理及结果展示
    • server:监听请求、处理逻辑、管理资源(如数据库、文件)及并发相应多客户端

星闪毗连编程

API

  星闪测距编程

RSSI(简朴介绍)



  • 参考资料

    • http://www.edatop.com/down/hwrf/mprf/MP-RF-19971.pdf
    • https://www.macrothink.org/journal/index.php/npa/article/download/527/400
    • 基于RSSI室内定位算法介绍_蓝牙rssi指纹定位-CSDN博客

  • 介绍

    • Received Signal Strength Indication:指吸收信号强度指示,用来权衡无线装备吸收到的信号强度,通常以dBm为单位,对于蓝牙与星闪而言,一样平常是-1dBm ~ -100dBm,数值越小信号越弱,一样平常数值的变化与距离的变化有关且为非线性关系,但仍受到其他复杂因素的影响,如阻挡、温湿度、多径效应等

  • 测距原理

    • 根据不同距离RSSI数值的不同确定不同的距离

  • 代码实现

    • 读取RSSI的接口在sle_connection_callbacks_t中
      1.   /**
      2.    * @defgroup bluetooth_sle_adv API
      3.    * @ingroup
      4.    * @{
      5.    */
      6.   
      7.   #ifndef SLE_SERVER_ADV_H
      8.   #define SLE_SERVER_ADV_H
      9.   
      10.   #include "sle_ssap_server.h"
      11.   
      12.   /* 广播ID */
      13.   #define SLE_ADV_HANDLE_DEFAULT                    1
      14.   
      15.   /**
      16.    * @if Eng
      17.    * @brief Definitaion of BLE ADV 通用广播结构.
      18.    * @else
      19.    * @brief SLE 广播普通数据结构。
      20.    * @endif
      21.    */
      22.   struct sle_adv_common_value {
      23.       uint8_t length;
      24.       uint8_t type;
      25.       uint8_t value;
      26.   };
      27.   
      28.   /**
      29.    * @if Eng
      30.    * @brief Definitaion of BLE ADV Channel mapping.
      31.    * @else
      32.    * @brief SLE 广播信道映射。
      33.    * @endif
      34.    */
      35.   typedef enum {
      36.       SLE_ADV_CHANNEL_MAP_77                 = 0x01,
      37.       SLE_ADV_CHANNEL_MAP_78                 = 0x02,
      38.       SLE_ADV_CHANNEL_MAP_79                 = 0x04,
      39.       SLE_ADV_CHANNEL_MAP_DEFAULT            = 0x07
      40.   } sle_adv_channel_map;
      41.   
      42.   /**
      43.    * @if Eng
      44.    * @brief Definitaion of SLE ADV Data Type.
      45.    * @else
      46.    * @brief SLE 广播数据类型
      47.    * @endif
      48.    */
      49.   typedef enum {
      50.       SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL                              = 0x01,   /*!< 发现等级 */
      51.       SLE_ADV_DATA_TYPE_ACCESS_MODE                                  = 0x02,   /*!< 接入层能力 */
      52.       SLE_ADV_DATA_TYPE_SERVICE_DATA_16BIT_UUID                      = 0x03,   /*!< 标准服务数据信息 */
      53.       SLE_ADV_DATA_TYPE_SERVICE_DATA_128BIT_UUID                     = 0x04,   /*!< 自定义服务数据信息 */
      54.       SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_16BIT_SERVICE_UUIDS         = 0x05,   /*!< 完整标准服务标识列表 */
      55.       SLE_ADV_DATA_TYPE_COMPLETE_LIST_OF_128BIT_SERVICE_UUIDS        = 0x06,   /*!< 完整自定义服务标识列表 */
      56.       SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_16BIT_SERVICE_UUIDS       = 0x07,   /*!< 部分标准服务标识列表 */
      57.       SLE_ADV_DATA_TYPE_INCOMPLETE_LIST_OF_128BIT_SERVICE_UUIDS      = 0x08,   /*!< 部分自定义服务标识列表 */
      58.       SLE_ADV_DATA_TYPE_SERVICE_STRUCTURE_HASH_VALUE                 = 0x09,   /*!< 服务结构散列值 */
      59.       SLE_ADV_DATA_TYPE_SHORTENED_LOCAL_NAME                         = 0x0A,   /*!< 设备缩写本地名称 */
      60.       SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME                          = 0x0B,   /*!< 设备完整本地名称 */
      61.       SLE_ADV_DATA_TYPE_TX_POWER_LEVEL                               = 0x0C,   /*!< 广播发送功率 */
      62.       SLE_ADV_DATA_TYPE_SLB_COMMUNICATION_DOMAIN                     = 0x0D,   /*!< SLB通信域域名 */
      63.       SLE_ADV_DATA_TYPE_SLB_MEDIA_ACCESS_LAYER_ID                    = 0x0E,   /*!< SLB媒体接入层标识 */
      64.       SLE_ADV_DATA_TYPE_EXTENDED                                     = 0xFE,   /*!< 数据类型扩展 */
      65.       SLE_ADV_DATA_TYPE_MANUFACTURER_SPECIFIC_DATA                   = 0xFF    /*!< 厂商自定义信息 */
      66.   } sle_adv_data_type;
      67.   
      68.   
      69.   /**
      70.    * @if Eng
      71.    * @brief  sle adv data config.
      72.    * @attention  NULL
      73.    * @retval ERRCODE_SLE_SUCCESS    Excute successfully
      74.    * @retval ERRCODE_SLE_FAIL       Execute fail
      75.    * @par Dependency:
      76.    * @li NULL
      77.    * @else
      78.    * @brief  sle广播数据配置。
      79.    * @attention  NULL
      80.    * @retval ERRCODE_SLE_SUCCESS    执行成功
      81.    * @retval ERRCODE_SLE_FAIL       执行失败
      82.    * @par 依赖:
      83.    * @li NULL
      84.    * @endif
      85.    */
      86.   static errcode_t sle_uuid_server_adv_init(void);
      87.   
      88.   
      89.   
      90.   /* Service UUID */
      91.   #define SLE_UUID_SERVER_SERVICE        0xABCD
      92.   
      93.   /* Property UUID */
      94.   #define SLE_UUID_SERVER_NTF_REPORT     0x1122
      95.   
      96.   /* Property Property */
      97.   #define SLE_UUID_TEST_PROPERTIES  (SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE)
      98.   
      99.   /* Descriptor Property */
      100.   #define SLE_UUID_TEST_DESCRIPTOR   (SSAP_PERMISSION_READ | SSAP_PERMISSION_WRITE)
      101.   
      102.   /**
      103.    * @if Eng
      104.    * @brief  SLE uuid server inir.
      105.    * @attention  NULL
      106.    * @retval ERRCODE_SLE_SUCCESS    Excute successfully
      107.    * @retval ERRCODE_SLE_FAIL       Execute fail
      108.    * @par Dependency:
      109.    * @li sle_ssap_server.h
      110.    * @else
      111.    * @brief  SLE UUID服务器初始化。
      112.    * @attention  NULL
      113.    * @retval ERRCODE_SLE_SUCCESS    执行成功
      114.    * @retval ERRCODE_SLE_FAIL       执行失败
      115.    * @par 依赖:
      116.    * @li sle_ssap_server.h
      117.    * @endif
      118.    */
      119.   errcode_t sle_uuid_server_init(void);
      120.   
      121.   /**
      122.    * @if Eng
      123.    * @brief  send data to peer device by uuid on uuid server.
      124.    * @attention  NULL
      125.    * @param  [in]  value  send value.
      126.    * @param  [in]  len    Length of send value。
      127.    * @retval ERRCODE_SLE_SUCCESS    Excute successfully
      128.    * @retval ERRCODE_SLE_FAIL       Execute fail
      129.    * @par Dependency:
      130.    * @li sle_ssap_server.h
      131.    * @else
      132.    * @brief  通过uuid server 发送数据给对端。
      133.    * @attention  NULL
      134.    * @retval ERRCODE_SLE_SUCCESS    执行成功
      135.    * @retval ERRCODE_SLE_FAIL       执行失败
      136.    * @par 依赖:
      137.    * @li sle_ssap_server.h
      138.    * @endif
      139.    */
      140.   errcode_t sle_uuid_server_send_report_by_uuid(uint8_t *data, uint16_t len);
      141.   
      142.   /**
      143.    * @if Eng
      144.    * @brief  send data to peer device by handle on uuid server.
      145.    * @attention  NULL
      146.    * @param  [in]  value  send value.
      147.    * @param  [in]  len    Length of send value。
      148.    * @retval ERRCODE_SLE_SUCCESS    Excute successfully
      149.    * @retval ERRCODE_SLE_FAIL       Execute fail
      150.    * @par Dependency:
      151.    * @li sle_ssap_server.h
      152.    * @else
      153.    * @brief  通过uuid server 发送数据给对端。
      154.    * @attention  NULL
      155.    * @retval ERRCODE_SLE_SUCCESS    执行成功
      156.    * @retval ERRCODE_SLE_FAIL       执行失败
      157.    * @par 依赖:
      158.    * @li sle_ssap_server.h
      159.    * @endif
      160.    */
      161.   errcode_t sle_uuid_server_send_report_by_handle(const uint8_t *data, uint8_t len);
      162.   #endif
      复制代码
    • 代码

      • server端、client端均可配置读取RSSI,不相互干扰,就算另一方未在sle_connection_callbacks_t布局体中配置read_rssi_cb也是可以直接读取的,后续处理的代码均是在client端进行,可自由配置


      • 代码
        1.   #include "app_init.h"
        2.   #include "watchdog.h"
        3.   #include "tcxo.h"
        4.   #include "systick.h"
        5.   #include "los_memory.h"
        6.   #include "securec.h"
        7.   #include "errcode.h"
        8.   #include "osal_addr.h"
        9.   #include "soc_osal.h"
        10.   #include "common_def.h"
        11.   
        12.   #include "sle_common.h"
        13.   #include "sle_errcode.h"
        14.   #include "sle_ssap_server.h"
        15.   #include "sle_connection_manager.h"
        16.   #include "sle_device_discovery.h"
        17.   #include "sle_transmition_manager.h"
        18.   #include "nv.h"
        19.   
        20.   #include "sle_server.h"
        21.   
        22.   //线程参数
        23.   #define DISTANCE_DEFAULT_KTHREAD_SIZE 0x2000
        24.   #define DISTANCE_DEFAULT_KTHREAD_PROI 26
        25.   
        26.   //本地设备参数
        27.   #define NAME_MAX_LENGTH 30
        28.   static uint8_t mac[SLE_ADDR_LEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};
        29.   static uint8_t sle_local_name[NAME_MAX_LENGTH] = "sle_connect_server";
        30.   //数据包长度
        31.   #define PKT_DATA_LEN 600
        32.   static unsigned char data[PKT_DATA_LEN];
        33.   //确保为小端格式存储
        34.   #define encode2byte_little(_ptr, data) \
        35.       do { \
        36.           *(uint8_t *)((_ptr) + 1) = (uint8_t)((data) >> 8); \
        37.           *(uint8_t *)(_ptr) = (uint8_t)(data); \
        38.       } while (0)
        39.   
        40.   //设备公开
        41.   #define SLE_CONN_INTV_MIN_DEFAULT                 0xA       //连接最小调度间隔12.5ms,单位125us
        42.   #define SLE_CONN_INTV_MAX_DEFAULT                 0xA       //连接最大调度间隔12.5ms,单位125us
        43.   #define SLE_ADV_INTERVAL_MIN_DEFAULT              0xC8      //连接调度间隔25ms,单位125us
        44.   #define SLE_ADV_INTERVAL_MAX_DEFAULT              0xC8      //超时时间5000ms,单位10ms
        45.   #define SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT      0x1F4     //超时时间4990ms,单位10ms
        46.   #define SLE_CONN_MAX_LATENCY                      0x1F3     //广播发送功率
        47.   #define SLE_ADV_TX_POWER  20                                //最大广播数据长度
        48.   #define SLE_ADV_DATA_LEN_MAX                      251     
        49.   
        50.   //设备连接
        51.   
        52.   #define DISTANCE_DEFAULT_CONN_INTERVAL 0xA0
        53.   #define DISTANCE_DEFAULT_TIMEOUT_MULTIPLIER 0x1f4
        54.   #define DISTANCE_DEFAULT_SCAN_INTERVAL 400
        55.   #define DISTANCE_DEFAULT_SCAN_WINDOW 20
        56.   //蓝牙地址索引
        57.   #define BT_INDEX_4     4
        58.   #define BT_INDEX_5     5
        59.   #define BT_INDEX_0     0
        60.   
        61.   static uint16_t g_conn_id = 0;
        62.   
        63.   // SSAP 参数
        64.   #define OCTET_BIT_LEN 8     //8比特长度
        65.   #define UUID_LEN_2     2    //两个字节
        66.   static char g_sle_uuid_app_uuid[UUID_LEN_2] = {0x0, 0x0};
        67.   static char g_sle_property_value[OCTET_BIT_LEN] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
        68.   
        69.   //传输参数
        70.   #define DEFAULT_SLE_DISTANCE_DATA_LEN 1500
        71.   #define DEFAULT_SLE_DISTANCE_MTU_SIZE 1500
        72.   static uint8_t g_server_id = 0;
        73.   static uint16_t g_service_handle = 0;
        74.   static uint16_t g_property_handle = 0;
        75.   static uint8_t sle_uuid_base[] = { 0x37, 0xBE, 0xA8, 0x80, 0xFC, 0x70, 0x11, 0xEA, 0xB7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
        76.   extern uint8_t gle_tx_acb_data_num_get(void);
        77.   
        78.   
        79.   
        80.   /* 检查 BTC 功率档位 */
        81.   
        82.   void sle_distance_server_set_nv(void)
        83.   {
        84.       uint16_t nv_value_len = 0;
        85.       uint8_t nv_value = 0;
        86.   
        87.       //读取指定NV数据项的值
        88.       uapi_nv_read(0x20A0, sizeof(uint16_t), &nv_value_len, &nv_value);
        89.       if (nv_value != 7) {     // 7:btc功率档位
        90.           nv_value = 7;       // 7:btc功率档位
        91.   
        92.           //写入NV数据项
        93.           uapi_nv_write(0x20A0, (uint8_t *)&(nv_value), sizeof(nv_value));
        94.       }
        95.       osal_printk("[distance server] The value of nv is set to %d.\r\n", nv_value);
        96.   }
        97.   
        98.   
        99.   
        100.   /* 本地设备参数 */
        101.   
        102.   //设置本端 MAC 地址
        103.   void sle_set_local_addr_init(void)
        104.   {
        105.       sle_addr_t addr = {0};
        106.       addr.type = 0;
        107.       memcpy_s(addr.addr, SLE_ADDR_LEN, mac, SLE_ADDR_LEN);
        108.       sle_set_local_addr(&addr);
        109.   }
        110.   
        111.   //设置本端名称
        112.   static uint16_t sle_set_adv_local_name(uint8_t *adv_data, uint16_t max_len)
        113.   {
        114.       errno_t ret;
        115.       uint8_t index = 0;
        116.   
        117.       uint8_t *local_name = sle_local_name;
        118.       uint8_t local_name_len = (uint8_t)strlen((char *)local_name);
        119.       for (uint8_t i = 0; i < local_name_len; i++) {
        120.           osal_printk("local_name[%d] = 0x%02x\r\n", i, local_name[i]);
        121.       }
        122.   
        123.       adv_data[index++] = local_name_len + 1;
        124.       adv_data[index++] = SLE_ADV_DATA_TYPE_COMPLETE_LOCAL_NAME;
        125.       ret = memcpy_s(&adv_data[index], max_len - index, local_name, local_name_len);
        126.       if (ret != EOK) {
        127.           osal_printk("memcpy fail\r\n");
        128.           return 0;
        129.       }
        130.       return (uint16_t)index + local_name_len;
        131.   }
        132.   
        133.   
        134.   
        135.   /* 设备公开 */
        136.   
        137.   //设备公开参数
        138.   static int sle_set_default_announce_param(void)
        139.   {   
        140.       //初始化
        141.       sle_announce_param_t param = {0};
        142.       param.announce_mode = SLE_ANNOUNCE_MODE_CONNECTABLE_SCANABLE;               //公开类型
        143.       param.announce_handle = SLE_ADV_HANDLE_DEFAULT;                             //公开句柄
        144.       param.announce_gt_role = SLE_ANNOUNCE_ROLE_T_CAN_NEGO;                      //gt角色协商指示
        145.       param.announce_level = SLE_ANNOUNCE_LEVEL_NORMAL;                           //发现等级
        146.       param.announce_channel_map = SLE_ADV_CHANNEL_MAP_DEFAULT;                   //设备公开信道
        147.       param.announce_interval_min = SLE_ADV_INTERVAL_MIN_DEFAULT;                 //最小设备公开周期
        148.       param.announce_interval_max = SLE_ADV_INTERVAL_MAX_DEFAULT;                 //最大设备公开周期
        149.       param.conn_interval_min = SLE_CONN_INTV_MIN_DEFAULT;                        //连接间隔最小取值
        150.       param.conn_interval_max = SLE_CONN_INTV_MAX_DEFAULT;                        //连接间隔最大取值
        151.       param.conn_max_latency = SLE_CONN_MAX_LATENCY;                              //最大休眠连接间隔
        152.       param.conn_supervision_timeout = SLE_CONN_SUPERVISION_TIMEOUT_DEFAULT;      //最大超时时间
        153.       param.announce_tx_power = SLE_ADV_TX_POWER;                                 //广播发射攻略
        154.       param.own_addr.type = 0;                                                    //本端地址
        155.       memcpy_s(param.own_addr.addr, SLE_ADDR_LEN, mac, SLE_ADDR_LEN);
        156.       
        157.       //设置参数
        158.       return sle_set_announce_param(param.announce_handle, &param);               
        159.   }
        160.   
        161.   void sle_enable_cbk(errcode_t status)
        162.   {
        163.       osal_printk("sle enable status:%02x\r\n", status);
        164.   }
        165.   
        166.   void sle_announce_enable_cbk(uint32_t announce_id, errcode_t status)
        167.   {
        168.       osal_printk("sle announce enable id:%02x, state:%02x\r\n", announce_id, status);
        169.   }
        170.   
        171.   void sle_announce_terminal_cbk(uint32_t announce_id)
        172.   {
        173.       osal_printk("sle announce terminal id:%02x\r\n", announce_id);
        174.   }
        175.   
        176.   void sle_announce_disable_cbk(uint32_t announce_id, errcode_t status)
        177.   {
        178.       osal_printk("sle announce disable id:%02x, state:%02x\r\n", announce_id, status);
        179.   }
        180.   
        181.   //设备公开回调函数
        182.   void sle_announce_register_cbks(void)
        183.   {
        184.       sle_announce_seek_callbacks_t seek_cbks = {0};
        185.       seek_cbks.sle_enable_cb = sle_enable_cbk;                   //SLE协议栈使能回调函数
        186.       seek_cbks.announce_enable_cb = sle_announce_enable_cbk;     //设备公开使能回调函数
        187.       seek_cbks.announce_terminal_cb = sle_announce_terminal_cbk; //设备公开停止回调函数
        188.       seek_cbks.announce_disable_cb = sle_announce_disable_cbk;   //设备公开关闭回调函数
        189.       sle_announce_seek_register_callbacks(&seek_cbks);           //将回调函数注册到协议层
        190.   }
        191.   
        192.   //设置广播数据
        193.   static uint16_t sle_set_adv_data(uint8_t *adv_data)
        194.   {
        195.       size_t len = 0;
        196.       uint16_t idx = 0;
        197.       errno_t  ret = 0;
        198.   
        199.       //填充设备发现级别
        200.       len = sizeof(struct sle_adv_common_value);
        201.       struct sle_adv_common_value adv_disc_level = {
        202.           .length = len - 1,
        203.           .type = SLE_ADV_DATA_TYPE_DISCOVERY_LEVEL,
        204.           .value = SLE_ANNOUNCE_LEVEL_NORMAL,
        205.       };
        206.   
        207.       ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_disc_level, len);
        208.       if (ret != EOK) {
        209.           osal_printk("adv_disc_level memcpy fail\r\n");
        210.           return 0;
        211.       }
        212.       idx += len;
        213.   
        214.       //填充访问模式
        215.       len = sizeof(struct sle_adv_common_value);
        216.       struct sle_adv_common_value adv_access_mode = {
        217.           .length = len - 1,
        218.           .type = SLE_ADV_DATA_TYPE_ACCESS_MODE,
        219.           .value = 0,
        220.       };
        221.       ret = memcpy_s(&adv_data[idx], SLE_ADV_DATA_LEN_MAX - idx, &adv_access_mode, len);
        222.       if (ret != EOK) {
        223.           osal_printk("memcpy fail\r\n");
        224.           return 0;
        225.       }
        226.       idx += len;
        227.       return idx;
        228.   }
        229.   
        230.   
        231.   static uint16_t sle_set_scan_response_data(uint8_t *scan_rsp_data)
        232.   {
        233.       uint16_t idx = 0;
        234.       errno_t ret;
        235.       size_t scan_rsp_data_len = sizeof(struct sle_adv_common_value);
        236.   
        237.       struct sle_adv_common_value tx_power_level = {
        238.           .length = scan_rsp_data_len - 1,
        239.           .type = SLE_ADV_DATA_TYPE_TX_POWER_LEVEL,
        240.           .value = SLE_ADV_TX_POWER,
        241.       };
        242.       ret = memcpy_s(scan_rsp_data, SLE_ADV_DATA_LEN_MAX, &tx_power_level, scan_rsp_data_len);
        243.       if (ret != EOK) {
        244.           osal_printk("sle scan response data memcpy fail\r\n");
        245.           return 0;
        246.       }
        247.       idx += scan_rsp_data_len;
        248.   
        249.       /* set local name */
        250.       idx += sle_set_adv_local_name(&scan_rsp_data[idx], SLE_ADV_DATA_LEN_MAX - idx);
        251.       return idx;
        252.   }
        253.   
        254.   static int sle_set_default_announce_data(void)
        255.   {
        256.       errcode_t ret;
        257.       uint8_t announce_data_len = 0;
        258.       uint8_t seek_data_len = 0;
        259.       sle_announce_data_t data = {0};
        260.       uint8_t adv_handle = SLE_ADV_HANDLE_DEFAULT;
        261.       uint8_t announce_data[SLE_ADV_DATA_LEN_MAX] = {0};
        262.       uint8_t seek_rsp_data[SLE_ADV_DATA_LEN_MAX] = {0};
        263.   
        264.       osal_printk("set adv data default\r\n");
        265.       
        266.       //设置广播数据
        267.       announce_data_len = sle_set_adv_data(announce_data);
        268.       data.announce_data = announce_data;
        269.       data.announce_data_len = announce_data_len;
        270.       //设置扫描相应数据
        271.       seek_data_len = sle_set_scan_response_data(seek_rsp_data);
        272.       data.seek_rsp_data = seek_rsp_data;
        273.       data.seek_rsp_data_len = seek_data_len;
        274.       //设置参数
        275.       ret = sle_set_announce_data(adv_handle, &data);
        276.       if (ret == ERRCODE_SLE_SUCCESS) {
        277.           osal_printk("[SLE DD SDK] set announce data success.");
        278.       } else {
        279.           osal_printk("[SLE DD SDK] set adv param fail.");
        280.       }
        281.       return ERRCODE_SLE_SUCCESS;
        282.   }
        283.   
        284.   static errcode_t sle_uuid_server_adv_init(void)
        285.   {
        286.       osal_printk("sle_uuid_server_adv_init in\r\n");
        287.   
        288.       sle_announce_register_cbks();
        289.       sle_set_default_announce_param();
        290.       sle_set_default_announce_data();
        291.       sle_start_announce(SLE_ADV_HANDLE_DEFAULT);         //开始设备公开
        292.       osal_printk("sle_uuid_server_adv_init out\r\n");
        293.       return ERRCODE_SLE_SUCCESS;
        294.   }
        295.   
        296.   // ssap 信息交换设置
        297.   void sle_ssaps_set_info(void)
        298.   {
        299.       ssap_exchange_info_t info = {0};
        300.       info.mtu_size = DEFAULT_SLE_DISTANCE_MTU_SIZE;
        301.       info.version = 1;
        302.       ssaps_set_info(g_server_id, &info);
        303.   }
        304.   
        305.   /* 设备连接 */
        306.   
        307.   //连接参数
        308.   void sle_distance_connect_param_init(void)
        309.   {
        310.       sle_default_connect_param_t param = {0};
        311.       param.enable_filter_policy = 0;                             //过滤功能
        312.       param.gt_negotiate = 0;
        313.       param.initiate_phys = 1;
        314.       param.max_interval = DISTANCE_DEFAULT_CONN_INTERVAL;
        315.       param.min_interval = DISTANCE_DEFAULT_CONN_INTERVAL;
        316.       param.scan_interval = DISTANCE_DEFAULT_SCAN_INTERVAL;
        317.       param.scan_window = DISTANCE_DEFAULT_SCAN_WINDOW;
        318.       param.timeout = DISTANCE_DEFAULT_TIMEOUT_MULTIPLIER;
        319.       sle_default_connection_param_set(&param);
        320.   }
        321.   
        322.   //连接状态改变回调函数
        323.   static void sle_connect_state_changed_cbk(uint16_t conn_id, const sle_addr_t *addr,
        324.       sle_acb_state_t conn_state, sle_pair_state_t pair_state, sle_disc_reason_t disc_reason)
        325.   {
        326.       osal_printk("[distance server] connect state changed conn_id:0x%02x, conn_state:0x%x, pair_state:0x%x, \
        327.           disc_reason:0x%x\r\n", conn_id, conn_state, pair_state, disc_reason);
        328.       osal_printk("[distance server] connect state changed addr:%02x:**:**:**:%02x:%02x\r\n",
        329.           addr->addr[BT_INDEX_0], addr->addr[BT_INDEX_4], addr->addr[BT_INDEX_5]);
        330.       g_conn_id = conn_id;
        331.       sle_connection_param_update_t parame = {0};
        332.       parame.conn_id = conn_id;
        333.       parame.interval_min = DISTANCE_DEFAULT_CONN_INTERVAL;
        334.       parame.interval_max = DISTANCE_DEFAULT_CONN_INTERVAL;
        335.       parame.max_latency = 0;
        336.       parame.supervision_timeout = DISTANCE_DEFAULT_TIMEOUT_MULTIPLIER;
        337.       if (conn_state ==  SLE_ACB_STATE_CONNECTED) {
        338.           sle_update_connect_param(&parame);
        339.       } else if (conn_state == SLE_ACB_STATE_DISCONNECTED) {
        340.           sle_start_announce(SLE_ADV_HANDLE_DEFAULT);
        341.       }
        342.   }
        343.   
        344.   //连接参数请求更新回调函数
        345.   void sle_sample_update_req_cbk(uint16_t conn_id, errcode_t status, const sle_connection_param_update_req_t *param)
        346.   {
        347.       unused(conn_id);
        348.       unused(status);
        349.       osal_printk("[ssap server] sle_sample_update_req_cbk interval_min:%02x, interval_max:%02x\n",
        350.           param->interval_min, param->interval_max);
        351.   }
        352.   
        353.   //连接参数更新完成回调函数
        354.   void sle_sample_update_cbk(uint16_t conn_id, errcode_t status, const sle_connection_param_update_evt_t *param)
        355.   {
        356.       unused(status);
        357.       osal_printk("[ssap server] updat state changed conn_id:%d, interval = %02x\n", conn_id, param->interval);
        358.   }
        359.   
        360.   //配对完成回调函数
        361.   static void sle_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
        362.   {
        363.       osal_printk("[distance server] pair complete conn_id:%02x, status:%x\r\n",
        364.           conn_id, status);
        365.       osal_printk("[distance server] pair complete addr:%02x:**:**:**:%02x:%02x\r\n",
        366.           addr->addr[BT_INDEX_0], addr->addr[BT_INDEX_4], addr->addr[BT_INDEX_5]);
        367.   }
        368.   
        369.   static void sle_conn_register_cbks(void)
        370.   {
        371.       sle_connection_callbacks_t conn_cbks = {0};
        372.       conn_cbks.connect_state_changed_cb = sle_connect_state_changed_cbk;     //连接状态改变回调函数
        373.       conn_cbks.connect_param_update_req_cb = sle_sample_update_req_cbk;      //连接参数请求更新回调函数
        374.       conn_cbks.connect_param_update_cb = sle_sample_update_cbk;              //连接参数更新完成回调函数
        375.       conn_cbks.pair_complete_cb = sle_pair_complete_cbk;                     //配对完成回调函数
        376.       sle_connection_register_callbacks(&conn_cbks);                          //将回调函数结构体注册到协议层
        377.   }
        378.   
        379.   
        380.   
        381.   /* SSAP server */
        382.   
        383.   //启动服务回调函数
        384.   static void ssaps_start_service_cbk(uint8_t server_id, uint16_t handle, errcode_t status)
        385.   {
        386.       osal_printk("[distance server] start service cbk server_id:%d, handle:%d, status:%d\r\n",
        387.           server_id, handle, status);
        388.   }
        389.   
        390.   //mtu 大小更新回调函数
        391.   static void ssaps_mtu_changed_cbk(uint8_t server_id, uint16_t conn_id,  ssap_exchange_info_t *mtu_size,
        392.       errcode_t status)
        393.   {
        394.       osal_printk("[distance server] ssaps write request cbk server_id:%d, conn_id:%d, mtu_size:%d, status:%d\r\n",
        395.           server_id, conn_id, mtu_size->mtu_size, status);
        396.   }
        397.   
        398.   //发送信息
        399.   static errcode_t sle_uuid_server_send_report_by_handle_id(uint8_t *data, uint16_t len, uint16_t connect_id)
        400.   {
        401.       ssaps_ntf_ind_t param = {0};
        402.       param.handle = g_property_handle;
        403.       param.type = SSAP_PROPERTY_TYPE_VALUE;
        404.       param.value = data;
        405.       param.value_len = len;
        406.   
        407.       //向对端发送通知
        408.       ssaps_notify_indicate(g_server_id, connect_id, &param);
        409.       return ERRCODE_SLE_SUCCESS;
        410.   }
        411.   
        412.   static uint8_t sle_flow_ctrl_flag(void)
        413.   {
        414.       return gle_tx_acb_data_num_get();
        415.   }
        416.   
        417.   static void send_data_thread_function(void)
        418.   {
        419.       //设置最大传输长度
        420.       sle_set_data_len(g_conn_id, DEFAULT_SLE_DISTANCE_DATA_LEN);
        421.       
        422.       osal_printk("code: GFSK, PHY 1MHZ, power: 20dbm \r\n");
        423.       int i = 0;
        424.       while (1) {
        425.           if (sle_flow_ctrl_flag() > 0) {
        426.               i++;
        427.               //填充数据
        428.               data[0] = (i >> 8) & 0xFF;  
        429.               data[1] = i & 0xFF;
        430.   
        431.               //发送数据包
        432.               sle_uuid_server_send_report_by_handle_id(data, PKT_DATA_LEN, g_conn_id);
        433.           }
        434.   
        435.       }
        436.   }
        437.   
        438.   //收到远端读请求回调函数
        439.   static void ssaps_read_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_read_cb_t *read_cb_para,
        440.       errcode_t status)
        441.   {
        442.       osal_printk("[distance server] ssaps read request cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n",server_id, conn_id, read_cb_para->handle, status);
        443.       
        444.       //创建新线程完成发送任务
        445.       osal_task *task_handle = NULL;
        446.       osal_kthread_lock();
        447.       task_handle = osal_kthread_create((osal_kthread_handler)send_data_thread_function,0, "RadarTask", DISTANCE_DEFAULT_KTHREAD_SIZE);
        448.       osal_kthread_set_priority(task_handle, DISTANCE_DEFAULT_KTHREAD_PROI + 1);
        449.       if (task_handle != NULL) {
        450.           osal_kfree(task_handle);
        451.       }
        452.       osal_kthread_unlock();
        453.       printf("kthread success\r\n");
        454.   }
        455.   
        456.   //收到远端写请求回调函数
        457.   static void ssaps_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para,
        458.       errcode_t status)
        459.   {
        460.       osal_printk("[distance server] ssaps write request cbk server_id:%d, conn_id:%d, handle:%d, status:%d\r\n",
        461.           server_id, conn_id, write_cb_para->handle, status);
        462.   }
        463.   
        464.   //注册 ssap server 回调
        465.   static void sle_ssaps_register_cbks(void)
        466.   {
        467.       ssaps_callbacks_t ssaps_cbk = {0};
        468.       ssaps_cbk.start_service_cb = ssaps_start_service_cbk;   //启动服务回调函数
        469.       ssaps_cbk.mtu_changed_cb = ssaps_mtu_changed_cbk;       //mtu 大小更新回调函数
        470.       ssaps_cbk.read_request_cb = ssaps_read_request_cbk;     //收到远端读请求回调函数
        471.       ssaps_cbk.write_request_cb = ssaps_write_request_cbk;   //收到远端写请求回调函数
        472.       ssaps_register_callbacks(&ssaps_cbk);
        473.   }
        474.   
        475.   //复制128-bit UUID 到结构体
        476.   static void sle_uuid_set_base(sle_uuid_t *out)
        477.   {
        478.       (void)memcpy_s(out->uuid, SLE_UUID_LEN, sle_uuid_base, SLE_UUID_LEN);
        479.       out->len = UUID_LEN_2;
        480.   }
        481.   
        482.   //将 uuid 转换为小端格式
        483.   static void sle_uuid_setu2(uint16_t u2, sle_uuid_t *out)
        484.   {
        485.       sle_uuid_set_base(out);
        486.       out->len = UUID_LEN_2;
        487.       encode2byte_little(&out->uuid[14], u2);
        488.   }
        489.   
        490.   static errcode_t sle_uuid_server_service_add(void)
        491.   {
        492.       errcode_t ret;
        493.       sle_uuid_t service_uuid = {0};
        494.       sle_uuid_setu2(SLE_UUID_SERVER_SERVICE, &service_uuid);
        495.       //添加一个ssap服务
        496.       ret = ssaps_add_service_sync(g_server_id, &service_uuid, 1, &g_service_handle);
        497.       if (ret != ERRCODE_SLE_SUCCESS) {
        498.           osal_printk("[distance server] sle uuid add service fail, ret:%x\r\n", ret);
        499.           return ERRCODE_SLE_FAIL;
        500.       }
        501.       return ERRCODE_SLE_SUCCESS;
        502.   }
        503.   
        504.   static errcode_t sle_uuid_server_property_add(void)
        505.   {
        506.       errcode_t ret;
        507.       ssaps_property_info_t property = {0};
        508.       ssaps_desc_info_t descriptor = {0};
        509.       uint8_t ntf_value[] = {0x01, 0x0};
        510.   
        511.       //添加特征信息
        512.       property.permissions = SLE_UUID_TEST_PROPERTIES;    //特征权限
        513.       sle_uuid_setu2(SLE_UUID_SERVER_NTF_REPORT, &property.uuid);     //SSAP 特征 UUID
        514.       property.value = osal_vmalloc(sizeof(g_sle_property_value));    //响应的数据
        515.       property.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_NOTIFY;    //操作指示
        516.       if (property.value == NULL) {
        517.           osal_printk("[distance server] sle property mem fail\r\n");
        518.           return ERRCODE_SLE_FAIL;
        519.       }
        520.       if (memcpy_s(property.value, sizeof(g_sle_property_value), g_sle_property_value,
        521.           sizeof(g_sle_property_value)) != EOK) {
        522.           osal_vfree(property.value);
        523.           osal_printk("[distance server] sle property mem cpy fail\r\n");
        524.           return ERRCODE_SLE_FAIL;
        525.       }
        526.   
        527.       //添加一个ssap特征
        528.       ret = ssaps_add_property_sync(g_server_id, g_service_handle, &property,  &g_property_handle);
        529.       if (ret != ERRCODE_SLE_SUCCESS) {
        530.           osal_printk("[distance server] sle uuid add property fail, ret:%x\r\n", ret);
        531.           osal_vfree(property.value);
        532.           return ERRCODE_SLE_FAIL;
        533.       }
        534.       
        535.       descriptor.permissions = SLE_UUID_TEST_DESCRIPTOR;      //特征权限
        536.       descriptor.operate_indication = SSAP_OPERATE_INDICATION_BIT_READ | SSAP_OPERATE_INDICATION_BIT_WRITE;       //操作指示
        537.       descriptor.type = SSAP_DESCRIPTOR_USER_DESCRIPTION;     //描述符类型
        538.       descriptor.value = ntf_value;       //数据
        539.       descriptor.value_len = sizeof(ntf_value);
        540.   
        541.       //添加一个ssap特征描述符
        542.       ret = ssaps_add_descriptor_sync(g_server_id, g_service_handle, g_property_handle, &descriptor);
        543.       if (ret != ERRCODE_SLE_SUCCESS) {
        544.           osal_printk("[distance server] sle uuid add descriptor fail, ret:%x\r\n", ret);
        545.           osal_vfree(property.value);
        546.           return ERRCODE_SLE_FAIL;
        547.       }
        548.       osal_vfree(property.value);
        549.       return ERRCODE_SLE_SUCCESS;
        550.   }
        551.   
        552.   //创建 server 实体
        553.   static errcode_t sle_uuid_server_add(void)
        554.   {
        555.       errcode_t ret;
        556.       sle_uuid_t app_uuid = {0};
        557.   
        558.       osal_printk("[distance server] sle uuid add service in\r\n");
        559.       app_uuid.len = sizeof(g_sle_uuid_app_uuid);     //设置 uuid 长度
        560.       if (memcpy_s(app_uuid.uuid, app_uuid.len, g_sle_uuid_app_uuid, sizeof(g_sle_uuid_app_uuid)) != EOK) {
        561.           return ERRCODE_SLE_FAIL;
        562.       }
        563.       //注册ssap服务端
        564.       ssaps_register_server(&app_uuid, &g_server_id);
        565.   
        566.       //若添加服务失败则注销服务端
        567.       if (sle_uuid_server_service_add() != ERRCODE_SLE_SUCCESS) {
        568.           ssaps_unregister_server(g_server_id);
        569.           return ERRCODE_SLE_FAIL;
        570.       }
        571.   
        572.       //若添加特征失败则注销服务端
        573.       if (sle_uuid_server_property_add() != ERRCODE_SLE_SUCCESS) {
        574.           ssaps_unregister_server(g_server_id);
        575.           return ERRCODE_SLE_FAIL;
        576.       }
        577.       
        578.       osal_printk("[distance server] sle uuid add service, server_id:%x, service_handle:%x, property_handle:%x\r\n",
        579.           g_server_id, g_service_handle, g_property_handle);
        580.       
        581.       //开始一个SSAP服务
        582.       ret = ssaps_start_service(g_server_id, g_service_handle);
        583.       if (ret != ERRCODE_SLE_SUCCESS) {
        584.           osal_printk("[distance server] sle uuid add service fail, ret:%x\r\n", ret);
        585.           return ERRCODE_SLE_FAIL;
        586.       }
        587.       osal_printk("[distance server] sle uuid add service out\r\n");
        588.       return ERRCODE_SLE_SUCCESS;
        589.   }
        590.   
        591.   
        592.   
        593.   
        594.   /* 初始化distance server */
        595.   
        596.   errcode_t sle_distance_server_init(void)
        597.   {
        598.       uapi_watchdog_disable();   
        599.       enable_sle();
        600.       printf("sle enable\r\n");
        601.       sle_distance_server_set_nv();
        602.       sle_conn_register_cbks();
        603.       sle_ssaps_register_cbks();
        604.       sle_uuid_server_add();
        605.       sle_uuid_server_adv_init();
        606.       sle_ssaps_set_info();
        607.       sle_distance_connect_param_init();
        608.       sle_set_local_addr_init();
        609.       osal_printk("[distance server] init ok\r\n");
        610.       return ERRCODE_SLE_SUCCESS;
        611.   }
        612.   
        613.   
        614.   
        615.   /* 函数入口 */
        616.   int sle_distance_init(void)
        617.   {
        618.       //初始化全局变量 data
        619.       for (int i = 0; i < PKT_DATA_LEN; i++) {
        620.           data[i] = 'A';
        621.           data[PKT_DATA_LEN - 1] = '\0';  //最后一个字节填充空字符,作为字符串的结尾标识
        622.       }
        623.       osal_msleep(1000);  //休眠1s
        624.       sle_distance_server_init();
        625.       
        626.       
        627.       return 0;
        628.   }
        629.   
        630.   /* 线程与优先级设置 */
        631.   static void sle_distance_entry(void)
        632.   {
        633.       osal_task *task_handle1 = NULL;
        634.       osal_kthread_lock();
        635.       task_handle1= osal_kthread_create((osal_kthread_handler)sle_distance_init, 0, "distance", DISTANCE_DEFAULT_KTHREAD_SIZE);
        636.       if (task_handle1 != NULL) {
        637.           osal_kthread_set_priority(task_handle1, DISTANCE_DEFAULT_KTHREAD_PROI);
        638.           osal_kfree(task_handle1);
        639.       }
        640.       osal_kthread_unlock();
        641.   }
        642.   
        643.   app_run(sle_distance_entry);
        复制代码


  • 读取效果

    • 两块板子的位置固定不变,可读取RSSI如下

    • 由输出的结果可知:RSSI的数值是不稳固的,而且波动是比较大的,图中的数据的极差为                                                   15                                          15                           15,是非常大的,就算舍弃最大值和最小值,其他的数据也是不停飘忽不定的,这种数据是无法直接使用的
    • 经过简朴的数学处理,将我们吸收到的数据进行处理之后绘制成曲线,可以更加清楚地感受RSSI值的不稳固性

    • 由上述所示,RSSI数值是不稳固的,这个不稳固除了硬件本身的题目之外,另有一部分是由于情况造成的噪声,这种不稳固性是可以通过数学处理淘汰的,在此我们选择卡尔曼滤波进行处理

卡尔曼滤波(简朴介绍)



  • 参考资料

    • 滤波笔记一:卡尔曼滤波(Kalman Filtering)详解-CSDN博客
    • 卡尔曼滤波算法 C语言实现 示例_卡尔曼滤波c语言实现-CSDN博客
    • 怎样通俗直白地理解卡尔曼滤波算法? - 知乎

  • 原理

    • 递归数据处理算法,主要用于动态体系的状态估计
    • 通过结合体系模型和测量数据来淘汰估计毛病,贝叶斯滤波器的原理,通过猜测和更新两个步骤来不断优化状态估计
    • 说人话:把传感器观测值和理论猜测值做加权平均;既不完全相信传感器,也不完全相信理论模型
    • 多维卡尔曼滤波模型与一维简化卡尔曼滤波模型介绍

公式类型多维卡尔曼滤波(矩阵形式)一维卡尔曼滤波(标量形式)简化阐明状态猜测方程                                                                                    x                                           ^                                                      k                                        −                                                  =                                     A                                                                  x                                           ^                                                                     k                                           −                                           1                                                                +                                     B                                                   u                                        k                                                           \hat{x}_k^- = \mathbf{A}\hat{\mathbf{x}}_{k-1} + \mathbf{B}\mathbf{u}_k                              x^k−​=Ax^k−1​+Buk​                                                                                    x                                           ^                                                      k                                        −                                                  =                                     A                                                                  x                                           ^                                                                     k                                           −                                           1                                                                +                                     B                                                   u                                        k                                                           \mathbf{\hat{x}}_k^- = \mathbf{A}\mathbf{\hat{x}}_{k-1} + \mathbf{B}\mathbf{u}_k                              x^k−​=Ax^k−1​+Buk​多维状态向量                                                        X                                              X                              X 退化为标量                                                   X                                          X                           X,矩阵                                                        A                                     ,                                     B                                              A,B                              A,B 退化为标量                                                   A                                  ,                                  B                                          A,B                           A,B协方差猜测方程                                                                     P                                        k                                        −                                                  =                                     A                                                   P                                                       k                                           −                                           1                                                                              A                                        T                                                  +                                     Q                                              \mathbf{P}_k^- = \mathbf{A}\mathbf{P}_{k-1}\mathbf{A}^T + \mathbf{Q}                              Pk−​=APk−1​AT+Q                                                                     P                                        k                                        −                                                  =                                                   A                                        2                                                                P                                                       k                                           −                                           1                                                                +                                     Q                                              \mathbf{P}_k^- = \mathbf{A}^2\mathbf{P}_{k-1} + \mathbf{Q}                              Pk−​=A2Pk−1​+Q协方差矩阵                                                        P                                     ,                                     Q                                              P,Q                              P,Q,退化为标量                                                   P                                  ,                                  Q                                          P,Q                           P,Q,矩阵乘法简化为平方运算卡尔曼增益计算                                                                     K                                        k                                                  =                                                   P                                        k                                        −                                                                H                                        T                                                  (                                     H                                                   P                                        k                                        −                                                                H                                        T                                                  +                                     R                                                   )                                                       −                                           1                                                                         \mathbf{K}_k = \mathbf{P}_k^-\mathbf{H}^T(\mathbf{H}\mathbf{P}_k^-\mathbf{H}^T + \mathbf{R})^{-1}                              Kk​=Pk−​HT(HPk−​HT+R)−1                                                                     K                                        k                                                  =                                                                                  P                                              k                                              −                                                          H                                                                                     H                                              T                                                                          P                                              k                                              −                                                          H                                           +                                           R                                                                         \mathbf{K}_k = \frac{\mathbf{P}_k^-\mathbf{H}}{\mathbf{H}^T\mathbf{P}_k^-\mathbf{H} + \mathbf{R}}                              Kk​=HTPk−​H+RPk−​H​协方差矩阵                                                        P                                     ,                                     Q                                              P,Q                              P,Q,退化为标量                                                   P                                  ,                                  Q                                          P,Q                           P,Q,矩阵乘法简化为平方运算状态更新方程                                                                                    x                                           ^                                                      k                                                  =                                                                  x                                           ^                                                      k                                        −                                                  +                                                   K                                        k                                                  (                                                   z                                        k                                                  −                                     H                                                                  x                                           ^                                                      k                                        −                                                  )                                              \mathbf{\hat{x}}_k = \mathbf{\hat{x}}_k^- + \mathbf{K}_k(\mathbf{z}_k -\mathbf{H}\mathbf{\hat{x}}_k^-)                              x^k​=x^k−​+Kk​(zk​−Hx^k−​)                                                                                    x                                           ^                                                      k                                                  =                                                                  x                                           ^                                                      k                                        −                                                  +                                                   K                                        k                                                  (                                                   z                                        k                                                  −                                     H                                                                  x                                           ^                                                      k                                        −                                                  )                                              \mathbf{\hat{x}}_k = \mathbf{\hat{x}}_k^- + \mathbf{K}_k(\mathbf{z}_k - \mathbf{H}\mathbf{\hat{x}}_k^-)                              x^k​=x^k−​+Kk​(zk​−Hx^k−​)向量运算退化为标量加减乘除协方差更新方程                                                                     P                                        k                                                  =                                     (                                     I                                     −                                                   K                                        k                                                  H                                     )                                                   P                                        k                                        −                                                           \mathbf{P}_k = (\mathbf{I} - \mathbf{K}_k\mathbf{H})\mathbf{P}_k^-                              Pk​=(I−Kk​H)Pk−​                                                                     P                                        k                                                  =                                     (                                     1                                     −                                                   K                                        k                                                  H                                     )                                                   P                                        k                                        −                                                           \mathbf{P}_k = (1 - \mathbf{K}_k\mathbf{H})\mathbf{P}_k^-                              Pk​=(1−Kk​H)Pk−​单位矩阵                                                        I                                              I                              I 退化为标量                                                   I                                          I                           I,矩阵减法退化为标量运算

  • 代码实现

    • 一维简化卡尔曼滤波模型C语言代码demo

      • 参数

        • 估算协方差:量化当前估计结果的不确定性
        • 卡尔曼增益:动态平衡模型猜测与测量值的权重
        • 过程噪声协方差:描述模型猜测毛病的方差(调)
        • 测量噪声协方差:传感器测量毛病的方差(可现实测量得出)

      1. #include <stdio.h>
      2. #include <stdlib.h>
      3. #include <math.h>
      4. #include "app_init.h"
      5. #include "soc_osal.h"
      6. #include "common_def.h"
      7. #include "systick.h"
      8. #include "securec.h"
      9. #include "tcxo.h"
      10. #include "sle_common.h"
      11. #include "sle_device_discovery.h"
      12. #include "sle_connection_manager.h"
      13. #include "sle_ssap_client.h"
      14. //线程参数
      15. #define SLE_DISTANCE_CLIENT_TASK_PRIO          26     //线程优先级
      16. #define SLE_DISTANCE_CLIENT_TASK_STACK_SIZE    0x2000     //线程的栈大小
      17. //本地设备参数
      18. static uint8_t local_addr[SLE_ADDR_LEN] = {0x13, 0x67, 0x5c, 0x07, 0x00, 0x51}; //自身 mac 地址
      19. static uint64_t g_conn_id;      //地址   
      20. //对端设备参数
      21. static uint8_t mac[SLE_ADDR_LEN] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66};    //待匹配的 server 端的 mac
      22. static sle_addr_t g_remote_addr = {0};   //创建设备地址结构体,初始化为0
      23. //设备发现
      24. #define SLE_SEEK_INTERVAL_DEFAULT   100     //扫描间隔
      25. #define SLE_SEEK_WINDOW_DEFAULT     100     //扫描窗口
      26. //设备连接
      27. #define DISTANCE_DEFAULT_CONN_INTERVAL 0x14    //连接时间
      28. #define DISTANCE_DEFAULT_SCAN_INTERVAL 400     //扫描间隔
      29. #define DISTANCE_DEFAULT_SCAN_WINDOW 20        //扫描时间
      30. #define DISTANCE_DEFAULT_TIMEOUT_MULTIPLIER 0x1f4  //超时时间
      31. //设备匹配
      32. #define SLE_MTU_SIZE_DEFAULT        1500    //链路最大传输单元
      33. // SSAP 参数
      34. #define UUID_16BIT_LEN 2        //UUID 长度为 16 位
      35. #define UUID_128BIT_LEN 16      //UUID 长度为 128 位
      36. static ssapc_find_service_result_t   g_find_service_result = {0};   //存储服务信息的全局变量
      37. /* 设备发现 */
      38. //设备发现参数初始化
      39. void sle_start_scan(void){
      40.      sle_seek_param_t param = {0};
      41.      param.own_addr_type = 0;   
      42.      param.filter_duplicates = 0;
      43.      param.seek_filter_policy = 0;
      44.      param.seek_phys = 1;    //扫描设备所使用的PHY,1M
      45.      param.seek_type[0] = 0; //被动扫描
      46.      param.seek_interval[0] = SLE_SEEK_INTERVAL_DEFAULT; //扫描间隔,time = N * 0.125ms
      47.      param.seek_window[0] = SLE_SEEK_WINDOW_DEFAULT;     //扫描窗口,time = N * 0.125ms
      48.      sle_set_seek_param(&param);     //公开扫描参数
      49.      sle_start_seek();   //开始公开扫描
      50. }
      51. void sle_sample_sle_enable_cbk(errcode_t status){
      52.      if (status == 0) {
      53.          sle_start_scan();
      54.      }
      55. }
      56. void sle_sample_seek_enable_cbk(errcode_t status){
      57.      if (status == 0) {
      58.          return;
      59.      }
      60. }
      61. //检验是否是目标对象,是则将其地址存入外部变量 g_remote_addr 中
      62. void sle_sample_seek_result_info_cbk(sle_seek_result_info_t *seek_result_data){      //传入一个指向扫描结果的指针
      63.      if (seek_result_data != NULL) {
      64.          if (memcmp(seek_result_data->addr.addr, mac, SLE_ADDR_LEN) == 0) {  // mac 地址比较
      65.              (void)memcpy_s(&g_remote_addr, sizeof(sle_addr_t), &seek_result_data->addr, sizeof(sle_addr_t));    //将 mac 地址存储到 addr 中
      66.              sle_stop_seek();    //停止扫描
      67.          }
      68.      }
      69. }
      70. void sle_sample_seek_disable_cbk(errcode_t status){
      71.      if (status == 0) {
      72.          sle_connect_remote_device(&g_remote_addr);  //发送连接请求
      73.      }
      74. }
      75. //注册设备发现回调函数
      76. static void sle_init(void){
      77.      static sle_announce_seek_callbacks_t g_seek_cbk = {0};
      78.      g_seek_cbk.sle_enable_cb = sle_sample_sle_enable_cbk;   // sle 协议栈回调函数使能
      79.      g_seek_cbk.seek_enable_cb = sle_sample_seek_enable_cbk; //扫描回调函数使能
      80.      g_seek_cbk.seek_result_cb = sle_sample_seek_result_info_cbk;    //根据结果关闭回调函数
      81.      g_seek_cbk.seek_disable_cb = sle_sample_seek_disable_cbk;   //关闭扫描回调函数
      82.      sle_announce_seek_register_callbacks(&g_seek_cbk);
      83. }
      84. /* 设备连接 */
      85. //连接参数
      86. void sle_distance_connect_param_init(void)
      87. {
      88.      sle_default_connect_param_t param = {0};
      89.      param.enable_filter_policy = 0;     //不过滤
      90.      param.gt_negotiate = 0;     //gt交互
      91.      param.initiate_phys = 1;    //通信带宽
      92.      param.max_interval = DISTANCE_DEFAULT_CONN_INTERVAL;   //最大连接间隔
      93.      param.min_interval = DISTANCE_DEFAULT_CONN_INTERVAL;   //最小连接间隔
      94.      param.scan_interval = DISTANCE_DEFAULT_SCAN_INTERVAL;  //扫描时间间隔
      95.      param.scan_window = DISTANCE_DEFAULT_SCAN_WINDOW;      //扫描时间
      96.      param.timeout = DISTANCE_DEFAULT_TIMEOUT_MULTIPLIER;   //超时时间
      97.      sle_default_connection_param_set(&param);
      98. }
      99. void sle_sample_connect_state_changed_cbk(uint16_t conn_id, const sle_addr_t *addr,sle_acb_state_t conn_state, sle_pair_state_t pair_state, sle_disc_reason_t disc_reason)
      100. {
      101.      osal_printk("[ssap client] conn state changed conn_id:%d, addr:%02x***%02x%02x\n", conn_id, addr->addr[0],addr->addr[4], addr->addr[5]); /* 0 4 5: addr index */
      102.      osal_printk("[ssap client] conn state changed disc_reason:0x%x\n", disc_reason);
      103.      if (conn_state == SLE_ACB_STATE_CONNECTED) {    //设备已连接
      104.          if (pair_state == SLE_PAIR_NONE) {      //未匹配则发起匹配
      105.              sle_pair_remote_device(&g_remote_addr);
      106.          }
      107.          g_conn_id = conn_id;
      108.      }
      109. }
      110. void sle_sample_update_req_cbk(uint16_t conn_id, errcode_t status, const sle_connection_param_update_req_t *param)
      111. {
      112.      unused(conn_id);
      113.      unused(status);
      114.      osal_printk("[ssap client] sle_sample_update_req_cbk interval_min = %02x, interval_max = %02x\n",param->interval_min, param->interval_max);
      115. }
      116. void sle_sample_pair_complete_cbk(uint16_t conn_id, const sle_addr_t *addr, errcode_t status)
      117. {
      118.      osal_printk("[ssap client] pair complete conn_id:%d, addr:%02x***%02x%02x\n", conn_id, addr->addr[0],addr->addr[4], addr->addr[5]); /* 0 4 5: addr index */
      119.      if (status == 0) {  //是否配对成功
      120.          ssap_exchange_info_t info = {0};
      121.          info.mtu_size = SLE_MTU_SIZE_DEFAULT;
      122.          info.version = 1;
      123.          ssapc_exchange_info_req(1, g_conn_id, &info);   //发送链路连接请求,配置连接参数
      124.      }
      125. }
      126. void sle_sample_update_cbk(uint16_t conn_id, errcode_t status, const sle_connection_param_update_evt_t *param)
      127. {
      128.      unused(status);
      129.      osal_printk("[ssap client] updat state changed conn_id:%d, interval = %02x\n", conn_id, param->interval);
      130. }
      131. //连接设备连接回调函数
      132. void sle_sample_connect_cbk_register(void)
      133. {
      134.      static sle_connection_callbacks_t    g_connect_cbk = {0};
      135.      g_connect_cbk.connect_state_changed_cb = sle_sample_connect_state_changed_cbk;  //连接状态改变
      136.      g_connect_cbk.connect_param_update_req_cb = sle_sample_update_req_cbk;  //请求连接参数更新
      137.      g_connect_cbk.pair_complete_cb = sle_sample_pair_complete_cbk;  //配对完成
      138.      g_connect_cbk.connect_param_update_cb = sle_sample_update_cbk;      //更新连接参数(已完成)
      139.      sle_connection_register_callbacks(&g_connect_cbk);
      140. }
      141. /* SSAP client */
      142. //更新mtu大小,启动服务发现
      143. void sle_sample_exchange_info_cbk(uint8_t client_id, uint16_t conn_id, ssap_exchange_info_t *param,errcode_t status)
      144. {
      145.      osal_printk("[ssap client] pair complete client id:%d status:%d\n", client_id, status);
      146.      osal_printk("[ssap client] exchange mtu, mtu size: %d, version: %d.\n",param->mtu_size, param->version);
      147.      
      148.      //服务发现
      149.      ssapc_find_structure_param_t find_param = {0};
      150.      find_param.type = SSAP_FIND_TYPE_PRIMARY_SERVICE;       //寻找指定的服务
      151.      find_param.start_hdl = 1;       //起始句柄
      152.      find_param.end_hdl = 0xFFFF;    //结束句柄
      153.      //调用服务发现函数
      154.      ssapc_find_structure(0, conn_id, &find_param);      
      155. }
      156. //为应用层提供信息
      157. void sle_sample_find_structure_cbk(uint8_t client_id, uint16_t conn_id, ssapc_find_service_result_t *service,errcode_t status)
      158. {
      159.      osal_printk("[ssap client] find structure cbk client: %d conn_id:%d status: %d \n",client_id, conn_id, status);
      160.      osal_printk("[ssap client] find structure start_hdl:[0x%02x], end_hdl:[0x%02x], uuid len:%d\r\n",service->start_hdl, service->end_hdl, service->uuid.len);
      161.      if (service->uuid.len == UUID_16BIT_LEN) {osal_printk("[ssap client] structure uuid:[0x%02x][0x%02x]\r\n",service->uuid.uuid[14], service->uuid.uuid[15]); /* 14 15: uuid index */
      162.      } else {
      163.          for (uint8_t idx = 0; idx < UUID_128BIT_LEN; idx++) {
      164.              osal_printk("[ssap client] structure uuid[%d]:[0x%02x]\r\n", idx, service->uuid.uuid[idx]);
      165.          }
      166.      }
      167.      //保存服务信息到全局变量
      168.      g_find_service_result.start_hdl = service->start_hdl;
      169.      g_find_service_result.end_hdl = service->end_hdl;
      170.      memcpy_s(&g_find_service_result.uuid, sizeof(sle_uuid_t), &service->uuid, sizeof(sle_uuid_t));
      171. }
      172. //处理服务或特征发现完成的事件,发送数据到对端设备
      173. void sle_sample_find_structure_cmp_cbk(uint8_t client_id, uint16_t conn_id,ssapc_find_structure_result_t *structure_result, errcode_t status)
      174. {
      175.      osal_printk("[ssap client] find structure cmp cbk client id:%d status:%d type:%d uuid len:%d \r\n",client_id, status, structure_result->type, structure_result->uuid.len);
      176.      if (structure_result->uuid.len == UUID_16BIT_LEN) {
      177.          osal_printk("[ssap client] find structure cmp cbk structure uuid:[0x%02x][0x%02x]\r\n",structure_result->uuid.uuid[14], structure_result->uuid.uuid[15]); /* 14 15: uuid index */
      178.      } else {
      179.          for (uint8_t idx = 0; idx < UUID_128BIT_LEN; idx++) {
      180.              osal_printk("[ssap client] find structure cmp cbk structure uuid[%d]:[0x%02x]\r\n", idx,structure_result->uuid.uuid[idx]);
      181.          }
      182.      }
      183.      //构造一个写操作(发送操作)
      184.      uint8_t data[] = {0x11, 0x22, 0x33, 0x44};
      185.      uint8_t len = sizeof(data);
      186.      ssapc_write_param_t param = {0};
      187.      param.handle = g_find_service_result.start_hdl;     //写操作的目标句柄
      188.      param.type = SSAP_PROPERTY_TYPE_VALUE;      //写类型
      189.      param.data_len = len;       //数据长度
      190.      param.data = data;      //写入数据
      191.      ssapc_write_req(0, conn_id, &param);    //发起写申请
      192. }
      193. //写操作完成确认函数
      194. void sle_sample_write_cfm_cbk(uint8_t client_id, uint16_t conn_id, ssapc_write_result_t *write_result,errcode_t status)
      195. {
      196.      osal_printk("[ssap client] write cfm cbk, client id: %d status:%d.\n", client_id, status);
      197.      ssapc_read_req(0, conn_id, write_result->handle, write_result->type);
      198. }
      199. //发现特征回调函数
      200. void sle_sample_find_property_cbk(uint8_t client_id, uint16_t conn_id,ssapc_find_property_result_t *property, errcode_t status)
      201. {
      202.      osal_printk("[ssap client] find property cbk, client id: %d, conn id: %d, operate ind: %d, ""descriptors count: %d status:%d.\n", client_id, conn_id, property->operate_indication,property->descriptors_count, status);
      203.      for (uint16_t idx = 0; idx < property->descriptors_count; idx++) {
      204.          osal_printk("[ssap client] find property cbk, descriptors type [%d]: 0x%02x.\n",idx, property->descriptors_type[idx]);
      205.      }
      206.      if (property->uuid.len == UUID_16BIT_LEN) {
      207.          osal_printk("[ssap client] find property cbk, uuid: %02x %02x.\n",property->uuid.uuid[14], property->uuid.uuid[15]); /* 14 15: uuid index */
      208.      } else if (property->uuid.len == UUID_128BIT_LEN) {
      209.          for (uint16_t idx = 0; idx < UUID_128BIT_LEN; idx++) {
      210.              osal_printk("[ssap client] find property cbk, uuid [%d]: %02x.\n",idx, property->uuid.uuid[idx]);
      211.          }
      212.      }
      213. }
      214. //解析读取数据并打印
      215. void sle_sample_read_cfm_cbk(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *read_data,errcode_t status)
      216. {
      217.      osal_printk("[ssap client] read cfm cbk client id: %d conn id: %d status: %d\n",client_id, conn_id, status);
      218.      osal_printk("[ssap client] read cfm cbk handle: %d, type: %d , len: %d\n",read_data->handle, read_data->type, read_data->data_len);
      219.      for (uint16_t idx = 0; idx < read_data->data_len; idx++) {
      220.          osal_printk("[ssap client] read cfm cbk[%d] 0x%02x\r\n", idx, read_data->data[idx]);
      221.      }
      222. }
      223. static void sle_distance_indication_cb(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
      224.      errcode_t status)
      225. {
      226.      unused(status);
      227.      unused(conn_id);
      228.      unused(client_id);
      229.      osal_printk("\n sle_distance_indication_cb sle uart recived data : %s\r\n", data->data);
      230. }
      231. //ssap client回调函数
      232. void sle_sample_ssapc_cbk_register(ssapc_notification_callback indication_cb)
      233. {
      234.      static ssapc_callbacks_t             g_ssapc_cbk = {0};
      235.      g_ssapc_cbk.exchange_info_cb = sle_sample_exchange_info_cbk;    //更新mtu大小回调钩子,启动服务发现
      236.      g_ssapc_cbk.find_structure_cb = sle_sample_find_structure_cbk;  //发现服务回调函数,为应用层提供信息
      237.      g_ssapc_cbk.find_structure_cmp_cb = sle_sample_find_structure_cmp_cbk;  //发现特征完成回调函数,完成写操作
      238.      g_ssapc_cbk.write_cfm_cb = sle_sample_write_cfm_cbk;    //写操作完成确认函数
      239.      g_ssapc_cbk.ssapc_find_property_cbk = sle_sample_find_property_cbk;     //发现特征回调函数.
      240.      g_ssapc_cbk.read_cfm_cb = sle_sample_read_cfm_cbk;  //解析读取数据并打印
      241.      g_ssapc_cbk.indication_cb = indication_cb;      //指示事件上报钩子
      242.      ssapc_register_callbacks(&g_ssapc_cbk);
      243. }
      244. /* 主函数 */
      245. static int sle_distance_client_task(const char *arg)
      246. {
      247.      unused(arg);
      248.      sle_addr_t local_address;
      249.      local_address.type = 0;
      250.      (void)memcpy_s(local_address.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN);
      251.      sle_init();
      252.      sle_distance_connect_param_init();
      253.      sle_sample_connect_cbk_register();
      254.      sle_sample_ssapc_cbk_register(sle_distance_indication_cb);
      255.      enable_sle();
      256.      sle_set_local_addr(&local_address);
      257.      osal_msleep(1000);
      258.      
      259.      return 0;
      260. }
      261. //创建运行任务的线程并进行初始化
      262. static void sle_distance_client_entry(void)
      263. {
      264.      osal_task *task_handle = NULL;
      265.      osal_kthread_lock();    //加锁
      266.      //创建线程
      267.      task_handle = osal_kthread_create((osal_kthread_handler)sle_distance_client_task, 0, "Sle_Distance_Task", SLE_DISTANCE_CLIENT_TASK_STACK_SIZE);
      268.      
      269.      if (task_handle != NULL) {
      270.          //设置优先级
      271.          osal_kthread_set_priority(task_handle, SLE_DISTANCE_CLIENT_TASK_PRIO);//
      272.          osal_kfree(task_handle);    //释放内存
      273.      }
      274.      osal_kthread_unlock();  //解锁
      275. }
      276. //运行该app
      277. app_run(sle_distance_client_entry);
      复制代码

    • 基于上述代码的结果
    • 移植代码
      1.   /**
      2.    * @if Eng
      3.    * @brief Struct of SLE connection manager callback function.
      4.    * @else
      5.    * @brief SLE连接管理回调函数接口定义。
      6.    * @endif
      7.    */
      8.   typedef struct {
      9.       sle_connect_state_changed_callback connect_state_changed_cb;         /*!< @if Eng Connect state changed callback.
      10.                                                                               @else   连接状态改变回调函数。 @endif */
      11.       sle_connect_param_update_req_callback connect_param_update_req_cb;   /*!< @if Eng Connect param updated callback.
      12.                                                                               @else   连接参数更新回调函数。 @endif */
      13.       sle_connect_param_update_callback connect_param_update_cb;           /*!< @if Eng Connect param updated callback.
      14.                                                                               @else   连接参数更新回调函数。 @endif */
      15.       sle_auth_complete_callback auth_complete_cb;                         /*!< @if Eng Authentication complete callback.
      16.                                                                               @else   认证完成回调函数。 @endif */
      17.       sle_pair_complete_callback pair_complete_cb;                         /*!< @if Eng Pairing complete callback.
      18.                                                                               @else   配对完成回调函数。 @endif */
      19.       sle_read_rssi_callback read_rssi_cb;                                 /*!< @if Eng Read rssi callback.
      20.                                                                               @else   读取rssi回调函数。 @endif */
      21.       sle_low_latency_callback low_latency_cb;                             /*!< @if Eng Set low latency callback.
      22.                                                                               @else   设置low latency回调函数。 @endif */
      23.       sle_set_phy_callback set_phy_cb;                                     /*!< @if Eng Set PHY callback.
      24.                                                                               @else   设置PHY回调函数。 @endif */
      25.   } sle_connection_callbacks_t;
      复制代码

  • 效果

    • 蓝色曲线为原始数据,橙色曲线为经过卡尔曼滤波之后的数据

    • 由上图可见,卡尔曼滤波对数据的处理本领非常强大,所得的数据基本满足使用要求,但从x轴上0-100与600-800段的数据可以看出,该滤波参数耽误比较严峻,只适合顶点数据的处理,不适合运动状态的数据的处理

数据标定(简朴介绍)



  • 原理
    - 在不考虑干扰情况的情况下RSSI是随着距离而变化的,故可使用不同的RSSI数值确定不同的距离
  • 实验操纵

    • 为了淘汰毛病,我们需要在完成卡尔曼滤波之后读取不同距离的RSSI值(相对稳固),求出其平均值,再将不同位置对应的RSSI进行匹配处理,绘制出其变化曲线,使用工具计算出距离与RSSI值的方程(可使用excel)

      • 以下展示在标定过程中的现象

        • 10cm处的数据
          20cm


      • 经过多次的数据收罗,最终可以得到以下的数据


        • 以上数据经过excel处理得来



  • 代码实现

    • 计算得出的方程是非线性的,且大多数包含                                                   e                                          e                           e ,需要不小的计算本领才能计算;而在单片机中,计算资源是有限的,无法进行非常复杂的计算(复杂计算还可能影响运行速度,使得输出速度满足不了我们的要求),故我们采用泰勒展开的方式对方程进行简化,方便单片机进行计算,对于上述数据得到的公式进行泰勒展开,取其前八项进行计算
    1. //配置回调函数
    2. g_connect_cbk.read_rssi_cb = sle_sample_read_rssi_cbk;  //读取rssi回调函数
    3. //回调函数内容,每次调用读取rssi的函数之后会挑战到该回调函数
    4. void sle_sample_read_rssi_cbk(uint16_t conn_id, int8_t rssi, errcode_t status)
    5. {
    6.     unused(conn_id);
    7.     unused(status);
    8.     osal_printk("rssi: %d \r\n",rssi);
    9. }
    10. //主函数中调用读取rssi的函数
    11. while (1)
    12. {
    13.     sle_read_remote_device_rssi(g_conn_id);
    14.     osal_msleep(1000);
    15. }
    复制代码

  • 最终效果


    • 因读取到的RRSI数值并不是与距离成稳固反比例关系,故在此次实验中,可以或许相对准确读取的范围只有 10~50cm,若需进步处理以得到更加准确的距离,请改进算法或期待更先进的芯片发布,此次实验仅仅作为一个初识HiSparkStudio与星闪的训练,并不作为一个真正可以落地的项目,请以训练为主。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表