LiteOS与SLE多设备数据传输实战

文章目录


本文与@minstrel poet共同完成
文中图片来源于网络,如有侵权,请联系作者删除


1.初识海思与星闪

1.1海思简介

海思官网

华为海思是华为旗下专注于半导体与集成电路设计的子公司,总部位于深圳,产品涵盖麒麟处理器、昇腾AI芯片、巴龙基带等,广泛应用于手机、通信设备与智能终端,是中国芯片设计的核心力量。

海思社区

社区提供了丰富的技术支持和开发支持,通过官方案例和技术论坛可以让开发者快速上手和解决开发过程中遇到的问题。

1.2星闪简介

国际星闪联盟

星闪是中国原生的新一代近距离无线连接技术品牌,汇聚了300多家头部企业和机构的集体智慧,用一套标准集合蓝牙和WIFI等传统无线技术的优势,适用于消费电子、智能家居、新能源汽车、工业智造等多种场景。

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

2.HiSpark环境搭建

下载 HiSparkStudio IDE 和 fbb_ws63 代码仓,并自行安装HiSparkStudio,导入 fbb_ws63 代码仓,编译案例代码验证。

2.1操作步骤

① HiSparkStudio下载

HiSpark Studio下载

HiSparkStudio IDE 在工具中心中

② fbb_ws63 代码仓下载

fbb_ws63仓库链接

③ fbb_ws63 代码仓介绍

  • 总体介绍

    ws63系列是2.4GHz Wi-Fi 6 星闪多模解决方案,其中ws63E支持2.4GHz的雷达人体活动检测功能,适用于大小家电、电工照明及对人体出没检测有需求的常电类物联网智能场景。该fbb_ws63代码包从统一开发平台FBB(Family Big Box,统一开发框架,统一API)构建而来,在该平台上开发的应用很容易被移植到其他星闪解决方案上,有效降低开发者门槛,缩短开发周期,支持开发者快速开发星闪产品。软件文档

  • 组成结构介绍

    • docs文件夹
      • 存放软件资料手册、IO复用关系表、用户指南手册,帮助用户快速了解WS63系列
    • src文件夹
      • 开发接入SDK源码包,用户基于源码进行二次开发
    • tools文件夹
      • 开发工具及环境搭建指南文档,帮助用户搭建开发环境
    • vendor文件夹
      • 存放合作产商的开发板硬件和软件资料,包含案例代码、硬件原理图、案例开发指南文档

④ 认识HiSparkStudio IDE和导入SDK源码及编译验证

  • 工作环境
  • 新建工程
  • 工程配置
  • 可以看到SDK源码已经导入,开始编译(编译过程较慢,需耐心等待)
  • 若编译失败,可能是python版本问题、环境变量问题、路径过长或路径带有中文等问题,可以参考FAQ中寻找解决办法

HiSparkStudio环境搭建无法编译、编译速度较慢等问题解决FAQ

2.2检查CH340驱动

  • 用Type-C线连接电脑与开发板,开发板上红灯(电源灯)亮起则表明已连接成功

  • 打开电脑设备管理器

  • 打开端口,查看是否识别出开发板

  • 安装CH340驱动

  • 下载驱动

  • 点击安装

  • 安装成功

3.实战演练

3.1认识WS63

https://bearpi.cn/core_board/bearpi/bm/h63/hardware/Pinout.html#📑-pinout

3.2点灯大师

  • 点击工程配置,找到程序加载板块,修改端口为在设备管理器中看的带有CH340字样的端口
  • 按图中步骤,编译烧录

若编译烧录成功,可以看到板载LED闪烁。

通过这个例子我们熟悉HiSparkStudio的基本操作。

  • 扩展
    • 令板载LED长亮

3.3认识HiSparkStudio编程方式

相信大家注意到了CMakeLists.txt文件和Kconfig文件,这在之前接触的STM32的工程是没有出现过的。

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中,可以找到下列内容

    复制代码
      ```c
       add_subdirectory_if_exist(application)
       add_subdirectory_if_exist(bt)
       add_subdirectory_if_exist(bootloader)
       add_subdirectory_if_exist(kernel)
       add_subdirectory_if_exist(drivers)
       add_subdirectory_if_exist(middleware)
       add_subdirectory_if_exist(open_source)
       add_subdirectory_if_exist(protocol)
       add_subdirectory_if_exist(test)
       add_subdirectory_if_exist(include)
       add_subdirectory_if_exist(vendor)
      
      ```
      
      - 该内容主要是检查每个子目录(括号中的内容)是否存在,存在则将其作为子项包含到当前的构建系统中,其中就包含了application文件夹
    • 进入到application文件夹的CMakeLists.txt中,可以找到add_subdirectory_if_exist(samples),此时编译链会指向samples文件夹

    • 进入到samples文件夹中的CMakeLists.txt中,可以找到下列内容

      c 复制代码
       if(DEFINED CONFIG_ENABLE_PERIPHERAL_SAMPLE)
       add_subdirectory_if_exist(peripheral)
       endif()
       
       install_sdk("${CMAKE_CURRENT_SOURCE_DIR}/peripheral" "*")
      • 判断语句的作用是如果定义了该配置选项,则调用add_subdirectory_if_exist(peripheral)加载其子模块
      • install_sdk:是将该目录中的文件安装到SDK中,方便编译时调用

    进入到peripheral文件夹中,其中的CMakeLists.txt文件如下:

    复制代码
      ```c
      if(DEFINED CONFIG_SAMPLE_SUPPORT_BLINKY)
          add_subdirectory_if_exist(blinky)
      endif()
      
      set(SOURCES "${SOURCES}" PARENT_SCOPE)
      
      ```
      
      - 与上一层类似

    进入到blinky文件夹,其中的CMakeLists.txt文件如下:

    复制代码
      ```c
      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的内容

      • 该内容直接定义了Application这个选项,并将其指向"application/Kconfig"中
      c 复制代码
       menu "Application"
           comment "Config the application."
           osource "application/Kconfig"
       endmenu

    打开application文件夹,找到Kconfig文件,找到关于samples文件夹的内容

    复制代码
      ```c
       config SAMPLE_ENABLE
           bool
           prompt "Enable Sample."
           default n
           help
               This option means support Samples.
       	
       	if SAMPLE_ENABLE
       	osource "application/samples/Kconfig"
       	endif
      
      ```
      
      - config SAMPLE_ENABLE:定义一个名为SAMPLE_ENABLE的配置选项
      - osource "application/samples/Kconfig":如果SAMPLE_ENABLE被启用,则加载路径application/samples/Kconfig中的配置选项

打开samples文件夹,找到Kconfig文件,找到关于peripheral文件夹的内容

复制代码
    ```c
     config ENABLE_PERIPHERAL_SAMPLE
         bool
         prompt "Enable the Sample of peripheral."
         default n
         depends on SAMPLE_ENABLE
     help
         This option means enable the sample of peripheral.
         
     if ENABLE_PERIPHERAL_SAMPLE
     osource "application/samples/peripheral/Kconfig"
     endif
    
    ```
    
    - depends on SAMPLE_ENABLE:依赖关系,depends on SAMPLE_ENABLE被选择才会显示本内容,其他同上
- 打开peripheral文件夹,打开Kconfig文件,找到blinky内容如下
    - 
    
    ```c
     config SAMPLE_SUPPORT_BLINKY
         bool
     	prompt "Support BLINKY Sample."
         default n
         depends on ENABLE_PERIPHERAL_SAMPLE
         help
             This option means support BLINKY Sample.
     if SAMPLE_SUPPORT_BLINKY
     menu "Blinky Sample Configuration"
         osource "application/samples/peripheral/blinky/Kconfig"
     endmenu
     endif
    
    ```
    
- 在blinky文件夹中的Kconfig内容如下:
    - 提供系统配置中提供Choose blinky pin的功能
    
    ```c
     config BLINKY_PIN
     int
     prompt "Choose blinky pin."
     default 2
    
    ```

总结

  • 用户使用Kconfig配置功能开关(配置系统,用户勾选需要的模块),CMakeLists根据 Kconfig 配置决定编译哪些模块(构建系统),从而构建编译链。相当于Kconfig是决策者,.config是命令,CMakeLists是执行者
  • Kconfig → 生成 .config → CMakeLists.txt 检查 .config 里的宏 → 决定构建哪些目录
  • 如果一开始不太会配置Kconfig和CMakeLists,可以参照demo中相同层级的Kconfig和CMakeLists来配置你自己的
  • Kconfig 语法里并不支持 C/C++ 风格的单行注释 // 。Kconfig 只认两种注释:
    1. # 井号开头
    2. /* ... */ 块注释
    3. 且注释必须写在help区域或顶行

3.4 Hello HiSpark

通过学习此案例,独立编写HiSpark代码。

  • 构建Hello_HiSpark.c的编译链

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

4.初试星闪

4.1详细介绍

  • 星闪是什么

  • 为什么需要星闪

  • 星闪的关键技术

  • 星闪的技术优势

  • 区别于蓝牙的底层原理

    维度 蓝牙 5.x / LE Audio 星闪(NearLink)SLB 模式
    物理层 2.4 GHz 传统 GFSK,1 MHz 窄信道 新设计 2.4 GHz SSB(Spread Spectrum Band)+ Polar 码,4/8 MHz 可变带宽
    调制&编码 GFSK + 1/2/3 字节包长 Polar + LDPC 码,1024QAM,物理层速率 12 Mbit/s → 空口吞吐 6~8 Mbit/s(蓝牙 5 仅 2 Mbit/s)
    时隙结构 固定 7.5 ms 连接间隔,最少 1.25 ms 微时隙 125 µs 级调度,单帧可动态缩至 50 µs,端到端延迟 < 0.25 ms(蓝牙 5 最低 5~7 ms)
    多设备并发 经典模式 7 从设备就卡顿 TDMA + 分布式调度 ,单网关 256 节点 200 kHz 跳频,理论 4 096 节点
    抗干扰 79 × 1 MHz 跳频,跳速 1 600 hops/s SSB 扩频 + 跳频 3 200 hops/s,把 Wi-Fi 信道当成"背景噪声"直接扩频压过去
    功耗策略 保持连接即定时唤醒 无连接(Connection-less) 事件驱动,设备 90% 时间可完全关射频,功耗比蓝牙再降 30%
    应用定位 耳机、键控、低速传感器 无线鼠标 4 kHz 轮询不丢包、车载多屏 2 ms 同步、工业 32 轴机械臂 < 1 ms 时钟同步------蓝牙做不到

4.2初试星闪串口透传

通过本次实验,体验星闪串口透传。

server端检测按键是否被按下,若按下,则发送指令给client端,让client端的板载LED亮起

  • server端主要代码
c 复制代码
#include "pinctrl.h"
#include "gpio.h"
#include "soc_osal.h"
#include "app_init.h"
#include "tcxo.h"
#include "osal_debug.h"
#include "osal_task.h"
#include "cmsis_os2.h"
#include "common_def.h"
#include "string.h"
#include "osal_wait.h"
#include "uart.h"
#include "securec.h" // 安全函数库,用于替代标准 C 库中不安全的函数

#include "sle_low_latency.h"                     // 低延迟通信框架
#include "sle_uart_server/sle_uart_server.h"     // Server 端头文件
#include "sle_uart_server/sle_uart_server_adv.h" // Server 端广播相关头文件
#include "sle_device_discovery.h"                // 设备发现相关头文件
#include "sle_errcode.h"                         // 错误码定义

#define SENDMSG_DURATION_MS 500

#define SENDMSG_TASK_PRIO 24
#define SENDMSG_TASK_STACK_SIZE 0x4000

#define TASK_STACK_SIZE 0x4000 // Task 堆栈大小
#define TASK_PRIO 24           // Task 优先级

#define QUEUE_SIZE 12 // 定义队列大小
#define JOYSTICK_PERCENT_QUEUE_NODE_SIZE 24

// SLE Server 端相关宏定义
#define SLE_UART_SERVER_DELAY_COUNT 5          // Server 端延时计数
#define SLE_UART_TASK_STACK_SIZE 0x1200        // 任务栈大小
#define SLE_ADV_HANDLE_DEFAULT 1               // 广播句柄
#define SLE_UART_SERVER_MSG_QUEUE_LEN 5        // 消息队列长度
#define SLE_UART_SERVER_MSG_QUEUE_MAX_SIZE 32  // 消息节点大小
#define SLE_UART_SERVER_QUEUE_DELAY 0xFFFFFFFF // 消息队列延时,此处设置为最大值
#define SLE_UART_SERVER_BUFF_MAX_SIZE 800      // SLE UART Server 端缓冲区最大大小
#define SLE_UART_TASK_PRIO 28                  // SLE 任务优先级
#define SLE_UART_TASK_DURATION_MS 2000         // SLE 任务休眠时间
#define NAME_MAX_LENGTH 16                     // SLE 广播名称最大长度

uint32_t ret1 = 0;
uint8_t dht_buff[5];

unsigned long g_sle_uart_server_msgqueue_id;                 // 消息队列句柄
#define SLE_UART_SERVER_LOG "[sle uart server]"              // 日志前缀
static uint8_t sle_local_name[NAME_MAX_LENGTH] = "NearLink"; // 设备本地名称,用于 SLE 广播和连接

static unsigned long SLE_Transfer_QueueID = 0; // SLE 传输队列ID

// 回调函数
static void ssaps_server_read_request_callback(uint8_t server_id,
                                               uint16_t conn_id,
                                               ssaps_req_read_cb_t *read_cb_para,
                                               errcode_t status)
{
    osal_printk("%s ssaps read request cbk callback server_id:%x, conn_id:%x, handle:%x, status:%x\r\n",
                SLE_UART_SERVER_LOG, server_id, conn_id, read_cb_para->handle, status);
}

static void ssaps_server_write_request_callback(uint8_t server_id,
                                                uint16_t conn_id,
                                                ssaps_req_write_cb_t *write_cb_para,
                                                errcode_t status)
{
    osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n",
                SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para->handle, status);

    if ((write_cb_para->length > 0) && write_cb_para->value) {
        osal_printk("\n sle uart received data : %s\r\n", write_cb_para->value);
    }
}

// 消息队列操作
static void sle_uart_server_create_msgqueue(void)
{
    if (osal_msg_queue_create("sle_uart_server_msgqueue", SLE_UART_SERVER_MSG_QUEUE_LEN,
                              (unsigned long *)&g_sle_uart_server_msgqueue_id, 0,
                              SLE_UART_SERVER_MSG_QUEUE_MAX_SIZE) != OSAL_SUCCESS) {
        osal_printk("%s sle_uart_server_create_msgqueue message queue create failed!\n", SLE_UART_SERVER_LOG);
    }
}

static void sle_uart_server_delete_msgqueue(void)
{
    osal_msg_queue_delete(g_sle_uart_server_msgqueue_id);
}

static void sle_uart_server_write_msgqueue(uint8_t *buffer_addr, uint16_t buffer_size)
{
    osal_msg_queue_write_copy(g_sle_uart_server_msgqueue_id, (void *)buffer_addr, (uint32_t)buffer_size, 0);
}

static int32_t sle_uart_server_receive_msgqueue(uint8_t *buffer_addr, uint32_t *buffer_size)
{
    return osal_msg_queue_read_copy(g_sle_uart_server_msgqueue_id, (void *)buffer_addr, buffer_size,
                                    SLE_UART_SERVER_QUEUE_DELAY);
}

static void sle_uart_server_rx_buf_init(uint8_t *buffer_addr, uint32_t *buffer_size)
{
    *buffer_size = SLE_UART_SERVER_MSG_QUEUE_MAX_SIZE;
    (void)memset_s(buffer_addr, *buffer_size, 0, *buffer_size);
}

// SLE UART Server 任务
static void *sle_uart_server_task(const char *arg)
{
    unused(arg);
    osal_printk("%s sle_uart_server_task start\r\n", SLE_UART_SERVER_LOG);

    int set_name_ret = set_SLE_local_name(sle_local_name);
    if (set_name_ret != ERRCODE_SLE_SUCCESS) {
        osal_printk("%s set SLE local name failed, ret = %d\r\n", SLE_UART_SERVER_LOG, set_name_ret);
        return NULL;
    } else {
        osal_printk("%s set SLE local name success, name = %s\r\n", SLE_UART_SERVER_LOG, sle_local_name);
    }

    uint8_t rx_buf[SLE_UART_SERVER_MSG_QUEUE_MAX_SIZE] = {0};
    uint32_t rx_length = SLE_UART_SERVER_MSG_QUEUE_MAX_SIZE;
    uint8_t sle_connect_state[] = "sle_dis_connect";

    sle_uart_server_create_msgqueue();
    sle_uart_server_register_msg(sle_uart_server_write_msgqueue);
    sle_uart_server_init(ssaps_server_read_request_callback, ssaps_server_write_request_callback);

    osal_printk("%s sle_uart_server_task init success\r\n", SLE_UART_SERVER_LOG);

    while (1) {
        sle_uart_server_rx_buf_init(rx_buf, &rx_length);
        if (sle_uart_server_receive_msgqueue(rx_buf, &rx_length) == OSAL_SUCCESS) {
            if (strncmp((const char *)rx_buf, (const char *)sle_connect_state, sizeof(sle_connect_state)) == 0) {
                errcode_t ret = sle_start_announce(SLE_ADV_HANDLE_DEFAULT);
                if (ret != ERRCODE_SLE_SUCCESS) {
                    osal_printk("%s sle_start_announce fail :%02x\r\n", SLE_UART_SERVER_LOG, ret);
                }
            }
        }
        osal_msleep(SLE_UART_TASK_DURATION_MS);
    }

    sle_uart_server_delete_msgqueue();
    return NULL;
}

// SLE 发送任务
static void *SELSendTask(void)
{
    osal_printk("SLE_Send Task Start\r\n");
    char data_string[500] = {0};

    uapi_tcxo_init();

    uapi_pin_set_mode(10, HAL_PIO_FUNC_GPIO);
    uapi_gpio_set_dir(10, GPIO_DIRECTION_INPUT);
   
    uint8_t status = 0, light = 0;

    while (1) {

        // 检测按键是否按下
        status = 1;

        if (status == 1) {
            light = 1;
            snprintf(data_string, sizeof(data_string), "light = %d", light);
            osal_printk("%s send data to client: %s\n", SLE_UART_SERVER_LOG, data_string);
            if (sle_uart_client_is_connected()) {
                sle_uart_server_send_report_by_handle((uint8_t *)data_string, strlen(data_string));
            } else {
                osal_printk("%s client is not connected! \r\n", SLE_UART_SERVER_LOG);
            }
            osal_msleep(500);
        } else if (status == 0) {
            light = 0;
        }
        osal_msleep(500);
    }

    return NULL;
}

// 主函数
static void sle_uart_server_entry(void)
{
    osal_task *SLE_Transmit_TaskHandle = NULL;
    osal_task *SLE_Send_TaskHandle = NULL;

    ret1 = osal_msg_queue_create("SLE_Transmit_Queue", QUEUE_SIZE, &SLE_Transfer_QueueID, 0,
                                 JOYSTICK_PERCENT_QUEUE_NODE_SIZE);
    if (ret1 != OSAL_SUCCESS) {
        osal_printk("Create SLE_Transmit_Queue failed, ret = %d\r\n", ret1);
        return;
    }

    osal_kthread_lock();

    SLE_Send_TaskHandle =
        osal_kthread_create((osal_kthread_handler)SELSendTask, NULL, "SLE_Send_Task", TASK_STACK_SIZE);
    SLE_Transmit_TaskHandle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask",
                                                  SLE_UART_TASK_STACK_SIZE);
    if (SLE_Transmit_TaskHandle != NULL && SLE_Send_TaskHandle != NULL) {
        osal_printk("Create task success.\r\n");
        osal_kthread_set_priority(SLE_Transmit_TaskHandle, SLE_UART_TASK_PRIO);
        osal_kthread_set_priority(SLE_Send_TaskHandle, TASK_PRIO);
    } else {
        osal_printk("Task Created Failed.\r\n");
    }

    osal_kthread_unlock();
}

/* Run the i2c_master_entry. */
app_run(sle_uart_server_entry);
  • client端主要代码
c 复制代码
/**
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: SLE UART Sample Source. \n
 *
 * History: \n
 * 2023-07-17, Create file. \n
 */
#include "common_def.h"
#include "soc_osal.h"
#include "app_init.h"
#include "pinctrl.h"
#include "gpio.h"
#include "uart.h"
#include <stdio.h>
#include <string.h>
// #include "pm_clock.h"

#define CONFIG_UART_TXD_PIN                 17
#define CONFIG_UART_RXD_PIN                 18
#define CONFIG_SLE_UART_BUS                 1
#define CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT    1

#include "sle_low_latency.h"
#if defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_SERVER)

#elif defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT)
#define SLE_UART_TASK_STACK_SIZE            0x600
#include "sle_connection_manager.h"
#include "sle_ssap_client.h"
#include "sle_uart_client.h"
#endif  /* CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT */

#define SLE_UART_TASK_PRIO                  28
#define SLE_UART_TASK_DURATION_MS           2000
#define SLE_UART_BAUDRATE                   115200
#define SLE_UART_TRANSFER_SIZE              512

#define CONFIG_UART_TXD_PIN                 17
#define CONFIG_UART_RXD_PIN                 18
#define CONFIG_SLE_UART_BUS                 1
#define CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT    1

typedef struct {
    int light;
} SLEData;

// 全局变量,存储接收到的数据
SLEData g_received_data = {0};
volatile int g_data_received = 0; // 标志,表示数据是否已接收

int parse_sle_data(const char *input, SLEData *data) {
    // 使用 sscanf 解析字符串
    int result = sscanf(input, "light = %d",
                        &data->light);

    // 检查解析结果
    if (result == 1) {
        return 1; // 解析成功
    } else {
        return 0; // 解析失败
    }
}

static uint8_t g_app_uart_rx_buff[SLE_UART_TRANSFER_SIZE] = { 0 };

static uart_buffer_config_t g_app_uart_buffer_config = {
    .rx_buffer = g_app_uart_rx_buff,
    .rx_buffer_size = SLE_UART_TRANSFER_SIZE
};

static void uart_init_pin(void)
{
    if (CONFIG_SLE_UART_BUS == 0) {
        uapi_pin_set_mode(CONFIG_UART_TXD_PIN, PIN_MODE_1);
        uapi_pin_set_mode(CONFIG_UART_RXD_PIN, PIN_MODE_1);       
    }else if (CONFIG_SLE_UART_BUS == 1) {
        uapi_pin_set_mode(CONFIG_UART_TXD_PIN, PIN_MODE_1);
        uapi_pin_set_mode(CONFIG_UART_RXD_PIN, PIN_MODE_1);       
    }
}

static void uart_init_config(void)
{
    uart_attr_t attr = {
        .baud_rate = SLE_UART_BAUDRATE,
        .data_bits = UART_DATA_BIT_8,
        .stop_bits = UART_STOP_BIT_1,
        .parity = UART_PARITY_NONE
    };

    uart_pin_config_t pin_config = {
        .tx_pin = CONFIG_UART_TXD_PIN,
        .rx_pin = CONFIG_UART_RXD_PIN,
        .cts_pin = PIN_NONE,
        .rts_pin = PIN_NONE
    };
    uapi_uart_deinit(CONFIG_SLE_UART_BUS);
    uapi_uart_init(CONFIG_SLE_UART_BUS, &pin_config, &attr, NULL, &g_app_uart_buffer_config);

}

#if defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_SERVER)

#elif defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT)

void sle_uart_notification_cb(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
    errcode_t status)
{
    unused(client_id);
    unused(conn_id);
    unused(status);
    // osal_printk("\n sle uart recived data : %s\r\n", data->data);
    uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)(data->data), data->data_len, 0);
    // 解析数据
    if (parse_sle_data((const char *)data->data, &g_received_data)) {
        g_data_received = 1; // 设置标志位
    } else {
        osal_printk("Failed to parse data.\r\n");
    }
}

void sle_uart_indication_cb(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
    errcode_t status)
{
    unused(client_id);
    unused(conn_id);
    unused(status);
    // osal_printk("\n sle uart recived data : %s\r\n", data->data);
    uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)(data->data), data->data_len, 0);
    // 解析数据
    if (parse_sle_data((const char *)data->data, &g_received_data)) {
        g_data_received = 1; // 设置标志位
    } else {
        osal_printk("Failed to parse data.\r\n");
    }
}

static void sle_uart_client_read_int_handler(const void *buffer, uint16_t length, bool error)
{
    unused(error);
    ssapc_write_param_t *sle_uart_send_param = get_g_sle_uart_send_param();
    uint16_t g_sle_uart_conn_id = get_g_sle_uart_conn_id();
    sle_uart_send_param->data_len = length;
    sle_uart_send_param->data = (uint8_t *)buffer;
    ssapc_write_req(0, g_sle_uart_conn_id, sle_uart_send_param);
}

static void *sle_uart_client_task(const char *arg)
{
    unused(arg);
    /* UART pinmux. */
    uart_init_pin();
    uapi_pin_set_mode(6, HAL_PIO_FUNC_GPIO);
    uapi_gpio_set_dir(6, GPIO_DIRECTION_OUTPUT);
    uapi_gpio_set_val(6, GPIO_LEVEL_LOW);

    /* UART init config. */
    uart_init_config();

    uapi_uart_unregister_rx_callback(CONFIG_SLE_UART_BUS);
    errcode_t ret = uapi_uart_register_rx_callback(CONFIG_SLE_UART_BUS,
                                                   UART_RX_CONDITION_FULL_OR_IDLE,
                                                   1, sle_uart_client_read_int_handler);
    sle_uart_client_init(sle_uart_notification_cb, sle_uart_indication_cb);
    
    if (ret != ERRCODE_SUCC) {
        osal_printk("Register uart callback fail.");
        return NULL;
    }

    while (1) {
        if (g_data_received) {
            // 数据已接收,处理数据
            osal_printk("Received Data T:\n");
            osal_printk("light: %d\n", g_received_data.light);
            if(g_received_data.light == 1)
            {
                uapi_gpio_set_val(2, GPIO_LEVEL_HIGH);
            }
            else
            {
                uapi_gpio_set_val(2, GPIO_LEVEL_LOW);
            }

            // 重置标志位
            g_data_received = 0;
        }

        // 其他任务逻辑
        osal_msleep(100); // 短暂休眠,避免占用过多 CPU 时间
    }

    return NULL;
}
#endif  /* CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT */

static void sle_uart_client_entry(void)
{
    osal_task *task_handle = NULL;
    osal_kthread_lock();
#if defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_SERVER)
    task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_server_task, 0, "SLEUartServerTask",
                                      SLE_UART_TASK_STACK_SIZE);
#elif defined(CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT)
    task_handle = osal_kthread_create((osal_kthread_handler)sle_uart_client_task, 0, "SLEUartDongleTask",
                                      SLE_UART_TASK_STACK_SIZE);
#endif /* CONFIG_SAMPLE_SUPPORT_SLE_UART_CLIENT */
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, SLE_UART_TASK_PRIO);
    }
    osal_kthread_unlock();
}

/* Run the sle_uart_entry. */
app_run(sle_uart_client_entry);
相关推荐
flashier2 小时前
ESP32学习笔记_WiFi(2)——TCP/UDP
笔记·学习·tcp/ip·wifi·esp32
煎蛋学姐2 小时前
SSM学习互助平台网站8f554(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
学习·ssm 框架·javaweb 开发·书籍分类
AI_零食2 小时前
鸿蒙跨端框架 Flutter 学习 Day 4:异步编程基础——Future 与非阻塞执行的物理真相
学习·flutter·harmonyos
QiZhang | UESTC2 小时前
学习日记day64
学习
爱吃生蚝的于勒2 小时前
【Linux】零基础学习命名管道-共享内存
android·linux·运维·服务器·c语言·c++·学习
简叙生活3 小时前
【CES直击:从“屏幕依赖”到“真实对话”,Lookee如何用声网技术重构英语学习?
学习·ces
又是进步的一天3 小时前
Kubernetes 证书体系与 OpenSSL 命令学习
学习·容器·kubernetes
栗少3 小时前
Three.js快速入门
学习
想进部的张同学3 小时前
RK3588 Docker 中部署 GStreamer + MPP 并固化镜像(完整踩坑实录)
学习