cruelfox

  • 2019-05-26
  • 加入了学习《基于GD32E231的金鱼自动投喂机器人设计》,观看 基于GD32E231的金鱼自动投喂机器人设计

  • 发表了主题帖: 【STM32WB55 测评】小结

      在试用 STM32WB55 Nucleo pack 套件之前,我对它还是很期待的。毕竟是 STM32 的第一款双核 MCU,又具有BLE 5.0 + 802.15.4 双模的无线功能,低功耗设计。不过实际体验的这几个星期下来,有很多意外。   1. 采用了双核的设计很好,可惜 CPU2 这个核(对一般开发用户)不开放。不开放还不仅是没有提供 CPU2 上程序的 SDK,仅提供了可选的几个编译好的 image(还是加密的不能反汇编),而且无法访问 CPU2 私有的 Flash、SRAM 部分,无法通过 SWD 对其调试。虽然称这特性为“安全”,然而这是开发板,并非卖到终端用户的产品,要防什么呢?总之结果就是:STM32WB55 只能当作一个单核 M4 MCU来用,而且还不能访问到片上全部资源(比如,想用整块 flash, SRAM2是不行的,不能全片擦除)。   2. 就算是不去动 CPU2 部分的东西,只开发 CPU1 的程序,那么怎么跟 CPU2 交互得知道吧。然而这方面的文档(据我能找到的)还是空白。CubeWB 中有不少例子,没错,可惜这些工程使用的程序库——STM32 WPAN,没有提供函数说明的文档,更没有编程手册。好,如果要新开发一个无线的应用应该怎么编写?总不能拿 demo 自己去改吧。我个人觉得(从 BLE_p2pServer 这个工程看来)这代码的结构真是乱啊,效率怎样先不管,嵌套的调用太多,使得每个函数的意图看不明显,再开发就很费劲了。   3. 低功耗呢,试下 BLE_p2pServer 这个应用运行时电流有6个多mA. 我从板子上 JP2 这个跳线这里测量的。既然板子后面还准备了电池,demo 程序也得注意省省电吧。我相信 MCU 省电能力是有的,只是 demo 软件上没做,或者没尽力实现。就说自带的 SMPS,代码里似乎也配置了一些与之有关的寄存器,但是我测试电流并没有发现任何效果——我断开 JP2, 用可调稳压电源给 MCU 供电,结果发现 VDD 从 3.2V 降到 2.4V,电流并没有增大,反而稍有减小。这不像是用了降压型DC-DC的效果。   4. 使用不同模式的无线功能,需要切换 CPU2 的 firmware. 但是这部分 Flash 的更新不能通过 SWD 直接进行,而得用 DFU 模式——还是为了保护 ST 的软件进行了加密举措。这是需要 CPU2 运行一个程序来负责 Flash 的擦除和写入,负责 Option bytes 的修改,这叫做 RSS --- Root Security Service.  但是 RSS 的程序自己也在 Flash 里面(可以升级)。我不知道全新的 STM32WB55 芯片里面是否已经烧写了 RSS 程序了(怎么烧进去的?),本坛网友有升级固件给升级坏了的,那样芯片就到了一个自锁死的状态,又没法全片擦除。DFU 升级做得不完善,为什么还需要提供固件烧写的地址,难道不可以在固件文件文件里面直接包含?烧错了地址不能用,怎么知道正确的地址?不同容量 flash 型号的地址应该不同吧?Nucleo 套件带的都是 1MB flash 的型号,其中默认 BLE stack 固件的地址是 0x80C0000 以后了,也就是 flash 的后 1/4 里面。若开发产品用 256kB flash 的型号,固件怎么办?   以上是我的主要几点试用感受。总体的就是 STM32WB55 这款对 STM32 开发爱好者不友好,尽管功能也许是强,尽管 Reference Manual 也如以往的 STM32 一样详细(Radio System一章除外),要拿来 DIY 用并不容易。

  • 2019-05-25
  • 发表了主题帖: 【STM32WB55 测评】BLE协议栈与双核通信

    本帖最后由 cruelfox 于 2019-5-25 17:12 编辑   我上一篇帖子的分析内容已经暗示了,IPCC 硬件IRQ与 BLE 通信有关系。IPCC 是 STM32WB55 片内两个 CPU 的通信渠道之一,那么它和 BLE 协议栈有着什么样的关系?本篇再深入一些调查。   上篇所述,在 p2p_Server demo 程序里面,当 SW1 按钮按下之后,最终会引起“任务”的执行,也就是 P2PS_Send_Notification() 这个函数被调用。其中起到发送通知效果的是这一条语句:     P2PS_STM_App_Update_Char(P2P_NOTIFY_CHAR_UUID, (uint8_t *)&P2P_Server_App_Context.ButtonControl); 这是调用了 WPAN 库 p2p_stm.c 中的函数 tBleStatus P2PS_STM_App_Update_Char(uint16_t UUID, uint8_t *pPayload) {   tBleStatus result = BLE_STATUS_INVALID_PARAMS;   switch(UUID)   {     case P2P_NOTIFY_CHAR_UUID:      result = aci_gatt_update_char_value(aPeerToPeerContext.PeerToPeerSvcHdle,                              aPeerToPeerContext.P2PNotifyServerToClientCharHdle,                               0, /* charValOffset */                              2, /* charValueLen */                              (uint8_t *)  pPayload);       break;     default:       break;   }   return result; } 其中再调用 BLE 协议栈函数 aci_gatt_update_char_value().   在 ble_gatt_aci.c 中,不难找到这个函数的实现: tBleStatus aci_gatt_update_char_value(uint16_t Service_Handle,                                       uint16_t Char_Handle,                                       uint8_t Val_Offset,                                       uint8_t Char_Value_Length,                                       uint8_t Char_Value[]) {   struct hci_request rq;   uint8_t cmd_buffer[BLE_CMD_MAX_PARAM_LEN];   aci_gatt_update_char_value_cp0 *cp0 = (aci_gatt_update_char_value_cp0*)(cmd_buffer);   tBleStatus status = 0;   int index_input = 0;   cp0->Service_Handle = htob(Service_Handle, 2);   index_input += 2;   cp0->Char_Handle = htob(Char_Handle, 2);   index_input += 2;   cp0->Val_Offset = htob(Val_Offset, 1);   index_input += 1;   cp0->Char_Value_Length = htob(Char_Value_Length, 1);   index_input += 1;   Osal_MemCpy((void *) &cp0->Char_Value, (const void *) Char_Value, Char_Value_Length);   index_input += Char_Value_Length;   Osal_MemSet(&rq, 0, sizeof(rq));   rq.ogf = 0x3f;   rq.ocf = 0x106;   rq.cparam = cmd_buffer;   rq.clen = index_input;   rq.rparam = &status;   rq.rlen = 1;   if (hci_send_req(&rq, FALSE) < 0)     return BLE_STATUS_TIMEOUT;   if (status)   {     return status;   }   return BLE_STATUS_SUCCESS; }   这里所做的,是按照参数填写了一个 aci_gatt_update_char_value_cp0 复合类型的数据(使用cmd_buffer的空间存放),再填写了一个 hci_request 结构变量 rq,将它的地址传给 hci_send_req() 函数调用。   在 ble_types.h 文件定义了上面的结构 typedef PACKED(struct)   {   uint16_t Service_Handle;   uint16_t Char_Handle;   uint8_t Val_Offset;   uint8_t Char_Value_Length;   uint8_t Char_Value[(BLE_CMD_MAX_PARAM_LEN - 6)/sizeof(uint8_t)]; } aci_gatt_update_char_value_cp0; 在 ST 文档 AN5270 中对这个结构有解释:   在 ble_const.h 中定义了 struct hci_request {   uint16_t ogf;   uint16_t ocf;   int      event;   void*    cparam;   int      clen;   void*    rparam;   int      rlen; }; 这是提供给 hci_send_req() 函数的,告诉它命令的类型(ogf, ocf指定),命令参数和返回参数的地址、长度。   hci_send_req() 的实现在 hci_tl.c 中,这里就不贴了,其中包括命令发送和等待返回的部分。命令发送的代码是:   opcode = ((p_cmd->ocf) & 0x03ff) | ((p_cmd->ogf) << 10);   SendCmd(opcode, p_cmd->clen, p_cmd->cparam); ocf 和 ogf 被合并成一个 16-bit 数据,和命令参数一起提供给 SendCmd() 函数 static void SendCmd(uint16_t opcode, uint8_t plen, void *param) {   pCmdBuffer->cmdserial.cmd.cmdcode = opcode;   pCmdBuffer->cmdserial.cmd.plen = plen;   memcpy( pCmdBuffer->cmdserial.cmd.payload, param, plen );   hciContext.io.Send(0,0);   return; } 命令代码和参数被复制到 cmdserial.cmd 这个结构里面,然后 hciCotext.io.Send() 被调用。这个 Send 是一个函数指针,在 hci_register_io_bus() 中被初始化为 TL_BLE_SendCmd,也就是: int32_t TL_BLE_SendCmd( uint8_t* buffer, uint16_t size ) {   ((TL_CmdPacket_t*)(TL_RefTable.p_ble_table->pcmd_buffer))->cmdserial.type = TL_BLECMD_PKT_TYPE;   HW_IPCC_BLE_SendCmd();   return 0; } 到这里就越来越明显了,IPCC 被用来向 CPU2 传递 BLE 命令。   具体如何发送? void HW_IPCC_BLE_SendCmd( void ) {     LL_C1_IPCC_SetFlag_CHx( IPCC, HW_IPCC_BLE_CMD_CHANNEL );     return; } 用到 HAL 库定义的 __STATIC_INLINE void LL_C1_IPCC_ClearFlag_CHx(IPCC_TypeDef *IPCCx, uint32_t Channel) {   WRITE_REG(IPCCx->C1SCR, Channel); } 也就是只写了一个寄存器标志位,那么,疑问来了:数据是怎么传递的?   因为数据是写到 pCmdBuffer 这个全局指针表示的地址的,这个指针在 TlInit() 中被初始化为传入参数,故又当 shci_init() 函数调用 TlInit() 时初始化, 最上层其实是在 appe_Tl_Init() 函数中作的初始化:   SHci_Tl_Init_Conf.p_cmdbuffer = (uint8_t*)&SystemCmdBuffer; 也就是 SystemCmdBuffer 这块数据用作向 CPU2 传递命令的共享内存。只要 CPU2 知道这个地址(比如事先约定好了,软件link时固定地址),一切就好解释了:通过 IPCC 设置标志,让 CPU1, CPU2 相互通过中断来触发事件处理;而通信的具体数据,就从固定内存位置去取。   于是 CPU1 处理 BLE 事务,只是将命令打包传递给 CPU2,并等待 CPU2 回应的数据。无线硬件方面自然是 CPU2 管理的,且不只是硬件,协议栈的管理也是 CPU2 完成。前面分析的这个 aci_gatt_update_char_value() 是软件层面的命令,而非控制器的命令。下图是 BLE 协议栈的结构,和硬件打交道的是 HCI (Host-Controller Interface), 不过和 STM32WB55 代码里面的 hci 好象不是一回事。   HCI 有一套命令,是蓝牙规范里面定义的。linux 下还可以用 hcitool 工具从命令行直接与控制器进行命令交互,HCI 命令也可以通过串行接口(SPI, UART等)来跑。HCI 有固定的数据格式,比如命令是由命令包组成,控制器向主机通知用事件包。   STM32WB55 BLE 支持的命令在 AN5270 文档中也列出了。看一个 hci_disconnect() 的实现: tBleStatus hci_disconnect(uint16_t Connection_Handle,                           uint8_t Reason) {   struct hci_request rq;   uint8_t cmd_buffer[BLE_CMD_MAX_PARAM_LEN];   hci_disconnect_cp0 *cp0 = (hci_disconnect_cp0*)(cmd_buffer);   tBleStatus status = 0;   int index_input = 0;   cp0->Connection_Handle = htob(Connection_Handle, 2);   index_input += 2;   cp0->Reason = htob(Reason, 1);   index_input += 1;   Osal_MemSet(&rq, 0, sizeof(rq));   rq.ogf = 0x01;   rq.ocf = 0x006;   rq.event = 0x0F;   rq.cparam = cmd_buffer;   rq.clen = index_input;   rq.rparam = &status;   rq.rlen = 1;   if (hci_send_req(&rq, FALSE) < 0)     return BLE_STATUS_TIMEOUT;   if (status)   {     return status;   }   return BLE_STATUS_SUCCESS; } 从 ogf, ocf 的值推测,这个命令被 CPU2 收到后,会交由控制器处理。   再看看接收方向,从 CPU2 发来的通知如何收到的。上一篇帖子里提到了 IPCC RX 中断,在处理过程中间有这个函数: void HW_IPCC_BLE_RxEvtNot(void) {   TL_EvtPacket_t *phcievt;   while(LST_is_empty(&EvtQueue) == FALSE)   {     LST_remove_head (&EvtQueue, (tListNode **)&phcievt);     BLE_IoBusEvtCallBackFunction(phcievt);   }   return; } 注意到这里用到一个全局变量(结构体)EvtQueue. 它的定义是 PLACE_IN_SECTION("MB_MEM2") ALIGN(4) static tListNode  EvtQueue; 前面的修饰含义是什么?其实 SystemCmdBuffer 的定义也有同样的修饰。有什么特别之处?   我用 arm-none-eabi-nm 程序查看一下编译生成 ELF 文件的符号表: ...... 20001e84 B errno 20001e88 B __bss_end__ 20001e88 B _ebss 20001e88 B end 20030000 A _estack 20030000 b TL_RefTable 2003001c b BleCmdBuffer 20030128 b TL_DeviceInfoTable 20030148 b TL_BleTable 20030158 b TL_ThreadTable 20030164 b TL_SysTable 2003016c b TL_MemManagerTable 20030188 b TL_TracesTable 2003018c b TL_Mac_802_15_4_Table 20030198 b FreeBufQueue 200301a0 b TracesEvtQueue 200301a8 d SystemCmdBuffer 200302b4 d BleSpareEvtBuffer 200303c0 d SystemSpareEvtBuffer 200304cc d EvtPool 20030a08 d EvtQueue 20030a10 d CsBuffer 20030a20 d SystemEvtQueue   发现玄机了:在地址 0x20030000 以后的地方,也就是 SRAM2 里面,还有一些变量(哪怕BLE程序不会用到)。这些变量虽然从 C 语言角度看和一般的全局变量没什么差别,但它们并没有和其它全局变量放在一起——放在 .data 和 .bss 段中。我大胆猜测:这些变量就是用于 CPU1 和 CPU2 交换数据的,因为 IPCC 只提供硬件中断机制,并无数据传递功能。   在 mbox_def.h 中对前面的几个 TL_xxxTable 相应的结构型作了定义,比如   typedef struct   {     MB_DeviceInfoTable_t    *p_device_info_table;     MB_BleTable_t           *p_ble_table;     MB_ThreadTable_t        *p_thread_table;     MB_SysTable_t           *p_sys_table;     MB_MemManagerTable_t   *p_mem_manager_table;     MB_TracesTable_t        *p_traces_table;     MB_Mac_802_15_4_t       *p_mac_802_15_4_table;   } MB_RefTable_t; 存放其它几个表的地址,又比如 typedef struct   {     uint8_t     *pcmd_buffer;     uint8_t     *pcs_buffer;     uint8_t     *pevt_queue;     uint8_t     *phci_acl_data_buffer;   } MB_BleTable_t; 存放了 BLE 的几个关键交互数据的地址。   这样的设计,只要 TL_RefTable 保证在 SRAM2 的最低地址,那么 CPU2 就可以定位它需要的其它数据结构的位置,CPU1 和 CPU2 通过内存中变量的共享,达到了命令、数据等传递目的。这样就完整回答了我前面的“数据怎么传递”的疑问。 此内容由EEWORLD论坛网友cruelfox原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2019-05-24
  • 回复了主题帖: 自制示波器电流探头研发成功:测STM32 FOC电机板

    电流互感器

  • 2019-05-23
  • 加入了学习《《小水电站自动控制》项目演示》,观看 《家庭智能监控》项目演示

  • 2019-05-22
  • 回复了主题帖: 如何利用超声波传感器检测蚊子电路

    LuJianchang 发表于 2019-5-22 09:42 搞错了,是超声波传感器检测到有蚊子就打开灯(紫色)招引蚊子的,并驱动NE555高压发生器产生高压,达到灭 ...
    一直开着招引就行了嘛 真要能检测到,我估计都飞到电网上了

  • 回复了主题帖: 【麦昆试用】python编程(2)

    viphotman 发表于 2019-5-22 09:22 这也太厉害了吧,怎么得出来的呀?
    这是基础乐理啊,十二平均律

  • 2019-05-21
  • 发表了主题帖: 【STM32WB55 测评】BLE demo程序执行过程浅析

    本帖最后由 cruelfox 于 2019-5-22 20:36 编辑   编译了 BLE_p2pServer 这个工程以后,我就可以用GDB进行跟踪调试了。跟踪跟踪,姑且看看这个软件框架是什么样的,不作详细分析了(时间也不允许)。   从 main.c 入手,主函数 main() 就写了一些函数调用而已: 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);   } }   首先是 HAL_Init() 这个库函数, 起初始化 HAL 库的作用,其中又调用了 stm32wbxx_hal_msp.c 中写的用户初始化代码 HAL_MspInit(), 执行工程相关的初始化。本工程执行了一个 __HAL_RCC_HSEM_CLK_ENABLE();   然后是 Reset_Device(), 这个函数就在 main.c 中,里面执行的是 Reset_BackupDomain() 和 Reset_IPCC() ——可能是这部分硬件并不是和CPU一起复位的,所以需要单独处理下。   接下来的 Config_HSE(), SystemClock_Config() 和 PeriphClock_Config() 是配置系统各种时钟的,根据程序运行的需要设置。   Init_Exti(), MX_GPIO_Init(), MX_DMA_Init(), MX_RF_Init(), MX_RTC_Init() 这几个函数配置 EXTI, GPIO, DMA, RTC 这几个基本的硬件。不过 MX_RF_Init() 函数里面是空的,硬件初始化并不在这里(估计是由另外那个CPU2做的)。以上这些函数都容易看懂,就是在使用无线功能以前做好其它片上设备的准备工作。HSEM 和 IPCC 是 STM32WB55 的两个特殊硬件,双核通信要用到。   然后,就到了 APPE_Init() 函数了,它在 app_entry.c 中定义。函数名的意思是初始化应用程序(前面的初始化只是硬件),它里面的函数会跟 WPAN 库打交道。 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();   appe_Tl_Init();         }   这其中 HW_TS_Init() 在 hw_timerserver.c 中定义,顾名思义是初始化软件定时器(服务)。瞅了瞅代码,这是基于RTC实现的功能,至于软件定时器提供给谁用的?姑且发现在 app_ble.c 中调用了 HW_TS_Create.   Init_Debug() 函数用了 dbg_trace.c 中的 DbgTraceInit(), 初始化了 USART1. 运行这个程序时通过串口输出的信息就是软件自带的 Debug 支持。   最后的 appe_Tl_Init() 又是重头戏了, 函数名中 "Tl" 是 Transport layer 的缩写,这个函数在 appe_entry.c 中,不过调用的是 WPAN 库里面的函数。由于我没有找到这个库的手册,这里需要如何初始化不清楚,从代码只能略猜一二。   APPE_Init() 执行完毕之后,回到 main() 当中,开始一个死循环:重复调用 SCH_Run(). 这个函数定义在 scheduler.c 中,也就是执行调度器的意思。但是这里的调度器和我熟悉的 FreeRTOS 的调度器不同,后者是不会返回的,因此不需要放在循环里面。为了确认这一点,我在 SCH_Run() 函数入口设置断点,然后恢复执行,CPU仍然会再次遇到断点。在 SCH_Run() 函数体中,执行每个任务是通过一条语句:    /** Execute the task */     TaskCb[31 - bit_nbr](); 这里有一个数组 TaskCb 存放任务对应的函数,不妨看看里面有什么: (gdb) print TaskCb $1 = {0x8001eb9 <Adv_Cancel>, 0x8002371 <P2PS_Send_Notification>,   0x80058ed <hci_user_evt_proc>, 0x8005f5d <shci_user_evt_proc>} 里面有四个“任务”,不过从我对 scheduler.c 的观察看来,这不是真正的 RTOS ——每个任务没有独立的堆栈,不能嵌套执行。   这四个任务分别在什么时候注册的呢?我在 SCH_RegTask() 函数入口设断点,再来跟踪一次。   第一次调用:appe_Il_Init() 里面, task_id=3   SCH_RegTask( CFG_TASK_SYSTEM_HCI_ASYNCH_EVT_ID, shci_user_evt_proc );   第二次调用:APP_BLE_Init() 里(app_ble.c),task_id=2   SCH_RegTask(CFG_TASK_HCI_ASYNCH_EVT_ID, hci_user_evt_proc);   第三次调用:仍然在 APP_BLE_Init() 里,task_id=0   SCH_RegTask(CFG_TASK_ADV_CANCEL_ID, Adv_Cancel);   第四次调用:在 p2p_server_app.c 的 P2PS_APP_Init() 函数里,task_id=1   SCH_RegTask( CFG_TASK_SW1_BUTTON_PUSHED_ID, P2PS_Send_Notification );   这里 CFG_TASK...ID 的定义在 app_conf.h 中可以找到。p2p Server程序一共注册了如上这四个任务。与其说是任务,不如说是事件处理子程序吧。从名称上看,task_id 3,4这两个分别是 hci 和 shci 的用户事件处理程序(HCI是 Host Controller Interface的话,SHCI又是什么呢?),分别在 hci_tl.c 和 shci_tl.c 中定义,结构也相似。task_id为0的这个是停止广播。task_id为1的这个子程序是向BLE client发送通知的,即按键触发的事件处理。   到这里,大概了解到 STM32WB55 的 demo 应用程序是“事件驱动”组织的。在初始化过程中建立起调度器,将处理不同事件的“任务”交由调度器管理。调度器在有任务需要处理的时候调用相应任务的函数。这背后当然还得有硬件中断(IRQ)来驱动。   不妨就跟踪一下按键引发的中断:EXTI的 IRQ 处理过程。按下SW1时,EXTI中断服务程序被硬件执行void PUSH_BUTTON_SW1_EXTI_IRQHandler(void) {   HAL_GPIO_EXTI_IRQHandler(BUTTON_SW1_PIN); }   就直接调用 HAL 库的中断处理函数 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);   } }   它调用用户编写的中断处理函数 void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin ) {   switch (GPIO_Pin)   {     case BUTTON_SW1_PIN:      APP_BLE_Key_Button1_Action();       break;     case BUTTON_SW2_PIN:       APP_BLE_Key_Button2_Action();       break;     case BUTTON_SW3_PIN:       APP_BLE_Key_Button3_Action();       break;     default:       break;   }   return; }   结果就是选择调用了 void APP_BLE_Key_Button1_Action(void) {   P2PS_APP_SW1_Button_Action(); }   最终是到这里 void P2PS_APP_SW1_Button_Action(void) {   SCH_SetTask( 1<<CFG_TASK_SW1_BUTTON_PUSHED_ID, CFG_SCH_PRIO_0);   return; }   层层嵌套调用很冗长吧,除去简单判断,就是做了个触发事件处理(任务)的操作。对按键的响应转移到了一个任务函数中,而不是在中断服务里面处理,这个特点又和RTOS一致。   再看另外一个,我跟踪了一次 IPCC接收中断(和CPU2通信使用)的响应过程: void IPCC_C1_RX_IRQHandler(void) {   HW_IPCC_Rx_Handler();   return; }   调用的是 hw_ipcc.c 中的 HW_IPCC_Rx_Handler 来处理 void HW_IPCC_Rx_Handler( void ) {   if (HW_IPCC_RX_PENDING( HW_IPCC_THREAD_NOTIFICATION_ACK_CHANNEL ))   {         HW_IPCC_THREAD_NotEvtHandler();   }   else if (HW_IPCC_RX_PENDING( HW_IPCC_BLE_EVENT_CHANNEL ))   {         HW_IPCC_BLE_EvtHandler();   }   else if (HW_IPCC_RX_PENDING( HW_IPCC_SYSTEM_EVENT_CHANNEL ))   {         HW_IPCC_SYS_EvtHandler();   }   else if (HW_IPCC_RX_PENDING( HW_IPCC_TRACES_CHANNEL ))   {         HW_IPCC_TRACES_EvtHandler();   }   else if (HW_IPCC_RX_PENDING( HW_IPCC_THREAD_CLI_NOTIFICATION_ACK_CHANNEL ))   {     HW_IPCC_THREAD_CliNotEvtHandler();   }   return; }   根据硬件寄存器标志位,选择了调用 HW_IPCC_BLE_EvtHandler 函数 static void HW_IPCC_BLE_EvtHandler( void ) {    HW_IPCC_BLE_RxEvtNot();    LL_C1_IPCC_ClearFlag_CHx( IPCC, HW_IPCC_BLE_EVENT_CHANNEL );    return; }   在清除 IPCC 寄存器相关标志位之前,调用 tl_mbox.c 中的 HW_IPCC_BLE_RxEvtNot 函数 void HW_IPCC_BLE_RxEvtNot(void) {   TL_EvtPacket_t *phcievt;   while(LST_is_empty(&EvtQueue) == FALSE)   {     LST_remove_head (&EvtQueue, (tListNode **)&phcievt);     BLE_IoBusEvtCallBackFunction(phcievt);   }   return; }   上面这个函数要处理一个列表,对每个 HCI 事件,调用回调函数 BLE_IoBusEvtCallBackFunction, 但这是一个函数指针 static void (* BLE_IoBusEvtCallBackFunction) (TL_EvtPacket_t *phcievt); 还需要找到它指向的函数。不难找到,在 TL_BLE_Init() 函数里面有这一行:   BLE_IoBusEvtCallBackFunction = pInitHciConf->IoBusEvtCallBack; 也就是 TL_BLE_Init() 传入参数——一个 TL_BLE_InitConf_t 类型的指针所指定的函数。TL_BLE_Init() 又是被间接调用的,在 TlInit() 函数中有如下代码:     Conf.IoBusEvtCallBack = TlEvtReceived;     hciContext.io.Init(&Conf);   所以,BLE_IoBusEvtCallBackFunction 实际上是 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; }   它里面的两个函数与调度器有关: 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; }   所以,这个中断也触发了事件,后面的处理工作还是由调度器管理的。   分析到这里,对 demo 程序的软件结构大概有了个了解。这是事件驱动的写法,可是嵌套的调用太多,分析起来不直观。不知道作为这上面软件的开发者感觉又如何。 此内容由EEWORLD论坛网友cruelfox原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 回复了主题帖: 大幅度正弦波信号频率采集电路

    这么高的电压,用电压互感器吧

  • 回复了主题帖: PNP三极管be极间电阻的作用分析

    R3, R2 串联起来已经起到 R1 的功能了:lol

  • 回复了主题帖: PNP三极管be极间电阻的作用分析

    这是要做超压关断保护吗? R2, R3是分压,不能省掉R3. 倒是 R1可以不用。 PNP管子并非是饱和/截止两种状态,还有线性区域。后面MOSFET也是。

  • 加入了学习《黑科技探头:碰一下就知电流大小(英文,国外网友评测)》,观看 黑科技探头:碰一下就知电流大小(英文,国外网友评测)

  • 2019-05-20
  • 回复了主题帖: 【STM32WB55 测评】BLE_p2pServer工程提取&编译

    我遇到的 printf 输出没有到串口的问题: 将 dbg_trace.c 中定义的 __write() 函数改名为 _write() 就好了. 这是C标准库实现的差异.

  • 回复了主题帖: 【麦昆试用】python编程(2)

    欢乐颂啊:titter: 看起来很奇怪,怎么有一个#A, 没有 D, E, B 这几个音。从后面 'A', 'G', 'F', 'F', 'G', 'A', 'G:24', 'F:8', 'F:32' 推断 F 是主音。 你这写得不对,升A 应该为 降B,大调式音阶没有 A, #A 这么写的。把我误导了:titter:  本来哼哼就知道的。

  • 加入了学习《色卡密码锁》,观看 色卡密码锁

  • 2019-05-18
  • 回复了主题帖: 【STM32WB55 测评】BLE_p2pServer工程提取&编译

    上面贴的代码中有一个 -$@  被论坛系统识别为 URL, 然后帖子就要审核了 :lol

  • 发表了主题帖: 【STM32WB55 测评】BLE_p2pServer工程提取&编译

      CubeWB 当中提供了不少的例子,可以给软件开发环境直接使用。支持三种IDE——IAR EWARM, MDK-ARM 和 SW4STM32. 后者是用GCC编译器的。我没有这些开发环境,一贯是用GCC+make直接搞,所以要费一番工夫了。当然是利用 SW4STM32 例子作为参考,这里有GCC需要用到的启动文件和链接脚本。   先练练手,就自己编译 Nucleo 上面带的 BLE_p2pServer 工程,好检验效果。目录是 CubeWB 软件包的 Projects\NUCLEO-WB55.Nucleo\Applications\BLE\BLE_p2pServer,里面的目录树结构如下: ├─Binary ├─Core │  ├─Inc │  └─Src ├─EWARM ├─MDK-ARM ├─STM32_WPAN │  ├─App │  └─Target └─SW4STM32     └─BLE_p2pServer 这里,Binary目录有一个编译好的 HEX 文件可作为参照。万一搞砸了搞不定可以拿它烧回板子去,恢复功能。Core 目录和 STM32_WPAN 目录下面都是源代码,其余目录就是不同开发环境各自的工程文件了。   我的目的是自己写一个 Makefile, 然后在命令行上执行一下 make 命令就可以编译完整个工程的文件,以后每次修改以后再 make 就可以。如果有 SW4STM32 的话我猜想它会生成一个 Makefile, 可惜我没有(曾经想下载,没成功)。然后呢,把用到的比如 HAL 库,无线库中的文件提取出来另外存放(我不想保留庞大的库的目录树,平常也不用),供编译现有工程和以它们为参照的开发。   光有 Core 和 STM32_WPAN 两个子目录的文件必然不够。用到的大量函数库的文件并不在这里,比如在 CubeWB 包中的 Drivers 目录下。对于头文件,可以根据工程源文件里面所有 #include 的线索去找,然后把这些文件搜集起来,另存放在一个目录下(因为ST的库大,文件多,并不是都用得着的);对于 .c 文件,只能在链接的时候根据缺失的符号(函数名或全局变量)去找它们在哪个文件里面——这两种都接近于“人肉”操作,当然自己写个工具程序来做也是可以。按照我以往的经验,对付 HAL 库这种的引用,费点手工就能搞定了。结果确实如此,HAL库的目录树还是简单的。然而,对于 WPAN 库,就是无线功能部分,我就搞不定了:文件太多,出现了编译错误,在没有报告头文件缺失的情况下就缺了类型定义。   于是放弃“人肉”,从 SW4STM32 工程的文件里面下手。这里的 .project 和 .cproject 两个文件是 XML 文件,当文本看是可以的。不难找到,所有的 include 的路径都写在里面了:   WPAN库 有那么多个子目录要包含,“人肉”搞果然不方便(后来发现原因是,不同目录下的头文件有重名的,难怪我会找错了)。于是我把这些路径都写到我的编译命令行参数 -I 中去,就可以顺利编译了。先借用原始的库目录树完成编译,再提取文件。   下面的问题是怎样确定那些 .c 文件是需要的。在工程文件中必然有线索,如下面这种:   那么把文本处理一下,不难得到文件列表了。按目录再整理整理,我的 Makefile 就差不多了。 default: test.hex CC =arm-none-eabi-gcc -c LD =arm-none-eabi-gcc -nostdlib CFLAGS =-g -O2 -Wall -mcpu=cortex-m4 -mthumb -DSTM32WB55xx -DUSE_STM32WBXX_NUCLEO -ffunction-sections -fdata-sections LDFLAGS=-mcpu=cortex-m4 -mthumb -T ../stm32wb55xx_flash_cm4.ld -Wl,--gc-sections INC =-I. -IWPAN -I../../ST -I../../CMSIS -I../BSP -I../HAL -I../radio/Inc APPOBJ = main.o \          app_entry.o \          hw_lpm.o \          hw_timerserver.o \          hw_uart.o \          stm32wbxx_hal_msp.o \          stm32wbxx_it.o \          app_ble.o \          p2p_server_app.o \          hw_ipcc.o \          stm32wbxx_nucleo.o           HALOBJ = hal/stm32wbxx_hal.o  \          hal/stm32wbxx_hal_cortex.o \          hal/stm32wbxx_hal_pwr.o \          hal/stm32wbxx_hal_pwr_ex.o \          hal/stm32wbxx_hal_rcc.o \          hal/stm32wbxx_hal_rcc_ex.o \          hal/stm32wbxx_hal_uart.o \          hal/stm32wbxx_hal_uart_ex.o \          hal/stm32wbxx_hal_gpio.o \          hal/stm32wbxx_hal_dma.o \          hal/stm32wbxx_hal_rtc.o \          hal/stm32wbxx_hal_rtc_ex.o BLEOBJ = radio/ble_gap_aci.o \          radio/ble_gatt_aci.o \          radio/ble_hal_aci.o \          radio/ble_hci_le.o \          radio/ble_l2cap_aci.o \          radio/dbg_trace.o \          radio/hci_tl.o \          radio/lpm.o \          radio/osal.o \          radio/otp.o \          radio/p2p_stm.o \          radio/scheduler.o \          radio/shci.o \          radio/shci_tl.o \          radio/stm_list.o \          radio/stm_queue.o \          radio/svc_ctl.o \          radio/tl_if.o \          radio/tl_mbox.o test.elf : $(APPOBJ) system_stm32wbxx.o startup_stm32wb55xx_cm4.o $(HALOBJ) $(BLEOBJ)     $(LD) $(LDFLAGS) $^ -o $[url=home.php?mod=space&uid=775551]@[/url] %.o : ../%.s     $(CC) $(CFLAGS) $< %.o : %.c     $(CC) $(CFLAGS) $(INC) $< %.o : WPAN/%.c     $(CC) $(CFLAGS) $(INC) $< %.o : ../BSP/%.c     $(CC) $(CFLAGS) $(INC) $< hal/%.o : ../HAL/%.c     $(CC) $(CFLAGS) $(INC) $< -o $@ radio/%.o : ../radio/%.c     $(CC) $(CFLAGS) $(INC) $< -o $@ %.hex : %.elf     arm-none-eabi-objcopy -Oihex $< $@复制代码   radio 子目录用来放 WPAN 库中提取出来的文件,把原始的目录树放弃了,简化嘛,毕竟也记不住。好在 .c 文件数量并不很多。radio/Inc 目录里面的头文件是后来确定后放进去的,用到哪些头文件在SW4STM32的工程文件里面并没有写。   各个文件编译完成后在链接时出错了,报告说 _read, _lseek 等函数找不到。无疑,这是工程里面用了 printf 引起的,C标准库文件系统的函数。我找到的解决办法是在 stm32wb55xx_flash_cm4.ld 中在那几个库文件列表处补充了一行 libnosys.a ( * )   终于得到了 HEX 文件。烧进板子去运行,Dongle 能连上它。但遗留问题是:虚拟串口没有信息输出,很可能还是 printf 相关的问题。 另外还有一个现象是,我生成的 HEX 文件要比 ST 提供的参考文件大 60% 多,编译器的差异也太明显了?   头文件的提取,最后我用了访问时间戳的办法,在一次全部编译的前后根据文件的访问时间变化来查找被用到的头文件。于是,WPAN 库的文件得到了精简。提取后目录树如下: │  startup_stm32wb55xx_cm4.s │  stm32wb55xx_flash_cm4.ld │   ├─BLE_p2pServer │  │  app_common.h │  │  app_conf.h │  │  app_entry.c │  │  app_entry.h │  │  hw_conf.h │  │  hw_lpm.c │  │  hw_timerserver.c │  │  hw_uart.c │  │  main.c │  │  main.h │  │  Makefile │  │  stm32wbxx_hal_conf.h │  │  stm32wbxx_hal_msp.c │  │  stm32wbxx_it.c │  │  stm32wbxx_it.h │  │  system_stm32wbxx.c │  │  utilities_conf.h │  │   │  ├─hal │  ├─radio │  └─WPAN │          app_ble.c │          app_ble.h │          ble_conf.h │          ble_dbg_conf.h │          hw_ipcc.c │          p2p_server_app.c │          p2p_server_app.h │          ├─BSP │      stm32wbxx_nucleo.c │      stm32wbxx_nucleo.h │       ├─HAL │  │  stm32wbxx_hal.c │  │  stm32wbxx_hal.h │  │  stm32wbxx_hal_cortex.c │  │  stm32wbxx_hal_cortex.h │  │  stm32wbxx_hal_def.h │  │  stm32wbxx_hal_dma.c │  │  stm32wbxx_hal_dma.h │  │  stm32wbxx_hal_dma_ex.h │  │  stm32wbxx_hal_flash.h │  │  stm32wbxx_hal_flash_ex.h │  │  stm32wbxx_hal_gpio.c │  │  stm32wbxx_hal_gpio.h │  │  stm32wbxx_hal_gpio_ex.h │  │  stm32wbxx_hal_hsem.h │  │  stm32wbxx_hal_ipcc.h │  │  stm32wbxx_hal_pwr.c │  │  stm32wbxx_hal_pwr.h │  │  stm32wbxx_hal_pwr_ex.c │  │  stm32wbxx_hal_pwr_ex.h │  │  stm32wbxx_hal_rcc.c │  │  stm32wbxx_hal_rcc.h │  │  stm32wbxx_hal_rcc_ex.c │  │  stm32wbxx_hal_rcc_ex.h │  │  stm32wbxx_hal_rtc.c │  │  stm32wbxx_hal_rtc.h │  │  stm32wbxx_hal_rtc_ex.c │  │  stm32wbxx_hal_rtc_ex.h │  │  stm32wbxx_hal_uart.c │  │  stm32wbxx_hal_uart.h │  │  stm32wbxx_hal_uart_ex.c │  │  stm32wbxx_hal_uart_ex.h │  │  stm32wbxx_ll_bus.h │  │  stm32wbxx_ll_cortex.h │  │  stm32wbxx_ll_crs.h │  │  stm32wbxx_ll_dma.h │  │  stm32wbxx_ll_dmamux.h │  │  stm32wbxx_ll_exti.h │  │  stm32wbxx_ll_hsem.h │  │  stm32wbxx_ll_ipcc.h │  │  stm32wbxx_ll_pwr.h │  │  stm32wbxx_ll_rcc.h │  │  stm32wbxx_ll_system.h │  │  stm32wbxx_ll_utils.h │  │   │  └─Legacy │          stm32_hal_legacy.h │          └─radio     │  ble_gap_aci.c     │  ble_gatt_aci.c     │  ble_hal_aci.c     │  ble_hci_le.c     │  ble_l2cap_aci.c     │  dbg_trace.c     │  hci_tl.c     │  lpm.c     │  osal.c     │  otp.c     │  p2p_stm.c     │  scheduler.c     │  shci.c     │  shci_tl.c     │  stm_list.c     │  stm_queue.c     │  svc_ctl.c     │  tl_if.c     │  tl_mbox.c     │       └─Inc             ble.h             ble_common.h             ble_const.h             ble_defs.h             ble_gap.h             ble_gap_aci.h             ble_gatt_aci.h             ble_gatt_server.h             ble_hal.h             ble_hal_aci.h             ble_hci_le.h             ble_l2cap_aci.h             ble_status.h             ble_types.h             bls.h             common_blesvc.h             compiler.h             crs_stm.h             dbg_trace.h             dis.h             eds_stm.h             hal_types.h             hci_const.h             hci_tl.h             hids.h             hrs.h             hts.h             hw.h             ias.h             link_layer.h             lls.h             lpm.h             mbox_def.h             mesh.h             osal.h             otas_stm.h             otp.h             p2p_stm.h             scheduler.h             shci.h             shci_tl.h             stm32_wpan_common.h             stm_list.h             stm_queue.h             svc_ctl.h             template_stm.h             tl.h             tps.h             utilities_common.h             uuid.h 此内容由EEWORLD论坛网友cruelfox原创,如需转载或用于商业用途需征得作者同意并注明出处

  • 2019-05-11
  • 加入了学习《DIY作品演示》,观看 基于MQTT消息通讯的IoT入门实例

  • 2019-05-09
  • 回复了主题帖: 1kHZ,0dB的测试信号

    topwon 发表于 2019-5-9 17:12 再具体一点吧。比如一台MP3播放器,我卡里的文件是“1kHZ,0dB.mp3”,用示波器观察lineout接口波形,先假设 ...
    不对,MP3文件表达的是数字信号。 数字信号和电压没有绝对的对应关系。 此处 0dB 应当是 0dBFS, 即最大幅度量化的正弦信号。

  • 回复了主题帖: 大牛分享滤波电容使用心得

    我看标题以为“牛”是指的变压器。

最近访客

< 1/6 >

统计信息

已有394人来访过

  • 芯币:5845
  • 好友:10
  • 主题:105
  • 回复:908
  • 课时:1
  • 资源:1

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言