基于NUCLEO-WB55开发板对代码进行解析以及实现


实验目的

本文利用STM32CubeMx初始化配置STM32WB55,使用自己生成的顾客服务与特性实现蓝牙之间的读写通知,通过写控制LED2亮灭,读取LED1的状态,通知LED3(300ms闪烁)来实现本实验的目的。

实验环境

  1. STM32CubeMx V6.1.0 , KeilMDK V5.33.0.0 ,ST BLE Sensor v4.6.1 (安卓版本) , WireShark V3.4.0
  2. NUCLE-WB55.Nucleo开发板
  3. STM32WB55参考手册 , NUCLE-WB55.Nucleo开发板原理图

硬件架构实现

  1. STM32WB55主要特性:2颗独立内核,CPU1主要是用户需求,通俗的讲,接收到的数据该如何去用。比如当客户端(ST BLE Sensor)写数据,服务器(NUCLE-WB55.Nucleo)将数据接收到,我们将接收到的数据进行利用。本文利用客户端写入0x01和0x00实现开发板上的LED2亮灭;CPU2的功能就是一个蓝牙芯片,例如Cannon的Bluenrg。
  2. 处理器间的通信:处理器间的通信是通过处理器间通信控制器 (IPCC)来实现的。
    IPCC主要特性:
  • 12 个通道传输状态信号
  • 每个处理器有两条中断线
  • 按通道屏蔽中断
  • 两种通道工作模式
    通过STM32WB55参考手册可知,CPU1和CPU2之间通过IPCC的RX和TX中断实现的。因此在编写代码过程中需使用到这两个中断。

软件架构的实现

本文通过STM32CubeMx生成,利用HAL库实现。

  1. 打开KeilMOK,在maim.c文件下可以看到一些初始化:

    HAL_Init(); //重置所有外围设备,初始化Flash接口和Systick。
    SystemClock_Config(); //配置系统时钟
    MX_GPIO_Init();   //初始化IO口
    MX_RF_Init();     //初始化RF,由于RF由于CPU2来控制的,因此在这里不配置,因此此函数为空
    MX_RTC_Init();    //初始化RTC
    MX_TIM2_Init();   //初始化定时器
    APPE_Init();      //STM32_WPAN的初始化代码

    本文主要是实现蓝牙通信,因此APPE_Init这个函数就是用来对蓝牙进行相关的操作。进入APPE_Init可以看到;

    SystemPower_Config(); //配置系统电源模式
    HW_TS_Init(hw_ts_InitMode_Full, &hrtc); //初始化TimerServer 
    BSP_LED_On(LED1); //顾客函数,相当于只要进入此函数,LED1点亮。
    appe_Tl_Init();    //初始化所有传输层
    return;

    前面三个函数不必过于纠结,在本文本中还用不到理解,主要是appe_Tl_Init这个函数:

    TL_MM_Config_t tl_mm_config;
    SHCI_TL_HciInitConf_t SHci_Tl_Init_Conf;
    /**< Reference table initialization */
    TL_Init();
    /**< System channel initialization */
    UTIL_SEQ_RegTask( 1<< CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID, UTIL_SEQ_RFU, shci_user_evt_proc );
    SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&SystemCmdBuffer;
    SHci_Tl_Init_Conf.StatusNotCallBack = APPE_SysStatusNot;
    shci_init(APPE_SysUserEvtRx, (void*) &SHci_Tl_Init_Conf);
    /**< Memory Manager channel initialization */
    tl_mm_config.p_BleSpareEvtBuffer = BleSpareEvtBuffer;
    tl_mm_config.p_SystemSpareEvtBuffer = SystemSpareEvtBuffer;
    tl_mm_config.p_AsynchEvtPool = EvtPool;
    tl_mm_config.AsynchEvtPoolSize = POOL_SIZE;
    TL_MM_Init( &tl_mm_config );
    TL_Enable();
    return;

    这里不过多解释,后面还会讲到这里,这里主要是APPE_SysUserEvtRx这个函数:

    static void APPE_SysUserEvtRx( void * pPayload )
    {
    UNUSED(pPayload);
    APPD_EnableCPU2( );  //使能CPU2
    APP_BLE_Init( );   //初始化BLE各层
    UTIL_LPM_SetOffMode(1U << CFG_LPM_APP, UTIL_LPM_ENABLE);
    return;
    }

    这个函数的作用就是实现CPU2使能,以及BLE各层初始化,这边也不过多解释,后面将会介绍。这样主函数初始化就结束了,程序从现在开始,就会等待就绪事件(VS_HCI_C2_Ready)。

  2. while(1)循环函数。
    在while(1)循环函数可以看到只用了一个函数 UTIL_SEQ_Run,看到这里估计会有些蒙。其实这里用到了调度器。
    这个时候就开始用到之前没有讲到的地方了。UTIL_SEQ_RegTask和 UTIL_SEQ_SetTask。
    UTIL_SEQ_RegTask这个函数类似于登记,UTIL_SEQ_SetTask类似于操作系统的信号量。
    比如上文中看到UTIL_SEQ_RegTask( 1<< CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID, UTIL_SEQ_RFU, shci_user_evt_proc );以及在app_entry.c文件末尾出可以找到shci_notify_asynch_evt这个函数,它包含了
    UTIL_SEQ_SetEvt( 1<< CFG_IDLEEVT_SYSTEM_HCI_CMD_EVT_RSP_ID );这个函数。从这里可以看出这两个函数:

    UTIL_SEQ_SetEvt( 1<< CFG_IDLEEVT_SYSTEM_HCI_CMD_EVT_RSP_ID );
    UTIL_SEQ_RegTask( 1<< CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID, UTIL_SEQ_RFU, shci_user_evt_proc );

    通俗的来讲,当程序进入到UTIL_SEQ_SetEvt( 1<< CFG_IDLEEVT_SYSTEM_HCI_CMD_EVT_RSP_ID );这个函数里,
    UTIL_SEQ_Run这个函数就会找到UTIL_SEQ_RegTask( 1<< CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID, UTIL_SEQ_RFU, shci_user_evt_proc );这个函数下的shci_user_evt_proc函数,最终程序执行shci_user_evt_proc函数里的东西。

  3. 蓝牙连接所使用的函数
    在custom_stm.c文件下,Custom_STM_Event_Handler函数就是对事件的处理,以下case语句就是Custom_STM_Event_Handler的部分代码,通过接收到的读写通知事件执行相关程序。

    case EVT_BLUE_GATT_ATTRIBUTE_MODIFIED:
           /* USER CODE BEGIN EVT_BLUE_GATT_ATTRIBUTE_MODIFIED */
             attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blue_evt->data; 
            if(attribute_modified->Attr_Handle == (CustomContext.CustomShortledserviceHdle + 4))
           {
             return_value = SVCCTL_EvtAckFlowEnable;
             if(attribute_modified->Attr_Data[0] & COMSVC_Notification)
             {
               Notification.Custom_Evt_Opcode = CUSTOM_STM_SHORTREADANDWRITEORNOTICECHAR_NOTIFY_ENABLED_EVT;
               Custom_STM_App_Notification(&Notification);
             }
             else
             {
               Notification.Custom_Evt_Opcode =CUSTOM_STM_SHORTREADANDWRITEORNOTICECHAR_NOTIFY_DISABLED_EVT;
               Custom_STM_App_Notification(&Notification);
             }
           }
           /* USER CODE END EVT_BLUE_GATT_ATTRIBUTE_MODIFIED */
           break;
         case EVT_BLUE_GATT_READ_PERMIT_REQ :
           /* USER CODE BEGIN EVT_BLUE_GATT_READ_PERMIT_REQ */
            read_perm_req = (aci_gatt_read_permit_req_event_rp0 *)blue_evt->data;
             if(read_perm_req->Attribute_Handle == (CustomContext.CustomShortledserviceHdle + 2))
             {
               return_value = SVCCTL_EvtAckFlowEnable;
               Notification.Custom_Evt_Opcode = CUSTOM_STM_SHORTREADANDWRITEORNOTICECHAR_READ_EVT;
               Custom_STM_App_Notification(&Notification);  
               aci_gatt_allow_read(read_perm_req->Connection_Handle);
             }
           /* USER CODE END EVT_BLUE_GATT_READ_PERMIT_REQ */
           break;
         case EVT_BLUE_GATT_WRITE_PERMIT_REQ:
           /* USER CODE BEGIN EVT_BLUE_GATT_WRITE_PERMIT_REQ */
            write_perm_req = (aci_gatt_write_permit_req_event_rp0*)blue_evt->data;
            if( write_perm_req->Attribute_Handle == (CustomContext.CustomShortledserviceHdle + 2))
             {
               return_value = SVCCTL_EvtAckFlowEnable;
               Notification.Custom_Evt_Opcode = CUSTOM_STM_SHORTREADANDWRITEORNOTICECHAR_WRITE_NO_RESP_EVT;
               Notification.DataTransfered.Length=write_perm_req->Data_Length;
               Notification.DataTransfered.pPayload=write_perm_req->Data;
               aci_gatt_write_resp(write_perm_req->Connection_Handle,write_perm_req->Attribute_Handle,0,0,write_perm_req->Data_Length,write_perm_req->Data);
               Custom_STM_App_Notification(&Notification);  
             }
           /* USER CODE END EVT_BLUE_GATT_WRITE_PERMIT_REQ */
           break;

    在通知过程中,本文利用TIM2定时器。每隔100ms发送通知,因此在定时器的回调函数中添加UTIL_SEQ_SetTask标志,在 APP_BLE_Init函数中添加UTIL_SEQ_SetTask函数。这里就不过多介绍。

实验步骤

STM32CubeMx配置

软件设计

软件设计视频中少添加了 attribute_modified = (aci_gatt_attribute_modified_event_rp0*)blue_evt->data,因此无法通知,可以参考下面例程。

资料

百度网盘
链接:https://pan.baidu.com/s/1r8_FHHor-gnewPku_j2hmQ
提取码:9g0g


文章作者: 潘苏皖
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 潘苏皖 !
评论
 上一篇
十一届蓝桥杯物联网程序题 十一届蓝桥杯物联网程序题
题目分析比赛题目原题参考蓝桥杯大赛—十一届蓝桥杯物联网 硬件分析蓝桥杯物联网基于STM32L071KBU微控制器和LoRa收发器。LoRa终端集成了CMSIS DPA Link 编程调试工具,与目标微控制器STM32L071KBU连接。调试
2021-04-09
下一篇 
基于Cannon小钢炮蓝牙通信添加服务与特性 基于Cannon小钢炮蓝牙通信添加服务与特性
实验目的本文主要目的是添加服务与特性,在BLE Scanner中实现读,写,通知。 实验环境 软件: stm32cubemx ,keilMDK,BLE Scanner, WireShark 硬件:Cannon小钢炮 资料:BlueNRG-1
2020-12-18
  目录