跪求一个可以用的爱奇艺会员领取VIP帐号密码 谢谢

鲁ICP备号-3nrf51822 DFU升级
nrf51822 DFU升级:对于单片机做的产品,要实现在线升级,单片机内部一般是两段代码,一个是bootloader程序,一个是用户app程序。
bootloader程序主要就是实现app升级的程序,它是单片机上电后首次运行的程序,app程序就是实现产品功能的程序。
对于nRF51822来说,稍微有点特殊,但是本质原理也是一样的,nRF51822芯片内部有段SoftDevice的程序,它是芯片上电后首次运行的程序,不过这段程序不负责程序升级,它是Nordic官方提供的蓝牙协议栈程序,当然它也具备一点bootloader的功能。
也就是说,芯片上电后,它会判断芯片内部是否有bootloader代码(bootloader代码位置固定,所以它能判断出是否有合法的bootloader程序),若有bootloader程序则会跳转到bootloader程序执行,若没有bootloader程序而只检测到了用户的app程序,那么就直接跳转到app程序运行,当然这个bootloader程序是我们自己写的代码,只是它存储的位置和app程序是不一样的。
bootloader程序差不多也只做两件事情,1:控制程序的跳转,比如跳转到app程序;2:实现app程序升级;
当然bootloader是我们自己写的,你要实现更多的功能也是可以的,但是最基本的功能就是这两个,bootloader运行后,检测直接跳转到app的条件是否满足(app程序合法,相关标志位合法),若满足就直接跳转到app执行app程序,若不满足就继续执行bootloader,等待升级的相关操作命令;
Flash空间布局
采用两种不同布局方式的差别:
(1)、single bank布局不支持softdevice及bootloader自身的升级,只支持该芯片上应用程序的升级。
(2)、采用single bank布局的方式中,一旦开始升级,芯片上原有的应用程序将不能被保存。而以dual bank布局的方式中,在开始接收新的二进制程序(image)时,原有的二进制程序(image)将会被保留,如果升级失败不会影响芯片上原有的程序。
目前的SDK DFU都是只支持dual bank升级,如果需要single bank需要使用6.0之前的版本sdk。
还有就是采用dual bank升级的代码大小不能超过总(内存-SoftDevice size - Bootloader size)/2.
DFU之应用程序
在NORDIC的SDK中,有丰富的文档和例子介绍nrf51822的OTA升级流程。
nRF51_SDK\examples\ble_peripheral\ble_app_hrs\pca1_with_dfu
nRF51_SDK\examples\dfu\bootloader\pca10028\dual_bank_ble_s110
就有完整的DFU例程。
一般写应用都是从以上上面的例程修改过来。我在这里就从零开始把DFU添加到一个我们本身没有DFU的应用上来。
DFU其实就是在原来的BLE应用上再添加一个服务,服务里包含了一个特征,往这个特征写入某个值(例程是0x01)就设置某些标志位,如果有记录了连接了主机的信息(主要是主机的mac地址),就把这些主机的mac地址放在某个内存,然后触发重启,就可以进入bootloader,bootloader就根据保存在某个内存地址上的标志位确定是否启动DFU,DFU会把保存的主机mac取出来,在广播时用定向广播,这样只有主机(这里是手机)能对这个nrf51822进行升级。
使用DFU升级的BLE程序都需要添加程序代码,增加DFU Service。具本分成以下几步:
(1) 将bootloader_util_arm.c,dfu_app_handler.c,ble_dfu.c三个文件添加到自己的工程中。这个三个文件的位置见SDK的说明。需要指出的是,除了这个三个文件,还应把这三个文件使用的头文件包括进工程,并将路径在Keil中配置好。
(2) 在服务初始化函数中初始华DFU服务。
#include &ble_dfu.h&
#include &dfu_app_handler.h&
ble_dfu_init_t dfus_
// Initialize the Device Firmware Update Service.
memset(&dfus_init, 0, sizeof(dfus_init));
dfus_init.evt_handler = dfu_app_on_dfu_
dfus_init.error_handler = NULL;
dfus_init.revision = DFU_REVISION;
err_code = ble_dfu_init(&m_dfus, &dfus_init);
APP_ERROR_CHECK(err_code);
dfu_app_reset_prepare_set(reset_prepare);
(3)在BLE 事件分发函数ble_dfu_on_ble_evt中使用:
ble_dfu_on_ble_evt(&m_dfus, p_ble_evt)
函数,将BLE事件分发给DFU service。
以上即可完成一个基本的支持DFU的应用。> Mac OS X & Linux下搭建Nrf51822开发环境与编译过程
Mac OS X & Linux下搭建Nrf51822开发环境与编译过程
不知道为什么Nordic官方不提供Mac的开发工具以及开发文档,也不知道国外有那么多的教程,为何都是让你绕圈子。官方推荐的IDE是Keil μVision4,这款软件是7年前开发的几乎没有更新过,体验极差。。非常影响写代码心情,所以花了点时间折腾,现在终于可以在Mac或者Linux上面开发了
首先购买开发板,否则下面的都是废话,可以找万能的taobao?
Step1 下载SDK
下载地址: 当前最高版本是 解压zip包到某个项目目录
Step2 下载交叉编译工具
即GCC ARM Embedded我解压到了/usr/local/gcc-arm-none-eabi-5_2-2015q4/
Step3 下载Jlink工具
Step4 配置编译工具路径
打开/..../nRF51_SDK_10.0.0_dc26b5e/components/toolchain/gcc/Makefile.posix改为:
GNU_INSTALL_ROOT&:=&/usr/local/gcc-arm-none-eabi-5_2-2015q4
GNU_VERSION&:=&4.9.3
GNU_PREFIX&:=&arm-none-eabi
编译例程代码
进入demo目录...../nRF51_SDK_10.0.0_dc26b5e/examples/ble_peripheral/ble_app_template/pca1/armgcc
如果安装了上述的segger的Jlink工具,那么在命令行中实际上可以直接使用JlinkExe进行烧录,稍微麻烦点,github有很多脚本。但是Nordic官方也提供了nrfjprog实际上是JlinkExe的封装。?
nrfjprog&--eraseall
烧录hex文件
nrfjprog&--program&/path-to-hex-file.hex
出现以下表示烧录成功
Parsing&hex&file.
Reading&flash&area&to&program&to&guarantee&it&is&erased.
Checking&that&the&area&to&write&is&not&protected.
Programing&device.
也可以使用Makefile中提供的方法
#烧录&softdevice
make&flash_softdevice
#烧录application
make&flash
Flash与RAM空间分配问题
因为不同版本的sdk以及不同的Nrf芯片协议栈softdevice的空间可能不同,所以在烧录application的时候,要指定的起始也不同
#makefile片段
nrf51422_xxac_s110:&OUTPUT_FILENAME&:=&nrf51422_xxac_s110
nrf51422_xxac_s110:&LINKER_SCRIPT=ble_app_template_gcc_nrf51.ld
引入了ble_app_template_gcc_nrf51.ld
/*&Linker&script&to&configure&memory&regions.&*/
SEARCH_DIR(.)
GROUP(-lgcc&-lc&-lnosys)
&&FLASH&(rx)&:&ORIGIN&=&0x18000,&LENGTH&=&0x28000
&&RAM&(rwx)&:&&ORIGIN&=&0x,&LENGTH&=&0x2000
&&.fs_data_out&ALIGN(4):
&&&&PROVIDE(&__start_fs_data&=&.);
&&&&KEEP(*(fs_data))
&&&&PROVIDE(&__stop_fs_data&=&.);
INCLUDE&&nrf5x_common.ld&
其中FLASH与RAM的区段要根据实际情况来设置,共计四个数值,ram起始与结束以及flash起始于结束.根据s110_nrf.0_migration_document.pdf文档的信息:?我们可以知道对于10.0的SDK即softdevice版本8.0的协议栈长度为0x18000,因为总长度为16 kB (0x40000),所以Application地址起始为0x18000,然后最大长度为:0x40000 - 0x18000 = 0x28000
the flash size of the device is assumed to be 256 kB (0x) and the RAM size of the device is assumed to be 16 kB (0x). RAM is always located from execution address 0x. The example will assume SoftDevice B described above will be used. The range of flash from 0 to 0x8 kB) is used by the SoftDevice protocol stack. The range of RAM from 0x to 0x kB) is used by the SoftDevice protocol stack.
内存从0x开始分配,16kb空间总长度为0x,SoftDevice 协议栈使用了8k(0x)内存,所以Application的RAM起始为:0x + 0x = 0x剩余可用RAM长度为8kb 0x2000
不知道为什么Nordic官方不提供Mac的开发工具以及开发文档,也不知道国外有那么多的教程,为何都是让你绕圈子。官方推荐的IDE是Keil μVision4,这款软件是7年前开发的几乎没有更新过,体验极差。
------分隔线----------------------------
相关最新文章
Copyright 2012- ( Coin163 ) All Rights Reserved &&后使用快捷导航没有帐号?
查看: 25710|回复: 12
注册时间最后登录阅读权限10积分15精华0帖子
白丁, 积分 15, 距离下一级还需 35 积分
TA的每日心情开心 14:58签到天数: 1 天连续签到: 1 天[LV.1]初来乍到
NRF51822实例代码说明& && && && && && && && && && && && && && &&&By:谢少旋& && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && && &说在前面:此说明用于nrf51822的主从机的实例代码解析,通过讲解主从机的双向通讯来说明如何添加自己的服务及特征。此说明为个人理解,可能与原意不同。
主机模块从main说起以\nrf51822\Board\nrf\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_PNVIC_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_
& & // 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_t type_
& && && && &// Initializeadvertisement report for parsing.
& && && && &adv_data.p_data =p_ble_evt-&evt.gap_evt.params.adv_report.
& && && && &adv_data.data_len =p_ble_evt-&evt.gap_evt.params.adv_report.
& && && && &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);
& && && && && & }
& && && && &}
& && && && &
& && &&&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&);
& && && && &}
& && && && &
& && &&&default:
& && && && &
--------------------------------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_
& & uint32_
& & 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_
& & uuid.type = m_base_uuid_
& & 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\nrf\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_
& & ble_uuid_t& && && &
& & ble_gatts_char_md_t char_
& & ble_gatts_attr_t& &
& & ble_gatts_attr_md_t attr_
& & ble_gatts_attr_md_t cccd_
& & ble_gatts_attr_md_t char_ud_
& & uint16_t& && && && &svc_
& & static uint8_t multilink_peripheral_
& & 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_
& & 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& & = &
& & attr.p_attr_md = &attr_
& & attr.max_len& &= 1;
& & attr.p_value& &= &multilink_peripheral_
& & 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_
& & 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_
& & char_md.p_char_user_desc& && &&&= multilink_peripheral_
& & 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\pca1\experimental\ble_app_uart的代码。
1)& && &调通串口。发现从机端是没有设置串口代码的。因此需要先在从机端添加相应的串口初始化代码。这里需要用到的uart_init()代码。直接复制即可。
2)& && &串口中断函数直接复制串口中断处理函数的代码void UART0_IRQHandler(void)。
看下中断处理函数做了些什么事情
-------------------------------- voidUART0_IRQHandler(void)---- -------------------------------------{
& & static uint8_tdata_array[BLE_NUS_MAX_DATA_LEN];& & static uint8_t index = 0;
& & uint32_t err_
& & /**@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()
3)& && &services_init()看下参考代码和从机的代码中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_&&
把接收数据的处理函数也已经添加进来了。这里只是把数据通过串口打印出来而已。并未做其他操作。
主机端:主机端的代码需要参考\Board\nrf\experimental\ble_app_hrs_c的代码
串口中断函数。同样的。将参考代码的串口中断函数复制过来。同样是缺乏m_nus这个发送的句柄,因此这里也同样需要添加服务。此外,由于主从机发送数据所调用的函数不同。这里还需要修改ble_nus_send_string(&m_nus,data_array, index + 1)这个函数。修改后的内容如下,(参考\Board\nrf\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_
& & if (len & WRITE_MESSAGE_LENGTH) {
& & }& && && &&&
& & 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_
& &p_msg-&req.write_req.gattc_params.len& && &=& &p_msg-&req.write_req.gattc_params.p_value = (uint8_t*)p_msg-&req.write_req.gattc_& & 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_& & p_msg-&type& && && && && && && && && && &&&= WRITE_REQ;
& & tx_buffer_process();
----------tx_send(ble_ecg_c_t* p_ble_ecg_c,char *str ,unsigned char len)------------
1)& && &参考hrs_c_init()函数添加新的服务。-------------------------------- hrs_c_init()-----------------------------------------
static voidhrs_c_init(void){
& & ble_hrs_c_init_t hrs_c_init_
& & hrs_c_init_obj.evt_handler =hrs_c_evt_
& & 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_
& & 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_
& & 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_
& & ble_uuid_t hrs_
& & hrs_uuid.type& && && && && &&&= m_base_uuid_
& && && & 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_& && &&&// Find the CCCD Handle of the HeartRate Measurement characteristic.& && &&&uint8_
& && &&&for (i = 0; i &p_evt-&params.discovered_db.char_ 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_& && && && && &mp_ble_hrs_c-&hrm_handle=& && && && && &&&p_evt-&params.discovered_db.charateristics.characteristic.handle_& && && && && &
& && && && &}
& && &&&LOG(&[HRS_C]: Heart Rate Servicediscovered at peer.\r\n&);& && &&&ble_hrs_c_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))
& & switch(p_ble_evt-&header.evt_id)
& && &&&caseBLE_GAP_EVT_CONNECTED:
& && && && &on_connect(p_nus,p_ble_evt);
& && && && &
& && &&&case BLE_GAP_EVT_DISCONNECTED:
& && && && &on_disconnect(p_nus,p_ble_evt);
& && && && &
& && &&&case BLE_GATTS_EVT_WRITE:
& && && && &on_write(p_nus,p_ble_evt);
& && && && &
& && && &&&caseBLE_GATTC_EVT_WRITE_RSP:& && && && && && && && && && && && && && && && && && && &
& && && && && && && &on_write_rsp(p_nus, p_ble_evt);
& && && && &&&
& && & default:
& && && && &// No implementationneeded.
& && && && &
}-------------------------------ble_nus_on_ble_evt-----------------------------------------
当有通知到来时,将会触发BLE_GATTS_EVT_WRITE,可以在这里将收到的数据通过串口打印出来。
至此,nrf51822的实例代码解析,以及如何添加自己的一个服务及特征,主从机的双向通讯全部解释完毕。当中的参数定义并没有加以补充。复制不同模块的函数时,要同时将参数、申明、宏定义等相关内容也一块复制过来。当然复制代码并不是直接复制,黏贴。而是在理解后参考着原代码修改成为自己的内容。
注册时间最后登录阅读权限10积分3精华0帖子
白丁, 积分 3, 距离下一级还需 47 积分
该用户从未签到
楼主,有啥方式能联系到你。很多地方不明白!
注册时间最后登录阅读权限60积分6125精华0帖子
状元, 积分 6125, 距离下一级还需 3875 积分
TA的每日心情开心 09:30签到天数: 162 天连续签到: 1 天[LV.7]常住居民III
楼主的文档看不到,
注册时间最后登录阅读权限10积分16精华0帖子
白丁, 积分 16, 距离下一级还需 34 积分
该用户从未签到
注册时间最后登录阅读权限60积分3775精华1帖子
状元, 积分 3775, 距离下一级还需 6225 积分
TA的每日心情奋斗5&天前签到天数: 349 天连续签到: 3 天[LV.8]以坛为家I
学习了,虽然还没有这个芯片。
喜欢在构思妙想,电子产品DIY是工作,也是一种爱好。
注册时间最后登录阅读权限10积分30精华0帖子
白丁, 积分 30, 距离下一级还需 20 积分
TA的每日心情慵懒 09:38签到天数: 4 天连续签到: 1 天[LV.2]偶尔看看I
楼主再讲讲其他的
注册时间最后登录阅读权限60积分3775精华1帖子
状元, 积分 3775, 距离下一级还需 6225 积分
TA的每日心情奋斗5&天前签到天数: 349 天连续签到: 3 天[LV.8]以坛为家I
感谢分享。。。
喜欢在构思妙想,电子产品DIY是工作,也是一种爱好。
注册时间最后登录阅读权限20积分51精华0帖子
童生, 积分 51, 距离下一级还需 149 积分
该用户从未签到
大神们,我现在修改uuid时遇到如下问题:
1.我做的是蓝牙串口透传,用的nRF51822 的APP_BLE_UART的例程。
2.把UUID修改为0-805f9b34fb
更改完之后sd_ble_uuid_vs_add 函数不会返回NRF_SUCCESS
请问哪位大神有这方面的经验,请帮助我解决下。谢谢~~~
注册时间最后登录阅读权限20积分86精华0帖子
童生, 积分 86, 距离下一级还需 114 积分
TA的每日心情慵懒 10:10签到天数: 21 天连续签到: 1 天[LV.4]偶尔看看III
厉害! 期待有更多的内容
注册时间最后登录阅读权限10积分12精华0帖子
白丁, 积分 12, 距离下一级还需 38 积分
TA的每日心情开心 20:34签到天数: 1 天连续签到: 1 天[LV.1]初来乍到
就想要一个串口打印函数。。nrf51822在有栈ble的时候,串口如何打印数据(实现printf功能)
爱板会员勋章
注册成为爱板网会员
站长推荐 /5
爱板网最新上线每周精选栏目,其中汇集科技前沿、评测、拆解、原创资料、最新最热活动!不容错过速速围观!
爱板网论坛面向所有的网友进行 “晒板有奖” 活动,只要你有板卡,就可以尽情来晒,不仅有机会获得一定的奖励,同时,还能一览其他网友晒出的各种板卡。活动详情
小e1智能硬件开发平台是易通星云旗下第一款物联网智能硬件开发平台,也是国内率先支持微信语音控制的智能硬件平台。现提供20块免费的该板卡供大家申请,快来看看吧!
随着8月份举行的全国大学生电子设计大赛日益临近,广大参赛者对大赛相关资料的需求也是日益迫切。作为国内知名的电子技术分享网站——爱板网已在爱板网论坛开通“2017全国大学生电子设计大赛资料专区”,热烈欢迎大家在此专区踊跃分享与大赛相关的资料和经验,作为活动的发起与组织者爱板网将给予每位发帖者相应的奖励。多重好礼在等着您哦!
本视频教程将从如何使用电源组件实现前端和负载点的功能,利用优化的模块实现电源转换功能;电源组件设计方法的优势;等方面入手,教你如何快速掌握高性能电源设计技巧。
Powered by Discuz!

我要回帖

更多关于 爱奇艺cto汤兴泡泡 的文章

 

随机推荐