Draft lite V1.0
EZ-LINK平台开发手册
1 平台简介
1.1 PCB设计
1.2 SDK架构
1.3 源码目录
2 开发环境
2.1 配置文件
2.2 库文件
2.3 IDE配置
2.4 编程框架
2.5 烧录Flash
2.6 调试
3 IOT接口
3.1 系统服务
3.2 硬件服务
3.3 WIFI
3.4 Bluetooth
3.5 GSM
3.6 GPS
缩略词:
BCPU
Baseband CPU
BTCPU
Bluetooth CPU
XCPU
System CPU
BSS
Basic Service Set
BSSID
BSS ID
COS
Common Operating System
1 平台简介 EZ-Link是RDA物联网应用集成开发平台,该平台包括SDK开发包,Eclipse集成开发环境,调试工具和烧录工具。SDK开发包提供了在EZ-Link平台进行物联网开发必需的模块和标准API接口,同时通过对硬件层抽象,提供对RDA物联网系列芯片的全面支持。 RDA物联网系列芯片即有独立蓝牙、WIFI芯片,也有在SoC上集成Wi-Fi、Bluetooth和GSM功能的组合芯片,并提供包括UART/ADC/I2S在内的硬件接口。 RDA特有的共存技术使用户可以独立或并行使用Wi-Fi及蓝牙功能。 本文阐述EZ-Link平台SDK的主要功能和使用方法。本文所描述SDK为精简版,该版本针对物联网设备开发周期短、市场反应敏感等特点,对EZ-Link SDK进行了一系列优化升级,以帮助客户提高开发效率。用户仅需要了解必要的接口,然后通过SDK提供的丰富示例代码,简单修改甚至零修改就可以实现一款完整的物联网应用。 1.1 PCB设计请参考各芯片数据手册。
1.2 SDK架构EZ-Link SDK具有模块化、可扩展的架构和清晰的层次结构,灵活的SDK架构为产品定制提供软件支撑,用户可以根据不同市场需求和变化,进行快速调整。
EZ-Link SDK的架构如下,每一部分将会在下面具体描述。
除了APP应用层需要客户完成,其余模块将由RDA统一打包发布给客户。 图1-1 EZ-Link SDK架构
最底层为硬件层,包括芯片和外设硬件。
第二层为硬件抽象层,该层隐藏底层硬件细节,为上层驱动提供统一接口,为实现不同芯片间平滑切换提供软件支持。
第三层为驱动和操作系统层。RDA在RTOS之上又提供了一层瘦COS层,该COS层封装了核心操作系统接口如任务调度,并发处理等,为服务层提供统一接口。COS层为用户提供两方面便利:用户可以随心所欲选择自己偏好的操作系统;用户可以移植已有的应用程序,复用代码,提高代码利用效率,加速开发进度。
第四层为服务层,服务层应用程序类似windows服务程序或linux deamon程序,这些程序长时间执行,为上层用户和应用提供特定的服务,如http,GPS服务等。上层用户可以通过标准接口使用服务层程序提供的服务,如多媒体应用程序可以通过GUI接口显示播放界面,同时可以通过media服务进行音频数据解码和播放。服务层应用程序通常为操作系统的任务(task),该任务直接调用COS接口以降低与RTOS的耦合。 服务层应用程序十分丰富,可以满足当前主流的物联网应用。良好的可扩展架构使用户可以添加更多自有服务程序或第三方应用程序,满足客户特殊的产品需求。 该层同时提供精简的IOT API,通过精简IOT API,用户可以快速开发一款简单的物联网应用,如智能水杯等。
最高层为应用程序层,用户通常工作在本层。应用程序层通过调用服务层接口进行软件开发。
EZ-Link各层之间以松散的方式耦合,各层提供的接口和实现分离,层内关注实现,层间关注接口。除此之外,EZ-Link SDK同样遵循模块化设计原则,方便用户对产品特性进行裁剪以适应客户需求,发挥用户平台、硬件资源的最大效率:
| RDA 芯片为本SDK设计参照的核心,芯片类型决定系统的CPU硬件架构、IO接口、总线等特性,进而影响服务层提供的服务、产品特性和源码编译等。芯片模块化使用户在无需修改上层软件的情况下,根据需求选择适当的芯片 | | 目前市面上嵌入式实时操作系统种类繁多,用户可以根据自己的偏好和使用习惯选择操作系统而不影响其他应用 | | 用户通常有更换外设的需求,驱动模块化正是满足该项需求 | | 服务层模块化,有效减少各个子模块之间耦合,方便用户删减与特定产品无关服务,添加新服务 |
本SDK支持多种RDA芯片。丰富的IO接口方便连接各种外设 硬件抽象层为上层驱动提供统一接口,方便驱动程序移植 驱动模块包括:GPIO,、UART、flash、LCD等 强大的实时操作系统: - 支持多任务处理
- 定时器管理
- 支持并发访问控制锁和任务间同步信号量
- 任务间通信机制
- 动态内存管理和glibc兼容的内存操作接口
提供对RTOS封装,可针对不同RTOS进行适配,使服务层和应用层程序摆脱对操作系统依赖 EZ-Link将系统功能抽象成模块化的服务,并提供标准的服务接口,极大简化编程过程。针对物联网特点,EZ-Link提供丰富的网络协议,方便不同设备商之间的智能设备互联互通: - 支持AllSeen协议
- 支持lwIP协议栈和http等应用层协议,方便扩展互联网应用
- 多媒体功能,音频、视频等
- GPS定位服务
- 支持DLNA协议
- GSM/GPRS服务
- 硬件层服务,主要针对GPIO、UART等串行总线接口
- OS服务,主要封装定时器、内存操作、调试等接口
简易IOT接口,简化编程,快速生成IOT应用,实现简单IOT产品 相对简单应用程序层,实现更复杂的物联网应用
1.3 源码目录SDK源码目录结构如下,各个目录将会在下面详细描述。
图1-2 EZ-Link SDK源码目录结构 图中各目录模块说明如下:
| | | | | RDA IOT平台代码,其下包括:chip、edrv、 rtos、 service等子目录,子目录会在下面依次说明 | | 平台芯片相关代码,包括:boot loader,hal层,寄存器地址定义和rom代码。通常用户无需修改rom代码 | | 包括外设驱动和芯片内驱动,外设驱动包括:LCD,FM等;芯片内部驱动为内部总线驱动,如GPIO,UART等 | | | | | | | 网络服务,提供http,lwna,tcp/ip操作接口 | | | | | | | | | | | | | | | | | | 影子目录,该目录在编译时由编译脚本生成,用来存放编译过程中生成的临时文件 | | | | Lod文件、Ramrun文件和其他GDB相关asm及map文件 |
表1-1 EZ-LINK SDK目录结构描述
2 开发环境EZ-LINK选用windows操作系统作主机(HOST),在windows系统中搭建交叉编译环境。 2.1 配置文件模块化的设计为用户定制产品提供了便利。通常定制产品通过修改配置文件来完成。配置文件在target目录,EZ-LINK配置文件主要涉及以下几个:
| | | | | 对target.def信息的集中处理,通常不需要用户修改 | | | | 用户功能宏头文件,控制用户APP特性 文件存在./target/include | | 硬件线路板配置 文件存在./target/include | | |
表2-1 配置文件
产品定义配置文件target.def包含下面一些选项,各选项精确的函数请参考配置文件说明注释: | | | | | | | | | | | | | | | | | | | | | 定义WIFI 32K时钟管脚,TCP/IP支持,SDIO,GPIO,SDMMC接口支持,DLNA接口支持,WIFI音箱支持 | | | | | | | | | | | | | | | | | | | CT_OPT:优化编译空间 WITHOUT_WERROR:告警是否停止编译 | | |
表2-2 产品配置文件选项 2.2 库文件EZ-Link开发平台库文件统一放置在./platform/lib/chip_name目录。用户开发程序时引用的头文件放置在各模块./inlcude目录。
库文件分为release和debug两个版本,通过文件名区分。debug版本包含调试信息,并可以使用GDB工具进行单步调试,调试信息可以通过串口打印到控制台;release版本不包含调试信息。正式发布的产品bin文件应当链接release版本库以减少bin文件体积,提高运行速度。
2.3 IDE配置EZ-LINK SDK选择Eclipse作为集成开发环境,SDK编译工具链统一集成在Eclipse中。
| Eclipse和JRE(Jave Run Environment)选用32位版本,可以兼容64和32位操作系统 |
- 下载EZ-LINK集成开发工具包,其中包含JRE安装程序和Eclipe软件
- 安装JRE,安装过程始终选择默认安装选项
- Eclipse无需安装,直接解压运行
Eclipse成功运行说明集成开发环境工作正常,下一步通过Eclipse配置编译工具,导入SDK库文件和源码。
打开Eclipse, 选择菜单“File/New”, 然后选择“RDA Project”。项目配置窗口如下,自定义工程名,选择项目代码路径: 图2-1 创建项目
项目基础代码由RDA提供,用户可以在基础代码之上实现自己特有的功能和应用。 文档中示例工程名为“iot”,选择代码路径后单击“Finish”按钮,生成工程如下: 图2-2 项目浏览器
在“Project Explorer”中点击右键,选择“Properties”,进入“Properties”窗口,选择“C/C++ Build”,然后选择“RDA Project”,配置目标: 图2-3 项目配置
在“Project Explorer”中点击右键,选择“RDA Tools”,然后可以看到两个编译选项:
图2-4 编译项目
在“Project Explorer”中展开根目录 iot,选择模块,在模块上点击右键,选择“RDA Tools”,然后可以看到如下两个选项:
图2-5 编译模块
2.4 编程框架对EZ-LINK开发平台而言,短短数十行代码就可以完成一个简单的物联网应用,如智能LED灯等。简单物联网应用编程框架有两部分组成:
- init()函数,负责资源初始化
- loop()函数,持续监听系统事件,并处理。loop函数不间断运行直至系统关机
图2-6 用户代码结构
include目录存放用户头文件,如果用户无自定义头文件,该目录可以为空,或者直接删除。
src目录存放用户源文件。源文件中实现init()和loop(),用户在init()中实现初始化代码,在loop()中处理外设如WIFI模块产生的事件。源文件结构参考示例代码。
makefile文件具有固定格式,简单易用。模块makefile甚至无需修改就可以正确编译用户代码,将用户代码与系统库文件一起编译链接生成可烧录的flash二进制文件。
## ----------------------------------------------------------- ## ## Don't touch the next line unless you know what you're doing.## ## ----------------------------------------------------------- ## include ${SOFT_WORKDIR}/env/compilation/compilevars.mk
# Name of the module LOCAL_NAME := apps/ap_customer
# list all modules APIs that are neccessary to compile this module LOCAL_API_DEPENDS := \ platform \ platform/service \ ## ----------------------------------------------------------- ## ## List all your sources here ## ## ----------------------------------------------------------- ## include src/make.srcs
## ----------------------------------------------------------- ## ## Do Not touch below this line ## ## ----------------------------------------------------------- ## include ${SOFT_WORKDIR}/env/compilation/compilerules.mk |
2.5 烧录Flash
烧录BIN文件使用USB转串口线,RDA提供的开发包中包含USB转串口驱动程序,点击安装。驱动安装成功后,依次查看windows“控制面板”->“设备管理”->“端口”,在“端口”选项下,有如下所示USB-to-Serial端口,说明安装成功:
图2-7 安装串口驱动
在Eclipse菜单项选择“windows”,然后选择“Preference”,选择“C/C++”,然后选择“RDA Tools”。在“Serial Setting”中选择串口和正确的Baudrate。
图2-8 串口配置
烧录相关按钮在Eclipse工具栏,如下图示。 自左向右分别是:
连接UART COM口:蓝线连接HST_RXD,白线连接HST_TXD,黑线接地。 启动Eclipse,点击工具栏“Connect Target”,连接成功后,点击“Down Flash”,选择Lod文件,点击“Download”: 图2-9 下载选项
| Flash Programmer File文件由build flash产生 System Lod File文件由build image产生,参考2.3 IDE配置
|
2.6 调试串口打印trace是最为常见的调试手段。EZ-LINK trace按模块分类,模块内按重要性分为不同等级。
通过宏开关,可以选择编译时是否包含特定模块trace。
通过Eclipse工具,可以选择运行时是否输出特定模块和该模块特定级别的trace。设置步骤如下:
在Eclipse菜单栏选择“windows”,然后选择“Preference”,选择“C/C++”,然后选择“RDA Tools”,最后单击“Trace Mask”按钮,弹出如下对话框。
图2-10 配置Trace
“Select Module”选择trace模块,16个level控制trace输出级别。
3 IOT接口3.1 系统服务3.1.1 内存头文件:iot_base.h 3.1.1.1 iot_Malloc | | | PVOID iot_Malloc(UINT32 size) | |
| | | | | |
|
| 3.1.1.2 iot_Free3.1.2 Trace头文件iot_base.h 3.1.2.1 iot_Printf | |
| iotPrintf("led status = %d ", ledOn); | | VOID iot_Printf(INT8* fmt,...) | |
| | | | | |
|
| 3.1.3 延时3.1.3.1 iot_DelayMs | | | VOID iot_delayMs(UIN32 ms) | |
| | | | | |
|
| 3.1.3.2 iot_DelaySeconds | | | VOID iot_DelaySeconds( UINT32 seconds) | |
| | | | | |
|
| 3.2 硬件服务3.2.1 UART3.2.1.1 uart_Open | | | VOID uart_Open(UINT8 id, UINT32 baudrate) | |
| | | | | | | | |
|
| 3.2.1.2 uart_Close | | | VOID uart_Close(UINT8 id); | |
| | |
| | |
|
| 3.2.1.3 uart_Write | | | UINT32 uart_Write(UINT8 id, CHAR ch); | |
| | | | | | | | | | |
| | | | 3.2.1.4 uart_Read | | | UINT32 uart_Read(UINT8 id, CHAR* buff) | |
| | | | | | | | | | |
| | | | 3.2.2 GPIO3.2.2.1 gpio_Open | | | VOID gpio_Open(UINT8 port, UINT8 direction) | |
| | | | | | | | |
|
| 3.2.2.2 gpio_Close | | | VOID gpio_Close(UINT8 port) | |
| | | | | |
|
| 3.2.2.3 gpio_Write | | | VOID gpio_Write(UINT8 port, UINT8 data) | |
| | | | | | | | |
|
| 3.2.2.4 gpio_Read | | | UINT32 gpio_Read(UINT8 port) | |
| | | | | |
|
| 3.2.2.5 示例程序简单物联网应用:智能LED灯,通过WIFI无线控制LED灯开关。现实生活中,可以减少装修布线,减轻对墙体破坏等。
// app_led.c #include “hal_gpio.h” #include “tcpip_sockets.h”
static struct sockaddr_in ledSckAddr; static INT8 ledSckServer; static INT8 ledSckClient = 0; ///Only accept 1 byte data(0 or 1), ok for turn on/off the LED #define MAX_RECV_BUFF 1 #define LED_SCK_PORT 5050 #define MAX_LED_CONNECTION 1
VOID init(VOID) { struct sockaddr_in clientAddr; HAL_GPIO_GPIO_ID_T ledGpioId; HAL_GPIO_DIRECTION_T direction;
///Use the tenth GPIO port ledGpioId = HAL_GPIO_10; direction = HAL_GPIO_DIRECTION_OUTPUT; ///Must open the GPIO before use it gpio_Open(ledGpioId, direction);
ledSckServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); memset(&ledSckAddr,0, sizeof(struct sockaddr_in));
ledSckAddr.sin_family=AF_INET; ledSckAddr.sin_addr.s_addr=htonl(INADDR_ANY); ledSckAddr.sin_port=htons(LED_SCK_PORT);
if(-1 == bind(ledSckServer, (struct sockaddr *)(& ledSckAddr),sizeof(sockaddr_in))) { return ; } }
VOID loop(VOID) { INT8 readLen = 0; UINT8 ledOn; /// If no connection, waiting for connect if (ledSckClient <= 0) { if(-1 == listen(ledSckServer, MAX_LED_CONNECTION)) { return ; } addrSize = sizeof(struct sockaddr_in); ledSckClient = accept(ledSckServer, (struct sockaddr *)(&clientAddr), &addrSize); if(-1 == ledSckClient) { return ; } } readLen = read(ledSckClient, &ledOn, MAX_REV_BUF_SIZE); if (readLen > 0) { if (0 == ledOn || 1 == ledOn) gpio_Write(ledGpioId, ledOn); } } | 3.3 WIFI3.3.1 编程接口头文件:iot_wifi.h 3.3.1.1 iot_Connect | | | INT32 iot_Connect(const char *addr, INT16 portno) | |
| | | | | | | | |
| |
| |
| | 3.3.1.2 iot_Send | | | INT32 iot_Send(INT32 sockfd, const INT8* buffer, UINT32 len) | |
| | | | | | | | | | | |
|
| 3.3.1.3 iot_Recv | | | INT32 iot_Recv(INT32 sockfd, INT8 *buffer, UINT32 len) | |
| | | | | | | | | | | |
|
| 3.3.1.4 iot_Close | | | INT32 iot_Close(INT32 sockfd) | |
| | | | |
|
|
| 3.3.2 示例程序#include "iot_wifi.h" // Source device id allocated by oneNet #define SRC_DEV "777" // Destination device id allocated by oneNet #define DST_DEV "778" // API key allocated by oneNet #define API_KEY "your api key" // Data stream name, created on oneNet #define DATA_STREAM "your data stream" #define MAX_RESP_BUF 32
// ============================================================================ // Loop forever // Send temperature value to cloud application whenever changed // @return void // ============================================================================ VOID loop() { // Server information for connection INT8 *server_ip = "183.230.40.39"; INT8 *port = "876"; INT8 *src_dev = SRC_DEV; INT8 *dst_dev = DST_DEV; INT8 *src_api_key = API_KEY; INT8 *data_stream = DATA_STREAM; DOUBLE temperature = 0.0; INT sock, ret; EdpPacket *pkg = NULL; INT8 buffer[MAX_RESP_BUF] = { 0 }; SaveDataType data_type = kTypeSimpleJsonWithoutTime;
recv_buf = NewBuffer(); // Connect to server sock = iot_Connect(server_ip, atoi(port)); if (sock < 0) { iot_Printf("Error connect cloud server. \n"); return; } // Build connect package follow oneNet EDP protocol pkg = PacketConnect1(src_dev, src_api_key); if (pkg == NULL) { iot_Printf("Build connect pkg failed.\n"); return; } // Send connection request pkg to cloud server ret = iot_Send(sock, (const char*)pkg->_data, pkg->_write_pos); DeleteBuffer(&pkg); while (1) { Temperature = user_poll_current_temperature(); // Build save data pkg follow oneNet EDP protocol pkg =PacketSavedataDouble(data_type,dst_dev,data_stream,temperature,0, NULL); if (pkg == NULL) { iot_Printf("Build data failed.\n"); return; } // Send temperature value pkg to clould server iot_Send(sock, (const char*)pkg->_data, pkg->_write_pos); DeleteBuffer(&pkg); iot_DelayMs(2000); } // Close socket iot_Close(sock); return; } | 3.4 Bluetooth3.4.1 编程接口头文件:iot_bt.h 3.4.1.1 bt_Open3.4.1.2 bt_Close3.4.1.3 bt_SetLocalName | | | VOID bt_SetLocalName(UINT8* name) | |
| | | | | | |
| 3.4.1.4 bt_SetVisible | | | VOID bt_SetVisable(UINT8 visable); | |
| | | | | | |
| 3.4.1.5 bt_ScanDevice | | | UINT32 bt_ScanDevice(UINT32 timeout) | |
| | | | | |
|
| 3.4.1.6 bt_BoneDevice | | | UINT32 bt_BondDevice( t_bdaddr device_addr, UINT32 timeout ) | |
| | | | | |
| 3.4.1.7 bt_GetBonedDevice | | | BOOL bt_GetBonedDevice(UINT32 index, rdabt_device_t_app *device); | |
| | | | | | | | | | |
| | | | 3.4.1.8 bt_SppConnect | | | BOOL bt_SppConnect(t_bdaddr device_addr, UINT8 timeout); | |
| | | | | | | | | | |
| | | | 3.4.1.9 bt_SppDisconnect | | | VOID bt_SppDisconnect(VOID) | |
| | |
| | |
|
| 3.4.1.10 bt_SppSend | | | INT32 bt_SppSend(UINT8* buf, UINT32 size) | |
| | | | | | | | | | |
| | | | 3.4.1.11 bt_SppRecv | | | INT32 bt_SppRecv(UINT32 timeout, UINT8 *buf) | |
| | | | | | | | | | |
| | | | 3.4.1.12 bt_SppAccept | | | BOOL bt_SppAccept(UINT32 timeout) | |
| | | | | | | |
| | | | 3.4.2 示例程序简单物联网应用:智能插座,通过蓝牙无线控制插座的开关。
// app_smart_plug.c
#include “bt_types.h” #include “hal_gpio.h”
static HAL_GPIO_GPIO_ID_T plugGpioId;
VOID init(VOID) { HAL_GPIO_DIRECTION_T direction;
// Use the fifth GPIO port plugGpioId = HAL_GPIO_15; direction = HAL_GPIO_DIRECTION_OUTPUT; // Must open the GPIO before use it gpio_Open(plugGpioId, direction ); bt_Open(VOID); }
VOID loop(VOID) { t_DataBuf btData;
memcpy(&btData, 0, sizeof(t_DataBuf)); // Return only when data available or error bt_SppRecv(&btData); if (btData.len > 0) { if (0 == (*btData.buff) || 1 == (*btData.buff) ) { gpio_Write(plugGpioId, *btData.buff); } } } |
全部资料51hei下载地址(含源码与手册):
http://www.51hei.com/bbs/dpj-135991-1.html
|