HID 私有协议

本示例用两个工程分别扮演 HID 私有协议用户指南 中的 master 和 slave 两个角色,他们之间可以互相发送消息,演示该协议的基本使用方法:

  • Master 首次使用或者按键触发时自动配对,Slave 首次使用或者上电时自动配对。

  • Master 和 Slave 自动回连。

  • Master 自动按照上报率上报数据,按键切换上报率、开关上报。

  • Slave 按键下发指令。

  • 连线、数据发送的 led 指示。

环境需求

该示例支持以下开发工具包:

开发套件

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

更多要求,请参考 快速入门

硬件连线

请参考 快速入门 中的 EVB 接口和模块

示例会用到 3 个 LED 显示相关状态,参考下表:

led

类型

说明

led 名

pin

link status

连接点亮/断线熄灭

LED4

P3_0

data transmit

master 向 slave 方向的数据传输每 1000 笔翻转一次

LED3

P3_1

slave cmd

slave 的 slave cmd 按键按下翻转一次

LED2

P1_0

master 示例会用到 3 个按键,参考下表:

master 按键

类型

说明

按键名

pin

pair

触发配对

KEY5

P1_2

switch report rate

切换上报率

KEY4

P0_6

switch report

开关上报

KEY3

P0_2

slave 示例会用到 1 个按键,参考下表:

slave 按键

类型

说明

按键名

pin

slave cmd

翻转 master 和 slave 的 LED

KEY5

P1_2

配置选项

配置宏 APP_MSG_COUNT_BLINK_TH 修改 master 向 slave 方向的数据传输时的 LED 闪烁频率。
配置宏 APP_PAIR_TIME 修改 master 按键配对时间和 slave 上电配对时间。

编译和下载

该示例可以在 SDK 文件夹中找到:

Project file:

samples\ppt\ppt_sync\proj_master\rtl87x2g\mdk
samples\ppt\ppt_sync\proj_slave\rtl87x2g\mdk

Project file:

samples\ppt\ppt_sync\proj_master\rtl87x2g\gcc
samples\ppt\ppt_sync\proj_slave\rtl87x2g\gcc

编译和运行该示例请遵循以下步骤:

  1. 打开项目文件。

  2. 要编译目标文件, 请参考 快速入门编译 APP Image 中列出的步骤。

  3. 编译成功后,项目文件所在目录的子目录 bin 下会生成app bin文件 app_MP_sdk_xxx.bin

  4. 要下载app bin到EVB板,请参考 快速入门MP Tool 中列出的步骤。

  5. 在EVB板上按下reset按钮,程序将开始运行。

测试验证

将两个示例工程分别烧录到两个EVB板后,用按键控制,然后使用 Debug Analyzer 工具获取日志、观察LED状态,查看运行结果。

测试阶段

  1. 首次使用此示例时,没有绑定信息,按下reset按钮后,两端都会自动进入配对状态,打印下面的日志。

    [APP] !**app_sync_start: pair
    

    配对成功后会打印下面的日志,并且点亮link status led。

    [APP] !**app_sync_event_cb: paired
    
  2. Master 连上之后默认会打开数据上报,此时 master 和 slave 会以相同的频率闪烁 data transmit led。 按下 master 的 switch report rate 按键,切换 master 上报率,会看到 master 和 slave 的 data transmit led 闪烁频率改变。 上报率切换顺序为 125, 250, 500, 1000, 2000, 4000 的循环,默认 1000。 led 在默认闪烁频率下,1K 时 1s 翻转一次,4K 时 0.25s 翻转一次,125 时 8s 翻转一次,其他依次类推。

  3. 按下 master 的 switch report 按键一次,开关 master 数据上报。当上报关闭时,master 和 slave 的 data transmit led 停止闪烁。

  4. 按下 slave 的 slave cmd 按键一次,master 和 slave 的 slave cmd led 同时翻转。

  5. 按下 master 的 pair 按键一次,master 会重新进入配对模式。重启 slave,slave 也会重新进入配对模式。重新配对成功后,会打印下面的日志,并且点亮 link status led。

    [APP] !**app_sync_event_cb: paired
    

代码介绍

本章的主要目的是帮助 APP 开发人员熟悉相关的开发流程。本章将按照以下几个部分进行介绍:

源码路径

  • Project directory: samples\ppt\ppt_sync\proj_xxx

  • Source code directory: samples\ppt\ppt_sync\src

应用程序项目中的源文件当前被分为几个组,如下所示。

└── Project: master(slave)
    └── secure_only_app
        ├── Device                   includes startup code
        ├── CMSE Library             Non-secure callable lib
        ├── Lib                      includes all binary symbol files that user application is built on
            ├── ROM_NS.lib
            ├── lowerstack.lib
            ├── rtl87x2g_sdk.lib
            ├── rtl87x2g_io.lib
            └── ppt_sync_master(slave).lib       includes the sync protocol stack lib
        ├── Peripheral               includes all peripheral drivers and module code used by the application
        └── APP                      includes the ble_peripheral user application implementation
            ├── main_ns.c            includes the io, os and platform initialization
            ├── app_task.c           includes app task initialization and main loop
            ├── app_bsp.c            includes the button, led and timer etc. functions
            └── app_sync_master(slave).c         includes sync protocol state machine management and button/led handle

初始化

当 EVB 板启动并且芯片被重置时,main 函数将被调用,它执行以下初始化函数:

int main(void)
{
    /* increase cpu clock to support 4k report rate */
    uint32_t actual_mhz = 0;
    pm_cpu_freq_set(125, &actual_mhz);

    DBG_DIRECT("Non-Secure World: main, clock %dMHz", actual_mhz);

    app_task_init();

    /* Start scheduler. */
    os_sched_start();

    /* Should not reach here as the scheduler is already started. */
    for (; ;)
    {
    }
}
  • pm_cpu_freq_set() 用于提升 CPU 频率,让 4K 高上报率时来得及处理。

  • app_task_init 用于初始化 app task,注册 task 主函数 app_main_task

  • os_sched_start 用于启动 os scheduler。当调度器启动后,app task 的主函数 app_main_task 会得到调度。

  • app_main_task 执行 sync protocol 和相关内容的初始化,示例如下。

void app_main_task(void *p_param)
{
    /* This task calls secure side functions. So allocate a secure context for
     * it. */
    //must locate at the first line
    os_alloc_secure_ctx(configMINIMAL_SECURE_STACK_SIZE);

    /* avoid ppt initialization conflict with ble psd procedure */
    os_delay(1000);

    /* init the basic peripheral */
    swd_pin_disable(); // On EVB, led pin reuse the swd pin, so need disable the swd
    led_init();
    app_hw_timer_init();

    /* init sync protocol */
    app_sync_init();
    app_sync_start();

    /* init button after app_sync_start since button may trigger interrupt to change the FSM */
    btn_init(app_handle_btn);

#if DLPS_EN
    /* call pwr_mgr_init last to make sure sync_dlps_init (app_sync_init) execute before DLPS_IORegister (pwr_mgr_init) */
    void pwr_mgr_init(void);
    pwr_mgr_init();
#endif

    while (1)
    {
        /* nothing to do */
        os_delay(1000000);
    }
}

注意

io、sync protocol 和平台 power manager 的初始化有一些依赖关系,注意先后顺序。

app_sync_init 会按角色初始化协议,并注册各类回调:

  • 事件回调 app_sync_event_cb

  • 接收回调 app_sync_receive_msg_cb

  • 心跳回调 app_sync_hb_cb

而在 sync_msg_send() 时都会注册消息发送回调 app_sync_send_msg_cb

状态机和事件处理

状态机运转的主要函数是 app_sync_start ,它会根据绑定信息和按键(上电状态)等信息决定是执行配对还是连接。 当配对和连接触发后,会产生各类事件,在 app_sync_event_cb 处理。例如配对失败事件,会重新执行 app_sync_start 。 另外按键也会产生事件,app_handle_btn 根据按键类型执行不同的动作,例如执行重新配对、发送数据等。 当 master 停止数据上报时,会进入 heartbeat 低功耗状态,这时会执行心跳回调 app_sync_hb_cb,通知心跳状态的进出。

数据收发

master 和 slave 之间会相互发送数据。master 打开上报时,会按照上报率定时持续发送消息,双方分别会根据发送和接收的消息数量来闪烁 led。 slave 只有按键按下时,才会发送控制 led 的消息。

定义格式如下,用户可以根据实际需要修改和扩充。 master 上报函数为 app_report_data ,数据格式如下:

/* Data send from master to slave */
typedef struct
{
    uint8_t opcode;
    uint32_t pkt_id;
    uint8_t data[0];
} __attribute__((packed)) app_master_pdu_t;

slave 下发的控制双方 led 状态函数为 app_send_cmd ,数据格式如下:

/* Data send from slave to master */
typedef struct
{
    uint8_t opcode;
    bool state;
} __attribute__((packed)) app_slave_pdu_t;