熊小宇的技术博客

Since 2016

  • 首页
  • 分类
  • 关于
  • 归档
  • 标签

深入NXP低功耗蓝牙SDK开发系列 - 编程框架剖析

发表于 2018-09-30

前言

很久没有更新技术博客了。最近一年都在做低功耗蓝牙和物联网等相关的事情,想把一些经验和心得写成博客分享出来,也算对自己工作的一种总结。开一个系列讲解NXP的低功耗蓝牙SDK开发的方方面面,目前NXP官方仅有一个英文版的《BLE Application Developer Guide》介绍SDK的使用,对于国内开发者来说不太友好。希望这个系列文章能作为官方文档的补充,帮助到正在使用和将要使用NXP蓝牙的芯片的朋友们。

weil家深入这个通用的低功耗蓝牙SDK与协议栈,迅速掌握NXP低功耗蓝牙的开发。假设用户已经具备了基本的BLE概念,如Profile、GAP、GATT、HCI、LL等各个层次的职责,广播与连接的行为,基于GATT数据传输的方式等。如果读者对这个概念还不熟悉,可以阅读《Getting Started with Bluetooth Low Energy》一书的第1至3章共75页内容,利用业余时间在2-3天内可以轻松完成。

NXP BLE SoC概况

目前NXP面向物联网和可穿戴设备数据传输应用的BLE SoC芯片要有QN和KW两大系列(型号特点见下表),除了QN902x使用原Quintic自带的协议栈SDK外,其他几个型号都采用了NXP MCUXpresso SDK和NXP自研的Bluetooth协议栈,呈现给用户的编程接口完全一致。无论是开发超低功耗的QN9080,支持Zigbee/Thread/BLE多模的KW41,还是面向汽车应用的KW36,对固件工程师来说,掌握了一款芯片的开发,拿下其他的也就是轻而易举之事。

型号 协议版本 内核频率 存储器资源 特色
QN902x BLE 4.2 Cortex-M0 @32MHz 128KB Flash / 64KB RAM / 128KB ROM 低成本
QN908x BLE 5.0 Cortex-M4F @32MHz 512KB Flash / 128KB RAM 超低功耗
KW41 BLE 4.2 Cortex-M0+ @48MHz 512KB Flash / 128KB RAM 多模多协议
KW36 BLE 5.0 Cortex-M0+ @48MHz 512KB Flash / 64KB RAM 汽车网络
K32W BLE 5.0 Cortex-M4F & Cortex-M0+ @72MHz 1.25MB Flash / 384KB RAM 单芯片高集成度

MCUXpresso SDK与NXP BLE协议栈

MCUXpresso SDK是NXP面向MCU市场推出的一套完整的软件开发套件,支持Kinetis, LPC, i.MX RT等通用微控制器以及QN和JN系列的无线连接微控制器。一个SDK包内包含了芯片硬件抽象层,外设驱动库,多个中间件库(根据芯片功能的不同包含不同的中间件,比如带Ethernet外设的MCU则会有lwip网络协议栈中间件),以及丰富的示例代码。

对于低功耗蓝牙MCU,它的BLE协议栈作为MCUXpresso SDK的一个中间件而存在。在用户解压缩SDK包后,在./middleware/wireless/bluetooth_x.y.z下可以找到协议栈的代码和库文件。低功耗蓝牙应用将包含这个目录下的文件,以使用协议栈提供的API和服务。所有低功耗蓝牙的示例代码可以再./boards//wireless_examples/bluetooth目录下找到。同时,低功耗蓝牙应用与通用MCU SDK还有一点差异,它需要一些额外的通用组件作为支撑,如OS环境抽象、非易失存储、队列管理,低功耗管理等,因此多引入了一个connectivity framework的中间件层。在./middleware/wireless/framework_x.y.z下可以找到framework包含的所有功能模块源代码。下图展示了SDK的文件结构中与低功耗蓝牙相关的目录。

MCUXpresso SDK文件目录组织

如果基于MCUXpresso IDE开发,通常会导出某个示例工程到workspace目录下,被导出的工程与解压缩后的SDK的layout稍有不同,它仅包含了该示例代码所需的驱动和中间件,并以一个平铺的方式组织文件夹,但文件夹下的内容并无区别。

NXP BLE SDK系统架构

NXP提供了一套完整的BLE协议栈和编程框架,并已通过了Bluetooth Core Spec 5.0的认证。用户第一次开发自己的应用时,应当先大致了解整个系统的组成,知道每个部分的职责,分别有哪些文件。有了一些基础,再通过查询API文档,很快便能掌握BLE应用开发。下图为BLE SDK总体的框架图,图中使用了几种不同的颜色来区分:

  • 应用(灰)
  • 低功耗蓝牙服务框架(黄)
  • Connectivtiy Framework(绿)
  • BLE协议栈(蓝)
  • 通用SDK(白)

NXP BLE SDK系统架构

OSA

OSA属于Connectivity Framework,单独介绍它是因为OSA服务于整个SDK和协议栈,并且也是系统复位后的入口。整个蓝牙协议栈和SDK的设计使用了RTOS下才有的多任务,消息和事件等服务。为了减少代码大小和栈的内存,适配一些资源紧张的SoC,在设计时才引入了OSA。

OSA提供一套接口封装,底层可以使用FreeRTOS或者Bare Metal前后台两种实现。如果底层为FreeRTOS,OSA只是简单的将FreeRTOS API转换为OSA API。如果底层底层为Bare Metal系统,OSA则内部实现了一套多任务、信号量、事件等RTOS API的模拟实现,当用户调用OSA_Start启动多任务调度环境时,实际上是陷入了一个while(1)的循环,在这个循环中会不断的检查任务队列的状态并选择优先级最高的任务来运行,这些任务之间无法抢占,因此每个任务的设计需要额外小心,占用CPU的时间最好不要超过2ms,以确保处理BLE事件的任务能及时得到调度。在编写OSA的任务代码时,有几个不同与一般RTOS的地方需要注意。

  • 任务体while(1)循环的最后需要加上一个条件判断,如果是gUseRtos_c==0,则直接跳出循环,相当于每次任务只执行一次。
  • 任务体while(1)循环之前的初始化工作需要特殊化处理,定义一个静态或者全局变量initailized的,并对它进行测试,确保在当OSA运行在Bare Metal配置时,这些初始化代码仅会运行一次。
  • OSA_EventWait, OSA_SemphoreWait, OSA_MutexLock等函数在OSA的FreeRTOS实现时会阻塞任务,而在Bare Metal实现中,调用这些API不会阻塞,而是通过内部一个状态机来检测资源,如果资源目前是不可获得状态,则直接返回。
  • OSA_TimeDelay函数在BM实现时是不会切换到其他任务运行的,而是在原地等待直到超时。

OSA的源码可以在实现./middleware/wireless/framework_x.y.z/OSAbstraction下找到,如感兴趣可以深入研读。

SDK提供的每一份BLE示例代码中都提供了freertos和bm两个工程。

Connectivity Framework

Connectivity Framework包含了众多的子模块,服务于应用和BLE协议栈。本系列文章将会抽取其中几个比较重要的几个子模块作为单独的文章来分析。本文希望先给读者一个总体概念,通过表格列出了各个模块的职责和必要性。标注为“必要”的模块如果缺失,最后整个BLE工程构建将会失败,说明该模块已被协议栈或者其他的代码引用了。标注为“非必要”的模块目的是服务应用层,如果用户的应用不需要这个功能,则可以移除。

子模块 职责 必要性 说明
FunctionLib memcpy等标准函数的封装 必要 通过头文件配置选择私有实现或直接使用C库
List 通用双向链表实现 必要 List是MemManager和Messaging的基础
MemManager 基于块的内存分配 必要 协议栈所需的动态内存都从这里分配
Messaging 消息队列服务 必要 不具备任务同步功能,需配合OSA Event使用
OSAbstraction 提供操作系统服务抽象 必要 整个系统调度都依赖于OSA
TimersManager 提供软定时服务 必要 协议栈超时机制需要使用到
SecLib 封装SMP加密操作 必要 部分芯片可以选择使用硬件加密加速引擎
LowPower 实现低功耗管理 非必要 仅当系统需要使用低功耗时用到
OtaSupport 提供OTA服务 非必要 仅包含OTA的应用需要用到
SerialsManager 提供不同串行通讯链路层的封装 非必要 当配置协议栈为DTM模式或Host Only模式时需要)
Flash 对不同芯片内部和外部Flash操作的封装 非必要 当包括NVM模块时为必要
NVM 在Flash上提供一套非易失存储机制 非必要 仅当系统需要用到Bonding时才为必要
MWS 多协议共存协议 非必要 当需要与外部WLAN芯片配
Shell 提供控制台交互和格式化打印功能 非必要 服务于应用程序
GPIO/LED/Keyboard 提供简单输入输出能力 非必要 服务于应用程序
Reset/Panic/RNG 对系统各个功能的简单封装 非必要 服务于应用程序

BLE协议栈

BLE协议栈以闭源库的形式提供,分为Host和Controller两部分。下图描述了组成Stack的主要文件和相互之间的依赖关系。image.png

Controller Stack负责与硬件和时序相关的蓝牙链路层处理,它通过HCI接口与Host Stack进行协同工作完成BLE数据交互。由于使用BLE SDK时几乎不需要直接与Controller Stack打交道,我们知道它的存在即可。

Host Stack是一个与硬件无关的C库,它通过HCI接口向Controller Stack发送命令和接收消息,负责完成Bluetooth Core Spec中的L2CAP, ATT, SM, GATT, GAP等多个层次职责。Host Stack通过一组头文件暴露出协议栈各个层的API和回调事件接口,理解Host Stack提供的服务和数据结构对于掌握BLE SDK开发至关重要。本系列专题将以独立文章分享各个部分服务的具体应用,这里先将协议栈的两类主要接口介绍给大家。

  1. API
    API即应用程序编程接口,是外部应用主动向协议栈发起请求的入口。协议栈所提供的绝大部分API都是异步的,利用Messaging消息队列向协议栈的Host任务发送一条后便返回。实际的处理是在Host任务中完成,再通过某个注册的callback回调函数来反馈执行的结果。

  2. Callback注册回调函数
    Callback注册回调函数是由应用程序提供的一个钩子函数,在协议栈内部处理中如果有需要通知到用户层事件发生或者数据变化时,将调用注册的钩子函数通知应用程序做出响应动作。

下面一段示例代码演示了Gap_Connect()和Gap_Disconnect()两个异步API的行为和他们所触发的callback回调函数后对应事件的响应。通过这段代码可以大致了解应用代码如何与协议栈进行交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 1. initiate API to request and connection and register a callback
bleResult_t rt;
rt = Gap_Connect(&gConnReqParams, Gap_ConnectionCallback);
if (rt != gBleSuccess_c) {
printf("Gap_Connect API fail, reason = %d\r\n", rt);
}
// when PC(R15) arrives here, the connection may NOT be setup
...
// 2. initiate API to request a disconnection action.
rt = Gap_Disconnect(peerDeviceId);
if (rt != gBleSuccess_c) {
printf("Gap_Disconnect fail, reason = %d\r\n", rt);
}
// when PC(R15) arrives here, the connection may NOT be disconnected
...
// 3. Event will be triggered when connection and disconnection happens
void Gap_ConnectionCallback(deviceId_t peerDeviceId, gapConnectionEvent_t* pConnectionEvent)
{
switch (pConnectionEvent->eventType)
{
case gConnEvtConnected_c:
// async event to notify that link is connected
printf("Connected to device (%d)\r\n", peerDeviceId);
break;
case gConnEvtDisconnected_c:
// async event to notify that link is disconnected
printf("Disconnected to device (%d)\r\n", peerDeviceId);
break;
...

低功耗蓝牙服务框架

低功耗蓝牙服务框架这个名称是笔者取给SDK中非协议栈部分的蓝牙代码取一个名字,涉及内容主要包括有HCI、FSCI、Profiles、Connection Manager, Service Discovery, App Thread, BLE Initialization, Stack Runtime Environment等几个部分,顾名思义是这些代码都是为实现BLE应用而服务的,目的是简化用户开发的难度。

  • Stack Runtime Environment

上一小节介绍的Host和Controller协议栈库提供的是一组可链接的符号,需要外部创建任务以提供运行时环境和创建必要的队列和事件资源,这就是Stack Runtime Environment的职责。协议栈提的了两个任务处理函数Host_TaskHandler()和Controller_TaskHandler(),分别在创建的Host_task和Controller_task里调用。另外任务处理函数还需要用到队列和事件资源,用户任务与Host_task交互、以及Host_task与Controller_task的交互都依赖于他们。下表说明了所创建资源的用途。

资源 类别 用途
gApp2Host_TaskQueue 消息队列 用户任务调用Host Stack API时,向这个队列插入一个请求
gHci2Host_TaskQueue 消息队列 Controller Taskdan产生HCI事件时,向这个队列插入一个请求
gHost_TaskEvent OSA事件 用于通知Host Stack队列中有新的消息插入
mControllerTaskEvent OSA事件 Host Task向Controller Task发送HCI命令时或中断向Controller Task请求服务
  • App Thread

App Thread是在系统启动后由OSA创建的第一个任务,它在完成BLE系统初始化后创建了一个应用后台,主要目的是服务Host stack触发的各类callback回调事件。通过Host_stack提供的Registeration API注册的回调函数被调用的上下文都是前面介绍的Host task里,如果在这些callback做一些耗时较长的用户动作(比如上面的printf打印输出),Host task其他部分会得不到响应,从而影响BLE协议交互。因此很有必要将这些callback回调事件的处理放到一个相对低优先级任务中,这个任务就是App Thread。首先Applmain.c中对Host stack提供的回调函数接口都做了实现,这些实现并不作实际处理,而是转发一个消息到App Thread。App Thread一直处于等待事件的状态,收到事件后再分发到各个自定义的callback中作相应处理。App Thread同时还可以通过等待另外一个队列来接收其他任务发送的Application消息并处理,以方便用户实现类似于协议栈的异步处理机制。下表列出了App Thread的队列和时间资源以及他们的用途。

资源 类别 用途
mHostAppInputQueue 消息队列 Host Stack产生的callback里将向这个队列插入一个请求
mAppCbInputQueue 消息队列 App Thread本身或者其他用户任务都可以向这个队列插入一个请求
mAppEvent OSA事件 用于通知App Thread有队列里有新的消息插入

下面代码段以App_Connect为例来展示了代码是如何通过App Thread来处理Host stack回调函数的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// User call App_Connect, instead of Gap_Connect
bleResult_t App_Connect(gapConnectionRequestParameters_t* pParameters,
gapConnectionCallback_t connCallback)
{
pfConnCallback = connCallback;
return Gap_Connect(pParameters, App_ConnectionCallback);
}
// Simplified App_ConnectionCallback, the function insert a message and notify App Thread
void App_ConnectionCallback (deviceId_t peerDeviceId, gapConnectionEvent_t* pConnectionEvent)
{
appMsgFromHost_t *pMsgIn = NULL;
uint8_t msgLen = GetRelAddr(appMsgFromHost_t, msgData) + sizeof(connectionMsg_t);
pMsgIn = MSG_Alloc(msgLen);
// use Applmain defined message type
pMsgIn->msgType = gAppGapConnectionMsg_c;
pMsgIn->msgData.connMsg.deviceId = peerDeviceId;
FLib_MemCpy(&pMsgIn->msgData.connMsg.connEvent, pConnectionEvent, sizeof(gapConnectionEvent_t));
MSG_Queue(&mHostAppInputQueue, pMsgIn);
OSA_EventSet(mAppEvent, gAppEvtMsgFromHostStack_c);
}
// Pseduo code of App_Thread
void App_Thread (uint32_t param)
{
while(1) {
OSA_EventWait(mAppEvent, osaEventFlagsAll_c, FALSE, osaWaitForever_c , &event);
if (event & gAppEvtMsgFromHostStack_c) {
while (MSG_Pending(&mHostAppInputQueue)) {
pMsgIn = MSG_DeQueue(&mHostAppInputQueue);
// check msgType and dispatch callback handler
if (pMsg->msgType == gAppGapConnectionMsg_c) {
pfConnCallback(pMsg->msgData.connMsg.deviceId, &pMsg->msgData.connMsg.connEvent);
} else if (pMsg->msgType == ...) {

通过下图可以全面的了解到App Thread, Host task和Controller task三者之间是如何完成交互的。掌握了这张图的消息交互流程,开发基于NXP BLE SDK的应用也就变的非常容易了。

ky

  • HCI
    HCI是低功耗蓝牙Controller协议栈和Host协议栈之间的沟通桥梁,对于SoC,两个协议栈都运行在同一芯片上,这部分功能实际上是简单的函数调用。当BLE SDK被配置为只作为controller Only模式(用于DTM),或者Host Only模式(controller使用另外一颗芯片)时,HCI的就需要与通过SerialManager来完成与外界通讯。

  • FSCI
    FSCI是Framework Serial Communication Interface的缩写,该模块定义了一套统一的接口将无线通讯微控制器(BLE,Thread,Zigbee)所提供的服务提供给另外一颗主处理器或者PC系统。对于BLE SoC而言,片上运行了完整的BLE Host和Controller协议栈,此时处于Network Processor网络处理器模式。外部处理器在只需要遵循FSCI协议便能控制SoC发起广播、建立连接和进行数据交换。

  • Profiles
    在GATT之上,Bluetooth SIG定义了多个标准的GATT based Profile帮助应用层互联互通,同时每个厂家也会定义一些简单的私有Profile来实现raw数据传输、OTA等功能。由于所有的Callback回调事件处理都是在应用中完成的,Profile在NXP BLE SDK中职责比较简单,主要负责完成Profile数据到GATT数据库的操作转换。

  • Conneciton Manager 与 Service Discovery
    连接建立和服务发现是每个BLE应用都需要经历的过程,BLE SDK将这部分通用的代码从应用层分离了出来,形成两对独立的.c/.h文件ble_conn_manager.c/h和ble_service_discovery.c/h。这样减少了应用层重复的代码,提高代码可维护性。这两个部分服务的功能在后面介绍蓝牙SDK具体编程的文章中都会有所涉及。

  • GATT Database
    通过一套宏定义的方法,完成静态或者动态GATT数据库的建立。第一次看到gatt_db.h会很难以理解里面的特殊格式,但要是“照猫画虎”的增加自己的service或者characeteristic还是比较简单的,这就是它的神奇之处。写一篇文章专门讲解gatt_db模块到底是如何通过一系列的宏定义来成这个工作的。

应用

BLE SDK的每个应用示例都包含了十分类似的应用层文件,下表简单描述了各个文件的职责。其中.c/h是整个应用的核心。

文件名 职责
app_config.c BLE广播参数、扫描参数、安全性相关的配置
app_preinclude.h 全局参数配置文件(其他文件不需要include此文件,在IDE里已配置)
gatt_db.h GATT database描述文件
gatt_uuid128.h 私有128位UUID描述文件
.c/h 应用示例代码,如heart_rate_sensor.c,广播发起、扫描,以及各种BLE协议栈事件的处理

小结

本文从零开始介绍NXP BLE SDK系统架构,逐步分析了各个子模块的功能。对于BLE协议栈和服务框两部分作了较为细致的讲解,以帮助大家了解自己编写的应用是如何与框架进行交互的。本文是《深入NXP低功耗蓝牙SDK开发系列》的第一篇,涵盖内容较广。如果您觉得其中某部分功能的介绍太过简单,不要心急,留言给我,后续会有多篇文章一步步教大家如何上手BLE SDK开发,敬请期待吧:)。

模块化你的printf打印信息

发表于 2017-02-18

处理器都会带有JTAG/SWD的调试端口进行侵入式的调试,但是在许多应用场景下这种方式仍是不太适合的。比如调试一个无线通讯系统,你希望捕获其中一些讯息但又无法停下来处理器来进行断点观察,因为一旦暂停则无法处理正在进行交互的信息而导致系统工作不正常。这种情况在代码中嵌入printf打印代码来调试最佳。

但当软件成规模的时候,控制台会有一堆的打印log产生,可能会造成几个负面影响:

  1. 影响代码体积
  2. 影响性能
  3. 难以找到需要关注的信息

如果手动去禁止和打开log无疑是不可取的,那么有什么简单而有效的来管理这些log呢?以下代码提供了一个很好的解决方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include "debug_config.h"
#if defined (CFG_MODULE_x_DEBUG_EN) && (CFG_MODULE_x_DEBUG_EN > 0)
#define MODULE_x_DEBUG printf
#else
#define MODULE_x_DEBUG 1 ? (void) 0 : (void) printf
#endif
void func(void)
{
MODULE_x_DEBUG("Hey I'm module x prompts\r\n");
}

上面打印语句在

CFG_MODULE_x_DEBUG_EN 1```的时候等同于
1
2
```
printf("Hey I'm module x prompts\r\n");

而当

CFG_MODULE_x_DEBUG_EN 0```或者没有定义的时候等同于
1
2
```
1 ? (void) 0 : (void)printf("Hey I'm module x prompts\r\n");

这句话句在编译阶段就会C编译器优化并不会对代码体积或者运行速度有影响。

通过这个方法在debug_config.h"中对于多个CFG_MODULE_x_DEBUG_EN宏定义进行管理可以很好做到单独使能或者禁止各个模块的打印信息。

I2C从机挂死分析和解决方法

发表于 2017-02-18

I2C几乎是嵌入系统中最为通用串行总线,MCU周边的各种器件只要对速度要求不高都可以使用。优点是兼容性好(几乎所有MCU都有I2C主机控制器,没有也可以用IO模拟),管脚占用少,芯片实现简单。I2C协议虽然简单,实际使用过程中小毛病还不少。今天先来看一个平日最为常见的问题:I2C从机挂死。

很多事情不难而且经常碰到,每次自认为懂了但最终让你站出来说清楚的时候却总是不能自圆其说,很难受。所以我决定写博客的时候就想尽量把内容写清楚详细甚至是透彻,希望让每一个阅读博文的同学都能看得明明白白,学会一点小知识。如果还有不清楚的可以留言交流.

I2C规范与特性

I2C是什么,我相信99%的同学能点到这篇博文对I2C也有了一定的了解,这里附上一份I2C鼻祖NXP (前Philips半导体)的一份权威手册:I2C-bus specification and user manual v.6

描述一下I2C最重要的几个特性,为了后面描述问题和解决方案作一些铺垫。

  1. I2C是由两根线(时钟SCL + 数据SDA)组成的多主多从串行同步通信总线。
  2. 规范要求接入I2C的器件,SCL时钟和SDA数据线都必须是双向开漏结构的,通过总线上的上拉电阻拉到逻辑高电平。这样的结构可以实现线与(&)功能。
  3. 一般情况下I2C的SDA只有在SCL为低电平的时候才能改变,为高电平的时候需要保持。对应到芯片设计上则是上升沿采样,下降沿变化。
  4. 两个例外情况由主机发出的总线起始条件START(SCL为高时SDA由高变低)和停止条件STOP(SCL为高时SDA由低变高)

挂死 = 挂了 + 死机

挂死这个词应该来源于英文hangs : To cause (a computer system) to halt so that input devices, such as the keyboard or the mouse, do not function.

前面提到因为线与&结构,是I2C总线设计上最关键的特征,用了这种结构才能实现

  • 多主机仲裁同步
  • 慢从机同步快主机

因为这个特性,只要总线上任何一个器件拉低了SDA或者SCL,其他器件都无法拉高它们,看到的都是低电平。如果有器件不释放总线,则整个总线上的通讯都会被暂停,我们成为I2C bus hangs:I2C总线挂死

因为I2C主机一般是可编程的器件,受我们控制,如果主机主动拉低了总线,我们可以通过调试代码了解原因,也可以很方便的通过复位I2C外设或者复位芯片来退出这种状态。而I2C从机往往不带RESET引脚,如果挂死了总线即使整个系统复位都无法解除,仅重新上下电才可以恢复。很多系统上是不可接受的,因此我们需要更加小心的处理I2C从机挂死的情况,下面分析也是针对I2C从机挂死来写的。

SDA挂死

先来看下哪些情况下I2C从机会需要拉低SDA线。

  1. 主机向从机写数据或地址时,从机如果发出ACK应答,则会第9个CLK的期间拉低SDA
  2. 主机读数据的时候,从机会在bit为0时对应的CLK期间拉低SDA

那什么情况I2C从机又可能钳住SDA线呢?我们先来看一个典型的I2C主机发起对某一器件地址读操作,读到的数据为10011000b,MSB在先也就是0x98。在图中地址字节第9个CLK期间从机拉低SDA表示对地址进行应答,在返回的数据字节的第2,3,6,7,8几个CLK器件从机拉低SDA输出逻辑0电平。

根据上面讲的I2C协议SCL为高的时候,SDA电平应保持,而等到SCL为低后(也就是下降沿后)才能发生改变。如果在上面几个CLK的前半个周期SCL拉高后主机不再拉低呢?从机会有什么动作?YES,从机会持续拉低着SDA,直到见到下一个他应该输出高电平的下降沿。

最常见的情况就是主机在通讯的过程中产生了复位。由于复位动作通常会立刻执行,外设状态机都恢复到默认状态,也就发不出完整的CLK了。那么等到主机复位完成回来后,SCL为高,SDA被从机拉低。主机无法发起START起始条件,不能开始下一次与从机的通讯,这称为SDA挂死。

要想办法恢复,我们先得知道从机什么时候会释放SDA。由于刚刚的SCL下降沿没有给出来,恢复总线要做的第一件事情就是在想办法用GPIO在SCL线上模拟一个下降沿,让从机状态机继续走下去。只发一个下降沿并不一定能将SDA释放,因为我们并不清楚当主机复位异常发生时刻从机到底处于图中哪一个状态,所以需要逐个CLK去探测,直到见到SDA被释放了,我们才终止并且发送STOP条件告诉从机这次坑爹的通讯结束了。

网上通常的传授的方法是模拟9个连续的CLK,但是我更喜欢上面的方法,一是速度快,二是具备可确定性。发送9个CLK我主要担心从机在最后一个CLK时又拉低了SDA,还是需要用到上面的方法来释放。

通过模拟几种情形来实际体会一下(从机对SDA的操作红色表示):

如果在地址字节第9个CLK拉高后主机复位。在模拟的第一个时钟低电平期间就可以看到SDA的释放,随后主机先拉低SDA,再模拟一个STOP结束条件。

在数据字节第2个CLK拉高后主机复位,在第二个模拟的时钟低电平期间才看到SDA释放

在数据字节第6个CLK拉高后主机复位,在第三个模拟的时钟低电平期间才看到SDA释放

通过以上三种情况的分析,想必你已经非常清楚改如何处理了,最后附上一个程序处理流程图:

SCL挂死

I2C从机主动拉低SCL线在规范中是一个合法的行为,称之为Clock Stretching(时钟扩展,我一般叫他时钟同步)。通常是主机请求数据( 收或者发)后从机需要一些时间处理,且没有多余Buffer可以接收接或者提供接下来的数据的时候从机则会拉低SCL一段时间直到有新的数据准备好。

SCL挂死(也就是前面所说一直拉低SCL)这种情况在标准I2C从器件上基本不会出现,因为只要芯片还在正常工作buffer总算有准备好的时候,自然就就释放SCL了。往往是使用用户使用MCU作为I2C从机时,程序设计上的问题导致MCU无法读取&填充buffer而导致,重点分析MCU I2C中断服务程序。

  1. I2C中断服务程序被意外屏蔽
  2. 中断服务程序中陷入了一些标志位查询的while(flag != xxx)死循环
  3. I2C功能系统被意外禁止

树莓派3B+ Raspbian桌面分辨率设置

发表于 2017-02-06

查看支持分辨率

第一次安装了Raspbian后启动系统会模式使用显示器“兼容模式”,这是一个VGA @ 60Hz的设置保证所有的HDMI连接到任何显示设备都能有显示。进入桌面后用户可以通过系统配置文件/boot/config.txt来修改分辨率

首先启动Shell输入命令查看系统所支持的模式:
如果用的是显示器:

1
/opt/vc/bin/tvservice -m DMT

如果接的是电视:

1
/opt/vc/bin/tvservice -m CEA

在我的DELL显示器上给出的列表中:

1
2
3
4
5
6
Group DMT has 11 modes:
mode 4: 640x480 @ 60Hz 4:3, clock:25MHz progressive
mode 6: 640x480 @ 75Hz 4:3, clock:31MHz progressive
...
(prefer) mode 82: 1920x1080 @ 60Hz 16:9, clock:148MHz progressive
mode 83: 1600x900 @ 60Hz 16:9, clock:108MHz progressive

选择合适分辨率

一般选择最优的模式,即1920x1080 @ 60Hz。
下面命令修改并确保参数是正确的,CTRL+O 回车保存,CTRL+X退出。

1
sudo nano /boot/config.txt

1
2
3
4
5
...
#hdmi_safe=1 // 禁止safe模式
hdmi_group=2 // 1=电视, 2=显示器
hdmi_mode=82 // 1920x1080@60Hz
...
1
Reboot

作死

手痒试一试选择不支持的分辨率(e.g. mode=3)…果然…黑屏了….

也不用担心,默认启动的时候按住SHIFT进入Recovery状态,Edit Config中也可以编辑/boot/config.txt文件,修改回正确数值即可。

喝咖啡理解三层云服务IaaS/PaaS/SaaS

发表于 2016-12-26

思考三层云服务的合理比喻

在网上看到一些把三层云服务比喻成吃披萨,开养猪场等例子,相当生动形象。知乎: 谁能举个通俗易懂的例子告诉我IAAS,SAAS,PAAS的区别? 但有一点似乎都被大家都忽略了,三层云服务所面对的客户是有所区别的。我觉得有必要把比喻打得更加贴切一些。写下自己的理解,从买一杯咖啡开始。

SaaS:软件即服务 = 开个咖啡店

SaaS 是 Software as a Service 的缩写

在穿过海岸城广场去往地铁站的路上,一字排开的临街商铺前总热闹非凡。各式各样的门店来满足着消费者的生活需求:

  • 餐饮
  • 文化
  • 教育
  • 电子产品
  • 其他…

SaaS服务提供商跟这些商铺一样,也是云服务中与终端消费者日常接触的一层,来满足各种不同的应用需求。困了我会买一杯香浓的咖啡,不需要自己选咖啡豆、买咖啡机再翻出各种攻略来研究怎么煮一上壶。同样我有文件要存储共享我会使用云盘,而不是自己建个存储平台再搭个FTP什么的,为了可靠安全的存储我可能会选择一些大公司产品,比如iCloud Drive,所以付点钱也是值得的。

除了办公、存储、社交、视频这些面向个人消费者的SaaS云服务,还有很大一部分是面向企业的SaaS,如Saleforce, GoToMeeting等。如果把企业看做一个消费者,这之间没有什么实质差别。

角色扮演:

  • SaaS 提供商:咖啡店及各种商铺
  • SaaS 消费者:普通消费者

PaaS:平台即服务 = 运营购物中心

PaaS 是 Platform as a Service 的缩写

看到生意如此火爆,我也打算开个熊小宇の咖啡店。第一步是找铺面,一开始宣传资金和运营经验都是不够的,需要找一个相对成熟商场。而Shopping Mall的运营中心正有我所有需要的东西:

  • 出租繁华地段的铺面
  • 合理的空间布局
  • 相邻商铺间的内容互补分析
  • 人流量与消费数据分析报告
  • 饮品相关的品牌和供应渠道

剩下要做的就是专注于设计自己的店铺,装修和请人干活了。购物中心先租赁或者购买物业的产权,进行品牌包装,整体设计和零售管理,来打造一个一站式的平台,形形色色的商家只要联系购物中心,缴纳租金和服务费就可以开店了。

云服务中PaaS这一层的就像是运营购物中心,PaaS提供给做SaaS服务的公司的一个基础平台,包括数据库、SDK、部署工具、控制台等等组件或框架,同时也提供行业解决方案与技术咨询&支持。

如果你的需求只是一杯咖啡,你是不会去跟购物中心打交道的。购物中心面向客户仅是需要开店的商家。同样PaaS面向SaaS开发者的服务,而SaaS则是面向个人或者企业消费者的服务。

角色扮演:

  • PaaS 提供商:购物中心运营商
  • PaaS 消费者:咖啡店及各种商铺

IaaS:基础设施即服务 = 当个地产开发商

IaaS 是 Infrastructure as a Service 的缩写

开了咖啡店又不满足了,我也希望能开一间小型的购物中心。了解过后知道一个购物中心也不都是从零开始打造的。首先会寻找一个繁华的地段、选择适合的场地建筑和基础设施,和开发商签约,再开始购物中心包装。而这么一个场地的建造则是由地产开发商完成的,包括:

  • 设计规划
  • 建筑施工
  • 水电安装
  • 管道铺设

整个工程工期漫长又耗资巨大,一般不是中小公司能玩转的。在基础设施建造完毕后,地产开发商再将毛坯物业出租或者出售给运营购物中心的企业,实现盈利。

IaaS跟这地产商建房子相似,提供给消费者的服务是对所有计算基础设施的利用,包括处理CPU/GPU、存储、网络和其它基本的计算资源,以及运行环境所需的操作系统与虚拟化环境。同样重要的一点:IaaS的客户是PaaS服务商。

很多时候一些大的服务提供商同时会提供IaaS和PaaS的服务,比如阿里云和亚马逊AWS。这就像万达广场,除了整体的设计建造是万达集团的同时也自己的Shopping Mall品牌来运营购物中心。

角色扮演:

  • IaaS 提供商:地产开发商
  • IaaS 消费者:购物中心运营商

本地部署

当然,我也可以自己从头开始打造一间咖啡店。买水泥、建房子、装水电、搞装修、买设备、进货、雇员工一切都自己来,这就是本地部署。不过算一笔账,开个咖啡店的成本本来只要二十万,时间一个月。但建房子成本需要一千万,时间两年,并且其中环节诸多,傻子才这么做啊。

第二代蓝牙BLE SoC横向比较

发表于 2016-12-16

BLE背景

从2010年7月Bluetooth SIG年发布蓝牙4.0 LE(Low Energy)低功耗版本蓝牙,到今天也才6个年头,而在iPhone和Android手机给力的支持下BLE得到了前所未有的快速普及。根据市场调研报告表明,到2020年,将会有84亿的设备通过BLE技术连接,市场符合增长率高达29%。
随着智能硬件和创客概念的迅猛发展,各种新概念的智能产品也层出不穷,BLE作为其中最为广泛应用的无线技术,成为了人和数字信息世界之间沟通的重要桥梁。这也是我们为什么要学习BLE的原因:只要你有好的idea,利用BLE技术可以迅速使你的idea和产品连接上智能手机和移动互联网的海量用户。

第二代SoC

回到产品,目前市面上常见的BLE SoC大体上可以分为两代,第一代是以TI、Nordic、CSR最早三家做BLE SOC的先驱者,经过几年的发展已经趋于成熟,价格也稳定在1美金以下,处于跑量的状态。第二代BLE SoC是在新的设计在制程更新到55nm后,功能性能功耗有着一次质的飞跃,除了之前几个老玩家以外,几家瞄准了IoT节点与Connectivity的MCU大厂也纷纷加入这场残酷战斗,增添了不少火药。

入围名单

  • nRF52832(Noridc)(表示不服:我可是第三代都公布)
  • DA14680(Dialog)
  • QN9080(NXP)(准备量产)
  • CC2640(TI)
  • PSoC4 BLE(Cypress)
  • EFR32BG1(Silicon Labs)

其他几颗SoC如 CSR102x (CSR),ATSAMB11(Microchip),TC35678(Toshiba)因资料不多或者或还未有成熟应用暂时没有放进来。

横向比较

小结

  • 最佳均衡奖:nRF52832 (最重要还是成熟)
  • 最低功耗奖:QN9080 (新星,8MHz 跑协议栈, 3.4mA Tx @ 0dBm, 3.6mA Rx)
  • 最佳射频奖:CC2640(-97dBm)
  • 最大功率奖:EFR32BG1(+19.5dBm,好像蓝牙SIG只定义了10dBm…Orz)
  • 最大容量奖:DA14680(1MB Flash)
  • 最佳配角奖:PSoc4 BLE(路过… 非黑)

电容器与水桶的类比

发表于 2016-10-11

第一部分:电容器的电容C的【定义式】和【决定式】


平行板电容器电容C的【定义式】

电容器所存储电荷量Q与两端电压U的比值为电容器的电容C,即

C = Q / U

表示当存储1Q的电荷量时若电压为1V,那么电容为1F。

上述定义式仅表示在电路中量度该物理量的一种方法,我们不可以说”平行板电容器的电容C跟两板间所带的电荷量Q成正比,跟两板间的电压U成反比”。因为电容器的电容C是客观存在的,当两板不带电时,电容C依然存在。它的大小有【决定式】来决定。

平行板电容器电容C的【决定式】

决定式就是表示该物理量的大小都与哪些因素有关系,通常是由其物理特性决定的,与所连接电路无关。对于电容器的电容C来说:

C = εrS / 4πkd

其中k为静电力常量,库仑定律中的 k = 1 / (4πε0),代入上面公式也可以得到更为简洁的公式:

C = εrε0S / d

ε0是真空介电常数,等于8.854187818 x 10-12 F/m
εr为平板电容器绝缘材料的相对介电常数,跟材料有关系,它没有量纲。εr越大,电容C越大
S为平行板正对面积。S越大,电容C越大
d为平行板之间的距离。d约大,电容C越小

第二部分:电容器与水桶的类比


电容器类比于水桶的概念,有助于快速理解电容器在电路中任一时刻电容C、电压U、电荷Q三者的关系。

根据电容器电容C的定义式得到:

电荷Q = 电容C * 电压U

对于水桶:

水的体积V = 底面积S * 水面高度H

我们可以做以下类比:

  • 电容C大小类比于水桶的底面积S(两者都是造出来以后就固定了)。
  • 电容器两端的电压U类比于水面高度H(注意,是水面的高度,不是桶的高度,放水的多少不同,水面高度是有变化的)。
  • 电容器存储的电荷Q类比于水桶内水的体积V。
  • 电容器的极限耐压值Umax类比于水桶的高度Hmax。

进一步来理解和验证以上的类比否合理:

  • 同样电荷Q向不同的电容器充电,电容C越大电容器,电压U越小。类比到水桶,同样体积的水V倒入不同的水桶里,底面积S越大的桶子,水面高度H越小。
  • 某一电容器,当有更多的电荷Q注入,电压U就会越来越高,直到达到电容器的极限耐压值Umax。类比到水桶,某一水桶,只有更多体积的水V倒入,水面高度H就会不断上升,直到达到水桶的高度Hmax。
  • 电容器可以容纳的最大电荷Qmax是指两端电压U达到极限耐压值时的值: Qmax = C * Umax
    类比到水桶,一个水桶可以盛水的最大水的体积Vmax是指当水面高度H达到水桶高度时的值:Vmax= S * Hmax

  • 平时我们说电容器充满了,是指在电容器两端的电压等于了外部电源电压,没有电势差之后,外部电源自然无法再向电容器充电。这时两者电压相同,C大的,所能存储的如果能进一步提高外部电压,电容器两端的电压U还可以继续升高直至极限耐压值Umax。类比到水桶,我们假设有两个水桶,两者底部是互通的。当水桶A(相当于电压源,也可以理解为电容器向电容器充电)的水面高度H比水桶B(相当于电容器)高时,水会从水桶A注入到水桶B中,直到两者的水面高度相同时停止。

FTF2016技术峰会上的可穿戴技术 - NFC支付手表

发表于 2016-10-10

产品


飞亚达最新发布的印系列智能手表。不同于目前数字屏的显示方式,这款手表拥有传统的石英表外观和一个智能的机芯,让钟情传统手表的商务人士也能体验智能设备带来的便利。同样类似的产品我比较喜欢的是Withing的activite steel,感觉Apple Watch瞬间被秒。

除了配备基本的健康管理、消息提醒、防丢功能外,这款手表还提供30天的超长待机和NFC公交支付两大亮点。公交支付目前是非常火的移动应用,其使用率和用户粘性极高。

9月1日小米发布的小米5 MiPay在手机端已经支持了北上广的公交卡应用

飞亚达在手表端实现公交刷卡将绝对是更为友好的体验,目前功能还在测试中,据说10月底先会首先支持深圳通。在印系列手表上NFC公交支付与其他公交手环产品不同的是,据了解采用了与小米5采用了相同的全终端方案。可以通过空中发卡的方式来开通多个城市公交系统的支持,意味着用户去到不同的城市也可以非常方便的下载和使用当地城市的公交卡。

技术


这款手表基于NXP支付手环参考设计方案,一个完整的硬件 + 软件可穿戴二次开发平台。其中包含了MCU、NFC前端与嵌入式Secure Element、蓝牙网络控制器以及加速度计等主要4颗芯片:

  • LPC5410x 低功耗微控制器
  • PN66T NFC前端控制器与嵌入式Secure Element
  • QN902x 低功耗蓝牙SoC
  • MMA8652 低功耗加速度计

系统框图如下:

在参考设计方案中,除了线路设计图还包含有完整的SDK软件开发套件。NXP可穿戴支付SDK包含了一个由FreeRTOS实时内核调度的NFC中间件和抽象的SE API应用编程接口,NFC业务开发可以直接通过SE API提供的接口进行而无需关心底层是如何沟通的,同时这组接口还扩展了LoaderService与MiFare Open Platform的功能。

Loader Service 使服务提供商可以简化NFC安全应用的部署,具体可参考NXP官网的一篇白皮书Loader Service: The Tipping Point for Secure NFC Payments

除了NFC和SE管理中间件,在SDK中还提供了多个系统组件以满足通用可穿戴设备Time to Market的要求:

  • LPCXpresso驱动库
  • 低功耗管理API和FreeRTOS tickless模式
  • 计步器算法
  • ANCS蓝牙Profile
  • OTA 空中升级
  • 参考Android APP

低功耗是可穿戴设备的一个重要指标,采用这个方案的系统待机功耗小于100uA,在典型工作场景下150mA电池可以使用一个月。

芯片 功耗 备注
LPC54101 ~25uA Power down模式 + RTC + 看门狗定时器
QN9022 ~25uA 可被发现,1秒慢广播
PN66T ~20uA Standby模式, RF场唤醒
MMA8652 ~2uA Standby模式

小结


这两年可穿戴产品炒得太过火热,行业新增概念和亮点并不多,特别是手环类的产品,除了时间管理、消息通知、健康运动和位置追踪几个主打应用,能真正解决用户痛点的应用少之又少。今年行业领跑者估值大幅度缩水,Jawbone甚至跌回了5年前水平,几大品牌在小米和其他国产手环产品的低价策略冲击下处境艰难。设备制造商在这样的竞争环境下必须不断创新,找准出路。NFC可穿戴是一个不错的技术路线,同时市场也日渐成熟。

总结NFC移动支付和公交应用带来的改变:

  • 服务提供商吸引客户通过手机和可穿戴设备进行业务操作,提升应用体验、增加用户粘度。
  • 软硬件设备制造商依托于服务器提供商的平台,迅速提高产品销量和提升毛利率。
  • 终端用户则简化了安全支付,公交充值,卡管理等等日常低效繁杂操作

FTF2016技术峰会上的可穿戴技术 - HexiWear

发表于 2016-10-10

HexiWear可穿戴技术原型开发平台



HexiWear是由mikroelektronika与NXP一起开发的基于MCU的开源IoT与可穿戴原型开发。所有资料包括原理图、PCB设计文件、固件以及App的源码都可以从HexiWear网站或者Github获取。

NOTE: 其中固件的框架和GUI,计步器库等内容值得学习以用于进行二次开发

从系统框图来看,在这个小小的手表空间里包含了7颗NXP芯片,涵盖了MCU主控、BLE无线连接、9轴运动传感器、压力传感器和电源管理功能,除此之外还支持温湿度、环境光传感器和心率传感器。反正能找的传感器都已经支持了,如果能加上GPS就完美了。在印象中也就是Microsoft Band 2(主控似乎是Kinetis K26)有这么多Sensor了。

  • Kinetis K64F微控制器(主控)
  • Kinetis KW40Z单芯片BLE与802.15.4多协议无线处理器
  • FXOS8700CQ六轴加速度与地磁传感器
  • FXAS21002三轴角速度传感器(陀螺仪)
  • MPL3115A2绝对压力传感器
  • MC34671单节锂电池充电管理器
  • Kinetis K20OpenSDA调试器

在我看来这个平台最能吸引开发者的是HexiWear能通过Docking板来扩展mikroelektronika提供的上百种click物联网模块,可用最快的速度进行原型的开发与验证。(mikroelektronika我真是服,编译器,软件,硬件模块什么都能造)

FTF2016技术峰会上的可穿戴技术 - NFMI无线耳塞

发表于 2016-10-10

市场


Hearable属于Wearable可穿戴的一个分支。每年都有数以千万的耳塞、耳机被销售和使用。相比手腕位置,用户群体更广,在耳机产品平台上发展智能技术越来越受到重视。下面来看看由一根线引发革命:真无线耳塞

尽管Apple近年来一直在控制创新风险,做出的产品对于市场还是有极强的冲击力。这次随着iPhone7出来的AirPods让双耳无线耳塞产品火(吐槽)了一把。同类产品早在两年前就有不少的创业型公司已经有开始众筹,相信在AirPods的刺激下整个市场在今年下半年和明年会有一个爆发期。目前ifixit和Chipworks还看不到拆解分析,需要了解AirPods的小伙伴可以看看这篇YY分析:AirPods - a Speculative Teardown。

在FTF上NXP展出的双耳无线耳塞技术基于NFMI的方案,这个技术算是NXP的独门绝技了。NFMI技术早在10年前就已经被采用在助听器技术中,用于提供两耳之间超低功耗的语音传输。NXP将这个技术进行的升级优化,提高了带宽以用于消费级的无线音乐传输目的,之前KickStart上非常火的无线耳机the dash就使用了NXP第一代的NFMI芯片,本次展出的是第二代NFMI芯片NXH2280。其他基于NFMI技术的耳机还有

  • the headphone from Bragi
  • Elite Sport from Jabra

NFMI = Near Field Magnetic Inductive = 近场无线磁传导

技术


NFMI为什么在这个市场上占有一席之地需要从需求谈起。目前市面上的普通无线耳塞技术都是基于蓝牙Classic的A2DP协议,而A2DP协议本身设计是点对点传输音频数据,当用户希望两个耳朵完全没有线缆时这个技术在实现上需要做一些修改,最为常见的是CSR提供的中继方案:一个耳塞收到A2DP的立体声audio之后,本地分离出左右声道再次通过蓝牙传输到另外一个耳塞,同时做同步控制。早期的无线耳塞Earin就是使用的这个技术。它最大的问题是人体皮肤对于2.4G信号的吸收,导致其无线设计非常具有挑战,特别当耳机体积做的很小的时候几乎无法解决两耳之间音频传输的稳定性。而NFMI技术的出现解决了这个问题,对于人体免疫的特性让他非常适合作音频中继的用途。这次摆在展台的是NXP开发的基于NXH2280的ADK,系统组成近似于:

ADK = Application Development Kit = 应用开发套件

大致的工作流程:从手机蓝牙通过A2DP协议将经过SBC,AAC或者aptX压缩的双声道音频传输到其中一个耳塞中的蓝牙SOC,经过解码后将两个声道分离,本地的通道经过一定的延时(等同于无线)后播放出来,对端的声道发送给本地NXH2280进行压缩后和传输, 在另外一只耳塞的NXH2280收到音频数据包进行解压缩、重采样、延时控制后输出给CODEC进行播放。

需要注意的是虽然NXH2280主要的市场是无线音频,但也可以用于其他需要超低功耗和近距离通讯,特别是紧贴人体的传感器应用场景,比如标贴医疗传感器,体内药丸等,全靠脑洞大开的电子工程小伙伴们开发新的想法了。

小结


最后总结一下NFMI技术的几个技术特点和限制:

  1. 超低功耗(传输经过压缩的48kHz单声道音频小于2mA)
  2. 免疫皮肤组织
  3. 可以在水下工作(游泳也可以听歌哦)
  4. 与其他无线技术的共存和兼容性
  5. 天然的安全性
  6. 在小天线和低功场景下,通讯距离短(<50cm)
  7. 吞吐率较低(596kbps)
Neo Xiong

Neo Xiong

测试

10 日志
© 2018 Neo Xiong
由 Hexo 强力驱动
主题 - NexT.Pisces