1. 主函数
下面结合上面的函数慢慢展开讲解。 2. 宏定义
3. 定时器
3.1. APP_TIMER_APPSH_INT宏Nordic的定时器是通过RTC1模拟出了一个定时器时钟队列,所以在初始化时需要传入的参数有两个: APP_TIMER_PRESCALER:时钟分频 APP_TIMER_OP_QUEUE_SIZE:timer的队列 第3个参数是是否启动调度,这里true表示启用任务调度。也就是说会调用app_timer_evt_schedule函数进行时间任务调度,后面会讲解。
上面的初始化时使用的宏,那么真正的初始化函数是:
上面又是一个宏进行封装的,实体如下:
不继续向下分析了,到这定时器就初始化完毕了。截取SDK说明文档中的说明吧!
0
|
|
|
|
3.2. 定时器创建app_timer_create定时器创建函数如下:
这里定时器句柄是通过宏进行定义的:
这个宏的函数原型是:
上面的“##” 表示连接前后的字符,也就是上面的宏表示 static app_timer_t m_sec_req_timer_id_data= { {0} }; static const app_timer_id_t timer_id = &m_sec_req_timer_id_data; 实际上这个宏就是定义个两个变量。
3.3. 回调函数sec_req_timeout_handler回调函数的作用,从名字上看的话,这个回调函数应该与安全管理有关,那么到底什么关系呢?我们知道ANCS服务必须是在配对绑定启动加密之后才能进行服务的,也就是必须进行配对绑定,然而配对就必须通过安全管理进行,所以这个回调函数是从机启动加密请求,然而为啥又通过定时器进行控制呢?这里只有当连接建立之后进行一次调用,所以在创建定时器时使用的APP_TIMER_MODE_SINGLE_SHOT参数,为什么需要用定时器,因为刚刚建立连接时需要给双方一定的缓冲时间,在后面启动定时器时会传入定时时间的。 看看这个函数吧:
这个回调函数有个入口参数,void * p_context。这个参数是在启动定时器时传进来的,当然看有没有用了,如果没有用,就传入NULL。
|
|
|
|
|
3.4. 启动定时器发送加密请求启动定时器的函数如下:
在ANCS应用中是在设备进行连接之后进行发起的,因为需要绑定,所以需要用到nordic的SDK中的设备管理文件,所以这个启动定时器是在设备管理事件回调函数中调用。
|
|
|
|
|
4. 协议栈初始化ble_stack_init()协议栈初始化ble_stack_init()函数原型如下:
第710函数设置低频时钟源,并初始化协议栈,第2个参数是在使用调度或者RTOS时传入的回调函数句柄,例如如果带有freertos时,会传入一个回调函数,这个回调函数会发出一个二值信号量,这个信号量发给协议栈。 第713行其实就是初始化结构体ble_enable_params,这个结构体如下:
第721行是根据程序所需的内存大小来动态的进行设置起始地址和ram大小。 在mdk中有一个可以设置RAM大小的地方:
但是程序并不是一定是从这个地方开始,所以这个函数进行重新调整,具体怎么调整可以自己看源码,如果需要调整会打印:
|
|
|
|
|
4.1. 协议栈事件有哪些第728和第732行是向协议栈和系统注册事件对应的处理函数。52832的整个协议都是通过事件进行驱动的,协议栈和事件可以向应用层发送许多的事件,应用层更具得到的事件在进行相应的处理,例如
那么这些怎么知道协议栈和系统到底能发送那些事件给应用层呢?可以查看一下注册函数传入的参数是什么:
也就只有知道ble_evt_t这个结构体中有哪些事件即可:
从上面可以了解到evt_id都是以BLE__EVT定义的枚举,然而有哪些模块可以上传事件呢?在上图的191行的联合体中定义了5个模块可以向应用发送事件。在ble_ranges.h的头文件中有如下宏:
这个宏就是各个事件枚举的起始值和结束值,根据这些宏可查找到这5个事件的所有枚举。分别为: l 公共事件
l GAP事件
l L2CAP事件
l GATT客户端事件
l GATT服务器事件
|
|
|
|
|
4.2. 系统事件有哪些先看看系统分发事件函数中到底分发了哪些东西:
追溯到函数里面去的话可以发现,上面两个函数用到的事件ID只用到两个: NRF_EVT_FLASH_OPERATION_SUCCESS //flash操作成功 NRF_EVT_FLASH_OPERATION_ERROR //flash操作失败 ble_advertising_on_sys_evt这个函数是当flash失败时开始广播。
|
|
|
|
|
5. 设备管理
5.1. 设备管理初始化在52832的SDK11中加了device_manager_init函数,包含了之前版本的安全参数初始化的内容。
device_manager_init这个函数传入的参数为bool类型,为true时擦除flash中存储的所有设备信息,为false时就不擦除绑定的设备信息,这些信息用于广播白名单列表以及秘钥的一些保存。 第469行调用pstorage_init()函数将flash进行初始化。 第465行将传入的参数赋值个一个结构体,这个结构体作为参数传入dm_init()函数,其实就是进行flash进行擦除的函数。 register_param.sec_param结构体变量将开始的定义的安全管理的宏传入结构体,这个安全管理参数只进行绑定,临时秘钥TK为6个0。 第490行为设备管理注册,第一个参数m_app_handle为这个函数的返回值,也就是设备管理返回的一个应用层用的句柄。52832最多可以保存8个设备的信息,多了就要去擦除。
|
|
|
|
|
5.2. 设备管理事件回调函数
在485行还传入了一个函数device_manager_evt_handler,这个函数的函数定义如下:
分别是: l 连接完成事件 l 连接断开事件 l 加密开始事件 l 加密完成事件 l 已经处于加密通信连接 l 加密重新建立
|
|
|
|
|
6. 对端设备服务发现对端设备有么有ANCS的服务,需要本地设备在连接之后去发现对端设备的服务,这里的初始化函数如下:
上面的调用了一个函数,同时也传入了一个函数。 ble_db_discovery_init函数定义如下:
db_disc_handler函数定义如下:
继续往下走可以知道ble_ancs_c_on_db_disc_evt函数是对ANCS服务的具体发现,传入的两个参数,第一个参数是应用层定义的关于ANCS的一个结构体,第二个参数是设备发现时协议栈向应用层上报的事件。
|
|
|
|
|
7. 任务调度队列Scheduler_init函数如下:
如果不跑rtos,SDK11中是使用一个队列来管理多个任务的,因为中断中不能处理太多和时间太长的任务,所以在中断中将相应的事件标志以及处理函数放入队列,然后在主函数中,一直轮训这个队列处理相应的任务。 从上面分析可以大概了解,应该有3个函数需要调用: l 列初始化函数APP_SCHED_INIT; l 事件注入队列的函数app_sched_event_put; l 放在主循环中的一个函数来轮训队列app_sched_execute。
|
|
|
|
|
7.1. APP_SCHED_INIT他其实是一个宏:
这里注意传入的两个参数:
l EVENT_SIZE
这个size的大小就像定义一个联合体,联合体所占用的内存是变量中占用内存最大的那个,这里的size也是这样的,假设有3个任务,这3任务产生的事件分别为1、3、7个,假设每个事件所占用的内存为1个字节。那么这里传入的EVENT_SIZE应该为7*1byte,即7个字节。 l QUEUE_SIZE 任务调度室队列的大小,这个队列其实就像一个缓存,中断函数是这个缓存的数据生成者,主循环是这个缓存的消耗者,假设中断生产的数据比主循环消耗的数据要快,那么是不是通过扩大缓存来解决这个不平衡的问题。通用这个QUEUE_SIZE也是根据中断产生事件的速度来觉得这个队列的大小的,假设有2个地方用到了app_sched_event_put,那么这个队列的最小设置为2。如果调用app_sched_event_put的频率非常高,那就就要尝试这扩大这个大小了。
|
|
|
|
|
7.2. app_sched_event_put他的函数定义如下:
传入的参数有3个,先看第3个app_sched_event_handler_t,这是一个函数指针。定义如下:
可以知道,前两个参数其实就在这个回调函数的参数。 7.2.1. app_timer_evt_schedule在讲定时器章节中的APP_TIMER_APPSH_INT宏中,有传入一个定时器的回调函数:
进入看看:
app_timer_evt_get函数就是任务的处理函数
7.2.2. ANCS实例中的任务调度时最终调用的定时器回调函数而这里实际的处理函数是在创建定时器时传入的回调函数:
|
|
|
|
|
8. 连接参数连接参数有哪些需要设置?4个供主机参考的参数,最小连接间隔、最大连接间隔、从机潜伏期和连接超时时间。具体函数如下:
第350行设置的是安全模式,在ble4.0中有如下几种安全模式: 安全模式1等级1:连接无需安全管理 安全模式1等级2:需要加密但无中间人保护/加密的未认证配对 安全模式1等级3:有加密且有中间人保护/加密的认证配对 安全模式2等级1:签名但无中间人保护/数据签名的未认证配对 安全模式2等级2:中间人签名保护/带数据签名的认证配对 这里设置的是安全模式1等级1,即没有安全需求,这里是不是有疑问,ANCS不是必须进行绑定吗?为啥设置怎么低的安全等级,因为这个里程中安全请求是有本地发起的。 第352行将设备名字传入协议栈。 第364行设置的就是基本的连接参数。
|
|
|
|
|
9. 广播初始化广播初始化主要是设置一些广播间隔时间、超时时间以及广播模式等等参数。也会设置广播内容,例如广播自己有哪些服务UUID,也可以广播自己想要得到哪些服务的UUID,也可以设置扫描应答的数据。本例具体函数如下:
这里需要注意的是,广播初始化一定要放到服务初始化函数的后面,因为广播初始化中有些参数是由服务初始化时传递上来的,例如: 第804行的UUID类型就是服务初始化时协议栈传出来的。 第814和815行设置的是自己希望对端设备拥有的服务的UUID,即ANCS服务。 第826行就是广播的初始化函数了,在这个函数中传入了一个事件回调函数on_adv_evt,在协议栈初始化时向协议栈以及系统注册的事件分发函数是连接之后的回调,这里只是一个广播事件回调函数,那就看看广播事件到底有哪些事件可以产生:
可见广播事件也不多啊!上面的最后两个事件如果白名单没有使能或者没有启用定向广播,这两个事件是可以不用的。看看本例中的初始化!没什么可以解释的。
|
|
|
|
|
10. 连接参数初始化连接参数主要设置了连接参数更新是的时间,至于连接间隔范围什么的在GAP初始化时进行了设置,看图:
第403行的意思是如果设置为true时,当参数更新失败时,自动断开连接;如果设置为false时,参数更新失败时,继续使用原来的参数保持连接。 11. 启动广播
启动广播函数如下:
不进去看了,可以看看传入的参数为BLE_ADV_MODE_FAST,即快速广播模式。那么产生的广播事件也一定是快速广播事件。
|
|
|
|
|
头像被屏蔽
· 2018-5-26 15:55:30
|
|
|
|
|
头像被屏蔽
· 2018-5-26 15:57:39
|
|
|
|
|