(五)基于大联大控股世平集团WPI-NXP S32K312 DVK核心板&海思Hi2821E星闪SLE技术的星闪车钥匙介绍

大联大控股世平集团WPI-NXP S32K312 DVK核心板

关于星闪

NearLink_LOGO
星闪目标是推动新一代无线短距通信技术 NearLink 的创新和产业生态,该技术可满足智能汽车、工业智造、智慧家庭、个人穿戴等多场景对低时延、高可靠、精同步、多并发的技术需求。
星闪1.0 标准共由 12 项标准组成,包括接入层、基础服务层、通用(架构、标识等)、安全以及测试等部分。

关于星闪SLE协议

‌星闪SLE协议 (SparkLink Low Energy)是星闪联盟推出的新一代近距离无线连接技术,专为IoT应用场景设计,特别是汽车数字钥匙等应用。‌

星闪SLE协议具有以下主要特点:

  • 高精度定位‌:能够实现分米级的精准测距,相较于传统蓝牙RSSI定位的2-5米测距精度,实现了巨大突破‌。
  • ‌低延时和高带宽‌:支持12Mbps的传输速率和250μs的时延,适用于低功耗设备接入‌。
  • ‌抗干扰能力‌:使用了5G同款的Polar码 ,显著提高了抗干扰能力‌。

星闪SLE协议的应用场景包括:

  • ‌汽车数字钥匙‌:在车钥匙的多锚点组网架构中,星闪SLE凭借其高带宽和低时延,能够实现更高效的数据传输和更快速的响应,提升用户体验‌。
  • ‌智能家居产品‌:如华为Q7路由器搭载的星闪网关,主要服务于星闪协议的智能家居产品‌。

小熊派 BearPi-BM H21E

BearPi-BM H21E

产品概述
  • ‌产品名称:BearPi-BM H21E
  • ‌产品功能:BearPi-BM H21E是一款基于高度集成 2.4GHz SoC BLE&SLE为主控芯片的核心板,具有灵活的数字接口,IO口可复用为任意功能,集成高性能 32bit 微处理器(MCU),硬件安全引擎以及丰富的外设接口,外设接口包括 SPI、UART、I2C、PWM、GPIO、USB2.0、NFC Tag、PDM、I2S/PCM、QDEC、KEYSCAN 键盘扫描电路,支持 8 路 13bit 分辨率 ADC、ADC 支持对接音频 AMIC,内置 SRAM 和合封 Flash,并支持在 Flash 上运行程序。
  • ‌目标用户群体:企业开发者
产品特点
  • 支持 BLE4.0/4.1/4.2/5.0/5.1/5.2/5.3/5.4 和 SLE1.0 双模共存
  • RISC-V 高性能 32bit CPU,最大主频支持 64MHz,支持浮点,支持 SWD。
  • 内置 160KB的SRAM和1MB的Flash。
  • 支持加密:AES(Advanced Encryption Standard)、SM4 和 TRNG(True Random Number Generator)
  • 支持丰富的对外接口
    • AFE(Analog Front-End)
    • 传感器通路(General Analog Front-End):支持 8 ch 13bit 1.6Msps SAR ADC,支持单端/差分/扫描模式,支持过采样和 buf 功能。
    • 音频通路(Audio Analog Front-End):支持复用 13bit SAR ADC,降采 样到 16ksps/8ksps;支持 40dB 倍放大。
    • 支持 2×I2C(The Inter-Integrated Circuit),可配置为 Master 和 Slave。
    • 支持 1 路 2 通道 I2S(Integrated Interchip Sound)/PCM( Pulse Code Modulation)。
    • 支持 2 通道 PDM(Pulse Density Modulation)接口。
    • 支持 3×SPI(Serial Peripheral Interface),支持 master 和 slave 模式可配。
    • 支持 3×UART(Universal Asynchronous Receiver-Transmitter ),最大速率 4Mbit/s,其中 2 个 4 线 UART。
    • 支持 6×PWM(Pulse Width Modulation)。
    • 支持 USB2.0(Universal Serial Bus),支持 FS/HS mode,最大支持 480Mbit/s。
    • 支持 NFC Type2 Tag 功能,支持 NFC 场唤醒功能。
    • 支持 QDEC(QuaDrature EnCoding )接口。
    • 支持 KeyScan 功能。
    • 支持 29 个 GPIO(General-Purpose Input/Output)。
功能接口

功能接口

编号 功能 说明
1 2.4G 天线 BLE 和 SLE 天线
2 主控芯片 BS21E(H2821E), RISC-V 高性能 32bit CPU,最大主频支持 64MHz,支持 BLE 和 SLE 双模共存,支持浮点,支持 SWD。
3 状态灯 慢闪:进入AT模式;快闪:进入测距模式;双闪:测距模式下设备已配对
引脚定义

引脚定义

Pins Group IO功能 模拟复用 数字复用
1 GPIO25 全功能路由 O32M -
2 GPIO26 全功能路由 - -
3 GPIO27 全功能路由 - -
4 GPIO28 全功能路由 AIN4 -
5 GPIO29 全功能路由 AIN5 -
6 GPIO30 全功能路由 AIN6 -
7 GPIO0 全功能路由 - XL1
8 GPIO1 全功能路由 - XL2
9 GPIO2 全功能路由 AIN0 -
10 GPIO3 全功能路由 AIN1 -
11 NC - - -
12 MIC_LDO MIC LDO - -
13 GPIO7 全功能路由 - USB DP
14 GPIO8 全功能路由 - USB DM
15 GND 电源地 - -
16 VIN 3.3V输入 - -
17 RESET 复位 - -
18 GPIO6 全功能路由 - -
19 GPIO11 全功能路由 - 高速 SPI_TXD
20 GPIO12 全功能路由 - 高速 SPI_RXD
21 GPIO19 调试串口 - -
22 GPIO20 调试串口 - -
23 GPIO9 全功能路由 NFC1 -
24 GPIO10 全功能路由 NFC2 -
25 GPIO4 全功能路由 AIN2 -
26 GPIO5 全功能路由 AIN3 -
27 GPIO13 全功能路由 - 高速 SPI_CS
28 GPIO14 全功能路由 - 高速 SPI_CLK
29 GPIO15 全功能路由 - -
30 GPIO16 全功能路由 - -
31 GPIO17 全功能路由 - -
32 GPIO18 全功能路由 - -
33 GPIO21 全功能路由 - -
34 SWCLK SWD - -
35 SWDIO SWD - -
36 GND 电源地 - -

注:

  1. “全功能路由”代表该 GPIO 管脚可复用为任意功能。
  2. "模拟复用”,“数字复用”,代表该功能下,管脚不可更改。
  3. 请勿在上电之前将GPIO2、GPIO3、GPIO4、GPIO5、GPIO25、GPIO28、GPIO29、GPIO30这几个引脚上拉,否则会导致芯片无法正常启动。

项目目的

本项目旨在将NXP车规级MCU S32K312丰富的车载接口、功能安全能力与海思Hi2821E星闪SLE技术的低功耗、厘米级测距、抗多径及高安全加密特性深度融合,打造新一代“星闪数字车钥匙”参考设计。实现国产化、低成本、高性能的车钥匙方案,推动星闪生态在汽车电子领域的发展。

项目目标

本项目计划基于大联大控股世平集团WPI-NXP S32K312 DVK核心板与海思Hi2821E模组完成星闪车钥匙原型开发并完成在车端部署单一星闪锚点。可实现星闪车钥匙对车端单一锚点的秒级解锁、3米迎宾、10米身份识别的分段式无感体验。

(六)基于海思Hi2821E星闪SLE技术的星闪车钥匙设计

基于海思Hi2821E星闪SLE技术的星闪车钥匙硬件设计

BOM表

序号 器件 位号 封装
1 BS-06-B4AA002 BT1 BAT-SMD_BS-06-B4BK006-1
2 10uF C1,C2 C0805 10uF
3 22uF C3 C0805 22uF
4 PZ254V-11-02P H1 HDR-TH_2P-P2.54-V-M
5 BearPi EBM H21E H2821E BearPi EBM H21E
6 NCD0805R1 LED1 LED0805-R-RD
7 1kΩ R1 R0805 1kΩ
8 M2螺丝 SCREW1,SCREW2,SCREW3,SCREW4 M2螺丝
9 SK-12E12-G5 SW1 SW-TH_SK-12E12-G5
10 TS665CJ SW2,SW3,SW4 SW-TH_4P-L6.0-W6.0-P4.50-LS6.5
11 AMS1117-3.3 U1 SOT-223-3_L6.5-W3.4-P2.30-LS7.0-BR
12 KH-TYPE-C-2P USB1 USB-SMD_KH-TYPE-C-2P

原理图

SCH_H2821E星闪车钥匙

外观

Panel_H2821E星闪车钥匙

基于海思Hi2821E星闪SLE技术的星闪车钥匙软件设计

软件设计结构

.├── NearLinkSLEKey
 │   ├── NearLinkSLEKey.c
 │   ├── sle_measure_dis_client
 │   │   ├── sle_measure_dis_client.c
 │   │   ├── sle_measure_dis_client.h
 │   │   ├── sle_measure_dis_client_slem.c
 │   │   └── sle_measure_dis_client_slem.h
 │   ├── Kconfig
 │   └── CMakeLists.txt
 └──────────────────────────────────────────

NearLinkSLEKey.c

/**
 * Copyright (c) BI1NDF 2025-2025. All rights reserved.
 *
 * Description: NearLink_SLE_Key. \n
 *
 * History: \n
 * 2025-08-25, Create file. \n
 */
#include "boards.h"
#include "pinctrl.h"
#include "gpio.h"
#include "osal_debug.h"
#include "cmsis_os2.h"
#include "app_init.h"
#include "soc_osal.h"
#include "common_def.h"
#include "app_init.h"
#include "sle_measure_dis_client/sle_measure_dis_client.h"

#define TASK_PRIORITY_HADM          27
#define STACK_SIZE_BASELINE 0x200
#define BTH_HADM_SERVICE_STACK_SIZE (STACK_SIZE_BASELINE * 4 + 0x200)

extern int carkey_send_open_cmd(void);
extern int carkey_send_close_cmd(void);
extern int carkey_send_trunk_cmd(void);

static void open_callback_func(pin_t pin, uintptr_t param)
{
    UNUSED(pin);
    UNUSED(param);
    osal_printk("OPEN BUTTON.\r\n");
    carkey_send_open_cmd();
}
static void close_callback_func(pin_t pin, uintptr_t param)
{
    UNUSED(pin);
    UNUSED(param);
    osal_printk("CLOSE BUTTON.\r\n");
    carkey_send_close_cmd();
}
static void trunk_callback_func(pin_t pin, uintptr_t param)
{
    UNUSED(pin);
    UNUSED(param);
    osal_printk("TRUNK BUTTON.\r\n");
    carkey_send_trunk_cmd();
}
static void button_entry(void)
{
    osal_printk("Init BUTTON.\r\n");
    uapi_pin_set_mode(CONFIG_OPEN_PIN, HAL_PIO_FUNC_GPIO);
    uapi_pin_set_mode(CONFIG_CLOSE_PIN, HAL_PIO_FUNC_GPIO);
    uapi_pin_set_mode(CONFIG_TRUNK_PIN, HAL_PIO_FUNC_GPIO);
    uapi_pin_set_pull(CONFIG_OPEN_PIN, PIN_PULL_UP);
    uapi_pin_set_pull(CONFIG_CLOSE_PIN, PIN_PULL_UP);
    uapi_pin_set_pull(CONFIG_TRUNK_PIN, PIN_PULL_UP);
    uapi_gpio_set_dir(CONFIG_OPEN_PIN, GPIO_DIRECTION_INPUT);
    uapi_gpio_set_dir(CONFIG_CLOSE_PIN, GPIO_DIRECTION_INPUT);
    uapi_gpio_set_dir(CONFIG_TRUNK_PIN, GPIO_DIRECTION_INPUT);
    uapi_gpio_register_isr_func(CONFIG_OPEN_PIN, GPIO_INTERRUPT_DEDGE, open_callback_func);
    uapi_gpio_register_isr_func(CONFIG_CLOSE_PIN, GPIO_INTERRUPT_DEDGE, close_callback_func);
    uapi_gpio_register_isr_func(CONFIG_TRUNK_PIN, GPIO_INTERRUPT_DEDGE, trunk_callback_func);
}
void sle_measure_dis_client_task(void)
{
    uint32_t ret;
    ret = measure_dis_client_init();
    check_rc_return_rc(ret, "con sle_measure_dis_client_init");
}
static void slemeasure_entry(void)
{
    osal_printk("Init SLE MEASURE.\r\n");
    osal_task *task_handle = NULL;
    osal_kthread_lock();
    task_handle = osal_kthread_create((osal_kthread_handler)sle_measure_dis_client_task,
                                      0, "SLEMeasureDisClient", BTH_HADM_SERVICE_STACK_SIZE);
    if (task_handle != NULL) {
        osal_kthread_set_priority(task_handle, TASK_PRIORITY_HADM);
        osal_kfree(task_handle);
    }
    osal_kthread_unlock();
}

/* Run the entry. */
app_run(button_entry);
app_run(slemeasure_entry);

sle_measure_dis_client.c

/**
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: SLE uart sample of client. \n
 *
 * History: \n
 * 2023-04-03, Create file. \n
 * 2025-08-27, Add Button for Key. \n
 */
#include "sle_measure_dis_client.h"
#include "sle_errcode.h"
#include "sle_common.h"
#include "sle_device_manager.h"
#include "sle_connection_manager.h"
#include "sle_device_manager.h"
#include "sle_device_discovery.h"
#include "sle_ssap_client.h"
#include "sle_hadm_manager.h"
#include "cmsis_os2.h"
#include "sle_measure_dis_client_slem.h"

uint8_t iq_msg_buff[sizeof(measure_ids_msg_t) + sizeof(measure_dis_stored_iq_data_t)];

#define BLE_SLE_TAG_TASK_DURATION_MS 10
#define ADV_TO_SERVER 2
/* UUID只支持2字节和16字节 */
#define SLEM_UUID_LEN SLE_UUID_LEN
#define KEY_CONNECT_DELAY_TIME 500
#define SLEM_IQ_DATALEN 512

static uint8_t g_stack_enable_status = 0;

uint8_t g_measure_dis_server_addr[SLE_ADDR_LEN] = { 1, 1, 1, 1, 1, 1 };
uint8_t g_measure_dis_client_addr[SLE_ADDR_LEN] = { 2, 2, 2, 2, 2, 2 };
uint8_t g_measure_dis_conn_id = SLEM_CONNET_INVAILD;

measure_dis_server_data_t g_measure_dis_server_data = {
    .server_uuid = {0x11, 0x22, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    .service_uuid = {0x11, 0x33, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
    .property_uuid = {0x11, 0x44, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0},
};

int carkey_send_open_cmd(void)
{
    if (g_measure_dis_conn_id == SLEM_CONNET_INVAILD) {
        osal_printk("SLE not connected, drop OPEN cmd.\r\n");
        return -1;
    }
    return measure_dis_client_write_server(0xA1,(uint8_t *)"OPEN",strlen("OPEN"));
}

int carkey_send_close_cmd(void)
{
    if (g_measure_dis_conn_id == SLEM_CONNET_INVAILD) {
        osal_printk("SLE not connected, drop CLOSE cmd.\r\n");
        return -1;
    }
    return measure_dis_client_write_server(0xA1,(uint8_t *)"CLOSE",strlen("CLOSE"));
}

int carkey_send_trunk_cmd(void)
{
    if (g_measure_dis_conn_id == SLEM_CONNET_INVAILD) {
        osal_printk("SLE not connected, drop TRUNK cmd.\r\n");
        return -1;
    }
    return measure_dis_client_write_server(0xA1,(uint8_t *)"TRUNK",strlen("TRUNK"));
}

errcode_t measure_dis_slem_set_param(uint16_t conn_id)
{
    sle_set_channel_sounding_param_ex_t param = {
        .acb_interval = 0,
        .con_anchor_num = 1,
        .cs_interval = 1,
        .freq_space = 0,
        .is_cs_param_chg = 0,
        .refresh_rate = 1,
    };
    errcode_t ret = sle_set_channel_sounding_param_ex(conn_id, &param);
    if ((ret != ERRCODE_SUCC) || (conn_id == SLEM_CONNET_INVAILD)) {
        osal_printk("slem set param failed, ret = %d.\n", ret);
    }
    osal_printk("slem set param finish. refresh_rate:%d \n", param.refresh_rate);
    return ERRCODE_SUCC;
}

errcode_t measure_dis_slem_set_enable(uint16_t conn_id)
{
    osal_printk("SLEM SET ENABLE conn_id:%x.\r\n", conn_id);
    errcode_t ret = sle_set_channel_sounding_enable(conn_id);
    /* 解决测距使能之后,马上断链导致没有底层回调 */
    if ((ret != ERRCODE_SUCC) || (conn_id == SLEM_CONNET_INVAILD)) {
        osal_printk("slem set enable failed, ret = %d.\n", ret);
    }
    return ERRCODE_SUCC;
}

static int measure_dis_client_discover_key(uint8_t type)
{
    uint32_t ret = ERRCODE_SLE_FAIL;
    uint8_t conn_id = g_measure_dis_conn_id;

    ssapc_find_structure_param_t param = {0};
    param.type = type,
    param.start_hdl = 1,
    param.end_hdl = 0xFFFF,
    param.uuid.len = SLEM_UUID_LEN;
    if (type == SSAP_FIND_TYPE_PRIMARY_SERVICE) {
        if (memcpy_s(param.uuid.uuid, SLEM_UUID_LEN,
            g_measure_dis_server_data.service_uuid, SLEM_UUID_LEN) != EOK) {
            return ERRCODE_MEMCPY;
        }
    } else if (type == SSAP_FIND_TYPE_PROPERTY) {
        if (memcpy_s(param.uuid.uuid, SLEM_UUID_LEN,
            g_measure_dis_server_data.property_uuid, SLEM_UUID_LEN) != EOK) {
            return ERRCODE_MEMCPY;
        }
    }
    ret = ssapc_find_structure(0, conn_id, &param);
    osal_printk("SLE START CLIENT DISCOVER SERVER. conn_id:%d, ret:%d \r\n", conn_id, ret);

    return ret;
}

static void measure_dis_cm_conn_state_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)
{
    unused(pair_state);
    unused(addr);
    unused(disc_reason);
    if (conn_state == SLE_ACB_STATE_CONNECTED) {
        osal_printk("Connected. conn_id:%d\r\n", conn_id);
        g_measure_dis_conn_id = conn_id;
        osal_msleep(MEASURE_DIS_NUM_CARRY_1000);
        measure_dis_client_discover_key(SSAP_FIND_TYPE_PROPERTY);
        ssap_exchange_info_t info = { 0 };
        info.mtu_size = SLEM_IQ_DATALEN;
        info.version = 1;
        ssapc_exchange_info_req(0, conn_id, &info);

        (void)osDelay(KEY_CONNECT_DELAY_TIME);
        measure_dis_slem_set_param(conn_id);
        (void)osDelay(KEY_CONNECT_DELAY_TIME);
        measure_dis_slem_set_enable(conn_id);
    } else if (conn_state == SLE_ACB_STATE_DISCONNECTED) {
        osal_printk("Disconnected disc_reason:0x%x .\r\n", disc_reason);
        g_measure_dis_conn_id = SLEM_CONNET_INVAILD;
        measure_dis_start_scan();
    }
}

errcode_t measure_dis_cm_register_cbks(void)
{
    sle_connection_callbacks_t cm_cbks = { 0 };
    cm_cbks.connect_state_changed_cb = measure_dis_cm_conn_state_cbk;
    cm_cbks.connect_param_update_req_cb = NULL;
    cm_cbks.connect_param_update_cb = NULL;
    cm_cbks.auth_complete_cb = NULL;
    cm_cbks.pair_complete_cb = NULL;
    cm_cbks.read_rssi_cb = NULL;

    return sle_connection_register_callbacks(&cm_cbks);
}

static void measure_dis_dd_enable_cbk(uint8_t status)
{
    osal_printk("sle enable result statue:0x%x .\r\n", status);
    g_stack_enable_status = 1;
}

static void measure_dis_dd_disenable_cbk(uint8_t status)
{
    osal_printk("sle disable result statue:0x%x .\r\n", status);
    g_stack_enable_status = 0;
}

static void measure_dis_dd_seek_result_cbk(sle_seek_result_info_t *seek_result_data)
{
    if (seek_result_data == NULL) {
        return;
    }

    if ((memcmp(g_measure_dis_server_addr, seek_result_data->addr.addr, SLE_ADDR_LEN) == 0)) {
        sle_stop_seek();
        sle_connect_remote_device(&(seek_result_data->addr));
    }
}

errcode_t measure_dis_dm_register_cbks(void)
{
    sle_dev_manager_callbacks_t dm_cbks = {
        .sle_power_on_cb = NULL,
        .sle_enable_cb = measure_dis_dd_enable_cbk,
        .sle_disable_cb = measure_dis_dd_disenable_cbk,
    };
    return sle_dev_manager_register_callbacks(&dm_cbks);
}

errcode_t measure_dis_dd_register_cbks(void)
{
    sle_announce_seek_callbacks_t dd_cbks  = {
        .announce_enable_cb = NULL,
        .announce_disable_cb = NULL,
        .announce_terminal_cb = NULL,
        .seek_enable_cb = NULL,
        .seek_disable_cb = NULL,
        .seek_result_cb = measure_dis_dd_seek_result_cbk,
    };
    return sle_announce_seek_register_callbacks(&dd_cbks);
}

STATIC void measure_dis_ssapc_find_structure_cbk(uint8_t client_id, uint16_t conn_id,
    ssapc_find_service_result_t *service, errcode_t status)
{
    osal_printk("[ssap client] find structure cbk client: %d conn_id:%d status: %d \n",
        client_id, conn_id, status);
    osal_printk("[ssap client] find structure cbk start_hdl: %d end_hdl:%d \n",
        service->start_hdl, service->end_hdl);

    g_measure_dis_server_data.begin_hdl= service->start_hdl;
    g_measure_dis_server_data.end_hdl = service->end_hdl;
}

STATIC void measure_dis_ssapc_find_property_cbk(uint8_t client_id, uint16_t conn_id,
    ssapc_find_property_result_t *property, errcode_t status)
{
    osal_printk("[ssap client] find property cbk client: %d conn_id:%d status: %d \n",
        client_id, conn_id, status);
    osal_printk("[ssap client] find property cbk handle: %d \n", property->handle);

    g_measure_dis_server_data.property_handle = property->handle;
}

STATIC void measure_dis_ssapc_find_structure_complete_cbk(uint8_t client_id, uint16_t conn_id,
    ssapc_find_structure_result_t *structure_result, errcode_t status)
{
    osal_printk("[ssap client] find structure complete cbk client: %d conn_id:%d status: %d \n",
                client_id, conn_id, status);
    if (structure_result == NULL) {
        return;
    }

    osal_printk("SERVICES DISCOVER COMPLETE. \r\n");
}

void measure_dis_client_msg_proc(uint8_t *data, uint16_t data_len)
{
    unused(data_len);
    uint32_t ret = ERRCODE_SLE_FAIL;
    measure_ids_msg_t *slem_profile_msg = (measure_ids_msg_t *)(data);

    switch (slem_profile_msg->type) {
        case SLEM_PROFILE_MSG_IQ:
            break;
        default:
            for (int i = 0; i < data_len; i++) {
                osal_printk("[%d]:%d\r\n", i, data[i]);
            }
            break;
    }

    if (unlikely(ret != ERRCODE_SLE_SUCCESS)) {
        osal_printk("client proc msg failed MSG_TYPE:0x%x ret:0x%x \r\n", slem_profile_msg->type, ret);
    }
}

/**
 * @brief  数据通知处理函数
 */
STATIC void measure_dis_ssapc_notification_cbk(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
    errcode_t status)
{
    unused(client_id);
    unused(status);
    osal_printk("[ssap client] notification info cbk client handle:%d, conn_id:0x%x, data_len:0x%x\n",
        data->handle, conn_id, data->data_len);

    measure_dis_client_msg_proc(data->data, data->data_len);
}

/**
 * @brief  数据指示处理函数
 */
STATIC void measure_dis_ssapc_indication_cb(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
    errcode_t status)
{
    unused(client_id);
    osal_printk("[ssap client] indication info cbk client %d,mtu_size:%d, version:0x%x, status:0x%x\n",
        conn_id, data->handle, data->type, status);
}

/**
 * @brief  MTU信息交换响应处理函数
 */
STATIC void measure_dis_ssapc_exchange_info_cbk(uint8_t client_id, uint16_t conn_id, ssap_exchange_info_t *param,
    errcode_t status)
{
    unused(client_id);
    osal_printk("[ssap client] exchange info cbk client %d,mtu_size:%d, version:0x%x, status:0x%x\n",
        conn_id, param->mtu_size, param->version, status);
}

errcode_t measure_dis_ssapc_register_cbks(void)
{
    ssapc_callbacks_t ssapc_cbks;

    osal_printk("[ssap client] client register cbk\r\n");
    /* 服务发现模块回调函数 */
    ssapc_cbks.find_structure_cb = measure_dis_ssapc_find_structure_cbk;
    ssapc_cbks.ssapc_find_property_cbk = measure_dis_ssapc_find_property_cbk;
    ssapc_cbks.find_structure_cmp_cb = measure_dis_ssapc_find_structure_complete_cbk;
    ssapc_cbks.read_cfm_cb = NULL;
    ssapc_cbks.read_by_uuid_cmp_cb = NULL;
    ssapc_cbks.write_cfm_cb = NULL;
    ssapc_cbks.exchange_info_cb = measure_dis_ssapc_exchange_info_cbk;
    ssapc_cbks.notification_cb = measure_dis_ssapc_notification_cbk;
    ssapc_cbks.indication_cb = measure_dis_ssapc_indication_cb;
    return ssapc_register_callbacks(&ssapc_cbks);
}

errcode_t measure_dis_set_local_addr(uint8_t *addr)
{
    sle_addr_t sle_addr = {0};
    if (memcpy_s(sle_addr.addr, SLE_ADDR_LEN, addr, SLE_ADDR_LEN) != EOK) {
        return ERRCODE_MEMCPY;
    };
    return sle_set_local_addr(&sle_addr);
}

errcode_t measure_dis_set_local_name(uint8_t *name, uint8_t len)
{
    return sle_set_local_name(name, len);
}

void measure_dis_protocol_stack_init(uint8_t *addr, uint8_t *name)
{
    uint32_t ret = ERRCODE_SLE_SUCCESS;

    ret |= measure_dis_dm_register_cbks();
    ret |= measure_dis_cm_register_cbks();
    ret |= measure_dis_dd_register_cbks();
    ret = enable_sle();
    while (g_stack_enable_status != 1) {
        osal_msleep(BLE_SLE_TAG_TASK_DURATION_MS);
    }
    measure_dis_set_local_addr(addr);
    measure_dis_set_local_name(name, strlen((char *)name));
}

static int measure_dis_client_write_proc(uint8_t type, uint16_t len, uint8_t *data)
{
    errcode_t ret = ERRCODE_FAIL;
    ssapc_write_param_t param = {0};
    uint8_t conn_id = g_measure_dis_conn_id;

    param.handle = g_measure_dis_server_data.property_handle;
    param.type = type;
    param.data_len = len;
    param.data = data;

    ret = ssapc_write_cmd(0, conn_id, &param);
    if (ret != ERRCODE_SUCC) {
        osal_printk("CLIENT SEND (KEY) FAIL. cid:%d, handle:%d \r\n", conn_id, param.handle);
    }

    return ret;
}

int measure_dis_client_write_server(uint32_t type, uint8_t *data, uint32_t data_len)
{
    uint32_t ret = ERRCODE_SUCC;
    uint16_t len = sizeof(measure_ids_msg_t) + data_len;
    osal_printk("client send to server, len:0x%x.\r\n", len);
    measure_ids_msg_t *slem_msg = (measure_ids_msg_t *)osal_kmalloc(len, 0);
    if (slem_msg == NULL) {
        return ERRCODE_MALLOC;
    }
    slem_msg->type = type;
    slem_msg->len = data_len;
    if (memcpy_s(slem_msg->data, data_len, data, data_len) != EOK) {
        osal_kfree(slem_msg);
        return ERRCODE_MEMCPY;
    }
    ret = measure_dis_client_write_proc(0, len, (uint8_t *)(slem_msg));
    if (ret != ERRCODE_SUCC) {
        osal_printk("client send to server failed.\n");
    }
    osal_kfree(slem_msg);

    return ret;
}

errcode_t measure_dis_set_scan(void)
{
    errcode_t ret;
    sle_seek_param_t param = {0};
    param.own_addr_type = 0;
    param.filter_duplicates = 1;
    param.seek_filter_policy = 1;
    param.seek_phys = 1;
    param.seek_type[0] = 1;
    param.seek_interval[0] = 0x12c0;
    param.seek_window[0] = 0x500;
    ret = sle_set_seek_param(&param);

    return ret;
}

int measure_dis_start_scan(void)
{
    errcode_t ret;
    ret = measure_dis_set_scan();
    if (ret != ERRCODE_SLE_SUCCESS) {
        return ret;
    }
    ret = sle_start_seek();
    if (ret != ERRCODE_SLE_SUCCESS) {
        return ret;
    }
    return ERRCODE_SLE_SUCCESS;
}

int measure_dis_client_init(void)
{
    uint32_t ret = ERRCODE_SLE_SUCCESS;
    uint8_t measure_dis_name[SLE_NAME_MAX_LEN] = {'s', 'l', 'e', 'm', '-', 'c', '\0'};
    osal_printk("START CLIENT INIT .\r\n");
    measure_dis_protocol_stack_init(g_measure_dis_client_addr, measure_dis_name);
    measure_dis_reg_callbacks();
    ret = measure_dis_ssapc_register_cbks();
    measure_dis_start_scan();
    return ret;
}
/* 钥匙 */

// btc回调函数
// lm_gle_rssi_info_cb(uint16_t conn_id, int8_t rssi)
void carkey_sle_rssi_report_cbk(uint16_t conn_id, int8_t rssi)
{
    osal_printk("SLE RSSI REPORT conn_id:%d, rssi:%d .\r\n", conn_id, rssi);
}

sle_measure_dis_client.h

/**
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: SLE MEASURE_DIS sample of client. \n
 *
 * History: \n
 * 2023-04-03, Create file. \n
 */
#ifndef SLE_MEASURE_DIS_CLIENT_H
#define SLE_MEASURE_DIS_CLIENT_H

#include <stdint.h>
#include <securec.h>
#include "errcode.h"
#include "std_def.h"
#include "soc_osal.h"
#include "common_def.h"
#include "sle_ssap_client.h"

#define MEASURE_DIS_NUM_CARRY_10_7     10000000
#define MEASURE_DIS_NUM_CARRY_1000     1000
#define MEASURE_DIS_NUM_CARRY_100      100
#define MEASURE_DIS_NUM_CARRY_10       10
#define MEASURE_DIS_NUM_CARRY_2        2

#define SLEM_CONNET_INVAILD 0xFF

typedef enum {
    SLEM_PROFILE_MSG_IQ = 0xFFFFFFEA,
} slem_profile_msg_type_t;

typedef struct {
    uint32_t        type;                             /*!< 消息类型 */
    uint32_t        len;                              /*!< 消息类型 */
    uint8_t         data[0];
} measure_ids_msg_t;

typedef struct server_data {
    uint8_t property_uuid[SLE_UUID_LEN];
    uint8_t server_uuid[SLE_UUID_LEN];
    uint8_t service_uuid[SLE_UUID_LEN];
    uint16_t begin_hdl;
    uint16_t end_hdl;
    uint16_t property_handle;
} measure_dis_server_data_t;

#define check_rc_return_rc(rc, err)                                                             \
    do {                                                                                         \
        if ((rc) != ERRCODE_SUCC) {                                                              \
            osal_printk("CARKEY ERROR: %s fail!: call %s return 0x%x!\n", err, __FUNCTION__, rc);   \
        }                                                                                        \
    } while (0)

int measure_dis_client_write_server(uint32_t type, uint8_t *data, uint32_t data_len);
int measure_dis_client_init(void);
int measure_dis_start_scan(void);

#endif

sle_measure_dis_client_slem.c

/**
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: SLE uart sample of client. \n
 *
 * History: \n
 * 2023-04-03, Create file. \n
 */
#include "sle_measure_dis_client.h"
#include "sle_errcode.h"
#include "sle_common.h"
#include "sle_ssap_client.h"
#include "sle_hadm_manager.h"
#include "cmsis_os2.h"
#include "sle_measure_dis_client_slem.h"

measure_dis_stored_iq_data_t g_local_iq_data;
uint8_t g_next_idx;
uint8_t g_store_local_iq_complete;

uint8_t g_iq_debug_flag = 0;

void measure_dis_print_iq_data(uint8_t num, uint8_t role, sle_channel_sounding_iq_trans_t *report)
{
    if (!g_iq_debug_flag) {
        return;
    }
    osal_printk("[iq_data]%d,%d,%d,%d,0x%x,",
                num, role, report->timestamp_sn, report->samp_cnt, report->rssi);
    sle_channel_sounding_qte_trans_t *data = report->data;
    uint16_t i = 0;
    while (i < POSALG_DATA_NUM) {
        osal_printk("[%d]:0x%x,0x%x", i, data[i].i_data, data[i].q_data);
        i++;
        if (i == POSALG_DATA_NUM) {
            osal_printk(".\r\n");
        } else {
            osal_printk(";");
        }
    }
}

errcode_t measure_dis_store_local_iq(sle_channel_sounding_iq_report_t *report)
{
    if (report->report_idx == 0) {
        g_local_iq_data.samp_cnt = 0;
        g_local_iq_data.rssi = report->rssi[0];
        g_local_iq_data.es_sn = report->es_sn;
        g_local_iq_data.timestamp_sn = report->timestamp_sn;
#if (defined(GLE_CS_MODE3_SUPPORT))
        g_local_iq_data.tof_result = report->tof_result;
#endif
        g_next_idx = 0;
    }
    if ((report->report_idx >= MEASURE_DIS_IQ_REPORT_CNT_MAX || report->timestamp_sn != g_local_iq_data.timestamp_sn) ||
        (g_next_idx != report->report_idx)) {
        osal_printk("SLEM STORE LOCAL IQ FAIL. report_idx:%d, timestamp:%d, g_next_idx:%d. \r\n",
                    report->report_idx, report->timestamp_sn, g_next_idx);
        return ERRCODE_INVALID_PARAM;
    }
    g_local_iq_data.samp_cnt += report->samp_cnt;
    uint8_t offset = report->report_idx * SLE_CS_IQ_REPORT_COUNT;
    for (uint8_t i = 0; i < report->samp_cnt; i++) {
        g_local_iq_data.data[offset + i].i_data = report->i_data[i];
        g_local_iq_data.data[offset + i].q_data = report->q_data[i];
    }
    osal_printk("local iq es:%d, time_stamp:%d group:%d, num:%d \r\n",
                report->es_sn, report->timestamp_sn, report->report_idx, 0);
    g_next_idx = report->report_idx + 1;

#if (defined(GLE_CS_MODE3_SUPPORT))
    osal_printk("local tof_result = %d.%03d \r\n", report->tof_result / MEASURE_DIS_NUM_CARRY_1000,
                report->tof_result % MEASURE_DIS_NUM_CARRY_1000);
#endif

    return ERRCODE_SUCC;
}

errcode_t measure_dis_recv_local_iq(uint16_t conn_id, sle_channel_sounding_iq_report_t *report)
{
    osal_printk("enter slem_recv_local_iq conn_id:%d. \r\n", conn_id);
    errcode_t ret = ERRCODE_SUCC;

    ret = measure_dis_store_local_iq(report);
    if (ret != ERRCODE_SUCC) {
        return ERRCODE_INVALID_PARAM;
    }

    if (report->report_idx + 1 == MEASURE_DIS_IQ_REPORT_CNT_MAX) {
        /* 如果是钥匙则发送给从锚点 */
        measure_dis_print_iq_data(0, 0, (sle_channel_sounding_iq_trans_t *)(&g_local_iq_data));
        measure_dis_client_write_server(SLEM_PROFILE_MSG_IQ,
                                        (uint8_t *)&g_local_iq_data, sizeof(sle_channel_sounding_iq_trans_t));
    }
    return ret;
}

void measure_dis_read_local_cs_caps_cb(sle_channel_sounding_caps_t *caps, errcode_t status)
{
    UNUSED(caps);
    osal_printk("SLEM READ LOCAL CAPS. status:%d. \r\n", status);
}

void measure_dis_read_remote_cs_caps_cb(uint16_t conn_id, sle_channel_sounding_caps_t *caps, errcode_t status)
{
    UNUSED(caps);
    UNUSED(conn_id);
    osal_printk("SLEM READ REMOTE CAPS. status:%d. \r\n", status);
}

void measure_dis_set_cs_param_cb(uint16_t conn_id, errcode_t status)
{
    UNUSED(conn_id);
    osal_printk("SLEM SET PARAM. status:%d. \r\n", status);
}

void measure_dis_cs_state_changed_cb(uint8_t slem_status, errcode_t status)
{
    unused(slem_status);
    osal_printk("SLEM STATE CHANGED. status:%d. \r\n", status);
}

void measure_dis_cs_iq_report_cb(uint16_t conn_id, sle_channel_sounding_iq_report_t *report)
{
    if (report->report_idx == 0) {
        osal_printk("RECEIVE LOCAL IQ. timestamp_sn:%d \r\n",  report->timestamp_sn);
    }
    measure_dis_recv_local_iq(conn_id, report);
}

errcode_t measure_dis_reg_callbacks(void)
{
    sle_hadm_callbacks_t scd_cbks = {0};

    scd_cbks.read_local_cs_caps_cb = measure_dis_read_local_cs_caps_cb;
    scd_cbks.read_remote_cs_caps_cb = measure_dis_read_remote_cs_caps_cb;
    scd_cbks.cs_state_changed_cb = measure_dis_cs_state_changed_cb;
    scd_cbks.cs_iq_report_cb = measure_dis_cs_iq_report_cb;

    errcode_t ret = sle_hadm_register_callbacks(&scd_cbks);
    osal_printk("SCD SET CALLBACK DONE. ret = %x.\r\n", ret);
    return ret;
}

sle_measure_dis_client_slem.h

/**
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 *
 * Description: SLE MEASURE_DIS sample of client. \n
 *
 * History: \n
 * 2023-04-03, Create file. \n
 */
#ifndef SLE_MEASURE_DIS_CLIENT_SLEM_H
#define SLE_MEASURE_DIS_CLIENT_SLEM_H

#include "sle_measure_dis_client_slem.h"
#include "sle_hadm_manager.h"

#define MEASURE_DIS_IQ_REPORT_CNT_MAX 1
#define POSALG_DATA_NUM 79
#define IQ_DATA_MAX (MEASURE_DIS_IQ_REPORT_CNT_MAX * SLE_CS_IQ_REPORT_COUNT)

typedef struct {
    uint16_t i_data;
    uint16_t q_data;
} measure_dis_stored_qte_trans_t;

typedef struct {
    uint8_t samp_cnt;
    uint8_t rssi;
    uint16_t es_sn;
    uint32_t timestamp_sn;
    measure_dis_stored_qte_trans_t data[IQ_DATA_MAX];
#if (defined(GLE_CS_MODE3_SUPPORT))
    uint32_t tof_result;
#endif
} measure_dis_stored_iq_data_t;

typedef struct {
    uint16_t i_data;
    uint16_t q_data;
} sle_channel_sounding_qte_trans_t;

typedef struct {
    uint8_t samp_cnt;
    uint8_t rssi;
    uint16_t es_sn;
    uint32_t timestamp_sn;
    sle_channel_sounding_qte_trans_t data[IQ_DATA_MAX];
    uint32_t tof_result;
} sle_channel_sounding_iq_trans_t;

errcode_t measure_dis_reg_callbacks(void);

#endif

Kconfig

#===============================================================================
# @brief    Kconfig file.
# Copyright (c) BI1NDF 2025-2025. All rights reserved.
#===============================================================================

config OPEN_PIN
    int
    prompt "Choose OPEN pin."
    depends on SAMPLE_SUPPORT_NEARLINKSLEKEY
    default 15

config CLOSE_PIN
    int
    prompt "Choose CLOSE pin."
    depends on SAMPLE_SUPPORT_NEARLINKSLEKEY
    default 16
    
config TRUNK_PIN
    int
    prompt "Choose TRUNK pin."
    depends on SAMPLE_SUPPORT_NEARLINKSLEKEY
    default 17

CMakeLists.txt

#===============================================================================
# @brief    cmake file
# Copyright (c) BI1NDF 2025-2025. All rights reserved.
#===============================================================================
set(SOURCES_LIST
    ${CMAKE_CURRENT_SOURCE_DIR}/sle_measure_dis_client/sle_measure_dis_client.c
    ${CMAKE_CURRENT_SOURCE_DIR}/sle_measure_dis_client/sle_measure_dis_client_slem.c
    ${CMAKE_CURRENT_SOURCE_DIR}/NearLinkSLEKey.c
)
set(HEADER_LIST ${CMAKE_CURRENT_SOURCE_DIR}/sle_measure_dis_client)

set(SOURCES "${SOURCES}" ${SOURCES_LIST} PARENT_SCOPE)
set(PUBLIC_HEADER "${PUBLIC_HEADER}" ${HEADER_LIST} PARENT_SCOPE)

基于海思Hi2821E星闪SLE技术的星闪车钥匙实物

基于海思Hi2821E星闪SLE技术的星闪车钥匙实物_正面
基于海思Hi2821E星闪SLE技术的星闪车钥匙实物_背面

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐