简介:《艾克姆科技nRF52832开发指南-上册》面向刚接触Nordic nRF52832与BLE 5.0、2.4GHz无线SoC的嵌入式开发者,尤其适合使用SDK15.2在IK-52832DK开发套件上做外设入门、课程实验与项目验证的读者。指南从硬件原理和实验程序两条线展开,先解析开发板供电与低功耗模式、指示灯与触摸按键、蜂鸣器、电位器、红外接收、USB转UART、I2S音频及显示模块接口,并交代硬件连接与开发工具准备;再以裸机工程为起点,逐步说明UART、GPIOTE、定时器等外设的配置方法、文件增补与头文件引用,便于读者按实验顺序建立软硬件框架。资源为1个PDF文件,压缩包约18.57MB,适合作为案头查阅与实验对照手册;目前已有2514人学习。其价值在于把外设原理、SDK15.2编程和实验修改路径串成可操作的学习线索,目录按硬件描述、开发工具与实验章节组织,帮助读者减少配置试错,并为后续BLE与2.4GHz应用开发打基础。
1. 一次点灯要跨三道坎:nRF52832 的开发门槛到底卡在哪里
nRF52832 这颗 Cortex-M4F 加 2.4GHz 射频的 SoC,把 BLE 5.0 和私有协议塞进 6×6mm 的封装里,参数表漂亮得让人手痒,可真正上手的人常常在同一天连翻三次车:第一次是程序烧进去了灯不亮,第二次是灯亮了手机却搜不到广播,第三次是广播出来了跑不过三秒就进 HardFault。这三件事表面上分别属于硬件、协议栈和工程配置,往深里挖却是同一套东西——Flash 地址空间怎么切、SDK15.2 的工程怎么被链接器拼起来、GPIO 的电气参数到底扛不扛得住负载。搞不清这三层,后面学 UART、GPIOTE、定时器都只是照抄例程,改一个引脚就塌方。这一篇就把这三层从现象拆到实现。
2. Flash 地址空间:为什么 BLE 程序必须分两步烧
裸机点灯的时候,MDK 里点一下 Download 就完事,整个过程没有任何仪式感。换成 BLE 例程,同样的操作,芯片上电后要么毫无反应,要么跑到一半停住。开发指南在「程序下载」一章里专门拆出「下载协议栈」和「下载应用程序」两步,很多人第一次看到会以为这是工具链的怪癖,其实这是 nRF52832 存储布局的必然结果。
2.1 0x00000000 到 0x00026000 不是你的地盘
nRF52832 的 512KB Flash 从 0x00000000 开始编址,但这块空间不是全部交给应用代码的。最前面的 4KB(0x0000_0000 ~ 0x0000_0FFF)是 MBR,也就是主引导记录,它负责校验 SoftDevice 的完整性、转发 SVC 调用、以及在 DFU 场景下决定跳到协议栈还是跳到 bootloader。紧接着从 0x0000_1000 开始是 SoftDevice,S132 协议栈 v6.x 大约占 0x25000 字节,所以应用工程的 IROM1 起点通常配置成 0x0002_6000。
这个分区不是建议,是硬约束。链接器把应用的向量表放在 0x26000,MBR 上电后先跑自己的校验,校验通过再跳到 SoftDevice 的复位入口,SoftDevice 初始化完射频和协议栈,最后按VTOR里登记的地址跳到应用。整条链条上每一环的地址都是写死的,应用如果被烧到 0x20000 或者 0x0,链条在第二环就断了。
提示:判断芯片里到底有什么,最直接的办法是读回头部几个字。用
nrfjprog --memrd 0x0 --n 16 -f NRF52看一眼,如果全是 0xFFFFFFFF,说明 MBR 被擦了;如果 0x1000 处不是有效的向量表形态,说明协议栈没烧或者烧坏了。
工程里这个起点不是一个数字那么简单,它同时出现在三个地方:MDK 的 Target 选项卡 IROM1、链接脚本nrf52_xxaa.icf里的 ROM 区域定义、以及sdk_config.h里跟 bootloader 相关的地址宏。三处不一致的时候,MDK 会以 Target 页为准(勾了 Use Memory Layout from Target Dialog 的前提下),但编译器生成的符号地址来自 icf,于是出现"能编译、能下载、跑不起来"的经典症状。
2.2 为什么顺序不能反:向量表与 RAM 的两次重定位
比 Flash 更容易翻车的是 RAM。SoftDevice 运行时要从 0x2000_0000 开始占用一段 RAM,具体长度取决于 GATT 表配置——连接数、ATT_MTU、注册了多少个服务,都会改变这个值。SDK15.2 的处理方式很直接:应用启动后调用nrf_sdh_ble_enable(),把当前的 RAM 起点传进去,协议栈算完自己的需求,如果发现不够,就返回NRF_ERROR_NO_MEM,同时把新的起点写回这个变量。
/* 应用启动阶段使能 BLE 协议栈,ram_start 既是输入也是输出 */ uint32_t ram_start = 0x20000000; /* 通常先按链接脚本里的 IRAM1 起点填 */ ret_code_t err_code = nrf_sdh_ble_enable(&ram_start); APP_ERROR_CHECK(err_code); /* 若协议栈需要更多空间,ram_start 会被改写成新的基址, 此时必须回头修改 MDK 的 IRAM1 Start 并重新编译 */这里ram_start是典型的 in-out 参数:传进去的时候代表"我以为的 RAM 起点",返回之后代表"协议栈实际需要的起点"。串口日志里那句RAM start address has changed to 0x20002xxx就是在提醒你改工程配置。忽略它的后果很严重——应用的全局变量和栈从 0x20000000 开始铺,协议栈的 GATT 表和连接上下文也在这块区域,两边互相踩,表现是随机 HardFault,位置飘忽不定,调试器断下来也看不出所以然。
向量的重定位同理。应用链接出来的__Vectors位于 0x26000,但上电瞬间 CPU 是从 0x0 取栈顶和复位向量的,这个差值由 MBR 和 SoftDevice 在使能过程中写SCB->VTOR补齐。所以顺序必须是先协议栈后应用:协议栈得先存在,才有谁能把 VTOR 指过去。
2.3 三种下载方式其实是同一件事
开发指南给了三条路:MDK 直接下载、J-Flash、nRFgo Studio。初看像三种工具,拆开看都是 J-Link 往 Flash 写数据,差别只在擦除粒度和地址映射的自动化程度。
MDK 的 Download 只负责写它自己生成的那一个 hex,落到 IROM1 指定的地址,所以协议栈必须提前单独烧。J-Flash 是独立工程,可以把两个 hex 一起拖进地址映射表,按各自的起始地址自动落位。nRFgo Studio 把流程拆成 Program SoftDevice 和 Program Application 两个按钮,底下调的还是同一套命令行。
# 全片擦除:清掉 Flash 和 UICR,NFC 引脚复用等设置会一起丢 nrfjprog --eraseall -f NRF52 # 第一步:烧协议栈,chiperase 保证协议栈区域干净 nrfjprog --program s132_nrf52_6.1.1_softdevice.hex --chiperase --verify -f NRF52 # 第二步:烧应用,sectorerase 只擦应用占用的扇区,不动协议栈 nrfjprog --program ble_app_template_pca10040_s132.hex --sectorerase --verify -f NRF52 # 复位并运行 nrfjprog --reset -f NRF52两个擦除参数的区别是这段脚本的全部技术含量所在。--chiperase作用于整片,--sectorerase只作用于要写的那几个扇区。用--chiperase去烧应用,协议栈会被顺手抹掉,症状就是"昨天还好好的,今天重新下载一次就不广播了"。
还有一个容易被忽略的区域:UICR,位于 0x1000_1000 之后。它保存着 NFC 引脚的复用配置、复位引脚选择、稳压器模式这些出厂/一次性配置。全片擦除会把 UICR 一起还原成默认值,于是 P0.09 和 P0.10 又变回了 NFC 天线接口——如果板子上这两个脚被拿去做普通 IO,擦完片它们就罢工了。解决方式是在工程里定义CONFIG_NFCT_PINS_AS_GPIOS,让启动文件在SystemInit阶段重写 UICR 的NFCPINS寄存器。
3. 从工程模板到 main:SDK15.2 的编译链接是怎么串起来的
新建裸机工程是整套教程里最不"性感"、却最容易中途放弃的一章。手册花了十几页讲规划目录、拷库文件、加组件、配选项卡,看起来繁琐,其实每一页都在回答同一个个问题:链接器怎么知道该把哪段代码放进 Flash、把哪些变量放进 RAM。
3.1 目录分层与 nrfx 的兼容壳
SDK15.2 的目录结构按职责切得比较清楚。components/放的是与芯片无关的软件积木——日志、按钮状态机、FIFO、定时器调度、BLE 服务封装;modules/nrfx/放的是新版 HAL,直接操作寄存器,跨 nRF51/52/52840 系列通用;integration/nrfx/legacy/则是给老代码准备的兼容层,nrf_drv_gpiote.c这类文件本质上是对nrfx_gpiote.c的薄封装,nrf_drv_gpiote_init()内部转调nrfx_gpiote_init()。
这个分层带来一个实实在在的好处:从开发指南的裸机例程迁到 BLE 例程时,GPIO、定时器这些外设代码几乎不用改,因为nrf_drv_*这层壳还在。代价是两套 API 共用同一份硬件状态,同一个外设实例被nrf_drv_xxx_init()和nrfx_xxx_init()各初始化一次,第二次会返回"已初始化"的错误码,看起来像 bug,实际是设计使然。
external/目录里放的是第三方的小件,nano printf 和 RTT 都在这里。前者关系到你能不能重定向printf,后者关系到没有串口时怎么把日志吐出来。这两个东西在调试阶段的价值,比任何外设例程都高。
config/sdk_config.h是 SDK15 最大的架构变化。SDK14 之前,各模块的开关散落在nrf_drv_config.h和一堆条件编译里;SDK15 把它们收拢到一个文件,并用 MDK 的 Configuration Wizard 注释格式组织,让图形界面能解析出树状菜单。所有XXX_ENABLED的宏,最终都从这个文件读出。
3.2 C/C++ 选项卡里的宏、头文件路径与 sdk_config.h
工程配置里最值得逐条看的是 Define 那一栏,它决定了哪些代码参与编译:
| 宏 | 作用 | 配置错了会怎样 |
|---|---|---|
BOARD_PCA10040 | 选中boards/pca10040.h里的引脚定义 | LED、按键引脚与实际板子不符,外设"没反应" |
NRF52832_XXAA | 告诉启动文件和链接脚本 Flash/RAM 的容量 | 容量判断出错,链接越界或浪费空间 |
SOFTDEVICE_PRESENT | 让nrf_sdh系列的封装代码参与编译 | BLE 例程报一堆 undefined symbol |
S132/NRF_SD_BLE_API_VERSION=6 | 指定协议栈型号与 API 版本 | 头文件与 hex 不匹配,运行时 SVC 调用返回非法 |
SWI_DISABLE0 | 关掉软件中断 0,给协议栈腾资源 | 某些例程里 GPIO 中断用不了 |
值得注意的是,Define 管的是"编译哪段代码",sdk_config.h管的是"编进来的代码怎么跑"。两者不同步是新手期最高频的坑:在 Define 里加了NRF_LOG_ENABLED=1,sdk_config.h里同名宏还是 0,结果一行日志都不出,还以为是串口坏了。
Include Paths 里必须有..\..\..\..\config这一条,否则sdk_config.h根本找不到;再加上components、modules/nrfx、modules/nrfx/hal、integration/nrfx、external/nano-lib等等,具体条目随例程规模浮动。少加一条的症状是某些头文件"明明在磁盘上"却 include 不到,这时候不要怀疑磁盘,去翻 Include Paths。
3.3 从 Reset_Handler 到 main:启动文件和链接脚本
裸机工程里最容易被视为黑盒的是启动文件arm_startup_nrf52.s。它是汇编写的,做四件事:定义向量表、提供默认的中断服务函数(全是一个死循环)、在Reset_Handler里调用SystemInit()、最后跳__main。
SystemInit()来自system_nrf52.c,负责把 64MHz 的时钟树配好、开 FPU、根据宏决定要不要改 UICR 的 NFC 引脚配置。__main是 ARM 编译器运行库的入口,它按 scatter 文件里的描述把.data段从 Flash 搬到 RAM、把.bss段清零,然后才跳到用户的main()。所以main函数里能放心使用带初值的全局变量,这份"放心"是__main换来的。
链接脚本nrf52_xxaa.icf定义了 ROM 和 RAM 的边界,它和 Target 选项卡之间有一个开关:勾选 Use Memory Layout from Target Dialog 时以对话框为准,否则以 icf 为准。这个开关在团队协作里制造过不少乌龙——A 同事改了 Target 页的 IROM 起点,B 同事用的是 icf,两人本地都能编译,CI 上就炸。
4. GPIO 的电气真相:驱动能力、限流电阻与按键消抖为什么绕不开
外设章节从 GPIO 开始讲,是有道理的。GPIO 是所有外设里唯一一个"软件配置正确但硬件不配合"的模块,也是唯一一个能让人在半小时内烧掉一颗芯片的模块。开发指南在 GPIO 那一章里专门写了引脚驱动能力和应用注意事项,这两节的价值远超例程本身。
4.1 PIN_CNF:一个寄存器决定方向、上下拉和驱动强度
nRF52832 每个引脚对应一个 32 位的PIN_CNF寄存器,关键位分布是:bit0 方向、bit1 输入缓冲连接、bit2~3 上下拉、bit8~10 驱动强度、bit16~17 检测(SENSE)。库函数nrf_gpio_cfg_output()其实是在一次写里把这几个字段都定死:
/* 库函数内部等价于下面这次配置:输出、断开输入缓冲、无上下拉、标准驱动、不检测 */ nrf_gpio_cfg(LED_1, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_S0S1, NRF_GPIO_PIN_NOSENSE); /* 需要更大电流时改成高驱动档 */ nrf_gpio_cfg(LED_1, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);DRIVE字段决定输出级的推挽强度:S0S1是标准档,H0H1是高驱动档,中间还有H0S1、S0H1两个不对称档位,用来平衡上升沿和下降沿。这几个档位对应的实际电流在毫安量级,具体数值要看数据手册的 DRIVE 表,不要凭记忆写进设计文档。
输出时断开输入缓冲(INPUT_DISCONNECT)是个容易被忽略的细节。输入缓冲连着,引脚上的电平变化会持续驱动内部的反相器,白白耗电;在电池供电的产品里,几十个引脚都这么干,待机电流能从微安级爬到毫安级。
4.2 限流电阻怎么算:从 LED 的 Vf 反推
开发板上的 LED 串联了一个电阻,手册里讲了这个电阻的选取,但没讲透背后的算式。核心就是欧姆定律加一条 LED 的伏安特性:
R = (VDD - Vf) / IVDD 取 3.3V,红色 LED 的正向压降 Vf 大约 1.8V,希望电流 2mA,算出来 R = 750Ω,实际取 1kΩ 得到约 1.5mA——这个亮度在室内足够看清楚,同时把 GPIO 的输出电流压在标准驱动档的能力范围内。
如果把电阻去掉直接接 LED,正向导通后电流只受 GPIO 输出级的内阻和 LED 自身内阻限制,瞬间就能超过 5mA 的上限。轻则亮度异常、引脚发热,重则输出级损坏。反过来,如果电阻取到 10kΩ,电流只剩 0.15mA,灯是亮的,但在强光环境下肉眼几乎看不出来,这时候新手往往会去怀疑代码。
注意:nRF52832 的 IO 电源域最高 3.6V。板上如果有 5V 器件,不能直接把它的输出接进 GPIO,也不能用 GPIO 直接驱动 5V 负载,中间必须加电平转换或者隔离。
4.3 按键:上拉、抖动与 20ms 的由来
轻触按键的电路是"一端接地、一端接 GPIO",GPIO 配成输入加上拉。按下时引脚被拉到 0V,松开时被内部上拉拉回 VDD。库函数的写法很简洁:
/* 配置为上拉输入,按键另一端接地,按下读到 0 */ nrf_gpio_cfg_input(BUTTON_1, NRF_GPIO_PIN_PULLUP); nrf_gpio_cfg_input(BUTTON_2, NRF_GPIO_PIN_PULLUP); if (nrf_gpio_pin_read(BUTTON_1) == 0) { /* 按下,注意这里还没有消抖 */ }内部上拉的阻值在十几千欧量级,按下时流过按键的电流约两百多微安。这个数字单看不大,但电池产品里如果按键长期被压住(比如装在包里),它就是一个持续存在的漏电通路。
机械触点在闭合瞬间会弹跳,5~20ms 内电平在 0 和 1 之间反复横跳。裸机阶段用nrf_delay_ms(20)阻塞等待是最省事的做法,代码直观、现象正确。但一旦工程里加入了 BLE,这种写法就是定时炸弹:协议栈靠中断和调度器维持连接事件,主循环里阻塞 20ms,恰好错过连接间隔,轻则丢包重连,重则连接断开。正确做法是换成app_timer起一个单次定时器,或者直接用components/libraries/button里的状态机,让消抖在后台完成。
4.4 蜂鸣器与电位器:为什么输出口不能直接带负载
有源蜂鸣器内置振荡电路,给直流就响,看起来是 GPIO 的理想负载。问题在于它的工作电流通常在十几到几十毫安,远超 GPIO 输出级的能力上限。开发板的电路里一定有一个三极管或者 MOS 管做开关,GPIO 只负责驱动基极(串联 1kΩ 限流),蜂鸣器的电流走 VDD 到地的独立回路。
无源蜂鸣器更麻烦,它需要 2~4kHz 的方波才能发声。用软件循环翻转 GPIO 会占满 CPU,在 BLE 场景下不可接受;正规做法是用 PWM 外设,或者用 TIMER 的 COMPARE 事件经 PPI 直接连到 GPIOTE 的 TASKS_OUT——整条链路不经过 CPU,占空比和频率由定时器寄存器决定,CPU 该睡就睡。
电位器那一路走的是 SAADC。12 位分辨率、内部参考 0.6V,增益配成 1/6 时量程刚好覆盖 3.6V,能直接量 3.3V 系统的满幅。电位器三个脚分别是 VDD、GND 和滑动端,滑动端接 ADC 输入,转动时输出 0 到 VDD 之间的分压。这里要注意输入阻抗匹配:电位器阻值太大(比如 1MΩ)会让 ADC 采样电容充不满,读数抖动;阻值太小又费电。10kΩ 到 100kΩ 是比较常见的折中。
5. 避坑与排查:烧写、编译、外设三类翻车现场
前面几章讲的是机制,这一章讲机制被违反之后的样子。下面这些现象在开发指南的"常见问题"小节里零散出现过,也在我自己和小伙伴的板子上反复上演。
5.1 烧写与启动:三类"烧进去了却不跑"
现象一:MDK 提示 Download 成功,芯片毫无反应,J-Link 能连上,读回 Flash 一片 0xFF。
原因是 BLE 例程的 IROM1 起点没改,程序被写到 0x0,把 MBR 和协议栈的头部覆盖了;或者全片擦除之后只烧了应用,协议栈是空的。MBR 校验失败,压根不会跳转。
解决办法是先确认 Target 页里 IROM1 的 Start 是 0x26000、Size 是 0x5A000,然后按"擦全片 → 烧协议栈 → 烧应用 → 复位"的顺序重来一遍,烧完用nrfjprog --memrd 0x0 --n 8确认头部不是全 FF。
现象二:串口日志打印RAM start address has changed to 0x20002xxx,紧接着 HardFault。
原因是 IRAM1 起点还停在 0x20000000,应用的数据段和协议栈的 GATT 表抢同一块内存。解决办法是把 Target 页的 IRAM1 Start 改成日志里打印的地址,Size 相应扣减,重新编译。改完之后最好再跑一遍,确认日志里不再出现这句提示——有时候 GATT 表变大,改一次还不够。
现象三:全片擦除后,原本当普通 IO 用的 P0.09、P0.10 不工作了。
原因是 UICR 的NFCPINS在擦除后恢复默认,这两个脚又变回 NFC 天线接口。解决办法是在工程里定义CONFIG_NFCT_PINS_AS_GPIOS,让SystemInit阶段重写 UICR;也可以在主函数开头手动写一次寄存器,但要注意 UICR 写入需要先擦除对应页。
5.2 编译与日志:定义和配置的两套真相
现象四:#include "sdk_config.h"报找不到文件,或者编译通过但日志一行都不出。
前者的原因是 Include Paths 里缺..\..\config;后者的原因是 Define 和sdk_config.h打架——在 C/C++ 选项卡里加了NRF_LOG_ENABLED=1,但sdk_config.h里同名宏是 0,而 SDK15 的日志代码认的是后者。
解决办法是统一从 MDK 的 Configuration Wizard 界面改sdk_config.h,不要两边同时改。改完顺手确认NRF_LOG_BACKEND_UART_ENABLED是开的,并且 UART 的 TX 引脚和板子上的 USB 转串口对得上。
现象五:链接时报一堆undefined symbol nrf_log_frontend_xxx或者nrf_sdh_xxx。
原因是源文件加进工程了,但对应的.c没加,或者SOFTDEVICE_PRESENT、S132这些宏没定义导致条件编译把实现整段跳过了。解决办法是按报错符号名反查它在哪个文件里定义,把文件加进工程组;宏的问题去 Define 栏补,不要试图用extern声明糊过去。
5.3 外设与硬件:引脚、抖动和电流
现象六:LED 闪烁代码逻辑正确,灯就是不亮,示波器量引脚也没有电平变化。
原因是BOARD_PCA10040定义的是官方参考板的引脚映射,与实际板子上 LED 接的脚不一致,代码在操作一个悬空的引脚。解决办法是翻原理图确认实际引脚号,直接用数字调用nrf_gpio_cfg_output();如果板子长期要用,就改boards目录下的板级头文件,让宏和实际硬件对上。
现象七:按一下键,程序响应了好几次。
原因是机械抖动加上轮询周期太短(比如主循环里没有延时,一毫秒读几十次)。解决办法是加 20ms 消抖窗口,同时确认输入配置了上拉——浮空输入在手指靠近时就会被工频干扰带得乱跳。
现象八:蜂鸣器一响,芯片就复位或者程序跑飞。
原因是 GPIO 直驱蜂鸣器,启动瞬间的浪涌电流把 VDD 拉低到欠压复位阈值以下。解决办法是加三极管或者 MOS 管做开关,GPIO 串 1kΩ 电阻驱动基极,蜂鸣器两端反并联续流二极管吸收反向电动势。
6. 设计取舍:什么时候该停在裸机,什么时候必须上协议栈
写到这里,前面的机制最终都会汇聚成一个工程决策:这个项目到底要不要 SoftDevice。这个问题没有标准答案,但有几个可以量化的判断维度。
| 维度 | 只用裸机 + 私有 2.4G | 上 SoftDevice + BLE 5.0 |
|---|---|---|
| 对端设备 | 自研节点之间通信 | 需要与手机、平板直连 |
| Flash 预算 | 512KB 全归应用,宽裕 | 协议栈先占约 150KB |
| 待机电流 | 可做到极低,完全自主控制 | 受广播间隔与连接参数约束 |
| 固件升级 | 自己写 bootloader,自由度高 | 可用 SDK 的 DFU,但地址布局更复杂 |
| 开发周期 | 射频协议全自己定,前期慢 | 现成 GAP/GATT,后期快 |
| 调试难度 | 逻辑全在自己手里 | 出问题要区分应用层与协议栈层 |
真正容易踩错的是"半上不上"的中间态:工程里加了 SoftDevice,但代码还是按裸机思路写,主循环里塞满nrf_delay_ms,中断里做长耗时操作。这种工程能跑,但一旦连接事件密集就丢包,而且现象随机,排查成本极高。判断标准其实很简单——只要调用了nrf_sdh_enable_request(),主循环里就不该再出现任何阻塞超过一个连接间隔的等待。
另一个值得权衡的是 nrfx 与 legacy 驱动的选择。新项目直接用nrfx_gpiote_*、nrfx_timer_*这一套,API 更贴近寄存器、跨系列移植成本低;维护老代码就继续用nrf_drv_*兼容层,但别在同一个外设实例上混用两套 API——第二次初始化返回的错误码会让你误以为是硬件故障。
还有一种情况值得单独说:流水灯、按键、蜂鸣器这类需求,轮询写起来五行代码,GPIOTE + PPI 写起来要配三个外设。前者的代价是 CPU 一直醒着,后者的代价是调试时看不见"代码在跑"。我的经验是,只要产品有电池,或者后面要加 BLE,就该在早期把轮询换成事件驱动;反过来,如果只是验证硬件是否焊好,用nrf_delay_ms点灯五分钟就能得出结论,没必要上全套。
从那以后我每次新建工程,第一件事不是写main,而是把 Target 页的 IROM1 和 IRAM1 抄在便签上,烧完先memrd看一眼 0x0 和 0x26000 各是什么,再决定要不要重烧协议栈。这套动作看着笨,却把"程序烧进去了为什么不跑"这类问题挡在了源头。这份上册从板级硬件原理一路讲到 GPIO、UART、GPIOTE、定时器,每个实验都标明了基于哪个例程修改、要加哪些文件、头文件怎么引用,配合手里的套件边看边改,比自己啃 SDK 头文件快得多,值得放在手边常翻。希望帮到你。
本文还有配套的精品资源,点击获取