一、移植前的准备工作
1. STM32的裸机工程模板
这个可以自己创建(创建过程参考之前的STM32裸机工程也有工程网盘在上面)
2.下载TencentOS tiny 源码
TencentOS tiny的源码可从TencentOS tiny GitHub仓库地址https://github.com/Tencent/TencentOS-tiny下载,如果GitHub下载慢,也可以通过腾讯工蜂开源仓下载,地址:https://git.code.tencent.com/Tencent_Open_Source/TencentOS-tiny
3.TencentOS tiny源码核心文件夹分析
打开TencentOS tiny源码文件,可以看见里面有12个文件夹,下面先来了解主要文件夹及其子文件夹的作用,然后将TencentOS tiny源码的核心文件提取出来,添加到工程根目录下的文件夹中,因为工程只需要有用的源码文件,而不是全部的TencentOS tiny源码,所以可以避免工程过于庞大。
一级目录 | 二 / 三级目录 | 说明 |
arch | arm | TencentOS tiny适配的IP核架构(含M核中断、调度、tick相关代码),对我们的移植很重要 |
arch | risc-v | TencentOS tiny适配的risc-v架构 |
board | TencentOS_tiny_EVB_MX | TencentOS tiny 定制开发板demo,包含AT适配框架、MQTT协议、安全组件等 |
component | connectivity / loraWAN | loRaWAN协议栈实现源码及适配层 |
connectivity / Eclipse-Paho-MQTT | MQTT协议栈实现源码及适配层 | |
connectivity / TencentCloud_SDK | 腾讯云C-SDK实现源码及适配层 | |
fs | 文件系统实现源码 | |
security | mbedtls 安全协议源码 | |
utils | 包含json相关源码 | |
devices | TencentOS tiny适配的一些外设驱动(如串口wifi gprs 驱动等) | |
doc | TencentOS tiny相关技术文档及开发指南(建议多看这部分) | |
examples | TencentOS tiny提供的功能示例 | |
kernel | core | TencentOS tiny内核源码(这部分是最重要的) |
hal | TencentOS tiny驱动抽象层 | |
pm | TencentOS tiny低功耗模块源码 | |
net | at | TencentOS tiny为串口类通信模组提供的AT框架实现层 |
lwip | TencentOS tiny为串口类LoraWAN模块提供的移植框架 | |
sal_module_wrapper | Lwip协议实现源码及适配层 | |
tencent_firmware_module_wrapper | TencentOS tiny为串口类网络模块(wifi gprs)提供的socket移植框架 | |
tencent_firmware_module_wrapper | TencentOS tiny提供的腾讯定制模组移植框架 | |
osal | cmsis_os | TencentOS tiny提供的cmsis os 适配 |
platform | hal | TencentOS tiny适配的部分芯片的驱动实现源码 |
vendor_bsp | 芯片厂家提供的原厂bsp固件库,如STM32的HAL库 | |
test | 存放TencentOS tiny提供的一些测试代码,含内核及上层模块示例及测试代码 | |
tools | 存放TencentOS tiny提供的工具,小程序,配置工具等 | |
简单提一下我们的重点文件夹:
*arch: TencentOS tiny是软件,单片机是硬件,为了使TencentOS tiny运行在单片机上面,TencentOS tiny和单片机必须关联在一起,那么如何关联呢?还是要通过代码来关联,这部分关联的文件叫接口文件,通常由汇编语言和C语言联合编写。这些接口文件都是跟硬件密切相关的,不同的硬件接口文件是不一样的,但都大同小异。
*TencentOS tiny在arch\arm\arm-v6m目录中存放了cortex m0内核的单片机的接口文件,在arch\arm\arm-v7m目录中存放了cortex m3、m4和m7内核的单片机的接口文件,以及一些通用的接口文件,基于这些内核的mcu都可以使用里面的接口文件。
*kernel:kernel是TencentOS tiny内核核心源码,它的重要性我也不用多说,毕竟整个内核就是由这里面的文件组成,而其他文件夹都是基于内核的组件。
*config:board就是TencentOS tiny为一些常用开发板开发的demo文件夹,其内有各个工程的配置文件,选一个与移植芯片最相机的开发板,找到它的配置文件tos_config.h,比如我们可以选择:TencentOS-tiny\board\STM32F103_SIM800A\TOS-CONFIG
这个配置文件很重要,后续在移植工程时,我们需要对这个配置文件进行修改,这样子可以裁剪TencentOS tiny的功能,得到最适合的工程配置。
这个congfig就是FreeRTOS一样,可以根据自己需求配置
所以我们需要以上分析的这三个文件
二、开始移植
第一步:创建文件夹TencentOS-tiny,将所有下载代码都添加进来
第二步:先创建文件夹TOS-CONFIG,用于存放TencentOS tiny的配置头文件,头文件后面在放
第三步:创建文件夹,添加文件
第四步:添加源码
(1)tos/kemel添加,从该路径TencentOS-tiny\kernel\core将源码添加进来
(2)添加arch平台代码
*1)从该路径TencentOS-tiny\arch\arm\arm-v7m\common将tos_cpu.c添加进来
tos_cpu.c是TencentOS tiny 的CPU适配文件,包括堆栈初始化,中断适配等,如果您的芯片是ARM Cortex M核,该文件可以不做改动,M0、M3 、M4、M7是通用的,其他IP核需要重新适配;
*2)从该路径TencentOS-tiny\arch\arm\arm-v7m\cortex-m3\armcc将port_c.c、port_s.S添加进来
port_s.S 文件是TencentOS tiny的任务调度汇编代码,主要做弹栈压栈等处理的,
port_c.c适配systick等
我们这边STM32F103是M3内核所以路径选择的是cortex-m3
这两个文件 每个IP核和编译器都是不一样的,如果您的芯片是ARM Cortex M核,我们都已经适配好
*3)从该路径TencentOS-tiny\board\TencentOS_Tiny_EVB_STM32WL\TOS_CONFIG将tos_config.h复制起来,这里随便选了一个例程的tos_config.h复制,我们把它复制到先前创建的TOS-CONFIG文件夹里面,后面在做修改。
因为这个配置文件很重要,后续在移植工程时,我们需要对这个配置文件进行修改,这样子可以裁剪TencentOS tiny的功能,得到最适合的工程配置
*4)从该路径TencentOS-tiny\osal\cmsis_os将cmsis_os.c添加进来
cmsis os是TencentOS tiny为了兼容cmsis标准而适配的OS抽象层,可以简化大家将业务从其他RTOS迁移到TencentOS tiny的工作量。
第五步:添加TencentOS tiny头文件目录
第六步:修改config.h
修改前:
#ifndef _TOS_CONFIG_H_
#define _TOS_CONFIG_H_
#include "stm32wlxx.h"
#define TOS_CFG_TASK_PRIO_MAX 10u// 配置TencentOS tiny默认支持的最大优先级数量
#define TOS_CFG_ROUND_ROBIN_EN 0u// 配置TencentOS tiny的内核是否开启时间片轮转
#define TOS_CFG_OBJECT_VERIFY_EN 1u// 配置TencentOS tiny是否校验指针合法
#define TOS_CFG_TASK_DYNAMIC_CREATE_EN 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_DEFAULT_POOL_EN 1u// TencentOS tiny 默认动态内存池功能宏
#define TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE 0x8000// 配置TencentOS tiny默认动态内存池大小
#define TOS_CFG_MUTEX_EN 1u// 配置TencentOS tiny是否开启互斥锁模块
#define TOS_CFG_MESSAGE_QUEUE_EN 1u// 配置TencentOS tiny是否开启消息队列模块
#define TOS_CFG_MAIL_QUEUE_EN 1u// 配置TencentOS tiny是否开启消息邮箱模块
#define TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN 1u// 配置TencentOS tiny是否开启优先级消息队列模块
#define TOS_CFG_PRIORITY_MAIL_QUEUE_EN 1u// 配置TencentOS tiny是否开启优先级消息邮箱模块
#define TOS_CFG_TIMER_EN 1u// 配置TencentOS tiny是否开启软件定时器模块
#define TOS_CFG_PWR_MGR_EN 0u// 配置TencentOS tiny是否开启外设电源管理模块
#define TOS_CFG_TICKLESS_EN 0u// 配置Tickless 低功耗模块开关
#define TOS_CFG_SEM_EN 1u// 配置TencentOS tiny是否开启信号量模块
#define TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN 1u// 配置TencentOS tiny是否开启任务栈深度检测
#define TOS_CFG_FAULT_BACKTRACE_EN 0u// 配置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
修改后:
#ifndef _TOS_CONFIG_H_
#define _TOS_CONFIG_H_
#include "stm32f10x.h"
#define TOS_CFG_TASK_PRIO_MAX 10u// 配置TencentOS tiny默认支持的最大优先级数量
#define TOS_CFG_ROUND_ROBIN_EN 1u// 配置TencentOS tiny的内核是否开启时间片轮转
#define TOS_CFG_OBJECT_VERIFY_EN 1u// 配置TencentOS tiny是否校验指针合法
#define TOS_CFG_TASK_DYNAMIC_CREATE_EN 1u// 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_DEFAULT_POOL_EN 1u// TencentOS tiny 默认动态内存池功能宏
#define TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE 0x200// 配置TencentOS tiny默认动态内存池大小
#define TOS_CFG_MUTEX_EN 1u// 配置TencentOS tiny是否开启互斥锁模块
#define TOS_CFG_MESSAGE_QUEUE_EN 1u// 配置TencentOS tiny是否开启消息队列模块
#define TOS_CFG_MAIL_QUEUE_EN 1u// 配置TencentOS tiny是否开启消息邮箱模块
#define TOS_CFG_PRIORITY_MESSAGE_QUEUE_EN 1u// 配置TencentOS tiny是否开启优先级消息队列模块
#define TOS_CFG_PRIORITY_MAIL_QUEUE_EN 1u// 配置TencentOS tiny是否开启优先级消息邮箱模块
#define TOS_CFG_TIMER_EN 1u// 配置TencentOS tiny是否开启软件定时器模块
#define TOS_CFG_PWR_MGR_EN 0u// 配置TencentOS tiny是否开启外设电源管理模块
#define TOS_CFG_TICKLESS_EN 0u// 配置Tickless 低功耗模块开关
#define TOS_CFG_SEM_EN 1u// 配置TencentOS tiny是否开启信号量模块
#define TOS_CFG_TASK_STACK_DRAUGHT_DEPTH_DETACT_EN 1u// 配置TencentOS tiny是否开启任务栈深度检测
#define TOS_CFG_FAULT_BACKTRACE_EN 0u// 配置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
第七步:编译下
有重复定义的函数,注释PendSV_Handler()函数
鉴于TencentOS tiny已经处理好PendSV与SysTick中断了,就不需要用户自己去处理,所以要在中断相关的源文件(stm32f10x_it.c文件)中注释(或者删除)PendSV_Handler()函数。
另外我们也把SysTick_Handler屏蔽掉,因为我们在另外建的BSP_SysTick_放置了SysTick_Handler
添加___weak关键字添加上去,变成弱定义即可
在编译下,编译通过!
第八步:编写SysTick_Handler()函数
SysTick中断服务函数是一个非常重要的函数,TencentOS tiny所有跟时间相关的事情都在里面处理,SysTick就是TencentOS tiny的一个心跳时钟,驱动着TencentOS tiny的运行,就像人的心跳一样,假如没有心跳,我们就相当于“挂掉”,同样的,TencentOS tiny没有了心跳,那么它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现一个TencentOS tiny的心跳时钟。
在SysTick_Handler函数中添加TencentOS tiny的调度处理函数,然后在BSP_Systick.c文件中包含 tos_k.h 头文件
#include "BSP_SysTick.h"
#include "tos_k.h"
//初始化延迟函数
//当使用OS的时候,此函数会初始化OS的时钟节拍
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void BSP_SysTickInit()
{
u32 reload;//装载次数
/*滴答定时频率配置72MHZ,而心跳中断需要1ms中断一次,那么装载计数值->X = 0.0001s/(1/72000000) = 72000次 */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK = SystemCoreClock = 72000000HZ
reload=SystemCoreClock/1000000; //reload = 72
reload*=1000000/TOS_CFG_CPU_TICK_PER_SECOND; //根据configTICK_RATE_HZ设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //滴答定时频率72MHZ,72000*(1/72000000) = 1ms
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
void SysTick_Handler(void)//1ms中断一次
{
if (tos_knl_is_running())
{
tos_knl_irq_enter();
tos_tick_handler();
tos_knl_irq_leave();
}
}
三、创建TencentOS tiny任务,测试移植结果
第一步:在mian.c 中添加TencentOS tiny 头文件,编写任务函数
#include "main.h"
#include "bsp_Stm32fx.h"
/* TencentOS头文件 */
#include "cmsis_os.h"
//task1
#define TASK1_STK_SIZE 256//任务栈大小
void task1(void *pdata);//任务函数
osThreadDef(task1, osPriorityNormal, 1, TASK1_STK_SIZE);//创建TASK1任务
//task2
#define TASK2_STK_SIZE 256//任务栈大小
void task2(void *pdata);//任务函数
osThreadDef(task2, osPriorityNormal, 1, TASK2_STK_SIZE);//创建TASK2任务
void BoardInit(void)
{
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//裸机工程配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//一旦一只哦操作系统需需要设置为中断分组4
BSP_SysTickInit();
BSP_GpioInit();
BSP_UartInit(115200);
}
int main(void)
{
BoardInit(); BSP_SetLed0(0); BSP_SetLed0(0);
printf("Welcome to TencentOS tiny,CoreClock:%d\r\n",SystemCoreClock);
osKernelInitialize(); //TOS Tiny kernel initialize
osThreadCreate(osThread(task1), NULL);// Create task1
osThreadCreate(osThread(task2), NULL);// Create task2
osKernelStart();//Start TOS Tiny
}
void task1(void *pdata)
{
int count = 1;
while(1)
{
printf("\r\nHello world!\r\n###This is task1 ,count is %d \r\n", count++);
BSP_SetLed0(2);
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++);
BSP_SetLed1(2);
osDelay(1000);
}
}
// NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);切记,这个中断分组一定要设置成组4,设置成组4,总共变成了0-15级抢占优先级,就没有响应优先级了。
原来裸机工程默认将STM32的优先级分组配置为2,即4个抢占优先级、4个亚优先级。而freeRTOS的中断配置没有处理亚优先级这种情况,即要求全部为抢占优先级。所以STM32移植freeRTOS时,应将中断优先级配置为4,即16个抢占优先级。
第二步:编译工程
下载进去,查看现象
串口数据正常输出,证明两个任务都有跑,
但是确发现板载的LED灯没有闪烁运行,这个很奇怪
第三步:查找LED驱动问题
经过查找,我发现只有把 USE Micro LIB勾选起来,LED灯才会闪烁,很奇怪。
从理论上分析,USE Micro LIB勾选起来只是牺牲运行速度从而达到减小代码所占的空间跟这个GPIO没关系才对啊,我找不到答案。目前带线就是这个问题
附录:
针对第三步的问题,如果有小伙伴知道请评论区告诉我下,感谢,此次移植到此结束啦!
工程文件:
链接:https://pan.baidu.com/s/1IscZGbKQpbXfAQ3YwfuGdw
提取码:9ohw