[STM32WB55 Review] A brief analysis of the execution process of the BLE demo program[Copy link]
This post was last edited by cruelfox on 2019-5-22 20:36 After compiling the BLE_p2pServer project, I can use GDB to trace and debug. Tracing, let's take a look at what this software framework looks like, without making a detailed analysis (time does not allow). Starting from main.c, the main function main() just writes some function calls: int main(void){ HAL_Init(); Reset_Device(); Config_HSE(); SystemClock_Config(); PeriphClock_Config(); Init_Exti(); MX_GPIO_Init(); MX_DMA_Init(); MX_RF_Init(); MX_RTC_Init(); APPE_Init(); while(1) { SCH_Run(~0); }} First is the HAL_Init() library function, which initializes the HAL The role of the library, which calls the user initialization code HAL_MspInit() written in stm32wbxx_hal_msp.c to perform project-related initialization. This project executes a __HAL_RCC_HSEM_CLK_ENABLE(); then Reset_Device(), this function is in main.c, which executes Reset_BackupDomain() and Reset_IPCC() - it may be that this part of the hardware is not reset with the CPU, so it needs to be handled separately. The following Config_HSE(), SystemClock_Config() and PeriphClock_Config() are used to configure various system clocks, which are set according to the needs of program operation. Init_Exti(), MX_GPIO_Init(), MX_DMA_Init(), MX_RF_Init(), MX_RTC_Init() These functions configure the basic hardware such as EXTI, GPIO, DMA, and RTC. However, the MX_RF_Init() function is empty, and the hardware initialization is not here (it is probably done by the other CPU2). The above functions are easy to understand, just to prepare other on-chip devices before using the wireless function. HSEM and IPCC are two special hardware of STM32WB55, which are used for dual-core communication. Then, it is the APPE_Init() function, which is defined in app_entry.c. The function name means to initialize the application (the previous initialization is only hardware), and the functions in it will deal with the WPAN library. void APPE_Init( void ){ SystemPower_Config(); HW_TS_Init(hw_ts_InitMode_Full, &hrtc); Init_Debug(); LPM_SetOffMode(1 << CFG_LPM_APP, LPM_OffMode_Dis); Led_Init(); Button_Init();[/b ] appe_Tl_Init(); } Among them, HW_TS_Init() is defined in hw_timerserver.c. As the name implies, it initializes the software timer (service). Looking at the code, this is a function based on RTC implementation. As for who the software timer is provided for? For the time being, it is found that HW_TS_Create is called in app_ble.c. The Init_Debug() function uses DbgTraceInit() in dbg_trace.c to initialize USART1. When running this program, the information output through the serial port is the debug support provided by the software. The last appe_Tl_Init() is the highlight again. The "Tl" in the function name is the abbreviation of Transport layer. This function is in appe_entry.c, but it calls the function in the WPAN library. Since I didn't find the manual for this library, I don't know how to initialize it. I can only guess from the code. After APPE_Init() is executed, it returns to main() and starts an infinite loop: repeatedly calling SCH_Run(). This function is defined in scheduler.c, which means executing the scheduler. However, the scheduler here is different from the FreeRTOS scheduler I am familiar with. The latter will not return, so it does not need to be placed in a loop. To confirm this, I set a breakpoint at the entrance of the SCH_Run() function, and then resume execution. The CPU will still encounter the breakpoint again. In the SCH_Run() function body, the execution of each task is through a statement: /** Execute the task */ TaskCb[31 - bit_nbr](); There is an array TaskCb that stores the functions corresponding to the tasks. Let's take a look at what's in it: (gdb) print TaskCb$1 = {0x8001eb9, 0x8002371, 0x80058ed, 0x8005f5dThere are four "tasks" in it, but from my observation of scheduler.c, it seems that this is not a real RTOS - each task does not have an independent stack and cannot be nested. When are these four tasks registered? I set a breakpoint at the entry of SCH_RegTask() function and trace it again. First call: in appe_Il_Init(), task_id=3 SCH_RegTask( CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID, shci_user_evt_proc ); Second call: in APP_BLE_Init() (app_ble.c), task_id=2 SCH_RegTask(CFG_TASK_HCI_ASYNCH_EVT_ID, hci_user_evt_proc); Third call: still in APP_BLE_Init(), task_id=0 SCH_RegTask(CFG_TASK_ADV_CANCEL_ID, Adv_Cancel); Fourth call: in p2p_server_app.c In the P2PS_APP_Init() function, task_id=1 SCH_RegTask( CFG_TASK_SW1_BUTTON_PUSHED_ID, P2PS_Send_Notification ); Here, the definition of CFG_TASK...ID can be found in app_conf.h. The p2p Server program registers a total of four tasks as above. Rather than calling them tasks, they are event handling subroutines. From the names, task_id 3 and 4 are user event handling procedures for hci and shci respectively (if HCI is Host Controller Interface, what is SHCI?), which are defined in hci_tl.c and shci_tl.c respectively, and the structures are similar. The one with task_id 0 stops broadcasting. The subroutine with task_id 1 sends notifications to the BLE client, that is, event handling triggered by a key press. At this point, we have roughly understood that the demo application of STM32WB55 is "event-driven". During the initialization process, a scheduler is established, and the "tasks" for handling different events are managed by the scheduler. The scheduler calls the function of the corresponding task when there is a task to be processed. Of course, there must be a hardware interrupt (IRQ) behind this to drive it. Let's track the interrupt caused by the key: the IRQ processing process of EXTI. When SW1 is pressed, the EXTI interrupt service routine is executed by the hardware void PUSH_BUTTON_SW1_EXTI_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(BUTTON_SW1_PIN); } Then the interrupt handling function of the HAL library is directly called void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u){__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);HAL_GPIO_EXTI_Callback(GPIO_Pin);}}It calls the interrupt handler function written by the uservoid HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { switch (GPIO_Pin) { case BUTTON_SW1_PIN: APP_BLE_Key_Button1_A ction(); break; case BUTTON_SW2_PIN: APP_BLE_Key_Button2_Action(); break; case BUTTON_SW3_PIN: APP_BLE_Key_Button3_Action(); break; default: break; } return;} The result is that the call is made void APP_BLE_Key_Button1_Action(void){ P2PS_APP_SW1_Button_Action();} Finally, it ends here void P2PS_APP_SW1_Button_Action(void){ SCH_SetTask( 1<" [color="#0000ff]HW_IPCC_Rx_Handler();" b]="" ble_iobusevtcallbackfunction="pInitHciConf-" ble_iobusevtcallbackfunction)="" ble_iobusevtcallbackfunction,="" cfg_sch_prio_0);[="" else="" false)[="" font][="" hci="" hw_ipcc.c="" hw_ipcc_ble_event_channel="" hw_ipcc_ble_evthandler="" hw_ipcc_ble_evthandler(="" hw_ipcc_ble_rxevtnot="" hw_ipcc_ble_rxevtnot(void)[="" hw_ipcc_rx_handler="" hw_ipcc_rx_handler(="" hw_ipcc_sys_evthandler();[="" hw_ipcc_system_event_channel="" hw_ipcc_thread_cli_notification_ack_channel="" hw_ipcc_thread_clinotevthandler();[="" hw_ipcc_thread_notevthandler();[="" hw_ipcc_thread_notification_ack_channel="" hw_ipcc_traces_channel="" hw_ipcc_traces_evthandler();[="" if="" ipcc="" ipcc,="" ipcc_c1_rx_irqhandler(void)[="" ipcc接收中断(和cpu2通信使用)的响应过程:="" ll_c1_ipcc_clearflag_chx(="" lst_remove_head="" return;[="" tl_ble_init()="" tl_evtpacket_t="" tl_mbox.c="" void="" while(lst_is_empty(&evtqueue)="=" {[="" }[="" 上面这个函数要处理一个列表,对每个="" 中的="" 事件,调用回调函数="" 但这是一个函数指针="" 再看另外一个,我跟踪了一次="" 函数="" 函数里面有这一行:="" 在清除="" 寄存器相关标志位之前,调用="" 层层嵌套调用很冗长吧,除去简单判断,就是做了个触发事件处理(任务)的操作。对按键的响应转移到了一个任务函数中,而不是在中断服务里面处理,这个特点又和rtos一致。="" 来处理="" 根据硬件寄存器标志位,选择了调用="" 调用的是="" 还需要找到它指向的函数。不难找到,在="">IoBusEvtCallBack; This is the function specified by the parameter passed to TL_BLE_Init() - a pointer of type TL_BLE_InitConf_t. TL_BLE_Init() is called indirectly again. There is the following code in the TlInit() function: Conf.IoBusEvtCallBack = TlEvtReceived; hciContext.io.Init(&Conf); Therefore, BLE_IoBusEvtCallBackFunction is actually the entry address of TlEvtReceived static void TlEvtReceived(TL_EvtPacket_t *hcievt){ if ( ((hcievt->evtserial.evt.evtcode) == TL_BLEEVT_CS_OPCODE) || ((hcievt->evtserial.evt.evtcode) == TL_BLEEVT_CC_OPCODE ) ) { LST_insert_tail(&HciCmdEventQueue, (tListNode *)hcievt); hci_cmd_resp_release(0); /**< Notify the application a full Cmd Event has been received */ } else { LST_insert_tail(&HciAsynchEventQueue, (tListNode *)hcievt);hci_notify_asynch_evt((void*) &HciAsynchEventQueue); /**< Notify the application a full HCI event has been received */ } return;} The two functions in it are related to the scheduler: void hci_notify_asynch_evt(void* pdata){SCH_SetTask(1 << CFG_TASK_HCI_ASYNCH_EVT_ID, CFG_SCH_PRIO_0); return;}void hci_cmd_resp_release(uint32_t flag){ SCH_SetEvt(1 << CFG_IDLEEVT_SYSTEM_HCI_CMD_EVT_RSP_ID); return;} So, this interrupt also triggers an event, and the subsequent processing is still managed by the scheduler. After analyzing this, we have a general understanding of the software structure of the demo program. This is an event-driven writing method, but there are too many nested calls, which makes the analysis not intuitive. I wonder how the developer of this software feels. This content is originally created by EEWORLD forum user cruelfox. If you need to reprint or use it for commercial purposes, you must obtain the author's consent and indicate the source