DA14580是Dialog公司研制的蓝牙单芯片,号称全球功耗最低,是TI CC2541的四分之一,是运动手环等穿戴类电子产品的常用芯片。但是DA14580的开发门槛不低,适合有蓝牙开发经验的团队来开发,不适合学习爱好者,在网络上搜索DA14580相关的开发文章,基本上都是对官方仅有的几篇文档进行简单翻译,还不如直接阅读英文原文。笔者将对DA14580的系统架构和应用开发框架进行分析,之后再讲解如何进行应用开发。

       对于蓝牙单芯片应用开发来说,我们要关注的问题是:蓝牙协议栈方面如何新增一个GATT profile(服务和特征值定义及操作)、SOC内核方面如何驱动外围设备、系统应用框架上如何使用定时器和任务间消息通信等等。DA14580单芯片发布时并不是一颗裸片,而是带有开发平台和SDK包,还有常用的应用例程(如防丢proximity),我们要做的就是通过SDK和相关的文档去理解它整个系统架构和应用框架,在这个基础上才能去完成以上三个方面的开发。

一、DA14580系统架构

DA14580是基于Cortex M0架构,内置ROM、OTP和RAM。其中ROM固化了大部分协议栈和操作系统(单任务)的代码实现,而OTP一次性编程则是为了降低成本,实现用户的差异化应用需求。当用户通过SPI NORFLASH引导或者直接通过JLINK下载代码到RAM进行调试后,就可以通过SmartSnippets工具下载代码到OTP。量产产品即从OTP开始引导执行。

DA14580集成的是第三方公司RW的蓝牙协议栈IP,范围包括GAT和GAP层及以下。因此我们可以在代码框架目录上看到RW开头命名的目录和头文件,官方文档涉及到蓝牙协议栈方面大部分都是RW公司出品。


二、DA14580 开发例程目录和SDK目录结构

DA14580的SDK开发平台使用keil,我们先来看看开发例程的目录结构,再来看SDK目录结构。前者简单一些,后者因为涉及到第三方IP、ROM等原因,目录实在是太多太细了,初接手真的会歇菜。

防丢(proximity,英文是接近的意思)的开发目录结构如下:


这里需要注意的是,ROM里面的固话代码,包括协议栈和单任务操作系统的相关管理代码也是整个工程应用的一部分,只不过没有列到开发目录里面。

SDK目录架构如下:


三、蓝牙profile和应用的角色和分工

从工程的代码目录结构来看,每个profile都有一个以profile(如proxr)命名的.c文件,也有一个以profile_task(如proxr_task)命名的.c文件;相应地,每个应用子任务也有一个app_profile(如app_proxr)的.C文件,和app_profile_task(如app_proxr_task)的.c文件。一般地:

在操作系统ke内核看来,Profile和profile_task共同完成一个task任务,其中app_proxr_task的task ID标识是TASK_PROXR。但app_profile和app_profile_task并不是一个具体的task任务,在代码目录的app目录,所有的task,包括app_proxr_task和app_batt_task(电池)、app_sec_task(安全)共同组成一个task,在app.c中完成任务创建,task的ID标识是TASK_APP。各个app_profile_task只不过完成应用的一个子场景功能,如防丢、电池告警等。

app是主动发送消息给profile,以执行相应的蓝牙GATT服务和操作,并接受回调。即app是profile的上层。

Profile任务执行GATT服务/属性的具体创建create、开启服务enable和属性特征的读写等操作,其调用ATT和GAP等底层接口来实现具体功能。Profile作为接口供给app层调用,app是通过消息通信来完成接口调用的。

app_profile的代码一般包括主动调用的接口实现,而app_profile_task则是接受消息回调的接口实现。两者的分工是非常清晰的。


四、应用开发框架

DA14580的应用开发框架的核心是基于状态机和消息回调。以下分析以防丢proxr为例。

1.    状态机

每个任务都必须明确自己的状态表,例如proxr的状态表是:


状态的初始化和转换是由用户主动切换的。在某个确定的状态时,内核会在对应的状态响应接口集中遍历所有发给该任务的消息。

每个任务都会在初始化时被创建,例如proxr任务的创建是:


这时,假设有个其他的任务发一个消息给TASK_PROXR,则会在proxr_disabled中查找相应的消息回调接口,并执行回调。

2.    消息回调

接下来看看各个状态的响应接口集,例如PROXR_CONNECTED连接状态时的状态响应接口集如下。可见,其会对两个消息进行回调,一个是底层ATT收到对特征值的写操作时执行回调,另一个应用层主动改写另一个特征值。在笔者的防丢和计步应用中,前者是实现防丢告警功能,后者是上报计步数据。


3.    任务间通信

消息发出之后,系统即会执行proxr_jibu_update_req_handler回调。

 

另外,笔者会根据文章的阅读量考虑进一步对DA14580的SDK进行分析,如系统启动过程、服务建立过程以及上面说的,如何进行应用开发,即蓝牙协议栈方面如何新增一个GATT profile(服务和特征值定义及操作)、SOC内核方面如何驱动外围设备、系统应用框架上如何使用定时器和任务间消息通信等等。


更多原创请关注微信公众号:嵌入式企鹅圈

                                                                              


       大概用了二十天结束了JS第一大模块ECMAScript核心的学习,尽管ECMAScript是一个重要的标准,但它并不是 

JavaScript唯一的部分,当然,也不是唯一被标准化的部分。实际上,一个完整的JavaScript实现是由以下 3个不同部

分组成的:


       核心(ECMAScript):JavaScript的核心ECMAScript描述了该语言的语法和基本对象。

       浏览器对象模型(BOM):BOM描述了与浏览器进行交互的方法和接口。

       文档对象模型(DOM):DOM描述了处理网页内容的方法和接口。

       那么继续接着来看第二大模块BOM(浏览器对象模型)的学习,在学习BOM之前,特意在网上看了一下BOM的介

绍,这个模块大概是没有一个统一的标准,公有的知识很少,各大浏览器在扩展着BOM实现功能。因此也只是学习一

些公有的BOM对象知识。

      一BOM概述

      IE 3.0和Netscape Navigator 3.0提供了一种特性BOM(浏览器对象模型),可以对浏览器窗口进行访问和操作。使

用BOM,开发者可以移动窗口、改变状态栏中的文本以及执行其他与页面内容不直接相关的动作。使BOM独树一帜

且又常常令人怀疑的地方在于,它只是JavaScript的一个部分,但是没有任何相关的标准。

       二BOM扩展

       BOM主要处理浏览器窗口和框架,不过通常浏览器特定的JavaScript扩展都被看做BOM的一部分。这些扩展包

括:

       1弹出新的浏览器窗口

       2移动、关闭浏览器窗口以及调整窗口大小

       3提供Web浏览器详细信息的定位对象

       4提供用户屏幕分辨率详细信息的屏幕对象

       5对cookie的支持

       6IE扩展了BOM,加入了ActiveXObject对象,可以通过JavaScript实例化ActiveX对象

       三BOM知识概括

       由于没有相关的BOM标准,每种浏览器都有自己的BOM实现。有一些事实上的标准,如具有一个窗口对象和一

个导航对象,不过每种浏览器可以为这些对象或其他对象定义自己的属性和方法。浏览器对象模型 (BOM)使

JavaScript有能力与浏览器“对话”。BOM尚无正式标准。由于现代浏览器几乎已经实现了 JavaScript交互性方面的相

同方法和属性,因此常被认为是BOM的方法和属性。

       BOM提供了一些访问窗口对象的一些方法,我们可以用它来移动窗口位置,改变窗口大小,打开新窗口和关闭窗

口,弹出对话框,进行导航以及获取客户的一些信息如:浏览器品牌版本,屏幕分辨率。

       四那么我们要学习BOM的哪些知识呢?

       1Window对象:客户端JS的顶层对象,每当或者标签出现,Window对象就会被自动创建。

       2Location对象:包含当前URL的信息。

       3Document对象:包含整个HTML文档,可以用来访问页面中的所有元素。

       4Screen对象:包含客户端显示屏的信息。

       5History对象:包含浏览器窗口访问过的URL。

       6Naviqator对象:包含客户端浏览器的信息。

       7Popupalert对象:可以在JavaScript中创建三种消息框:警告框、确认框、提示框。

       8Timing对象:在一个设定的时间间隔之后来执行代码,而不是在函数被调用后立即执行。

       9Cookies对象:用来识别用户。

       以上就是自己对JS中BOM的理解了,有什么不妥的地方,在后面逐渐深入学习后也会将其纠正。

一. 题目描述

Given a matrix ofmn elements (mrows, n columns), return all elements of the matrix in spiral order.
For example, Given the following matrix:

[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]

You should return [1,2,3,6,9,8,7,4,5].

二. 题目分析

题意:给定一个m*n的矩阵,从外围一层一层的打印出矩阵中的元素内容。解题的方法有多种,以下采用的方法是,使用四个数字分别记录上下左右四个边界的位置(beginX,endX,beginY,endY),不断循环以收窄这些边界,最终当两个边界重叠时,结束循环。

同时,当循环完成后,即得到所求数组。

三. 示例代码

class Solution 
{
public:
    vector<int> spiralOrder(vector<vector<int> >& matrix) {
        vector<int> result;
        if (matrix.empty()) return result;
        int beginX = 0, endX = matrix[0].size() - 1;
        int beginY = 0, endY = matrix.size() - 1;
        while (1) {
            // 从左到右
            for (int i = beginX; i <= endX; ++i)
                result.push_back(matrix[beginY][i]);
            if (++beginY > endY) break;
            // 从上到下
            for (int i = beginY; i <= endY; ++i)
                result.push_back(matrix[i][endX]);
            if (beginX > --endX) break;
            // 从右到左
            for (int i = endX; i >= beginX; --i)
                result.push_back(matrix[endY][i]);
            if (beginY > --endY) break;
            // 从下到上
            for (int i = endY; i >= beginY; --i)
                result.push_back(matrix[i][beginX]);
            if (++beginX > endX) break;
        }
        return result;
    }
};

ServletContext是整个Web应用程序运行后的代表对象,可以通过ServletConfig的getServletContext()方法来取得,之后就可以利用ServletContext来取得Web应用程序的相关资源或信息。

ServletContext简介

可以用ServletContext来与Web应用程序进行沟通,甚至是取得同一服务器上其他Web应用程序的ServletContext。

getRequestDispatcher()

该方法可以取得RequestDispatcher实例,使用时路径的指定必须以“/”作为开头,这个斜杠代表应用程序环境根目录(Context Root)。取得RequestDispatcher实例之后,就可以进行请求的转发(Forward)或包含(Include)。

this.getRequestDispatcher("/pages/some.jsp").forward(request, response);

以”/”作为开头时成为环境相对(context-relative)路径,没有以”/”作为开头则成为请求相对(request-relative)路径。

getResourcePaths()

如果想要知道Web应用程序的某个目录中都有哪些文件,则可以使用getResourcePaths()方法。使用时,指定路径必须以”/”作为开头,表示相对于应用程序环境根目录。例如:

public class ShowPathServlet extends HttpServlet{
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<head>");
        out.println("<title>Resource Paths</title");
        out.println("</head>");
        out.println("<body>");
        out.println("<ul>");
        Iterator<String> paths = getServletContext().getResourcePaths("/").iterator();
        while (paths.hasNext()) {
            String path = paths.next();
            out.println("<li>" + path + "</li>");
        }
        out.println("</ul>");
        out.println("</body>");
        out.println("</html>");
        out.close();
    }
}

getResourceAsStream()

如果想在Web应用程序中读取某个文件的内容,则可以使用getResourceAsStream()方法,执行路径时必须以“/”作为开头,表示相对于应用程序环境根目录,运行结果会返回InputStream实例,接着就可以运用它来读取文件内容。

使用java.io下的File、FileReader、FileInputStream等与文件读取相关的类时,可以指定绝对路径或相对路径。绝对路径是指文件在服务器上的真实路径。必须注意的是,指定相对路径时,此时路径不是相对于Web应用程序根目录,而是相对于启动Web容器时的命令执行目录。以Tomcat为例,若在Servlet中执行以下语句:

out.println(new File("filename").getAbsolutePath());

则显示的是filename位于Tomcat目录下的bin目录中。

ServletContext初始参数

每个Servlet都会有一个相对应的ServletConfig对象,我们可以在web.xml中定义Servlet时设置初始参数,之后通过ServletConfig的getInitParameter()方法来读取初始参数。通常最适合读取初始参数的位置在Servlet的无参数init()方法之中,因为Web容器初始Servlet后,会调用有参数的init()方法,而该方法又会调用无参数的init()方法。

每个Web应用程序都会有一个相对应的ServletContext,针对应用程序初始化时所需用到的一些参数数据,可以在web.xml中设置应用程序初始化参数,设置时使用<context-param>标签来定义。例如:

<web-app ...>
  <context-param>
      <param-name>MESSAGE</param-name>
      <param-value>/WEB-INF</param-value>
  </context-param>
</web-app>

ServletContextListener

如果想要知道Web应用程序何时初始化或何时结束销毁,可以实现ServletContextListener,并在web.xml中设置告知web容器,在web应用程序初始化后果结束销毁前,调用ServletContextListener实现类中相对应的contextInitialized()或contextDestroyed()。

ServletContextListener接口定义如下:

/**
 * Implementations of this interface receive notifications about changes to the
 * servlet context of the web application they are part of. To receive
 * notification events, the implementation class must be configured in the
 * deployment descriptor for the web application.
 *
 * @see ServletContextEvent
 * @since v 2.3
 */

public interface ServletContextListener extends EventListener {

    /**
     ** Notification that the web application initialization process is starting.
     * All ServletContextListeners are notified of context initialization before
     * any filter or servlet in the web application is initialized.
     * @param sce Information about the ServletContext that was initialized
     */
    public void contextInitialized(ServletContextEvent sce);

    /**
     ** Notification that the servlet context is about to be shut down. All
     * servlets and filters have been destroy()ed before any
     * ServletContextListeners are notified of context destruction.
     * @param sce Information about the ServletContext that was destroyed
     */
    public void contextDestroyed(ServletContextEvent sce);
}

当web容器调用contextInitialized()或contextDestroyed()时,会传入ServletContextEvent,其封装了ServletContext,可以通过ServletContextEvent的getServletContext()方法取得ServletConfig,之后就可以进行ServletContext初始参数的读取了。

对于多个Servlet都会使用到的信息,可以把它设置为ServletContext初始参数,可以在web.xml中做如下定义:

...
  <context-param>
      <param-name>MESSAGE</param-name>
      <param-value>/WEB-INF</param-value>
  </context-param>
  <listener>
      <listener-class>club.cuxing.AvatarInitalizer</listener-class>
  </listener>
...

<listener><listener-class>标签用来定义实现了ServletContextListener接口的类名称。

ServletContext属性

在整个web应用程序生命周期内,Servlet所需共享的数据可以设置为ServletContext属性。由于ServletContext在web应用程序期间都会一直存在,所以对于设置为ServletContext属性的数据,除非你主动移除,否则也是一直存活于web应用程序之中的。ServletContext的相关方法:

  • setAttribute() 设置对象为ServletContext属性
  • getAttribute() 取出某个属性
  • removeAttribute() 移除某个属性