NRF51822实例代码说明
2015-11-10 14:02
375 查看
说在前面:此说明用于nrf51822的主从机的实例代码解析,通过讲解主从机的双向通讯来说明如何添加自己的服务及特征。此说明为个人理解,可能与原意不同。
目录
主机模块
从main说起
app_trace_init()
ble_stack_init()
client_handling_init()
device_manager_init
scan_start
从机模块
从main说起
gap_params_init()
advertising_init()
services_init()
advertising_start()
以串口的收发为例
从机端
主机端
主机模块从main说起:
以\nrf51822\Board\nrf6310\s120\experimental\ble_app_multilink_central为例。工作流程(从main看起):初始化了参数之后,开启扫描,然后就进入循环。其中led,buttons的内容非开发所需,此处忽略。此外,需要说明的是,所有的sd_开头的函数均为nrf51822系统函数,具体的使用可以参考官网sdk的文档说明。
--------------------------------main--------------------------------------------------------------------------------------
接下来,具体看看各函数
app_trace_init()初始化追踪应用,既初始化串口。
具体就一条函数,设置相应的引脚。
simple_uart_config(RTS_PIN_NUMBER,TX_PIN_NUMBER,CTS_PIN_NUMBER,RX_PIN_NUMBER,HWFC);
如果需要在串口设置输入中断,需要添加如下代码:
NRF_UART0->INTENSET= UART_INTENSET_RXDRDY_Enabled << UART_INTENSET_RXDRDY_Pos;
NVIC_SetPriority(UART0_IRQn,APP_IRQ_PRIORITY_LOW);// APP_IRQ_PRIORITY_HIGH
NVIC_EnableIRQ(UART0_IRQn);
并在main函数中添加中断处理函数的实现:void UART0_IRQHandler(void){}
注意:名字必须是这个。这个底层系统调用时已经定义好名字了。无须申明,只需要具体实现即可。
值得注意的是simple_uart_config具体函数实现中有可以设置串口波特率,所有的波特率均有宏定义,只需直接更改宏即可。
NRF_UART0->BAUDRATE=(UART_BAUDRATE_BAUDRATE_Baud38400<<UART_BAUDRATE_BAUDRATE_Pos);
ble_stack_init(); 蓝牙协议栈的初始化
--------------------------------ble_stack_init()-------------------------------------------------------------------------
根据注释可以知道大概每个函数是做什么的。
2.1)SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
初始化SD处理模块,用于初始化时钟,内存等信息。需要关注的是
NRF_CLOCK_LFCLKSRC_XTAL_20_PPM这是时钟参数,跟设置波特率一样,换相应的宏即可。
2.2) softdevice_ble_evt_handler_set(ble_evt_dispatch);
设置蓝牙事件处理,后面细讲。
2.3)softdevice_sys_evt_handler_set(sys_evt_dispatch);
设置系统事件处理。
接下来,看下ble_evt_dispatch具体的内容:
2.2.1) dm_ble_evt_handler(p_ble_evt);<----模块管理
2.2.2) client_handling_ble_evt_handler(p_ble_evt);<---注册的服务处理
2.2.3) on_ble_evt(p_ble_evt);<--蓝牙事件处理
大概的流程基本一致,就是事件处理,只是不同函数关注和处理的数据不同。以on_ble_evt(p_ble_evt)为例。
--------------------------------on_ble_evt(p_ble_evt)-----------------------------------------------------------------
就是一个switch事件,根据不同的事件类型处理做不同的事情。
如:BLE_GAP_EVT_ADV_REPORT
只要有设备进行广播,并且广播是可见的,而自身是扫描状态的话,就会进入这个事件。具体是将接收到的数据进行匹配,一旦匹配成功就关闭扫描并发送连接请求。其中,需要关注的是:BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME这个宏。这是主从设备广播的数据匹配类型,在主从两端都需要进行设置。
此处再列举几个常见的宏定义,并加以大致的解释。(详细可以参考协议栈初始化中的其他函数。)
BLE_GAP_EVT_TIMEOUT:超时处理。
BLE_GATTC_EVT_WRITE_RSP:写请求回复。每当主机发送一次数据给从机,从机会回一个写事件的回复,当主机收到写事件的回复之后才能再次发送数据。
BLE_GATTC_EVT_HVX:收到数据。这个事件表示从机发送了数据到主机。
BLE_GAP_EVT_CONNECTED:连接成功。
BLE_GAP_EVT_DISCONNECTED:断开连接。
BLE_GAP_EVT_SEC_PARAMS_REQUEST:从机请求连接参数
BLE_GAP_EVT_CONN_SEC_UPDATE:从机请求更改连接参数
client_handling_init() 初始化客户端处理
--------------------------------client_handling_init()-----------------------------------------------------------------
这个函数用于注册服务以及特征,以及服务的处理。
其中sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type);用于16bit注册服务。
SIG规定了一套标准的uuid的规则,具体的uuid的规则和类型可以参考实例代码。
uuid.uuid= MULTILINK_PERIPHERAL_SERVICE_UUID;添加4bit服务,相当于起别名。
ble_db_discovery_register(&uuid,db_discovery_evt_handler);注册服务发现处理函数。
device_manager_init 设备管理处理化
这个函数用于初始化一些设备参数,如(扫描时间,扫描间隔,接收数据的大小等);不需要手动更改,相应的参数修改请在main中的宏定义中修改。
scan_start 启动扫描
这函数用于将前一个函数初始化的数据填入扫描函数中,并开始扫描,不需要手动更改。
至此,全部准备工作完毕,接来下主机就会进入循环的监听模式,当有相应的事件到来就会自动调用协议栈中相应的模块进行调度。
从机模块从main说起:
以\nrf51822\Board\nrf6310\s120\experimental\ble_app_multilink_peripheral为例。与主机的工作流程类似(从main看起):初始化了参数之后,开启广播,然后就进入循环。只是从机需要初始化的东西有部分与主机会有些不同,其中led,buttons,gpio的内容非开发所需,此处忽略。此外,由于主从设备是配合使用的。ble_stack_init(); device_manager_init();这两个的初始化与主机的初始化基本一致,只是相应的处理方式和参数名定义会不同。需要查看差异的话,可以对比查看。(比如,从机会有广播包的事情处理,而主机则没有。)这里只对其他函数进行说明。
--------------------------------main-------------------------------------------------------------------------
接下来,具体看看各函数
gap_params_init();这个函数用于初始化所有的需要的GAP参数,如(设备名称,连接时间,超时时间等)。同样的,这个函数不需要修改,相应的参数需要在宏定义中修改。
advertising_init();这个函数用于初始化广播包。需要注意的有如下参数:
Flag:设备广播的可见范围,如有限可见,常可见等设置,详见宏定义的说明。
advdata.name_type:广播包中包含的设备名称类型,与主机端配套设置。如全名称,短名称,和不显示名称。
services_init();这个函数用于初始化服务,与主机端的client_handing对应。
-------------------------------- services_init()-----------------------------------------------------------
需要注意如何添加一个服务,特征。
首先,添加一个服务到服务表中。这于主机的是一样的。
sd_ble_uuid_vs_add(&base_uuid,&m_base_uuid_type);
其次,将该服务设置为主服务列表当中。
sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,&uuid, &svc_test);
再者,设置服务之后,需要在这个服务中添加相应的特征。
sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID,
&char_md,
&attr,
&m_char_handles);
需要说明的是,无论是服务,还是特征都有自己对应的属性,在注册之前都需要设置其对应的属性,这点在实例代码中已经有了很好的模板。有需要添加自己的服务或者特征都可以通过这个模板进行修改。
advertising_start();开始广播,这与主机的开始扫描是相对应的。设置广播类型,时间,超时时间等参数后开始广播。同样的,这个函数也不需要修改,修改的部分都在宏定义处修改。
至此,全部准备工作完毕,接来下从机就会进入循环的广播模式,当有相应的事件到来就会自动调用协议栈中相应的模块进行调度。
以串口的收发为例接下来,将以串口的收发为例。讲述具体数据的发送和接收。
Nrf51822的实例代码优点就是所有的函数均是模块化的。因此可以不加修改整个拷贝即可使用。
从机端:从机端的代码需要参考\Board\pca10001\s110\experimental\ble_app_uart的代码。
[align=left]1) 调通串口。[/align]
发现从机端是没有设置串口代码的。因此需要先在从机端添加相应的串口初始化代码。这里需要用到的uart_init()代码。直接复制即可。
[align=left]2) 串口中断函数[/align]
直接复制串口中断处理函数的代码void UART0_IRQHandler(void)。
看下中断处理函数做了些什么事情
-------------------------------- voidUART0_IRQHandler(void)-----------------------------------------
-------------------------------- voidUART0_IRQHandler(void)---- -------------------------------------
接收串口中的消息,当遇到回车或者字符数够了之后,将数据发送出去。
ble_nus_send_string(&m_nus,data_array, index + 1);这条语句中的m_nus的参数就是数据发送的句柄。会发现根本就没有对这个参数进行任何的设置,数据是发不出去的。因此要对这个参数进行相应的处理。这里就需要用到services_init()
[align=left]3) services_init()[/align]
看下参考代码和从机的代码中services_init()的差异,会发现参考代码对该函数又进行了进一步的封装。其中就有对m_nus的操作。同样的。整个复制过来。(包括内部实现的函数)
这里需要记住注册了的服务的uuid和BLE_UUID_NUS_SERVICE。
同时注册的特征id:BLE_UUID_NUS_TX_CHARACTERISTIC,
BLE_UUID_NUS_RX_CHARACTERISTIC也需要记住。
这样发送的部分就已经做完了。
那么接收函数呢?
其实也已经做好了。在复制services_init()函数时, nus_init.data_handler = nus_data_handler;
把接收数据的处理函数也已经添加进来了。这里只是把数据通过串口打印出来而已。并未做其他操作。
主机端:主机端的代码需要参考\Board\nrf6310\s120\experimental\ble_app_hrs_c的代码
串口中断函数。同样的。将参考代码的串口中断函数复制过来。同样是缺乏m_nus这个发送的句柄,因此这里也同样需要添加服务。此外,由于主从机发送数据所调用的函数不同。这里还需要修改ble_nus_send_string(&m_nus,data_array, index + 1)这个函数。修改后的内容如下,(参考\Board\nrf6310\s120\experimental\ble_app_uart_c中的tx_send()的代码。)
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
[align=left]1) 参考hrs_c_init()函数添加新的服务。[/align]
-------------------------------- hrs_c_init()-----------------------------------------
-------------------------------- hrs_c_init()-----------------------------------------
修改部分:
1.(建议,非必须)将所有hrs相关的单词(包括文件名,宏定义,函数名,变量名等)改掉。
2. hrs_c_evt_handler这个函数是数据接收的实现函数。
3. ble_hrs_c_init(&m_ble_hrs_c, &hrs_c_init_obj)中m_ble_hrs_c这个参数的名称必须与串口中断中的m_nus这个参数必须是同一参数,具体起什么名字自己定一个全局变量即可。
4.修改ble_hrs_c_init()
---------------------------------- ble_hrs_c_init()------------------------------------------------
-------------------------------- ble_hrs_c_init()-----------------------------------------
会发现,这里并没有添加服务到服务表中。这里因为心率服务在SIG中已经定义好了。无需用户再进行添加。只需要根据需要再添加特征即可。
这里需要添加一个服务uuid。添加或修改如下代码:
需要注意的是,uuid必须与从机的uuid一致。BLE_UUID_NUS_SERVICE与从机的一致。这样才能确保主机能找到从机的服务。
再修改db_discover_evt_handler服务发现处理函数。
--------------------------------db_discover_evt_handler-----------------------------------------
--------------------------------db_discover_evt_handler-----------------------------------------
这里是发现服务后的处理函数。先确保找到的是正确的服务。然后将服务里的特征与主机定义的特征进行匹配,匹配成功后获取相应的参数值。具体修改如下:
将BLE_UUID_HEART_RATE_SERVICE换成BLE_UUID_NUS_SERVICE
将BLE_UUID_TYPE_BLE换成m_base_uuid_type
将BLE_UUID_HEART_RATE_MEASUREMENT_CHAR换成BLE_UUID_NUS_TX_CHARACTERISTIC,并仿照这块代码,添加一个BLE_UUID_NUS_RX_CHARACTERISTIC的特征发现。
最后在蓝牙协议栈的初始化中,添加服务的处理事件。这里可以参考串口服务ble_nus_on_ble_evt中的代码。
--------------------------------ble_nus_on_ble_evt-----------------------------------------
当有通知到来时,将会触发BLE_GATTS_EVT_WRITE,可以在这里将收到的数据通过串口打印出来。
至此,nrf51822的实例代码解析,以及如何添加自己的一个服务及特征,主从机的双向通讯全部解释完毕。当中的参数定义并没有加以补充。复制不同模块的函数时,要同时将参数、申明、宏定义等相关内容也一块复制过来。当然复制代码并不是直接复制,黏贴。而是在理解后参考着原代码修改成为自己的内容。
目录
主机模块
从main说起
app_trace_init()
ble_stack_init()
client_handling_init()
device_manager_init
scan_start
从机模块
从main说起
gap_params_init()
advertising_init()
services_init()
advertising_start()
以串口的收发为例
从机端
主机端
主机模块从main说起:
以\nrf51822\Board\nrf6310\s120\experimental\ble_app_multilink_central为例。工作流程(从main看起):初始化了参数之后,开启扫描,然后就进入循环。其中led,buttons的内容非开发所需,此处忽略。此外,需要说明的是,所有的sd_开头的函数均为nrf51822系统函数,具体的使用可以参考官网sdk的文档说明。
--------------------------------main--------------------------------------------------------------------------------------
int main(void) { //Initialization of various modules. app_trace_init(); leds_init(); buttons_init(); ble_stack_init(); client_handling_init(); device_manager_init(); // Startscanning for devices. scan_start(); for (;;) { power_manage(); } }--------------------------------main-------------------------------------------------------------------------------------
接下来,具体看看各函数
app_trace_init()初始化追踪应用,既初始化串口。
具体就一条函数,设置相应的引脚。
simple_uart_config(RTS_PIN_NUMBER,TX_PIN_NUMBER,CTS_PIN_NUMBER,RX_PIN_NUMBER,HWFC);
如果需要在串口设置输入中断,需要添加如下代码:
NRF_UART0->INTENSET= UART_INTENSET_RXDRDY_Enabled << UART_INTENSET_RXDRDY_Pos;
NVIC_SetPriority(UART0_IRQn,APP_IRQ_PRIORITY_LOW);// APP_IRQ_PRIORITY_HIGH
NVIC_EnableIRQ(UART0_IRQn);
并在main函数中添加中断处理函数的实现:void UART0_IRQHandler(void){}
注意:名字必须是这个。这个底层系统调用时已经定义好名字了。无须申明,只需要具体实现即可。
值得注意的是simple_uart_config具体函数实现中有可以设置串口波特率,所有的波特率均有宏定义,只需直接更改宏即可。
NRF_UART0->BAUDRATE=(UART_BAUDRATE_BAUDRATE_Baud38400<<UART_BAUDRATE_BAUDRATE_Pos);
ble_stack_init(); 蓝牙协议栈的初始化
--------------------------------ble_stack_init()-------------------------------------------------------------------------
uint32_t err_code; // Initialize the SoftDevicehandler module. SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM,false); // Register with theSoftDevice handler module for BLE events. err_code =softdevice_ble_evt_handler_set(ble_evt_dispatch); APP_ERROR_CHECK(err_code); // Register with theSoftDevice handler module for System events. err_code =softdevice_sys_evt_handler_set(sys_evt_dispatch); APP_ERROR_CHECK(err_code);--------------------------------ble_stack_init()-------------------------------------------------------------------------
根据注释可以知道大概每个函数是做什么的。
2.1)SOFTDEVICE_HANDLER_INIT(NRF_CLOCK_LFCLKSRC_XTAL_20_PPM, false);
初始化SD处理模块,用于初始化时钟,内存等信息。需要关注的是
NRF_CLOCK_LFCLKSRC_XTAL_20_PPM这是时钟参数,跟设置波特率一样,换相应的宏即可。
2.2) softdevice_ble_evt_handler_set(ble_evt_dispatch);
设置蓝牙事件处理,后面细讲。
2.3)softdevice_sys_evt_handler_set(sys_evt_dispatch);
设置系统事件处理。
接下来,看下ble_evt_dispatch具体的内容:
2.2.1) dm_ble_evt_handler(p_ble_evt);<----模块管理
2.2.2) client_handling_ble_evt_handler(p_ble_evt);<---注册的服务处理
2.2.3) on_ble_evt(p_ble_evt);<--蓝牙事件处理
大概的流程基本一致,就是事件处理,只是不同函数关注和处理的数据不同。以on_ble_evt(p_ble_evt)为例。
--------------------------------on_ble_evt(p_ble_evt)-----------------------------------------------------------------
switch (p_ble_evt->header.evt_id) { case BLE_GAP_EVT_ADV_REPORT: { data_t adv_data; data_t type_data; // Initializeadvertisement report for parsing. adv_data.p_data =p_ble_evt->evt.gap_evt.params.adv_report.data; adv_data.data_len =p_ble_evt->evt.gap_evt.params.adv_report.dlen; err_code =adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &type_data); if (err_code != NRF_SUCCESS) { // Compare shortlocal name in case complete name does not match. err_code =adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &type_data); } // Verify if short orcomplete name matches target. if ((err_code ==NRF_SUCCESS) && (0 ==memcmp(TARGET_DEV_NAME,type_data.p_data,type_data.data_len))) { err_code =sd_ble_gap_scan_stop(); if (err_code !=NRF_SUCCESS) { APPL_LOG("[APPL]: Scan stop failed, reason %d\r\n", err_code); } err_code =sd_ble_gap_connect(&p_ble_evt->evt.gap_evt.params.adv_report.\ peer_addr, &m_scan_param, &m_connection_param); if (err_code !=NRF_SUCCESS) { APPL_LOG("[APPL]: Connection Request Failed, reason %d\r\n",err_code); } } break; } case BLE_GAP_EVT_TIMEOUT: if(p_ble_evt->evt.gap_evt.params.timeout.src ==BLE_GAP_TIMEOUT_SRC_SCAN) { APPL_LOG("[APPL]: Scan Timedout.\r\n"); } else if(p_ble_evt->evt.gap_evt.params.timeout.src == BLE_GAP_TIMEOUT_SRC_CONN) { APPL_LOG("[APPL]: Connection Request Timedout.\r\n"); } break; default: break; }--------------------------------on_ble_evt(p_ble_evt)-----------------------------------------------------------------
就是一个switch事件,根据不同的事件类型处理做不同的事情。
如:BLE_GAP_EVT_ADV_REPORT
只要有设备进行广播,并且广播是可见的,而自身是扫描状态的话,就会进入这个事件。具体是将接收到的数据进行匹配,一旦匹配成功就关闭扫描并发送连接请求。其中,需要关注的是:BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME这个宏。这是主从设备广播的数据匹配类型,在主从两端都需要进行设置。
此处再列举几个常见的宏定义,并加以大致的解释。(详细可以参考协议栈初始化中的其他函数。)
BLE_GAP_EVT_TIMEOUT:超时处理。
BLE_GATTC_EVT_WRITE_RSP:写请求回复。每当主机发送一次数据给从机,从机会回一个写事件的回复,当主机收到写事件的回复之后才能再次发送数据。
BLE_GATTC_EVT_HVX:收到数据。这个事件表示从机发送了数据到主机。
BLE_GAP_EVT_CONNECTED:连接成功。
BLE_GAP_EVT_DISCONNECTED:断开连接。
BLE_GAP_EVT_SEC_PARAMS_REQUEST:从机请求连接参数
BLE_GAP_EVT_CONN_SEC_UPDATE:从机请求更改连接参数
client_handling_init() 初始化客户端处理
--------------------------------client_handling_init()-----------------------------------------------------------------
uint32_t err_code; uint32_t i; ble_uuid128_t base_uuid = MULTILINK_PERIPHERAL_BASE_UUID; err_code =sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type); APP_ERROR_CHECK(err_code); nrf_gpio_range_cfg_output(8, 15); for (i = 0; i < MAX_CLIENTS; i++) { m_client.state = IDLE; } m_client_count = 0; db_discovery_init(); // Register with discovery module for thediscovery of the service. ble_uuid_t uuid; uuid.type = m_base_uuid_type; uuid.uuid =MULTILINK_PERIPHERAL_SERVICE_UUID; err_code =ble_db_discovery_register(&uuid, db_discovery_evt_handler); APP_ERROR_CHECK(err_code);--------------------------------client_handling_init()-----------------------------------------------------------------
这个函数用于注册服务以及特征,以及服务的处理。
其中sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type);用于16bit注册服务。
SIG规定了一套标准的uuid的规则,具体的uuid的规则和类型可以参考实例代码。
uuid.uuid= MULTILINK_PERIPHERAL_SERVICE_UUID;添加4bit服务,相当于起别名。
ble_db_discovery_register(&uuid,db_discovery_evt_handler);注册服务发现处理函数。
device_manager_init 设备管理处理化
这个函数用于初始化一些设备参数,如(扫描时间,扫描间隔,接收数据的大小等);不需要手动更改,相应的参数修改请在main中的宏定义中修改。
scan_start 启动扫描
这函数用于将前一个函数初始化的数据填入扫描函数中,并开始扫描,不需要手动更改。
至此,全部准备工作完毕,接来下主机就会进入循环的监听模式,当有相应的事件到来就会自动调用协议栈中相应的模块进行调度。
从机模块从main说起:
以\nrf51822\Board\nrf6310\s120\experimental\ble_app_multilink_peripheral为例。与主机的工作流程类似(从main看起):初始化了参数之后,开启广播,然后就进入循环。只是从机需要初始化的东西有部分与主机会有些不同,其中led,buttons,gpio的内容非开发所需,此处忽略。此外,由于主从设备是配合使用的。ble_stack_init(); device_manager_init();这两个的初始化与主机的初始化基本一致,只是相应的处理方式和参数名定义会不同。需要查看差异的话,可以对比查看。(比如,从机会有广播包的事情处理,而主机则没有。)这里只对其他函数进行说明。
--------------------------------main-------------------------------------------------------------------------
int main(void) { ble_stack_init(); leds_init(); timers_init(); gpiote_init(); buttons_init(); device_manager_init(); gap_params_init(); advertising_init(); services_init(); advertising_start(); for (;;) { power_manage(); }--------------------------------main-------------------------------------------------------------------------
接下来,具体看看各函数
gap_params_init();这个函数用于初始化所有的需要的GAP参数,如(设备名称,连接时间,超时时间等)。同样的,这个函数不需要修改,相应的参数需要在宏定义中修改。
advertising_init();这个函数用于初始化广播包。需要注意的有如下参数:
Flag:设备广播的可见范围,如有限可见,常可见等设置,详见宏定义的说明。
advdata.name_type:广播包中包含的设备名称类型,与主机端配套设置。如全名称,短名称,和不显示名称。
services_init();这个函数用于初始化服务,与主机端的client_handing对应。
-------------------------------- services_init()-----------------------------------------------------------
uint32_t err_code; ble_uuid_t uuid; ble_gatts_char_md_t char_md; ble_gatts_attr_t attr; ble_gatts_attr_md_t attr_md; ble_gatts_attr_md_t cccd_md; ble_gatts_attr_md_t char_ud_md; uint16_t svc_test; static uint8_t multilink_peripheral_data; static uint8_t multilink_peripheral_ud[] ="Modifiable multilink_peripheral Data"; ble_uuid128_t base_uuid =MULTILINK_PERIPHERAL_BASE_UUID; err_code =sd_ble_uuid_vs_add(&base_uuid, &m_base_uuid_type); APP_ERROR_CHECK(err_code); uuid.type = m_base_uuid_type; uuid.uuid = MULTILINK_PERIPHERAL_SERVICE_UUID; err_code =sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &uuid,&svc_test); APP_ERROR_CHECK(err_code); uuid.uuid = MULTILINK_PERIPHERAL_CHAR_UUID; memset(&attr, 0,sizeof(ble_gatts_attr_t)); attr.p_uuid = &uuid; attr.p_attr_md = &attr_md; attr.max_len = 1; attr.p_value = &multilink_peripheral_data; attr.init_len = sizeof(multilink_peripheral_data); memset(&attr_md, 0,sizeof(ble_gatts_attr_md_t)); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&attr_md.write_perm); attr_md.vloc = BLE_GATTS_VLOC_STACK; attr_md.vlen = 0; memset(&cccd_md, 0,sizeof(ble_gatts_attr_md_t)); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_ENC_NO_MITM(&cccd_md.write_perm); cccd_md.vloc = BLE_GATTS_VLOC_STACK; memset(&char_md, 0,sizeof(ble_gatts_char_md_t)); char_md.p_cccd_md = &cccd_md; char_md.char_props.notify = 1; char_md.char_props.indicate = 1; char_md.char_props.read = 1; char_md.char_props.write = 1; char_md.char_ext_props.wr_aux = 1; char_md.p_user_desc_md = &char_ud_md; char_md.p_char_user_desc = multilink_peripheral_ud; char_md.char_user_desc_size = (uint8_t)strlen((char*)multilink_peripheral_ud); char_md.char_user_desc_max_size =(uint8_t)strlen((char *)multilink_peripheral_ud); memset(&char_ud_md, 0,sizeof(ble_gatts_attr_md_t)); char_ud_md.vloc = BLE_GATTS_VLOC_STACK; char_ud_md.vlen = 1; BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_ud_md.read_perm); BLE_GAP_CONN_SEC_MODE_SET_OPEN(&char_ud_md.write_perm); err_code =sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID, &char_md, &attr, &m_char_handles); APP_ERROR_CHECK(err_code);-------------------------------- services_init()-----------------------------------------------------------
需要注意如何添加一个服务,特征。
首先,添加一个服务到服务表中。这于主机的是一样的。
sd_ble_uuid_vs_add(&base_uuid,&m_base_uuid_type);
其次,将该服务设置为主服务列表当中。
sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY,&uuid, &svc_test);
再者,设置服务之后,需要在这个服务中添加相应的特征。
sd_ble_gatts_characteristic_add(BLE_GATT_HANDLE_INVALID,
&char_md,
&attr,
&m_char_handles);
需要说明的是,无论是服务,还是特征都有自己对应的属性,在注册之前都需要设置其对应的属性,这点在实例代码中已经有了很好的模板。有需要添加自己的服务或者特征都可以通过这个模板进行修改。
advertising_start();开始广播,这与主机的开始扫描是相对应的。设置广播类型,时间,超时时间等参数后开始广播。同样的,这个函数也不需要修改,修改的部分都在宏定义处修改。
至此,全部准备工作完毕,接来下从机就会进入循环的广播模式,当有相应的事件到来就会自动调用协议栈中相应的模块进行调度。
以串口的收发为例接下来,将以串口的收发为例。讲述具体数据的发送和接收。
Nrf51822的实例代码优点就是所有的函数均是模块化的。因此可以不加修改整个拷贝即可使用。
从机端:从机端的代码需要参考\Board\pca10001\s110\experimental\ble_app_uart的代码。
[align=left]1) 调通串口。[/align]
发现从机端是没有设置串口代码的。因此需要先在从机端添加相应的串口初始化代码。这里需要用到的uart_init()代码。直接复制即可。
[align=left]2) 串口中断函数[/align]
直接复制串口中断处理函数的代码void UART0_IRQHandler(void)。
看下中断处理函数做了些什么事情
-------------------------------- voidUART0_IRQHandler(void)-----------------------------------------
{ static uint8_tdata_array[BLE_NUS_MAX_DATA_LEN]; static uint8_t index = 0; uint32_t err_code; /**@snippet [Handling the data receivedover UART] */ data_array[index] = simple_uart_get(); index++; if ((data_array[index - 1] == '\n') ||(index >= (BLE_NUS_MAX_DATA_LEN - 1))) { err_code = ble_nus_send_string(&m_nus,data_array, index + 1); if (err_code !=NRF_ERROR_INVALID_STATE) { APP_ERROR_CHECK(err_code); } index = 0; } /**@snippet [Handling the data receivedover UART] */ }
-------------------------------- voidUART0_IRQHandler(void)---- -------------------------------------
接收串口中的消息,当遇到回车或者字符数够了之后,将数据发送出去。
ble_nus_send_string(&m_nus,data_array, index + 1);这条语句中的m_nus的参数就是数据发送的句柄。会发现根本就没有对这个参数进行任何的设置,数据是发不出去的。因此要对这个参数进行相应的处理。这里就需要用到services_init()
[align=left]3) services_init()[/align]
看下参考代码和从机的代码中services_init()的差异,会发现参考代码对该函数又进行了进一步的封装。其中就有对m_nus的操作。同样的。整个复制过来。(包括内部实现的函数)
这里需要记住注册了的服务的uuid和BLE_UUID_NUS_SERVICE。
同时注册的特征id:BLE_UUID_NUS_TX_CHARACTERISTIC,
BLE_UUID_NUS_RX_CHARACTERISTIC也需要记住。
这样发送的部分就已经做完了。
那么接收函数呢?
其实也已经做好了。在复制services_init()函数时, nus_init.data_handler = nus_data_handler;
把接收数据的处理函数也已经添加进来了。这里只是把数据通过串口打印出来而已。并未做其他操作。
主机端:主机端的代码需要参考\Board\nrf6310\s120\experimental\ble_app_hrs_c的代码
串口中断函数。同样的。将参考代码的串口中断函数复制过来。同样是缺乏m_nus这个发送的句柄,因此这里也同样需要添加服务。此外,由于主从机发送数据所调用的函数不同。这里还需要修改ble_nus_send_string(&m_nus,data_array, index + 1)这个函数。修改后的内容如下,(参考\Board\nrf6310\s120\experimental\ble_app_uart_c中的tx_send()的代码。)
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
void tx_send(ble_ecg_c_t *p_ble_ecg_c,char *str ,unsigned char len){ tx_message_t * p_msg; if (len > WRITE_MESSAGE_LENGTH) { return ; } p_msg =&m_tx_buffer[m_tx_insert_index++]; m_tx_insert_index &= TX_BUFFER_MASK; strncpy(p_msg->req.write_req.gattc_value,str,len); p_msg->req.write_req.gattc_params.handle = p_ble_ecg_c->ecg_rx_handle; p_msg->req.write_req.gattc_params.len = len; p_msg->req.write_req.gattc_params.p_value = (uint8_t*)p_msg->req.write_req.gattc_value; p_msg->req.write_req.gattc_params.offset = 0; p_msg->req.write_req.gattc_params.write_op = BLE_GATT_OP_WRITE_REQ; p_msg->conn_handle =p_ble_ecg_c->conn_handle; p_msg->type = WRITE_REQ; tx_buffer_process(); }
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
[align=left]1) 参考hrs_c_init()函数添加新的服务。[/align]
-------------------------------- hrs_c_init()-----------------------------------------
static voidhrs_c_init(void) { ble_hrs_c_init_t hrs_c_init_obj; hrs_c_init_obj.evt_handler =hrs_c_evt_handler; uint32_t err_code = ble_hrs_c_init(&m_ble_hrs_c,&hrs_c_init_obj); APP_ERROR_CHECK(err_code); }
-------------------------------- hrs_c_init()-----------------------------------------
修改部分:
1.(建议,非必须)将所有hrs相关的单词(包括文件名,宏定义,函数名,变量名等)改掉。
2. hrs_c_evt_handler这个函数是数据接收的实现函数。
3. ble_hrs_c_init(&m_ble_hrs_c, &hrs_c_init_obj)中m_ble_hrs_c这个参数的名称必须与串口中断中的m_nus这个参数必须是同一参数,具体起什么名字自己定一个全局变量即可。
4.修改ble_hrs_c_init()
---------------------------------- ble_hrs_c_init()------------------------------------------------
uint32_tble_hrs_c_init(ble_hrs_c_t * p_ble_hrs_c, ble_hrs_c_init_t * p_ble_hrs_c_init) { if ((p_ble_hrs_c == NULL) ||(p_ble_hrs_c_init == NULL)) { return NRF_ERROR_NULL; } ble_uuid_t hrs_uuid; hrs_uuid.type = BLE_UUID_TYPE_BLE; hrs_uuid.uuid = BLE_UUID_HEART_RATE_SERVICE; mp_ble_hrs_c = p_ble_hrs_c; mp_ble_hrs_c->evt_handler = p_ble_hrs_c_init->evt_handler; mp_ble_hrs_c->conn_handle = BLE_CONN_HANDLE_INVALID; mp_ble_hrs_c->hrm_cccd_handle =BLE_GATT_HANDLE_INVALID; returnble_db_discovery_register(&hrs_uuid, db_discover_evt_handler); }
-------------------------------- ble_hrs_c_init()-----------------------------------------
会发现,这里并没有添加服务到服务表中。这里因为心率服务在SIG中已经定义好了。无需用户再进行添加。只需要根据需要再添加特征即可。
这里需要添加一个服务uuid。添加或修改如下代码:
ble_uuid128_t nus_base_uuid = {0x9E, 0xCA, 0xDC, 0x24,0x0E, 0xE5, 0xA9, 0xE0, 0x93,0xF3, 0xA3, 0xB5, 0x00, 0x00, 0x40, 0x6E}; mp_ble_hrs_c = p_ble_hrs_c; err_code =sd_ble_uuid_vs_add(&nus_base_uuid, &m_base_uuid_type); if (err_code != NRF_SUCCESS) { return err_code; } ble_uuid_t hrs_uuid; hrs_uuid.type = m_base_uuid_type; hrs_uuid.uuid = BLE_UUID_NUS_SERVICE;
需要注意的是,uuid必须与从机的uuid一致。BLE_UUID_NUS_SERVICE与从机的一致。这样才能确保主机能找到从机的服务。
再修改db_discover_evt_handler服务发现处理函数。
--------------------------------db_discover_evt_handler-----------------------------------------
static voiddb_discover_evt_handler(ble_db_discovery_evt_t * p_evt) { // Check if the Heart Rate Service wasdiscovered. if (p_evt->evt_type ==BLE_DB_DISCOVERY_COMPLETE && p_evt->params.discovered_db.srv_uuid.uuid ==BLE_UUID_HEART_RATE_SERVICE && p_evt->params.discovered_db.srv_uuid.type == BLE_UUID_TYPE_BLE) { mp_ble_hrs_c->conn_handle =p_evt->conn_handle; // Find the CCCD Handle of the HeartRate Measurement characteristic. uint8_t i; for (i = 0; i <p_evt->params.discovered_db.char_count; i++) { if(p_evt->params.discovered_db.charateristics.characteristic.uuid.uuid == BLE_UUID_HEART_RATE_MEASUREMENT_CHAR) { // Found Heart Ratecharacteristic. Store CCCD handle and break. mp_ble_hrs_c->hrm_cccd_handle = p_evt->params.discovered_db.charateristics.cccd_handle; mp_ble_hrs_c->hrm_handle= p_evt->params.discovered_db.charateristics.characteristic.handle_value; break; } } LOG("[HRS_C]: Heart Rate Servicediscovered at peer.\r\n"); ble_hrs_c_evt_t evt; evt.evt_type =BLE_HRS_C_EVT_DISCOVERY_COMPLETE; mp_ble_hrs_c->evt_handler(mp_ble_hrs_c, &evt); } }
--------------------------------db_discover_evt_handler-----------------------------------------
这里是发现服务后的处理函数。先确保找到的是正确的服务。然后将服务里的特征与主机定义的特征进行匹配,匹配成功后获取相应的参数值。具体修改如下:
将BLE_UUID_HEART_RATE_SERVICE换成BLE_UUID_NUS_SERVICE
将BLE_UUID_TYPE_BLE换成m_base_uuid_type
将BLE_UUID_HEART_RATE_MEASUREMENT_CHAR换成BLE_UUID_NUS_TX_CHARACTERISTIC,并仿照这块代码,添加一个BLE_UUID_NUS_RX_CHARACTERISTIC的特征发现。
最后在蓝牙协议栈的初始化中,添加服务的处理事件。这里可以参考串口服务ble_nus_on_ble_evt中的代码。
--------------------------------ble_nus_on_ble_evt-----------------------------------------
void ble_nus_on_ble_evt(ble_nus_t * p_nus, ble_evt_t * p_ble_evt) { if ((p_nus == NULL) ||(p_ble_evt == NULL)) { return; } switch(p_ble_evt->header.evt_id) { caseBLE_GAP_EVT_CONNECTED: on_connect(p_nus,p_ble_evt); break; case BLE_GAP_EVT_DISCONNECTED: on_disconnect(p_nus,p_ble_evt); break; case BLE_GATTS_EVT_WRITE: on_write(p_nus,p_ble_evt); break; caseBLE_GATTC_EVT_WRITE_RSP: on_write_rsp(p_nus, p_ble_evt); break; default: // No implementationneeded. break; } }-------------------------------ble_nus_on_ble_evt-----------------------------------------
当有通知到来时,将会触发BLE_GATTS_EVT_WRITE,可以在这里将收到的数据通过串口打印出来。
至此,nrf51822的实例代码解析,以及如何添加自己的一个服务及特征,主从机的双向通讯全部解释完毕。当中的参数定义并没有加以补充。复制不同模块的函数时,要同时将参数、申明、宏定义等相关内容也一块复制过来。当然复制代码并不是直接复制,黏贴。而是在理解后参考着原代码修改成为自己的内容。
相关文章推荐
- PHP self与static区别
- 各种排序算法的分析及java实现
- 关于eclipse导入工程出现“SystemProperties cannot be resolved”时的解决方法
- JAVA设计模式之单例模式
- c# winform窗体关闭事件
- java碰撞小球
- C++中成员以其在类中声明的顺序构造
- Mac系统安装JavaSE
- windows下 android jdk sdk环境配置
- ThinkPHP之__construct()和__initialize()
- 配置asp.net 2.0的项目到IIS7
- C++轻量级日志类CLogger的使用(更新)
- [LeetCode]题解(python):045-Jump Game II
- Python核心数据类型——布尔型
- 深入理解PHP内核(一)
- php源码安装
- C++中replace()函数使用方法汇总
- Python核心数据类型——集合(Set)
- Java并发编程:volatile关键字解析
- C++输入一个字符串,将其逆序输出