天天看点

STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能

硬件环境:

(1)开发板:ST官方的stm32wb55-st-nucleo开发板

软件环境:

(1)开发环境:RT-Thread Studio 2.0.0

(2)RTOS:RT-Thread 4.0.x

(3)到ST官方下载stm32wb55开发包stm32cube_fw_wb_v1.10.0.zip,本次移植会用到官方开发包内的无线库(STM32_WPAN文件夹)和BLE简单外设示例(BLE_Peripheral_Lite)。

实现目标:将BLE_Peripheral_Lite示例BLE收发功能移植到RT-Thread系统。

开始移植:

1. 打开RT-Thread Studio,在SDK Manger内添加stm32wb55_st_nucleo资源包。

STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能

2. 基于开发板stm32wb55_st_nucleo建立rt-thread工程。

3. 编译工程无错误,下载到开发板,但无法运行。排查后发现是启动汇编文件默认跳转到main,应该更改为entry,所以将如下文件:

STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能

如下内容:

/* Call the application s entry point.*/

    bl  main
           

修改为:

/* Call the application s entry point.*/

    bl  entry
           

再次编译、下载、运行成功:

STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能

3. 在工程目录内新建modules目录(后期放自己的模块驱动),并新建SConscript文件:

import os

from building import *

objs = []

cwd  = GetCurrentDir()

list = os.listdir(cwd)

for item in list:

    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):

        objs = objs + SConscript(os.path.join(item, 'SConscript'))

Return('objs')
           

4. 在modules目录内新建ble文件夹,并新建SConscript文件:

from building import *


cwd     = GetCurrentDir()

CPPPATH = [cwd]

src     = Glob('*.c')

group = DefineGroup('modules', src, depend = [''], CPPPATH = CPPPATH)

# search in the file system implementation

list = os.listdir(cwd)

for item in list:

    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):

        group = group + SConscript(os.path.join(item, 'SConscript'))

Return('group')
           

5. 将ST官方固件库stm32cube_fw_wb_v1.10.0内的无线库STM32_WPAN复制到工程的modules/ble目录内,并在STM32_WPAN目录内新建SConscript文件:

import rtconfig

from building import *

# get current directory

cwd = GetCurrentDir()

# The set of source files associated with this SConscript file.

src = Split('''

ble/core/auto/ble_events.c

ble/core/auto/ble_gap_aci.c

ble/core/auto/ble_gatt_aci.c

ble/core/auto/ble_hal_aci.c

ble/core/auto/ble_hci_le.c

ble/core/auto/ble_l2cap_aci.c

ble/svc/Src/svc_ctl.c

interface/patterns/ble_thread/tl/hci_tl.c

interface/patterns/ble_thread/tl/hci_tl_if.c

interface/patterns/ble_thread/tl/shci_tl.c

interface/patterns/ble_thread/tl/shci_tl_if.c

interface/patterns/ble_thread/tl/tl_mbox.c

interface/patterns/ble_thread/shci/shci.c

ble/core/template/osal.c

utilities/otp.c

utilities/stm_list.c

utilities/stm_queue.c

''')

path = [cwd + '',

    cwd + '/interface/patterns/ble_thread',

    cwd + '/interface/patterns/ble_thread/tl',

    cwd + '/interface/patterns/ble_thread/shci',

    cwd + '/utilities',

    cwd + '/ble',

    cwd + '/ble/core/template',

    cwd + '/ble/core/']

group = DefineGroup('modules', src, depend = [''], CPPPATH = path)

Return('group')
           

6. link.lds连接文件增加RAM_SHARED区:

MEMORY

{

    ROM (rx) : ORIGIN = 0x08000000, LENGTH = 1024k /* 1024KB flash */

    RAM (rw) : ORIGIN = 0x20000000, LENGTH = 192k  /* 192KB sram */

    RAM_SHARED (xrw) : ORIGIN = 0x20030000, LENGTH = 10K

}
           

7. board.c内的时钟配置SystemClock_Config()开启RF时钟:

以下:

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2

                              |RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK;
           

更改为:

RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK4|RCC_CLOCKTYPE_HCLK2

                              |RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
           

8.将ST官方固件库stm32cube_fw_wb_v1.10.0内的示例工程BLE_Peripheral_Lite内的inc和Src内的文件,复制到自己工程的modules/ble目录内。之后就是无聊而漫长的编译、找错误、修改、删除、增加等等的细节工作,最终可以正常运行,蓝牙助手可以连接成功,收发收据正常。另外,ST的示例BLE用户接口个人感觉不友好,写了一个ble_api.h的接口文件,测试ble_sample.c调用ble_api.h接口文件实现基本功能,详见测试工程代码。

最后初步移植完的目录文件(还可以进一步精简):

STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能

其中,ble_sample.c是测试程序:

void ble_sample(void)

{
    static uint8_t send_buf[2];

    ble_api.gap_notice_attach = user_gap_notification;

    ble_api.gatt_notice_attach = user_gatt_notification;

    ble_api.init();

    while(1)

    {

        send_buf[0] = 0;

        send_buf[1] ++;

        ble_api.notify_send(send_buf,2); //发送2字节测试数据

        rt_thread_mdelay(1000);

    }

}

static void user_gap_notification(user_gap_event_t event)

{

    switch (event)

    {

    case BLE_INIT_OK:

        LOG_D("ble init ok!");

        ble_api.advertising_start();

        break;

    case GAP_CONNECT:

        LOG_D("ble connect!");

        break;

    case GAP_DISCONNECT:

        LOG_D("ble disconnect!");

        ble_api.advertising_start();

        break;

    default:

        break;

    }

}

static void user_gatt_notification(user_gatt_notice_t *p_notice)

{

    switch (p_notice->event)

    {

    case GATT_NOTIFY_ENABLE:

        LOG_D("GATT_NOTIFY_ENABLE!");

        break;

    case GATT_NOTIFY_DISABLE:

        LOG_D("GATT_NOTIFY_DISABLE!");

        break;

    case GATT_WRITE_CHAR:

        LOG_D("GATT_WRITE_CHAR! length=%d,payload[0]=0x%02X,[1]=0x%02X,",p_notice->length,p_notice->payload[0],p_notice->payload[1]);

        break;

    default:

        break;

    }

}

MSH_CMD_EXPORT(ble_sample, ble sample start.);
           

在控制台输入ble_sample即可测试功能:

STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能
STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能
STM32WB55在RT-Thread系统上移植官方蓝牙BLE功能

9. ST的蓝牙BLE移植最主要的就是需要建立2个互斥量、2个信号量、1个事件集(2个事件),协议栈的运行特别是多线程时,ST可能是考虑到了一些互斥逻辑,ST的BLE协议栈会有对应的调用接口,分为SHCI、HCI两组接口,这两组格式基本一致。每组接口分别对应获取和释放互斥量、获取和释放信号量、事件通知MCU该调用特定的函数。再建立一个协议栈事件处理线程,完成这些后,再增加两个中断接口用于双核间通信,这样协议栈就可以运行了,调用协议栈的应用接口就可以进行蓝牙通信操作了,如广播、注册用户服务、接收发送数据、更改连接参数等等。移植主要内容汇总:

(1)协议栈事件通知调用接口

void hci_notify_asynch_evt(void* pdata)

{

    (void)pdata;

    rt_err_t result;

    result = rt_event_send(async_evt_id,HCI_EVENT);

    RT_ASSERT(result == RT_EOK);

}

void shci_notify_asynch_evt(void* pdata)

{

    (void)pdata;

    rt_err_t result;

    result = rt_event_send(async_evt_id,SHCI_EVENT);

    RT_ASSERT(result == RT_EOK);

}
           

(2)协议栈信号量获取和释放调用接口

void hci_cmd_resp_wait(uint32_t timeout)

{

    (void)timeout;

    rt_err_t result;

    result = rt_sem_take(hci_sem_id, RT_WAITING_FOREVER);

    RT_ASSERT(result == RT_EOK);

}

void hci_cmd_resp_release(uint32_t flag)

{

    (void)flag;

    rt_err_t result;

    result = rt_sem_release(hci_sem_id);

    RT_ASSERT(result == RT_EOK);

}

void shci_cmd_resp_wait(uint32_t timeout)

{

    (void)timeout;

    rt_err_t result;

    result = rt_sem_take(shci_sem_id, RT_WAITING_FOREVER);

    RT_ASSERT(result == RT_EOK);

}

void shci_cmd_resp_release(uint32_t flag)

{

    (void)flag;

    rt_err_t result;

    result = rt_sem_release(shci_sem_id);

    RT_ASSERT(result == RT_EOK);

}
           

(3)协议栈互斥量获取和释放调用接口

static void BLE_StatusNotificationCallback(HCI_TL_CmdStatus_t status)

{

    rt_err_t result;

    /* Callback not implemented - code flow under control of the developer */

    switch (status)

    {

        case HCI_TL_CmdBusy:

            result = rt_mutex_take(hci_mtx_id,RT_WAITING_FOREVER);

            RT_ASSERT(result == RT_EOK);

            break;

        case HCI_TL_CmdAvailable:

            rt_mutex_release(hci_mtx_id);

            break;

        default:

            break;

    }

}

static void SYS_StatusNotificationCallback( SHCI_TL_CmdStatus_t status )

{

    rt_err_t result;

    /* Callback not implemented - code flow under control of the developer */

    switch (status)

    {

        case SHCI_TL_CmdBusy:

            result = rt_mutex_take(shci_mtx_id,RT_WAITING_FOREVER);

            RT_ASSERT(result == RT_EOK);

            break;

        case SHCI_TL_CmdAvailable:

            rt_mutex_release(shci_mtx_id);

            break;

        default:

            break;

    }

}
           

(4)协议栈运行事件处理线程

static void ble_evt_proc_thread_entry(void *parameter)

{

    (void)parameter;

    rt_uint32_t e;

    for(;;)

    {

        if (rt_event_recv(async_evt_id, (SHCI_EVENT | HCI_EVENT),

                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,

                          RT_WAITING_FOREVER, &e) == RT_EOK)

        {

            if (e & SHCI_EVENT)

            {

                shci_user_evt_proc();

            }

            if (e & HCI_EVENT)

            {

                hci_user_evt_proc();

            }

        }

    }

}
           

(5)增加M0与M4和通信用到的中断接口

void IPCC_C1_TX_IRQHandler(void)
{
    HW_IPCC_Tx_Handler();
    return;
}

void IPCC_C1_RX_IRQHandler(void)
{
    HW_IPCC_Rx_Handler();
    return;
}
           

 总结

本次移植其实比较初级,是自己学习的一个记录,也不严谨,只是以实现功能和学习为目的。实际应用还得仔细研究协议栈ST是怎么实现的,精炼代码,提高可靠性,研究连接等参数。同时,本次移植是使协议栈运行起来,并没有进行添加服务UUID、各读写特征、更改广播信息等等,这些东西可参考官方例程进行添加和修改。

本次移植的代码下载地址:

链接:https://pan.baidu.com/s/1soNaPZRKFlceaUO4PyEVuw

提取码:9wwd