nrf52840蓝⽛协议栈样例分析
蓝⽛SDK的example ⽂件夹提供了开发BLE的模板⼯程,它具有通⽤性,可以为⾃⼰开发⼯程提供参考。打开
examples\ble_peripheral\ble_app_template⽂件夹下的main.c⽂件,主函数main的内容为:
/**@brief Function for application main entry.
*/
int main(void)
{
bool era_bonds;
// Initialize.
log_init();
timers_init();
buttons_leds_init(&era_bonds);
power_management_init();
ble_stack_init();
gap_params_init();
gatt_init();
advertising_init();
rvices_init();
conn_params_init();
peer_manager_init();
// Start execution.
NRF_LOG_INFO("Template example started.");
application_timers_start();
advertising_start(era_bonds);
// Enter main loop.
for(;;)
{
idle_state_handle();
}
}
main函数的初始化部分主要分为三部分:
⼀是外设的初始化如定时器,按键,LED灯,串⼝等
⼆是协议层的初始化,如协议栈初始化,GAP和GATT初始化,⼴播初始化,连接参数初始化等。
三是应⽤层初始化,如服务初始化,蓝⽛服务初始化。
⼀、外设
1.1、log打印初始化
log_init()函数就是打印初始化函数,内容如下:
/**@brief Function for initializing the nrf log module.
*/
static void log_init(void)
{
ret_code_t err_code =NRF_LOG_INIT(NULL);
APP_ERROR_CHECK(err_code);
NRF_LOG_DEFAULT_BACKENDS_INIT();
}
1.2、定时器
蓝⽛协议栈下的定时器是软件定时器,并采⽤软件中断来进⾏触发操作。Nordic官⽅库关于定时器的驱动库定义在app_timer.c和app_timer.h⽂件中
1.2.1、定时器初始化
main函数中的 timers_init()是定时器初始化函数
/**@brief Function for the Timer initialization.
*
* @details Initializes the timer module. This creates and starts application timers.
*/
static void timers_init(void)
{
// Initialize timer module.
ret_code_t err_code =app_timer_init();
APP_ERROR_CHECK(err_code);
// Create timers.
/* YOUR_JOB: Create any timers to be ud by the application.
Below is an example of how to create a timer.
For every new timer needed, increa the value of the macro APP_TIMER_MAX_TIMERS by
one.
ret_code_t err_code;
err_code = app_timer_create(&m_app_timer_id, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
APP_ERROR_CHECK(err_code); */
}
调⽤的API函数有app_timer_init()和app_timer_create()。app_timer_init()函数就是初始化计数器的相关⼯作。app_timer_create()函数为
ret_code_t app_timer_create(app_timer_id_t const* p_timer_id,
app_timer_mode_t mode,
app_timer_timeout_handler_t timeout_handler);
p_timer_id是定时器标识符指针
mode是定时器模式
timeout_handler是定时器超时溢出的中断 处理函数
1.2.2、定时器开始
main函数中的定时器开始函数为application_timers_start()
/**@brief Function for starting timers.
*/
static void application_timers_start(void)
{
/* YOUR_JOB: Start your timers. below is an example of how to start a timer.
ret_code_t err_code;
err_code = app_timer_start(m_app_timer_id, TIMER_INTERVAL, NULL);
APP_ERROR_CHECK(err_code); */
}
app_timer_start函数是定时器开始函数,其参数具体为:
ret_code_t app_timer_start(app_timer_t * p_timer, uint32_t timeout_ticks,void* p_context)
第⼀个参数p_timer为前⾯的定时器ID
第⼆个参数timeout_ticks是超时的时间间隔
第三个参数为p_context,⼀般可以设置为NULL。
1.3、LED和按键
Nordic的蓝⽛协议栈中的库函数提供了⼀个BSP来⽀持按键和LED灯。
1.3.1、按键与LED的初始化
按键与LED的初始化函数为
/**@brief Function for initializing buttons and leds.
*
* @param[out] p_era_bonds Will be true if the clear bonding button was presd to wake the application up. */
static void buttons_leds_init(bool * p_era_bonds)
{
ret_code_t err_code;
bsp_event_t startup_event;
err_code =bsp_init(BSP_INIT_LEDS | BSP_INIT_BUTTONS, bsp_event_handler);
APP_ERROR_CHECK(err_code);
err_code =bsp_btn_ble_init(NULL,&startup_event);
APP_ERROR_CHECK(err_code);
*p_era_bonds =(startup_event == BSP_EVENT_CLEAR_BONDING_DATA);
平面设计实习报告
}
bsp_init函数实现按键和LED的初始化,同时触发对应的设备任务。
⼆、能源管理
能源管理初始化函数为power_management_init(),⼀般不变动。
三、协议栈
ble_stack_init()是协议初始化函数,协议栈初始化主要做以下⼯作:
1、协议栈回复使能应答,主要⼯作是协议栈时钟初始化配置
2、初始化协议栈,设置协议栈相关处理函数,
3、使能协议栈
4、注册蓝⽛处理调度事件
具体代码如下:
/**@brief Function for initializing the BLE stack.
*
* @details Initializes the SoftDevice and the BLE event interrupt.
*/
static void ble_stack_init(void)
{
ret_code_t err_code;
err_code =nrf_sdh_enable_request();
APP_ERROR_CHECK(err_code);
// Configure the BLE stack using the default ttings.
// Fetch the start address of the application RAM.
uint32_t ram_start =0;
err_code =nrf_sdh_ble_default_cfg_t(APP_BLE_CONN_CFG_TAG,&ram_start);
APP_ERROR_CHECK(err_code);
// Enable BLE stack.
err_code =nrf_sdh_ble_enable(&ram_start);
APP_ERROR_CHECK(err_code);
// Register a handler for BLE events.
NRF_SDH_BLE_OBSERVER(m_ble_obrver, APP_BLE_OBSERVER_PRIO, ble_evt_handler,NULL);
}
3.1、协议栈使能应答
协议栈使能应答代码为
ret_code_t nrf_sdh_enable_request(void)
{
ret_code_t ret_code;
if(m_nrf_sdh_enabled)
{
return NRF_ERROR_INVALID_STATE;
}
m_nrf_sdh_continue = true;
// Notify obrvers about SoftDevice enable request.
if(sdh_request_obrver_notify(NRF_SDH_EVT_ENABLE_REQUEST)== NRF_ERROR_BUSY)
{
// Enable process was stopped.
return NRF_SUCCESS;
}
// Notify obrvers about starting SoftDevice enable process.
sdh_state_obrver_notify(NRF_SDH_EVT_STATE_ENABLE_PREPARE);
nrf_clock_lf_cfg_t const clock_lf_cfg =
{
.source = NRF_SDH_CLOCK_LF_SRC,
.rc_ctiv = NRF_SDH_CLOCK_LF_RC_CTIV,
.rc_temp_ctiv = NRF_SDH_CLOCK_LF_RC_TEMP_CTIV,
.accuracy = NRF_SDH_CLOCK_LF_ACCURACY
文章博客};
CRITICAL_REGION_ENTER();
#ifdef ANT_LICENSE_KEY
ret_code =sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler, ANT_LICENSE_KEY);
#el
ret_code =sd_softdevice_enable(&clock_lf_cfg, app_error_fault_handler);
#endif
m_nrf_sdh_enabled =(ret_code == NRF_SUCCESS);
CRITICAL_REGION_EXIT();
if(ret_code != NRF_SUCCESS)
{
return ret_code;
}
小气鬼m_nrf_sdh_continue = fal;
m_nrf_sdh_suspended = fal;
/
/ Enable event interrupt.
// Interrupt priority has already been t by the stack.
softdevices_evt_irq_enable();
// Notify obrvers about a finished SoftDevice enable process.
sdh_state_obrver_notify(NRF_SDH_EVT_STATE_ENABLED);
return NRF_SUCCESS;
}
这个函数是⽤于观察者初始化协议栈是否开始使能。观察者就是开发者,代码中设置了交互⽅式,如串⼝等对设备状态进⾏观察,并且给观察者分配了回调函数
3.2、协议栈默认配置设置
协议栈默认配置设置,也是协议栈的初始化。函数代码如下:
ret_code_t nrf_sdh_ble_default_cfg_t(uint8_t conn_cfg_tag, uint32_t * p_ram_start)
{
uint32_t ret_code;
ret_code =nrf_sdh_ble_app_ram_start_get(p_ram_start);
if(ret_code != NRF_SUCCESS)
{
return ret_code;
}
#if defined(S112)||defined(S312)
STATIC_ASSERT(NRF_SDH_BLE_CENTRAL_LINK_COUNT ==0,"When using s112, NRF_SDH_BLE_CENTRAL_LINK_COUNT must be 0."); #endif
// Overwrite some of the default ttings of the BLE stack.
// If any of the calls to sd_ble_cfg_t() fail, log the error but carry on so that
// wrong RAM ttings can be caught by nrf_sdh_ble_enable() and a meaningful error
// message will be printed to the ur suggesting the correct value.
ble_cfg_t ble_cfg;
/*********************************设置连接数⽬和⾓⾊****************************************************************/
#if(NRF_SDH_BLE_TOTAL_LINK_COUNT != 0)
// Configure the connection count.设置连接数⽬
memt(&ble_cfg,0,sizeof(ble_cfg));
__cfg_tag = conn_cfg_tag;//设置标号
_cfg.params.gap__count = NRF_SDH_BLE_TOTAL_LINK_COUNT;//总的连接数量
练字用什么笔最好_cfg.params.gap_conn_cfg.event_length = NRF_SDH_BLE_GAP_EVENT_LENGTH;//
GAP事件长度
ret_code =sd_ble_cfg_t(BLE_CONN_CFG_GAP,&ble_cfg,*p_ram_start);//添加协议栈配置
if(ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_t() returned %s when attempting to t BLE_CONN_CFG_GAP.",
nrf_strerror_get(ret_code));
}
// Configure the connection roles.配置连接⾓⾊
memt(&ble_cfg,0,sizeof(ble_cfg));
#if!defined(S122)
ble_cfg.le_count_cfg.periph_role_count = NRF_SDH_BLE_PERIPHERAL_LINK_COUNT;//从机⾓⾊数⽬
#endif// !defined (S122)
#if!defined(S112)&&!defined(S312)&&!defined(S113)
ble_cfg.le_al_role_count = NRF_SDH_BLE_CENTRAL_LINK_COUNT;//主机⾓⾊数⽬
ble_cfg.le_al_c_count =MIN(NRF_SDH_BLE_CENTRAL_LINK_COUNT,
BLE_GAP_ROLE_COUNT_CENTRAL_SEC_DEFAULT);
#endif// !defined (S112) && !defined(S312) && !defined(S113)
清风阁我爱黄
ret_code =sd_ble_cfg_t(BLE_GAP_CFG_ROLE_COUNT,&ble_cfg,*p_ram_start);
if(ret_code != NRF_SUCCESS)
岗位等级工资制
{
茄汁鸡翅NRF_LOG_ERROR("sd_ble_cfg_t() returned %s when attempting to t BLE_GAP_CFG_ROLE_COUNT.",
nrf_strerror_get(ret_code));
}
/*********************************设置MTU协商值****************************************************************/
// Configure the maximum ATT MTU.
#if(NRF_SDH_BLE_GATT_MAX_MTU_SIZE != 23)//如果MTU协商值不是23
memt(&ble_cfg,0x00,sizeof(ble_cfg));
__cfg_tag = conn_cfg_tag;//设置连接编号
_cfg.params.gatt_conn_cfg.att_mtu = NRF_SDH_BLE_GATT_MAX_MTU_SIZE;//设置
MTU字节长度
酮戊二酸ret_code =sd_ble_cfg_t(BLE_CONN_CFG_GATT,&ble_cfg,*p_ram_start);//协议栈配置的添加
if(ret_code != NRF_SUCCESS)
{
NRF_LOG_ERROR("sd_ble_cfg_t() returned %s when attempting to t BLE_CONN_CFG_GATT.",
nrf_strerror_get(ret_code));
}