BLE-NRF51822教程2-工程初始化流程
代码讲解基于资料包里配套的 sdk5.1版本中的串口demo。高版本的sdk基本都是差不多的。
代码在路径xxxxx\keil\ARM\Device\Nordic\nrf51822\Board\pca10001\s110\experimental中
一:main函数整体注释:
int main(void)
{
//初始化LED指示灯,用来指示广播和连接状态
leds_init();
//初始化软件定时器模块
timers_init();
//设置按键作为 DETECT signal 用来唤醒system off模式,具体参看数据手册power 章节
buttons_init();
//主要设置uart的引脚,波特率。接收,发送中断等。并开启uart模块中断
uart_init();
//协议栈初试化,设置时钟,demo里面设置为外部时钟。并且注册事件派发函数
ble_stack_init();
//GAP一些参数的设置,设置设备名,设置PPCP(外围设备首选链接参数)。(手机连上某个蓝牙设备后可以从Generic Access Service中看到设置的这些参数)
gap_params_init();
//服务初始化。添加uart的串口服务。主要提供两个特征值来供手机和板子以及电脑的通信
services_init();
//设置广播数据以及扫描响应数据
advertising_init();
//链接参数设置。主要设置什么时候发起更新链接参数请求以及间隔和最大尝试次数。
conn_params_init();
//安全参数初始化。
sec_params_init();
simple_uart_putstring(START_STRING);
//设置广播类型,白名单,间隔,超时等特性。并开始广播。
advertising_start();
for (;;)
{
//电源管理,调用arm0的指令__WFE();进入睡眠
power_manage();
}
}
二:函数单独解析:
1 leds_init
static void leds_init(void)
{
nrf_gpio_cfg_output(ADVERTISING_LED_PIN_NO);
nrf_gpio_cfg_output(CONNECTED_LED_PIN_NO);
}
设置的PIN_CONFIG寄存器使能两个引脚的作为输出功能。用来当做指示灯指示广播和链接的状态。
2 timers_init
static void timers_init(void)
{
// Initialize timer module
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_MAX_TIMERS, APP_TIMER_OP_QUEUE_SIZE, false);
}
初始化软件定时器模块,该定时器模块并不是使用timer0-2来实现定时功能。而是使用51822中的RTC1 来软件模拟出定时器模块。RTC1使用32.768K时钟经过分频后是时钟来作为时钟源。所以该函数内部实现就是设置RTC1相关的寄存器和做一些初始化。其原理和timer 定时/计数器模块类似。具体细节参考芯片数据手册。
APP_TIMER_PRESCALER:设置分频系数。(以32.768K来分频)
APP_TIMER_MAX_TIMERS:设置可以创建的最大定时器个数
APP_TIMER_OP_QUEUE_SIZE:定时器操作队列,因为是用RTC模拟的软件定时器,因此内部 是维护了一个软件定时器的操作队列
False:不使用调度,调度模块没有细看。貌似51822关于调度的都是传False不使用调度。 51822的协议栈实现是基于异步事件驱动的。
3buttons_init
static void buttons_init(void)
{
nrf_gpio_cfg_sense_input(WAKEUP_BUTTON_PIN,
BUTTON_PULL,
NRF_GPIO_PIN_SENSE_LOW);
}
这里的按键设置比较简单,主要通过PIN_CNF寄存器来设置一个IO口来作为来作为sensing mechanism机制的引脚。这里是设置了WAKEUP_BUTTON_PIN这个引脚来作为这个功能,设置成低电平时触发这个机制。而这个机制类似一个wakeup机制,当其被触发时会产生一个DETECT signal而这个信号会将cpu从system off模式中唤醒。
4 uart_init
static void uart_init(void)
{
simple_uart_config(RTS_PIN_NUMBER, TX_PIN_NUMBER, CTS_PIN_NUMBER, RX_PIN_NUMBER, HWFC);
NRF_UART0->INTENSET = UART_INTENSET_RXDRDY_Enabled<
NVIC_SetPriority(UART0_IRQn, APP_IRQ_PRIORITY_LOW);
NVIC_EnableIRQ(UART0_IRQn);
/**@snippet [UART Initialization] */
}
初始化uart设置输入输出引脚,是否关闭流控。一般使用官方例子的时候都要先将流控关掉,HWFC为False。然后打开uart的接收中断,打开uart模块的中断功能,以及设置优先级。 波特率在simple_uart_config中设置,该函数设置完引脚后使能uart开启uart的接收和发送功能。
5 ble_stack_init
static void ble_stack_init(void)
{
// Initialize SoftDevice.
SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
// Subscribe for BLE events.
uint32_t err_code = softdevice_ble_evt_handler_set(ble_evt_dispatch);
//设置LFCLK(32.768K)的时钟源(协议栈需要使用),这里设置为外部晶振。False为不使用调度。softdevice_ble_evt_handler_set(ble_evt_dispatch);注册事件派发程序,基础1-协议栈概述说明过,当BLE收到广播,链接请求,对端设备数据等后底层处理完会上抛给上册app一个事件,这个事件的上抛过程是协议栈触发SWI中断,在中断内部将事件放入队列,然后调用app中的SWI中断。App中的SWI中断会get队列中的事件,并最终会调用注册的ble_evt_dispatch函数,这个函数再将事件发给各个服务以及模块的事件处理函数来处理各个服务及模块自己感兴趣的事件。相关原理基础1-协议栈概述视频教程中有说明。
6gap_params_init
static void gap_params_init(void)
ble_gap_conn_params_tgap_conn_params;
ble_gap_conn_sec_mode_tsec_mode;
BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
//设置设备名,该设备名就是在手机app扫描蓝牙设备时显示的名字。
err_code = sd_ble_gap_device_name_set(&sec_mode,
(const uint8_t *) DEVICE_NAME,
memset(&gap_conn_params, 0, sizeof(gap_conn_params));
//当然,外围设备也可以之后通过sd_ble_gap_ppcp_get来获取之前设置的参数然后通过连接参数跟新请求函数向中央设备请求更改连接参数。
gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
gap_conn_params.slave_latency = SLAVE_LATENCY;
gap_conn_params.conn_sup_timeout = CONN_SUP_TIMEOUT;
err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
7 services_init
static void services_init(void)
memset(&nus_init, 0, sizeof(nus_init));
// nus_data_handler就是将板子收到的数据通过串口打印到电脑上
nus_init.data_handler = nus_data_handler;
err_code =ble_nus_init(&m_nus,& nus_init);
(1) ble_nus_init该函数中实现添加服务以及添加特征值
uint32_t ble_nus_init(ble_nus_t * p_nus, constble_nus_init_t * p_nus_init)
if ((p_nus == NULL) || (p_nus_init == NULL))
// 初始化连接句柄,因为现在并未与手机连接所以先赋值无效。
p_nus->conn_handle = BLE_CONN_HANDLE_INVALID;
p_nus->data_handler = p_nus_init->data_handler;
p_nus->is_notification_enabled = false;
// 因为是自己定义的uuid,所以需要调用该函数来赋值p_nus->uuid_type
//该函数会将这个nus_base_uuid放到协议栈内部的表中
err_code = sd_ble_uuid_vs_add(&nus_base_uuid,& p_nus->uuid_type);
if (err_code != NRF_SUCCESS)
{
returnerr_code;
}
//设置服务uuid以及uuid_type(就是上面调用的函数或得的)
ble_uuid.type = p_nus->uuid_type;
ble_uuid.uuid = BLE_UUID_NUS_SERVICE;
// 到这里就添加服务到协议栈内部表中了
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,
err_code = rx_char_add(p_nus, p_nus_init);
err_code = tx_char_add(p_nus, p_nus_init);
本篇教程主要说明协议栈的整体框架和。关于服务的创建和特征值的添加。这里不做细将,主要因为特征值的添加涉及到很多的概念和参数设置。后面分单独发一篇教程针对如何创建自己的服务并添加特征值。
8advertising_init
static void advertising_init(void)
//该标志主要设置广播类型为有限可发现模式,并且设置不支持经典蓝牙
//相比于一般可发现模式的广播,有限可发现模式的广播平率更快,但是只能最多维持 //30s
uint8_t flags = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
ble_uuid_tadv_uuids[] = {{BLE_UUID_NUS_SERVICE, m_nus.uuid_type}};
//appearance为”外观”,他就是一个整形值,代表设备是一个手环,手机什么的。
memset(&advdata, 0, sizeof(advdata));
advdata.name_type = BLE_ADVDATA_FULL_NAME;
advdata.include_appearance = false;
advdata.flags.size = sizeof(flags);
advdata.flags.p_data = &flags;
//这里设置的是扫描响应数据。该数据在设备收到扫描请求的时候才会发出去。
//有时候需要广播的数据可能太多,广播包中放不下,那么就可以放在扫描响应
//数据中,这样对端设备便可以通过扫描请求来或得剩下的数据。
memset(&scanrsp, 0, sizeof(scanrsp));
scanrsp.uuids_complete.uuid_cnt = sizeof(adv_uuids) / sizeof(adv_uuids[0]);
scanrsp.uuids_complete.p_uuids =adv_uuids;
err_code = ble_advdata_set(&advdata,& scanrsp);
9 conn_params_init
static void conn_params_init(void)
ble_conn_params_init_tcp_init;
memset(&cp_init, 0, sizeof(cp_init));
//这里连接参数设置为NULL的原因是前面的gap_params_init函数中已经设置了连接 //参数并调用了sd_ble_gap_ppcp_set将参数设置到了协议栈中。所以这里既是不设置,
//下面的ble_conn_params_init会自动判断是否为空,为空就调用提取函数,从协议栈
//下面主要是设置一些连接参数更新的事件,以及更新周期和最大最大尝试更新次数。
cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
cp_init.next_conn_params_update_delay = NEXT_CONN_PARAMS_UPDATE_DELAY;
cp_init.max_conn_params_update_count = MAX_CONN_PARAMS_UPDATE_COUNT;
cp_init.start_on_notify_cccd_handle = BLE_GATT_HANDLE_INVALID;
cp_init.disconnect_on_fail = false;
cp_init.evt_handler = on_conn_params_evt;
cp_init.error_handler = conn_params_error_handler;
err_code =ble_conn_params_init(&cp_init);
10 sec_params_init
超时时间:比如配对过程中某一步的确认超过这个时间还未收到那么便是超时。APP会收到SD上抛的状态事件,状态为超时
Bond: 是否绑定。如果需要绑定,配对过程会有第三步的秘钥分发,然后app将秘钥存储在falsh这样下次就可以避免了下次重复配对的过程。
:当使能了MITM 并且两端设备一个有键盘,一个有显示屏时,配对过程中就会显示一个配对码,对端设备通过键盘再输入。
OOB:与MITM类似,只是配对码不是通过键盘输入而是通过两端设备别的通信通道传输,比如NFC,当然前提是该通信链路是安全的。不如也没必要绕个弯而不直接用BLE来传输了。
后面就是设置加密秘钥的最大和最小值。加密秘钥的大小在7-16字节之间
m_sec_params.timeout = SEC_PARAM_TIMEOUT;
m_sec_params.bond = SEC_PARAM_BOND;
m_sec_params.mitm = SEC_PARAM_MITM;
m_sec_params.io_caps = SEC_PARAM_IO_CAPABILITIES;
m_sec_params.oob = SEC_PARAM_OOB;
m_sec_params.min_key_size = SEC_PARAM_MIN_KEY_SIZE;
m_sec_params.max_key_size = SEC_PARAM_MAX_KEY_SIZE;
11 advertising_start
static void advertising_start(void)
ble_gap_adv_params_t adv_params;
memset(&adv_params, 0, sizeof(adv_params));
定向广播:用来快速建立和目标设备建立连接。报文中包含自己以及目标地址。
adv_params.type = BLE_GAP_ADV_TYPE_ADV_IND;
adv_params.p_peer_addr = NULL;
//可设置为是否过滤掉非白名单中的扫描请以及非白名单中的连接请求或者两者都过滤。
adv_params.fp = BLE_GAP_ADV_FP_ANY;
//设置广播间隔和广播超时,超时时间到期如果设备还未连接那么app会收到协议栈上
//抛的广播超时时间。App可以做自己想做的处理,比如让设备进入睡眠。
adv_params.interval = APP_ADV_INTERVAL;
adv_params.timeout = APP_ADV_TIMEOUT_IN_SECONDS;
err_code = sd_ble_gap_adv_start(&adv_params);
nrf_gpio_pin_set(ADVERTISING_LED_PIN_NO);
BLE-NRF51822教程2-工程初始化流程相关推荐
- cocos creator麻将教程系列(四)—— 达达麻将客户端初始化流程
达达麻将客户端初始化流程 达达麻将版图 客户端代码结构 1: scripts文件夹下: (1) 3rdparty: 第三方代码 socket-io; (2) Comonents: 游戏中挂到节点上的组 ...
- Direct3D 12入门教程之 ---- Direct3D 12初始化流程
注:以下内容参考自 书籍:<DirectX 12 3D>游戏开发实战, 微软官方的 DirectX样例程序:DirectX-Graphics-Samples, 参见github链接:htt ...
- SpringMVC源码剖析(三)- DispatcherServlet的初始化流程
我们启动web服务器,在浏览器中输入地址,就可以看到浏览器上输出我们写好的页面.为了更好的理解上面这个过程,你需要学习关于Servlet生命周期的三个阶段,就是所谓的"init-servic ...
- OSAL初始化流程分析
我使用的协议栈版本及例子信息: ZigBee2006\Texas Instruments\ZStack-1.4.3-1.2.1\Projects\zstack\Samples\SampleApp ...
- jpcsp源码解读之二:main函数与jpcsp的初始化流程
虽然这个软件是用java语言编写,面向对象,可是总要有个开始的入口,这里关心的就是,main函数在哪里. 似乎java中也可以没有main函数,也可能是我的错误认识.暂且不管,jpcsp中是有main ...
- 蓝牙BLE设备主机重启回连流程分析
本文出自:<蓝牙BLE设备主机重启回连流程分析> 如果一个BLE设备已经与蓝牙中心设备连接上,那么当中心设备的断电重启,其依然会和配对过的BLE设备连接上,而不需要重新走配对的流程,这个过 ...
- 安卓连接ble蓝牙设备教程(目录)
安卓连接ble蓝牙设备教程(目录) 零.新建android工程(安卓蓝牙ble教程) 一.权限和build.gradle配置并开启蓝牙(安卓蓝牙ble教程) 二.搜索蓝牙并连接(安卓蓝牙ble教程) ...
- 【blender教程】从头到尾全流程创建一辆吉普车
[blender教程]从头到尾全流程创建一辆吉普车 持续时间41小时 30分 包括项目文件 1280X720 MP4 语言:英语+中文字幕(根据原英文字幕机译更准确)+原英文字幕 大小解压后:34.7 ...
- Linux内核网络栈1.2.13-网卡设备的初始化流程
参考资料 <<linux内核网络栈源代码情景分析>> 网卡设备的初始化 本文主要描述一下网卡设备的整个初始化的过程,该过程主要就是根据设备的硬件信息来获取与传输网络数据,注册相 ...
最新文章
- 太绝了,赠你34张网络知识架构思维导图
- 【GAN优化】GAN优化专栏栏主小米粥自述,脚踏实地,莫问前程
- Topshelf创建Windows服务
- mongod启动问题
- Python中字典的增、删、查
- Git Pull Failed:Could not read from remote repository
- Android之——AsyncTask和Handler对照
- log4j.xml示例_log4j.xml示例配置
- kuangbin最短路 模板
- 毫米和像素怎么换算_自己计算出来的关于像素和厘米单位的换算
- Spring入门学习
- 「笔耕不辍」MQ的原理以及持久化
- everedit 格式化json_Visual studio code (VS code)
- java创建短信平台_Java通过SMS短信平台实现发短信功能
- ajax 提交间隔,jQuery+Ajax实现限制查询间隔的方法
- 老闪创业那些事儿(58)——C轮融资变身超级独角兽
- 推荐系统中的pointwise和pairwise区别
- 【一家之言】如果一家SaaS公司越来越像软件公司,那离失败就不远了
- 51单片机OLED收银电子秤称重计价清零去皮金额累计HX711
- 算法与数据结构 --- 串,数组和广义表 --- 串
热门文章
- 此图形驱动程序无法找到兼容的图形硬件的解决方案(复制粘贴源于百度http://jingyan.baidu.com/article...
- java中long类型的空值怎么表示,【关于long类型的转换】传进来的是String类型是或null或0如何转成long类型...
- Black-Scholes 期权定价公式的来龙去脉
- 5G手机开打价格战,4G手机将被加速淘汰
- 为什么需要WhatsApp多账号批量管理,使用SendWS做WhatsApp账号多开,云控批量管理的功能介绍
- Windows下Python安装并为pip配置阿里镜像
- 深圳市基层就业补贴申请
- 由配置Rabbitmq多virtual_host失效认识@Qualifier
- 华为matex鸿蒙,华为MateX推迟至9月,或预装鸿蒙系统,价格是唯一败笔
- bzoj4444: [Scoi2015]国旗计划(线段树+倍增)