一、nrf51822芯片和协议栈特性
51822芯片特色:
- 各个模块的功能引脚可以随意选择,并不像传统的单片机通过IO复用实现,固定好了某个功能只能选用哪个IO口。传统单片机的IO功能复用通过寄存器使能其第二功能,而51822每个模块都能通过模块的寄存器选用各个引脚。
- PPI系统。相比中断更能降低处理器的占用,降低功耗。
协议栈特性:
- 协议堆栈和应用程序代码分离,没有链接时间依存性,分开编译和更新。没有专用的应用程序框架或RTOS依存性,TI的就有个轮询框架。
- 堆栈为百分百的异步并由事件驱动,并向应用程序层提供线程安全管理程序调用SVC的应用程序接口。
协议栈向APP发送信息是通过事件发动的!
APP向协议栈发送信息是通过API接口,而API接口通过SVC机制实现的。
协议栈如何发送数据给应用程序:
当有事件来临,比如连接请求或者对端设备发来了数据,协议栈内部会进行处理,处理完就会置位SWI2中断,中断内部就会调用事件分发,发送一个事件,给了事件分发任务(dispatch),这个任务会发给每个服务,每个服务会判断这个事件是什么,如果是自己感兴趣的就进行处理,比如连接请求是每个服务都感兴趣,服务可能就会保存连接句柄,因为连接句柄是后续处理都要用的。
(if部分一般是不通的,没有使能调度部分。)
这个中断的触发在协议栈内部,我们看不到,比如协议栈收到发给自己的数据要进行处理,就会包装一个事件,然后触发这个SWI2中断函数!
intern_softdevice_events_execute函数中就会间接调用ble_evt_dispatch函数,把这个事件交给后者处理 ,后者就把事件发给各个服务。
(最开始的ble_stack_init协议栈初始化函数中通过函数softdevice_ble_evt_handler_set(ble_evt_dispatch),赋值给全局变量m_ble_evt_handler = ble_evt_dispatch;)
static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
{
dm_ble_evt_handler(p_ble_evt);
ble_conn_params_on_ble_evt(p_ble_evt);
bsp_btn_ble_on_ble_evt(p_ble_evt);
on_ble_evt(p_ble_evt);
ble_advertising_on_ble_evt(p_ble_evt);
/*YOUR_JOB add calls to _on_ble_evt functions from each service your application is using
ble_xxs_on_ble_evt(&m_xxs, p_ble_evt);
ble_yys_on_ble_evt(&m_yys, p_ble_evt);
*/
}
应用程序如何发数据给协议栈:
在服务中通过调用API即可。
比如调用数据发送函数,然后就是协议栈的事情了,我们也看不到代码实现了。大致过程如下:
number就相当于ID号一样。加入我们调用的数据发送功能函数的ID是3,就相当于调用了_SVC(3),然后就会“陷入”协议栈中的SVC中断函数。
上图中,下半部是协议栈的代码,上半部是我们自己写的程序代码,分别有一个中断向量表。但是实际上硬件只认一个,所以应用程序要紧挨着协议栈存储,协议栈即可找到应用程序中设定的“中断向量表”,需要的时候跳转就可。(协议栈中会有一个指针指向烧写协议栈后的最末端的地址。)
实际上,当真正的外部一切中断到来时,都是到达协议栈内部的中断向量表中,找到对应的中断处理函数,此函数会找到APP中断向量表的偏移,最终定位到用户写的handler中断处理函数。
二、nrf51 sdk框架
必备的部分如下:
int main(void)
{
ble_stack_init();
gap_params_init();
advertising_init();
services_init();//跟自己创建的服务相关,不同的服务细节不同但大致建立过程一致,通常在官方的例子上修改一些参数即可。
ble_advertising_start(BLE_ADV_MODE_FAST);
// Enter main loop.
for (;;)
{
power_manage();//进入睡眠。
/*内部仅仅调用了sd_app_ev