w494143467 发表于 2021-6-14 11:19

【环境专家之智能手表】Part6:BLE广播温度、湿度和气压数据

本帖最后由 w494143467 于 2021-6-14 17:00 编辑

<p><strong>1.介绍</strong></p>

<p>通信协议定义完成之后,那就要开始和传感器进行数据通信了,首先选择最难的【BME680】,【BME680】是集成式高精度气体、压力、湿度和温度传感器,安森美官方也提供了相关的例程,为什么说他最难呢,需要周期性的进行数据采集,而且带有BSEC算法库,处理起来会比一般的传感器难。</p>

<p><strong>2.详细设计</strong></p>

<p>其实官方提供了代码例程还是非常不错的,不需要自己从0开发驱动代码,但是合并代码还是有一定的难度,这里在移植的过程中用了两种方案。</p>

<p>方案1:选择【ble_perpheral_server_hrp】心率例程为初始工程,将【BME680 + BSEC Example(RSL10-SENSE-GEVK)】传感器例程移植到心率例程上(失败);</p>

<p>方案2:选择【BME680 + BSEC Example(RSL10-SENSE-GEVK)】传感器例程为初始工程,将【ble_perpheral_server_hrp】心率例程移植到心率例程上;</p>

<p>一开始的时候我选择的是【方案1】,因为上一篇选择的就是【ble_perpheral_server_hrp】心率例程,所以为了在原始的工程上继续下去,所以刚开始选择了方案1,但是在添加库的时候出现了一些问题,由于【BME680 + BSEC Example(RSL10-SENSE-GEVK)】传感器例程使用的是HAL库,而且采用了软件定时器【Software Timer】,这与心率例程有冲突,而且在【Board Support】中没有BME680这个传感器选项,导致移植起来困难比较多,尝试了移植,但是没能成功,主要原因是【Software Timer】移植失败。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图1</p>

<p>接下来就使用【方案2】了,方案2移植过程也不是很顺利,挺多坎坷,这里分享一下经验,首先在【BME680 + BSEC Example(RSL10-SENSE-GEVK)】传感器例程的配置中添加上这些库。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图2</p>

<p>可以看到这里例程中是有很多个传感器的库。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图3</p>

<p>然后将上一篇的那些文件拷贝到该项目中,主要包含这几个文件。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图4</p>

<p>然后将上一篇中【app.c】中的内容拷贝到该项目中的【main.c】中。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图5</p>

<p>这里有一个<strong>大坑</strong>需要注意一下,移植过来的内容他会报错有很多未定义的数据结构,那就是缺少了一定宏定义,这个问题我找了挺久的,把心率例程中的预处理全部搬过来就行了,但是直接写入如下配置文件会出现一个问题,当重新配置库【xxx.rteconfig】文件或重启IDE之后,这个预处理会被初始化。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图6</p>

<p>这时候只能找别的方法初始化这些宏定义,经过尝试,在【rsl10.h】这个头文件中对初始化这些宏定义即可,代码如下;</p>

<pre>
<code class="language-cpp">#define RSL10_CID 101
#define CFG_PRF_DISS 1
#define CFG_PRF_HRPS 1
#define SECURE_CONNECTION
#define APP_BONDLIST_SIZE 28
#define CFG_BOND_LIST_IN_NVR2 true
#define CFG_BLE 1
#define CFG_ALLROLES 1
#define CFG_APP
#define CFG_APP_BATT
#define CFG_ATTS 1
#define CFG_CON        8
#define CFG_EMB 1
#define CFG_HOST 1
#define CFG_RF_ATLAS 1
#define CFG_ALLPRF 1
#define CFG_PRF 1
#define CFG_NB_PRF 8
#define CFG_CHNL_ASSESS 1
#define CFG_SEC_CON 1
#define CFG_EXT_DB
#define CFG_PRF_BASS 1</code></pre>

<p>这些完成之后,一般编译都能够正常通过,编译通过了还不能用,由于两个例程都是一个项目,那么肯定会有重复初始化的地方,首先解决时钟重复初始化或者遗漏的地方,main函数内容如下所示;</p>

<pre>
<code class="language-cpp">int main(void)
{

        /* Configure hardware and initialize BLE stack */
        Device_Initialize();

        BDK_Initialize();

        /* Debug/trace initialization. In order to enable UART or RTT trace,
       * configure the 'RSL10_DEBUG' macro in app_trace.h */
        TRACE_INIT();
        PRINTF("\n\rble_peripheral_server_bond has started %s!\r\n", __TIME__);

        /* Configure application-specific advertising data and scan response data.
       * The advertisement period will change after 30 s as per 5.1.1 */
        APP_SetAdvScanData();
        APP_FollowSetAdvScanData();

        /* Initialize the default device name characteristic data */
        APP_InitDevInfo();

//        /* Configure Battery Service Server */
//        BASS_Setup();

        /* Configure Device Information Service Server */
        DISS_Setup();

        /* Configure Heart Rate Service Server */
        HRPS_Setup();

        /* Add application message handlers */
        MsgHandler_Add(TASK_ID_GAPM, APP_GAPM_GATTM_Handler);
        MsgHandler_Add(GATTM_ADD_SVC_RSP, APP_GAPM_GATTM_Handler);
        MsgHandler_Add(TASK_ID_GAPC, APP_GAPC_Handler);
        MsgHandler_Add(APP_LED_TIMEOUT, APP_LED_Timeout_Handler);
        MsgHandler_Add(APP_BATT_LEVEL_LOW, APP_BASS_BattLevelLow_Handler);
        MsgHandler_Add(APP_TIMEOUT_WHITELIST, APP_WhitelistTimerHandler);

        /* Reset the GAP manager. Trigger GAPM_CMP_EVT / GAPM_RESET when finished. See APP_GAPM_GATTM_Handler */
        GAPM_ResetCmd();

    LED_Initialize(LED_RED);
    LED_Initialize(LED_GREEN);
    LED_Initialize(LED_BLUE);

    BTN_Initialize(BTN0);

    printf("\r\nAPP: BME680_BSEC example.\r\n");

    /* Clear saved BSEC state if button is pressed after reset. */
    if (BTN_Read(BTN0) == BTN_PRESSED)
    {
      I2CEeprom m24rf64;
      uint8_t buffer = { 0 };

      I2CEeprom_Initialize(APP_BSEC_EEPROM_I2C_ADDR,
                APP_BSEC_EEPROM_PAGE_SIZE, &amp;m24rf64);

      I2CEeprom_Write(APP_BSEC_EEPROM_ADDR, buffer,
                APP_BSEC_EEPROM_MAGIC_SIZE, &amp;m24rf64);

      printf("APP: Deleted saved BSEC state.\r\n");
    }

    /* Initialize BSEC library */
    {
      bsec_env_return_val retval;
      bsec_env_init_struct bsec_init_params = APP_BSEC_INIT_STRUCT_VALUE;

      retval = BSEC_ENV_Initialize(&amp;bsec_init_params);

      ASSERT_DEBUG(retval.bme680_status == BME680_OK);
      ASSERT_DEBUG(retval.bsec_status == BSEC_OK);
    }

    LED_On(LED_GREEN);
    HAL_Delay(500);
    LED_Off(LED_GREEN);

    SwTimer_Initialize(&amp;bsec_timer);
    SwTimer_ExpireInMs(&amp;bsec_timer, 1);

    printf("APP: Entering main loop.\r\n");
    while (1)
    {
//            Kernel_Schedule();    /* Dispatch all events in Kernel queue */
//                Sys_Watchdog_Refresh();
//                SYS_WAIT_FOR_EVENT;    /* Wait for an event before re-executing the scheduler */
      /* Execute any events that occurred and refresh Watchdog. */
      BDK_Schedule();

      if (SwTimer_IsExpired(&amp;bsec_timer) == true)
      {
            int64_t next_call;

            next_call = BSEC_ENV_Process(&amp;bsec_process_params);
            SwTimer_ExpireInMs(&amp;bsec_timer, next_call);

            printf("BSEC: Next process call in: %lu ms.\r\n",
                  (uint32_t) next_call);

            LED_Toggle(LED_GREEN);
      }

      SYS_WAIT_FOR_INTERRUPT;
    }

    return 0;
}</code></pre>

<p>其中【Device_Initialize()】和【BDK_Initialize()】函数中都初始化了时钟,经过代码分析将【BDK_Initialize()】初始化时钟和内核部分注释如图7,移植时钟部分的【HAL_TICK_Init()】函数到【Device_Initialize()】函数中如下图8位置即可。</p>

<p style="text-align: center;"></p>

<p style="text-align: center;">图7</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图8</p>

<p>最后在读取数据的地方添加上蓝牙广播的内容修改,这里需要注意的是,修改广播之后想让修改的内容广播出去需要先关闭广播然后再开启广播,这样蓝牙广播内容才能修改成功。</p>

<pre>
<code class="language-cpp">    uint32_t temp = output-&gt;temperature;
    Broadcaster_Data = temp;        //温度
    temp = output-&gt;humidity;
    Broadcaster_Data = temp;        //湿度
    temp = output-&gt;raw_pressure;
    Broadcaster_Data = temp/65535%256;        //气压高位
    Broadcaster_Data = temp/256%256;        //气压中位
    Broadcaster_Data = temp%256;        //气压低位
    GAPM_CancelCmd();
    APP_FollowSetAdvScanData();
    set_adv_data(&amp;app_adv_info);
    HRPS_StartAdvertisement();</code></pre>

<p>这样最终就完成了两个例程的合并,来看一下广播中的数据吧,检测的频率为3S左右。</p>

<p class="imagemiddle" style="text-align: center;"></p>

<p style="text-align: center;">图9</p>

<p><strong>3.总结</strong></p>

<p>这次移植可以说困难重重,主要原因是一个例程采用了HAL库和软件定时器,另一个例程没有用到,而且两个都属于一个完整的项目,有冲突的地方,所以移植起来比较困难,不过有经验了之后以后移植就比较容易了,后续的传感器都比较简单就先不移植了,下一篇就先写矿井外设备接收广播包并解析,同时显示到屏幕上。</p>

火辣西米秀 发表于 2021-6-15 09:05

<p>使用HAL库和软件定时器移植困难的原因是什么,是因为与心率例程有冲突么</p>

<p>楼主做的很值得学习</p>

w494143467 发表于 2021-6-15 09:46

火辣西米秀 发表于 2021-6-15 09:05
使用HAL库和软件定时器移植困难的原因是什么,是因为与心率例程有冲突么

楼主做的很值得学习

<p>引脚有冲突,缺少库等问题</p>

sipower 发表于 2021-6-23 16:24

<p>比较深入,值得学习</p>

reayfei 发表于 2021-7-8 00:59

本帖最后由 reayfei 于 2021-7-8 21:36 编辑

<p>请教大神。</p>

<p>1,APP_FollowSetAdvScanData();这个函数打开编译不过,不知要再包含哪个文件才行。</p>

w494143467 发表于 2021-7-10 08:33

reayfei 发表于 2021-7-8 00:59
请教大神。

1,APP_FollowSetAdvScanData();这个函数打开编译不过,不知要再包含哪个文件才行。

<p>得看你用得是哪个例程改得。</p>
页: [1]
查看完整版本: 【环境专家之智能手表】Part6:BLE广播温度、湿度和气压数据