<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN">
	<id>http://doc.iotxx.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Erjin</id>
	<title>谷雨文档中心 - 用户贡献 [zh-cn]</title>
	<link rel="self" type="application/atom+xml" href="http://doc.iotxx.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Erjin"/>
	<link rel="alternate" type="text/html" href="http://doc.iotxx.com/%E7%89%B9%E6%AE%8A:%E7%94%A8%E6%88%B7%E8%B4%A1%E7%8C%AE/Erjin"/>
	<updated>2026-05-01T23:59:58Z</updated>
	<subtitle>用户贡献</subtitle>
	<generator>MediaWiki 1.31.1</generator>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=GY-AutoTest%E4%BD%9C%E4%B8%9A%E6%8C%87%E5%AF%BC%E4%B9%A6&amp;diff=3054</id>
		<title>GY-AutoTest作业指导书</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=GY-AutoTest%E4%BD%9C%E4%B8%9A%E6%8C%87%E5%AF%BC%E4%B9%A6&amp;diff=3054"/>
		<updated>2020-09-02T06:08:01Z</updated>

		<summary type="html">&lt;p&gt;Erjin：首次创建&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GY-AutoTest自动测试系统（以下简称AutoTest系统），是谷雨第二代模块测试系统。它在第一代的基础上，优化系统结构，减少测试人员动作，提高测试效率。AutoTest系统是对相应模块进行功能性检查，以达到检验的目的。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=GY-AutoTest%E4%BD%9C%E4%B8%9A%E6%8C%87%E5%AF%BC%E4%B9%A6&amp;diff=3053</id>
		<title>GY-AutoTest作业指导书</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=GY-AutoTest%E4%BD%9C%E4%B8%9A%E6%8C%87%E5%AF%BC%E4%B9%A6&amp;diff=3053"/>
		<updated>2020-09-02T05:58:54Z</updated>

		<summary type="html">&lt;p&gt;Erjin：首次创建&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;GY-AutoTest自动测试系统，是谷雨第二代模块测试系统。它在第一代的基础上，优化系统结构，减少测试人员动作，提高测试效率。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2646</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2646"/>
		<updated>2020-01-10T02:48:28Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 添加python下载地址 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了（[https://www.python.org/downloads/release/python-2717/ Python下载地址]）。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本路径。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot; lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成修改后，保存Makefile.windows文件。&lt;br /&gt;
&lt;br /&gt;
回到micro-ecc目录下，运行build_all.bat文件，再次执行编译。此时命令行中正常输出编译过程。现在的micro-ecc的各个平台目录下有了相应的.a或.lib文件。&lt;br /&gt;
&lt;br /&gt;
==== bootloader烧写 ====&lt;br /&gt;
再次编译bootloader工程，此时已没有任何错误警告。工程生成secure_bootloader_ble_s132_pca10040.hex文件，这个bootloader文件。由于此bootloader程序是基于BLE的，所以要想运行程序，还要SoftDevice。接下来所有操作都是以谷雨的NRF52832DK评估板为硬件平台进行操作。&lt;br /&gt;
&lt;br /&gt;
烧写步骤开发者可以查看《烧录bootloader（Programming the bootloader）》章节。&lt;br /&gt;
&lt;br /&gt;
打开nRFgo Studio，擦除器件，烧写SoftDevice，烧写生成的bootloader文件。运行程序，评估板的D1,D2 led亮点亮。打开手机端的nRF Connect，并打开Scan，便可以看到名称为DfuTarg广播设备。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect 扫描截图]]&lt;br /&gt;
&lt;br /&gt;
=== 生成DFU .zip包 ===&lt;br /&gt;
要想完成DFU升级设备程序。DFU.zip程序包是必不可少的。DFU 主机是通过.zip包将新程序发送给DFU 设备。所以.zip包要包含我们想要升级的hex文件，初始化数据和包的签名。&lt;br /&gt;
&lt;br /&gt;
接下来的教程，我们只做application部分的升级。&lt;br /&gt;
* 准备一个应用程序&lt;br /&gt;
在SDK中选择一个例程编译并生成相应的hex文件。为了简单起见，选择一个led_blinky例子，路径examples/ble_peripheral/ble_app_blinky。将生成的hex文件拷到一个开发者创建的目录下，并改名ble_app_blinky.hex（主要是为后面命令输入简单点）。&lt;br /&gt;
* 生成.zip文件&lt;br /&gt;
将《生成密钥对》章节，生成的私钥拷到指定目录下（最好与ble_app_blinky.hex在同个目录下）。利用nrfutil打包生成.zip文件。&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil pkg generate --hw-version 52 --application-version 1 --application ble_app_blinky.hex --sd-req 0xaf --key-file private.pem app_dfu_package.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;解释部分参数：&lt;br /&gt;
* --hw-version&lt;br /&gt;
默认情况下，是要与你的芯片匹配。如果开发者使用的是nRF51 芯片，这里就要填写“51”；如果使用的是nRF52芯片，这里就要填写“52”。但如果想要填写自己的硬件版本号，你可以在bootloader中程序中，使用#define NRF_DFU_HW_VERSION your_hw_number 定义自己的号。&lt;br /&gt;
* --application-version&lt;br /&gt;
默认情况下，应用程序版本号是从0开始的。为了能顺利更新应用程序，应用程序的版本号必须大于等于bootloader中存在数据。因为bootloader是第一次使用，bootloader里存的版本号是0，我们在这里将application-version 写成1。&lt;br /&gt;
* --sd-req&lt;br /&gt;
这个参数是标示SofteDevice 代号（code number）。在写这个文档时，我们使用的SofteDevice是S132 V6.1.0，它的代号是0xAF。开发者可以通过'''nrfutil pkg generate --help'''指令查询SoftDevice的代号。如果仍没有列出代号，可以通过nRFgo Studio查看，只要用评估板烧写相应的SoftDevice。&lt;br /&gt;
* --applicatioin&lt;br /&gt;
告诉nrfutil，将要升级的应用程序。&lt;br /&gt;
&lt;br /&gt;
==== 测试DFU ====&lt;br /&gt;
现在，已经准备好了DFU .zip文件，bootloader也已经在NRF52832DK评估板上运行。接下只要将.zip文件通过DFU主机发送给设备就可以了，便完成application空中升级。由于bootloader在启动后没有发现有效的application，就会进入DFU模式（bootloader章节有说明）。&lt;br /&gt;
* 拷贝上述生成的app_dfu_package.zip&lt;br /&gt;
一般我们会使用手机作为DFU主机（因为手机方便）。并配合nRF Connect手机应用&lt;br /&gt;
* 使用nRF Connect完成DFU&lt;br /&gt;
nRF Connect目前只有Android版的APK，开发者可以自行下载，也可以在谷雨的资料包里找到nRF Connect手机安装包。下面将描述手机操作步骤。&lt;br /&gt;
* 打开nRF Connect，并进行扫描，可以发现一个DfuTarg设备。如下图所示。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect]]&lt;br /&gt;
* 点击旁边的'''CONNECT'''按钮，连接Dfutarg设备，截图如下图所示。&lt;br /&gt;
[[文件:DfuTarg dfu1.jpg|居中|缩略图]]&lt;br /&gt;
在上图的右上角，有个DFU按钮，此按钮用于选择升级文件，即上述步骤中生成的.zip。在这里我们选择Distribution packet(ZIP)。点击'''OK'''按钮，选择上述打包的app_dfu_package.zip文件。&lt;br /&gt;
[[文件:Dfu select type.jpg|居中|缩略图]]&lt;br /&gt;
* 传输ZIP文件&lt;br /&gt;
选定zip文件后，nRF Connect便开始向设备传输，要升级的文件。&lt;br /&gt;
[[文件:DFU App.jpg|居中|缩略图]]&lt;br /&gt;
* 应用程序运行&lt;br /&gt;
传输完成后，再使用nRF Connect进行扫描，发现已经找到DfuTarg设备了，但是有个Ghostyu_Blinky设备。DFU升级应用程序已经成功，应用程序已经运行。&lt;br /&gt;
[[文件:Dfu app run.jpg|居中|缩略图]]&lt;br /&gt;
&lt;br /&gt;
=== Bootloader切换DFU模式 ===&lt;br /&gt;
如果升级的application中没有进入DFU模式相关的代码，可以使用bootloader中NRF_BL_DFU_ENTER_METHOD_BUTTON方法进入DFU模式。在实验部分烧写的bootloader程序中，可以通过16号引脚进入DFU模式。具体方法操作方法如下（以谷雨的NRF52832KD评估板为硬件平台）：&lt;br /&gt;
* 按下按钮S4，并保持（16号引脚，对应评估板按钮S4）&lt;br /&gt;
* 复位评估板&lt;br /&gt;
* 此时系统就会进入DFU模式，开发者可以通过nRF Connect自行验证&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2641</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2641"/>
		<updated>2020-01-09T03:45:11Z</updated>

		<summary type="html">&lt;p&gt;Erjin：bootloader switch to DFU mode&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本路径。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot; lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成修改后，保存Makefile.windows文件。&lt;br /&gt;
&lt;br /&gt;
回到micro-ecc目录下，运行build_all.bat文件，再次执行编译。此时命令行中正常输出编译过程。现在的micro-ecc的各个平台目录下有了相应的.a或.lib文件。&lt;br /&gt;
&lt;br /&gt;
==== bootloader烧写 ====&lt;br /&gt;
再次编译bootloader工程，此时已没有任何错误警告。工程生成secure_bootloader_ble_s132_pca10040.hex文件，这个bootloader文件。由于此bootloader程序是基于BLE的，所以要想运行程序，还要SoftDevice。接下来所有操作都是以谷雨的NRF52832DK评估板为硬件平台进行操作。&lt;br /&gt;
&lt;br /&gt;
烧写步骤开发者可以查看《烧录bootloader（Programming the bootloader）》章节。&lt;br /&gt;
&lt;br /&gt;
打开nRFgo Studio，擦除器件，烧写SoftDevice，烧写生成的bootloader文件。运行程序，评估板的D1,D2 led亮点亮。打开手机端的nRF Connect，并打开Scan，便可以看到名称为DfuTarg广播设备。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect 扫描截图]]&lt;br /&gt;
&lt;br /&gt;
=== 生成DFU .zip包 ===&lt;br /&gt;
要想完成DFU升级设备程序。DFU.zip程序包是必不可少的。DFU 主机是通过.zip包将新程序发送给DFU 设备。所以.zip包要包含我们想要升级的hex文件，初始化数据和包的签名。&lt;br /&gt;
&lt;br /&gt;
接下来的教程，我们只做application部分的升级。&lt;br /&gt;
* 准备一个应用程序&lt;br /&gt;
在SDK中选择一个例程编译并生成相应的hex文件。为了简单起见，选择一个led_blinky例子，路径examples/ble_peripheral/ble_app_blinky。将生成的hex文件拷到一个开发者创建的目录下，并改名ble_app_blinky.hex（主要是为后面命令输入简单点）。&lt;br /&gt;
* 生成.zip文件&lt;br /&gt;
将《生成密钥对》章节，生成的私钥拷到指定目录下（最好与ble_app_blinky.hex在同个目录下）。利用nrfutil打包生成.zip文件。&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil pkg generate --hw-version 52 --application-version 1 --application ble_app_blinky.hex --sd-req 0xaf --key-file private.pem app_dfu_package.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;解释部分参数：&lt;br /&gt;
* --hw-version&lt;br /&gt;
默认情况下，是要与你的芯片匹配。如果开发者使用的是nRF51 芯片，这里就要填写“51”；如果使用的是nRF52芯片，这里就要填写“52”。但如果想要填写自己的硬件版本号，你可以在bootloader中程序中，使用#define NRF_DFU_HW_VERSION your_hw_number 定义自己的号。&lt;br /&gt;
* --application-version&lt;br /&gt;
默认情况下，应用程序版本号是从0开始的。为了能顺利更新应用程序，应用程序的版本号必须大于等于bootloader中存在数据。因为bootloader是第一次使用，bootloader里存的版本号是0，我们在这里将application-version 写成1。&lt;br /&gt;
* --sd-req&lt;br /&gt;
这个参数是标示SofteDevice 代号（code number）。在写这个文档时，我们使用的SofteDevice是S132 V6.1.0，它的代号是0xAF。开发者可以通过'''nrfutil pkg generate --help'''指令查询SoftDevice的代号。如果仍没有列出代号，可以通过nRFgo Studio查看，只要用评估板烧写相应的SoftDevice。&lt;br /&gt;
* --applicatioin&lt;br /&gt;
告诉nrfutil，将要升级的应用程序。&lt;br /&gt;
&lt;br /&gt;
==== 测试DFU ====&lt;br /&gt;
现在，已经准备好了DFU .zip文件，bootloader也已经在NRF52832DK评估板上运行。接下只要将.zip文件通过DFU主机发送给设备就可以了，便完成application空中升级。由于bootloader在启动后没有发现有效的application，就会进入DFU模式（bootloader章节有说明）。&lt;br /&gt;
* 拷贝上述生成的app_dfu_package.zip&lt;br /&gt;
一般我们会使用手机作为DFU主机（因为手机方便）。并配合nRF Connect手机应用&lt;br /&gt;
* 使用nRF Connect完成DFU&lt;br /&gt;
nRF Connect目前只有Android版的APK，开发者可以自行下载，也可以在谷雨的资料包里找到nRF Connect手机安装包。下面将描述手机操作步骤。&lt;br /&gt;
* 打开nRF Connect，并进行扫描，可以发现一个DfuTarg设备。如下图所示。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect]]&lt;br /&gt;
* 点击旁边的'''CONNECT'''按钮，连接Dfutarg设备，截图如下图所示。&lt;br /&gt;
[[文件:DfuTarg dfu1.jpg|居中|缩略图]]&lt;br /&gt;
在上图的右上角，有个DFU按钮，此按钮用于选择升级文件，即上述步骤中生成的.zip。在这里我们选择Distribution packet(ZIP)。点击'''OK'''按钮，选择上述打包的app_dfu_package.zip文件。&lt;br /&gt;
[[文件:Dfu select type.jpg|居中|缩略图]]&lt;br /&gt;
* 传输ZIP文件&lt;br /&gt;
选定zip文件后，nRF Connect便开始向设备传输，要升级的文件。&lt;br /&gt;
[[文件:DFU App.jpg|居中|缩略图]]&lt;br /&gt;
* 应用程序运行&lt;br /&gt;
传输完成后，再使用nRF Connect进行扫描，发现已经找到DfuTarg设备了，但是有个Ghostyu_Blinky设备。DFU升级应用程序已经成功，应用程序已经运行。&lt;br /&gt;
[[文件:Dfu app run.jpg|居中|缩略图]]&lt;br /&gt;
&lt;br /&gt;
=== Bootloader切换DFU模式 ===&lt;br /&gt;
如果升级的application中没有进入DFU模式相关的代码，可以使用bootloader中NRF_BL_DFU_ENTER_METHOD_BUTTON方法进入DFU模式。在实验部分烧写的bootloader程序中，可以通过16号引脚进入DFU模式。具体方法操作方法如下（以谷雨的NRF52832KD评估板为硬件平台）：&lt;br /&gt;
* 按下按钮S4，并保持（16号引脚，对应评估板按钮S4）&lt;br /&gt;
* 复位评估板&lt;br /&gt;
* 此时系统就会进入DFU模式，开发者可以通过nRF Connect自行验证&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2640</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2640"/>
		<updated>2020-01-09T03:44:06Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 测试DFU */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本路径。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot; lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成修改后，保存Makefile.windows文件。&lt;br /&gt;
&lt;br /&gt;
回到micro-ecc目录下，运行build_all.bat文件，再次执行编译。此时命令行中正常输出编译过程。现在的micro-ecc的各个平台目录下有了相应的.a或.lib文件。&lt;br /&gt;
&lt;br /&gt;
==== bootloader烧写 ====&lt;br /&gt;
再次编译bootloader工程，此时已没有任何错误警告。工程生成secure_bootloader_ble_s132_pca10040.hex文件，这个bootloader文件。由于此bootloader程序是基于BLE的，所以要想运行程序，还要SoftDevice。接下来所有操作都是以谷雨的NRF52832DK评估板为硬件平台进行操作。&lt;br /&gt;
&lt;br /&gt;
烧写步骤开发者可以查看《烧录bootloader（Programming the bootloader）》章节。&lt;br /&gt;
&lt;br /&gt;
打开nRFgo Studio，擦除器件，烧写SoftDevice，烧写生成的bootloader文件。运行程序，评估板的D1,D2 led亮点亮。打开手机端的nRF Connect，并打开Scan，便可以看到名称为DfuTarg广播设备。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect 扫描截图]]&lt;br /&gt;
&lt;br /&gt;
=== 生成DFU .zip包 ===&lt;br /&gt;
要想完成DFU升级设备程序。DFU.zip程序包是必不可少的。DFU 主机是通过.zip包将新程序发送给DFU 设备。所以.zip包要包含我们想要升级的hex文件，初始化数据和包的签名。&lt;br /&gt;
&lt;br /&gt;
接下来的教程，我们只做application部分的升级。&lt;br /&gt;
* 准备一个应用程序&lt;br /&gt;
在SDK中选择一个例程编译并生成相应的hex文件。为了简单起见，选择一个led_blinky例子，路径examples/ble_peripheral/ble_app_blinky。将生成的hex文件拷到一个开发者创建的目录下，并改名ble_app_blinky.hex（主要是为后面命令输入简单点）。&lt;br /&gt;
* 生成.zip文件&lt;br /&gt;
将《生成密钥对》章节，生成的私钥拷到指定目录下（最好与ble_app_blinky.hex在同个目录下）。利用nrfutil打包生成.zip文件。&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil pkg generate --hw-version 52 --application-version 1 --application ble_app_blinky.hex --sd-req 0xaf --key-file private.pem app_dfu_package.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;解释部分参数：&lt;br /&gt;
* --hw-version&lt;br /&gt;
默认情况下，是要与你的芯片匹配。如果开发者使用的是nRF51 芯片，这里就要填写“51”；如果使用的是nRF52芯片，这里就要填写“52”。但如果想要填写自己的硬件版本号，你可以在bootloader中程序中，使用#define NRF_DFU_HW_VERSION your_hw_number 定义自己的号。&lt;br /&gt;
* --application-version&lt;br /&gt;
默认情况下，应用程序版本号是从0开始的。为了能顺利更新应用程序，应用程序的版本号必须大于等于bootloader中存在数据。因为bootloader是第一次使用，bootloader里存的版本号是0，我们在这里将application-version 写成1。&lt;br /&gt;
* --sd-req&lt;br /&gt;
这个参数是标示SofteDevice 代号（code number）。在写这个文档时，我们使用的SofteDevice是S132 V6.1.0，它的代号是0xAF。开发者可以通过'''nrfutil pkg generate --help'''指令查询SoftDevice的代号。如果仍没有列出代号，可以通过nRFgo Studio查看，只要用评估板烧写相应的SoftDevice。&lt;br /&gt;
* --applicatioin&lt;br /&gt;
告诉nrfutil，将要升级的应用程序。&lt;br /&gt;
&lt;br /&gt;
==== 测试DFU ====&lt;br /&gt;
&lt;br /&gt;
=== 现在，已经准备好了DFU .zip文件，bootloader也已经在NRF52832DK评估板上运行。接下只要将.zip文件通过DFU主机发送给设备就可以了，便完成application空中升级。由于bootloader在启动后没有发现有效的application，就会进入DFU模式（bootloader章节有说明）。 ===&lt;br /&gt;
* 拷贝上述生成的app_dfu_package.zip&lt;br /&gt;
一般我们会使用手机作为DFU主机（因为手机方便）。并配合nRF Connect手机应用&lt;br /&gt;
* 使用nRF Connect完成DFU&lt;br /&gt;
nRF Connect目前只有Android版的APK，开发者可以自行下载，也可以在谷雨的资料包里找到nRF Connect手机安装包。下面将描述手机操作步骤。&lt;br /&gt;
* 打开nRF Connect，并进行扫描，可以发现一个DfuTarg设备。如下图所示。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect]]&lt;br /&gt;
* 点击旁边的'''CONNECT'''按钮，连接Dfutarg设备，截图如下图所示。&lt;br /&gt;
[[文件:DfuTarg dfu1.jpg|居中|缩略图]]&lt;br /&gt;
在上图的右上角，有个DFU按钮，此按钮用于选择升级文件，即上述步骤中生成的.zip。在这里我们选择Distribution packet(ZIP)。点击'''OK'''按钮，选择上述打包的app_dfu_package.zip文件。&lt;br /&gt;
[[文件:Dfu select type.jpg|居中|缩略图]]&lt;br /&gt;
* 传输ZIP文件&lt;br /&gt;
选定zip文件后，nRF Connect便开始向设备传输，要升级的文件。&lt;br /&gt;
[[文件:DFU App.jpg|居中|缩略图]]&lt;br /&gt;
* 应用程序运行&lt;br /&gt;
传输完成后，再使用nRF Connect进行扫描，发现已经找到DfuTarg设备了，但是有个Ghostyu_Blinky设备。DFU升级应用程序已经成功，应用程序已经运行。&lt;br /&gt;
[[文件:Dfu app run.jpg|居中|缩略图]]&lt;br /&gt;
&lt;br /&gt;
=== Bootloader切换DFU模式 ===&lt;br /&gt;
如果升级的application中没有进入DFU模式相关的代码，可以使用bootloader中NRF_BL_DFU_ENTER_METHOD_BUTTON方法进入DFU模式。在实验部分烧写的bootloader程序中，可以通过16号引脚进入DFU模式。具体方法操作方法如下（以谷雨的NRF52832KD评估板为硬件平台）：&lt;br /&gt;
* 按下按钮S4，并保持（16号引脚，对应评估板按钮S4）&lt;br /&gt;
* 复位评估板&lt;br /&gt;
* 此时系统就会进入DFU模式，开发者可以通过nRF Connect自行验证&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2638</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2638"/>
		<updated>2020-01-09T02:40:15Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 测试DFU */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本路径。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot; lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成修改后，保存Makefile.windows文件。&lt;br /&gt;
&lt;br /&gt;
回到micro-ecc目录下，运行build_all.bat文件，再次执行编译。此时命令行中正常输出编译过程。现在的micro-ecc的各个平台目录下有了相应的.a或.lib文件。&lt;br /&gt;
&lt;br /&gt;
==== bootloader烧写 ====&lt;br /&gt;
再次编译bootloader工程，此时已没有任何错误警告。工程生成secure_bootloader_ble_s132_pca10040.hex文件，这个bootloader文件。由于此bootloader程序是基于BLE的，所以要想运行程序，还要SoftDevice。接下来所有操作都是以谷雨的NRF52832DK评估板为硬件平台进行操作。&lt;br /&gt;
&lt;br /&gt;
烧写步骤开发者可以查看《烧录bootloader（Programming the bootloader）》章节。&lt;br /&gt;
&lt;br /&gt;
打开nRFgo Studio，擦除器件，烧写SoftDevice，烧写生成的bootloader文件。运行程序，评估板的D1,D2 led亮点亮。打开手机端的nRF Connect，并打开Scan，便可以看到名称为DfuTarg广播设备。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect 扫描截图]]&lt;br /&gt;
&lt;br /&gt;
=== 生成DFU .zip包 ===&lt;br /&gt;
要想完成DFU升级设备程序。DFU.zip程序包是必不可少的。DFU 主机是通过.zip包将新程序发送给DFU 设备。所以.zip包要包含我们想要升级的hex文件，初始化数据和包的签名。&lt;br /&gt;
&lt;br /&gt;
接下来的教程，我们只做application部分的升级。&lt;br /&gt;
* 准备一个应用程序&lt;br /&gt;
在SDK中选择一个例程编译并生成相应的hex文件。为了简单起见，选择一个led_blinky例子，路径examples/ble_peripheral/ble_app_blinky。将生成的hex文件拷到一个开发者创建的目录下，并改名ble_app_blinky.hex（主要是为后面命令输入简单点）。&lt;br /&gt;
* 生成.zip文件&lt;br /&gt;
将《生成密钥对》章节，生成的私钥拷到指定目录下（最好与ble_app_blinky.hex在同个目录下）。利用nrfutil打包生成.zip文件。&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil pkg generate --hw-version 52 --application-version 1 --application ble_app_blinky.hex --sd-req 0xaf --key-file private.pem app_dfu_package.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;解释部分参数：&lt;br /&gt;
* --hw-version&lt;br /&gt;
默认情况下，是要与你的芯片匹配。如果开发者使用的是nRF51 芯片，这里就要填写“51”；如果使用的是nRF52芯片，这里就要填写“52”。但如果想要填写自己的硬件版本号，你可以在bootloader中程序中，使用#define NRF_DFU_HW_VERSION your_hw_number 定义自己的号。&lt;br /&gt;
* --application-version&lt;br /&gt;
默认情况下，应用程序版本号是从0开始的。为了能顺利更新应用程序，应用程序的版本号必须大于等于bootloader中存在数据。因为bootloader是第一次使用，bootloader里存的版本号是0，我们在这里将application-version 写成1。&lt;br /&gt;
* --sd-req&lt;br /&gt;
这个参数是标示SofteDevice 代号（code number）。在写这个文档时，我们使用的SofteDevice是S132 V6.1.0，它的代号是0xAF。开发者可以通过'''nrfutil pkg generate --help'''指令查询SoftDevice的代号。如果仍没有列出代号，可以通过nRFgo Studio查看，只要用评估板烧写相应的SoftDevice。&lt;br /&gt;
* --applicatioin&lt;br /&gt;
告诉nrfutil，将要升级的应用程序。&lt;br /&gt;
&lt;br /&gt;
==== 测试DFU ====&lt;br /&gt;
现在，已经准备好了DFU .zip文件，bootloader也已经在NRF52832DK评估板上运行。接下只要将.zip文件通过DFU主机发送给设备就可以了，便完成application空中升级。由于bootloader在启动后没有发现有效的application，就会进入DFU模式（bootloader章节有说明）。&lt;br /&gt;
* 拷贝上述生成的app_dfu_package.zip&lt;br /&gt;
一般我们会使用手机作为DFU主机（因为手机方便）。并配合nRF Connect手机应用&lt;br /&gt;
* 使用nRF Connect完成DFU&lt;br /&gt;
nRF Connect目前只有Android版的APK，开发者可以自行下载，也可以在谷雨的资料包里找到nRF Connect手机安装包。下面将描述手机操作步骤。&lt;br /&gt;
* 打开nRF Connect，并进行扫描，可以发现一个DfuTarg设备。如下图所示。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect]]&lt;br /&gt;
* 点击旁边的'''CONNECT'''按钮，连接Dfutarg设备，截图如下图所示。&lt;br /&gt;
[[文件:DfuTarg dfu1.jpg|居中|缩略图]]&lt;br /&gt;
在上图的右上角，有个DFU按钮，此按钮用于选择升级文件，即上述步骤中生成的.zip。在这里我们选择Distribution packet(ZIP)。点击'''OK'''按钮，选择上述打包的app_dfu_package.zip文件。&lt;br /&gt;
[[文件:Dfu select type.jpg|居中|缩略图]]&lt;br /&gt;
* 传输ZIP文件&lt;br /&gt;
选定zip文件后，nRF Connect便开始向设备传输，要升级的文件。&lt;br /&gt;
[[文件:DFU App.jpg|居中|缩略图]]&lt;br /&gt;
* 应用程序运行&lt;br /&gt;
传输完成后，再使用nRF Connect进行扫描，发现已经找到DfuTarg设备了，但是有个Ghostyu_Blinky设备。DFU升级应用程序已经成功，应用程序已经运行。&lt;br /&gt;
[[文件:Dfu app run.jpg|居中|缩略图]]&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Dfu_app_run.jpg&amp;diff=2637</id>
		<title>文件:Dfu app run.jpg</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Dfu_app_run.jpg&amp;diff=2637"/>
		<updated>2020-01-09T02:39:44Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;app_run&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DFU_App.jpg&amp;diff=2636</id>
		<title>文件:DFU App.jpg</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DFU_App.jpg&amp;diff=2636"/>
		<updated>2020-01-09T02:19:37Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;application&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Dfu_select_type.jpg&amp;diff=2635</id>
		<title>文件:Dfu select type.jpg</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Dfu_select_type.jpg&amp;diff=2635"/>
		<updated>2020-01-09T02:11:54Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;filetype&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DfuTarg_dfu1.jpg&amp;diff=2634</id>
		<title>文件:DfuTarg dfu1.jpg</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DfuTarg_dfu1.jpg&amp;diff=2634"/>
		<updated>2020-01-09T02:05:56Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;dfu&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DfuTarg_connect.jpg&amp;diff=2632</id>
		<title>文件:DfuTarg connect.jpg</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DfuTarg_connect.jpg&amp;diff=2632"/>
		<updated>2020-01-09T01:53:59Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;connect&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2629</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2629"/>
		<updated>2020-01-08T09:32:24Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* bootloader烧写 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本路径。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot; lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成修改后，保存Makefile.windows文件。&lt;br /&gt;
&lt;br /&gt;
回到micro-ecc目录下，运行build_all.bat文件，再次执行编译。此时命令行中正常输出编译过程。现在的micro-ecc的各个平台目录下有了相应的.a或.lib文件。&lt;br /&gt;
&lt;br /&gt;
==== bootloader烧写 ====&lt;br /&gt;
再次编译bootloader工程，此时已没有任何错误警告。工程生成secure_bootloader_ble_s132_pca10040.hex文件，这个bootloader文件。由于此bootloader程序是基于BLE的，所以要想运行程序，还要SoftDevice。接下来所有操作都是以谷雨的NRF52832DK评估板为硬件平台进行操作。&lt;br /&gt;
&lt;br /&gt;
烧写步骤开发者可以查看《烧录bootloader（Programming the bootloader）》章节。&lt;br /&gt;
&lt;br /&gt;
打开nRFgo Studio，擦除器件，烧写SoftDevice，烧写生成的bootloader文件。运行程序，评估板的D1,D2 led亮点亮。打开手机端的nRF Connect，并打开Scan，便可以看到名称为DfuTarg广播设备。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect 扫描截图]]&lt;br /&gt;
&lt;br /&gt;
=== 生成DFU .zip包 ===&lt;br /&gt;
要想完成DFU升级设备程序。DFU.zip程序包是必不可少的。DFU 主机是通过.zip包将新程序发送给DFU 设备。所以.zip包要包含我们想要升级的hex文件，初始化数据和包的签名。&lt;br /&gt;
&lt;br /&gt;
接下来的教程，我们只做application部分的升级。&lt;br /&gt;
* 准备一个应用程序&lt;br /&gt;
在SDK中选择一个例程编译并生成相应的hex文件。为了简单起见，选择一个led_blinky例子，路径examples/ble_peripheral/ble_app_blinky。将生成的hex文件拷到一个开发者创建的目录下，并改名ble_app_blinky.hex（主要是为后面命令输入简单点）。&lt;br /&gt;
* 生成.zip文件&lt;br /&gt;
将《生成密钥对》章节，生成的私钥拷到指定目录下（最好与ble_app_blinky.hex在同个目录下）。利用nrfutil打包生成.zip文件。&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil pkg generate --hw-version 52 --application-version 1 --application ble_app_blinky.hex --sd-req 0xaf --key-file private.pem app_dfu_package.zip&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;解释部分参数：&lt;br /&gt;
* --hw-version&lt;br /&gt;
默认情况下，是要与你的芯片匹配。如果开发者使用的是nRF51 芯片，这里就要填写“51”；如果使用的是nRF52芯片，这里就要填写“52”。但如果想要填写自己的硬件版本号，你可以在bootloader中程序中，使用#define NRF_DFU_HW_VERSION your_hw_number 定义自己的号。&lt;br /&gt;
* --application-version&lt;br /&gt;
默认情况下，应用程序版本号是从0开始的。为了能顺利更新应用程序，应用程序的版本号必须大于等于bootloader中存在数据。因为bootloader是第一次使用，bootloader里存的版本号是0，我们在这里将application-version 写成1。&lt;br /&gt;
* --sd-req&lt;br /&gt;
这个参数是标示SofteDevice 代号（code number）。在写这个文档时，我们使用的SofteDevice是S132 V6.1.0，它的代号是0xAF。开发者可以通过'''nrfutil pkg generate --help'''指令查询SoftDevice的代号。如果仍没有列出代号，可以通过nRFgo Studio查看，只要用评估板烧写相应的SoftDevice。&lt;br /&gt;
* --applicatioin&lt;br /&gt;
告诉nrfutil，将要升级的应用程序。&lt;br /&gt;
&lt;br /&gt;
==== 测试DFU ====&lt;br /&gt;
现在，已经准备好了DFU .zip文件，bootloader也已经在NRF52832DK评估板上运行。接下只要将.zip文件通过DFU主机发送给设备就可以了，便完成application空中升级。&lt;br /&gt;
* 拷贝上述生成的app_dfu_package.zip&lt;br /&gt;
一般我们会使用手机作为DFU主机（因为手机方便）。并配合nRF Connect手机应用&lt;br /&gt;
* 使用nRF Connect完成DFU&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2624</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2624"/>
		<updated>2020-01-08T07:26:37Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 安装gcc-arm编译器 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本路径。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot; lang=&amp;quot;makefile&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成修改后，保存Makefile.windows文件。&lt;br /&gt;
&lt;br /&gt;
回到micro-ecc目录下，运行build_all.bat文件，再次执行编译。此时命令行中正常输出编译过程。现在的micro-ecc的各个平台目录下有了相应的.a或.lib文件。&lt;br /&gt;
&lt;br /&gt;
==== bootloader烧写 ====&lt;br /&gt;
再次编译bootloader工程，此时已没有任何错误警告。工程生成secure_bootloader_ble_s132_pca10040.hex文件，这个bootloader文件。由于此bootloader程序是基于BLE的，所以要想运行程序，还要SoftDevice。接下来所有操作都是以谷雨的NRF52832DK评估板为硬件平台进行操作。&lt;br /&gt;
&lt;br /&gt;
烧写步骤开发者可以查看《烧录bootloader（Programming the bootloader）》章节。&lt;br /&gt;
&lt;br /&gt;
打开nRFgo Studio，擦除器件，烧写SoftDevice，烧写生成的bootloader文件。运行程序，评估板的D1,D2 led亮点亮。打开手机端的nRF Connect，并打开Scan，便可以看到名称为DfuTarg广播设备。&lt;br /&gt;
[[文件:DFUTag.jpg|居中|缩略图|nRF Connect 扫描截图]]&lt;br /&gt;
&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DFUTag.jpg&amp;diff=2623</id>
		<title>文件:DFUTag.jpg</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:DFUTag.jpg&amp;diff=2623"/>
		<updated>2020-01-08T07:18:43Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;dfutarg&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2621</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2621"/>
		<updated>2020-01-08T06:03:03Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 安装gcc-arm编译器 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的出错信息。经仔细查看PC上没有安装相应的编译器。开发者可以在在谷雨NRF52832DK评估板DFU相关目录下找到相应的gcc_arm编译器，也可以自行在下载。&lt;br /&gt;
[[文件:Make error.png|居中|缩略图|677x677像素|编译出错截图]]&lt;br /&gt;
点击安装gcc_arm编译器，谷雨提供是（gcc-arm-none-eabi-8-2019-q3-update-win32-sha2.exe）。安装过程不做说明。&lt;br /&gt;
&lt;br /&gt;
安装完成后需要修改SDK安装目录下components/toolchain/gcc/Makefile.windows文件。将编译器版本改成当前我们安装的版本。Makefile.windows默认是'''GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/6 2017-q2-update/bin/'''。这个是编译安装的路径。需要将其改成已经安装的8-2019-q3-update版本。最后修改完成后内容如下。&amp;lt;syntaxhighlight line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
GNU_INSTALL_ROOT := C:/Program Files (x86)/GNU Tools ARM Embedded/8 2019-q3-update/bin/&lt;br /&gt;
GNU_VERSION := 6.3.1&lt;br /&gt;
GNU_PREFIX := arm-none-eabi&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Make_error.png&amp;diff=2619</id>
		<title>文件:Make error.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Make_error.png&amp;diff=2619"/>
		<updated>2020-01-08T05:46:38Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;comile error&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2618</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2618"/>
		<updated>2020-01-08T03:59:53Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* git安装 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错误。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。生成密钥对，需要使用nrfutil工具。nrfutil在nRFgo studio工具安装路径中也有，但是版本非常低（0.3.0版本）。当然，开发者如果想要使用更新版本的nrfutil，可以自己安装。&lt;br /&gt;
&lt;br /&gt;
nrfutil是一个python工具，所以开发者只要安装Python就可以了。但是要注意，Python必须在2.7-3.0版本之间。Python安装完成后，在Python的路径下使用'''python -m pip install nrfutil'''命令安装nrfutil。&lt;br /&gt;
&lt;br /&gt;
nrfutil会安装在python路径下的Scripts文件夹内。强烈建议将nrfutil配置到PC的环境变量中，这样就不需要到Scripts文件夹下执行nrfutil命令。&lt;br /&gt;
&lt;br /&gt;
===== 生成密钥对 =====&lt;br /&gt;
* 生成自己的私钥（private key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys generate private.pem&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* 根据私钥生成公钥（public key）&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;py&amp;quot;&amp;gt;&lt;br /&gt;
nrfutil keys display --key pk --format code private.pem --out_file public_key.c&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;此命令执行完成后，会在当前路径下生成public_key.c文件。这个文件是根据私钥生成的公钥数组。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2019-09-25 (YY-MM-DD) at 17:57:45 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;需要将uint8_t pk[64]复制到Bootloader工程的Application下的dfu_public_key.c文件中，替换#error的内容。完后如下：&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/* This file was automatically generated by nrfutil on 2018-09-08 (YY-MM-DD) at 06:07:33 */&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;sdk_config.h&amp;quot;&lt;br /&gt;
#include &amp;quot;stdint.h&amp;quot;&lt;br /&gt;
#include &amp;quot;compiler_abstraction.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
#if NRF_CRYPTO_BACKEND_OBERON_ENABLED&lt;br /&gt;
/* Oberon backend is changing endianness thus public key must be kept in RAM. */&lt;br /&gt;
#define _PK_CONST&lt;br /&gt;
#else&lt;br /&gt;
#define _PK_CONST const&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/* This file was generated with a throwaway private key, that is only inteded for a debug version of the DFU project.&lt;br /&gt;
  Please see https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md to generate a valid public key. */&lt;br /&gt;
&lt;br /&gt;
#ifdef NRF_DFU_DEBUG_VERSION &lt;br /&gt;
&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) _PK_CONST uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x40, 0xe5, 0x14, 0xb4, 0x6d, 0xb9, 0x83, 0xc7, 0x1c, 0x33, 0x92, 0x17, 0x35, 0x11, 0xe2, 0x00, 0x8b, 0x52, 0x24, 0xbd, 0xbb, 0x6b, 0x6a, 0xe8, 0x68, 0x1a, 0x32, 0xfb, 0x77, 0x15, 0xe1, 0xe1, &lt;br /&gt;
    0xd9, 0xbc, 0x43, 0xbb, 0x55, 0x6f, 0xf6, 0x9e, 0x3d, 0x04, 0x49, 0x5b, 0xbc, 0x47, 0xa3, 0x69, 0x68, 0x24, 0x15, 0x4b, 0x5e, 0x9c, 0x9d, 0x6b, 0xf4, 0x4e, 0x62, 0x59, 0xd7, 0x24, 0xc4, 0x71&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#else&lt;br /&gt;
/** @brief Public key used to verify DFU images */&lt;br /&gt;
__ALIGN(4) const uint8_t pk[64] =&lt;br /&gt;
{&lt;br /&gt;
    0x18, 0xd3, 0xbc, 0xdd, 0x92, 0x7a, 0xdd, 0xa7, 0x73, 0xbf, 0x11, 0xb7, 0x08, 0x59, 0x6d, 0x23, 0x52, 0xf6, 0x47, 0x01, 0xaf, 0xdb, 0xdc, 0xaf, 0x5a, 0x83, 0x03, 0x1a, 0x0a, 0xf8, 0x5b, 0xaf, &lt;br /&gt;
    0x9c, 0x0d, 0x37, 0x9c, 0x77, 0x69, 0xd4, 0x14, 0xab, 0x4c, 0x32, 0x02, 0x94, 0xc3, 0x15, 0x6e, 0xfd, 0x9d, 0xc9, 0xe5, 0xc3, 0x33, 0x1a, 0x69, 0xf3, 0x85, 0xa2, 0x31, 0x85, 0xc6, 0x97, 0x42&lt;br /&gt;
};&lt;br /&gt;
#endif&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成public_key的复制之后，再次编译bootloader工程。&lt;br /&gt;
&lt;br /&gt;
此时已经没有了public key相关错误了，但是报了没有micro_ecc_lib_nrf52.a文件。具体内容如下：&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;&lt;br /&gt;
Fatal Error[Li001]: could not open file &amp;quot;E:\Nordic_BLE\NRF52832_new\nRF5_SDK_15.2.0_9412b96\external\micro-ecc\nrf52hf_iar\armgcc\micro_ecc_lib_nrf52.a&amp;quot; &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;这个原因是上面下载了micro_ecc源码之后，没有编译生成相应的库文件造成的。&lt;br /&gt;
&lt;br /&gt;
===== 安装make工具 =====&lt;br /&gt;
在git安装章节中，build_all.bat文件的最后有相应的make命令，这些make命令就是编译指令（所有编译相关的操作，都是通过makefile完成的，开发者不用编写makefile，SDK中已经帮用户完成这些makefile文件，开发者只要安装make工具，并双击运行build_all.bat）。&lt;br /&gt;
&lt;br /&gt;
make 工具开发者可以自行安装，也可以在谷雨NRF52832DK评估板DFU相关目录下找到make工具。如果是绿色运行软件，开发者要进行PC环境变量设置，否则在运行build_all.bat时会报错。&lt;br /&gt;
&lt;br /&gt;
===== 安装gcc-arm编译器 =====&lt;br /&gt;
运行build_all.bat文件后，再次编译bootloader工程，发现错误依旧存在。在次返回到micro-ecc目录下，发现各个平台目录下没有相应的.a库文件。难道源码有错误，导致make出错？带个这个疑问，在build_all.bat文件后，加上一个pause的bat命令，即不让windows命令窗口自行退出。再次运行build_all.bat文件。&lt;br /&gt;
&lt;br /&gt;
发现命令窗口确实打印出相应的&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2616</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2616"/>
		<updated>2020-01-07T08:48:14Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 存储空间布局（Memory layout） */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK12以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
[[文件:Micro ecc.png|居中|缩略图|600x600像素]]&lt;br /&gt;
其中build_all.bat脚本文件（Windows），是用于编译micro-ecc源码的。在运行时会检查当前系统中是否安装了git。如果安装了，会进一步检查当前目录下是否有相应的源码，如果没有会用git去github上去下载。build_all.bat文件内容如下:&amp;lt;syntaxhighlight lang=&amp;quot;bat&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
@ECHO OFF&lt;br /&gt;
&lt;br /&gt;
:: This script will use git (must be in %PATH%) and arm-none-eabi tools in combination with GNU Make&lt;br /&gt;
:: to both fetch and compile all variants of micro-ecc for the nRF5 families&lt;br /&gt;
&lt;br /&gt;
WHERE &amp;gt;nul 2&amp;gt;nul git&lt;br /&gt;
IF %ERRORLEVEL% NEQ 0 (&lt;br /&gt;
    ECHO &amp;quot;git is not installed. Please install and append to PATH.&amp;quot;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
IF NOT EXIST micro-ecc/uECC.c (&lt;br /&gt;
    ECHO &amp;quot;micro-ecc not found! Let's pull it from HEAD.&amp;quot;&lt;br /&gt;
    git clone https://github.com/kmackay/micro-ecc.git&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
make -C nrf51_armgcc/armgcc&lt;br /&gt;
make -C nrf51_iar/armgcc&lt;br /&gt;
make -C nrf51_keil/armgcc&lt;br /&gt;
make -C nrf52hf_armgcc/armgcc&lt;br /&gt;
make -C nrf52hf_iar/armgcc&lt;br /&gt;
make -C nrf52hf_keil/armgcc&lt;br /&gt;
make -C nrf52nf_armgcc/armgcc&lt;br /&gt;
make -C nrf52nf_iar/armgcc&lt;br /&gt;
make -C nrf52nf_keil/armgcc&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;如果开发者的PC上没有安装git，则需要去网络找到git，并进行安装。此处安装过程不作说明。&lt;br /&gt;
&lt;br /&gt;
安装git完成后，双击build_all.bat文件，此时git就会去下载micro-ecc源码。此时目录下多了一个micro-ecc文件夹，发现里面有相应的uECC.h与uECC.c等相关文件。&lt;br /&gt;
&lt;br /&gt;
回到IAR工程，再次编译，发现没有报缺少micro_ecc相关的文件，只报了没有相应的公钥错。&lt;br /&gt;
&lt;br /&gt;
===== 安装nrfutil =====&lt;br /&gt;
为了解决没有公钥的错误，我们需要public key与priavte key一对密钥对，使用ECDSA_P256_SHA256算法对DFU程序进行签名并加密。&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Micro_ecc.png&amp;diff=2614</id>
		<title>文件:Micro ecc.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Micro_ecc.png&amp;diff=2614"/>
		<updated>2020-01-07T08:11:03Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;directory&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2613</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2613"/>
		<updated>2020-01-07T08:10:10Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 存储空间布局（Memory layout） */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
&lt;br /&gt;
=== 实验前准备 ===&lt;br /&gt;
关与DFU流程（nrf_dfu）与DFU协议（nrf_dfu_transport）这里不在详细说明，有兴趣的开发者可以自行查看Nordic的官方文档，可以在谷雨NRF52832DK评估板的资料中下载Noridc的SDK离线文档（推荐，目前最新为nRF5_SDK_15.2.0_offline_doc）。&lt;br /&gt;
&lt;br /&gt;
接下来的说明与操作都基于DFU的secure bootloader中以ble为传输方式的bootloader。其位于Nordic SDK安装路径下/nRF5_SDK_15.2.0_9412B96/examples/dfu/secure_bootloader/pca_10040_ble。开发者安装不同版本的SDK可能会有所差异。&lt;br /&gt;
[[文件:Secure bootloader.png|居中|缩略图|609x609像素]]&lt;br /&gt;
在secure bootloader目录下有多个bootloader工程，这里我们使用pca10040_ble（S132），而pca_10056_ble是S140，RF52832器件不支持S140的SoftDevice。&lt;br /&gt;
&lt;br /&gt;
==== 辅助工具安装 ====&lt;br /&gt;
SDK112以后,DUF功能对升级文件进行了ECDSA签名加密,防止误升级未授权的程序。而Nordic使用micro-ecc开源软件实现ECDSA。&lt;br /&gt;
&lt;br /&gt;
开发者初次安装SDK时，在SDK中是没有micro-ecc源码的，需要开发者去github上下载。如果开发都没有下载micro_ecc源码，则在编译bootloader时，编译器会报各种错误。主要有两个方面的如下：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!错误内容&lt;br /&gt;
!原因&lt;br /&gt;
|-&lt;br /&gt;
|Fatal Error[Pe035]: #error directive: &amp;quot;Debug public key not valid for production.&lt;br /&gt;
Please see &amp;lt;nowiki&amp;gt;https://github.com/NordicSemiconductor/pc-nrfutil/blob/master/README.md&amp;lt;/nowiki&amp;gt; to generate it&amp;quot;&lt;br /&gt;
|没有有效的签名公钥&lt;br /&gt;
|-&lt;br /&gt;
|各种与micro-ecc的头文件：uECC.h等&lt;br /&gt;
|没有micro-ecc源码&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===== git安装 =====&lt;br /&gt;
micro-ecc是外部开源软件，所以在Nordic的SDK中放到的协议栈目录下的external目录下。在external目录下，开发者会发现有一个micro-ecc目录。进入micro-ecc目录后，确定没有相应的源码，只有不同编译平台链接相关文件。&lt;br /&gt;
&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Secure_bootloader.png&amp;diff=2607</id>
		<title>文件:Secure bootloader.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Secure_bootloader.png&amp;diff=2607"/>
		<updated>2020-01-07T07:22:48Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Detail on secure bootloader&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2606</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2606"/>
		<updated>2020-01-07T06:37:58Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 烧录bootloader（Programming the bootloader） */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014（NRF_UICR_BOOTLOADER_START_ADDRESS）。因此，在烧写bootloader之前必须正确的设置UICR.BOOTLOADERADDR的值（这个操作开发者不用担心，bootloader程序已经做了）。&lt;br /&gt;
&lt;br /&gt;
烧写bootloader程序要求以下几个步骤：&lt;br /&gt;
* 擦除设备&lt;br /&gt;
* 烧写SoftDevice（使用nRFgo Studio工具）&lt;br /&gt;
* 编译bootloader&lt;br /&gt;
* 烧写bootloader&lt;br /&gt;
&lt;br /&gt;
==== 存储空间布局（Memory layout） ====&lt;br /&gt;
当添加bootloader到设备中时，你必须意识到不同的固件组件放到存储器哪里。&lt;br /&gt;
&lt;br /&gt;
下表展示了不同器件与SoftDevice的存储空间分配：&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!组件&lt;br /&gt;
!存储空间范围nRF52832（S132 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52840（S140 v6.1.x）&lt;br /&gt;
!存储空间范围nRF52810（S112 v6.1.x）&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader setttings&lt;br /&gt;
|0x0007 F000 - 0x0008 0000 （4kB）&lt;br /&gt;
|0x000F F000 - 0x0010 0000 (4 kB)&lt;br /&gt;
|0x0002 F000 - 0x0003 0000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|MBR parameter storage&lt;br /&gt;
|0x0007 E000 - 0x0007 F000 (4 kB)&lt;br /&gt;
|0x000F E000 - 0x000F F000 (4 kB)&lt;br /&gt;
|0x0002 E000 - 0x0002 F000 (4 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Bootloader&lt;br /&gt;
|0x0007 8000 - 0x0007 E000 (24 kB)&lt;br /&gt;
|0x000F 8000 - 0x000F E000 (24 kB)&lt;br /&gt;
|0x0002 8000 - 0x0002 E000 (24 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Application area(incl. free space)&lt;br /&gt;
|0x0002 6000 - 0x0007 8000 (328 kB)&lt;br /&gt;
|0x0002 6000 - 0x000F 8000 (840 kB)&lt;br /&gt;
|0x0001 9000 - 0x0002 8000 (60 kB)&lt;br /&gt;
|-&lt;br /&gt;
|SoftDevice&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0002 6000 (148 kB)&lt;br /&gt;
|0x0000 1000 - 0x0001 9000 (96 kB)&lt;br /&gt;
|-&lt;br /&gt;
|Master Boot Record(MBR)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|0x0000 0000 - 0x0000 1000 (4 kB)&lt;br /&gt;
|}&lt;br /&gt;
下图展现了nRF52系列器件的默认空间分配。nRF52832有512kB flash大小，nRF52840有1024kB flash大小，nRF52810有192kB flash大小。&lt;br /&gt;
[[文件:Bootloader memory nrf52.png|居中|缩略图|963x963像素|Memory layout for nRF52]]&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Bootloader_memory_nrf52.png&amp;diff=2605</id>
		<title>文件:Bootloader memory nrf52.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Bootloader_memory_nrf52.png&amp;diff=2605"/>
		<updated>2020-01-07T06:36:01Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;memory layout for nRF52 devices&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Bootloader_memory_layout.png&amp;diff=2604</id>
		<title>文件:Bootloader memory layout.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Bootloader_memory_layout.png&amp;diff=2604"/>
		<updated>2020-01-07T06:30:52Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52 Devices default memory layout&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2598</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2598"/>
		<updated>2020-01-07T04:00:17Z</updated>

		<summary type="html">&lt;p&gt;Erjin：programmer bootloader&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
&lt;br /&gt;
==== 看门狗定时器支持（Watchdog timer support） ====&lt;br /&gt;
bootloader 检测看门狗是否活动并喂它以阻止看门狗复位。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 依赖（Bootloader dependencies） ====&lt;br /&gt;
Bootloader模块除在传输时，不依赖SoftDevice。只有在使用BLE DFU 传输方式才依赖SoftDevice。&lt;br /&gt;
&lt;br /&gt;
Bootloader也支持系统中根本不存SoftDevice的情况。&lt;br /&gt;
&lt;br /&gt;
==== 烧录bootloader（Programming the bootloader） ====&lt;br /&gt;
在系统启动期间，如果bootloader已经安装，MBR（主引导记录，Master Boot Record）负责启动bootloader。为此MBR必须知道bootloader的开始地址。bootloader开始地址存放到UICR.BOOTLOADERADDR中，UICR.BOOTLOADERADDR被映射在0x10001014。&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2597</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2597"/>
		<updated>2020-01-07T03:34:07Z</updated>

		<summary type="html">&lt;p&gt;Erjin：dfu add the bootloader contents&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
=== Bootloader详细说明 ===&lt;br /&gt;
在DFU过程中，Bootloader占据作用的位置，这章节将详细说明DFU过程中，Bootloader所做的工用。&lt;br /&gt;
&lt;br /&gt;
Bootloader主要负责的事项：&lt;br /&gt;
* 引导应用程序&lt;br /&gt;
* 激活新固件&lt;br /&gt;
* 进入DFU 模式，激活DFU传输，并交付接收到的新固件&lt;br /&gt;
* 喂狗看门狗定时器&lt;br /&gt;
在Nordic提供的SDK中，每个bootloader例子都包含一DFU传输方式。&lt;br /&gt;
&lt;br /&gt;
==== Bootloader 设置页信息 ====&lt;br /&gt;
这个设置页（settings page）存储在非易失性存储器（flash）中，用于保存bootloader与DFU相关的信息。这个设置页主要包含以下几方面的内容：&lt;br /&gt;
* 当前固件大小，CRC-32校验值&lt;br /&gt;
* 挂起固件大小，CRC-32校验值&lt;br /&gt;
* 固件更新进度&lt;br /&gt;
* 固件激活时度&lt;br /&gt;
* 当前固件版本（应用程序与bootloader）&lt;br /&gt;
* 一些特殊数据&lt;br /&gt;
&lt;br /&gt;
==== 固件激活（Fireware activation） ====&lt;br /&gt;
固件激活是固件更新最后一步。在启动期间，bootloader将会读取settings page里的相应信息，基于这些信息固件激活会被触发。固件激活涉及到新固件copy，以代替旧固件（如果应用程序是双块区域的，这个将在memory layout会用说明），同时会更改settings page信息，以请允许新固件启动。bootloader 要确保copy过程中断电安全。&lt;br /&gt;
&lt;br /&gt;
==== DFU 模式（DFU mode） ====&lt;br /&gt;
在DFU模式，bootloader会打开DFU传输，以便设备接收新的固件。bootloader进入DFU模式，可以通过以下几个条件：&lt;br /&gt;
* 没有有效的应用程序存在&lt;br /&gt;
* SoftDevice与有效的应用程序都存在时，host请求bootloader进行应用程序更新&lt;br /&gt;
* 进入DFU模式可以通过以下几个源触发：&lt;br /&gt;
** Button（NRF_BL_DFU_ENTER_METHOD_BUTON）&lt;br /&gt;
** Pin reset（NRF_BL_DFU_ENTER_METHOD_PINRESET)）&lt;br /&gt;
** GPREGRET寄存器设置特定的值（NRF_BL_DFU_ENTER_METHOD_GPREGRET）&lt;br /&gt;
** 应用程序通过向settings page写入请求（NRF_BL_DFU_ENTER_METHOD_BUTTONLESS）&lt;br /&gt;
当进入DFU 模式，不活动定时器被启动。当定时器到期时，bootloader就会复位。任何DFU活动都会使不活动定时器重启。不活动定时器超时时间默认是NRF_BL_DFU_INACTIVITY_TIMEOUT_MS。如果SoftDevice激活后，设备进入DFU模式，不活动定时器超时时间被设置为NRF_BL_DFU_CONTINUATION_TIMEOUT_MS。&lt;br /&gt;
&lt;br /&gt;
==== 启动应用程序（Starting the application） ====&lt;br /&gt;
基于settings page的信息，bootloader可以确定是否有应用程序存在，处在什么位置。在启动应用程序之前，bootloader会检查程序的完整性。当然完整性检查也可以在特定的情况下跳过以减少启动时间（NRF_BL_APP_CRC_CHECK_SKIPPED_ON_GPREGRET2，NRF_BL_APP_CRC_CHECK_SKIPPED_ON_SYSTEMOFF_RESET）。只要以下之一条件发生，bootloader就会进入DFU模式：&lt;br /&gt;
* 没有application安装&lt;br /&gt;
* 完整性检查失败&lt;br /&gt;
* 没有setttings page&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2590</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2590"/>
		<updated>2020-01-06T09:28:52Z</updated>

		<summary type="html">&lt;p&gt;Erjin：nrf_bootloader写&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]由上图可以看出它分为nrf_bootloader,nrf_crypto,nrf_dfu和nrf_dfu_transport功能模块。其中nrf_crypto实现安全特性，签名bootloader。在Nordic提供的SDK中，提供Secure Bootloader和Open Bootloader两种类型多种传输方式的bootloader。&lt;br /&gt;
&lt;br /&gt;
''注：关于nrf_bootloader,nrf_dfu,nrf_dfu_transport详细说明，可以查看Nordic的nRF5_SDK_15.2.0文档。''&lt;br /&gt;
&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2536</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2536"/>
		<updated>2019-12-13T07:09:09Z</updated>

		<summary type="html">&lt;p&gt;Erjin：first record&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基本的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。但bootloader最重要的功能就是DFU。它主要有以下几个特性：&lt;br /&gt;
* 更新application,SoftDevice和bootloader&lt;br /&gt;
* 认证更新&lt;br /&gt;
* 降级预防&lt;br /&gt;
* 硬件兼容性验证&lt;br /&gt;
* 多种传输方式：（BLE，UART，USBD）&lt;br /&gt;
* 支持application 携带或不带SoftDevice&lt;br /&gt;
* 支持用独立于SoftDevice的固件替换依赖于SoftDevice的固件&lt;br /&gt;
* 支持使用依赖于SoftDevice的固件替换独立于SoftDevice的固件&lt;br /&gt;
''注：开发者可以查看Nordic的官方原文文档说明。Bootloader and DFU modules章节。''&lt;br /&gt;
&lt;br /&gt;
下面是bootloader功能模块的结构框图：&lt;br /&gt;
[[文件:NRF52832DK Bootloader Modules.png|居中|缩略图|428x428像素|bootloader modules]]&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:NRF52832DK_Bootloader_Modules.png&amp;diff=2535</id>
		<title>文件:NRF52832DK Bootloader Modules.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:NRF52832DK_Bootloader_Modules.png&amp;diff=2535"/>
		<updated>2019-12-13T07:08:08Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;NRF52832DK Bootloader Modules&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2534</id>
		<title>NRF52832DK-DFU固件升级教程</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK-DFU%E5%9B%BA%E4%BB%B6%E5%8D%87%E7%BA%A7%E6%95%99%E7%A8%8B&amp;diff=2534"/>
		<updated>2019-12-13T04:03:22Z</updated>

		<summary type="html">&lt;p&gt;Erjin：第一版，Bootloader部分&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DFU是Device Firmware Update的缩写，翻译成中文是设备固件升级。设备固件升级是现在电子设备开发过程中不可规避的问题。下面将以谷雨物联的NRF52832DK评估板为硬件基础，详细介绍DFU的流程。&lt;br /&gt;
&lt;br /&gt;
=== Bootloader 与 DFU 模型 ===&lt;br /&gt;
在Nordic提供的SDK中，bootloader 与DFU是其中的一部分。开发者可以在安装的SDK目录中找到。当然开发者也可以在它们的基础上，开发编译自己的bootloader。&lt;br /&gt;
&lt;br /&gt;
一个基础的bootloader在运行后，将会启动指定空间的用户程序。当然可以在几个不同的用户程序间切换，或者在启动用户程序之前对设备进行初始化。&lt;br /&gt;
[[分类:NRF52832DK]]&lt;br /&gt;
[[分类:实验手册]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2508</id>
		<title>LED 闪烁控制软件模块</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2508"/>
		<updated>2019-12-09T08:25:35Z</updated>

		<summary type="html">&lt;p&gt;Erjin：led闪烁模块说明初版结束&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LED闪烁功能，作为常用需求。将其归纳为软件模块，与其他项目或工程共享原代码。&lt;br /&gt;
&lt;br /&gt;
=== 文件组成 ===&lt;br /&gt;
功能模块由六个文件组成，分别承载不同作用。&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!文件名称&lt;br /&gt;
!作用&lt;br /&gt;
|-&lt;br /&gt;
|bsp_status_led.c&lt;br /&gt;
|对应用层函数进行定义&lt;br /&gt;
|-&lt;br /&gt;
|bsp_status_led.h&lt;br /&gt;
|对应用层函数进行声明&lt;br /&gt;
|-&lt;br /&gt;
|ledConfig.c&lt;br /&gt;
|对硬件操作接口进行抽象&lt;br /&gt;
|-&lt;br /&gt;
|ledConfig.h&lt;br /&gt;
|对硬件操作接口进行声明&lt;br /&gt;
|-&lt;br /&gt;
|hal_led.c&lt;br /&gt;
|对硬件接口进行定义&lt;br /&gt;
|-&lt;br /&gt;
|hal_led.h&lt;br /&gt;
|对硬件接口进行声明&lt;br /&gt;
|}&lt;br /&gt;
在LED闪烁功能模块中，用到硬件包括一个GPIO引脚，一个定时器。开发者需要根据自身硬件情况，自行选择相应的硬件。并将这些硬件按要求实现相应的函数。这些函数在hal_led.h与hal_led.c文件中进行声明与定义。&lt;br /&gt;
&lt;br /&gt;
==== hal_led.h与hal_led.c ====&amp;lt;!-- hal_led.h中与硬件相关函数声明 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*定时器超时回调函数指针*/&lt;br /&gt;
typedef void (*fnTimerTimeoutCb_t)(bool);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// function declare&lt;br /&gt;
/*Create a timer*/&lt;br /&gt;
void HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb);&lt;br /&gt;
&lt;br /&gt;
/*Start timer*/&lt;br /&gt;
void HalLed_TimerStart(uint32_t ms, void *pContext);&lt;br /&gt;
/*Stop timer*/&lt;br /&gt;
void HalLed_TimerStop(void);&lt;br /&gt;
/*Pin init*/&lt;br /&gt;
void HalLed_PinInit(void);&lt;br /&gt;
/*Pin set */&lt;br /&gt;
void HalLed_PinSet(bool level);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;各函数功能及形参解释说明，如下表所示。&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!函数名&lt;br /&gt;
!解释&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb)&lt;br /&gt;
|创建一个定时器，且定时器能达到秒级的周期。&lt;br /&gt;
pFnTimeoutCb：是定时器超时回调函数指针&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_Timerstart(uint32_t ms, void *pContext)&lt;br /&gt;
|启动定时器。&lt;br /&gt;
ms：是定时周期&lt;br /&gt;
&lt;br /&gt;
pContext：传入的参数指针，会在超时回调函数中使用&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_Timerstop(void)&lt;br /&gt;
|停止定时器&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_PinInit(void)&lt;br /&gt;
|初始化与LED相连的GPIO引脚，并保持LED灭状态&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_PinSet(void)&lt;br /&gt;
|设置GPIO的高低电平，即控制LED亮灭。&lt;br /&gt;
level：true -&amp;gt;LED亮&lt;br /&gt;
&lt;br /&gt;
false -&amp;gt; LED灭&lt;br /&gt;
|}&amp;lt;!-- hal_led.c就是对上述函数进行定义。而各个函数体是与开发者的硬件相关联 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Create a time object for blink led&lt;br /&gt;
//&lt;br /&gt;
// param : pFnTimeoutCb -&amp;gt; the timeout cb&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerStart&lt;br /&gt;
//&lt;br /&gt;
// brief: Start timer&lt;br /&gt;
//&lt;br /&gt;
// param : ms -&amp;gt; the timeout ,unit = ms&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerStart(uint32_t ms,void *pContext)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerStop&lt;br /&gt;
//&lt;br /&gt;
// brief: Stop timer&lt;br /&gt;
//&lt;br /&gt;
// param : ms -&amp;gt; the timeout ,unit = ms&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerStop(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_PinInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Pin init&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_PinInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_PinInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Pin set &lt;br /&gt;
//&lt;br /&gt;
// param : level -&amp;gt; true -&amp;gt; open,false -&amp;gt; off&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_PinSet(bool level)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ledConfig.h与ledConfig.c ====&lt;br /&gt;
ledConfig.h与ledConfig.c是对LED闪烁模块的硬件进行抽象，即hal_led.h与hal_led.c中定义的功能函数。&amp;lt;!-- ledConfig.h抽象接口声明 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*定时器初始化函数指针*/&lt;br /&gt;
typedef void (*fnTimerInit_t)(fnTimerTimeoutCb_t);&lt;br /&gt;
/*定时器启动函数指针*/&lt;br /&gt;
typedef void (*fnTimerStart_t)(uint32_t ms,void*pContext);&lt;br /&gt;
&lt;br /&gt;
/*定时器停止函数指针*/&lt;br /&gt;
typedef void (*fnTimerStop_t)(void);&lt;br /&gt;
&lt;br /&gt;
/*Led 引脚初始化函数指针*/&lt;br /&gt;
typedef void (*fnPinInit_t)(void);&lt;br /&gt;
/*Led 引脚高低电平设置函数指针*/&lt;br /&gt;
typedef void (*fnPinLevelSet_t)(bool level);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : ledBlinkCfg_t&lt;br /&gt;
//&lt;br /&gt;
// Brief : 描述LED闪烁控制接口函数&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  fnTimerInit_t    fnTimerInit;&lt;br /&gt;
  fnTimerStart_t   fnTimerStart;&lt;br /&gt;
  fnTimerStop_t    fnTimerStop;&lt;br /&gt;
  fnPinInit_t      fnPinInit;&lt;br /&gt;
  fnPinLevelSet_t  fnPinLevelSet;&lt;br /&gt;
  &lt;br /&gt;
}ledModuleCfg_t;&lt;br /&gt;
  &lt;br /&gt;
extern ledModuleCfg_t  ledModule;  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;hal_led.h就是根据ledConfig.h声明函数接口进行定义的。&lt;br /&gt;
&lt;br /&gt;
其中结构体ledModuleCfg_t就是对硬件层进行抽象。其中包括定时器定义，定时器启动，定时器停止，引脚gpio初始化，引脚高低电平设置。&amp;lt;!-- ledConfig.c是ledModuleCfg_t进行初始化 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*led operate interface*/&lt;br /&gt;
ledModuleCfg_t  ledModule = {&lt;br /&gt;
  .fnTimerInit   = HalLed_TimerInit,&lt;br /&gt;
  .fnTimerStart  = HalLed_TimerStart,&lt;br /&gt;
  .fnTimerStop   = HalLed_TimerStop,&lt;br /&gt;
  .fnPinInit     = HalLed_PinInit,&lt;br /&gt;
  .fnPinLevelSet = HalLed_PinSet&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bsp_status_led.h 与bsp_status_led.c ====&lt;br /&gt;
这两个文件是开发者可以调用函数的声明与定义。开发者可以调用相应函数对LED闪烁状态进行设置。led支持如下6种状态。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  LED_STATUS_OFF,          //关闭&lt;br /&gt;
  LED_STATUS_ON,           //常亮&lt;br /&gt;
  LED_STATUS_BLINK1,       //闪烁1，周期200ms,占空比50：亮100，灭100&lt;br /&gt;
  LED_STATUS_BLINK2,       //闪烁2，周期1000，占空比10：亮100，灭900&lt;br /&gt;
  LED_STATUS_BLINK3,       //闪烁3，周期1000，占空比50：亮500，灭500&lt;br /&gt;
  LED_STATUS_BLINK4,       //闪烁4，周期2000，占空比5： 亮100，灭1900&lt;br /&gt;
  LED_STATUS_END&lt;br /&gt;
}eLEDStatus;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;当然开发者也可以，根据自己的需求，对led闪烁状态进行修改。包括周期，占空比。具体修改的地方在bsp_status_led.c文件头部。各个闪烁状态由宏定义定义，如下。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_BLINK1_PERIOD   200&lt;br /&gt;
#define LED_BLINK1_DUTY     50&lt;br /&gt;
#define LED_BLINK1_ON_MS    100&lt;br /&gt;
#define LED_BLINK1_OFF_MS   (LED_BLINK1_PERIOD - LED_BLINK1_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK2_PERIOD   1000&lt;br /&gt;
#define LED_BLINK2_DUTY     10&lt;br /&gt;
#define LED_BLINK2_ON_MS    100&lt;br /&gt;
#define LED_BLINK2_OFF_MS   (LED_BLINK2_PERIOD - LED_BLINK2_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK3_PERIOD   1000&lt;br /&gt;
#define LED_BLINK3_DUTY     50&lt;br /&gt;
#define LED_BLINK3_ON_MS    500&lt;br /&gt;
#define LED_BLINK3_OFF_MS   (LED_BLINK3_PERIOD - LED_BLINK3_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK4_PERIOD   2000&lt;br /&gt;
#define LED_BLINK4_DUTY     5&lt;br /&gt;
#define LED_BLINK4_ON_MS    100&lt;br /&gt;
#define LED_BLINK4_OFF_MS   (LED_BLINK4_PERIOD - LED_BLINK4_ON_MS)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 闪烁次数定义 =====&lt;br /&gt;
在代码中，各个闪烁状态都有相应的次数限制。其中0表示此状态闪烁结束，将进行下一个状态，0xFF表示永久闪烁，直到当前状态被人为改变，即闪烁次数被置零，或改为其他数值；或被弹出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_BLINK_STOP      0&lt;br /&gt;
#define LED_BLINK_FOREVER   0xFF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 应用函数声明 =====&lt;br /&gt;
在用户层提供了两种方式。一种是普通模式，一种是栈模式。&lt;br /&gt;
&lt;br /&gt;
普通模式中，开发者只需调用LED_Set函数，即可对当前led状态进行设置。状态结束后，led处在灭状态，等待用户进行下个状态设置。而在栈模式中，开发者调用LED_StackPush函数时，会将led状态压入栈中，并设置led状态。当led闪烁状态结束后，继续之前led状态，直到栈空为至。当然在这个过程中开发者也可以调用LED_Set函数，对当前led闪烁状态进行设置。只是led闪烁状态结束后会继续栈的上个led状态，而不会继续LED_Set函数调用前的状态。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*initial led module*/&lt;br /&gt;
void LED_Init(void);&lt;br /&gt;
/*set the led status*/&lt;br /&gt;
void LED_Set(eLEDStatus,uint8_t times);&lt;br /&gt;
&lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
/*push the led status to ledstack.it will be placed at the top*/&lt;br /&gt;
bool LED_StackPush(eLEDStatus,uint8_t times);&lt;br /&gt;
&lt;br /&gt;
/*pop the top node from ledstack*/&lt;br /&gt;
void LED_StackPop(void);&lt;br /&gt;
&lt;br /&gt;
/*clear stack*/&lt;br /&gt;
void LED_StackClear(void);&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中与栈有关的函数是通过宏LED_PRORITY_EN来控制。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!函数名&lt;br /&gt;
!解释&lt;br /&gt;
!&lt;br /&gt;
|-&lt;br /&gt;
|void LED_Init(void)&lt;br /&gt;
|对LED闪烁模块进行初始化。在任何函数调用之前进行调用&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|void LED_Set( eLEDStatus status，uint8_t times)&lt;br /&gt;
|设置led闪烁状态&lt;br /&gt;
status：设置的led状态&lt;br /&gt;
&lt;br /&gt;
times：闪烁次数&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|bool LED_StackPush(eLEDStatus status, uint8_t times)&lt;br /&gt;
|设置led闪烁状态，并压入栈中&lt;br /&gt;
status：设置的led状态&lt;br /&gt;
&lt;br /&gt;
times：闪烁次数&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|void LED_StackPop(void)&lt;br /&gt;
|弹出栈顶状态&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|void LED_StackClear(void)&lt;br /&gt;
|清空栈里所有led状态，并将led设置灭状态&lt;br /&gt;
|&lt;br /&gt;
|}&amp;lt;!-- bsp_status_led.c函数定义 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*blink status*/&lt;br /&gt;
/*&lt;br /&gt;
{&lt;br /&gt;
  1.period  -&amp;gt; 闪烁周期&lt;br /&gt;
  2.duty    -&amp;gt; 引脚高电平占整个周期的百分比&lt;br /&gt;
  3.times   -&amp;gt; 闪烁次数&lt;br /&gt;
}&lt;br /&gt;
*/&lt;br /&gt;
#define LED_BLINK1_PERIOD   200&lt;br /&gt;
#define LED_BLINK1_DUTY     50&lt;br /&gt;
#define LED_BLINK1_ON_MS    100&lt;br /&gt;
#define LED_BLINK1_OFF_MS   (LED_BLINK1_PERIOD - LED_BLINK1_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK2_PERIOD   1000&lt;br /&gt;
#define LED_BLINK2_DUTY     10&lt;br /&gt;
#define LED_BLINK2_ON_MS    100&lt;br /&gt;
#define LED_BLINK2_OFF_MS   (LED_BLINK2_PERIOD - LED_BLINK2_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK3_PERIOD   1000&lt;br /&gt;
#define LED_BLINK3_DUTY     50&lt;br /&gt;
#define LED_BLINK3_ON_MS    500&lt;br /&gt;
#define LED_BLINK3_OFF_MS   (LED_BLINK3_PERIOD - LED_BLINK3_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK4_PERIOD   2000&lt;br /&gt;
#define LED_BLINK4_DUTY     5&lt;br /&gt;
#define LED_BLINK4_ON_MS    100&lt;br /&gt;
#define LED_BLINK4_OFF_MS   (LED_BLINK4_PERIOD - LED_BLINK4_ON_MS)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK_STOP      0&lt;br /&gt;
#define LED_BLINK_FOREVER   0xFF&lt;br /&gt;
&lt;br /&gt;
#define LED_ON              true&lt;br /&gt;
#define LED_OFF             false&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// typedef area&lt;br /&gt;
//&lt;br /&gt;
/*定义当前led工作状态信息*/&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  bool    ledLevelFlag;    //true -&amp;gt; high,false -&amp;gt; low&lt;br /&gt;
  uint8_t ledPointer;&lt;br /&gt;
}ledBlinkInfo_t;&lt;br /&gt;
&lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
//定义LED状态的栈空间&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t stack[LED_STATUS_END];   //栈的最大空间&lt;br /&gt;
  uint8_t num;                     //栈当前含有元素个数&lt;br /&gt;
  &lt;br /&gt;
}ledStack_t;&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// variable define&lt;br /&gt;
//&lt;br /&gt;
&lt;br /&gt;
static uint8_t ledBlinkTimesTable[LED_STATUS_END] = {0x00};&lt;br /&gt;
&lt;br /&gt;
static ledBlinkInfo_t ledInfo = {LED_OFF,0x00}; &lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
static  ledStack_t ledStack = {{0x00},0x00};&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// function define&lt;br /&gt;
//&lt;br /&gt;
/*定时次的超时回调函数*/&lt;br /&gt;
static void ledTimeoutCb(bool level);&lt;br /&gt;
&lt;br /&gt;
/*获取led定时时间*/&lt;br /&gt;
static uint16_t ledGetBlinkTime(bool level);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : initial led module&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  ledModule.fnTimerInit(ledTimeoutCb);&lt;br /&gt;
  ledModule.fnPinInit();&lt;br /&gt;
  ledInfo.ledLevelFlag = LED_OFF;&lt;br /&gt;
  ledInfo.ledPointer = NULL;&lt;br /&gt;
  memset(ledBlinkTimesTable,0,LED_STATUS_END);&lt;br /&gt;
  &lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
  memset(&amp;amp;ledStack,0,sizeof(ledStack_t));&lt;br /&gt;
  LED_StackPush(LED_STATUS_ON,0xFF);&lt;br /&gt;
  LED_StackPush(LED_STATUS_BLINK3,0xFF);&lt;br /&gt;
#else&lt;br /&gt;
  LED_Set(LED_STATUS_BLINK3,0xFF);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : set the led status&lt;br /&gt;
//&lt;br /&gt;
// param : ledStatus -&amp;gt; 设置led状态&lt;br /&gt;
//         times     -&amp;gt; Led闪烁次数，0xFF 表示一直执行，直到下一次状态改变&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Set(eLEDStatus ledStatus, uint8_t times)&lt;br /&gt;
{&lt;br /&gt;
  if(LED_STATUS_END &amp;lt;= ledStatus)&lt;br /&gt;
  {&lt;br /&gt;
    return ;&lt;br /&gt;
  }&lt;br /&gt;
//  if(times == LED_BLINK_STOP)&lt;br /&gt;
//  {&lt;br /&gt;
//    return;&lt;br /&gt;
//  }&lt;br /&gt;
  &lt;br /&gt;
  ledInfo.ledPointer = ledStatus;&lt;br /&gt;
  ledInfo.ledLevelFlag = LED_OFF;&lt;br /&gt;
  ledBlinkTimesTable[ledStatus] = times;&lt;br /&gt;
  ledModule.fnTimerStop();&lt;br /&gt;
  &lt;br /&gt;
  if(LED_BLINK_STOP == times)&lt;br /&gt;
  {&lt;br /&gt;
    ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  switch(ledStatus)&lt;br /&gt;
  {&lt;br /&gt;
  case LED_STATUS_OFF:&lt;br /&gt;
    {&lt;br /&gt;
      ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);   &lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_ON:&lt;br /&gt;
    {&lt;br /&gt;
      ledInfo.ledLevelFlag = LED_ON;&lt;br /&gt;
      ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK1:&lt;br /&gt;
    {&lt;br /&gt;
      ledInfo.ledLevelFlag = LED_ON;&lt;br /&gt;
      ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
      ledModule.fnTimerStart(LED_BLINK1_ON_MS,&amp;amp;ledInfo.ledLevelFlag);&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK2:&lt;br /&gt;
    {&lt;br /&gt;
      ledInfo.ledLevelFlag = LED_ON;&lt;br /&gt;
      ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
      ledModule.fnTimerStart(LED_BLINK2_ON_MS,&amp;amp;ledInfo.ledLevelFlag);      &lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK3:&lt;br /&gt;
    {&lt;br /&gt;
      ledInfo.ledLevelFlag = LED_ON;&lt;br /&gt;
      ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
      ledModule.fnTimerStart(LED_BLINK3_ON_MS,&amp;amp;ledInfo.ledLevelFlag);&lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK4:&lt;br /&gt;
    {&lt;br /&gt;
      ledInfo.ledLevelFlag = LED_ON;&lt;br /&gt;
      ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
      ledModule.fnTimerStart(LED_BLINK4_ON_MS,&amp;amp;ledInfo.ledLevelFlag);      &lt;br /&gt;
    }&lt;br /&gt;
    break;&lt;br /&gt;
  default :&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : LED_StackPush&lt;br /&gt;
//&lt;br /&gt;
// brief : push the led status to ledstack.it will be placed at the top&lt;br /&gt;
//&lt;br /&gt;
// param : ledStatus -&amp;gt; 设置led状态&lt;br /&gt;
//         times     -&amp;gt; Led闪烁次数，0x00 表示一直执行，直到下一次状态改变&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
bool LED_StackPush(eLEDStatus ledStatus,uint8_t times)&lt;br /&gt;
{&lt;br /&gt;
  if(LED_STATUS_END &amp;lt;= ledStatus)&lt;br /&gt;
  {&lt;br /&gt;
    return false;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(times == LED_BLINK_STOP)&lt;br /&gt;
  {&lt;br /&gt;
    return false;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if(ledStack.num &amp;gt; LED_STATUS_END)&lt;br /&gt;
  {&lt;br /&gt;
    return false;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ledStack.stack[ledStack.num] = ledStatus;&lt;br /&gt;
  ledStack.num++;&lt;br /&gt;
  LED_Set(ledStatus,times);&lt;br /&gt;
  &lt;br /&gt;
  return true;&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : LED_StackPop&lt;br /&gt;
//&lt;br /&gt;
// brief : pop the top node from ledstack&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_StackPop(void)&lt;br /&gt;
{&lt;br /&gt;
  if(ledStack.num == 0)&lt;br /&gt;
  {&lt;br /&gt;
    return;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  ledStack.num -= 1;&lt;br /&gt;
  //停止当前LED状态&lt;br /&gt;
  ledInfo.ledLevelFlag = LED_OFF;&lt;br /&gt;
  ledModule.fnTimerStop();  &lt;br /&gt;
  ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
  &lt;br /&gt;
  if(ledStack.num)&lt;br /&gt;
  {&lt;br /&gt;
    eLEDStatus led_id = (eLEDStatus)ledStack.stack[ledStack.num - 1];&lt;br /&gt;
    uint8_t times = ledBlinkTimesTable[led_id];&lt;br /&gt;
    if(times == LED_BLINK_STOP)&lt;br /&gt;
    {&lt;br /&gt;
      LED_StackPop();&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_Set(led_id,times);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : LED_StackClear&lt;br /&gt;
//&lt;br /&gt;
// brief : clear the stack of led&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_StackClear(void)&lt;br /&gt;
{&lt;br /&gt;
  ledStack.num = 0;&lt;br /&gt;
  //停止当前LED状态&lt;br /&gt;
  ledInfo.ledLevelFlag = LED_OFF;&lt;br /&gt;
  ledModule.fnTimerStop();  &lt;br /&gt;
  ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
#endif&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// static functions define area&lt;br /&gt;
//&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : ledTimeoutCb&lt;br /&gt;
//&lt;br /&gt;
// brief : the timeout callback to the led&lt;br /&gt;
//&lt;br /&gt;
// param : level -&amp;gt; true:pin high;false:pin low&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void ledTimeoutCb(bool level)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t ms = 0;&lt;br /&gt;
  if(level == LED_ON)&lt;br /&gt;
  {&lt;br /&gt;
    ledInfo.ledLevelFlag = LED_OFF;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    ledInfo.ledLevelFlag = LED_ON;&lt;br /&gt;
    if(LED_BLINK_FOREVER != ledBlinkTimesTable[ledInfo.ledPointer])&lt;br /&gt;
    {&lt;br /&gt;
      ledBlinkTimesTable[ledInfo.ledPointer] -= 1;&lt;br /&gt;
      if(ledBlinkTimesTable[ledInfo.ledPointer] == LED_BLINK_STOP)&lt;br /&gt;
      {&lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
        LED_StackPop();   //弹出当前led状态&lt;br /&gt;
        return;&lt;br /&gt;
#else&lt;br /&gt;
        return;&lt;br /&gt;
#endif&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  ms = ledGetBlinkTime(ledInfo.ledLevelFlag);&lt;br /&gt;
  ledModule.fnPinLevelSet(ledInfo.ledLevelFlag);&lt;br /&gt;
  ledModule.fnTimerStart(ms,&amp;amp;ledInfo.ledLevelFlag);  &lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : ledGetBlinkTime&lt;br /&gt;
//&lt;br /&gt;
// brief : Get the time with led status&lt;br /&gt;
//&lt;br /&gt;
// param : level -&amp;gt; true:pin high;false:pin low&lt;br /&gt;
//&lt;br /&gt;
// return : time&lt;br /&gt;
static uint16_t ledGetBlinkTime(bool level)&lt;br /&gt;
{&lt;br /&gt;
  uint16_t ms = 0;&lt;br /&gt;
  switch(ledInfo.ledPointer)&lt;br /&gt;
  {&lt;br /&gt;
  case LED_STATUS_BLINK1:&lt;br /&gt;
    ms = level ? LED_BLINK1_ON_MS : LED_BLINK1_OFF_MS;&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK2:&lt;br /&gt;
    ms = level ? LED_BLINK2_ON_MS : LED_BLINK2_OFF_MS;&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK3:&lt;br /&gt;
    ms = level ? LED_BLINK3_ON_MS : LED_BLINK3_OFF_MS;&lt;br /&gt;
    break;&lt;br /&gt;
  case LED_STATUS_BLINK4:&lt;br /&gt;
    ms = level ? LED_BLINK4_ON_MS : LED_BLINK4_OFF_MS;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  return ms;&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2507</id>
		<title>LED 闪烁控制软件模块</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2507"/>
		<updated>2019-12-09T08:22:01Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LED闪烁功能，作为常用需求。将其归纳为软件模块，与其他项目或工程共享原代码。&lt;br /&gt;
&lt;br /&gt;
=== 文件组成 ===&lt;br /&gt;
功能模块由六个文件组成，分别承载不同作用。&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!文件名称&lt;br /&gt;
!作用&lt;br /&gt;
|-&lt;br /&gt;
|bsp_status_led.c&lt;br /&gt;
|对应用层函数进行定义&lt;br /&gt;
|-&lt;br /&gt;
|bsp_status_led.h&lt;br /&gt;
|对应用层函数进行声明&lt;br /&gt;
|-&lt;br /&gt;
|ledConfig.c&lt;br /&gt;
|对硬件操作接口进行抽象&lt;br /&gt;
|-&lt;br /&gt;
|ledConfig.h&lt;br /&gt;
|对硬件操作接口进行声明&lt;br /&gt;
|-&lt;br /&gt;
|hal_led.c&lt;br /&gt;
|对硬件接口进行定义&lt;br /&gt;
|-&lt;br /&gt;
|hal_led.h&lt;br /&gt;
|对硬件接口进行声明&lt;br /&gt;
|}&lt;br /&gt;
在LED闪烁功能模块中，用到硬件包括一个GPIO引脚，一个定时器。开发者需要根据自身硬件情况，自行选择相应的硬件。并将这些硬件按要求实现相应的函数。这些函数在hal_led.h与hal_led.c文件中进行声明与定义。&lt;br /&gt;
&lt;br /&gt;
==== hal_led.h与hal_led.c ====&amp;lt;!-- hal_led.h中与硬件相关函数声明 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*定时器超时回调函数指针*/&lt;br /&gt;
typedef void (*fnTimerTimeoutCb_t)(bool);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// function declare&lt;br /&gt;
/*Create a timer*/&lt;br /&gt;
void HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb);&lt;br /&gt;
&lt;br /&gt;
/*Start timer*/&lt;br /&gt;
void HalLed_TimerStart(uint32_t ms, void *pContext);&lt;br /&gt;
/*Stop timer*/&lt;br /&gt;
void HalLed_TimerStop(void);&lt;br /&gt;
/*Pin init*/&lt;br /&gt;
void HalLed_PinInit(void);&lt;br /&gt;
/*Pin set */&lt;br /&gt;
void HalLed_PinSet(bool level);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;各函数功能及形参解释说明，如下表所示。&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!函数名&lt;br /&gt;
!解释&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb)&lt;br /&gt;
|创建一个定时器，且定时器能达到秒级的周期。&lt;br /&gt;
pFnTimeoutCb：是定时器超时回调函数指针&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_Timerstart(uint32_t ms, void *pContext)&lt;br /&gt;
|启动定时器。&lt;br /&gt;
ms：是定时周期&lt;br /&gt;
&lt;br /&gt;
pContext：传入的参数指针，会在超时回调函数中使用&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_Timerstop(void)&lt;br /&gt;
|停止定时器&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_PinInit(void)&lt;br /&gt;
|初始化与LED相连的GPIO引脚，并保持LED灭状态&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_PinSet(void)&lt;br /&gt;
|设置GPIO的高低电平，即控制LED亮灭。&lt;br /&gt;
level：true -&amp;gt;LED亮&lt;br /&gt;
&lt;br /&gt;
false -&amp;gt; LED灭&lt;br /&gt;
|}&amp;lt;!-- hal_led.c就是对上述函数进行定义。而各个函数体是与开发者的硬件相关联 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Create a time object for blink led&lt;br /&gt;
//&lt;br /&gt;
// param : pFnTimeoutCb -&amp;gt; the timeout cb&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerStart&lt;br /&gt;
//&lt;br /&gt;
// brief: Start timer&lt;br /&gt;
//&lt;br /&gt;
// param : ms -&amp;gt; the timeout ,unit = ms&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerStart(uint32_t ms,void *pContext)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerStop&lt;br /&gt;
//&lt;br /&gt;
// brief: Stop timer&lt;br /&gt;
//&lt;br /&gt;
// param : ms -&amp;gt; the timeout ,unit = ms&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerStop(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_PinInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Pin init&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_PinInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_PinInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Pin set &lt;br /&gt;
//&lt;br /&gt;
// param : level -&amp;gt; true -&amp;gt; open,false -&amp;gt; off&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_PinSet(bool level)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ledConfig.h与ledConfig.c ====&lt;br /&gt;
ledConfig.h与ledConfig.c是对LED闪烁模块的硬件进行抽象，即hal_led.h与hal_led.c中定义的功能函数。&amp;lt;!-- ledConfig.h抽象接口声明 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*定时器初始化函数指针*/&lt;br /&gt;
typedef void (*fnTimerInit_t)(fnTimerTimeoutCb_t);&lt;br /&gt;
/*定时器启动函数指针*/&lt;br /&gt;
typedef void (*fnTimerStart_t)(uint32_t ms,void*pContext);&lt;br /&gt;
&lt;br /&gt;
/*定时器停止函数指针*/&lt;br /&gt;
typedef void (*fnTimerStop_t)(void);&lt;br /&gt;
&lt;br /&gt;
/*Led 引脚初始化函数指针*/&lt;br /&gt;
typedef void (*fnPinInit_t)(void);&lt;br /&gt;
/*Led 引脚高低电平设置函数指针*/&lt;br /&gt;
typedef void (*fnPinLevelSet_t)(bool level);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : ledBlinkCfg_t&lt;br /&gt;
//&lt;br /&gt;
// Brief : 描述LED闪烁控制接口函数&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  fnTimerInit_t    fnTimerInit;&lt;br /&gt;
  fnTimerStart_t   fnTimerStart;&lt;br /&gt;
  fnTimerStop_t    fnTimerStop;&lt;br /&gt;
  fnPinInit_t      fnPinInit;&lt;br /&gt;
  fnPinLevelSet_t  fnPinLevelSet;&lt;br /&gt;
  &lt;br /&gt;
}ledModuleCfg_t;&lt;br /&gt;
  &lt;br /&gt;
extern ledModuleCfg_t  ledModule;  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;hal_led.h就是根据ledConfig.h声明函数接口进行定义的。&lt;br /&gt;
&lt;br /&gt;
其中结构体ledModuleCfg_t就是对硬件层进行抽象。其中包括定时器定义，定时器启动，定时器停止，引脚gpio初始化，引脚高低电平设置。&amp;lt;!-- ledConfig.c是ledModuleCfg_t进行初始化 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*led operate interface*/&lt;br /&gt;
ledModuleCfg_t  ledModule = {&lt;br /&gt;
  .fnTimerInit   = HalLed_TimerInit,&lt;br /&gt;
  .fnTimerStart  = HalLed_TimerStart,&lt;br /&gt;
  .fnTimerStop   = HalLed_TimerStop,&lt;br /&gt;
  .fnPinInit     = HalLed_PinInit,&lt;br /&gt;
  .fnPinLevelSet = HalLed_PinSet&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bsp_status_led.h 与bsp_status_led.c ====&lt;br /&gt;
这两个文件是开发者可以调用函数的声明与定义。开发者可以调用相应函数对LED闪烁状态进行设置。led支持如下6种状态。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  LED_STATUS_OFF,          //关闭&lt;br /&gt;
  LED_STATUS_ON,           //常亮&lt;br /&gt;
  LED_STATUS_BLINK1,       //闪烁1，周期200ms,占空比50：亮100，灭100&lt;br /&gt;
  LED_STATUS_BLINK2,       //闪烁2，周期1000，占空比10：亮100，灭900&lt;br /&gt;
  LED_STATUS_BLINK3,       //闪烁3，周期1000，占空比50：亮500，灭500&lt;br /&gt;
  LED_STATUS_BLINK4,       //闪烁4，周期2000，占空比5： 亮100，灭1900&lt;br /&gt;
  LED_STATUS_END&lt;br /&gt;
}eLEDStatus;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;当然开发者也可以，根据自己的需求，对led闪烁状态进行修改。包括周期，占空比。具体修改的地方在bsp_status_led.c文件头部。各个闪烁状态由宏定义定义，如下。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_BLINK1_PERIOD   200&lt;br /&gt;
#define LED_BLINK1_DUTY     50&lt;br /&gt;
#define LED_BLINK1_ON_MS    100&lt;br /&gt;
#define LED_BLINK1_OFF_MS   (LED_BLINK1_PERIOD - LED_BLINK1_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK2_PERIOD   1000&lt;br /&gt;
#define LED_BLINK2_DUTY     10&lt;br /&gt;
#define LED_BLINK2_ON_MS    100&lt;br /&gt;
#define LED_BLINK2_OFF_MS   (LED_BLINK2_PERIOD - LED_BLINK2_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK3_PERIOD   1000&lt;br /&gt;
#define LED_BLINK3_DUTY     50&lt;br /&gt;
#define LED_BLINK3_ON_MS    500&lt;br /&gt;
#define LED_BLINK3_OFF_MS   (LED_BLINK3_PERIOD - LED_BLINK3_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK4_PERIOD   2000&lt;br /&gt;
#define LED_BLINK4_DUTY     5&lt;br /&gt;
#define LED_BLINK4_ON_MS    100&lt;br /&gt;
#define LED_BLINK4_OFF_MS   (LED_BLINK4_PERIOD - LED_BLINK4_ON_MS)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 闪烁次数定义 =====&lt;br /&gt;
在代码中，各个闪烁状态都有相应的次数限制。其中0表示此状态闪烁结束，将进行下一个状态，0xFF表示永久闪烁，直到当前状态被人为改变，即闪烁次数被置零，或改为其他数值；或被弹出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_BLINK_STOP      0&lt;br /&gt;
#define LED_BLINK_FOREVER   0xFF&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===== 应用函数声明 =====&lt;br /&gt;
在用户层提供了两种方式。一种是普通模式，一种是栈模式。&lt;br /&gt;
&lt;br /&gt;
普通模式中，开发者只需调用LED_Set函数，即可对当前led状态进行设置。状态结束后，led处在灭状态，等待用户进行下个状态设置。而在栈模式中，开发者调用LED_StackPush函数时，会将led状态压入栈中，并设置led状态。当led闪烁状态结束后，继续之前led状态，直到栈空为至。当然在这个过程中开发者也可以调用LED_Set函数，对当前led闪烁状态进行设置。只是led闪烁状态结束后会继续栈的上个led状态，而不会继续LED_Set函数调用前的状态。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*initial led module*/&lt;br /&gt;
void LED_Init(void);&lt;br /&gt;
/*set the led status*/&lt;br /&gt;
void LED_Set(eLEDStatus,uint8_t times);&lt;br /&gt;
&lt;br /&gt;
#ifdef LED_PRORITY_EN&lt;br /&gt;
/*push the led status to ledstack.it will be placed at the top*/&lt;br /&gt;
bool LED_StackPush(eLEDStatus,uint8_t times);&lt;br /&gt;
&lt;br /&gt;
/*pop the top node from ledstack*/&lt;br /&gt;
void LED_StackPop(void);&lt;br /&gt;
&lt;br /&gt;
/*clear stack*/&lt;br /&gt;
void LED_StackClear(void);&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中与栈有关的函数是通过宏LED_PRORITY_EN来控制。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!函数名&lt;br /&gt;
!解释&lt;br /&gt;
!&lt;br /&gt;
|-&lt;br /&gt;
|void LED_Init(void)&lt;br /&gt;
|对LED闪烁模块进行初始化。在任何函数调用之前进行调用&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|void LED_Set( eLEDStatus status，uint8_t times)&lt;br /&gt;
|设置led闪烁状态&lt;br /&gt;
status：设置的led状态&lt;br /&gt;
&lt;br /&gt;
times：闪烁次数&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|bool LED_StackPush(eLEDStatus status, uint8_t times)&lt;br /&gt;
|设置led闪烁状态，并压入栈中&lt;br /&gt;
status：设置的led状态&lt;br /&gt;
&lt;br /&gt;
times：闪烁次数&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|void LED_StackPop(void)&lt;br /&gt;
|弹出栈顶状态&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|void LED_StackClear(void)&lt;br /&gt;
|清空栈里所有led状态，并将led设置灭状态&lt;br /&gt;
|&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2506</id>
		<title>LED 闪烁控制软件模块</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2506"/>
		<updated>2019-12-09T07:25:49Z</updated>

		<summary type="html">&lt;p&gt;Erjin：初版正在进行中&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LED闪烁功能，作为常用需求。将其归纳为软件模块，与其他项目或工程共享原代码。&lt;br /&gt;
&lt;br /&gt;
=== 文件组成 ===&lt;br /&gt;
功能模块由六个文件组成，分别承载不同作用。&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!文件名称&lt;br /&gt;
!作用&lt;br /&gt;
|-&lt;br /&gt;
|bsp_status_led.c&lt;br /&gt;
|对应用层函数进行定义&lt;br /&gt;
|-&lt;br /&gt;
|bsp_status_led.h&lt;br /&gt;
|对应用层函数进行声明&lt;br /&gt;
|-&lt;br /&gt;
|ledConfig.c&lt;br /&gt;
|对硬件操作接口进行抽象&lt;br /&gt;
|-&lt;br /&gt;
|ledConfig.h&lt;br /&gt;
|对硬件操作接口进行声明&lt;br /&gt;
|-&lt;br /&gt;
|hal_led.c&lt;br /&gt;
|对硬件接口进行定义&lt;br /&gt;
|-&lt;br /&gt;
|hal_led.h&lt;br /&gt;
|对硬件接口进行声明&lt;br /&gt;
|}&lt;br /&gt;
在LED闪烁功能模块中，用到硬件包括一个GPIO引脚，一个定时器。开发者需要根据自身硬件情况，自行选择相应的硬件。并将这些硬件按要求实现相应的函数。这些函数在hal_led.h与hal_led.c文件中进行声明与定义。&lt;br /&gt;
&lt;br /&gt;
==== hal_led.h与hal_led.c ====&amp;lt;!-- hal_led.h中与硬件相关函数声明 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*定时器超时回调函数指针*/&lt;br /&gt;
typedef void (*fnTimerTimeoutCb_t)(bool);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// function declare&lt;br /&gt;
/*Create a timer*/&lt;br /&gt;
void HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb);&lt;br /&gt;
&lt;br /&gt;
/*Start timer*/&lt;br /&gt;
void HalLed_TimerStart(uint32_t ms, void *pContext);&lt;br /&gt;
/*Stop timer*/&lt;br /&gt;
void HalLed_TimerStop(void);&lt;br /&gt;
/*Pin init*/&lt;br /&gt;
void HalLed_PinInit(void);&lt;br /&gt;
/*Pin set */&lt;br /&gt;
void HalLed_PinSet(bool level);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;各函数功能及形参解释说明，如下表所示。&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!函数名&lt;br /&gt;
!解释&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb)&lt;br /&gt;
|创建一个定时器，且定时器能达到秒级的周期。&lt;br /&gt;
pFnTimeoutCb：是定时器超时回调函数指针&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_Timerstart(uint32_t ms, void *pContext)&lt;br /&gt;
|启动定时器。&lt;br /&gt;
ms：是定时周期&lt;br /&gt;
&lt;br /&gt;
pContext：传入的参数指针，会在超时回调函数中使用&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_Timerstop(void)&lt;br /&gt;
|停止定时器&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_PinInit(void)&lt;br /&gt;
|初始化与LED相连的GPIO引脚，并保持LED灭状态&lt;br /&gt;
|-&lt;br /&gt;
|HalLed_PinSet(void)&lt;br /&gt;
|设置GPIO的高低电平，即控制LED亮灭。&lt;br /&gt;
level：true -&amp;gt;LED亮&lt;br /&gt;
&lt;br /&gt;
false -&amp;gt; LED灭&lt;br /&gt;
|}&amp;lt;!-- hal_led.c就是对上述函数进行定义。而各个函数体是与开发者的硬件相关联 --&amp;gt;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Create a time object for blink led&lt;br /&gt;
//&lt;br /&gt;
// param : pFnTimeoutCb -&amp;gt; the timeout cb&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerInit(fnTimerTimeoutCb_t pFnTimeoutCb)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerStart&lt;br /&gt;
//&lt;br /&gt;
// brief: Start timer&lt;br /&gt;
//&lt;br /&gt;
// param : ms -&amp;gt; the timeout ,unit = ms&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerStart(uint32_t ms,void *pContext)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_TimerStop&lt;br /&gt;
//&lt;br /&gt;
// brief: Stop timer&lt;br /&gt;
//&lt;br /&gt;
// param : ms -&amp;gt; the timeout ,unit = ms&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_TimerStop(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_PinInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Pin init&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_PinInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : HalLed_PinInit&lt;br /&gt;
//&lt;br /&gt;
// brief: Pin set &lt;br /&gt;
//&lt;br /&gt;
// param : level -&amp;gt; true -&amp;gt; open,false -&amp;gt; off&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void HalLed_PinSet(bool level)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== ledConfig.h与ledConfig.c ====&lt;br /&gt;
ledConfig.h与ledConfig.c是对LED闪烁模块的硬件进行抽象，即hal_led.h与hal_led.c中定义的功能函数。&amp;lt;!-- ledConfig.h抽象接口声明 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*定时器初始化函数指针*/&lt;br /&gt;
typedef void (*fnTimerInit_t)(fnTimerTimeoutCb_t);&lt;br /&gt;
/*定时器启动函数指针*/&lt;br /&gt;
typedef void (*fnTimerStart_t)(uint32_t ms,void*pContext);&lt;br /&gt;
&lt;br /&gt;
/*定时器停止函数指针*/&lt;br /&gt;
typedef void (*fnTimerStop_t)(void);&lt;br /&gt;
&lt;br /&gt;
/*Led 引脚初始化函数指针*/&lt;br /&gt;
typedef void (*fnPinInit_t)(void);&lt;br /&gt;
/*Led 引脚高低电平设置函数指针*/&lt;br /&gt;
typedef void (*fnPinLevelSet_t)(bool level);&lt;br /&gt;
&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : ledBlinkCfg_t&lt;br /&gt;
//&lt;br /&gt;
// Brief : 描述LED闪烁控制接口函数&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  fnTimerInit_t    fnTimerInit;&lt;br /&gt;
  fnTimerStart_t   fnTimerStart;&lt;br /&gt;
  fnTimerStop_t    fnTimerStop;&lt;br /&gt;
  fnPinInit_t      fnPinInit;&lt;br /&gt;
  fnPinLevelSet_t  fnPinLevelSet;&lt;br /&gt;
  &lt;br /&gt;
}ledModuleCfg_t;&lt;br /&gt;
  &lt;br /&gt;
extern ledModuleCfg_t  ledModule;  &lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;hal_led.h就是根据ledConfig.h声明函数接口进行定义的。&lt;br /&gt;
&lt;br /&gt;
其中结构体ledModuleCfg_t就是对硬件层进行抽象。其中包括定时器定义，定时器启动，定时器停止，引脚gpio初始化，引脚高低电平设置。&amp;lt;!-- ledConfig.c是ledModuleCfg_t进行初始化 --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
/*led operate interface*/&lt;br /&gt;
ledModuleCfg_t  ledModule = {&lt;br /&gt;
  .fnTimerInit   = HalLed_TimerInit,&lt;br /&gt;
  .fnTimerStart  = HalLed_TimerStart,&lt;br /&gt;
  .fnTimerStop   = HalLed_TimerStop,&lt;br /&gt;
  .fnPinInit     = HalLed_PinInit,&lt;br /&gt;
  .fnPinLevelSet = HalLed_PinSet&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== bsp_status_led.h 与bsp_status_led.c ====&lt;br /&gt;
这两个文件是开发者可以调用函数的声明与定义。开发者可以调用相应函数对LED闪烁状态进行设置。led支持如下6种状态。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  LED_STATUS_OFF,          //关闭&lt;br /&gt;
  LED_STATUS_ON,           //常亮&lt;br /&gt;
  LED_STATUS_BLINK1,       //闪烁1，周期200ms,占空比50：亮100，灭100&lt;br /&gt;
  LED_STATUS_BLINK2,       //闪烁2，周期1000，占空比10：亮100，灭900&lt;br /&gt;
  LED_STATUS_BLINK3,       //闪烁3，周期1000，占空比50：亮500，灭500&lt;br /&gt;
  LED_STATUS_BLINK4,       //闪烁4，周期2000，占空比5： 亮100，灭1900&lt;br /&gt;
  LED_STATUS_END&lt;br /&gt;
}eLEDStatus;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;当然开发者也可以，根据自己的需求，对led闪烁状态进行修改。包括周期，占空比。具体修改的地方在bsp_status_led.c文件头部。各个闪烁状态由宏定义定义，如下。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define LED_BLINK1_PERIOD   200&lt;br /&gt;
#define LED_BLINK1_DUTY     50&lt;br /&gt;
#define LED_BLINK1_ON_MS    100&lt;br /&gt;
#define LED_BLINK1_OFF_MS   (LED_BLINK1_PERIOD - LED_BLINK1_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK2_PERIOD   1000&lt;br /&gt;
#define LED_BLINK2_DUTY     10&lt;br /&gt;
#define LED_BLINK2_ON_MS    100&lt;br /&gt;
#define LED_BLINK2_OFF_MS   (LED_BLINK2_PERIOD - LED_BLINK2_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK3_PERIOD   1000&lt;br /&gt;
#define LED_BLINK3_DUTY     50&lt;br /&gt;
#define LED_BLINK3_ON_MS    500&lt;br /&gt;
#define LED_BLINK3_OFF_MS   (LED_BLINK3_PERIOD - LED_BLINK3_ON_MS)&lt;br /&gt;
&lt;br /&gt;
#define LED_BLINK4_PERIOD   2000&lt;br /&gt;
#define LED_BLINK4_DUTY     5&lt;br /&gt;
#define LED_BLINK4_ON_MS    100&lt;br /&gt;
#define LED_BLINK4_OFF_MS   (LED_BLINK4_PERIOD - LED_BLINK4_ON_MS)&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2504</id>
		<title>LED 闪烁控制软件模块</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2504"/>
		<updated>2019-12-09T03:55:45Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LED闪烁功能，作为常用需求。将其归纳为软件模块，与其他项目或工程共享原代码。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2503</id>
		<title>LED 闪烁控制软件模块</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=LED_%E9%97%AA%E7%83%81%E6%8E%A7%E5%88%B6%E8%BD%AF%E4%BB%B6%E6%A8%A1%E5%9D%97&amp;diff=2503"/>
		<updated>2019-12-09T03:50:01Z</updated>

		<summary type="html">&lt;p&gt;Erjin：第一次创建&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;LED闪烁功能，作为常用需求。将其总结一个软件模块&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1781</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1781"/>
		<updated>2019-07-18T07:07:45Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* flash readwrite*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|-&lt;br /&gt;
|10_温度实验&lt;br /&gt;
|Temperture&lt;br /&gt;
|芯片上温度传感器&lt;br /&gt;
|-&lt;br /&gt;
|11_Flash操作&lt;br /&gt;
|NVMC&lt;br /&gt;
|片上Flash操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;br /&gt;
&lt;br /&gt;
=== 温度传感器实验 ===&lt;br /&gt;
NRF52832芯片内部，自带一个温度传感器，并有线性补尝。它最大的分辨率为0.25度。本实验利用温度传感测量芯片周围环境的温度。它工作不需要外围电路。&lt;br /&gt;
&lt;br /&gt;
温度测量通过触发TASK_START，当完成测量时DATARDY事件将会产生。通过读取TEMP寄存器，读取当前温度。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中10_temp_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  int32_t volatile temp = INT32_MAX;&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  &lt;br /&gt;
  UpdateLCD(temp);&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_START = 1;  //启动温度测量任务&lt;br /&gt;
    while (NRF_TEMP-&amp;gt;EVENTS_DATARDY == 0)&lt;br /&gt;
    {&lt;br /&gt;
        // Do nothing.&lt;br /&gt;
    }&lt;br /&gt;
    NRF_TEMP-&amp;gt;EVENTS_DATARDY = 0;&lt;br /&gt;
    //读取温度值&lt;br /&gt;
    temp = (nrf_temp_read() / 4);&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_STOP = 1;    //停止测量任务&lt;br /&gt;
    UpdateLCD(temp);&lt;br /&gt;
    &lt;br /&gt;
    LED_Toggle(0);               //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数前部，对用到的外设进行初始化，包括LED，LCD。在for循环中，启动，停止温度测量。RNF_TEMP-&amp;gt;TASKS_START置1，将启动温度测量，NRF_TEMP-&amp;gt;EVENTS_DATARDY是测量完成事件，如果DATARDY事件置1，表明温度测量已经完成。nrf_temp_read函数将读取TEMP寄存器返回当前温度值。其值除以4，即丢弃小数部分。&lt;br /&gt;
&lt;br /&gt;
UpdateLCD函数，用于改变屏幕的内部。其输入参数为当前的温度值。在其内部对温度区间进行划分，小于20度属于NORMAL，显示屏会显示绿色背景；在20到27度之间，属于WARN，显示屏会显示黄色；大于27度，属于EMERGENT，显示屏会显示红色。同时也会显示当前温度值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  TEMP_DEFAULT,&lt;br /&gt;
  TEMP_NORMAL,&lt;br /&gt;
  TEMP_WARN,&lt;br /&gt;
  TEMP_EMERGENT&lt;br /&gt;
}TEMP_RANGE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void UpdateLCD(int32_t temp)&lt;br /&gt;
{&lt;br /&gt;
  static TEMP_RANGE LEVEL = TEMP_DEFAULT;&lt;br /&gt;
  bool isChange = false;&lt;br /&gt;
  if(temp == INT32_MAX)&lt;br /&gt;
  {&lt;br /&gt;
    LEVEL = TEMP_DEFAULT;&lt;br /&gt;
    isChange = true ;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    if(temp &amp;lt; 20)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_NORMAL)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_NORMAL;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    else if(temp &amp;lt; 27)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_WARN)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_WARN;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_EMERGENT)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_EMERGENT;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(isChange)&lt;br /&gt;
  {&lt;br /&gt;
    switch(LEVEL)&lt;br /&gt;
    {&lt;br /&gt;
      case TEMP_DEFAULT: &lt;br /&gt;
        GUI_SetBkColor(GUI_WHITE);//设置蓝色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_NORMAL:&lt;br /&gt;
        GUI_SetBkColor(GUI_GREEN);//设置蓝色背景色   &lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_WARN:&lt;br /&gt;
        GUI_SetBkColor(GUI_YELLOW);//设置绿色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      default:&lt;br /&gt;
        GUI_SetBkColor(GUI_RED);//设置红色背景色&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispString(&amp;quot;Temperture\r\nExample&amp;quot;); &lt;br /&gt;
    isChange = false;&lt;br /&gt;
  }&lt;br /&gt;
  if(LEVEL)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;Temp:%dC&amp;quot;,temp);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示当前的温度值。并根据温度值，显示屏的背景色也会改变。&lt;br /&gt;
&lt;br /&gt;
=== Flash读写操作 ===&lt;br /&gt;
一般MCU都有两种类型的存储单元：易失性存储单元和非易失性存储单元。易失性存储单元一般俗称RAM，非易失性存储单元一般俗称ROM。它们的特性这里不做详细说明。本例程中主要描述与操作对象为FLASH，即ROM。在NRF52832芯片中，操作FLASH是通过NVMC控制器进行管理。它们都会被芯片映射到地址空间中，如下图所示。&lt;br /&gt;
[[文件:Addr space.png|居中|缩略图|758x758像素]]&lt;br /&gt;
NRF52832使用Code区域的低地址空间映射Flash，作为代码区域。除了可以存储CPU执行程序外，也可以用于存储数据常量。NRF52832片上共有512K字节大小的Flash，分为128页，每页大小为4K。每页分为8个块，每块大小512字节。&lt;br /&gt;
&lt;br /&gt;
对Flash进行写数据，要先擦除，然后再写入（必须要字对齐）。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中11_flash_readwrite_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  clock_config();                          //初始化时钟&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();                              //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);            //初始化串口&lt;br /&gt;
  Flash_info_init();&lt;br /&gt;
  bsp_board_lcd_init();                    //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                              //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Flash Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  printf(&amp;quot;Flash Test\r\n&amp;quot;);&lt;br /&gt;
  Flash_info_out();&lt;br /&gt;
  printf(CLI_PROMPT_STRINGS);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    APP_UartPoll();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对用到的外设和软件模块进行初始化。其中flash_info_out函数获取芯片的flash信息，包括页数，页大小，flash大小，RAM大小等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
static void Flash_info_out(void)&lt;br /&gt;
{&lt;br /&gt;
  uint32_t codesize = NRF_FICR-&amp;gt;CODESIZE;&lt;br /&gt;
  uint32_t codepagesize = NRF_FICR-&amp;gt;CODEPAGESIZE;&lt;br /&gt;
  uint32_t ramsize = NRF_FICR-&amp;gt;INFO.RAM;&lt;br /&gt;
  uint32_t romsize = NRF_FICR-&amp;gt;INFO.FLASH;&lt;br /&gt;
  uint32_t deviceid0 = NRF_FICR-&amp;gt;DEVICEID[0];&lt;br /&gt;
  uint32_t deviceid1 = NRF_FICR-&amp;gt;DEVICEID[1];&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
  //打印芯片信息&lt;br /&gt;
  printf(&amp;quot;FLASH INFO:\r\npage num :%d\r\npage size:%d\r\nram size:%dk\r\nflash size:%dk\r\ndevice id0:0x%08X\r\ndevice id1:0x%08X\r\n&amp;quot;, &lt;br /&gt;
           codesize,&lt;br /&gt;
           codepagesize,&lt;br /&gt;
           ramsize,&lt;br /&gt;
           romsize,&lt;br /&gt;
           deviceid0,&lt;br /&gt;
           deviceid1&lt;br /&gt;
           );&lt;br /&gt;
 &lt;br /&gt;
  GUI_Disprintf(&amp;quot;\r\nPNum:%d\r\n&amp;quot;,flash_info.page_num);&lt;br /&gt;
  GUI_Disprintf(&amp;quot;PSize:%d\r\n&amp;quot;,flash_info.page_size);&lt;br /&gt;
  GUI_Disprintf(&amp;quot;Addr:0x%X\r\n&amp;quot;,flash_info.opt_addr);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;CLI_PROMPT_STRINGS是命令行提示字符，用于提示命令输入。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define CLI_PROMPT_STRINGS   &amp;quot;\r\nNRF_Flash#:&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;所有串口命令处理都在APP_UartPoll函数中。有关串口低层收发处理，此例程中不作说明，开发者可以查看《08_UART收发实验》例程。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
static int APP_UartPoll(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len = 0;&lt;br /&gt;
  switch(uart_evt.evt_type)&lt;br /&gt;
  {&lt;br /&gt;
    case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
    case UART_EVT_RX_OVERFLOW:&lt;br /&gt;
      //检查是否将会溢出&lt;br /&gt;
      if((cmd_buf.len + uart_evt.status) &amp;gt; (USER_CMD_MAX_LEN -1))&lt;br /&gt;
      {&lt;br /&gt;
        len = UART_BUF_SIZE - 1 - cmd_buf.len;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        len = uart_evt.status;&lt;br /&gt;
      }&lt;br /&gt;
      //回显&lt;br /&gt;
      len = UART_Read(cmd_buf.Buf + cmd_buf.len,len);&lt;br /&gt;
      UART_Write(cmd_buf.Buf + cmd_buf.len,len);&lt;br /&gt;
      cmd_buf.len += len;&lt;br /&gt;
      cmd_buf.Buf[cmd_buf.len] = 0;&lt;br /&gt;
      &lt;br /&gt;
      //进行命令解析&lt;br /&gt;
      if((cmd_buf.len &amp;gt;= USER_CMD_MAX_LEN) || (strchr((char const*)cmd_buf.Buf,'\r')))&lt;br /&gt;
      {&lt;br /&gt;
        printf(&amp;quot;\r\n&amp;quot;);&lt;br /&gt;
        if(CLI_Process(cmd_buf.Buf))&lt;br /&gt;
        {&lt;br /&gt;
          printf(&amp;quot;error\r\n&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        cmd_buf.len = 0;&lt;br /&gt;
        printf(CLI_PROMPT_STRINGS);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
      break;&lt;br /&gt;
  default :&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  return len;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;串口在收到控制台发来数据后，通过UART_EVT_RX_TIMEOUT事件回调到上层，并将收到数据回显到控制台，以显示开发者输入字符。接下来就是对命令数据进行结束识别。结束条件分为两种：一是最大长度为USER_CMD_MAX_LEN；二是回车换行符。&lt;br /&gt;
&lt;br /&gt;
最终对命令进行解析的是CLI_Process函数。利用strtok库函数对命令数据进行分割，分割字符为空格。所以开发者在命令输入时，要以空格作为分隔。最后将分割的数据与个数传入CMD_ExcuteHandle函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
CMD_CODE_T CLI_Process(uint8_t* buf)&lt;br /&gt;
{&lt;br /&gt;
  char *pTmp = NULL;&lt;br /&gt;
  char* argv[10];&lt;br /&gt;
  size_t count = 0;&lt;br /&gt;
  &lt;br /&gt;
  if(buf == NULL)&lt;br /&gt;
  {&lt;br /&gt;
    //数据内容为空，直接返回&lt;br /&gt;
    return CMD_BAD_BUF;&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  pTmp = (char*)strchr((char const*)buf,'\r');&lt;br /&gt;
  if(pTmp)&lt;br /&gt;
  {&lt;br /&gt;
    *pTmp = '\0';&lt;br /&gt;
  }&lt;br /&gt;
  pTmp = (char*)buf;&lt;br /&gt;
  &lt;br /&gt;
  //提取指令返回的参数，此时buf已经遭到破坏&lt;br /&gt;
  while((argv[count] = strtok(pTmp,&amp;quot; &amp;quot;)) != NULL)&lt;br /&gt;
  {&lt;br /&gt;
    count++;&lt;br /&gt;
    pTmp = NULL;&lt;br /&gt;
    if(count &amp;gt;=10)&lt;br /&gt;
    {&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  return CMD_ExcuteHandle(count,argv);&lt;br /&gt;
  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;CMD_ExcuteHandle函数会根据输入参数查找命令库。如果查找到将执行命令，否则返回CMD_NOT_FIND。&lt;br /&gt;
&lt;br /&gt;
与命令相关的数据结构如下。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef void (*cmd_handler)(size_t argc, char **argv);&lt;br /&gt;
&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  const char*  cmd;       //cmd&lt;br /&gt;
  cmd_handler  handler;   //handler&lt;br /&gt;
}cmd_item_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中cmd是命令名字，handler是命令执行函数。所以开发者，想要实现命令，只要向cmd_item中添加命令名字与处理函数即可，如下。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//增加命令条目&lt;br /&gt;
const cmd_item_t cmd_item[] = &lt;br /&gt;
{&lt;br /&gt;
  {STRINGIFY(read),Flash_read_cmd},&lt;br /&gt;
  {STRINGIFY(write),Flash_write_cmd},&lt;br /&gt;
  {STRINGIFY(erase),Flash_erase_cmd},&lt;br /&gt;
  {NULL,NULL}&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显地当前操作Flash信息，包括页号，此页大小及所在地址。而此时控制台也会收到相应的信息。&lt;br /&gt;
[[文件:Flash uart reset.png|居中|缩略图|547x547像素]]&lt;br /&gt;
开发者可以根据命令条目，输入命令进行简单调试。read -&amp;gt; write -&amp;gt; read。&lt;br /&gt;
[[文件:Flash readwrite.png|居中|缩略图|720x720像素]]&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Flash_readwrite.png&amp;diff=1780</id>
		<title>文件:Flash readwrite.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Flash_readwrite.png&amp;diff=1780"/>
		<updated>2019-07-18T07:06:04Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;debug&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Flash_uart_reset.png&amp;diff=1779</id>
		<title>文件:Flash uart reset.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Flash_uart_reset.png&amp;diff=1779"/>
		<updated>2019-07-18T07:01:25Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;por uart get&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1778</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1778"/>
		<updated>2019-07-16T04:08:41Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* nRF52832DK基础实验说明列表 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|-&lt;br /&gt;
|10_温度实验&lt;br /&gt;
|Temperture&lt;br /&gt;
|芯片上温度传感器&lt;br /&gt;
|-&lt;br /&gt;
|11_Flash操作&lt;br /&gt;
|NVMC&lt;br /&gt;
|片上Flash操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;br /&gt;
&lt;br /&gt;
=== 温度传感器实验 ===&lt;br /&gt;
NRF52832芯片内部，自带一个温度传感器，并有线性补尝。它最大的分辨率为0.25度。本实验利用温度传感测量芯片周围环境的温度。它工作不需要外围电路。&lt;br /&gt;
&lt;br /&gt;
温度测量通过触发TASK_START，当完成测量时DATARDY事件将会产生。通过读取TEMP寄存器，读取当前温度。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中10_temp_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  int32_t volatile temp = INT32_MAX;&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  &lt;br /&gt;
  UpdateLCD(temp);&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_START = 1;  //启动温度测量任务&lt;br /&gt;
    while (NRF_TEMP-&amp;gt;EVENTS_DATARDY == 0)&lt;br /&gt;
    {&lt;br /&gt;
        // Do nothing.&lt;br /&gt;
    }&lt;br /&gt;
    NRF_TEMP-&amp;gt;EVENTS_DATARDY = 0;&lt;br /&gt;
    //读取温度值&lt;br /&gt;
    temp = (nrf_temp_read() / 4);&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_STOP = 1;    //停止测量任务&lt;br /&gt;
    UpdateLCD(temp);&lt;br /&gt;
    &lt;br /&gt;
    LED_Toggle(0);               //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数前部，对用到的外设进行初始化，包括LED，LCD。在for循环中，启动，停止温度测量。RNF_TEMP-&amp;gt;TASKS_START置1，将启动温度测量，NRF_TEMP-&amp;gt;EVENTS_DATARDY是测量完成事件，如果DATARDY事件置1，表明温度测量已经完成。nrf_temp_read函数将读取TEMP寄存器返回当前温度值。其值除以4，即丢弃小数部分。&lt;br /&gt;
&lt;br /&gt;
UpdateLCD函数，用于改变屏幕的内部。其输入参数为当前的温度值。在其内部对温度区间进行划分，小于20度属于NORMAL，显示屏会显示绿色背景；在20到27度之间，属于WARN，显示屏会显示黄色；大于27度，属于EMERGENT，显示屏会显示红色。同时也会显示当前温度值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  TEMP_DEFAULT,&lt;br /&gt;
  TEMP_NORMAL,&lt;br /&gt;
  TEMP_WARN,&lt;br /&gt;
  TEMP_EMERGENT&lt;br /&gt;
}TEMP_RANGE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void UpdateLCD(int32_t temp)&lt;br /&gt;
{&lt;br /&gt;
  static TEMP_RANGE LEVEL = TEMP_DEFAULT;&lt;br /&gt;
  bool isChange = false;&lt;br /&gt;
  if(temp == INT32_MAX)&lt;br /&gt;
  {&lt;br /&gt;
    LEVEL = TEMP_DEFAULT;&lt;br /&gt;
    isChange = true ;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    if(temp &amp;lt; 20)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_NORMAL)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_NORMAL;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    else if(temp &amp;lt; 27)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_WARN)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_WARN;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_EMERGENT)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_EMERGENT;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(isChange)&lt;br /&gt;
  {&lt;br /&gt;
    switch(LEVEL)&lt;br /&gt;
    {&lt;br /&gt;
      case TEMP_DEFAULT: &lt;br /&gt;
        GUI_SetBkColor(GUI_WHITE);//设置蓝色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_NORMAL:&lt;br /&gt;
        GUI_SetBkColor(GUI_GREEN);//设置蓝色背景色   &lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_WARN:&lt;br /&gt;
        GUI_SetBkColor(GUI_YELLOW);//设置绿色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      default:&lt;br /&gt;
        GUI_SetBkColor(GUI_RED);//设置红色背景色&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispString(&amp;quot;Temperture\r\nExample&amp;quot;); &lt;br /&gt;
    isChange = false;&lt;br /&gt;
  }&lt;br /&gt;
  if(LEVEL)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;Temp:%dC&amp;quot;,temp);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示当前的温度值。并根据温度值，显示屏的背景色也会改变。&lt;br /&gt;
&lt;br /&gt;
=== Flash读写操作 ===&lt;br /&gt;
一般MCU都有两种类型的存储单元：易失性存储单元和非易失性存储单元。易失性存储单元一般俗称RAM，非易失性存储单元一般俗称ROM。它们的特性这里不做详细说明。本例程中主要描述与操作对象为FLASH，即ROM。在NRF52832芯片中，操作FLASH是通过NVMC控制器进行管理。它们都会被芯片映射到地址空间中，如下图所示。&lt;br /&gt;
[[文件:Addr space.png|居中|缩略图|758x758像素]]&lt;br /&gt;
NRF52832使用Code区域的低地址空间映射Flash，作为代码区域。除了可以存储CPU执行程序外，也可以用于存储数据常量。NRF52832片上共有512K字节大小的Flash，分为128页，每页大小为4K。每页分为8个块，每块大小512字节。&lt;br /&gt;
&lt;br /&gt;
对Flash进行写数据，要先擦除，然后再写入（必须要字对齐）。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Addr_space.png&amp;diff=1777</id>
		<title>文件:Addr space.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Addr_space.png&amp;diff=1777"/>
		<updated>2019-07-16T03:47:27Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nrf52832 address space&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1776</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1776"/>
		<updated>2019-07-15T09:49:39Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* nRF52832DK基础实验说明列表 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|-&lt;br /&gt;
|10_温度实验&lt;br /&gt;
|Temperture&lt;br /&gt;
|芯片上温度传感器&lt;br /&gt;
|-&lt;br /&gt;
|11_Flash操作&lt;br /&gt;
|NVNC&lt;br /&gt;
|片上Flash操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;br /&gt;
&lt;br /&gt;
=== 温度传感器实验 ===&lt;br /&gt;
NRF52832芯片内部，自带一个温度传感器，并有线性补尝。它最大的分辨率为0.25度。本实验利用温度传感测量芯片周围环境的温度。它工作不需要外围电路。&lt;br /&gt;
&lt;br /&gt;
温度测量通过触发TASK_START，当完成测量时DATARDY事件将会产生。通过读取TEMP寄存器，读取当前温度。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中10_temp_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  int32_t volatile temp = INT32_MAX;&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  &lt;br /&gt;
  UpdateLCD(temp);&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_START = 1;  //启动温度测量任务&lt;br /&gt;
    while (NRF_TEMP-&amp;gt;EVENTS_DATARDY == 0)&lt;br /&gt;
    {&lt;br /&gt;
        // Do nothing.&lt;br /&gt;
    }&lt;br /&gt;
    NRF_TEMP-&amp;gt;EVENTS_DATARDY = 0;&lt;br /&gt;
    //读取温度值&lt;br /&gt;
    temp = (nrf_temp_read() / 4);&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_STOP = 1;    //停止测量任务&lt;br /&gt;
    UpdateLCD(temp);&lt;br /&gt;
    &lt;br /&gt;
    LED_Toggle(0);               //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数前部，对用到的外设进行初始化，包括LED，LCD。在for循环中，启动，停止温度测量。RNF_TEMP-&amp;gt;TASKS_START置1，将启动温度测量，NRF_TEMP-&amp;gt;EVENTS_DATARDY是测量完成事件，如果DATARDY事件置1，表明温度测量已经完成。nrf_temp_read函数将读取TEMP寄存器返回当前温度值。其值除以4，即丢弃小数部分。&lt;br /&gt;
&lt;br /&gt;
UpdateLCD函数，用于改变屏幕的内部。其输入参数为当前的温度值。在其内部对温度区间进行划分，小于20度属于NORMAL，显示屏会显示绿色背景；在20到27度之间，属于WARN，显示屏会显示黄色；大于27度，属于EMERGENT，显示屏会显示红色。同时也会显示当前温度值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  TEMP_DEFAULT,&lt;br /&gt;
  TEMP_NORMAL,&lt;br /&gt;
  TEMP_WARN,&lt;br /&gt;
  TEMP_EMERGENT&lt;br /&gt;
}TEMP_RANGE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void UpdateLCD(int32_t temp)&lt;br /&gt;
{&lt;br /&gt;
  static TEMP_RANGE LEVEL = TEMP_DEFAULT;&lt;br /&gt;
  bool isChange = false;&lt;br /&gt;
  if(temp == INT32_MAX)&lt;br /&gt;
  {&lt;br /&gt;
    LEVEL = TEMP_DEFAULT;&lt;br /&gt;
    isChange = true ;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    if(temp &amp;lt; 20)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_NORMAL)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_NORMAL;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    else if(temp &amp;lt; 27)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_WARN)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_WARN;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_EMERGENT)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_EMERGENT;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(isChange)&lt;br /&gt;
  {&lt;br /&gt;
    switch(LEVEL)&lt;br /&gt;
    {&lt;br /&gt;
      case TEMP_DEFAULT: &lt;br /&gt;
        GUI_SetBkColor(GUI_WHITE);//设置蓝色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_NORMAL:&lt;br /&gt;
        GUI_SetBkColor(GUI_GREEN);//设置蓝色背景色   &lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_WARN:&lt;br /&gt;
        GUI_SetBkColor(GUI_YELLOW);//设置绿色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      default:&lt;br /&gt;
        GUI_SetBkColor(GUI_RED);//设置红色背景色&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispString(&amp;quot;Temperture\r\nExample&amp;quot;); &lt;br /&gt;
    isChange = false;&lt;br /&gt;
  }&lt;br /&gt;
  if(LEVEL)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;Temp:%dC&amp;quot;,temp);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示当前的温度值。并根据温度值，显示屏的背景色也会改变。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1775</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1775"/>
		<updated>2019-07-15T08:54:29Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 实验现象 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|-&lt;br /&gt;
|10_温度实验&lt;br /&gt;
|Temperture&lt;br /&gt;
|芯片上温度传感器&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;br /&gt;
&lt;br /&gt;
=== 温度传感器实验 ===&lt;br /&gt;
NRF52832芯片内部，自带一个温度传感器，并有线性补尝。它最大的分辨率为0.25度。本实验利用温度传感测量芯片周围环境的温度。它工作不需要外围电路。&lt;br /&gt;
&lt;br /&gt;
温度测量通过触发TASK_START，当完成测量时DATARDY事件将会产生。通过读取TEMP寄存器，读取当前温度。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中10_temp_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  int32_t volatile temp = INT32_MAX;&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  &lt;br /&gt;
  UpdateLCD(temp);&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_START = 1;  //启动温度测量任务&lt;br /&gt;
    while (NRF_TEMP-&amp;gt;EVENTS_DATARDY == 0)&lt;br /&gt;
    {&lt;br /&gt;
        // Do nothing.&lt;br /&gt;
    }&lt;br /&gt;
    NRF_TEMP-&amp;gt;EVENTS_DATARDY = 0;&lt;br /&gt;
    //读取温度值&lt;br /&gt;
    temp = (nrf_temp_read() / 4);&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_STOP = 1;    //停止测量任务&lt;br /&gt;
    UpdateLCD(temp);&lt;br /&gt;
    &lt;br /&gt;
    LED_Toggle(0);               //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数前部，对用到的外设进行初始化，包括LED，LCD。在for循环中，启动，停止温度测量。RNF_TEMP-&amp;gt;TASKS_START置1，将启动温度测量，NRF_TEMP-&amp;gt;EVENTS_DATARDY是测量完成事件，如果DATARDY事件置1，表明温度测量已经完成。nrf_temp_read函数将读取TEMP寄存器返回当前温度值。其值除以4，即丢弃小数部分。&lt;br /&gt;
&lt;br /&gt;
UpdateLCD函数，用于改变屏幕的内部。其输入参数为当前的温度值。在其内部对温度区间进行划分，小于20度属于NORMAL，显示屏会显示绿色背景；在20到27度之间，属于WARN，显示屏会显示黄色；大于27度，属于EMERGENT，显示屏会显示红色。同时也会显示当前温度值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
typedef enum&lt;br /&gt;
{&lt;br /&gt;
  TEMP_DEFAULT,&lt;br /&gt;
  TEMP_NORMAL,&lt;br /&gt;
  TEMP_WARN,&lt;br /&gt;
  TEMP_EMERGENT&lt;br /&gt;
}TEMP_RANGE;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void UpdateLCD(int32_t temp)&lt;br /&gt;
{&lt;br /&gt;
  static TEMP_RANGE LEVEL = TEMP_DEFAULT;&lt;br /&gt;
  bool isChange = false;&lt;br /&gt;
  if(temp == INT32_MAX)&lt;br /&gt;
  {&lt;br /&gt;
    LEVEL = TEMP_DEFAULT;&lt;br /&gt;
    isChange = true ;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    if(temp &amp;lt; 20)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_NORMAL)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_NORMAL;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    else if(temp &amp;lt; 27)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_WARN)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_WARN;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_EMERGENT)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_EMERGENT;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(isChange)&lt;br /&gt;
  {&lt;br /&gt;
    switch(LEVEL)&lt;br /&gt;
    {&lt;br /&gt;
      case TEMP_DEFAULT: &lt;br /&gt;
        GUI_SetBkColor(GUI_WHITE);//设置蓝色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_NORMAL:&lt;br /&gt;
        GUI_SetBkColor(GUI_GREEN);//设置蓝色背景色   &lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_WARN:&lt;br /&gt;
        GUI_SetBkColor(GUI_YELLOW);//设置绿色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      default:&lt;br /&gt;
        GUI_SetBkColor(GUI_RED);//设置红色背景色&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispString(&amp;quot;Temperture\r\nExample&amp;quot;); &lt;br /&gt;
    isChange = false;&lt;br /&gt;
  }&lt;br /&gt;
  if(LEVEL)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;Temp:%dC&amp;quot;,temp);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示当前的温度值。并根据温度值，显示屏的背景色也会改变。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1774</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1774"/>
		<updated>2019-07-15T07:37:52Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 温度传感器实验 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|-&lt;br /&gt;
|10_温度实验&lt;br /&gt;
|Temperture&lt;br /&gt;
|芯片上温度传感器&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;br /&gt;
&lt;br /&gt;
=== 温度传感器实验 ===&lt;br /&gt;
NRF52832芯片内部，自带一个温度传感器，并有线性补尝。它最大的分辨率为0.25度。本实验利用温度传感测量芯片周围环境的温度。它工作不需要外围电路。&lt;br /&gt;
&lt;br /&gt;
温度测量通过触发TASK_START，当完成测量时DATARDY事件将会产生。通过读取TEMP寄存器，读取当前温度。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中10_temp_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  int32_t volatile temp = INT32_MAX;&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  &lt;br /&gt;
  UpdateLCD(temp);&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_START = 1;  //启动温度测量任务&lt;br /&gt;
    while (NRF_TEMP-&amp;gt;EVENTS_DATARDY == 0)&lt;br /&gt;
    {&lt;br /&gt;
        // Do nothing.&lt;br /&gt;
    }&lt;br /&gt;
    NRF_TEMP-&amp;gt;EVENTS_DATARDY = 0;&lt;br /&gt;
    //读取温度值&lt;br /&gt;
    temp = (nrf_temp_read() / 4);&lt;br /&gt;
    NRF_TEMP-&amp;gt;TASKS_STOP = 1;    //停止测量任务&lt;br /&gt;
    UpdateLCD(temp);&lt;br /&gt;
    &lt;br /&gt;
    LED_Toggle(0);               //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数前部，对用到的外设进行初始化，包括LED，LCD。在for循环中，启动，停止温度测量。RNF_TEMP-&amp;gt;TASKS_START置1，将启动温度测量，NRF_TEMP-&amp;gt;EVENTS_DATARDY是测量完成事件，如果DATARDY事件置1，表明温度测量已经完成。nrf_temp_read函数将读取TEMP寄存器返回当前温度值。其值除以4，即丢弃小数部分。&lt;br /&gt;
&lt;br /&gt;
UpdateLCD函数，用于改变屏幕的内部。其输入参数为当前的温度值。在其内部对温度区间进行划分，小于20度属于NORMAL，显示屏会显示绿色背景；在20到27度之间，属于WARN，显示屏会显示黄色；大于27度，属于EMERGENT，显示屏会显示红色。同时也会显示当前温度值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void UpdateLCD(int32_t temp)&lt;br /&gt;
{&lt;br /&gt;
  static TEMP_RANGE LEVEL = TEMP_DEFAULT;&lt;br /&gt;
  bool isChange = false;&lt;br /&gt;
  if(temp == INT32_MAX)&lt;br /&gt;
  {&lt;br /&gt;
    LEVEL = TEMP_DEFAULT;&lt;br /&gt;
    isChange = true ;&lt;br /&gt;
  }&lt;br /&gt;
  else&lt;br /&gt;
  {&lt;br /&gt;
    if(temp &amp;lt; 20)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_NORMAL)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_NORMAL;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    else if(temp &amp;lt; 27)&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_WARN)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_WARN;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      if(LEVEL != TEMP_EMERGENT)&lt;br /&gt;
      {&lt;br /&gt;
        LEVEL = TEMP_EMERGENT;&lt;br /&gt;
        isChange = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(isChange)&lt;br /&gt;
  {&lt;br /&gt;
    switch(LEVEL)&lt;br /&gt;
    {&lt;br /&gt;
      case TEMP_DEFAULT: &lt;br /&gt;
        GUI_SetBkColor(GUI_WHITE);//设置蓝色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_NORMAL:&lt;br /&gt;
        GUI_SetBkColor(GUI_GREEN);//设置蓝色背景色   &lt;br /&gt;
        break;&lt;br /&gt;
      case TEMP_WARN:&lt;br /&gt;
        GUI_SetBkColor(GUI_YELLOW);//设置绿色背景色&lt;br /&gt;
        break;&lt;br /&gt;
      default:&lt;br /&gt;
        GUI_SetBkColor(GUI_RED);//设置红色背景色&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispString(&amp;quot;Temperture\r\nExample&amp;quot;); &lt;br /&gt;
    isChange = false;&lt;br /&gt;
  }&lt;br /&gt;
  if(LEVEL)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;Temp:%dC&amp;quot;,temp);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示当前的温度值。并根据温度值，显示屏的背景色也会改变。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1773</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1773"/>
		<updated>2019-07-15T01:22:48Z</updated>

		<summary type="html">&lt;p&gt;Erjin：温度传感实验&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|-&lt;br /&gt;
|10_温度实验&lt;br /&gt;
|Temperture&lt;br /&gt;
|芯片上温度传感器&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;br /&gt;
&lt;br /&gt;
=== 温度传感器实验 ===&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1771</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1771"/>
		<updated>2019-07-12T02:08:13Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* ADC 采集实验*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
NRF52832芯片中8路ADC采集通道。可以配置成单端模式和差分模式。本例程中光敏器件是连接在P0.30引脚上，即ADC通道6。采用单端方式对电压进行采集。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中09_adc_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init(); //LCD 实始化&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);//设置蓝色背景色&lt;br /&gt;
  GUI_Clear();          &lt;br /&gt;
  GUI_SetColor(GUI_RED);   //设置红色字体&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();         //ADC初始化      &lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    //发起ADC采集&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
    LED_Toggle(0);      //翻转LED0&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数中，对LED，ADC，LCD进行初始化与设置。ADC具体的初始化在LUX_SaadcInit函数中，对ADC通道进行配置。main函数for循环中，使用nrf_delay_ms延时函数，周期性地触发ADC采集，同时翻转LED0进行指示。&lt;br /&gt;
&lt;br /&gt;
例子中，采用SAADC_BLOCKING宏定义进行编译区分ADC同步采集和ADC异步采集。完成ADC转换后，将结果通过显示屏显示出。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void LUX_SaadcInit(void)&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
  nrf_saadc_channel_config_t channel_config =&lt;br /&gt;
      NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN6);&lt;br /&gt;
  nrf_drv_saadc_init(NULL, saadc_callback);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_saadc_channel_init(6, &amp;amp;channel_config);&lt;br /&gt;
  &lt;br /&gt;
#ifndef  SAADC_BLOCKING &lt;br /&gt;
  nrf_drv_saadc_buffer_convert(m_buffer_pool, SAMPLES_IN_BUFFER);&lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LUX_SaadcInit函数中，调用nrf_drv_saadc_init函数初始化ADC，并传入saadc_callback事件回调函数（异步ADC采集使用）。nrf_drv_saadc_channel_init函数对ADC通道6，进行默认配置。其中包括参考源，增益，单端模式，引脚等配置。下面是saadc_callback回调函数的实现，在其中会监视NRF_DRV_SAADC_EVT_DONE事件（ADC采集转转换完成），并将结果显示在屏幕上。此回调函数调用，只在进行ADC异步转换时，才会被调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
void saadc_callback(nrf_drv_saadc_evt_t const * p_event)&lt;br /&gt;
{&lt;br /&gt;
    if (p_event-&amp;gt;type == NRF_DRV_SAADC_EVT_DONE)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_saadc_buffer_convert(p_event-&amp;gt;data.done.p_buffer, SAMPLES_IN_BUFFER);&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[0]);&lt;br /&gt;
    for(uint8_t i = 1 ; i &amp;lt; SAMPLES_IN_BUFFER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Disprintf(&amp;quot;ADC:%04d\r\n&amp;quot;,p_event-&amp;gt;data.done.p_buffer[i]);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832DK上的LCD显示屏会显示光敏器件电压的ADC结果，同时每转换一次，LED0会状态会翻转一次。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1770</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1770"/>
		<updated>2019-07-11T09:42:36Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/* 光照度实验 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
光照度是通过光敏传感器，将光照度转成电信号。在NRF52832DK评估板上，利用光敏元件，将光强度转成电压信号，通过ADC对电压信号进行采集，从而可以测量光照度。其原理图如下图所示。&lt;br /&gt;
[[文件:Lux.png|居中|缩略图|313x313像素]]&lt;br /&gt;
当光照度增加时，流过Q6的电流变大，从而分得的电压变小；当光照度变小时，流过Q6的电流变小，从而分得的电压变大。所以光照度与电压信号成反比。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();           //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  GUI_SetBkColor(GUI_BLUE);&lt;br /&gt;
  GUI_Clear();&lt;br /&gt;
  GUI_SetColor(GUI_RED);&lt;br /&gt;
  GUI_DispString(&amp;quot;ADC Example&amp;quot;);&lt;br /&gt;
  LUX_SaadcInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
#if defined(SAADC_BLOCKING)&lt;br /&gt;
    if(nrf_drv_saadc_sample_convert(NRF_SAADC_INPUT_AIN6,m_buffer_pool) == NRF_SUCCESS)&lt;br /&gt;
    {&lt;br /&gt;
      GUI_DisprintfAt(0,32,&amp;quot;ADC:%04d&amp;quot;,m_buffer_pool[0]);&lt;br /&gt;
    }&lt;br /&gt;
#else&lt;br /&gt;
    nrf_drv_saadc_sample();    &lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Lux.png&amp;diff=1766</id>
		<title>文件:Lux.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Lux.png&amp;diff=1766"/>
		<updated>2019-07-11T05:47:14Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;lux 原理图&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1765</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1765"/>
		<updated>2019-07-11T05:33:16Z</updated>

		<summary type="html">&lt;p&gt;Erjin：/*光照度*/&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;br /&gt;
&lt;br /&gt;
=== 光照度实验 ===&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1764</id>
		<title>NRF52832DK基础实验</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=NRF52832DK%E5%9F%BA%E7%A1%80%E5%AE%9E%E9%AA%8C&amp;diff=1764"/>
		<updated>2019-07-11T03:46:52Z</updated>

		<summary type="html">&lt;p&gt;Erjin：UART 收发实验&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;nRF52832是Nordic公司推出一款高性能无线SOC芯片，在芯片上可以运行多种协议栈，包括蓝牙BLE，NFC,ANT,802.15.4G，其中BLE协议栈可以支持到BLE5.0。为此谷雨物联推出一款基于nRF52832芯片评估板，nRF52832DK。&lt;br /&gt;
&lt;br /&gt;
nRF52832DK评估板上设计了丰富实用的外围设备。其中有4路LED，4路按键输入，一路MINI usb转UART，三路PWM RGB灯珠，一路有源蜂鸣器，一路光敏，一路振动马达，TFT显示器接口，NFC标签接口。&lt;br /&gt;
&lt;br /&gt;
=== nRF52832DK基础实验说明列表 ===&lt;br /&gt;
方便开发者，更快，更容易上手nRF52832芯片的外设操作，为此我们提供和整理nRF52832DK外围电路实验说明。见下表所示。&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!实验名称&lt;br /&gt;
!实验所需外设&lt;br /&gt;
!实验简单说明&lt;br /&gt;
|-&lt;br /&gt;
|01_LED亮灭实验&lt;br /&gt;
|GPIO &lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|02_按键输入实验（poll）&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|03_按键输入实验（int）&lt;br /&gt;
|GPIO边沿中断&lt;br /&gt;
|熟悉GPIO边沿中断&lt;br /&gt;
|-&lt;br /&gt;
|04_振动马达实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|05_蜂鸣器实验&lt;br /&gt;
|GPIO&lt;br /&gt;
|熟悉GPIO操作&lt;br /&gt;
|-&lt;br /&gt;
|06_RGB实验&lt;br /&gt;
|PWM&lt;br /&gt;
|熟悉PWM操作&lt;br /&gt;
|-&lt;br /&gt;
|07_TFT实验(tft_lcd_144，tft_lcd_130)&lt;br /&gt;
|SPI&lt;br /&gt;
|熟悉SPI操作&lt;br /&gt;
|-&lt;br /&gt;
|08_UART收发实验&lt;br /&gt;
|UART&lt;br /&gt;
|熟悉UART操作&lt;br /&gt;
|-&lt;br /&gt;
|09_光照度实验&lt;br /&gt;
|ADC&lt;br /&gt;
|熟悉ADC操作&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== LED亮灭实验 ===&lt;br /&gt;
LED亮灭实验是展示nRF52832的GPIO输出配置，使开发者更直观的了解GPIO输出。GPIO输出是熟悉一款MCU的开始。下面将简单的介绍并分析相关代码。在NRF52832DK评估板上有4路LED资源，分别处在PIN17，PIN18，PIN19，PIN20四个引脚上。LED电路原理图，如下图所示。其为低电平有效，即引脚为低电平时LED被点亮，引脚为高电平时LED熄灭。&lt;br /&gt;
[[文件:FOUR LINES LED.png|居中|缩略图|402x402像素|LED 外设电路]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中01_led_blinkly工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中LED_Init函数用于初始化LED引脚。将四路LED引脚初始化输出模式，并置高电平，即熄灭LED。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;LED_START，LED_STOP是两个宏，标记LED开始引脚到LED结束引脚范围。配合nrf_gpio_range_cfg_output函数，可实现批量设置。nrf_gpio_pin_set设置LED引脚输出高电平。&lt;br /&gt;
&lt;br /&gt;
完成LED引脚配置，进入while循环。在循环中遍历所有的LED引脚，翻转引脚高低电平，达到闪烁的目的。nrf_delay_ms函数用于软件延时。nrf_gpio_pin_toggle对引脚电平进行翻转。参数是LED引脚。在nrf_gpio_pin_toggle内部，先读取引脚当前的高低电平状态，然后根据返回的状态进行取反，再设置OUT寄存器。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。&lt;br /&gt;
&lt;br /&gt;
=== 按键输入实验(poll) ===&lt;br /&gt;
01_LED亮灭实验是操作GPIO引脚输出，而本实验是操作GPIO的输入。利用GPIO输入引脚电平变化，来监测按键按下动作。在NRF52832DE评估板上，提供了四路按键资源，分别占用PIN13，PIN14，PIN15，PIN16四个引脚上。按键电路原理图，如下图所示。由原理图可知，按键是低电平有效。当按键按下引脚为低电平，释放时会高电平。'''注，按键引脚必须要使能引脚上拉功能，否则可能告成按键识别不可靠。'''&lt;br /&gt;
[[文件:Four key sech.png|居中|缩略图|463x463像素|按键外设原理图]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中02_key_press工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      if(BTN_state_get(i))&lt;br /&gt;
      {&lt;br /&gt;
        LED_On(i);&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
        LED_Off(i);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
    }&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在LED_Init函数中调用nrf_gpio_range_cfg_output，将初始化NRF52832DK评估板LED引脚。LED将GPIO引脚初始化为输出。BTN_Init函数中调用nrf_gpio_range_cfg_input按键初始化上拉输入。此实验中只是用GPIO输入，所以只能采用轮询的方式，周期性查询按键引脚电平状态。当按键按下，BTN_state_get函数返回true，否则返回false。&lt;br /&gt;
&lt;br /&gt;
为增加互动性，将用4个LED分别指示4个按键状态。按键按下点亮LED,按键释放熄灭LED。其代码实现如while中的for循环。LED_On点亮指定LED，LED_Off熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 按键中断输入实验（int） ===&lt;br /&gt;
02_按键输入实验是采用轮询方式不断查询引脚电平状态。引脚默认为高电平，当按下按键后，引脚会变成低电平。而此实验将使用nRF52832的GPIOTE边沿中断方式来监测按键动作。中断方式监测按键，带来了高灵敏度，高效率，同时也增加了按键按下不可靠性。主要原因按键按下会产生电平抖动。要求高可靠性可以为此要加入消抖。由02的原理图中可以见，没有硬件上的消抖，所以只能在驱动程序上进行消抖操作（此例程中没加入消抖）。&lt;br /&gt;
&lt;br /&gt;
在例子中，使用了RF52832的GPIOTE功能，GPIOTE与GPIO使用上有所区别。但是如果一个引脚使用了GPIOTE功能，GPIO功能将不启作用，引脚上的所有操作将由GPIOTE支配，直到GPIOTE失能。&lt;br /&gt;
&lt;br /&gt;
如果开发者要详细了解GPIOTE可以查看nRF52832的芯片手册《nRF52832_OPS.PDF》。《nRF52832 Objective Product Specification v0.6.3》&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中03_key_press_int工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在例程中使用了GPIOTE功能，所以在main函数开始处就初始化GPIOTE驱动（在使用gpiote相关函数之前，一定要先调用gpiote初始化函数nrf_drv_gpiote_init，否则可能产生不可预知问题）。接着配置LED引脚。在代码中提供两种方式，一种与02实验一至，另一种是gpiote方式。它们通过LED_GPOITE宏进行选择编译。这里主要介绍GPIOTE方式。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :LED_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化LED引脚为输出模式，并熄灭LED&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void LED_Init(void)&lt;br /&gt;
{&lt;br /&gt;
#if defined(LED_GPIOTE) &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_drv_gpiote_out_config_t out_config = GPIOTE_CONFIG_OUT_SIMPLE(true);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_out_init(Leds[i], &amp;amp;out_config);&lt;br /&gt;
  }&lt;br /&gt;
#else  &lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  &lt;br /&gt;
  //配置LED引脚为输出模式&lt;br /&gt;
  nrf_gpio_range_cfg_output(LED_START, LED_STOP);&lt;br /&gt;
  &lt;br /&gt;
  //置LED引脚为高电平，即LED灭&lt;br /&gt;
  for(i = 0 ; i &amp;lt; LEDS_NUMBER; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_gpio_pin_set(Leds[i]);&lt;br /&gt;
  }  &lt;br /&gt;
#endif&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nRF52832的GPIOTE只有8个通道，所以每个通道都要进行配置。在LED引脚中，选择简单的输出配置即GPIOTE_CONFIG_OUT_SIMPLE，它是一个宏。是对nrf_drv_gpiote_out_config_t类型成员初始化结构体。其中参数表示引脚配置成GPIOTE时默认引脚状态。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_out_init函数将对指定引脚进行GPIOTE_CONFIG_OUT_SIMPLE配置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :Btn_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化Btn引脚为输入，全边沿敏感模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_Init(void)&lt;br /&gt;
{&lt;br /&gt;
  //创建引脚配置结构&lt;br /&gt;
  nrf_drv_gpiote_in_config_t btn_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);&lt;br /&gt;
  btn_config.pull = NRF_GPIO_PIN_PULLUP;&lt;br /&gt;
  //配置Btn引脚为边沿敏感&lt;br /&gt;
  for(uint8_t i = 0 ; i &amp;lt; BUTTONS_NUMBER ; i++)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_gpiote_in_init(Btns[i], &amp;amp;btn_config, BTN_pin_handler);&lt;br /&gt;
    nrf_drv_gpiote_in_event_enable(Btns[i], true);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;BTN_Init函数是对按键的引脚进行TOGGLE sense输入配置。其中GPIOTE_CONFIG_IN_SENSE_TOGGLE也宏，是对nrf_drv_gpiote_in_config_t类型成员初始化结构体。参数true表示使用IN_EVENT配置，而非PORT_EVENT。由于按键外部硬件没有上拉，所以这里要配置成上拉，即pull成员变量设置成NRF_GPIO_PIN_PULLUP。&lt;br /&gt;
&lt;br /&gt;
nrf_drv_gpiote_in_init函数将按键引脚配置成GPIOTE_CONFIG_IN_SENSE_TOGGLE模式，同时要传入中断回调函数。最后要调用nrf_drv_gpiote_in_event_enable函数使能引脚IN_EVENT。&lt;br /&gt;
&lt;br /&gt;
中断方式采集按键，是一种异步方式，所以在回调函数中要进行逻辑上的处理。然而在这个例程中，四个按键使用同一个回调函数。所以要在回调函数中要进行按按键识别，包括按键位置识别，还有按键动作。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :BTN_pin_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : BTN引脚边沿中断回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : pin -&amp;gt; 引脚号&lt;br /&gt;
//         action -&amp;gt; 极性变化形为&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BTN_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)&lt;br /&gt;
{&lt;br /&gt;
  bool flag = false;&lt;br /&gt;
  switch(pin)&lt;br /&gt;
  {&lt;br /&gt;
  case BUTTON_1:&lt;br /&gt;
  case BUTTON_2:&lt;br /&gt;
  case BUTTON_3:&lt;br /&gt;
  case BUTTON_4:&lt;br /&gt;
    flag = true;&lt;br /&gt;
    break;&lt;br /&gt;
  default:&lt;br /&gt;
    break;&lt;br /&gt;
  }&lt;br /&gt;
  if(flag)&lt;br /&gt;
  {&lt;br /&gt;
    uint8_t idx = BTN_Pin_To_Idx(pin);&lt;br /&gt;
    //读取pin电平状态&lt;br /&gt;
    if(nrf_drv_gpiote_in_is_set(pin))&lt;br /&gt;
    {&lt;br /&gt;
      LED_Off(idx);   //按键释放,熄灭LED&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      LED_On(idx);   //按键按下,点亮LED&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中flag是有效按键动作的标记。只有是BUTTON_1，BUTTON_2，BUTTON_3，BUTTON_4按键才是有效动作。通过nrf_drv_gpiote_in_is_set查询引脚当前电平状态，确定按键是按下，还是释放动作。并根据按键动作，点亮或熄灭LED。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED是全灭状态。&lt;br /&gt;
# 按下SW1，LED1亮；释放SW1，LED1灭&lt;br /&gt;
# 按下SW2，LED2亮；释放SW2，LED2灭&lt;br /&gt;
# 按下SW3，LED3亮；释放SW3，LED3灭&lt;br /&gt;
# 按下SW3，LED4亮；释放SW4，LED4灭&lt;br /&gt;
&lt;br /&gt;
=== 振动马达实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路振动马达，方便开发者对蓝牙ANCS开发。其直流马达的工作原理不用多介绍，只要给它提供直流电即可工作。MOTOR端的高低电平，将会另三极管打开与关闭，从而控制振动马达工作。MOTOR是连接在nrf52832的P0.12引脚上。&lt;br /&gt;
[[文件:MOTOR.png|居中|缩略图|500x500像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中04_motor_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Motor_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_MOTOR_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《01_LED亮灭实验》十相似，只是在其基础上添加了MOTOR相关的代码。&lt;br /&gt;
&lt;br /&gt;
Motor_Init函数是对MOTOR引脚进行输出初始化。nrf_gpio_pin_toggle函数是对MOTOR引脚的电平进行翻转，从而输出高低电平，即马达就会振动与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其马达就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== 蜂鸣器实验 ===&lt;br /&gt;
在NRF52832DK评估板上，设计有一路有源蜂鸣器，方便开发者对蓝牙ANCS开发。其蜂鸣器的工作原理不用多介绍，只要给它提供直流电即可工作。BUZZER端的高低电平，将会另三极管打开与关闭，从而控制蜂鸣器工作。BUZZER是连接在nrf52832的P0.11引脚上。&lt;br /&gt;
[[文件:Beep.png|居中|缩略图|494x494像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中05_buzzer_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  Buzzer_Init();&lt;br /&gt;
  LED_Init();&lt;br /&gt;
&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环点亮熄灭LED，间隔500ms&lt;br /&gt;
    for( uint8_t i = 0; i &amp;lt; LEDS_NUMBER ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_gpio_pin_toggle(Leds[i]);&lt;br /&gt;
      nrf_gpio_pin_toggle(BSP_BUZZER_0);&lt;br /&gt;
      nrf_delay_ms(500);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;main函数与《04_振动马达实验》十相似，只是将MOTOR相关代码替换成了BUZZER代码。&lt;br /&gt;
&lt;br /&gt;
Buzzer_Init函数是对BUZZER引脚进行输出初始化。nrf_gpio_pin_toggle函数是对BUZZER引脚的电平进行翻转，从而输出高低电平，即蜂鸣器就会工作与关闭。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时NRF52832的LED便会每500ms依次点亮LED；当四个LED全部点亮后，再以500ms依次熄灭LED，直到全部熄灭。期间，每一次LED状态改变，其蜂鸣器就会工作一次或关闭一次，间隔时间是500ms。&lt;br /&gt;
&lt;br /&gt;
=== RGB实险 ===&lt;br /&gt;
在NRF52832DK评估板上，提供一个RGB调色灯。方便开发者利用NRF52832开发蓝牙RGB灯。RGB调光灯采用三路PWM分别控制三色灯的亮度达到调色目标。在NRF52832芯片中有三个PWM外设，每个外设有4路PWM。NRF52832的PWM外设细节，可以查阅芯片手册。&lt;br /&gt;
[[文件:RGB.png|居中|缩略图|500x500像素]]&lt;br /&gt;
从原理图可以看出，每路灯都是由三极管控制，所以控制端高电平表示打开，低电平表示关闭。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中06_rgb_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  GPIOTE_Init();&lt;br /&gt;
  &lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
  BTN_Init();   //BTN 初始化&lt;br /&gt;
  RGB_PwmInit();&lt;br /&gt;
  &lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    //循环，间隔100ms&lt;br /&gt;
    nrf_delay_ms(100);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，RGB_PwmInit是初始化PWM函数。 在RGB_PwmInit函数中，配置了pwm输出引脚，时钟频率，计数方式等。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :RGB_PwmInit&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化RGB pwm模式&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void RGB_PwmInit(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_pwm_config_t const rgb_config =&lt;br /&gt;
  {&lt;br /&gt;
      .output_pins =&lt;br /&gt;
      {&lt;br /&gt;
          RGB_PWM_R , // channel 0&lt;br /&gt;
          RGB_PWM_G , // channel 1&lt;br /&gt;
          RGB_PWM_B , // channel 2&lt;br /&gt;
          NRF_DRV_PWM_PIN_NOT_USED // channel 3&lt;br /&gt;
      },&lt;br /&gt;
      .irq_priority = APP_IRQ_PRIORITY_LOWEST,&lt;br /&gt;
      .base_clock   = NRF_PWM_CLK_1MHz,&lt;br /&gt;
      .count_mode   = NRF_PWM_MODE_UP,        //向上计数方式&lt;br /&gt;
      .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,&lt;br /&gt;
      .load_mode    = NRF_PWM_LOAD_WAVE_FORM, //WaveForm加载方式&lt;br /&gt;
      .step_mode    = NRF_PWM_STEP_AUTO&lt;br /&gt;
  };&lt;br /&gt;
  &lt;br /&gt;
  nrf_drv_pwm_init(&amp;amp;m_RGB, &amp;amp;rgb_config, rgb_pwm_handler);&lt;br /&gt;
  nrf_drv_pwm_simple_playback(&amp;amp;m_RGB, &amp;amp;m_rgb_seq, 1,&lt;br /&gt;
                                      NRF_DRV_PWM_FLAG_LOOP);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_pwm_config_t 是初始化PWM外设配置结构体。在其中设置的pwm输出引脚，中断优先级，时钟频率，计数方式，计数长度，比较值加载方式等。调用nrf_drv_pwm_init初始化函数，传入事件回调函数rgb_pwm_handler，方便开发者监视相关事件及参数修改。在此例程中，回调数主要用来修改相应PWM通道的比较值，实现PWM的占空比从0%到100%变化。改变通道数是通过按键来修改RGB_OP_CH值。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :rgb_pwm_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : rgb pwm事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
static void rgb_pwm_handler(nrf_drv_pwm_evt_type_t event_type)&lt;br /&gt;
{&lt;br /&gt;
  if (event_type == NRF_DRV_PWM_EVT_FINISHED)&lt;br /&gt;
  {&lt;br /&gt;
    uint16_t *p_channel = NULL;&lt;br /&gt;
    //获取当前wm通道数值地址&lt;br /&gt;
    switch(RGB_OP_CH)&lt;br /&gt;
    {&lt;br /&gt;
    case PWM_R:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_0;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_G:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_1;&lt;br /&gt;
      break;&lt;br /&gt;
    case PWM_B:&lt;br /&gt;
      p_channel = &amp;amp;m_rgb_seq_values.channel_2;&lt;br /&gt;
      break;&lt;br /&gt;
    default:&lt;br /&gt;
      return;&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    if(value_dir)   //控制修改数据方向。&lt;br /&gt;
    {&lt;br /&gt;
      //UP &lt;br /&gt;
      *p_channel += RGB_MIN_STEP;&lt;br /&gt;
&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = m_rgb_seq_values.counter_top;&lt;br /&gt;
        value_dir = false;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
    else&lt;br /&gt;
    {&lt;br /&gt;
      //Down&lt;br /&gt;
      *p_channel -= RGB_MIN_STEP;&lt;br /&gt;
      if(*p_channel &amp;gt;= m_rgb_seq_values.counter_top)&lt;br /&gt;
      {&lt;br /&gt;
        *p_channel = 0;&lt;br /&gt;
        value_dir = true;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;RGB_MIN_STEP是每次变化的步长，这里设置为1。value_dir是控制修改数据的方向，当比较值达到最大值或最小值时就会更改方向，使比较值向相反的方向增加或减小。&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。会发现评估板的RGB灯会不断的改变颜色。原因是PWMR路的PWM的占空化不断变化所致，如下图PWR所示，而其他二路PWM占空比保持不变。&lt;br /&gt;
[[文件:F0001TEK.jpg|居中|缩略图|640x640像素|PWMR]]&lt;br /&gt;
按键S1，S2，S3分别控制PWM的R，G，B分量。而S4则会暂停PWM的占空比，直到S1，S2，S3按下。&lt;br /&gt;
&lt;br /&gt;
=== TFT实验(tft_lcd_144，tft_lcd_130) ===&lt;br /&gt;
NRF52832DK评估板上，提供一路LCD接口。使用NRF52832的SPI外设。在SPI外设中有两种工作模式，分别为EasyDMA方式，普通DMA模式。使用DMA模式时，开发者要注意。所有使用SPI发送的数据，都必须在RAM中，否则将发生错误。SPI的CPOL与CPHA具体参数及意义，这里将不再详述。详细的细节部分，开发者可以查阅NRF52832的芯片手册。&lt;br /&gt;
[[文件:Tft.png|居中|缩略图|383x383像素]]&lt;br /&gt;
DIS_BL与DIS_D/C引脚，是TFT屏的控制引脚。DIS_BL是控制背光，DIS_D/C是控制收发数据的属性，是命令数据，还是颜色数据。&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中07_tft_spi_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&lt;br /&gt;
&lt;br /&gt;
'''LCD驱动部分代码说明，在此不会进行说明。开发者可以查看《谷雨显示接口原理说明》，里面详细介绍了屏幕驱动部分。'''&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  LED_Init();   //LED 初始化&lt;br /&gt;
&lt;br /&gt;
  bsp_board_lcd_init();&lt;br /&gt;
  GUI_Init();&lt;br /&gt;
  for(uint8_t i = 0;;)&lt;br /&gt;
  {&lt;br /&gt;
    GUI_SetBkColor(color[i%3]);&lt;br /&gt;
    GUI_Clear();&lt;br /&gt;
    GUI_DispStringAt(&amp;quot;SPI lcd Test\r\n&amp;quot;,0,0);&lt;br /&gt;
    LED_Toggle(i++%3);&lt;br /&gt;
    nrf_delay_ms(500);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在main函数中，bsp_board_lcd_init函数用于初始化SPI外设。在此例子中没有使用EasyDMA模式，而使用普通的SPI模式，具本的宏定义配置在SDK_CONFIG.H中。bsp_board_lcd_init函数中，配置SCK，MOSI，SS引脚，时钟频率，及SPI工作模式，字节方向等。NRF_DRV_SPI_DEFAULT_CONFIG是一个宏，定义了nrf_drv_spi_config_t的结构数据。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : bsp_board_lcd_init&lt;br /&gt;
// &lt;br /&gt;
// brife : init lcd hardware.ex spi, gpio&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void bsp_board_lcd_init(void)&lt;br /&gt;
{&lt;br /&gt;
  nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;&lt;br /&gt;
  spi_config.ss_pin   = LCD_SPI_SS_PIN;&lt;br /&gt;
  //spi_config.miso_pin = SPI_MISO_PIN;    //NOT USED&lt;br /&gt;
  spi_config.mosi_pin = LCD_SPI_MOSI_PIN;&lt;br /&gt;
  spi_config.sck_pin  = LCD_SPI_SCK_PIN;&lt;br /&gt;
  spi_config.frequency = NRF_DRV_SPI_FREQ_8M;&lt;br /&gt;
  &lt;br /&gt;
  //block mode&lt;br /&gt;
  nrf_drv_spi_init(&amp;amp;spi, &amp;amp;spi_config, NULL, NULL);&lt;br /&gt;
&lt;br /&gt;
  //Config the bl and mode pin to output&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_BL_PIN);&lt;br /&gt;
  nrf_gpio_cfg_output(LCD_MODE_PIN);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;NRF_DRV_SPI_DEFAULT_CONFIG宏定义内容。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
#define NRF_DRV_SPI_DEFAULT_CONFIG                           \&lt;br /&gt;
{                                                            \&lt;br /&gt;
    .sck_pin      = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .mosi_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .miso_pin     = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .ss_pin       = NRF_DRV_SPI_PIN_NOT_USED,                \&lt;br /&gt;
    .irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY,         \&lt;br /&gt;
    .orc          = 0xFF,                                    \&lt;br /&gt;
    .frequency    = NRF_DRV_SPI_FREQ_4M,                     \&lt;br /&gt;
    .mode         = NRF_DRV_SPI_MODE_0,                      \&lt;br /&gt;
    .bit_order    = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST,         \&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;nrf_drv_spi_init函数是正式初始化spi函数。将配置好的config结构转入其中，第三，第四参数是回调函数参数。如果传入回调函数指针，将进行异步数据收发，调用收发函数将立即返回。数据接收完成后，调用回调函数。如果传入NULL，将进行阻塞模式，只有接收完成后，收发函数才会返回。&lt;br /&gt;
&lt;br /&gt;
根据《'''谷雨显示接口原理说明'''》要求，要实现SPI发送函数，背光控制函数，数据命令控制函数等，方便显示屏驱动调用。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdBL&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdBL(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_BL_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCD&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_MODE_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCD(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  nrf_gpio_pin_write(LCD_MODE_PIN, opt? 1 : 0 );&lt;br /&gt;
}&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : BSP_LcdCs&lt;br /&gt;
// &lt;br /&gt;
// brife : set the LCD_BL_PIN pin level&lt;br /&gt;
//&lt;br /&gt;
// param : opt -&amp;gt; 0 OR 1&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
void BSP_LcdCs(uint8_t opt)&lt;br /&gt;
{&lt;br /&gt;
  //nrf_gpio_pin_write(SPI_SS_PIN, opt ? 1 : 0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSend(uint8_t data)&lt;br /&gt;
{&lt;br /&gt;
  spi_tmp = data;&lt;br /&gt;
  nrf_drv_spi_transfer(&amp;amp;spi, &amp;amp;spi_tmp, 1, NULL, 0);&lt;br /&gt;
&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t BSP_LcdSendMore(uint8_t* buf, uint16_t len)&lt;br /&gt;
{&lt;br /&gt;
  //单次发送最大255个字节，这里做了相应的分包。&lt;br /&gt;
  uint8_t cnt_max = len &amp;gt;&amp;gt; 7;&lt;br /&gt;
  uint8_t cnt_rest = len &amp;amp; 0x007F;&lt;br /&gt;
  uint8_t i = 0;&lt;br /&gt;
  if(cnt_max)&lt;br /&gt;
  {&lt;br /&gt;
    for( ; i &amp;lt; cnt_max ; i++)&lt;br /&gt;
    {&lt;br /&gt;
      nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), 128, NULL, 0);&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
  if(cnt_rest)&lt;br /&gt;
  {&lt;br /&gt;
    nrf_drv_spi_transfer(&amp;amp;spi, buf + (i &amp;lt;&amp;lt; 7), cnt_rest, NULL, 0);&lt;br /&gt;
  }&lt;br /&gt;
  return NRF_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中BSP_LcdSendMore函数里，将数据包进行了分包处理，因为nrf_drv_spi_transfer的最大长度为255。实现上述函数后，在lcd_gpio.c文件中进行调用。&lt;br /&gt;
&lt;br /&gt;
上述函数只是，LCD驱动运行前的准备。在调用任务显示函数前，一定要先调用GUI_Init函数，它将初始化LCD驱动相关的结构。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。此时屏幕上，便会交替显示红，绿，蓝三色，同时第一行显示“SPI lcd Test”字样。&lt;br /&gt;
&lt;br /&gt;
=== UART 收发实验 ===&lt;br /&gt;
在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上，通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分，这里不做说明。占用芯片引脚为P0.5，P0.6，P0.7，P0.8。&lt;br /&gt;
[[文件:Uart.png|居中|缩略图|587x587像素]]&lt;br /&gt;
&lt;br /&gt;
==== 代码分析 ====&lt;br /&gt;
开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程（IAR工程）。&lt;br /&gt;
&lt;br /&gt;
在IAR的Workspace中点开Application，双击main.c文件，打开main.c。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn :main&lt;br /&gt;
//&lt;br /&gt;
// brief : 主程序入口&lt;br /&gt;
//&lt;br /&gt;
// param : none&lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
int main(void)&lt;br /&gt;
{&lt;br /&gt;
  uint8_t len;&lt;br /&gt;
  LED_Init();                     //LED 初始化&lt;br /&gt;
  UART_Init(APP_UartEvtHandle);   //初始化串口&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
  bsp_board_lcd_init();           //初始化LCD使用的外设，SPI,GPIO&lt;br /&gt;
  GUI_Init();                     //初始化LCD&lt;br /&gt;
  GUI_DispStringAt(&amp;quot;Uart Test\r\n&amp;quot;,0,0);  //显示字符&lt;br /&gt;
  &lt;br /&gt;
  //串口打印字符串&lt;br /&gt;
  UART_Write(&amp;quot;Uart Test\r\n&amp;quot;,sizeof(&amp;quot;Uart Test\r\n&amp;quot;)-1);&lt;br /&gt;
  &lt;br /&gt;
  GUI_SetColor(GUI_BLUE);&lt;br /&gt;
  for(;;)&lt;br /&gt;
  {&lt;br /&gt;
    switch(uart_evt.evt_type)&lt;br /&gt;
    {&lt;br /&gt;
      case UART_EVT_RX_TIMEOUT:&lt;br /&gt;
        len = UART_Read(Buf,uart_evt.status);&lt;br /&gt;
        UART_Write(Buf,len);      //从串口发出&lt;br /&gt;
        Buf[len] = 0;&lt;br /&gt;
        GUI_DispString((char const*)Buf);&lt;br /&gt;
        uart_evt.evt_type = UART_EVT_NONE;&lt;br /&gt;
        break;&lt;br /&gt;
    default :&lt;br /&gt;
      break;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
    if(GUI_IsFull())    //判断是否为满屏&lt;br /&gt;
    {&lt;br /&gt;
      GUI_Clear();      //清屏&lt;br /&gt;
      GUI_GotoXY(0,0);  //回到原点&lt;br /&gt;
      FontColorChange();//改变字体颜色&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;其中，UART_Init是初始化串口函数，参数是传入的事件回调函数指针，可以为NULL。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : UART_Init&lt;br /&gt;
//&lt;br /&gt;
// brief : 初始化串口&lt;br /&gt;
//&lt;br /&gt;
// param : pHandle -&amp;gt;处理串口事件&lt;br /&gt;
//&lt;br /&gt;
// return :&lt;br /&gt;
void UART_Init(uart_user_callback pHandle)&lt;br /&gt;
{&lt;br /&gt;
  m_uart_callback = pHandle;&lt;br /&gt;
  &lt;br /&gt;
  uart_fifo_init();&lt;br /&gt;
  uart_timer_init();&lt;br /&gt;
  //baudrate = 115200&lt;br /&gt;
  nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG;&lt;br /&gt;
  uartConfig.pseltxd = TX_PIN_NUMBER;&lt;br /&gt;
  uartConfig.pselrxd = RX_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselcts = CTS_PIN_NUMBER;&lt;br /&gt;
  //uartConfig.pselrts = RTS_PIN_NUMBER;&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_init(&amp;amp;m_Uart,&amp;amp;uartConfig,uart_event_handler);&lt;br /&gt;
&lt;br /&gt;
  nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在UART_Init函数中，指定串口的TX，RX引脚，不使能流控制。且格式为115200，8，N，1。在调用nrf_drv_uart_init时，传入事件回调函数指针uart_event_handler，即使用导步模式。如果传入NULL，即使用阻塞模式。在uart_event_handler事件回调函数中，监视以下三个事件&lt;br /&gt;
# NRF_DRV_UART_EVT_RX_DONE&lt;br /&gt;
# NRF_DRV_UART_EVT_ERROR，&lt;br /&gt;
# NRF_DRV_UART_EVT_TX_DONE&lt;br /&gt;
NRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件；NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件，其中包括帧错误，无有效停止位等；NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。&lt;br /&gt;
&lt;br /&gt;
为有效控制数据帧之间的间隔，这里引入了定时器，用于监控串口接收空闲。其工作原理如下，当接收到有效数据时，打开定时器，在定时时间之内再次接收到数据时，清除定时器记数，从零重新记时。如果超时，则认为此帧数据已经结束，向上层上报接收超时事件，并携带此帧数据长度。定时器的初始化部分，不是此例程的重点，这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// fn : timer_uart_event_handler&lt;br /&gt;
//&lt;br /&gt;
// brief : 定时器事件回调函数&lt;br /&gt;
//&lt;br /&gt;
// param : event_type -&amp;gt; 事件类型&lt;br /&gt;
//         p_context  -&amp;gt; 事件附加指针  &lt;br /&gt;
//&lt;br /&gt;
// return : none&lt;br /&gt;
&lt;br /&gt;
void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (event_type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_TIMER_EVENT_COMPARE0:&lt;br /&gt;
      //串口接收超时&lt;br /&gt;
      {&lt;br /&gt;
        uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT;   //上报超时事件&lt;br /&gt;
        uart_user_evt.status = UART_BUF_DATA_LEN(&amp;amp;m_RxBuf); //携带数据长度&lt;br /&gt;
        if(m_uart_callback != NULL)&lt;br /&gt;
        {&lt;br /&gt;
          m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
&lt;br /&gt;
    default:&lt;br /&gt;
      //Do nothing.&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
//串口事件回调数据&lt;br /&gt;
static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context)&lt;br /&gt;
{&lt;br /&gt;
  switch (p_event-&amp;gt;type)&lt;br /&gt;
  {&lt;br /&gt;
    case NRF_DRV_UART_EVT_RX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        //关闭超时定时器&lt;br /&gt;
        nrf_drv_timer_disable(&amp;amp;m_TimerUart);&lt;br /&gt;
        //查询空闲字节&lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          //&lt;br /&gt;
          fifo_set(&amp;amp;m_RxBuf,rxBuf);&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        if(UART_BUF_FREE_SAPCE_LEN(&amp;amp;m_RxBuf))  &lt;br /&gt;
        {&lt;br /&gt;
          nrf_drv_uart_rx(&amp;amp;m_Uart, rxBuf,1);&lt;br /&gt;
          //启动超时定时器&lt;br /&gt;
          nrf_drv_timer_enable(&amp;amp;m_TimerUart);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
          //没有可用空间&lt;br /&gt;
          uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW;&lt;br /&gt;
          uart_user_evt.status = UART_BUF_SIZE;&lt;br /&gt;
          if(m_uart_callback != NULL)&lt;br /&gt;
          {&lt;br /&gt;
            m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_ERROR:&lt;br /&gt;
       &lt;br /&gt;
      break;&lt;br /&gt;
    case NRF_DRV_UART_EVT_TX_DONE:&lt;br /&gt;
      {&lt;br /&gt;
        uint8_t tmp;&lt;br /&gt;
        if (uart_data_get(&amp;amp;m_TxBuf,&amp;amp;tmp) == NRF_SUCCESS)&lt;br /&gt;
        {&lt;br /&gt;
            nrf_drv_uart_tx(&amp;amp;m_Uart, &amp;amp;tmp, 1);&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // Last byte from FIFO transmitted, notify the application.&lt;br /&gt;
            uart_user_evt.evt_type = UART_EVT_TX_EMPTY;&lt;br /&gt;
            if(m_uart_callback != NULL)&lt;br /&gt;
            {&lt;br /&gt;
              m_uart_callback(&amp;amp;uart_user_evt);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
      }&lt;br /&gt;
        break;&lt;br /&gt;
    default:&lt;br /&gt;
        break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;在timer_uart_event_handler函数中，主要监视定时器的超时事件，向上层上报串口接收超时事件。在uart_event_handler函数中，完成接收与发送工作。为配合串口收发工作，程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置，write_pos记录写入数据位置。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot; line=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
//******************************************************************************&lt;br /&gt;
// Name : buf_fifo_t&lt;br /&gt;
//&lt;br /&gt;
// brief : 串口接收缓存&lt;br /&gt;
//&lt;br /&gt;
typedef struct&lt;br /&gt;
{&lt;br /&gt;
  uint8_t* pbuf;&lt;br /&gt;
  volatile uint16_t  read_pos; &lt;br /&gt;
  volatile uint16_t  write_pos;&lt;br /&gt;
}buf_fifo_t;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;为方便使用，同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据，并更改read_pos；fifo_set 是将数据写入write_pos位置，并更新write_pos。&amp;lt;syntaxhighlight lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  *p_byte = pFifo-&amp;gt;pbuf[pFifo-&amp;gt;read_pos];&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;read_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;read_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;read_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte)&lt;br /&gt;
{&lt;br /&gt;
  pFifo-&amp;gt;pbuf[pFifo-&amp;gt;write_pos] = *p_byte;&lt;br /&gt;
  &lt;br /&gt;
  pFifo-&amp;gt;write_pos++;&lt;br /&gt;
  if(pFifo-&amp;gt;write_pos &amp;gt;= UART_BUF_SIZE)&lt;br /&gt;
  {&lt;br /&gt;
    pFifo-&amp;gt;write_pos = 0;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;完成串口驱动部分后，在main函数中，只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件，并在事件处理结构中，对串口数据进行读取并显示。&lt;br /&gt;
&lt;br /&gt;
==== 实验现象 ====&lt;br /&gt;
硬件准备：&lt;br /&gt;
# Jlink-Lite仿真器或J-Link仿真器&lt;br /&gt;
# NRF52832DK评估板&lt;br /&gt;
# TFT-LCD-144或TFT-LCD-130显示屏&lt;br /&gt;
软件准备：&lt;br /&gt;
&lt;br /&gt;
串口助手或相类似的软件&lt;br /&gt;
&lt;br /&gt;
编译工程，点击IAR IDE工具栏中绿色三角仿真按钮，IAR便会将程序下载到nRF52832中，点击全速运行即可。打开串口助手，选择相应串口号，通信格式为115200，8，N，1。在串口助手中发送数据，此时NRF52832DK上的LCD显示屏会显示发送的数据，同时串口助手本身也会收到发送的数据。&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
	<entry>
		<id>http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Uart.png&amp;diff=1763</id>
		<title>文件:Uart.png</title>
		<link rel="alternate" type="text/html" href="http://doc.iotxx.com/index.php?title=%E6%96%87%E4%BB%B6:Uart.png&amp;diff=1763"/>
		<updated>2019-07-11T01:10:34Z</updated>

		<summary type="html">&lt;p&gt;Erjin：&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;uart scheme&lt;/div&gt;</summary>
		<author><name>Erjin</name></author>
		
	</entry>
</feed>