first commit for opensource
first commit for opensource
53
doc/1.TencentOS tiny产品简介.md
Normal file
@@ -0,0 +1,53 @@
|
||||
## TencentOS tiny 产品简介
|
||||
TencentOS tiny是腾讯面向物联网领域开发的实时终端操作系统,具有低功耗,低资源占用,模块化,可裁剪等特性。
|
||||
TencentOS tiny提供最精简的RTOS内核,内核组件可裁剪可配置,可灵活移植到多种终端MCU上。而且,基于RTOS内核,提供了COAP/MQTT/TLS/DTLS等常用物联网协议栈及组件,方便用户快速接入腾讯云IoT Hub。
|
||||
TencentOS tiny为物联网终端厂家提供一站式软件解决方案,方便各种物联网设备快速接入腾讯云,可支撑智慧城市、智能水表、智能家居、智能穿戴、车联网等多种行业应用。
|
||||
|
||||
TencentOS tiny的优势:
|
||||
|
||||
__1.小体积__
|
||||
最小内核:RAM 0.6KB,ROM 1.8KB
|
||||
典型LoraWAN及传感器应用:RAM 3.3KB,ROM 12KB
|
||||
__2.低功耗__
|
||||
休眠最低功耗低至2 uA
|
||||
支持外设功耗管理框架,方便开发人员降低设备功耗,延长设备寿命
|
||||
__3.丰富的IoT组件__
|
||||
集成主流IoT协议栈(MQTT、CoAP、LoRaWAN、NB-IoT等)
|
||||
多种通信模组SAL层适配框架,降低网络硬件适配时间成本
|
||||
支持OTA升级
|
||||
提供简单易用端云API,加速用户业务接入腾讯云
|
||||
__4.可靠的安全框架__
|
||||
多样化的安全分级方案
|
||||
均衡安全需求&成本控制
|
||||
__5.良好的可移植性__
|
||||
内核及IoT组件高度解耦,提供标准适配层
|
||||
提供自动化移植工具,提升开发效率
|
||||
__6.便捷的调试手段__
|
||||
提供云化的最后一屏调试功能
|
||||
故障现场信息自动上传云平台,方便开发人员调试分析
|
||||
|
||||
TencentOS tiny可以有效减少开发人员在任务管理、硬件支持、网络协议支持、安全方案以及文件系统、 KV 存储、在线升级等常用功能组件相关的开发中所需要的时间和工作量,并且能够快速高效的实现设备的低功耗运行,同时提供强大的开发调试功能, 减少开发测试人力成本,能够让客户的产品实现快速开发和快速上线交付。同时,极低的资源占用可有效降低客户产品对硬件规格的需求,可有效降低设备成本。
|
||||
|
||||
## TencentOS tiny整体架构
|
||||
|
||||
|
||||

|
||||
|
||||
TencentOS tiny主要由一个轻量级RTOS内核+多个物联网组件构成,如下图所示,为TencentOS tiny的主体架构图,从下到上主要包括:
|
||||
|
||||
__CPU库__ :TencentOS tiny支持的CPU IP核架构,当前主要支持ARM Cortex M0/3/4/7,后续可支持更多IP核,支持更多种类的开发板;
|
||||
|
||||
__驱动管理层__ :包括板级支持包(BSP,主要由MCU芯片厂家开发与维护)、硬件抽象(HAL,主要由TencentOS tiny提供,方便不同芯片的适配与移植)、设备驱动(Drivers,比如wifi、GPRS、LORA等模块的驱动程序);
|
||||
|
||||
__内核__ :TencentOS tiny实时内核包括任务管理、实时调度、时间管理、中断管理、内存管理、异常处理、软件定时器、链表、消息队列、信号量、互斥锁、事件标志等模块;
|
||||
|
||||
__IoT协议栈__ :TencentOS tiny提供lwip、AT Adapter、SAL层,支持不同的网络硬件,如以太网、串口WIFI、GPRS、NB-IoT、4G等通信模块。TCP/IP网络协议栈上提供常用的物联网协议栈,如COAP、MQTT,支撑终端业务快速接入腾讯云;
|
||||
|
||||
__安全框架__ :TencentOS tiny为了确保物联网终端数据传输安全以及设备认证安全,提供了比较完整的安全解决方案。安全框架提供的DTLS和TLS安全协议加固了COAP及MQTT的传输层,可确保物联网终端在对接腾讯云时实现安全认证和数据加密;另外针对低资源的终端硬件,安全框架还提供与腾讯云IoTHub配套的密钥认证方案,确保资源受限设备也能在一定程度上实现设备安全认证;
|
||||
|
||||
__组件框架__ :TencentOS tiny提供文件系统、KV存储、自组网、JS引擎、低功耗框架、设备框架、OTA、调试工具链等一系列组件,供用户根据业务场景选用;
|
||||
|
||||
__开放API__ (规划开发中):TencentOS tiny将在协议中间件和框架层上提供开放API函数,方便用户调用中间件功能,使用户不用过多关心中间件具体实现,快速对接腾讯云,实现终端业务上云的需求,期望最大程度减少终端物联网产品开发周期,节省开发成本;
|
||||
|
||||
__示例应用__ :TencentOS tiny提供的示例代码,模块测试代码等,方便用户参考使用。
|
||||
|
12
doc/2.TencentOS tiny购买指南_免费版.md
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
|
||||
TencentOS tiny 服务免费。
|
||||
|
||||
当您遇到问题时,请联系我们寻求相应的帮助。
|
||||
|
||||
联系方式
|
||||
|
||||
邮箱:[supowang@tencent.com](mailto:supowang@tencent.com)
|
||||
|
||||
电话:075586013388转75674
|
||||
|
23
doc/3.TencentOS tiny快速入门.md
Normal file
@@ -0,0 +1,23 @@
|
||||
#### 第 1 步: 登录注册
|
||||
|
||||
登录腾讯云官网。如果没有账号,请参考账号注册教程(https://www.qcloud.com/document/product/378/8415)。
|
||||
|
||||
### 第 2 步:线上内测申请
|
||||
|
||||
进入TencentOS tiny介绍页面,点击 立即申请,填写TencentOS tiny试用申请单,填写完成后点击提交,完成线上申请。
|
||||
|
||||
### 第 3 步:申请审核
|
||||
|
||||
腾讯云平台接到服务申请单后,进行服务需求审核,审核通过后腾讯物联网产品团队将联系您进行初步需求确认与商务洽谈。
|
||||
|
||||
### 第 4 步:需求确认
|
||||
完成初步需求评审与商务洽谈后,确认适用产品版本并评估相关信息。
|
||||
|
||||
### 第 5 步:示例代码下载
|
||||
腾讯云TencentOS tiny产品团队提供试用代码工程下载链接,您可以直接下载工程,根据工程包内的开发指南进行试用。
|
||||
|
||||
### 第 6 步:试用反馈
|
||||
您对TencentOS tiny产品试用过程中有任何问题或者建议,欢迎跟我们的产品团队反馈。
|
||||
|
||||
反馈邮箱:supowang@tencent.com
|
||||
|
2079
doc/4.TencentOS-tiny开发指南.md
Normal file
3153
doc/5.TencentOS-tiny-SDK文档.md
Normal file
19
doc/6.TencentOS tiny常见问题.md
Normal file
@@ -0,0 +1,19 @@
|
||||
1、物联网操作系统提供哪些功能?
|
||||
TencentOS tiny提供了基础RTOS内核、OS适配层、网络适配框架、驱动适配框架、IoT端云SDK,OTA升级、文件系统、异常处理、开发API等功能;
|
||||
|
||||
2、物联网操作系统支持哪些应用场景?
|
||||
TencentOS tiny可支撑智慧城市、智能水表、智能家居、智能穿戴、车联网等多种物联网行业应用,能为为物联网终端厂家和终端设备提供一站式软件解决方案。
|
||||
|
||||
3、物联网操作系统支持哪些芯片模组?
|
||||
TencentOS tiny 支持主流的芯片和通信模组,如STM32、NXP、国民技术、华大半导体、瑞兴恒方等,未来将进一步扩展生态,支持更多不同架构的芯片和模组。
|
||||
|
||||
4、物联网操作系统如何移植?
|
||||
TencentOS tiny提供常见MCU的移植指南,用户根据文档指南进行操作即可,主要是适配不同芯片的任务调度汇编和部分硬件资源;
|
||||
|
||||
5、物联网操作系统如何收费?
|
||||
TencentOS tiny物联网操作系统是腾讯物联网生态链底层关键一环,是一个免费开源的终端软件栈,模组和终端厂家可以免费移植到自己的产品中,支持产品业务快速接入腾讯云IoTHub。
|
||||
|
||||
6、物联网操作系统如何实现与腾讯云物联网平台的对接?
|
||||
TencentOS tiny 提供多种通信网络支持,用户可以使用TencentOS tiny 提供的SDK组件接入到腾讯云物联网平台,目前SDK集成了wifi、2G、NB-IoT、LoRa等方式的链路层协议、也集成了MQTT、LoRaWAN等应用层协议,用户可以根据业务的需求选择合适的方式进行对接。
|
||||
|
||||
|
14
doc/7.TencentOS tiny词汇表.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## TencentOS tiny 词汇术语表
|
||||
|
||||
| 词汇ID | 缩略语 | 中文词汇 | 中文定义 | 英文词汇 | 英文定义 |
|
||||
| :-------- | ------ | :--------------------- | ------------------------------------------------------------ | ------------------------------------- | ------------------------------------------------------------ |
|
||||
| RTOS_001 | RTOS | 实时操作系统 | 实时操作系统(RTOS)是指当外界事件或数据产生时,能够接受并以足够快的速度予以处理,其处理的结果又能在规定的时间之内来控制生产过程或对处理系统做出快速响应,调度一切可利用的资源完成实时任务,并控制所有实时任务协调一致运行的操作系统。 | Real Time Operating System | Real-time operating system (RTOS) means that when external events or data are generated, it can be accepted and processed at a fast enough speed. The result of the processing can control the production process or make a quick response to the processing system within a specified time. In response, schedule all available resources to complete real-time tasks and control the operating system in which all real-time tasks run in unison. |
|
||||
| MQTT001 | MQTT | 消息队列遥测传输 | 消息队列遥测传输是ISO 标准下基于发布/订阅范式的消息协议 | Message Queuing Telemetry Transport | Message Queuing Telemetry Transport is an ISO standard publish-subscribe-based messaging protocol. |
|
||||
| CoAP001 | CoAP | 受限制的应用协议 | 受限制的应用协议是一种专用的Web传输协议,用于物联网中的受约束节点和受约束的网络。 | Constrained Application Protocol | Constrained Application Protocol is a specialized Internet Application Protocol for constrained devices, as defined in RFC 7252. |
|
||||
| NB-IoT001 | NB-IoT | 窄带物联网 | 窄带物联网, 是可与蜂窝网融合演进的低成本电信级高可靠性、高安全性广域物联网技术。 | Narrow Band Internet of Things | The narrowband Internet of Things is a low-cost carrier-class, high-reliability, high-security wide-area Internet of Things technology that can evolve with cellular networks. |
|
||||
| LORa001 | LoRa | 远距离无线电 | 远距离无线电, 是semtech公司创建的低功耗局域网无线标准。 | Long Range Radio | Long-range radio is a low-power LAN wireless standard created by Semtech. |
|
||||
| LPWA001 | LPWA | 低功耗广域网络 | 低功耗广域网络,是面向物联网中远距离和低功耗的通信需求,出现的一种物联网无线连接技术,具有传输距离远、节点功耗低、网络结构简单、运行维护成本低的技术特点。 | Low Power Wide Area | Low-power wide-area network is a kind of communication technology for long-distance and low-power communication in the Internet of Things. An Internet of Things wireless connection technology has the technology of long transmission distance, low node power consumption, simple network structure and low operation and maintenance cost. Features. |
|
||||
| SAL001 | SAL | 套接字抽象层 | | Socket Abstraction Layer | |
|
||||
| DTLS001 | DTLS | 数据包传输层安全性协议 | 数据包传输层安全性协议,安全传输层协议不能用来保证UDP上传输的数据的安全,因此Datagram TLS是在现存的TLS协议架构上提出扩展,使之支持UDP,即成为TLS的一个支持数据报传输的版本。 | Datagram Transport Layer Security | Datagram transport layer security protocol, the secure transport layer protocol can not be used to ensure the security of data transmitted over UDP. Therefore, Datagram TLS is extended on the existing TLS protocol architecture to support UDP, which is a supporting data of TLS. The version of the transmission. |
|
||||
| TLS001 | TLS | 安全传输层协议 | 安全传输层协议用于在两个通信应用程序之间提供保密性和数据完整性。 | Transport Layer Security | The secure transport layer protocol is used to provide confidentiality and data integrity between two communication applications. |
|
||||
| OTA001 | OTA | 空中下载技术 | 空中下载技术,物联网领域主要用于从云端触发升级,更新终端设备的固件。 | Over-the-Air Technology | Over-the-Air Technology, the Internet of Things field is mainly used to trigger upgrades from the cloud and update the firmware of the terminal device. |
|
390
doc/8.TencentOS-tiny对接腾讯云IoTHub开发指南.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# TencentOS tiny端云对接开发指南(IoTHub)
|
||||
|
||||
| Revision | Date | Description |
|
||||
| -------- | --------- | ----------- |
|
||||
| 1.0 | 2019-1-15 | 文档初版 |
|
||||
| 2.0 | 2019-8-13 | 文档V2.0 |
|
||||
|
||||
概览
|
||||
----
|
||||
|
||||
腾讯物联网通信(IoT Hub)服务,旨在提供一个安全、稳定、高效的连接平台,帮助开发者低成本、快速地实现“设备-设备”、“设备-用户应用”、“设备-云服务”之间可靠、高并发的数据通信。也就是说,腾讯物联网通信可以实现设备之间的互动、设备的数据上报和配置下发,还可以基于规则引擎和腾讯云产品打通,方便快捷的实现海量设备数据的存储、计算以及智能分析。总之,基于腾讯物联网通信,开发者可以低成本实现“设备-数据-应用-云服务”的连接,快速搭建物联网应用平台。
|
||||
|
||||
**腾讯云物联网通信产品架构**
|
||||
|
||||

|
||||
|
||||
在上图所示的架构中, 本文只讨论"用户设备"和腾讯云的对接. 这上图中可以看到, "用户设备"和腾讯云之间是基于**MQTT**协议或是**CoAP**协议进行通信的. 因此, 在"用户设备"上, 需要包含连接SDK或是实现了MQTT或CoAP协议的代码. 本文讲述MQTT连接方法, CoAP协议类似.
|
||||
|
||||
**MQTT简介**
|
||||
|
||||
物联网 (IoT) 设备必须连接互联网. 通过连接到互联网, 设备就能相互协作, 以及与后端服务协同工作. 互联网的基础网络协议是 TCP/IP. MQTT(消息队列遥测传输)是基于 TCP/IP 协议栈而构建的, 已成为 IoT 通信的标准. MQTT本身是个轻量级的协议, 可以在硬件受限设备上实现. 同时, MQTT协议支持在各方之间异步通信的消息协议, 异步消息协议在空间和时间上将消息发送者与接收者分离, 因此可以在不可靠的网络环境中进行扩展. MQTT的灵活性使得为 IoT 设备和服务的多样化应用场景提供支持成为可能.
|
||||
|
||||

|
||||
|
||||
MQTT协议基于发布(Publish)和订阅(Subscribe)模型. 在网络中定义了两种实体类型: **消息代理端(Broker)**和**客户端(Client)**, 代理是一个服务器, 它从客户端接收所有消息, 然后将这些消息路由到相关的目标客户端. 客户端是能够与代理交互来发送和接收消息的任何事物. 客户端可以是现场的 IoT 传感器, 或者是数据中心内处理 IoT 数据的应用程序.
|
||||
|
||||
Client和Broker之间的发布和订阅是根据主题(Topic)来进行的, 不同的客户端可以向不同的主题发布消息, 设备只有在订阅了某个主题后, 才能收到相应主题的消息.
|
||||
|
||||
|
||||
|
||||
云端设置
|
||||
--------
|
||||
|
||||
云端设置主要包含了, 创建新产品, 创建新设备
|
||||
|
||||
登录腾讯云, 搜索"云产品"下的"物联网通信"产品, 或直接访问 https://console.cloud.tencent.com/iotcloud
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
**创建新产品**
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
在"添加新产品"的时候, 注意选择. 产品类型选择**普通产品**, 产品名称随意, 认证方式选择**密钥认证**, 数据格式选择**自定义**.
|
||||
|
||||
> 关于认证方式和数据格式的选择解释
|
||||
>
|
||||
> 认证方式默认的是证书认证, 数据格式默认的是json格式.
|
||||
>
|
||||
> 对于认证方式, 指定了设备通过何种方式和云端进行双向认证. 默认的证书方式相对于密钥认证安全性高一点, 但是问题在于证书方式需要在嵌入式设备端存储证书同时实现证书的相关处理, 对设备的RAM和ROM要求较高, 相对而言, 密钥认证的方式资源占用量就小点, 由于我们主要支持的设备都是小型嵌入式设备, 因此选用密钥认证.
|
||||
>
|
||||
> 数据格式指的是设备和云端进行数据交互时候使用的格式, json格式为文本字符串, 可读性高, 并且便于解析, 对于功能复杂的设备交互而已比较理想, 但是对于小型设备或是定制设备, 数据单一, 或是有自定义的格式(二进制或是文本), 这种时候, 用自定义的数据格式, 一方面节约流量, 另一方面比较灵活.
|
||||
|
||||
注: 这里的数据格式选择会影响之后腾讯云"规则引擎"组件的设置.
|
||||
|
||||

|
||||
|
||||
新建完产品后, 会获得一个平台分配的**productID**.
|
||||
|
||||
|
||||
|
||||
**创建新设备**
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
设置的时候只需要设置设备名称即可, 由于我们在创建产品的时候, 认证方式选择了密钥认证, 因此在创建设备的时候将会提供设备对应的密钥, 这里选择默认的"使用物联通通信提供的密钥"即可.
|
||||
|
||||

|
||||
|
||||
添加完设备后, 会告知设备对应的密钥. 该密钥将会用于之后设备与平台通信时的认证.
|
||||
|
||||
为了实现设备间的通信, 我们还需要创建第二个设备, 操作同上, 不妨将其命名为"dev2".
|
||||
|
||||

|
||||
|
||||
此时, 在产品"my_product"下面, 有2个我们添加的设备, 分别为"dev1"和"dev2".
|
||||
|
||||
|
||||
|
||||
**Topic设置**
|
||||
|
||||
我们知道, 设备通过MQTT协议进行通信, 是基于发布(publish)和订阅(subscribe)相关的话题(topic)来进行的, 因此, 还需要在云端对话题进行设置.
|
||||
|
||||

|
||||
|
||||
我们可以在"权限列表"中看到Topic对应的操作权限, 此处还可以添加新的Topic.
|
||||
|
||||
在这里, 我们可以看到, 平台默认配置了两类的Topic, 用于执行发布和订阅. 这里之所以是两类而不是两个, 是因为Topic里使用了变量. 这里的`QOW7EO9S31`实际上是productID; `${deviceName}`为平台设置的变量, 即设备名; `control`和`event`为Topic名字. 所以, 在我们创建了2个设备dev1和dev2的情况下, 在my_product产品下, 即存在4个Topic, 分别为:
|
||||
|
||||
- QOW7EO9S31/dev1/control 订阅权限
|
||||
- QOW7EO9S31/dev1/event 发布权限
|
||||
- QOW7EO9S31/dev2/control 订阅权限
|
||||
- QOW7EO9S31/dev2/event 发布权限
|
||||
|
||||
这里默认的Topic已经足够我们使用, 不需要额外添加Topic和权限了.
|
||||
|
||||
> MQTT的Topic本身是一个普通的字符串, 但是可以由多个层级组成, 根据`/`来划分. 这种分层的Topic结构使得主题的过滤和匹配变得相对灵活.
|
||||
>
|
||||
> 多层通配符"#"
|
||||
>
|
||||
> 必须位于最后一个字符, 匹配多层级. 例如对于`QOW7EO9S31/#`, 将会匹配`QOW7EO9S31/dev1`, `QOW7EO9S31/dev2`, `QOW7EO9S31/dev1/control`, `QOW7EO9S31/dev1/event`, `QOW7EO9S31/dev2/control`和`QOW7EO9S31/dev2/event`主题.
|
||||
>
|
||||
> 单层通配符"+"
|
||||
>
|
||||
> 匹配单个层级, 在主题的任何层级都可以使用, 包括第一个层级和最后一个层级.
|
||||
>
|
||||
> 例如, 对于`QOW7EO9S31/+/event`, 将会匹配`QOW7EO9S31/dev1/event`和`QOW7EO9S31/dev2/event`.
|
||||
|
||||
|
||||
|
||||
**设置规则引擎**
|
||||
|
||||
规则引擎本身不属于MQTT协议的范畴, 但是平台侧出于安全角度考虑添加了规则引擎, 实现了Topic之间的转发操作, 我们需要合理的设置规则引擎才能实现多个设备之间的数据收发, 由于理解起来比较复杂, 我们这里简要讲解下为什么需要规则引擎, 规则引擎的作用, 如何设置规则引擎.
|
||||
|
||||
1. 为什么需要规则引擎
|
||||
|
||||
在上节的Topic中, 我们知道, 在平台侧, 对于不同的Topic, 规定了不同的权限, 例如, 对于`QOW7EO9S31/dev1/event`这个Topic, 只具有发布权限, 而对于`QOW7EO9S31/dev1/control`这个Topic, 只具有订阅权限. 对于设备dev1, 很自然的, 会朝`QOW7EO9S31/dev1/event`这个Topic发送数据, 并且订阅`QOW7EO9S31/dev1/control`这个Topic的消息. 但是这里就会涉及到, event的数据最后到哪去, control的数据从哪里来的问题.
|
||||
|
||||
在本文的例子中, 我们希望dev1和dev2发生交互, 即相互收发消息. 由于MQTT是基于Topic的发布订阅机制, 因此, dev1想要获得dev2的数据, 直觉上, 需要订阅dev2发布消息的那个Topic. 假定dev2朝`QOW7EO9S31/dev2/event`Topic上发送数据, 那么dev1想要获得dev2发布的消息, 最直接的办法是订阅同样的Topic, 即`QOW7EO9S31/dev2/event`, 但是这里存在几个问题, 首先, event Topic只具有发布权限, 没有订阅权限, 其次, **在平台侧, 规定了, 不允许跨设备发布或是订阅Topic**, 也就是说, 对于dev1, 只能看到或只允许访问`QOW7EO9S31/dev1`这个Topic以及其下属的Topic, 不能访问`QOW7EO9S31/dev2`及其下属Topic.
|
||||
|
||||
> 平台侧添加不允许跨设备访问Topic的规则虽然不直观, 但却是合理的. 如果不添加这条限制, 那么一个设备可以不加限制的订阅同一个产品下所有其他设备的Topic, 获取其上报的消息, 这存在潜在的安全漏洞.
|
||||
|
||||
2. 规则引擎的作用
|
||||
|
||||
因为不允许直接跨设备访问Topic, 所以需要依靠"规则引擎"来手动添加规则, 将指定的Topic消息转发到另一个Topic上, 实现不同设备之间的通信.
|
||||
|
||||

|
||||
|
||||
上图介绍了规则引擎的主要作用"republish", 即将一个Topic下的消息republish到另一个Topic下. 从图中我们可以看到, 规则引擎将`QOW7EO9S31/dev2/event`的消息republish到了`QOW7EO9S31/dev1/control`下. 将`QOW7EO9S31/dev1/event`的消息republish到了`QOW7EO9S31/dev2/control`下.
|
||||
|
||||
这样, 对于dev1而言, 只需要订阅`QOW7EO9S31/dev1/control`就可以接收来自`QOW7EO9S31/dev2/event`的消息了. dev2同理.
|
||||
|
||||
3. 设置规则引擎
|
||||
|
||||

|
||||
|
||||
在物联网通信界面选择"规则引擎"--"新建规则", 随意指定一个规则名称, 我们这里不妨设置为"1to2".
|
||||
|
||||

|
||||
|
||||
这里, 我们看到规则的详细设置信息, 主要包括"筛选数据"和"行为操作". "筛选数据"针对指定Topic接收到的消息内容进行进一步的筛选, 比如匹配消息中的字段来决定是否执行之后的设置的"行为操作". 而"行为操作"则是指定对通过匹配的消息进行何种操作, 主要的操作有"数据转发到另一个Topic(Republish)", "转发到第三方服务(Forward)"以及转发到腾讯云各个对应组件中.
|
||||
|
||||

|
||||
|
||||
上图是设置好的规则, 这里, 我们将"筛选数据"部分的筛选字段设置为`*`, 筛选的Topic为`QOW7EO9S31/dev1/event`, 条件设置为空, 即不筛选数据, 全部匹配. 然后, 执行的操作是将数据转发到`QOW7EO9S31/dev2/control`, 设置完这条规则, 就实现了dev2通过订阅control就能收到dev1发送到event的数据.
|
||||
|
||||
> 关于"筛选数据"的设定
|
||||
>
|
||||
> 由于我们在新建产品, 设置数据格式的时候选择了自定义数据格式, 在自定义数据格式的情况下, 当前平台将其当做二进制流来处理, 也就无法通过匹配字段进行数据筛选.
|
||||
>
|
||||
> 如果在进行产品的时候, 使用数据格式是json, 那么此处就可以根据json中的字段进行SQL的匹配和筛选.
|
||||
|
||||
同理, 我们再设置新的一个规则"2to1", 实现`QOW7EO9S31/dev2/event`到`QOW7EO9S31/dev1/control`的转发.
|
||||
|
||||

|
||||
|
||||
这样, 在平台侧dev1到dev2的双向数据通路就打通了.
|
||||
|
||||
**云日志和消息队列CMQ**
|
||||
|
||||
在平台侧都设置好后, 我们在之后的测试过程或是通信过程中, 往往还需要查看平台是否收到了设备发送上来的消息, 对消息执行了哪些操作, 消息的具体内容(payload)是什么. 腾讯云提供了物联网通信产品的"云日志"功能和腾讯云组件"消息队列CMQ".
|
||||
|
||||
云日志
|
||||
|
||||
可以在产品列表下找到"云日志", 点击搜索即可显示对应的行为日志
|
||||
|
||||

|
||||
|
||||
参考日志如下, 可以看到日志记录了设备的连接, 连接断开, 发布, 订阅等行为, 也记录了规则引擎的操作, 还有CMQ队列的一些行为日志. 但是关于设备发布的消息内容, 在云日志中无法查看, 需要借助消息队列CMQ.
|
||||
|
||||

|
||||
|
||||
消息队列CMQ
|
||||
|
||||
可以在产品列表中找到"消息队列"选项, 设置队列所想要接收的消息类型后保存配置, 即可将平台侧收到的设备消息额外发送到腾讯云消息队列CMQ组件中.
|
||||
|
||||

|
||||
|
||||
设置完消息队列后可以在"云产品"中搜索CMQ, 即可找到对应的消息队列, 点击"开始接收消息" 接收消息队列中的内容, 参考如下
|
||||
|
||||

|
||||
|
||||
其中可以看到有些消息带有"PayloadLen"和"Playload"字段, 即为具体的消息内容.
|
||||
|
||||
> 在密钥认证下, 消息的内容(payload)是经过base64编码的, 所以在平台侧看到的数据类似乱码实际上是经过编码后的结果, 想要查看具体的内容, 可以在linux下, `echo <payload> | base64 --decode`.
|
||||
|
||||
设备终端设置
|
||||
------------
|
||||
|
||||
在前文中我们提到, 设备终端跟腾讯云之间的通信采用的是MQTT协议, 而MQTT协议需要消息代理端和客户端相互配合, 因此, 我们在终端设备上, 需要**实现一个MQTT Client**. 腾讯云上支持的MQTT协议和标准协议区别不大, 可以在[MQTT协议说明](https://cloud.tencent.com/document/product/634/14065)查看具体区别. 因此, 技术上来说, 设备终端只要实现了标准的MQTT客户端, 均可以跟腾讯云正常通信.
|
||||
|
||||
在本节中, 我们介绍如何使用腾讯IoT OS与腾讯云通过MQTT进行通信.
|
||||
|
||||
腾讯IoT OS集成了对MQTT和腾讯云的支持, 开发者只需要通过简单的设置即可实现与腾讯云的通信, 整体步骤主要包含:
|
||||
|
||||
- 项目工程中添加MQTT组件相关文件
|
||||
- 适配AT HAL层串口及网络接口函数
|
||||
- 执行脚本生成MQTT配置头文件
|
||||
- 调用OS提供的接口完成和腾讯云的对接
|
||||
|
||||
**项目工程中添加MQTT组件相关文件**
|
||||
|
||||
在工程中新建`hal`、`at`和`mqtt`组, 并添加下列源文件,可以参考TencentOS_tiny\board\TencentOS_tiny_EVB_MX\KEIL\tencent_os_mqtt工程:
|
||||
|
||||

|
||||
|
||||
上述文件分别位于`TencentOS_tiny\net\at`, `TencentOS_tiny\components\connectivity\Eclipse-Paho-MQTT`和`TencentOS_tiny\platform\hal\st\stm32l4xx\src`目录下.
|
||||
|
||||
相应的, 也需要引入对应的头文件
|
||||
|
||||

|
||||
|
||||
**适配AT HAL层串口及网络接口函数**
|
||||
|
||||
上层MQTT的操作最终都会转为为对底层通信设备的socket操作, OS集成了sal_module_wrapper层, 但是由于各个开发板硬件不同, 因此需要对sal_module_wrapper进行适配. 适配过程的核心数据结构为`sal_module_t`, 在`TencentOS_tiny\net\sal_module_wrapper\sal_module_wrapper.h`文件中定义:
|
||||
|
||||
```c
|
||||
typedef struct sal_module_st{
|
||||
int (*init)(void);
|
||||
int (*get_local_mac)(char *mac);
|
||||
int (*get_local_ip)(char *ip, char *gw, char *mask);
|
||||
int (*parse_domain)(const char *host_name, char *host_ip, size_t host_ip_len);
|
||||
int (*connect)(const char *ip, const char *port, sal_proto_t proto);
|
||||
int (*send)(int sock, const void *buf, size_t len);
|
||||
int (*recv_timeout)(int sock, void *buf, size_t len, uint32_t timeout);
|
||||
int (*recv)(int sock, void *buf, size_t len);
|
||||
int (*sendto)(int sock, char *ip, char *port, const void *buf, size_t len);
|
||||
int (*recvfrom)(int sock, char *ip, char *port, void *buf, size_t len);
|
||||
int (*recvfrom_timeout)(int sock, char *ip, char *port, void *buf, size_t len, uint32_t timeout);
|
||||
int (*close)(int sock);
|
||||
}sal_module_t;
|
||||
```
|
||||
|
||||
在使用的时候, 并需要全部实现, 只需实现下列几个核心的接口即可:
|
||||
|
||||
```c
|
||||
sal_module_t sal_module_esp8266 = {
|
||||
.init = esp8266_init,
|
||||
.connect = esp8266_connect,
|
||||
.send = esp8266_send,
|
||||
.recv_timeout = esp8266_recv_timeout,
|
||||
.recv = esp8266_recv,
|
||||
.sendto = esp8266_sendto,
|
||||
.recvfrom = esp8266_recvfrom,
|
||||
.recvfrom_timeout = esp8266_recvfrom_timeout,
|
||||
.close = esp8266_close,
|
||||
.parse_domain = esp8266_parse_domain,
|
||||
};
|
||||
```
|
||||
|
||||
这里接口对应的`esp8266_xxx`函数需要开发者根据硬件上具有的通信模组进行相应的适配.
|
||||
|
||||
该结构填充完后, 需要在使用MQTT之前, 调用一下初始化接口注册串口和网络API函数:
|
||||
|
||||
```c
|
||||
esp8266_sal_init(hal_uart_port_t uart_port);
|
||||
```
|
||||
|
||||
**执行脚本生成MQTT配置头文件**
|
||||
|
||||
从之前介绍腾讯云端配置的章节中可以知道, 在MQTT客户端, 所需要的核心信息只包含:
|
||||
|
||||
- Product ID
|
||||
- Device Name
|
||||
- Password
|
||||
|
||||
开发者在终端节点开发过程中, 可以使用`TencentOS_tiny\tools`下的`mqtt_config_gen.py`脚本来动态生成MQTT配置文件`mqtt_config.h`
|
||||
|
||||
```shell
|
||||
$ python mqtt_config_gen.py
|
||||
product id:QOW7EO9S31
|
||||
device name:dev1
|
||||
password:k05qMb3EXM5CE5ocNcsDvA==
|
||||
subscribe topic:[default:control]
|
||||
publish topic:[default:event]
|
||||
===============Generate tos_mqtt_config.h==================
|
||||
#ifndef TOS_MQTT_CONFIG_H
|
||||
#define TOS_MQTT_CONFIG_H
|
||||
|
||||
#define MQTT_SERVER_IP "111.230.189.156"
|
||||
#define MQTT_SERVER_PORT "1883"
|
||||
#define MQTT_PRODUCT_ID "QOW7EO9S31"
|
||||
#define MQTT_DEV_NAME "dev1"
|
||||
#define MQTT_CLIENT_ID "QOW7EO9S31dev1"
|
||||
#define MQTT_USR_NAME "QOW7EO9S31dev1;21010406;12365;2147483648"
|
||||
#define MQTT_PASSWORD "49344dd251a98d07e33ba54d4d2a0640303c6e8c;hmacsha1"
|
||||
#define MQTT_SUBSCRIBE_TOPIC "QOW7EO9S31/dev1/control"
|
||||
#define MQTT_PUBLISH_TOPIC "QOW7EO9S31/dev1/event"
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
如上所示, 只需要按提示依次输入腾讯云端提供的`Product ID`, `Device Name`和`Password`即可, 至于订阅/发布的topic, 如果采用默认的control和event直接回车, 也可自行设置. 脚本会在脚本所在目录下生成一个`tos_mqtt_config.h`的头文件, 开发者将其放置到自己的工程目录下任意位置并在工程中添加头文件引用即可.
|
||||
|
||||
**调用TencentOS tiny提供的接口完成和腾讯云的对接**
|
||||
|
||||
以下是与腾讯云对接并收发信息的例程:
|
||||
|
||||
```c
|
||||
void mqtt_demo(void)
|
||||
{
|
||||
int count = 1;
|
||||
int sock_id = 0;
|
||||
uint8_t read_data[40];
|
||||
int read_len;
|
||||
char topic[30];
|
||||
char buffer[32];
|
||||
|
||||
mqtt_con_param_t con_param;
|
||||
con_param.keep_alive_interval = 2000;
|
||||
con_param.cleansession = 1;
|
||||
con_param.username = MQTT_USR_NAME;
|
||||
con_param.password = MQTT_PASSWORD;
|
||||
con_param.client_id = MQTT_CLIENT_ID;
|
||||
|
||||
mqtt_pub_param_t pub_param;
|
||||
pub_param.dup = 0;
|
||||
pub_param.qos = 0;
|
||||
pub_param.retained = 0;
|
||||
pub_param.id = 0;
|
||||
pub_param.topic = MQTT_PUBLISH_TOPIC;
|
||||
|
||||
mqtt_sub_param_t sub_param;
|
||||
sub_param.count = 1;
|
||||
sub_param.dup = 0;
|
||||
sub_param.id = 1;
|
||||
sub_param.req_qos = 0;
|
||||
sub_param.topic = MQTT_SUBSCRIBE_TOPIC;
|
||||
|
||||
#ifdef USE_ESP8266
|
||||
esp8266_sal_init(HAL_UART_PORT_0);
|
||||
esp8266_join_ap("SheldonDai", "srnr6x9xbhmb0");
|
||||
#endif
|
||||
|
||||
#ifdef USE_NB_BC35
|
||||
int bc35_28_95_sal_init(hal_uart_port_t uart_port);
|
||||
bc35_28_95_sal_init(HAL_UART_PORT_0);
|
||||
#endif
|
||||
|
||||
sock_id = tos_mqtt_connect(MQTT_SERVER_IP, MQTT_SERVER_PORT, &con_param);
|
||||
|
||||
printf("socket id: %d\r\n",sock_id);
|
||||
|
||||
if (tos_mqtt_subscribe(sock_id, &sub_param) != 0) {
|
||||
printf("subscribe failed!!!\n");
|
||||
}
|
||||
|
||||
while (1) {
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "@%3.1f@%3.1f@%.1f", 25.8,80.6, 90.1);
|
||||
|
||||
printf("publish topic\r\n");
|
||||
pub_param.topic = MQTT_PUBLISH_TOPIC;
|
||||
pub_param.payload = (unsigned char *)buffer;
|
||||
pub_param.payload_len = sizeof(buffer);
|
||||
if (tos_mqtt_publish(sock_id, &pub_param) != 0) {
|
||||
printf("publish failed!!!\n");
|
||||
}
|
||||
|
||||
printf("MQTT count: %d\r\n",count);
|
||||
printf("read buf\r\n");
|
||||
read_len = tos_mqtt_receive(topic, sizeof(topic), read_data, sizeof(read_data));
|
||||
if (read_len >= 0) {
|
||||
printf("---------->topic: %s, payload: %s, payload_len: %d\n", topic, read_data, read_len);
|
||||
}
|
||||
|
||||
count++;
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
可以看到, 其中核心的接口为:
|
||||
|
||||
`tos_mqtt_connect`: 连接腾讯云
|
||||
|
||||
`tos_mqtt_subscribe`: 订阅Topic
|
||||
|
||||
`tos_mqtt_receive`: 读取订阅消息
|
||||
|
||||
`tos_mqtt_publish`: 发布消息
|
||||
|
||||
开发者可以根据自己的业务需求来调用这些接口完成特定的业务逻辑.
|
281
doc/TencentOS tiny内核移植指南(KEIL版).md
Normal file
@@ -0,0 +1,281 @@
|
||||
# TencentOS tiny 内核移植参考指南(Keil版)
|
||||
|
||||
### 一、移植前的准备
|
||||
|
||||
1. ##### 准备目标硬件(开发板/芯片/模组)
|
||||
|
||||
TencentOS tiny目前主要支持ARM Cortex M核芯片的移植,比如STM32 基于Cortex M核全系列、NXP 基于Cortex M核全系列等。本教程将使用STM32官方Demo开发板 NUCLEO-L073RZ进行示例移植,其他 ARM Cortex M系列开发板和芯片移植方法类似。
|
||||
|
||||
调试ARM Cortex M核还需要仿真器, NUCLEO-L073RZ自带ST-Link调试器,如果您的开发板或者芯片模组没有板载仿真器,就需要连接外置的仿真器,如J-Link、U-Link之类的。
|
||||
|
||||
|
||||
|
||||
2. ##### 准备编译器环境
|
||||
|
||||
本移植指南针对的是Keil编译器,所以我们移植内核前需要先安装Keil编译器,能编译ARM Cortex M核的Keil编译器现在也叫MDK,最新版本5.28a,下载地址为:https://www.keil.com/demo/eval/arm.htm
|
||||
|
||||
填写注册信息即可下载,下载完成在windows环境下按照提示安装即可,安装完成后需要自行购买软件License,避免32K Flash下载限制。
|
||||
|
||||
由于新版本的MDK编译器和芯片支持包是分离的,所以MDK(Keil)安装完成后,还需要安装对应芯片的器件支持包(PACK包),比如本教程示例的NUCLEO-L037RZ开发板的芯片是STM32L073RZ,就需要安装*Keil.STM32L0xx_DFP.2.0.1.pack*系列器件支持包,MDK所有支持芯片的PACK包下载地址为:http://www.keil.com/dd2/Pack/#/eula-container,您只需要根据您的芯片型号下载对应的PACK包即可,当然您也可以在MDK集成开发环境中在线下载安装。
|
||||
|
||||
3. ##### 准备芯片对应的裸机工程
|
||||
|
||||
移植TencentOS tiny基础内核需要您提前准备一个芯片对应的裸机工程,裸机工程包含基本的芯片启动文件、基础配置(时钟、主频等)、以及串口、基本GPIO驱动用于RTOS测试。
|
||||
|
||||
本教程使用ST官方的STM32CubeMX软件来自动化生成MDK裸机工程,STM32CubeMX的下载地址为:
|
||||
|
||||
https://www.st.com/content/st_com/zh/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-configurators-and-code-generators/stm32cubemx.html
|
||||
|
||||
安装STM32CubeMx还需要事先安装好JDK环境,您可以在互联网上查找如何安装和配置JDK环境,此处不再赘述。
|
||||
|
||||
CubeMX安装完成后,我们就可以使用CubeMX来给NUCLEO-L037RZ开发板生成裸机工程了,如果您的芯片不是STM32,而是其他厂商的ARM Cortex M系列,您可以根据产商的指导准备裸机工程,后续的内核移植步骤是一致的。
|
||||
|
||||
###### 3.1 首先启动STM32CubeMX,新建工程
|
||||
|
||||

|
||||
|
||||
###### 3.2 选择MCU型号
|
||||
|
||||

|
||||
|
||||
如上图所示:通过MCU筛选来找到自己开发板对应的芯片型号,双击后弹出工程配置界面,如下图:
|
||||
|
||||

|
||||
|
||||
###### 3.3 Pin设置界面配置时钟源
|
||||
|
||||

|
||||
|
||||
###### 3.4 Pin设置界面配置串口
|
||||
|
||||

|
||||
|
||||
###### 3.5 Pin设置界面配置GPIO
|
||||
|
||||

|
||||
|
||||
###### 3.6 配置总线时钟
|
||||
|
||||

|
||||
|
||||
###### 3.7 工程生成参数配置
|
||||
|
||||

|
||||
|
||||
###### 3.8 代码生成方式配置
|
||||
|
||||

|
||||
|
||||
###### 3.9 生成工程
|
||||
|
||||

|
||||
|
||||
###### 3.10 keil下的裸机工程
|
||||
|
||||
点击生成代码后,生成的裸机工程效果如下:
|
||||
|
||||

|
||||
|
||||
这样NUCLEO-L073RZ裸机工程生成完成,该工程可直接编译并烧写在板子上运行。
|
||||
|
||||
4. ##### 准备TencentOS tiny的源码
|
||||
|
||||
TencentOS tiny的源码已经在内网开源,git下载地址为:http://git.code.oa.com/TencentOS_tiny/TencentOS_tiny.git
|
||||
|
||||
|
||||
|
||||
| 一级目录 | 二级目录|说明 |
|
||||
| -------- | --------- | --------------- |----------|
|
||||
| arch | arm | TencentOS tiny适配的IP核架构(含M核中断、调度、tick相关代码) |
|
||||
| board | NUCLEO_L073RZ | 移植目标芯片的工程文件 |
|
||||
| kernel | core | TencentOS tiny内核源码|
|
||||
| | pm | TencentOS tiny低功耗模块源码 |
|
||||
| osal | cmsis_os | TencentOS tiny提供的cmsis os 适配 |
|
||||
|
||||
由于本教程只介绍TencentOS tiny的内核移植,所以这里只需要用到 arch、board、kernel、osal四个目录下的源码。
|
||||
|
||||
### 二、内核移植
|
||||
|
||||
1. ##### 代码目录规划
|
||||
|
||||

|
||||
|
||||
如图所示,新建TencentOS_tiny主目录,并在主目录下添加四个子目录,其中arch、kernel、osal从代码仓直接拷贝过来即可,而board目录下则放入我们前面生成的裸机工程代码,我们移植的开发板取名叫NUCLEO_L073RZ,裸机代码全部拷贝到下面即可,如下图所示:
|
||||
|
||||

|
||||
|
||||
接下来进入TencentOS_tiny\board\NUCLEO_L073RZ\MDK-ARM目录,打开keil工程,我们开始添加TencentOS tiny的内核代码。
|
||||
|
||||
2. ##### 添加arch平台代码
|
||||
|
||||

|
||||
|
||||
tos_cpu.c是TencentOS tiny 的CPU适配文件,包括堆栈初始化,中断适配等,如果您的芯片是ARM Cortex M核,该文件可以不做改动,M0、M3 、M4、M7是通用的,其他IP核需要重新适配;
|
||||
|
||||
port_s.S 文件是TencentOS tiny的任务调度汇编代码,主要做弹栈压栈等处理的,port_c.c适配systick等,这两个文件 每个IP核和编译器都是不一样的,如果您的芯片是ARM Cortex M核,我们都已经适配好,比如现在我们移植的芯片是STM32L073RZ,是ARM Cortex M0+核,使用的编译器是KEIL,所以我们选择arch\arm\arm-v7m\cortex-m0+\armcc下的适配代码,如果你的开发板是STM32F429IG,M4核,编译器是GCC,则可以选择arch\arm\arm-v7m\cortex-m4\gcc目录下的适配文件。
|
||||
|
||||
3. ##### 添加内核源码
|
||||
|
||||
内核源码kerne目录下包含core和pm两个目录,其中core下为基础内核,pm是内核中的低功耗组件;基础移植的时候可以不添加pm目录下的代码,如下图所示,添加基本内核源码:
|
||||
|
||||

|
||||
|
||||
4. ##### 添加cmsis os源码
|
||||
|
||||
cmsis os是TencentOS tiny为了兼容cmsis标准而适配的OS抽象层,可以简化大家将业务从其他RTOS迁移到TencentOS tiny的工作量。
|
||||
|
||||

|
||||
|
||||
5. ##### 添加TencentOS tiny头文件目录
|
||||
|
||||
添加头文件目录前,我们在要移植的工程目录下新增一个 TOS_CONFIG文件夹,用于存放TencentOS tiny的配置头文件,也就是接下来要新建的tos_config.h文件;
|
||||
|
||||
TencentOS tiny所有要添加的头文件目录如下:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
6. ##### 新建TencentOS tiny系统配置文件 tos_config.h
|
||||
|
||||
*#ifndef TOS_CONFIG_H*
|
||||
*#define TOS_CONFIG_H*
|
||||
|
||||
*#include "stm32l0xx.h" // 目标芯片头文件,用户需要根据情况更改*
|
||||
|
||||
*#define TOS_CFG_TASK_PRIO_MAX 10u // 配置TencentOS tiny默认支持的最大优先级数量*
|
||||
|
||||
*#define TOS_CFG_ROUND_ROBIN_EN 1u // 配置TencentOS tiny的内核是否开启时间片轮转*
|
||||
|
||||
*#define TOS_CFG_OBJECT_VERIFY 0u // 配置TencentOS tiny是否校验指针合法*
|
||||
|
||||
*#define TOS_CFG_EVENT_EN 1u // TencentOS tiny 事件模块功能宏*
|
||||
|
||||
*#define TOS_CFG_MMBLK_EN 1u //配置TencentOS tiny是否开启内存块管理模块*
|
||||
|
||||
*#define TOS_CFG_MMHEAP_EN 1u // 配置TencentOS tiny是否开启动态内存模块*
|
||||
|
||||
*#define TOS_CFG_MMHEAP_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小*
|
||||
|
||||
*#define TOS_CFG_MUTEX_EN 1u // 配置TencentOS tiny是否开启互斥锁模块*
|
||||
|
||||
*#define TOS_CFG_QUEUE_EN 1u // 配置TencentOS tiny是否开启队列模块*
|
||||
|
||||
*#define TOS_CFG_TIMER_EN 1u // 配置TencentOS tiny是否开启软件定时器模块*
|
||||
|
||||
*#define TOS_CFG_SEM_EN 1u // 配置TencentOS tiny是否开启信号量模块*
|
||||
|
||||
*#define TOS_CFG_TICKLESS_EN 0u // 配置Tickless 低功耗模块开关*
|
||||
|
||||
*#if (TOS_CFG_QUEUE_EN > 0u)*
|
||||
*#define TOS_CFG_MSG_EN 1u*
|
||||
*#else*
|
||||
*#define TOS_CFG_MSG_EN 0u*
|
||||
*#endif*
|
||||
|
||||
*#define TOS_CFG_MSG_POOL_SIZE 10u // 配置TencentOS tiny消息队列大小*
|
||||
|
||||
*#define TOS_CFG_IDLE_TASK_STK_SIZE 64u // 配置TencentOS tiny空闲任务栈大小*
|
||||
|
||||
*#define TOS_CFG_CPU_TICK_PER_SECOND 1000u // 配置TencentOS tiny的tick频率*
|
||||
|
||||
*#define TOS_CFG_CPU_CLOCK (SystemCoreClock) // 配置TencentOS tiny CPU频率*
|
||||
|
||||
*#define TOS_CFG_TIMER_AS_PROC 1u // 配置是否将TIMER配置成函数模式*
|
||||
|
||||
*#endif*
|
||||
|
||||
按照上面的模板配置好TencentOS tiny的各项功能后,将tos_config.h 文件放入要移植的board工程目录下即可,例如本教程是放到board\NUCLEO_L073RZ\TOS_CONFIG目录下。
|
||||
|
||||
这样,TencentOS tiny的源码就全部添加完毕了。
|
||||
|
||||
### 三、创建TencentOS tiny任务,测试移植结果
|
||||
|
||||
1. ##### 修改部分代码
|
||||
###### 修改stm32l0xx_it.c的中断函数
|
||||
在stm32l0xx_it.c文件中包含 tos.h 头文件
|
||||

|
||||
|
||||
在stm32l0xx_it.c文件中的PendSV_Handler函数前添加__weak关键字,因为该函数在TencentOS tiny的调度汇编中已经重新实现;同时在SysTick_Handler函数中添加TencentOS tiny的调度处理函数,如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
2. ##### 编写TencentOS tiny 测试任务
|
||||
|
||||
###### 在mian.c 中添加TencentOS tiny 头文件,编写任务函数
|
||||
|
||||
#include "cmsis_os.h"
|
||||
//task1
|
||||
#define TASK1_STK_SIZE 256
|
||||
void task1(void *pdata);
|
||||
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
|
||||
|
||||
//task2
|
||||
#define TASK2_STK_SIZE 256
|
||||
void task2(void *pdata);
|
||||
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
|
||||
|
||||
void task1(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello world!\r\n###This is task1 ,count is %d \r\n", count++);
|
||||
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
|
||||
osDelay(2000);
|
||||
}
|
||||
}
|
||||
void task2(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello TencentOS !\r\n***This is task2 ,count is %d \r\n", count++);
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int fputc(int ch, FILE *f)
|
||||
{
|
||||
if (ch == '\n')
|
||||
{
|
||||
HAL_UART_Transmit(&huart2, (void *)"\r", 1,30000);
|
||||
}
|
||||
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
|
||||
return ch;
|
||||
}
|
||||
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
继续在main.c 的mian函数中硬件外设初始化代码后添加TencentOS tiny的初始化代码:
|
||||
|
||||
osKernelInitialize(); //TOS Tiny kernel initialize
|
||||
osThreadCreate(osThread(task1), NULL);// Create task1
|
||||
osThreadCreate(osThread(task2), NULL);// Create task2
|
||||
osKernelStart();//Start TOS Tiny
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
3. ##### 编译下载测试TencentOS tiny移植结果
|
||||
|
||||

|
||||
|
||||
按照上图指示,进行编译下载到开发板即可完成TencentOS tiny的测试,如下图所示,可以看到串口交替打印信息,表示两个任务正在进行调度,切换运行:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
226
doc/TencentOS tiny内核移植指南(MacOS STM32CubeIDE版).md
Normal file
@@ -0,0 +1,226 @@
|
||||
# TencentOS tiny内核移植指南(MacOS+STM32CubeIDE版)
|
||||
|
||||
## 一、工程创建
|
||||
|
||||
具体的工程创建方法和初始参数设置方法略去。
|
||||
|
||||
需要强调的是本文是以`STM32L431`芯片为基础的,如果是其它芯片,后面的步骤请做相应的调整。
|
||||
|
||||
最后生成的工程目录如下:
|
||||
|
||||

|
||||
|
||||
## 二、TencentOS tiny代码准备
|
||||
|
||||
只需要保留TencentOS这些目录下的代码,其它目录皆可删除。
|
||||
|
||||

|
||||
|
||||
|
||||
在`arch`目录里需要删除的所有代码目录列表
|
||||
|
||||
```
|
||||
arch/arm/arm-v7m/cortex-m0+/
|
||||
arch/arm/arm-v7m/cortex-m3/
|
||||
arch/arm/arm-v7m/cortex-m4/armcc/
|
||||
arch/arm/arm-v7m/cortex-m4/iccarm/
|
||||
arch/arm/arm-v7m/cortex-m7/
|
||||
```
|
||||
|
||||
**注: STM32L431是Cortex-M4内核,所以如果是其它芯片,例如STM32F103就应该删除`arch/arm/arm-v7m/cortex-m4/`目录,保留`arch/arm/arm-v7m/cortex-m3/gcc`目录。**
|
||||
|
||||
重命名代码顶层目录
|
||||
|
||||
```
|
||||
$ mv TencentOS_tiny tiny
|
||||
```
|
||||
|
||||
## 三、移植工作
|
||||
|
||||
1. 把代码`tiny`拷贝到第一步创建的工程目录里
|
||||
|
||||

|
||||
|
||||
刷新工程
|
||||
|
||||

|
||||
|
||||
把代码添加到工程里
|
||||
|
||||

|
||||
|
||||
2. 把下列头文件目录添加到工程里
|
||||
|
||||
```
|
||||
tiny/arch/arm/arm-v7m/common/include/
|
||||
tiny/arch/arm/arm-v7m/cortex-m4/gcc/ # 如上所述,这个目录视特定芯片而定
|
||||
tiny/kernel/core/include/
|
||||
tiny/kernel/pm/include/
|
||||
tiny/osal/cmsis_os/
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
3. 在Inc目录下新建一个头文件`tos_config.h`
|
||||
|
||||
```
|
||||
#ifndef _TOS_CONFIG_H_
|
||||
#define _TOS_CONFIG_H_
|
||||
|
||||
#include "stm32l431xx.h" // 目标芯片头文件,视特定芯片而定
|
||||
|
||||
#include "stddef.h"
|
||||
|
||||
#define TOS_CFG_TASK_PRIO_MAX 10u // 配置TencentOS tiny默认支持的最大优先级数量
|
||||
|
||||
#define TOS_CFG_ROUND_ROBIN_EN 1u // 配置TencentOS tiny的内核是否开启时间片轮转
|
||||
|
||||
#define TOS_CFG_OBJECT_VERIFY 0u // 配置TencentOS tiny是否校验指针合法
|
||||
|
||||
#define TOS_CFG_EVENT_EN 1u // TencentOS tiny 事件模块功能宏
|
||||
|
||||
#define TOS_CFG_MMHEAP_EN 1u // 配置TencentOS tiny是否开启动态内存模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小
|
||||
|
||||
#define TOS_CFG_MUTEX_EN 1u // 配置TencentOS tiny是否开启互斥锁模块
|
||||
|
||||
#define TOS_CFG_QUEUE_EN 1u // 配置TencentOS tiny是否开启队列模块
|
||||
|
||||
#define TOS_CFG_TIMER_EN 1u // 配置TencentOS tiny是否开启软件定时器模块
|
||||
|
||||
#define TOS_CFG_SEM_EN 1u // 配置TencentOS tiny是否开启信号量模块
|
||||
|
||||
#if (TOS_CFG_QUEUE_EN > 0u)
|
||||
#define TOS_CFG_MSG_EN 1u
|
||||
#else
|
||||
#define TOS_CFG_MSG_EN 0u
|
||||
#endif
|
||||
|
||||
#define TOS_CFG_MSG_POOL_SIZE 10u // 配置TencentOS tiny消息队列大小
|
||||
|
||||
#define TOS_CFG_IDLE_TASK_STK_SIZE 256u // 配置TencentOS tiny空闲任务栈大小
|
||||
|
||||
#define TOS_CFG_CPU_TICK_PER_SECOND 1000u // 配置TencentOS tiny的tick频率
|
||||
|
||||
#define TOS_CFG_CPU_CLOCK (SystemCoreClock) // 配置TencentOS tiny CPU频率
|
||||
|
||||
#define TOS_CFG_TIMER_AS_PROC 1u // 配置是否将TIMER配置成函数模式
|
||||
|
||||
#endif
|
||||
|
||||
```
|
||||
|
||||
|
||||
4. 修改`Src/stm32l4xx_it.c`里的代码:
|
||||
|
||||
* 将`void PendSV_Handler(void)`修改为`__weak void PendSV_Handler(void)`
|
||||
* 在`SysTick_Handler`函数中`HAL_IncTick();`之后添加代码
|
||||
|
||||
```
|
||||
if(tos_knl_is_running()) {
|
||||
tos_knl_irq_enter();
|
||||
tos_tick_handler();
|
||||
tos_knl_irq_leave();
|
||||
}
|
||||
```
|
||||
|
||||
5. 修改`Src/main.c`
|
||||
|
||||
添加代码
|
||||
|
||||
```
|
||||
#include "cmsis_os.h"
|
||||
|
||||
//task1
|
||||
#define TASK1_STK_SIZE 512
|
||||
void task1(void *pdata);
|
||||
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
|
||||
|
||||
//task2
|
||||
#define TASK2_STK_SIZE 512
|
||||
void task2(void *pdata);
|
||||
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
|
||||
|
||||
void task1(void *pdata)
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
|
||||
osDelay(200);
|
||||
}
|
||||
}
|
||||
|
||||
void task2(void *pdata) {
|
||||
while(1) {
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
**注:代码中的LED是在创建工程的GPIO选项里指定的,是`PC13`引脚,这个是依据TencentOS tiny EVB_MX开发板而决定的,相应的开发板请做相应调整。**
|
||||
|
||||
主函数代码
|
||||
|
||||
```
|
||||
int main(void)
|
||||
{
|
||||
/* USER CODE BEGIN 1 */
|
||||
|
||||
/* USER CODE END 1 */
|
||||
|
||||
|
||||
/* MCU Configuration--------------------------------------------------------*/
|
||||
|
||||
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
|
||||
HAL_Init();
|
||||
|
||||
/* USER CODE BEGIN Init */
|
||||
|
||||
/* USER CODE END Init */
|
||||
|
||||
/* Configure the system clock */
|
||||
SystemClock_Config();
|
||||
|
||||
/* USER CODE BEGIN SysInit */
|
||||
|
||||
/* USER CODE END SysInit */
|
||||
|
||||
/* Initialize all configured peripherals */
|
||||
MX_GPIO_Init();
|
||||
/* USER CODE BEGIN 2 */
|
||||
|
||||
osKernelInitialize(); //TOS Tiny kernel initialize
|
||||
osThreadCreate(osThread(task1), NULL); // Create task1
|
||||
osThreadCreate(osThread(task2), NULL); // Create task2
|
||||
osKernelStart(); //Start TOS Tiny
|
||||
|
||||
/* USER CODE END 2 */
|
||||
|
||||
/* Infinite loop */
|
||||
/* USER CODE BEGIN WHILE */
|
||||
while (1)
|
||||
{
|
||||
/* USER CODE END WHILE */
|
||||
|
||||
/* USER CODE BEGIN 3 */
|
||||
}
|
||||
/* USER CODE END 3 */
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 附1 清理代码目录命令
|
||||
|
||||
```
|
||||
ls | egrep -v "arch|kernel|osal" | xargs rm -rf
|
||||
rm -rf arch/arm/arm-v7m/cortex-m0+/
|
||||
rm -rf arch/arm/arm-v7m/cortex-m3/
|
||||
rm -rf arch/arm/arm-v7m/cortex-m4/armcc/
|
||||
rm -rf arch/arm/arm-v7m/cortex-m4/iccarm/
|
||||
rm -rf arch/arm/arm-v7m/cortex-m7/
|
||||
```
|
289
doc/TencentOS-tiny-porting-gcc.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# TencentOS tiny 内核移植参考指南(GCC版)
|
||||
|
||||
## 一、移植前的准备
|
||||
### 1. 准备目标硬件(开发板/芯片/模组)
|
||||
|
||||
TencentOS tiny目前主要支持ARM Cortex M核芯片的移植,比如STM32 基于Cortex M核全系列、NXP 基于Cortex M核全系列等。本教程将使用STM32官方Demo开发板 NUCLEO-L073RZ进行示例移植,其他 ARM Cortex M系列开发板和芯片移植方法类似。
|
||||
|
||||
调试ARM Cortex M核还需要仿真器, NUCLEO-L073RZ自带ST-Link调试器,如果您的开发板或者芯片模组没有板载仿真器,就需要连接外置的仿真器,如J-Link、U-Link之类的。
|
||||
|
||||
### 2.准备编译器环境
|
||||
|
||||
本移植指南针对的是GCC编译器,所以我们移植内核前需要先安装GCC编译器,能编译ARM Cortex M核的GCC编译器下载地址为:[https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads]()
|
||||
|
||||

|
||||
|
||||
如上图所示,下载windows exe版本的gcc编译器(Linux下也可以下载linux版本的编译器),下载完成在windows环境下按照提示安装,如下图,
|
||||
|
||||

|
||||
|
||||
注意:安装完毕后需要将“Add path to environment variable”勾选上。
|
||||
|
||||
由于windows环境下没有make环境,所以我们还需要安装一个make工具,Make 工具用于 build 工程, 使用 Makefile 编译工程。 版本:gnu-mcu-eclipse-build-tools-2.9-20170629-1013-win64-setup.exe
|
||||
|
||||
下载地址:[https://github.com/gnu-mcu-eclipse/windows-build-tools/releases/tag/v2.9-20170629-1013]()
|
||||
直接双击安装,完成后将安装后的make命令目录添加到windows的path环境变量中,默认安装目录为:
|
||||
C:\Program Files\GNU MCU Eclipse\Build Tools\2.9-20170629-1013\bin\bin,在环境变量中添加安装目录即可。
|
||||
|
||||
### 3. 准备芯片对应的裸机工程
|
||||
|
||||
移植TencentOS tiny基础内核需要您提前准备一个芯片对应的裸机工程,裸机工程包含基本的芯片启动文件、基础配置(时钟、主频等)、以及串口、基本GPIO驱动用于RTOS测试。
|
||||
|
||||
本教程使用ST官方的STM32CubeMX软件来自动化生成GCC裸机工程,STM32CubeMX的下载地址为:
|
||||
|
||||
[https://www.st.com/content/st_com/zh/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-configurators-and-code-generators/stm32cubemx.html]()
|
||||
|
||||
安装STM32CubeMx还需要事先安装好JDK环境,您可以在互联网上查找如何安装和配置JDK环境,此处不再赘述。
|
||||
|
||||
CubeMX安装完成后,我们就可以使用CubeMX来给NUCLEO-L037RZ开发板生成裸机工程了,如果您的芯片不是STM32,而是其他厂商的ARM Cortex M系列,您可以根据产商的指导准备裸机工程,后续的内核移植步骤是一致的。
|
||||
|
||||
#### 3.1 首先启动STM32CubeMX,新建工程
|
||||
|
||||

|
||||
|
||||
#### 3.2 选择MCU型号
|
||||
|
||||

|
||||
|
||||
如上图所示:通过MCU筛选来找到自己开发板对应的芯片型号,双击后弹出工程配置界面,如下图:
|
||||
|
||||

|
||||
#### 3.3 Pin设置界面配置时钟源
|
||||
|
||||

|
||||
|
||||
#### 3.4 Pin设置界面配置串口
|
||||
|
||||

|
||||
|
||||
#### 3.5 Pin设置界面配置GPIO
|
||||

|
||||
|
||||
#### 3.6 配置总线时钟
|
||||
|
||||

|
||||
|
||||
#### 3.7 工程生成参数配置
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
#### 3.9 生成工程
|
||||
|
||||

|
||||
|
||||
#### 3.10 编译GCC下的裸机工程
|
||||
|
||||
点击生成代码后,生成的裸机工程效果如下,在makefile同级目录下打开windows的cmd命令窗口(目录下按shift+鼠标右键,点击在此处打开命令窗口),输入make命令,就可以编译gcc裸机工程了,编译完成后,可以看到生成了elf和bin文件,通过工具就可以下载bin文件到开发板运行了。
|
||||
|
||||

|
||||
|
||||
### 4. 准备TencentOS tiny的源码
|
||||
|
||||
TencentOS tiny的源码已经在内网开源,git下载地址为:[http://git.code.oa.com/TencentOS_tiny/TencentOS_tiny.git]()
|
||||
|
||||
|一级目录 | 二级目录 | 说明 |
|
||||
|---------|---------|---------|
|
||||
| arch | arm | TencentOS tiny适配的IP核架构(含M核中断、调度、tick相关代码) |
|
||||
| board | NUCLEO_L073RZ | 移植目标芯片的工程文件 |
|
||||
| kernel | core | TencentOS tiny内核源码|
|
||||
| | pm | TencentOS tiny低功耗模块源码 |
|
||||
| osal | cmsis_os | TencentOS tiny提供的cmsis os 适配 |
|
||||
|
||||
由于本教程只介绍TencentOS tiny的内核移植,所以这里只需要用到 arch、board、kernel、osal四个目录下的源码。
|
||||
|
||||
## 二、内核移植
|
||||
|
||||
### 1. 代码目录规划
|
||||
|
||||

|
||||
|
||||
如图所示,新建TencentOS_tiny主目录,并在主目录下添加四个子目录,其中arch、kernel、osal从代码仓直接拷贝过来即可,而board目录下则放入我们前面生成的裸机工程代码,我们移植的开发板取名叫NUCLEO_L073RZ,裸机代码全部拷贝到下面即可,如下图所示:
|
||||
|
||||

|
||||
|
||||
接下来进入TencentOS_tiny\board\NUCLEO_L073RZ目录,打开Makefile,我们开始添加TencentOS tiny的内核代码。
|
||||
|
||||
### 2. 添加arch平台代码
|
||||
|
||||
添加代码前先在makefile文件下定义一个TOP_DIR路径,作为TencentOS tiny的顶级目录,当前makefile文件在二级子目录下面,我们可以定义相对顶级目录为TOP_DIR = ../../,如下图所示:
|
||||
|
||||

|
||||
|
||||
然后在makefile中 C_SOURCES后面添加 arch C代码,在ASM_SOURCES后面添加一个ASM_SOURCES_S 分组,加入调度汇编代码.S.
|
||||
|
||||

|
||||
|
||||
tos_cpu.c是TencentOS tiny 的CPU适配文件,包括堆栈初始化,中断适配等,如果您的芯片是ARM Cortex M核,该文件可以不做改动,M0、M3 、M4、M7是通用的,其他IP核需要重新适配;
|
||||
|
||||
port_s.S 文件是TencentOS tiny的任务调度汇编代码,主要做弹栈压栈等处理的,port_c.c适配systick等,这两个文件 每个IP核和编译器都是不一样的,如果您的芯片是ARM Cortex M核,我们都已经适配好,比如现在我们移植的芯片是STM32L073RZ,是ARM Cortex M0+核,使用的编译器是GCC,所以我们选择arch\arm\arm-v7m\cortex-m0+\gcc下的适配代码,如果你的开发板是STM32F429IG,M4核,编译器是keil,则可以选择arch\arm\arm-v7m\cortex-m4\armcc目录下的适配文件。
|
||||
|
||||
### 3. 添加内核源码
|
||||
|
||||
内核源码kerne目录下包含core和pm两个目录,其中core下为基础内核,pm是内核中的低功耗组件;基础移植的时候可以不添加pm目录下的代码,如下图所示,添加基本内核源码:
|
||||
|
||||

|
||||
|
||||
### 4. 添加cmsis os源码
|
||||
|
||||
cmsis os是TencentOS tiny为了兼容cmsis标准而适配的OS抽象层,可以简化大家将业务从其他RTOS迁移到TencentOS tiny的工作量。
|
||||
|
||||

|
||||
|
||||
### 5. 添加TencentOS tiny头文件目录
|
||||
|
||||
添加头文件目录前,我们在要移植的工程目录下新增一个 TOS_CONFIG文件夹,用于存放TencentOS tiny的配置头文件,也就是接下来要新建的tos_config.h文件;
|
||||
|
||||
TencentOS tiny所有要添加的头文件目录如下:
|
||||
|
||||

|
||||
|
||||
### 6. 修改makefile编译规则
|
||||
|
||||
由于我们TencentOS tiny的调度汇编port_s.S文件用的后缀是.S 而不是.s,所以我们要在makefile中添加编译.S的规则,如下图所示:
|
||||
|
||||

|
||||
|
||||
### 7. 新建TencentOS tiny系统配置文件 tos_config.h
|
||||
|
||||
```
|
||||
#ifndef TOS_CONFIG_H
|
||||
#define TOS_CONFIG_H
|
||||
|
||||
#include "stm32l0xx.h" // 目标芯片头文件,用户需要根据情况更改
|
||||
|
||||
#define TOS_CFG_TASK_PRIO_MAX 10u // 配置TencentOS tiny默认支持的最大优先级数量
|
||||
|
||||
#define TOS_CFG_ROUND_ROBIN_EN 1u // 配置TencentOS tiny的内核是否开启时间片轮转
|
||||
|
||||
#define TOS_CFG_OBJECT_VERIFY 0u // 配置TencentOS tiny是否校验指针合法
|
||||
|
||||
#define TOS_CFG_EVENT_EN 1u // TencentOS tiny 事件模块功能宏
|
||||
|
||||
#define TOS_CFG_MMBLK_EN 1u //配置TencentOS tiny是否开启内存块管理模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_EN 1u // 配置TencentOS tiny是否开启动态内存模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小
|
||||
|
||||
#define TOS_CFG_MUTEX_EN 1u // 配置TencentOS tiny是否开启互斥锁模块
|
||||
|
||||
#define TOS_CFG_QUEUE_EN 1u // 配置TencentOS tiny是否开启队列模块
|
||||
|
||||
#define TOS_CFG_TIMER_EN 1u // 配置TencentOS tiny是否开启软件定时器模块
|
||||
|
||||
#define TOS_CFG_SEM_EN 1u // 配置TencentOS tiny是否开启信号量模块
|
||||
|
||||
#define TOS_CFG_TICKLESS_EN 0u // 配置Tickless 低功耗模块开关
|
||||
|
||||
#if (TOS_CFG_QUEUE_EN > 0u)
|
||||
#define TOS_CFG_MSG_EN 1u
|
||||
#else
|
||||
#define TOS_CFG_MSG_EN 0u
|
||||
#endif
|
||||
|
||||
#define TOS_CFG_MSG_POOL_SIZE 10u // 配置TencentOS tiny消息队列大小
|
||||
|
||||
#define TOS_CFG_IDLE_TASK_STK_SIZE 128u // 配置TencentOS tiny空闲任务栈大小
|
||||
|
||||
#define TOS_CFG_CPU_TICK_PER_SECOND 1000u // 配置TencentOS tiny的tick频率
|
||||
|
||||
#define TOS_CFG_CPU_CLOCK (SystemCoreClock) // 配置TencentOS tiny CPU频率
|
||||
|
||||
#define TOS_CFG_TIMER_AS_PROC 1u // 配置是否将TIMER配置成函数模式
|
||||
|
||||
#endif
|
||||
```
|
||||
按照上面的模板配置好TencentOS tiny的各项功能后,将tos_config.h 文件放入要移植的board工程目录下即可,例如本教程是放到board\NUCLEO_L073RZ\TOS_CONFIG目录下。
|
||||
|
||||
这样,TencentOS tiny的源码就全部添加完毕了。
|
||||
|
||||
## 三、创建TencentOS tiny任务,测试移植结果
|
||||
|
||||
### 1. 修改部分代码
|
||||
#### 修改stm32l0xx_it.c的中断函数
|
||||
在stm32l0xx_it.c(board\NUCLEO_L073RZ\Src目录下)文件中包含 tos.h 头文件
|
||||

|
||||
|
||||
在stm32l0xx_it.c文件中的PendSV_Handler函数前添加___weak关键字,因为该函数在TencentOS tiny的调度汇编中已经重新实现;同时在SysTick_Handler函数中添加TencentOS tiny的调度处理函数,如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
### 2. 编写TencentOS tiny 测试任务
|
||||
|
||||
#### 在mian.c 中添加TencentOS tiny 头文件,编写任务函数
|
||||
|
||||
```
|
||||
#include "cmsis_os.h"
|
||||
//task1
|
||||
#define TASK1_STK_SIZE 512
|
||||
void task1(void *pdata);
|
||||
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
|
||||
|
||||
//task2
|
||||
#define TASK2_STK_SIZE 512
|
||||
void task2(void *pdata);
|
||||
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
|
||||
|
||||
void task1(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello world!\r\n###This is task1 ,count is %d \r\n", count++);
|
||||
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
|
||||
osDelay(2000);
|
||||
}
|
||||
}
|
||||
void task2(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello TencentOS !\r\n***This is task2 ,count is %d \r\n", count++);
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int _write(int fd, char *ptr, int len)
|
||||
{
|
||||
(void)HAL_UART_Transmit(&huart2, (uint8_t *)ptr, len, 0xFFFF);
|
||||
return len;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
如图:
|
||||

|
||||
|
||||
继续在main.c 的mian函数中硬件外设初始化代码后添加TencentOS tiny的初始化代码:
|
||||
|
||||
```
|
||||
osKernelInitialize(); //TOS Tiny kernel initialize
|
||||
osThreadCreate(osThread(task1), NULL);// Create task1
|
||||
osThreadCreate(osThread(task2), NULL);// Create task2
|
||||
osKernelStart();//Start TOS Tiny
|
||||
```
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
### 3. 编译下载测试TencentOS tiny移植结果
|
||||
|
||||
完成代码编辑后回到TencentOS_tiny\board\NUCLEO_L073RZ目录下,找到makefile文件,该目录下打开cmd命令窗口,输入make命令进行编译,得到bin文件,然后将bin文件通过下载工具下载到开发板即可完成TencentOS tiny的测试,如下图所示,可以看到串口交替打印信息,表示两个任务正在进行调度,切换运行:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
278
doc/TencentOS-tiny-porting-iar.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# TencentOS tiny 内核移植参考指南(IAR版)
|
||||
|
||||
## 一、移植前的准备
|
||||
|
||||
### 1. 准备目标硬件(开发板/芯片/模组)
|
||||
|
||||
TencentOS tiny目前主要支持ARM Cortex M核芯片的移植,比如STM32 基于Cortex M核全系列、NXP 基于Cortex M核全系列等。本教程将使用STM32官方Demo开发板 NUCLEO-L073RZ进行示例移植,其他 ARM Cortex M系列开发板和芯片移植方法类似。
|
||||
|
||||
调试ARM Cortex M核还需要仿真器, NUCLEO-L073RZ自带ST-Link调试器,如果您的开发板或者芯片模组没有板载仿真器,就需要连接外置的仿真器,如J-Link、U-Link之类的。
|
||||
|
||||
|
||||
### 2. 准备编译器环境
|
||||
|
||||
本移植指南针对的是IAR编译器,所以我们移植内核前需要先安装IAR编译器,IAR最新版本8.40,下载地址为:[https://www.iar.com/iar-embedded-workbench/#!?currentTab=free-trials]()
|
||||
下载完成在windows环境下按照提示安装即可,安装完成后可以免费试用30天,30后需要自行购买软件License。
|
||||
|
||||
### 3.准备芯片对应的裸机工程
|
||||
|
||||
移植TencentOS tiny基础内核需要您提前准备一个芯片对应的裸机工程,裸机工程包含基本的芯片启动文件、基础配置(时钟、主频等)、以及串口、基本GPIO驱动用于RTOS测试。
|
||||
|
||||
本教程使用ST官方的STM32CubeMX软件来自动化生成IAR裸机工程,STM32CubeMX的下载地址为:
|
||||
|
||||
[ https://www.st.com/content/st_com/zh/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-configurators-and-code-generators/stm32cubemx.html]()
|
||||
|
||||
安装STM32CubeMx还需要事先安装好JDK环境,您可以在互联网上查找如何安装和配置JDK环境,此处不再赘述。
|
||||
|
||||
CubeMX安装完成后,我们就可以使用CubeMX来给NUCLEO-L037RZ开发板生成裸机工程了,如果您的芯片不是STM32,而是其他厂商的ARM Cortex M系列,您可以根据产商的指导准备裸机工程,后续的内核移植步骤是一致的。
|
||||
|
||||
#### 3.1 首先启动STM32CubeMX,新建工程
|
||||
|
||||

|
||||
|
||||
#### 3.2 选择MCU型号
|
||||
|
||||

|
||||
|
||||
如上图所示:通过MCU筛选来找到自己开发板对应的芯片型号,双击后弹出工程配置界面,如下图:
|
||||
|
||||

|
||||
#### 3.3 Pin设置界面配置时钟源
|
||||
|
||||

|
||||
|
||||
#### 3.4 Pin设置界面配置串口
|
||||
|
||||

|
||||
|
||||
#### 3.5 Pin设置界面配置GPIO
|
||||

|
||||
|
||||
#### 3.6 配置总线时钟
|
||||
|
||||

|
||||
|
||||
#### 3.7 工程生成参数配置
|
||||
|
||||

|
||||
|
||||
#### 3.8 代码生成方式配置
|
||||
|
||||

|
||||
|
||||
#### 3.9 生成工程
|
||||
|
||||

|
||||
|
||||
#### 3.10 IAR下的裸机工程
|
||||
|
||||
点击生成代码后,生成的裸机工程效果如下:
|
||||
|
||||

|
||||
|
||||
这样NUCLEO-L073RZ裸机工程生成完成,该工程可直接编译并烧写在板子上运行。
|
||||
|
||||
### 4. 准备TencentOS tiny的源码
|
||||
TencentOS tiny的源码已经在内网开源,git下载地址为:[http://git.code.oa.com/TencentOS_tiny/TencentOS_tiny.git]()
|
||||
|
||||
|
||||
|
||||
|一级目录 | 二级目录 | 说明 |
|
||||
|---------|---------|---------|
|
||||
| arch | arm | TencentOS tiny适配的IP核架构(含M核中断、调度、tick相关代码) |
|
||||
| board | NUCLEO_L073RZ | 移植目标芯片的工程文件 |
|
||||
| kernel | core | TencentOS tiny内核源码|
|
||||
| | pm | TencentOS tiny低功耗模块源码 |
|
||||
| osal | cmsis_os | TencentOS tiny提供的cmsis os 适配 |
|
||||
|
||||
由于本教程只介绍TencentOS tiny的内核移植,所以这里只需要用到 arch、board、kernel、osal四个目录下的源码。
|
||||
|
||||
## 二、内核移植
|
||||
|
||||
### 1. 代码目录规划
|
||||
|
||||

|
||||
|
||||
如图所示,新建TencentOS_tiny主目录,并在主目录下添加四个子目录,其中arch、kernel、osal从代码仓直接拷贝过来即可,而board目录下则放入我们前面生成的裸机工程代码,我们移植的开发板取名叫NUCLEO_L073RZ,裸机代码全部拷贝到下面即可,如下图所示:
|
||||
|
||||

|
||||
|
||||
接下来进入TencentOS_tiny\board\NUCLEO_L073RZ\EWARM目录,打开IAR工程,我们开始添加TencentOS tiny的内核代码。
|
||||
|
||||
### 2. 添加arch平台代码
|
||||
|
||||

|
||||
|
||||
我们在IAR的代码导航页面添加 tos/arch分组,用来添加TencentOS tiny的arch源码。
|
||||
|
||||
tos_cpu.c是TencentOS tiny 的CPU适配文件,包括堆栈初始化,中断适配等,如果您的芯片是ARM Cortex M核,该文件可以不做改动,M0、M3 、M4、M7是通用的,其他IP核需要重新适配;
|
||||
|
||||
port_s.S 文件是TencentOS tiny的任务调度汇编代码,主要做弹栈压栈等处理的,port_c.c适配systick等,这两个文件 每个IP核和编译器都是不一样的,如果您的芯片是ARM Cortex M核,我们都已经适配好,比如现在我们移植的芯片是STM32L073RZ,是ARM Cortex M0+核,使用的编译器是IAR,所以我们选择arch\arm\arm-v7m\cortex-m0+\iccarm下的适配代码,如果你的开发板是STM32F429IG,M4核,编译器是GCC,则可以选择arch\arm\arm-v7m\cortex-m4\gcc目录下的适配文件。
|
||||
|
||||
### 3. 添加内核源码
|
||||
|
||||
内核源码kerne目录下包含core和pm两个目录,其中core下为基础内核,pm是内核中的低功耗组件;基础移植的时候可以不添加pm目录下的代码,如下图所示,我们在IAR代码导航页添加tos/kernel分组,用来添加基础内核源码:
|
||||
|
||||

|
||||
|
||||
### 4. 添加cmsis os源码
|
||||
|
||||
cmsis os是TencentOS tiny为了兼容cmsis标准而适配的OS抽象层,可以简化大家将业务从其他RTOS迁移到TencentOS tiny的工作量,我们在IAR的代码导航页面添加 tos/cmsis-os分组,来添加cmsis-os的源代码。
|
||||
|
||||

|
||||
|
||||
### 5. 添加TencentOS tiny头文件目录
|
||||
|
||||
添加头文件目录前,我们在要移植的工程目录下新增一个 TOS_CONFIG文件夹,用于存放TencentOS tiny的配置头文件,也就是接下来要新建的tos_config.h文件;
|
||||
|
||||
TencentOS tiny所有要添加的头文件目录如下:
|
||||
|
||||

|
||||
|
||||
### 6. 新建TencentOS tiny系统配置文件 tos_config.h
|
||||
|
||||
```
|
||||
#ifndef TOS_CONFIG_H
|
||||
#define TOS_CONFIG_H
|
||||
|
||||
#include "stm32l0xx.h" // 目标芯片头文件,用户需要根据情况更改
|
||||
|
||||
#define TOS_CFG_TASK_PRIO_MAX 10u // 配置TencentOS tiny默认支持的最大优先级数量
|
||||
|
||||
#define TOS_CFG_ROUND_ROBIN_EN 1u // 配置TencentOS tiny的内核是否开启时间片轮转
|
||||
|
||||
#define TOS_CFG_OBJECT_VERIFY 0u // 配置TencentOS tiny是否校验指针合法
|
||||
|
||||
#define TOS_CFG_EVENT_EN 1u // TencentOS tiny 事件模块功能宏
|
||||
|
||||
#define TOS_CFG_MMBLK_EN 1u //配置TencentOS tiny是否开启内存块管理模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_EN 1u // 配置TencentOS tiny是否开启动态内存模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小
|
||||
|
||||
#define TOS_CFG_MUTEX_EN 1u // 配置TencentOS tiny是否开启互斥锁模块
|
||||
|
||||
#define TOS_CFG_QUEUE_EN 1u // 配置TencentOS tiny是否开启队列模块
|
||||
|
||||
#define TOS_CFG_TIMER_EN 1u // 配置TencentOS tiny是否开启软件定时器模块
|
||||
|
||||
#define TOS_CFG_SEM_EN 1u // 配置TencentOS tiny是否开启信号量模块
|
||||
|
||||
#define TOS_CFG_TICKLESS_EN 0u // 配置Tickless 低功耗模块开关
|
||||
|
||||
#if (TOS_CFG_QUEUE_EN > 0u)
|
||||
#define TOS_CFG_MSG_EN 1u
|
||||
#else
|
||||
#define TOS_CFG_MSG_EN 0u
|
||||
#endif
|
||||
|
||||
#define TOS_CFG_MSG_POOL_SIZE 10u // 配置TencentOS tiny消息队列大小
|
||||
|
||||
#define TOS_CFG_IDLE_TASK_STK_SIZE 128u // 配置TencentOS tiny空闲任务栈大小
|
||||
|
||||
#define TOS_CFG_CPU_TICK_PER_SECOND 1000u // 配置TencentOS tiny的tick频率
|
||||
|
||||
#define TOS_CFG_CPU_CLOCK (SystemCoreClock) // 配置TencentOS tiny CPU频率
|
||||
|
||||
#define TOS_CFG_TIMER_AS_PROC 1u // 配置是否将TIMER配置成函数模式
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
按照上面的模板配置好TencentOS tiny的各项功能后,将tos_config.h 文件放入要移植的board工程目录下即可,例如本教程是放到board\NUCLEO_L073RZ\TOS_CONFIG目录下。
|
||||
|
||||
这样,TencentOS tiny的源码就全部添加完毕了。
|
||||
|
||||
### 三、创建TencentOS tiny任务,测试移植结果
|
||||
|
||||
### 1. 修改部分代码
|
||||
#### 修改stm32l0xx_it.c的中断函数,在stm32l0xx_it.c文件中包含 tos.h 头文件
|
||||

|
||||
|
||||
在stm32l0xx_it.c文件中的PendSV_Handler函数前添加___weak关键字,因为该函数在TencentOS tiny的调度汇编中已经重新实现;同时在SysTick_Handler函数中添加TencentOS tiny的调度处理函数,如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
### 2. 编写TencentOS tiny 测试任务
|
||||
|
||||
#### 在mian.c 中添加TencentOS tiny 头文件,编写任务函数
|
||||
```
|
||||
#include "cmsis_os.h"
|
||||
//task1
|
||||
#define TASK1_STK_SIZE 256
|
||||
void task1(void *pdata);
|
||||
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
|
||||
|
||||
//task2
|
||||
#define TASK2_STK_SIZE 256
|
||||
void task2(void *pdata);
|
||||
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
|
||||
|
||||
void task1(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello world!\r\n###This is task1 ,count is %d \r\n", count++);
|
||||
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
|
||||
osDelay(2000);
|
||||
}
|
||||
}
|
||||
void task2(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello TencentOS !\r\n***This is task2 ,count is %d \r\n", count++);
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int fputc(int ch, FILE *f)
|
||||
{
|
||||
if (ch == '\n')
|
||||
{
|
||||
HAL_UART_Transmit(&huart2, (void *)"\r", 1,30000);
|
||||
}
|
||||
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
|
||||
return ch;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
继续在main.c 的mian函数中硬件外设初始化代码后添加TencentOS tiny的初始化代码:
|
||||
|
||||
```
|
||||
osKernelInitialize(); //TOS Tiny kernel initialize
|
||||
osThreadCreate(osThread(task1), NULL);// Create task1
|
||||
osThreadCreate(osThread(task2), NULL);// Create task2
|
||||
osKernelStart();//Start TOS Tiny
|
||||
```
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
### 3. 编译下载测试TencentOS tiny移植结果
|
||||
|
||||

|
||||
|
||||
按照上图指示,进行编译下载到开发板即可完成TencentOS tiny的测试,如下图所示,可以看到串口交替打印信息,表示两个任务正在进行调度,切换运行:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
278
doc/TencentOS-tiny-porting-keil.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# TencentOS tiny 内核移植参考指南(Keil版)
|
||||
|
||||
## 一、移植前的准备
|
||||
### 1. 准备目标硬件(开发板/芯片/模组)
|
||||
|
||||
TencentOS tiny目前主要支持ARM Cortex M核芯片的移植,比如STM32 基于Cortex M核全系列、NXP 基于Cortex M核全系列等。本教程将使用STM32官方Demo开发板 NUCLEO-L073RZ进行示例移植,其他 ARM Cortex M系列开发板和芯片移植方法类似。
|
||||
|
||||
调试ARM Cortex M核还需要仿真器, NUCLEO-L073RZ自带ST-Link调试器,如果您的开发板或者芯片模组没有板载仿真器,就需要连接外置的仿真器,如J-Link、U-Link之类的。
|
||||
|
||||
|
||||
|
||||
### 2.准备编译器环境
|
||||
|
||||
本移植指南针对的是Keil编译器,所以我们移植内核前需要先安装Keil编译器,能编译ARM Cortex M核的Keil编译器现在也叫MDK,最新版本5.28a,下载地址为:[https://www.keil.com/demo/eval/arm.htm]()
|
||||
填写注册信息即可下载,下载完成在windows环境下按照提示安装即可,安装完成后需要自行购买软件License,避免32K Flash下载限制。
|
||||
由于新版本的MDK编译器和芯片支持包是分离的,所以MDK(Keil)安装完成后,还需要安装对应芯片的器件支持包(PACK包),比如本教程示例的NUCLEO-L037RZ开发板的芯片是STM32L073RZ,就需要安装*Keil.STM32L0xx_DFP.2.0.1.pack*系列器件支持包,MDK所有支持芯片的PACK包下载地址为:[http://www.keil.com/dd2/Pack/#/eula-container]() ,您只需要根据您的芯片型号下载对应的PACK包即可,当然您也可以在MDK集成开发环境中在线下载安装。
|
||||
|
||||
### 3. 准备芯片对应的裸机工程
|
||||
|
||||
移植TencentOS tiny基础内核需要您提前准备一个芯片对应的裸机工程,裸机工程包含基本的芯片启动文件、基础配置(时钟、主频等)、以及串口、基本GPIO驱动用于RTOS测试。
|
||||
|
||||
本教程使用ST官方的STM32CubeMX软件来自动化生成MDK裸机工程,STM32CubeMX的下载地址为:
|
||||
|
||||
[ https://www.st.com/content/st_com/zh/products/development-tools/software-development-tools/stm32-software-development-tools/stm32-configurators-and-code-generators/stm32cubemx.html]()
|
||||
|
||||
安装STM32CubeMx还需要事先安装好JDK环境,您可以在互联网上查找如何安装和配置JDK环境,此处不再赘述。
|
||||
|
||||
CubeMX安装完成后,我们就可以使用CubeMX来给NUCLEO-L037RZ开发板生成裸机工程了,如果您的芯片不是STM32,而是其他厂商的ARM Cortex M系列,您可以根据产商的指导准备裸机工程,后续的内核移植步骤是一致的。
|
||||
|
||||
#### 3.1 首先启动STM32CubeMX,新建工程
|
||||
|
||||

|
||||
|
||||
#### 3.2 选择MCU型号
|
||||
|
||||

|
||||
|
||||
如上图所示:通过MCU筛选来找到自己开发板对应的芯片型号,双击后弹出工程配置界面,如下图:
|
||||
|
||||

|
||||
#### 3.3 Pin设置界面配置时钟源
|
||||
|
||||

|
||||
|
||||
#### 3.4 Pin设置界面配置串口
|
||||
|
||||

|
||||
|
||||
#### 3.5 Pin设置界面配置GPIO
|
||||

|
||||
|
||||
#### 3.6 配置总线时钟
|
||||
|
||||

|
||||
|
||||
#### 3.7 工程生成参数配置
|
||||
|
||||

|
||||
|
||||
#### 3.8 代码生成方式配置
|
||||
|
||||

|
||||
|
||||
#### 3.9 生成工程
|
||||
|
||||

|
||||
|
||||
#### 3.10 keil下的裸机工程
|
||||
|
||||
点击生成代码后,生成的裸机工程效果如下:
|
||||

|
||||
|
||||
这样NUCLEO-L073RZ裸机工程生成完成,该工程可直接编译并烧写在板子上运行。
|
||||
|
||||
### 4. 准备TencentOS tiny的源码
|
||||
TencentOS tiny的源码已经在内网开源,git下载地址为:[http://git.code.oa.com/TencentOS_tiny/TencentOS_tiny.git]()
|
||||
|
||||
|
||||
|
||||
|一级目录 | 二级目录 | 说明 |
|
||||
|---------|---------|---------|
|
||||
| arch | arm | TencentOS tiny适配的IP核架构(含M核中断、调度、tick相关代码) |
|
||||
| board | NUCLEO_L073RZ | 移植目标芯片的工程文件 |
|
||||
| kernel | core | TencentOS tiny内核源码|
|
||||
| | pm | TencentOS tiny低功耗模块源码 |
|
||||
| osal | cmsis_os | TencentOS tiny提供的cmsis os 适配 |
|
||||
|
||||
由于本教程只介绍TencentOS tiny的内核移植,所以这里只需要用到 arch、board、kernel、osal四个目录下的源码。
|
||||
|
||||
## 二、内核移植
|
||||
|
||||
### 1. 代码目录规划
|
||||
|
||||

|
||||
|
||||
如图所示,新建TencentOS_tiny主目录,并在主目录下添加四个子目录,其中arch、kernel、osal从代码仓直接拷贝过来即可,而board目录下则放入我们前面生成的裸机工程代码,我们移植的开发板取名叫NUCLEO_L073RZ,裸机代码全部拷贝到下面即可,如下图所示:
|
||||
|
||||

|
||||
|
||||
接下来进入TencentOS_tiny\board\NUCLEO_L073RZ\MDK-ARM目录,打开keil工程,我们开始添加TencentOS tiny的内核代码。
|
||||
|
||||
### 2. 添加arch平台代码
|
||||
|
||||

|
||||
|
||||
tos_cpu.c是TencentOS tiny 的CPU适配文件,包括堆栈初始化,中断适配等,如果您的芯片是ARM Cortex M核,该文件可以不做改动,M0、M3 、M4、M7是通用的,其他IP核需要重新适配;
|
||||
|
||||
port_s.S 文件是TencentOS tiny的任务调度汇编代码,主要做弹栈压栈等处理的,port_c.c适配systick等,这两个文件 每个IP核和编译器都是不一样的,如果您的芯片是ARM Cortex M核,我们都已经适配好,比如现在我们移植的芯片是STM32L073RZ,是ARM Cortex M0+核,使用的编译器是KEIL,所以我们选择arch\arm\arm-v7m\cortex-m0+\armcc下的适配代码,如果你的开发板是STM32F429IG,M4核,编译器是GCC,则可以选择arch\arm\arm-v7m\cortex-m4\gcc目录下的适配文件。
|
||||
|
||||
### 3. 添加内核源码
|
||||
|
||||
内核源码kerne目录下包含core和pm两个目录,其中core下为基础内核,pm是内核中的低功耗组件;基础移植的时候可以不添加pm目录下的代码,如下图所示,添加基本内核源码:
|
||||
|
||||

|
||||
|
||||
### 4. 添加cmsis os源码
|
||||
|
||||
cmsis os是TencentOS tiny为了兼容cmsis标准而适配的OS抽象层,可以简化大家将业务从其他RTOS迁移到TencentOS tiny的工作量。
|
||||
|
||||

|
||||
|
||||
### 5. 添加TencentOS tiny头文件目录
|
||||
|
||||
添加头文件目录前,我们在要移植的工程目录下新增一个 TOS_CONFIG文件夹,用于存放TencentOS tiny的配置头文件,也就是接下来要新建的tos_config.h文件;
|
||||
|
||||
TencentOS tiny所有要添加的头文件目录如下:
|
||||
|
||||

|
||||
|
||||
|
||||
### 6. 新建TencentOS tiny系统配置文件 tos_config.h
|
||||
|
||||
```
|
||||
#ifndef TOS_CONFIG_H
|
||||
#define TOS_CONFIG_H
|
||||
|
||||
#include "stm32l0xx.h" // 目标芯片头文件,用户需要根据情况更改
|
||||
|
||||
#define TOS_CFG_TASK_PRIO_MAX 10u // 配置TencentOS tiny默认支持的最大优先级数量
|
||||
|
||||
#define TOS_CFG_ROUND_ROBIN_EN 1u // 配置TencentOS tiny的内核是否开启时间片轮转
|
||||
|
||||
#define TOS_CFG_OBJECT_VERIFY 0u // 配置TencentOS tiny是否校验指针合法
|
||||
|
||||
#define TOS_CFG_EVENT_EN 1u // TencentOS tiny 事件模块功能宏
|
||||
|
||||
#define TOS_CFG_MMBLK_EN 1u //配置TencentOS tiny是否开启内存块管理模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_EN 1u // 配置TencentOS tiny是否开启动态内存模块
|
||||
|
||||
#define TOS_CFG_MMHEAP_POOL_SIZE 0x100 // 配置TencentOS tiny动态内存池大小
|
||||
|
||||
#define TOS_CFG_MUTEX_EN 1u // 配置TencentOS tiny是否开启互斥锁模块
|
||||
|
||||
#define TOS_CFG_QUEUE_EN 1u // 配置TencentOS tiny是否开启队列模块
|
||||
|
||||
#define TOS_CFG_TIMER_EN 1u // 配置TencentOS tiny是否开启软件定时器模块
|
||||
|
||||
#define TOS_CFG_SEM_EN 1u // 配置TencentOS tiny是否开启信号量模块
|
||||
|
||||
#define TOS_CFG_TICKLESS_EN 0u // 配置Tickless 低功耗模块开关
|
||||
|
||||
#if (TOS_CFG_QUEUE_EN > 0u)
|
||||
#define TOS_CFG_MSG_EN 1u
|
||||
#else
|
||||
#define TOS_CFG_MSG_EN 0u
|
||||
#endif
|
||||
|
||||
#define TOS_CFG_MSG_POOL_SIZE 10u // 配置TencentOS tiny消息队列大小
|
||||
|
||||
#define TOS_CFG_IDLE_TASK_STK_SIZE 128u // 配置TencentOS tiny空闲任务栈大小
|
||||
|
||||
#define TOS_CFG_CPU_TICK_PER_SECOND 1000u // 配置TencentOS tiny的tick频率
|
||||
|
||||
#define TOS_CFG_CPU_CLOCK (SystemCoreClock) // 配置TencentOS tiny CPU频率
|
||||
|
||||
#define TOS_CFG_TIMER_AS_PROC 1u // 配置是否将TIMER配置成函数模式
|
||||
|
||||
#endif
|
||||
```
|
||||
按照上面的模板配置好TencentOS tiny的各项功能后,将tos_config.h 文件放入要移植的board工程目录下即可,例如本教程是放到board\NUCLEO_L073RZ\TOS_CONFIG目录下。
|
||||
|
||||
这样,TencentOS tiny的源码就全部添加完毕了。
|
||||
|
||||
## 三、创建TencentOS tiny任务,测试移植结果
|
||||
|
||||
### 1. 修改部分代码
|
||||
#### 修改stm32l0xx_it.c的中断函数,在stm32l0xx_it.c文件中包含 tos.h 头文件
|
||||

|
||||
|
||||
在stm32l0xx_it.c文件中的PendSV_Handler函数前添加___weak关键字,因为该函数在TencentOS tiny的调度汇编中已经重新实现;同时在SysTick_Handler函数中添加TencentOS tiny的调度处理函数,如下图所示:
|
||||
|
||||

|
||||
|
||||
|
||||
### 2. 编写TencentOS tiny 测试任务
|
||||
|
||||
#### 在mian.c 中添加TencentOS tiny 头文件,编写任务函数
|
||||
|
||||
```
|
||||
#include "cmsis_os.h"
|
||||
//task1
|
||||
#define TASK1_STK_SIZE 256
|
||||
void task1(void *pdata);
|
||||
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
|
||||
|
||||
//task2
|
||||
#define TASK2_STK_SIZE 256
|
||||
void task2(void *pdata);
|
||||
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
|
||||
|
||||
void task1(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello world!\r\n###This is task1 ,count is %d \r\n", count++);
|
||||
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
|
||||
osDelay(2000);
|
||||
}
|
||||
}
|
||||
void task2(void *pdata)
|
||||
{
|
||||
int count = 1;
|
||||
while(1)
|
||||
{
|
||||
printf("\r\nHello TencentOS !\r\n***This is task2 ,count is %d \r\n", count++);
|
||||
osDelay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int fputc(int ch, FILE *f)
|
||||
{
|
||||
if (ch == '\n')
|
||||
{
|
||||
HAL_UART_Transmit(&huart2, (void *)"\r", 1,30000);
|
||||
}
|
||||
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
|
||||
return ch;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
继续在main.c 的mian函数中硬件外设初始化代码后添加TencentOS tiny的初始化代码:
|
||||
|
||||
```
|
||||
osKernelInitialize(); //TOS Tiny kernel initialize
|
||||
osThreadCreate(osThread(task1), NULL);// Create task1
|
||||
osThreadCreate(osThread(task2), NULL);// Create task2
|
||||
osKernelStart();//Start TOS Tiny
|
||||
```
|
||||
|
||||
|
||||
如图:
|
||||
|
||||

|
||||
|
||||
### 3. 编译下载测试TencentOS tiny移植结果
|
||||
|
||||

|
||||
|
||||
按照上图指示,进行编译下载到开发板即可完成TencentOS tiny的测试,如下图所示,可以看到串口交替打印信息,表示两个任务正在进行调度,切换运行:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
BIN
doc/TencentOS-tiny代码目录.xlsx
Normal file
BIN
doc/TencentOS-tiny定制开发板入门指南.docx
Normal file
BIN
doc/TencentOS-tiny定制开发板入门指南.pdf
Normal file
81
doc/code/2.2 task manager/main.c
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_PRIO4 512
|
||||
#define STK_SIZE_TASK_PRIO5 1024
|
||||
|
||||
|
||||
k_stack_t stack_task_prio4[STK_SIZE_TASK_PRIO4];
|
||||
k_stack_t stack_task_prio5[STK_SIZE_TASK_PRIO5];
|
||||
|
||||
k_task_t task_prio4;
|
||||
k_task_t task_prio5;
|
||||
|
||||
extern void entry_task_prio4(void *arg);
|
||||
extern void entry_task_prio5(void *arg);
|
||||
|
||||
uint32_t arg_task_prio4_array[3] = {
|
||||
1, 2, 3,
|
||||
};
|
||||
|
||||
char *arg_task_prio5_string = "arg for task_prio5";
|
||||
|
||||
void dump_uint32_array(uint32_t *array, size_t len)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
printf("%d\t", array[i]);
|
||||
}
|
||||
printf("\n\n");
|
||||
|
||||
}
|
||||
|
||||
void entry_task_prio4(void *arg)
|
||||
{
|
||||
uint32_t *array_from_main = (uint32_t *)arg;
|
||||
|
||||
printf("array from main:\n");
|
||||
dump_uint32_array(array_from_main, 3);
|
||||
|
||||
while (K_TRUE) {
|
||||
printf("task_prio4 body\n");
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_prio5(void *arg)
|
||||
{
|
||||
int i = 0;
|
||||
char *string_from_main = (char *)arg;
|
||||
printf("string from main:\n");
|
||||
printf("%s\n\n", string_from_main);
|
||||
|
||||
while (K_TRUE) {
|
||||
if (i == 2) {
|
||||
printf("i = %d\n", i);
|
||||
tos_task_suspend(&task_prio4);
|
||||
} else if (i == 4) {
|
||||
printf("i = %d\n", i);
|
||||
tos_task_resume(&task_prio4);
|
||||
} else if (i == 6) {
|
||||
printf("i = %d\n", i);
|
||||
tos_task_destroy(&task_prio4);
|
||||
}
|
||||
printf("task_prio5 body\n");
|
||||
tos_task_delay(1000);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
(void)tos_task_create(&task_prio4, "task_prio4", entry_task_prio4, (void *)(&arg_task_prio4_array[0]),
|
||||
4, stack_task_prio4, STK_SIZE_TASK_PRIO4, 0);
|
||||
(void)tos_task_create(&task_prio5, "task_prio5", entry_task_prio5, (void *)arg_task_prio5_string, 5,
|
||||
stack_task_prio5, STK_SIZE_TASK_PRIO5, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
84
doc/code/2.3.1 mutex/main.c
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_WRITER 512
|
||||
#define STK_SIZE_TASK_READER 512
|
||||
|
||||
k_stack_t stack_task_writer[STK_SIZE_TASK_WRITER];
|
||||
k_stack_t stack_task_reader[STK_SIZE_TASK_READER];
|
||||
|
||||
k_task_t task_writer;
|
||||
k_task_t task_reader;
|
||||
|
||||
extern void entry_task_writer(void *arg);
|
||||
extern void entry_task_reader(void *arg);
|
||||
|
||||
k_mutex_t critical_resource_locker;
|
||||
|
||||
static uint32_t critical_resource[3];
|
||||
|
||||
static void write_critical_resource(int salt)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
printf("writting critical resource:\n");
|
||||
for (i = 0; i < 3; ++i) {
|
||||
printf("%d\t", salt + i);
|
||||
critical_resource[i] = salt + i;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void entry_task_writer(void *arg)
|
||||
{
|
||||
size_t salt = 0;
|
||||
k_err_t err;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_mutex_pend(&critical_resource_locker);
|
||||
if (err == K_ERR_NONE) {
|
||||
write_critical_resource(salt);
|
||||
tos_mutex_post(&critical_resource_locker);
|
||||
}
|
||||
tos_task_delay(1000);
|
||||
++salt;
|
||||
}
|
||||
}
|
||||
|
||||
static void read_critical_resource(void)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
printf("reading critical resource:\n");
|
||||
for (i = 0; i < 3; ++i) {
|
||||
printf("%d\t", critical_resource[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void entry_task_reader(void *arg)
|
||||
{
|
||||
k_err_t err;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_mutex_pend(&critical_resource_locker);
|
||||
if (err == K_ERR_NONE) {
|
||||
read_critical_resource();
|
||||
tos_mutex_post(&critical_resource_locker);
|
||||
}
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
tos_mutex_create(&critical_resource_locker);
|
||||
(void)tos_task_create(&task_writer, "writer", entry_task_writer, NULL,
|
||||
4, stack_task_writer, STK_SIZE_TASK_WRITER, 0);
|
||||
(void)tos_task_create(&task_reader, "reader", entry_task_reader, NULL,
|
||||
4, stack_task_reader, STK_SIZE_TASK_READER, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
102
doc/code/2.3.2 semaphore/main.c
Normal file
@@ -0,0 +1,102 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_PRODUCER 512
|
||||
#define STK_SIZE_TASK_CONSUMER 512
|
||||
|
||||
k_stack_t stack_task_producer[STK_SIZE_TASK_PRODUCER];
|
||||
k_stack_t stack_task_consumer[STK_SIZE_TASK_CONSUMER];
|
||||
|
||||
k_task_t task_producer;
|
||||
k_task_t task_consumer;
|
||||
|
||||
extern void entry_task_producer(void *arg);
|
||||
extern void entry_task_consumer(void *arg);
|
||||
|
||||
k_mutex_t buffer_locker;
|
||||
k_sem_t full;
|
||||
k_sem_t empty;
|
||||
|
||||
#define RESOURCE_COUNT_MAX 3
|
||||
|
||||
struct resource_st {
|
||||
int cursor;
|
||||
uint32_t buffer[RESOURCE_COUNT_MAX];
|
||||
} resource = { 0, {0} };
|
||||
|
||||
static void produce_item(int salt)
|
||||
{
|
||||
printf("produce item:\n");
|
||||
|
||||
printf("%d", salt);
|
||||
resource.buffer[resource.cursor++] = salt;
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void entry_task_producer(void *arg)
|
||||
{
|
||||
size_t salt = 0;
|
||||
k_err_t err;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_sem_pend(&empty, TOS_TIME_FOREVER);
|
||||
if (err != K_ERR_NONE) {
|
||||
continue;
|
||||
}
|
||||
err = tos_mutex_pend(&buffer_locker);
|
||||
if (err != K_ERR_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
produce_item(salt);
|
||||
|
||||
tos_mutex_post(&buffer_locker);
|
||||
tos_sem_post(&full);
|
||||
tos_task_delay(1000);
|
||||
++salt;
|
||||
}
|
||||
}
|
||||
|
||||
static void consume_item(void)
|
||||
{
|
||||
printf("cosume item:\n");
|
||||
printf("%d\t", resource.buffer[--resource.cursor]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void entry_task_consumer(void *arg)
|
||||
{
|
||||
k_err_t err;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_sem_pend(&full, TOS_TIME_FOREVER);
|
||||
if (err != K_ERR_NONE) {
|
||||
continue;
|
||||
}
|
||||
tos_mutex_pend(&buffer_locker);
|
||||
if (err != K_ERR_NONE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
consume_item();
|
||||
|
||||
tos_mutex_post(&buffer_locker);
|
||||
tos_sem_post(&empty);
|
||||
tos_task_delay(2000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
tos_mutex_create(&buffer_locker);
|
||||
tos_sem_create(&full, 0);
|
||||
tos_sem_create(&empty, RESOURCE_COUNT_MAX);
|
||||
(void)tos_task_create(&task_producer, "producer", entry_task_producer, NULL,
|
||||
4, stack_task_producer, STK_SIZE_TASK_PRODUCER, 0);
|
||||
(void)tos_task_create(&task_consumer, "consumer", entry_task_consumer, NULL,
|
||||
4, stack_task_consumer, STK_SIZE_TASK_CONSUMER, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
118
doc/code/2.3.3 event/main.c
Normal file
@@ -0,0 +1,118 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_LISTENER 512
|
||||
#define STK_SIZE_TASK_TRIGGER 512
|
||||
|
||||
k_stack_t stack_task_listener1[STK_SIZE_TASK_LISTENER];
|
||||
k_stack_t stack_task_listener2[STK_SIZE_TASK_LISTENER];
|
||||
k_stack_t stack_task_trigger[STK_SIZE_TASK_TRIGGER];
|
||||
|
||||
k_task_t task_listener1;
|
||||
k_task_t task_listener2;
|
||||
k_task_t task_trigger;
|
||||
|
||||
extern void entry_task_listener1(void *arg);
|
||||
extern void entry_task_listener2(void *arg);
|
||||
extern void entry_task_trigger(void *arg);
|
||||
|
||||
const k_event_flag_t event_eeny = (k_event_flag_t)(1 << 0);
|
||||
const k_event_flag_t event_meeny = (k_event_flag_t)(1 << 1);
|
||||
const k_event_flag_t event_miny = (k_event_flag_t)(1 << 2);
|
||||
const k_event_flag_t event_moe = (k_event_flag_t)(1 << 3);
|
||||
|
||||
k_event_t event;
|
||||
|
||||
void entry_task_listener1(void *arg)
|
||||
{
|
||||
k_event_flag_t flag_match;
|
||||
k_err_t err;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
|
||||
&flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ALL | TOS_OPT_EVENT_PEND_CLR);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("entry_task_listener1:\n");
|
||||
printf("eeny, meeny, miny, moe, they all come\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_listener2(void *arg)
|
||||
{
|
||||
k_event_flag_t flag_match;
|
||||
k_err_t err;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_event_pend(&event, event_eeny | event_meeny | event_miny | event_moe,
|
||||
&flag_match, TOS_TIME_FOREVER, TOS_OPT_EVENT_PEND_ANY | TOS_OPT_EVENT_PEND_CLR);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("entry_task_listener2:\n");
|
||||
if (flag_match == event_eeny) {
|
||||
printf("eeny comes\n");
|
||||
}
|
||||
if (flag_match == event_meeny) {
|
||||
printf("meeny comes\n");
|
||||
}
|
||||
if (flag_match == event_miny) {
|
||||
printf("miny comes\n");
|
||||
}
|
||||
if (flag_match == event_moe) {
|
||||
printf("moe comes\n");
|
||||
}
|
||||
if (flag_match == (event_eeny | event_meeny | event_miny | event_moe)) {
|
||||
printf("all come\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_trigger(void *arg)
|
||||
{
|
||||
int i = 1;
|
||||
|
||||
while (K_TRUE) {
|
||||
if (i == 2) {
|
||||
printf("entry_task_trigger:\n");
|
||||
printf("eeny will come\n");
|
||||
tos_event_post(&event, event_eeny);
|
||||
}
|
||||
if (i == 3) {
|
||||
printf("entry_task_trigger:\n");
|
||||
printf("meeny will come\n");
|
||||
tos_event_post(&event, event_meeny);
|
||||
}
|
||||
if (i == 4) {
|
||||
printf("entry_task_trigger:\n");
|
||||
printf("miny will come\n");
|
||||
tos_event_post(&event, event_miny);
|
||||
}
|
||||
if (i == 5) {
|
||||
printf("entry_task_trigger:\n");
|
||||
printf("moe will come\n");
|
||||
tos_event_post(&event, event_moe);
|
||||
}
|
||||
if (i == 6) {
|
||||
printf("entry_task_trigger:\n");
|
||||
printf("all will come\n");
|
||||
tos_event_post(&event, event_eeny | event_meeny | event_miny | event_moe);
|
||||
}
|
||||
tos_task_delay(1000);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
tos_event_create(&event, (k_event_flag_t)0u);
|
||||
(void)tos_task_create(&task_listener1, "listener1", entry_task_listener1, NULL,
|
||||
3, stack_task_listener1, STK_SIZE_TASK_LISTENER, 0);
|
||||
(void)tos_task_create(&task_listener2, "listener2", entry_task_listener2, NULL,
|
||||
4, stack_task_listener2, STK_SIZE_TASK_LISTENER, 0);
|
||||
(void)tos_task_create(&task_trigger, "trigger", entry_task_trigger, NULL,
|
||||
4, stack_task_trigger, STK_SIZE_TASK_TRIGGER, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
103
doc/code/2.3.4 queue/main.c
Normal file
@@ -0,0 +1,103 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_RECEIVER 512
|
||||
#define STK_SIZE_TASK_SENDER 512
|
||||
|
||||
#define PRIO_TASK_RECEIVER_HIGHER_PRIO 4
|
||||
#define PRIO_TASK_RECEIVER_LOWER_PRIO (PRIO_TASK_RECEIVER_HIGHER_PRIO + 1)
|
||||
|
||||
k_stack_t stack_task_receiver_higher_prio[STK_SIZE_TASK_RECEIVER];
|
||||
k_stack_t stack_task_receiver_lower_prio[STK_SIZE_TASK_RECEIVER];
|
||||
k_stack_t stack_task_sender[STK_SIZE_TASK_SENDER];
|
||||
|
||||
k_task_t task_receiver_higher_prio;
|
||||
k_task_t task_receiver_lower_prio;
|
||||
k_task_t task_sender;
|
||||
|
||||
k_queue_t queue;
|
||||
|
||||
extern void entry_task_receiver_higher_prio(void *arg);
|
||||
extern void entry_task_receiver_lower_prio(void *arg);
|
||||
extern void entry_task_sender(void *arg);
|
||||
|
||||
void entry_task_receiver_higher_prio(void *arg)
|
||||
{
|
||||
k_err_t err;
|
||||
void *msg_received;
|
||||
size_t msg_size;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_queue_pend(&queue, &msg_received, &msg_size, TOS_TIME_FOREVER);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("entry_task_receiver_higher_prio:\n");
|
||||
printf("message body: %s\n", (char *)msg_received);
|
||||
printf("message size: %d\n", msg_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_receiver_lower_prio(void *arg)
|
||||
{
|
||||
k_err_t err;
|
||||
void *msg_received;
|
||||
size_t msg_size;
|
||||
|
||||
while (K_TRUE) {
|
||||
err = tos_queue_pend(&queue, &msg_received, &msg_size, TOS_TIME_FOREVER);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("entry_task_receiver_lower_prio:\n");
|
||||
printf("message body: %s\n", (char *)msg_received);
|
||||
printf("message size: %d\n", msg_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_sender(void *arg)
|
||||
{
|
||||
int i = 1;
|
||||
char *msg_to_one_receiver = "message for one receiver[with highest priority]";
|
||||
char *msg_to_all_receiver = "message for all receivers";
|
||||
|
||||
while (K_TRUE) {
|
||||
if (i == 2) {
|
||||
printf("entry_task_sender:\n");
|
||||
printf("send a message to one receiver, and shoud be the highest priority one\n");
|
||||
tos_queue_post(&queue, msg_to_one_receiver, strlen(msg_to_one_receiver));
|
||||
}
|
||||
if (i == 3) {
|
||||
printf("entry_task_sender:\n");
|
||||
printf("send a message to all recevier\n");
|
||||
tos_queue_post_all(&queue, msg_to_all_receiver, strlen(msg_to_all_receiver));
|
||||
}
|
||||
if (i == 4) {
|
||||
printf("entry_task_sender:\n");
|
||||
printf("send a message to one receiver, and shoud be the highest priority one\n");
|
||||
tos_queue_post(&queue, msg_to_one_receiver, strlen(msg_to_one_receiver));
|
||||
}
|
||||
if (i == 5) {
|
||||
printf("entry_task_sender:\n");
|
||||
printf("send a message to all recevier\n");
|
||||
tos_queue_post_all(&queue, msg_to_all_receiver, strlen(msg_to_all_receiver));
|
||||
}
|
||||
tos_task_delay(1000);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
tos_queue_create(&queue);
|
||||
(void)tos_task_create(&task_receiver_higher_prio, "receiver_higher_prio",
|
||||
entry_task_receiver_higher_prio, NULL, PRIO_TASK_RECEIVER_HIGHER_PRIO,
|
||||
stack_task_receiver_higher_prio, STK_SIZE_TASK_RECEIVER, 0);
|
||||
(void)tos_task_create(&task_receiver_lower_prio, "receiver_lower_prio",
|
||||
entry_task_receiver_lower_prio, NULL, PRIO_TASK_RECEIVER_LOWER_PRIO,
|
||||
stack_task_receiver_lower_prio, STK_SIZE_TASK_RECEIVER, 0);
|
||||
(void)tos_task_create(&task_sender, "sender", entry_task_sender, NULL,
|
||||
4, stack_task_sender, STK_SIZE_TASK_SENDER, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
70
doc/code/2.4.1 mmheap/main.c
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
|
||||
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
|
||||
|
||||
k_task_t task_demo;
|
||||
|
||||
extern void entry_task_demo(void *arg);
|
||||
|
||||
void entry_task_demo(void *arg)
|
||||
{
|
||||
void *p = K_NULL, *p_aligned = K_NULL;
|
||||
int i = 0;
|
||||
|
||||
while (K_TRUE) {
|
||||
if (i == 1) {
|
||||
p = tos_mmheap_alloc(0x30);
|
||||
if (p) {
|
||||
printf("alloc: %x\n", (cpu_addr_t)p);
|
||||
}
|
||||
} else if (i == 2) {
|
||||
if (p) {
|
||||
printf("free: %x\n", p);
|
||||
tos_mmheap_free(p);
|
||||
}
|
||||
} else if (i == 3) {
|
||||
p = tos_mmheap_alloc(0x30);
|
||||
if (p) {
|
||||
printf("alloc: %x\n", (cpu_addr_t)p);
|
||||
}
|
||||
} else if (i == 4) {
|
||||
p_aligned = tos_mmheap_aligned_alloc(0x50, 16);
|
||||
if (p_aligned) {
|
||||
printf("aligned alloc: %x\n", (cpu_addr_t)p_aligned);
|
||||
if ((cpu_addr_t)p_aligned % 16 == 0) {
|
||||
printf("%x is 16 aligned\n", (cpu_addr_t)p_aligned);
|
||||
} else {
|
||||
printf("should not happen\n");
|
||||
}
|
||||
}
|
||||
} else if (i == 5) {
|
||||
p = tos_mmheap_realloc(p, 0x40);
|
||||
if (p) {
|
||||
printf("realloc: %x\n", (cpu_addr_t)p);
|
||||
}
|
||||
} else if (i == 6) {
|
||||
if (p) {
|
||||
tos_mmheap_free(p);
|
||||
}
|
||||
if (p_aligned) {
|
||||
tos_mmheap_free(p_aligned);
|
||||
}
|
||||
}
|
||||
|
||||
tos_task_delay(1000);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
(void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
|
||||
4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
68
doc/code/2.4.2 mmblk/main.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
|
||||
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
|
||||
|
||||
k_task_t task_demo;
|
||||
|
||||
|
||||
#define MMBLK_BLK_NUM 5
|
||||
#define MMBLK_BLK_SIZE 0x20
|
||||
|
||||
k_mmblk_pool_t mmblk_pool;
|
||||
|
||||
uint8_t mmblk_pool_buffer[MMBLK_BLK_NUM * MMBLK_BLK_SIZE];
|
||||
|
||||
void *p[MMBLK_BLK_NUM] = { K_NULL };
|
||||
|
||||
extern void entry_task_demo(void *arg);
|
||||
|
||||
void entry_task_demo(void *arg)
|
||||
{
|
||||
void *p_dummy = K_NULL;
|
||||
k_err_t err;
|
||||
int i = 0;
|
||||
|
||||
printf("mmblk_pool has %d blocks, size of each block is 0x%x\n", 5, 0x20);
|
||||
for (; i < MMBLK_BLK_NUM; ++i) {
|
||||
err = tos_mmblk_alloc(&mmblk_pool, &p[i]);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("%d block alloced: 0x%x\n", i, p[i]);
|
||||
} else {
|
||||
printf("should not happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
err = tos_mmblk_alloc(&mmblk_pool, &p_dummy);
|
||||
if (err == K_ERR_MMBLK_POOL_EMPTY) {
|
||||
printf("blocks exhausted, all blocks is alloced\n");
|
||||
} else {
|
||||
printf("should not happen\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < MMBLK_BLK_NUM; ++i) {
|
||||
err = tos_mmblk_free(&mmblk_pool, p[i]);
|
||||
if (err != K_ERR_NONE) {
|
||||
printf("should not happen\n");
|
||||
}
|
||||
}
|
||||
err = tos_mmblk_free(&mmblk_pool, p[0]);
|
||||
if (err == K_ERR_MMBLK_POOL_FULL) {
|
||||
printf("pool is full\n");
|
||||
} else {
|
||||
printf("should not happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
tos_mmblk_pool_create(&mmblk_pool, mmblk_pool_buffer, MMBLK_BLK_NUM, MMBLK_BLK_SIZE);
|
||||
(void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
|
||||
4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
42
doc/code/2.5 time/main.c
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
|
||||
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
|
||||
|
||||
k_task_t task_demo;
|
||||
|
||||
extern void entry_task_demo(void *arg);
|
||||
|
||||
void entry_task_demo(void *arg)
|
||||
{
|
||||
k_time_t ms;
|
||||
k_tick_t systick, after_systick;
|
||||
|
||||
systick = tos_millisec2tick(2000);
|
||||
printf("%d millisec equals to %lld ticks\n", 2000, systick);
|
||||
|
||||
ms = tos_tick2millisec(1000);
|
||||
printf("%lld ticks equals to %d millisec\n", (k_tick_t)1000, ms);
|
||||
|
||||
systick = tos_systick_get();
|
||||
printf("before sleep, systick is %lld\n", systick);
|
||||
|
||||
tos_sleep_ms(2000);
|
||||
|
||||
after_systick = tos_systick_get();
|
||||
printf("after sleep %d ms, systick is %lld\n", 2000, after_systick);
|
||||
|
||||
printf("milliseconds sleep is about: %d\n", tos_tick2millisec(after_systick - systick));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
(void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
|
||||
4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
47
doc/code/2.6 timer/main.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
|
||||
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
|
||||
|
||||
k_task_t task_demo;
|
||||
|
||||
extern void entry_task_demo(void *arg);
|
||||
|
||||
void oneshot_timer_cb(void *arg)
|
||||
{
|
||||
printf("this is oneshot timer callback, current systick: %lld\n", tos_systick_get());
|
||||
}
|
||||
|
||||
void periodic_timer_cb(void *arg)
|
||||
{
|
||||
printf("this is periodic timer callback, current systick: %lld\n", tos_systick_get());
|
||||
}
|
||||
|
||||
void entry_task_demo(void *arg)
|
||||
{
|
||||
k_timer_t oneshot_tmr;
|
||||
k_timer_t periodic_tmr;
|
||||
|
||||
tos_timer_create(&oneshot_tmr, 3000, 0, oneshot_timer_cb, K_NULL, TOS_OPT_TIMER_ONESHOT);
|
||||
tos_timer_create(&periodic_tmr, 2000, 3000, periodic_timer_cb, K_NULL, TOS_OPT_TIMER_PERIODIC);
|
||||
|
||||
printf("current systick: %lld\n", tos_systick_get());
|
||||
tos_timer_start(&oneshot_tmr);
|
||||
tos_timer_start(&periodic_tmr);
|
||||
|
||||
while (K_TRUE) {
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
(void)tos_task_create(&task_demo, "receiver_higher_prio", entry_task_demo, NULL,
|
||||
4, stack_task_demo, STK_SIZE_TASK_DEMO, 0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
71
doc/code/2.7 robin/main.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
#define STK_SIZE_TASK_SAMPLE 512
|
||||
|
||||
#define PRIO_TASK_DEMO 4
|
||||
#define PRIO_TASK_SAMPLE (PRIO_TASK_DEMO - 1)
|
||||
|
||||
const k_timeslice_t timeslice_demo1 = 10;
|
||||
const k_timeslice_t timeslice_demo2 = 20;
|
||||
|
||||
k_stack_t stack_task_demo1[STK_SIZE_TASK_DEMO];
|
||||
k_stack_t stack_task_demo2[STK_SIZE_TASK_DEMO];
|
||||
k_stack_t stack_task_sample[STK_SIZE_TASK_SAMPLE];
|
||||
|
||||
k_task_t task_demo1;
|
||||
k_task_t task_demo2;
|
||||
k_task_t task_sample;
|
||||
|
||||
extern void entry_task_demo1(void *arg);
|
||||
extern void entry_task_demo2(void *arg);
|
||||
extern void entry_task_sample(void *arg);
|
||||
|
||||
uint64_t demo1_counter = 0;
|
||||
uint64_t demo2_counter = 0;
|
||||
|
||||
void entry_task_demo1(void *arg)
|
||||
{
|
||||
while (K_TRUE) {
|
||||
++demo1_counter;
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_demo2(void *arg)
|
||||
{
|
||||
while (K_TRUE) {
|
||||
++demo2_counter;
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_sample(void *arg)
|
||||
{
|
||||
while (K_TRUE) {
|
||||
printf("demo1_counter: %lld\n", demo1_counter);
|
||||
printf("demo2_counter: %lld\n", demo2_counter);
|
||||
printf("demo2_counter / demo1_counter = %f\n",
|
||||
(double)demo2_counter / demo1_counter);
|
||||
printf("should almost equals to:\n");
|
||||
printf("timeslice_demo2 / timeslice_demo1 = %f\n\n", (double)timeslice_demo2 / timeslice_demo1);
|
||||
tos_task_delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
tos_robin_config(TOS_ROBIN_STATE_ENABLED, (k_timeslice_t)500u);
|
||||
(void)tos_task_create(&task_demo1, "demo1", entry_task_demo1, NULL,
|
||||
PRIO_TASK_DEMO, stack_task_demo1, STK_SIZE_TASK_DEMO,
|
||||
timeslice_demo1);
|
||||
(void)tos_task_create(&task_demo2, "demo2", entry_task_demo2, NULL,
|
||||
PRIO_TASK_DEMO, stack_task_demo2, STK_SIZE_TASK_DEMO,
|
||||
timeslice_demo2);
|
||||
(void)tos_task_create(&task_sample, "sample", entry_task_sample, NULL,
|
||||
PRIO_TASK_SAMPLE, stack_task_sample, STK_SIZE_TASK_SAMPLE,
|
||||
0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
123
doc/code/2.8.1 msg_queue/main.c
Normal file
@@ -0,0 +1,123 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
|
||||
#define PRIO_TASK_DEMO 4
|
||||
|
||||
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
|
||||
|
||||
k_task_t task_demo;
|
||||
|
||||
k_msg_queue_t msg_queue;
|
||||
|
||||
struct msg_st {
|
||||
char *msg;
|
||||
size_t size;
|
||||
} msgs[TOS_CFG_MSG_POOL_SIZE] = {
|
||||
{ "msg 0", 6 },
|
||||
{ "msg 1", 6 },
|
||||
{ "msg 2", 6 },
|
||||
};
|
||||
|
||||
struct msg_st dummy_msg = { "dummy msg", 10 };
|
||||
|
||||
extern void entry_task_demo(void *arg);
|
||||
|
||||
void fifo_opt(void) {
|
||||
k_err_t err;
|
||||
int i = 0;
|
||||
char *msg_received = K_NULL;
|
||||
size_t msg_size = 0;
|
||||
|
||||
for (; i < TOS_CFG_MSG_POOL_SIZE; ++i) {
|
||||
printf("msg put: %s\n", msgs[i].msg);
|
||||
err = tos_msg_queue_put(&msg_queue, (void *)msgs[i].msg, msgs[i].size, TOS_OPT_MSG_PUT_FIFO);
|
||||
if (err != K_ERR_NONE) {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
err = tos_msg_queue_put(&msg_queue, (void *)dummy_msg.msg, dummy_msg.size, TOS_OPT_MSG_PUT_FIFO);
|
||||
if (err == K_ERR_MSG_QUEUE_FULL) {
|
||||
printf("msg queue is full\n");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < TOS_CFG_MSG_POOL_SIZE; ++i) {
|
||||
err = tos_msg_queue_get(&msg_queue, (void **)&msg_received, &msg_size);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("msg received: %s\n", msg_received);
|
||||
printf("msg size: %d\n", msg_size);
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
err = tos_msg_queue_get(&msg_queue, (void **)&msg_received, &msg_size);
|
||||
if (err == K_ERR_MSG_QUEUE_EMPTY) {
|
||||
printf("msg queue is empty\n");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
void lifo_opt(void) {
|
||||
k_err_t err;
|
||||
int i = 0;
|
||||
char *msg_received = K_NULL;
|
||||
size_t msg_size = 0;
|
||||
|
||||
for (; i < TOS_CFG_MSG_POOL_SIZE; ++i) {
|
||||
printf("msg put: %s\n", msgs[i].msg);
|
||||
err = tos_msg_queue_put(&msg_queue, (void *)msgs[i].msg, msgs[i].size, TOS_OPT_MSG_PUT_LIFO);
|
||||
if (err != K_ERR_NONE) {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
err = tos_msg_queue_put(&msg_queue, (void *)dummy_msg.msg, dummy_msg.size, TOS_OPT_MSG_PUT_LIFO);
|
||||
if (err == K_ERR_MSG_QUEUE_FULL) {
|
||||
printf("msg queue is full\n");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < TOS_CFG_MSG_POOL_SIZE; ++i) {
|
||||
err = tos_msg_queue_get(&msg_queue, (void **)&msg_received, &msg_size);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("msg received: %s\n", msg_received);
|
||||
printf("msg size: %d\n", msg_size);
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
err = tos_msg_queue_get(&msg_queue, (void **)&msg_received, &msg_size);
|
||||
if (err == K_ERR_MSG_QUEUE_EMPTY) {
|
||||
printf("msg queue is empty\n");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_demo(void *arg)
|
||||
{
|
||||
tos_msg_queue_create(&msg_queue);
|
||||
|
||||
printf("max msg in pool: %d\n", TOS_CFG_MSG_POOL_SIZE);
|
||||
printf("msg queue using TOS_OPT_MSG_PUT_FIFO\n");
|
||||
fifo_opt();
|
||||
|
||||
printf("msg queue using TOS_OPT_MSG_PUT_LIFO\n");
|
||||
lifo_opt();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
(void)tos_task_create(&task_demo, "demo1", entry_task_demo, NULL,
|
||||
PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
|
||||
0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
114
doc/code/2.8.2 fifo/main.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include "tos.h"
|
||||
#include "mcu_init.h"
|
||||
|
||||
#define STK_SIZE_TASK_DEMO 512
|
||||
|
||||
#define PRIO_TASK_DEMO 4
|
||||
|
||||
k_stack_t stack_task_demo[STK_SIZE_TASK_DEMO];
|
||||
|
||||
k_task_t task_demo;
|
||||
|
||||
#define FIFO_BUFFER_SIZE 5
|
||||
uint8_t fifo_buffer[FIFO_BUFFER_SIZE];
|
||||
|
||||
k_fifo_t fifo;
|
||||
|
||||
extern void entry_task_demo(void *arg);
|
||||
|
||||
void char_push(void)
|
||||
{
|
||||
k_err_t err;
|
||||
int i = 0;
|
||||
uint8_t data;
|
||||
|
||||
for (i = 0; i < FIFO_BUFFER_SIZE; ++i) {
|
||||
printf("char pushed: %c\n", 'a' + i);
|
||||
err = tos_fifo_push(&fifo, 'a' + i);
|
||||
if (err != K_ERR_NONE) {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
err = tos_fifo_push(&fifo, 'z');
|
||||
if (err == K_ERR_FIFO_FULL) {
|
||||
printf("fifo is full: %s\n", tos_fifo_is_full(&fifo) ? "TRUE" : "FALSE");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
|
||||
for (i = 0; i < FIFO_BUFFER_SIZE; ++i) {
|
||||
err = tos_fifo_pop(&fifo, &data);
|
||||
if (err == K_ERR_NONE) {
|
||||
printf("%d pop: %c\n", i, data);
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
err = tos_fifo_pop(&fifo, &data);
|
||||
if (err == K_ERR_FIFO_EMPTY) {
|
||||
printf("fifo is empty: %s\n", tos_fifo_is_empty(&fifo) ? "TRUE" : "FALSE");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
void stream_push(void)
|
||||
{
|
||||
int count = 0, i = 0;
|
||||
uint8_t stream[FIFO_BUFFER_SIZE] = { 'a', 'b', 'c', 'd', 'e' };
|
||||
uint8_t stream_dummy[1] = { 'z' };
|
||||
uint8_t stream_pop[FIFO_BUFFER_SIZE];
|
||||
|
||||
count = tos_fifo_push_stream(&fifo, &stream[0], FIFO_BUFFER_SIZE);
|
||||
if (count != FIFO_BUFFER_SIZE) {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
|
||||
count = tos_fifo_push_stream(&fifo, &stream_dummy[0], 1);
|
||||
if (count == 0) {
|
||||
printf("fifo is full: %s\n", tos_fifo_is_full(&fifo) ? "TRUE" : "FALSE");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
|
||||
count = tos_fifo_pop_stream(&fifo, &stream_pop[0], FIFO_BUFFER_SIZE);
|
||||
if (count == FIFO_BUFFER_SIZE) {
|
||||
printf("stream popped:\n");
|
||||
for (i = 0; i < FIFO_BUFFER_SIZE; ++i) {
|
||||
printf("%c", stream_pop[i]);
|
||||
}
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
|
||||
count = tos_fifo_pop_stream(&fifo, &stream_pop[0], 1);
|
||||
if (count == 0) {
|
||||
printf("fifo is empty: %s\n", tos_fifo_is_empty(&fifo) ? "TRUE" : "FALSE");
|
||||
} else {
|
||||
printf("should never happen\n");
|
||||
}
|
||||
}
|
||||
|
||||
void entry_task_demo(void *arg)
|
||||
{
|
||||
tos_fifo_create(&fifo, &fifo_buffer[0], FIFO_BUFFER_SIZE);
|
||||
|
||||
printf("fifo, dealing with char\n");
|
||||
char_push();
|
||||
|
||||
printf("fifo, dealing with stream\n");
|
||||
stream_push();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
board_init();
|
||||
tos_knl_init();
|
||||
(void)tos_task_create(&task_demo, "demo1", entry_task_demo, NULL,
|
||||
PRIO_TASK_DEMO, stack_task_demo, STK_SIZE_TASK_DEMO,
|
||||
0);
|
||||
tos_knl_start();
|
||||
}
|
||||
|
BIN
doc/img/add_dev.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
doc/img/add_dev_done.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
doc/img/add_prodcut.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
doc/img/cmq.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
doc/img/cmq_info.png
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
doc/img/create_new_product.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
doc/img/dev_list.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
doc/img/log.png
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
doc/img/log_search.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
doc/img/login_iotcloud.png
Normal file
After Width: | Height: | Size: 33 KiB |
BIN
doc/img/mqtt_client_broker.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
doc/img/mqtt_file.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
doc/img/mqtt_header.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
doc/img/new_device.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
doc/img/new_rule.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
doc/img/product_info.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
doc/img/qcloud_arch.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
doc/img/rule_engine.png
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
doc/img/rule_info.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
doc/img/rule_set.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
doc/img/rule_set_2to1.png
Normal file
After Width: | Height: | Size: 36 KiB |
BIN
doc/img/topic_list.png
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
doc/picture/introduction/Partners.png
Normal file
After Width: | Height: | Size: 268 KiB |
BIN
doc/picture/introduction/TencentOS_tiny_Architecture.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
doc/picture/introduction/TencentOS_tiny_log.png
Normal file
After Width: | Height: | Size: 13 MiB |