1 前沿
让我们先简单回顾下物联网(The Internet Of Things)的历史,
1999年,物联网概念第一次被提出;
2009年,“感知中国”的概念被提出,物联网被正式列入国家新兴战略性产业之一;
2013年,Semtech LoRa技术商用;
2016年,NB-IoT R13核心技术标准的冻结,全球运营商有了基于标准化的物联网专有协议,窄带物联网迎来规模商用;
2017年,以NB-IoT、LoRa为代表的「LPWAN」物联网技术异军突起与迅猛发展,LPWAN的技术特性让万物互联场景的实现变成了可能;
2019年,5G商用正式拉开序幕,将为物联网产业的发展搭建一条高速公路.....
同时伴随着十几年互联网技术发展,大数据、云计算、人工智能(机器学习)、IoT通信技术等多种技术的成熟与不断融合,物联网的愿景再一次进化——让万物互联照进现实。
全球权威的信息提供商IHS、IDC、Gartner公司的分析报告都指出到2020年,全球可连接设备数将超过200~300多亿。面对如此海量的连接设备,物联网再一次成为了群雄逐鹿的赛场。同时,面对物联网高度碎片化的特点,各路大佬们纷纷摇旗呐喊,不约而同打造物联网生态。
2016年,HUAWEI CONNECT 大会上,华为正式发布了以IoT联接管理平台为核心的OceanConnect IoT平台。
2017年开始,腾讯云全面布局物联网,为行业客户提供加速物联网+的基础服务,包括:基础云服务、智能化的AI大数据服务,以及物联PaaS服务。
2017年底,华为确定了与IoT紧密相关的新愿景:“把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界”。
2018云栖大会·深圳峰会阿里云总裁胡晓明宣布:阿里巴巴将全面进军物联网领域,IoT是阿里巴巴集团继电商、金融、物流、云计算后新的主赛道。
2018年3月,阿里云发布支持LoRa协议的LinkWAN平台。
2019年7月,腾讯发布一站式物联网开发平台:腾讯云IoT Explorer,或将为物联网应用的爆炸式增长扫除最后一道门槛。
物联网RTOS作为IoT终端侧的基础软件,在一定程度上可以破解物联网下游的碎片化困局,加快下游物联网应用的开发进程,同时海量的物联网应用爆发与普及也会带动芯片\存储等成本持续降低,正如同早些年的IoT WiFi芯片,现在的IoT BLE芯片,规模效应后,单片成本已经快探底,成本承压也会变得不太敏感,进而可进一步释放物联网终端侧的硬件资源瓶颈。各路大佬布局物联网RTOS,卡位数据入口,使得RTOS插上物联网翅膀,在万物互联时代再一次起飞翱翔...
接下来,我们来尝鲜一下物联网RTOS新贵——TencentOS tiny
1.1 IoT RTOS的新贵TencentOS tiny
TencentOS作为IoT RTOS的后起之秀,具备了物联网RTOS几大典型特征:
- 扩芯片平台
- 硬实时内核、多任务、IPC通信...
- 小的FLASH\RAM开销
- 低功耗
- 多种联网、上云能力
- 安全能力
- 丰富组件
下文学习如何基于MDK与STM32L4实现第一个TencentOS tiny应用程序——串口输出“Hello World”,作为TencentOS tiny入门的第一个里程碑,打开TencentOS tiny知识的大门。
PS:本文代码基于TencentOS tiny 1.0版本(2019.09.18正式对外开源版本)
1.2 内容提要
下文主要内容涉及了TencentOS tiny入门知识、基于MDK的开发环境搭建、TencentOS tiny内核启动过程、main入口与应用层代码入口、helloworld应用示例等。
- TencentOS tiny入门知识
- TencentOS tiny系统架构
- TencentOS tiny源代码目录树
- 基于MDK的开发环境搭建
- 基于MDK的TencentOS tiny启动流程
- TencentOS tiny main函数入口
- 应用程序入口
- TencentOS tiny 第一个应用helloworld
2 TencentOS tiny入门知识
2.1 TencentOS tiny系统架构
TencentOS tiny系统架构符合分层架构设计与组件化架构设计原则。TencentOS tiny架构至下(层)而上(层),分别是
- 2.2 TencentOS tiny文件夹目录树
TencentOS tiny系统架构体现在源代码文件组织结构,如下图所示
TencentOS tiny源代码一级文件夹目录树
3 开发环境搭建
开发环境主要取决于使用的硬件芯片平台与个人开发习惯。
TencentOS tiny目前主要支持Cortex-M、Risc-V的32位MCU,当前支持BSP里面都提供了KEIL(MDK)示例工程,部分也同时提供了IAR、GCC,对于习惯在Window开发Cortex-M产品的小伙伴会比较熟悉。
针对开发环境,简单对比下国内另外两款非常火的IoT RTOS:
- RT-Thread4.0.x这块会更完善,提供了基于Python & Scons且无需安装的Env工具,通过简单修改Kconfig与SConscript文件,就可以非常快速生成MDK、IAR工程,工具用熟以后,移植是非常高效的,另外RT-Thread发展历史悠久,积累支持的BSP也非常多。
- AliOS Things2.1.x同样提供了基于Python & Scons的AOS-Cube工具,可快速生成MDK、IAR工程,但目前支持的BSP尚不太多,还正在完善中。
3.1 基于MDK与STM32L4的开发环境搭建
查看下 TencentOS tiny\board 是否有与目标主芯片相似的示例工程
- 如果是已有相似平台的话,直接拷贝该示例工程作为项目原型,然后根据实际硬件,重新配置,移植也会相对简单高效。比如本文平台目标芯片为STM32L476VGT6,STM32同系列芯片可以共用相同底层代码,因此这里直接修改TencentOS tiny\board\NUCLEO_STM32L476RG
- 如果没有的话,需要参考官方移植文档,具体可如“6 参考”章节相关内容。
4 基于MDK的TOS内核启动流程
在嵌入式系统(MCU)中,启动文件(比如startup_stm32l476xx.s)是芯片上电最先运行的一段程序,是整个系统软件非常关键的部分,如果启动文件出现错误,则整个系统就跑不起来。
4.1 MCU的启动过程
MCU上电后,启动文件会自动进行一些MCU芯片底层的初始化,构建程序运行必要的环境,包括:
- 堆栈空间定义与初始化
- 变量初始化
- 申请与定义中断向量表
- 定义复位中断函数(Reset_Handler)
- 用SystemInit()函数来初始化系统的各种时钟、向量表偏移,然后调用(跳转到)__main()函数
- __main()调用库函数初始化堆栈
- 其他中断异常服务函数定义(弱[WEAK]申明)
通常,芯片原厂都提供了MDK对应内核芯片(比如ST STM32L4xx...)启动文件,启动文件由汇编程序编写,一般命名为startup_xxx.s,xxx为支持的芯片型号,比如startup_stm32l476xx.s。
基于MDK开发,一般可直接使用芯片原厂提供的启动文件,不需要涉及启动文件的修改。
4.2 内核启动过程
通过“4.1 MCU的启动过程”完成了MCU从系统上电开始运行到系统进入应用程序主代码(main函数)的准备工作。接下来便是TencentOS tiny内核启动。
下文结合TencentOS tiny与board\NUCLEO_STM32L476RG的开发板示例进行说明。
4.2.1 TencentOS tiny的main程序入口
TencentOS tiny的main()函数主要完成以下工作:
- board_init()板级初始化,初始化系统时钟(system clock)、相关外设初始化(TencentOS tiny目前没有提供设备驱动框架,因此这块跟裸机开发一样,直接使用STM32CubeMX生成代码)
- 调用osKernelInitialize,调用内核相关初始化操作,用户根据实际项目通过board\NUCLEO_STM32L476RG\TOS_CONFIG\tos_config.h相关宏定制内核服务等
- 在这里面会创建系统IDLE任务
- 调用osThreadCreate,创建一个application_entry任务,在application_entry()任务中会开始执行应用层代码。application_entry()默认采用__weak弱定义,用户重新自定义实现application_entry()即可.
- 最后调用osKernelStart,启动系统调度
TencentOS tiny内核启动流程,如下所示:
从TencentOS tiny实际代码看看:
1. main()函数所处的位置位于硬件平台的BSP文件夹中,比如\board\NUCLEO_STM32L476RG\main.c。
int main(void)
{
board_init();
printf("Welcome to TencentOS tiny\r\n");
osKernelInitialize(); // TOS Tiny kernel initialize
osThreadCreate(osThread(application_entry), NULL); // Create TOS Tiny task
osKernelStart(); // Start TOS Tiny
}
2. board\LR_STM32L476RG\BSP\Src\mcu_init.c定义了硬件平台的系统时钟、GPIO、串口等MCU外设初始化
void board_init(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
}
4.2.2 应用程序入口
TencentOS tiny默认通过弱定义的application_entry()任务作为应用层代码的入口,用户根据实际项目重新自定义application_entry(),并添加应用层代码。
应用层参考代码放在example目录下,在application_entry()函数中实现应用代码,如“5章节” Hello Word示例应用。
__weak void application_entry(void *arg)
{
while (1) {
printf("This is a demo task,please use your task entry!\r\n");
tos_task_delay(1000);
}
}
5 第一个应用 HelloWorld
TencentOS tiny的hello world应用程序放在TencentOS-tiny\examples\hello_world\hello_world.c
#define TASK1_STK_SIZE 512
void task1(void *arg);
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);
#define TASK2_STK_SIZE 512
void task2(void *arg);
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);
// 每2s钟printf hello world
void task1(void *arg)
{
int count = 1;
while (1) {
printf("###This is task1,Hello World %d\r\n", count++);
osDelay(2000);
}
}
// 每1s钟printf hello world
void task2(void *arg)
{
int count = 1;
while (1) {
#if TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN > 0u
k_err_t rc;
int depth;
rc = tos_task_stack_draught_depth(K_NULL, &depth);
printf("%d %d\n", rc, depth);
#endif
printf("***This is task2,Hello World %d\r\n", count++);
osDelay(1000);
}
}
// 重新定义application_entry,将自动取代已有__week修饰的application_entry
void application_entry(void *arg)
{
osThreadCreate(osThread(task1), NULL); // Create task1
osThreadCreate(osThread(task2), NULL); // Create task2
}
使用Doxygen生成的函数调用关系,可以比较清楚看到函数之间的调用逻辑,如下图所示
application_entry系统调用图(局部放大)
task1函数调用图
task2函数调用图
通过Jlink等下载到STM32L4硬件板子上,并运行,实际效果如下:
串口Hello World示例
6 参考
- TencenOS tiny开源代码库