个人对SDK框架的一些理解,如有错误欢迎斧正。
flash 分区
在不包含DFU的情况下,nrf52832 flash划分为:
-
MBR
0x00000000 - 0x00001000 为主引导程序(MBR),包括中断向量表和主引导程序两部分,其中中断向量表用于处理派发中断回调函数,主引导程序主要用于判断是否存在DFU,判断跳转地址。
-
SoftDevice
0x00001000 - 0x00026000 为协议栈程序
-
APP
用户程序
RAM分配
-
协议栈占用RAM
一般情况下把MBR+SoftDevice 统称为协议栈,协议栈占用RAM大小可以从协议栈配套文档获得。如果协议栈RAM过小,log信息会给出相应提示。
-
用户APP使用RAM
-
在包含DFU的情况下,nrf52832 flash划分为:
- MBR
0x00000000 - 0x00001000 为主引导程序(MBR),包括中断向量表和主引导程序两部分,其中中断向量表用于处理派发中断回调函数,主引导程序主要用于判断是否存在DFU,判断跳转地址。
- SoftDevice
0x00001000 - 0x00026000 为协议栈程序
- APP
用户程序
- bootLoader
DFU 蓝牙空中升级boot,地址根据DFU程序大小自行设置。
- bootLoader Parameter
0x0007E000 - 0x00080000 DFU 蓝牙空中升级boot参数区,用于记录升级信息(升级过程断电的话,下次可以接着升级,相当与有断点续传功能),APP版本,BOOT版本,APP校验,参数区校验等信息。如果没有此分区,程序将无法正常运行。
nRF52832上电后从固定位置0x0000 0000开始执行程序,flash 0x0000 0000–0x0002 6000存放Nordic的协议栈s132,协议栈s132前面4KB(0x0000-0x1000)为主引导程序(MBR),MBR根据地址0xFF8或者0x1000 1014中是否存在DFU程序起始地址决定跳转地址,如果地址0xFF8或者0x1000 1014中存在DFU程序起始地址则会跳转至DFU,DFU运行结束后程序会跳转0x0000 1000,然后协议栈根据协议栈大小跳转至协议栈结束地址也就是APP起始地址0x26000,至此开始执行APP程序。
用户app如何调用协议栈#define SVCALL(number, return_type, signature) return_type __svc(number) signature
用户APP通过触发SVC中断的方式调用协议栈相关 *** 作,将协议栈和用户程序完全分开,互不干预。协议栈相关函数声明都在ble.h中,以sd开头。以 sd_ble_enable 函数为例,用户在调用 sd_ble_enable 时,会触发SVC中断,协议栈中SVC中断服务函数根据 SVC服务号(SD_BLE_ENABLE)调用协议栈相应的函数进行处理,然后返回处理结果。
//SVC中断的等效代码,具体不是这样的
unsigned long svc_handler(int svc_num, void * param)
{
switch(svc_num)
{
case SD_BLE_ENABLE:
{
...//协议栈 *** 作
break;
}
...
}
}
从上面代码可以看出SVC中断和其他中断服务函数不同,有参数和返回值,有参数是因为在进SVC中断前将参数入栈,中断服务函数从对应栈空间取参数。返回值则是在退出时将返回值存入R0寄存器中。具体可以看 《Cortex-M3权威指南》 第11章 使用异常系统
协议栈如何上报状态到用户观察者函数当蓝牙事件产生时(比如扫描到广播包,连接主从机成功等),协议栈会通过软中断(SWI)将蓝牙事件分发给用户APP,用户在观察者回调函数中添加自己的处理代码。
nrf52832 flash中存在3个中断向量表,但是在APP仿真时发现SCB->VTOR为 0x00000000,因此只有MBR Vector才是真的中断向量表,发生中断时会进入MBR Vector,在MBR Vector 中调用 SoftDevice Vector 中的中断服务函数,然后在SoftDevice Vector 中的中断服务函数中再调用 app vector table。
由于中断服务函数多次跳转,相比其他单片机,nrf52832 用户中断服务函数的中断延时相对比较高。
#define NRF_SECTION_ITEM_REGISTER(section_name, section_var) \
section_var __attribute__ ((section(STRINGIFY(section_name)))) __attribute__((used))
#define NRF_SECTION_SET_ITEM_REGISTER(_name, _priority, _var) \
NRF_SECTION_ITEM_REGISTER(CONCAT_2(_name, _priority), _var)
#define NRF_SDH_BLE_OBSERVER(_name, _prio, _handler, _context) \
STATIC_ASSERT(NRF_SDH_BLE_ENABLED, "NRF_SDH_BLE_ENABLED not set!"); \
STATIC_ASSERT(_prio < NRF_SDH_BLE_OBSERVER_PRIO_LEVELS, "Priority level unavailable."); \
NRF_SECTION_SET_ITEM_REGISTER(sdh_ble_observers, _prio, static nrf_sdh_ble_evt_observer_t _name) = \
{ \
.handler = _handler, \
.p_context = _context \
}
#define APP_BLE_OBSERVER_PRIO 3
//注册观察者函数
NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
在用户app中注册观察者函数,在观察者函数中处理蓝牙事件。注册观察者函数使用了一堆宏定义,这一大堆宏定义最终变成如下
static nrf_sdh_ble_evt_observer_t m_ble_observer __attribute__ ((section(sdh_ble_observers3))) __attribute__((used)) =
{
.handler = ble_evt_handler,
.p_context = NULL
}
其实就干了两件事:
- 声明了一个静态变量
- 将该静态变量存放在 sdh_ble_observers3 段中
软件中断服务函数最终调用 app软件中断服务函数 SD_EVT_IRQHandler, 在 nrf_sdh_evts_poll 中遍历调用sdh_ble_observers0、sdh_ble_observers1等flash段中所有的观察者回调函数。
因此nrf52832 sdk中蓝牙各个部分之间的耦合度非常小,每个部分都有自己的观察者回调函数。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)