拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

nRF52840 Keil开发环境搭建:协议栈烧录与BLE调试全攻略

nRF52840 Keil开发环境搭建:协议栈烧录与BLE调试全攻略 搞蓝牙开发几年身边的同事问得最多的不是“怎么用BLE”而是“Keil里为什么一编译就报错”“协议栈到底烧到哪里去了”“好不容易连上手机数据却收不到”。这些问题看着小真卡住的时候能把人耗到怀疑人生。尤其是nRF52840这颗片子性能强、外设多但配套的软件体系第一次接触确实容易懵。这篇就把我平时从零搭nRF52840开发环境Keil版的完整流程摊开讲清楚从协议栈烧录到手机APP调试按实际操作顺序走一遍把该避的坑都提前标出来。不管是你刚拿到一块52840开发板还是从STM32转过来想玩BLE这篇都能帮你把环境一次摆平。1. 方案选型与前置准备1.1 为什么选Keil MDK做nRF52840开发先说结论nRF52840官方主推的SDK是nRF5 SDK用的IDE是Segger Embedded StudioSES但国内用Keil的人依然非常多。原因很实在绝大多数人最早接触嵌入式就是从Keil开始的MDK的操作逻辑、调试界面、快捷键都熟。Keil的AC5编译器对nRF5 SDK的兼容性是官方验证过的工程文件直接提供了Keil版本。网上关于nRF52840的教程、例程、论坛提问很大一部分都是Keil工程遇到问题好搜。调试时用J-Link配合Keil看变量、看内存、断点调试都很顺手。SES当然也有它的优势比如编译速度快、对GCC支持好但如果你是Keil党完全没必要换工具链。nRF5 SDK里从15.2.0到17.1.0版本都直接附带Keil工程文件双击就能打开省去手动移植的功夫。1.2 硬件与软件清单照着准备就行在开始之前先把需要的东西列一个清单缺啥补啥类别具体项目说明硬件nRF52840开发板我用的是官方PCA10056第三方兼容板也行只要引出SWD接口即可硬件J-Link调试器推荐正版或靠谱的V9/V10版本兼容性好硬件手机Android/iOS均可用来做APP调试软件Keil MDK建议MDK 5.37及以上版本AC5/AC6都能用软件nRF5 SDK推荐17.1.0版本最新也最稳定软件nRF Connect for Desktop官方工具套件烧录、调试、日志全包软件nRF Connect for Mobile手机上的BLE调试工具做APP联调必备软件J-Link驱动最新版本即可确保Keil能识别仿真器这里特别提醒一句Keil的版本不要追太新也不要太老。实测过MDK 5.23到5.37都能正常编译nRF5 SDK但太新的MDK比如5.38以上有时候默认切到AC6编译器SDK里部分旧代码会有兼容性警告。稳妥做法是装MDK 5.37然后把编译器设置成AC5兼容性最好。1.3 安装过程的关键点安装步骤网上很多我只说容易被忽略的第一Keil安装路径不要带中文和空格默认C:\Keil_v5就行。第二nRF5 SDK解压后建议放到一个纯英文路径下例如D:\nRF5_SDK_17.1.0。第三J-Link驱动安装前先不要插调试器装完再插避免驱动识别异常。注意如果你用的是nRF52840-DK官方开发板板载自带J-Link OB调试器不需要额外买外置调试器。插上USB线就能在电脑里识别到一个串口和一个J-Link调试器。但第三方板子大概率没有板载调试器就需要外接J-Link。2. 协议栈与SDK核心逻辑先搞懂再动手2.1 SoftDevice协议栈是干什么的很多从STM32转过来的朋友第一次听到协议栈三个字是懵的。STM32上做蓝牙通常是外挂一个BLE模块模块里面已经帮你把蓝牙协议跑完了你只管串口发数据。但nRF52840是SoC芯片也就是蓝牙协议栈和应用代码都跑在同一颗芯片上。那协议栈以什么形式存在nRF52840的协议栈叫SoftDevice目前主流用的是S140它是编译好的二进制文件作为独立固件烧录到芯片的固定Flash区域。应用代码跑在上面通过API调用协议栈的服务。简单类比协议栈就像一个操作系统内核你的应用代码是运行在它之上的程序。系统调用帮你管网络、管蓝牙连接你只管调接口。S140支持蓝牙5.0支持同时连接多路从机支持广播、扫描、连接、GATT服务等这些是最常用的能力。2.2 协议栈占用多少Flash地址怎么划分nRF52840的Flash是1MBRAM是256KB。S140协议栈会占用最前面的一部分空间资源起始地址大小S140协议栈Flash0x000000000x39000约228KB应用代码Flash0x00039000剩余部分协议栈RAM起始地址0x20000128根据配置而定应用RAM起始地址0x20003B80左右剩余部分这就是为什么你在SDK的Keil工程里会看到一堆层层的偏移地址设置。很多人一打开工程看到Flash起始地址不是0x08000000而是0x00039000就蒙了其实这就是给协议栈留出了空间。**烧录时必须先烧协议栈再烧应用固件。**协议栈烧录只需要一次每次编译下载应用代码时不要擦除协议栈。这点后面详细说。2.3 链接脚本分散加载文件别乱改Keil工程里需要配置一个分散加载文件sct文件这个文件定义了代码段、数据段在Flash和RAM中的布局。在nRF5 SDK中每个例程的Keil工程都有对应的配置文件。常见问题是有些人做应用开发时会往工程里加自己的库文件和代码发现编译报错说空间不足或者地址冲突就想去改sct文件。这个文件在你不熟悉的时候千万不要动首先确认你的编译优化等级和宏定义是否正确其次检查是不是Keil版本太老导致编译输出中有错乱。2.4 协议栈的API调用机制SoftDevice向应用代码暴露的API包括两类函数调用接口如sd_ble_gap_adv_start()、sd_ble_gatts_characteristic_add()等以sd_开头。异步事件回调协议栈通过SOFTDEVICE_EVT_BASE中断往应用上报事件应用在事件处理函数里根据事件ID分发处理。如果你的代码逻辑是上电初始化后调用一个API让广播立刻开始这种思路在nRF52840上行不通。协议栈运行后事件驱动的模型意味着你发起一个操作比如启动广播协议栈完成之后会回调一个事件给你你在这个事件里做下一步操作。这也是为什么很多初学者看示例代码半天看不懂流程总觉得代码是跳着跑的。3. Keil工程配置与编译烧录3.1 找到合适的工程文件打开SDK目录下的examples/ble_peripheral里面按应用场景分了很多子目录。以最经典的ble_app_blinky为例路径是nRF5_SDK_17.1.0/examples/ble_peripheral/ble_app_blinky/pca10056/s140/keil5这个目录里面就是针对PCA10056开发板、S140协议栈、Keil MDK的工程文件。直接打开ble_app_blinky.uvprojx即可。不同开发板、不同协议栈版本对应不同的工程目录路径中间的pca10056表示官方开发板型号s140表示使用了S140协议栈。如果你的开发板是第三方的nRF52840模组只要Flash和RAM参数和52840一致通常直接选pca10056的工程就能跑。3.2 编译前需要检查的5个配置项打开Keil工程后不要急着编译先花两分钟过一遍这几个配置1. Device型号Options for Target - Device确认选的芯片是nRF52840_xxAA。这个不对的话Flash算法、寄存器定义全是错的。2. 宏定义C/C选项卡的Define里常见需要保留的是BOARD_PCA10056 CONFIG_GPIO_AS_PINRESET S140其中S140这个宏决定了SDK编译时按S140协议栈的API来处理BOARD_PCA10056决定板级初始化时引脚如何映射。如果是自定义板子但不能改板级配置的情况下可以先沿用PCA10056方案再用bsp_init()做引脚重映射。3. 编译器版本C/C选项卡的Version下拉菜单建议选AC5。AC6也能编译但是SDK部分老代码用的是AC5特性在AC6下会有警告或者error对于新手来说AC5最稳。4. Flash Download配置Utilities选项卡 - Settings - Flash Download页面中确认勾选了Reset and Run并且Programming Algorithm里有nRF52840的外部Flash算法。如果没有需要手动Add具体在SDK目录的components/toolchain/gcc或者Keil安装目录下的Flash目录找nRF52840的FLM文件。5. 分散加载文件Linker选项卡里勾选Use Memory Layout from Target Dialog这样直接用Target页签的地址配置。如果你勾选了Use Scatter File那就要确保sct文件路径正确内容和目标地址匹配。实际操作来看工程下载下来90%的情况不用改动就能编译通过。真有报错99%是宏定义问题或者编译器版本切换导致先往这两个方向排查。3.3 编译并生成HEX文件编译快捷键F7。第一次编译比较慢要编译SDK里大量的库文件大概需要2到5分钟看电脑性能。编译通过后会在_build目录下生成hex文件。这里说一个很有用的设置在Options for Target - Output选项卡里勾选Create HEX File这样每次编译都会额外生成hex文件方便用其他工具烧录。另外在Output选项卡里可以修改输出文件名默认是工程名。建议改成有意义的名字比如blinky_s140.hex因为后面要用nRF Connect或者命令行烧录时文件名太乱容易搞混是哪个固件。3.4 使用J-Link烧录协议栈拿到新芯片或者把芯片擦干净之后第一步是先烧协议栈。方法一用nRF Connect for Desktop。打开nRF Connect for Desktop - Programmer应用 - Connect Device选择你的J-Link设备。然后在界面里点Add File选择S140协议栈的hex文件nRF5_SDK_17.1.0/components/softdevice/s140/hex/s140_nrf52_7.2.0_softdevice.hex选择后点击Write协议栈就会烧到0x00000000起始的Flash地址。烧完后你会看到Flash前0x39000区域被占用这就是协议栈。注意不要点Erase All。方法二用J-Flash Lite命令行。打开J-Flash Lite选择芯片型号nRF52840_xxAA然后选择协议栈hex文件点Program Device。过程和图形界面一样只是更快。方法三用nrfjprog命令行工具。如果你安装了nRF Connect for Desktop自带的命令行工具可以直接在终端操作nrfjprog --program s140_nrf52_7.2.0_softdevice.hex --chiperase --reset重点注意--chiperase会先全片擦除这个参数只在第一次烧协议栈时使用。以后只烧应用固件时千万不能加这个参数否则把协议栈也擦了蓝牙会直接罢工。3.5 烧录应用固件协议栈就位后应用固件的烧录有两种情况1. 开发阶段用Keil直接下载这种最简单编译通过后点LOAD按钮或F8。前提是前面检查过的Flash Download配置正确且连接目标芯片时不要勾选全片擦除。这种模式下只会更新应用代码区协议栈保留不动。2. 批量生产或者脱离Keil烧录用nRF Connect Programmer或者nrfjprognrfjprog --program ble_app_blinky_s140.hex --reset不带--chiperase就是增量写入不会动协议栈。这两步烧录流程是我最想让你在第一次接触nRF52840时就建立起来的肌肉记忆。很多人后续遇到的蓝牙连不上、广播不出来问题一半是因为协议栈没烧对或者被误擦除了。3.6 一个容易被忽略的烧录参数在Keil的Flash Download配置里有一个Erase Sectors和Erase Full Chip选项。有些人在Flash Download里为了图省事勾了Erase Full Chip每次下载应用代码时把整个芯片都擦一遍协议栈每次都被擦掉。然后你发现一个诡异的现象第一次烧完能跑第二次断电再上电就不跑了。这是因为第二次烧录时全片擦除把协议栈干掉了芯片上只有应用代码没有协议栈跑不起来。正确做法是选择Erase Sectors只擦除应用区域所在的扇区。4. 从串口日志到APP调试4.1 串口日志开发调试的第一双眼睛nRF52840 SDK中日志功能默认走的通道有三种UART、RTT、串口。最推荐的是RTT原因是它不需要额外占用串口引脚通过J-Link就能直接读到日志而且速度很快。要在Keil工程里开启RTT日志需要做两件事确认宏定义中有NRF_LOG_ENABLED且其值为1这个宏默认就开着。在custom_board.h或者pca10056.h中定义NRF_LOG_BACKEND_RTT_ENABLED为1。然后编译下载在J-Link RTT ViewerJ-Link驱动自带工具里连接芯片选择nRF52840_xxAA连接后就能看到日志输出。日志级别可以设置NRF_LOG_INFO(BLE event: 0x%x, p_ble_evt-header.evt_id); NRF_LOG_ERROR(ERROR code: %d, err_code); NRF_LOG_DEBUG(debug %d, val);建议开发阶段把日志级别调到Debug量产时再改成Info或者关闭减少资源消耗。4.2 nRF Connect手机APP联调全流程烧完固件、日志能跑了接下来就是拿手机连。手机装nRF Connect for MobileAndroid在应用市场搜nRF ConnectiOS在App Store搜索。打开APP点击SCANNER就能看到周围所有的BLE广播设备。如果你看到设备列表里有一个名字和你代码里广播名一致的外设恭喜广播已经正常工作了。点Connect连接界面下方会列出这个设备的所有GATT Service。在蓝牙BLE开发中数据交互全靠GATT客户端和服务端的通信最小单位叫Characteristic特征值它有自己的UUID、读写权限和通知能力。在SDK的ble_app_blinky例程中服务端定义了一个LED特征值写入和一个Button特征值通知。// 定义特征值的UUID自定义服务基址 #define BLE_UUID_LEDSERVICE_SERVICE_UUID 0x0001 #define BLE_UUID_LED_CHAR_UUID 0x0002 #define BLE_UUID_BUTTON_CHAR_UUID 0x0003手机APP操作流程是找到Service UUID为0x0001的服务。点击Unknown CharacteristicUUID 0x0002往下滑到Write Value区域填入一个字节比如0x01。观察开发板上的LED是否点亮。如果点亮说明写入链路是通的。然后触发开发板上的按键如果你的代码里配置了Button特征值通知APP上订阅通知点Notify旁边的图标按键事件就会实时弹出来。这一步通了整个BLE的收发链路就全打通了手机写数据到板子、板子主动上报数据到手机两个方向都验证完毕。后续做更复杂的透传、OTA、DFU、多点连接都是在这个基础上扩展。4.3 没有手机时的备选调试方案有时候手机不在旁边或者APP调试不方便可以用nRF Connect for Desktop的Bluetooth Low Energy应用连接开发板同样可以浏览GATT服务读写特征值。它的好处是可以同时调试多个连接、能看到完整的连接参数、还能看底层的空中包信息。另一个备选是J-Link RTT Viewer里直接打印收到的数据前提是你的代码里把接收事件打出来。两种方案都能在没有手机的情况下快速验证收发逻辑。5. BLE抓包工具与日志分析5.1 为什么不建议裸看代码排查蓝牙问题蓝牙协议是无线协议很多问题的根源在空中包或者时序上比如两个设备交互时报错、连接后频繁断开、数据丢包如果只盯代码看很难找到真正的原因。我遇到过一个印象很深的case设备广播正常、手机也能搜到但一点连接就失败。看代码编译没问题、逻辑没问题最后抓了空中的包才发现是连接请求参数里slave latency设了一个过大的值手机端处理不过来直接断连。这种问题不看空中的包光看代码是真的看不出来的。所以抓包工具是排查BLE问题时最快解决问题的路径。5.2 nRF52840可以用哪些抓包方案目前市面上做BLE抓包的主要有几种方案方案成本易用性解码能力nRF52840 Dongle Wireshark低百元级高BLE 5.0全协议Nordic官方PPK2抓包中中需配合nRF ConnectEllisys蓝牙分析仪高低行业顶尖对个人开发者来说nRF52840 Dongle加Wireshark是最优解。Dongle刷一个抓包固件插电脑上就变成一个BLE抓包器Wireshark里选这个接口就能看到空中的所有蓝牙包。5.3 抓包的基本流程和关键信息抓包固件烧录方式和烧协议栈类似用nRF Connect Programmer直接写入Dongle即可。固件路径nRF5_SDK_17.1.0/components/softdevice/s140/hex/sniffer_nrf52840_7.2.0.hex烧完之后把Dongle插到电脑USB口打开Wireshark选择对应的接口就能实时看到周围所有的BLE广播包和连接数据包。看包的过程重点看这三样东西1. 广播包Advertising设备有没有在发广播、广播间隔是多少、广播内容是什么设备名、服务UUID、厂商数据。2. 连接请求CONNECT_REQ连接参数协商结果包括连接间隔、从机延迟、监控超时时间。3. 数据通道包LL Data每个连接事件中是否有丢包、重传观察CRC Fail计数数据收发逻辑对不对。5.4 抓包实战排障案例举个例子你的设备每隔30秒要上报一次传感器数据但手机端偶尔漏收。代码层面看数据确实发了日志都打了。抓包看设备在连接事件里发送了Notification但紧接着的下一个连接事件里属于这个包的对端ACK没回来然后协议栈自动重传。如果重传发生在监控超时时间内手机上其实还是能收到只是延迟了。最坏的情况是重传次数耗尽数据就丢了。排查方向就转为查看射频环境是否有干扰、连接间隔是否设置得过长、发送方会不会有一个大包把连接事件时间打满。这些信息只有抓包才能看到这也是我强烈建议每个做BLE开发的人都买一个Dongle的原因。6. 常见问题与排查技巧实录6.1 编译报错与解决我在开发过程中整理了一份高频报错清单建议截图保存报错信息原因解决方案../../../../../../components/softdevice/...file not foundSDK路径不对或工程文件被移动重新打开正确路径下的uvprojxError: L6915E: Library reports error: No space in execution regionsFlash空间不足检查是否分配给了协议栈太多空间error: #20: identifier sd_ble_gap_adv_start is undefined缺少S140宏定义在C/C Define中加入S140warning: #177-D: variable was declared but never referenced部分变量未使用忽略或用NRF_UNUSED_VARIABLE消除Error: Flash Download failed - Cortex-M4J-Link连接问题检查接线、驱动、芯片型号选择6.2 能编译但烧录失败常见死法烧录失败90%的问题出在J-Link和目标板的连接上。逐个排查确认J-Link驱动安装成功设备管理器里能看到J-Link设备。确认接线正确SWDIO、SWCLK、GND、VDD四根线。板子要独立供电或者通过VDD给J-Link参考电压J-Link需要知道目标电压才能设置电平。Keil的Debug设置里要选对调试器类型Options for Target - Debug - Use J-LINK/J-TRACE Cortex。如果提示Connection refused目标板可能处于低功耗模式按住复位键再烧录。6.3 广播不出来排查顺序是什么新板子第一次跑例程广播不出来是最容易碰到的问题第一步看指示灯。官方例程如果正常运行开发板会有一个LED在闪烁这个闪烁本身就是广播状态的指示。第二步看串口日志。RTT Viewer里可以看到这样的日志info a广告已启动如果日志显示广告启动失败比如返回错误码0x300002说明协议栈API返回了错误通常是参数问题。要看具体错误码对照SDK头文件里的定义寻找原因。第三步查射频。有时板子天线没有接好、射频匹配网络焊接有问题会导致虽然广播逻辑没问题但手机搜不到信号。这种情况检查硬件连接尤其检查天线馈点处。第四步确认协议栈版本。编译时宏定义写的S140但实际烧录的协议栈版本是S113或老版本也会导致运行异常。6.4 连接后掉线问题排查连接成功几秒后自动断开这类问题多数跟连接参数、监控超时时间以及供电稳定性有关。连接间隔Connection Interval设置太短——假设设了7.5ms这对射频时隙消耗很大在环境复杂的情况下容易丢包一丢包监控超时时间算得不够就会触发断连。排查方法是打开抓包工具看断开之前发生了什么是超时还是对端主动断开。另一个被忽略的坑是供电不稳。蓝牙在收发瞬间电流脉冲很高如果供电不足或电源纹波过大会影响无线收发灵敏度和系统工作稳定性。建议开发阶段给板子用好的USB口供电或者是加一个10uF100nF的去耦电容。6.5 协议栈被锁死永久锁定问题网上流传一个恐怖的说法nRF52840有永久锁定机制一旦设置错误就报废了。其实官方文档里讲的是PIN_CNF里设置了PIN_RESET保护位加上某些操作后SWD接口被禁用就出现Wireless debug lock或者Read-back protection锁定警告。这种情况下芯片确实通过SWD连不上。碰到这个情况不要直接判死刑先试一下通过串口或者USB DFU方式恢复。部分芯片可以进入DFU模式后重新刷Bootloader来解绑。如果真进入永久锁定debug port保护已无法解除换颗芯片也就十几块钱的事。但更关键的是预防调试期间在宏定义里把CONFIG_GPIO_AS_PINRESET给关掉或者直接保持默认配置不要主动去设置芯片的debug port保护寄存器。新手阶段老老实实用官方默认配置别去折腾这些锁。7. 我的实操心得与总结如果把nRF52840开发比作泡茶协议栈烧录就是先烧水应用编译就是放茶叶APP调试就是用茶杯喝茶。水没烧开就放茶叶茶味出不来协议栈没烧对后面应用写得再好也白搭。这套流程你完整走一遍对nRF52840的开发模型、Flash分区、GATT通信就会建立起真实的手感。有一个细节建议你在后续开发中保持开发初期就把日志系统搭好所有关键事件都用NRF_LOG_INFO打出来。日志不是可有可无的装饰品它是你排查问题时最重要的线索来源。我就是从一次代码里怎么都找不到bug、最后靠日志定位到是因为一个全局变量在中断和主循环里同时读写导致数据错乱之后养成了无日志不编程的习惯。最后分享一个小技巧nRF5 SDK的例程结构其实很清晰遇到没见过的外设功能时不要自己凭空去写先在examples/peripheral目录里找对应的例程。比如要做PWM直接看examples/peripheral/pwm_library要做ADC看examples/peripheral/saadc。把例程代码读懂改参数比从头撸快十倍。这也是我每次接手新项目时的第一动作实测效率最高。
返回列表