1、TMOS简介
TMOS是沁恒微电子针对蓝牙协议栈开发的“操作系统”,是简化版的OSAL(Operating System Abstraction Layer),即操作系统抽象层,一种以实现多任务为核心的系统资源管理机制。简单而言,TMOS实现了类似操作系统的某些功能,但并不能称之为真正意义上的操作系统。
2、TMOS工作机制分析
TMOS是通过时间片轮询的方式实现多任务调度运行,实际上每次只有一个任务运行。系统时钟来源于芯片RTC,单位为625us。
用户通过注册**任务(Task)将自定义的事件(Event)**添加到TMOS的任务链表中,由TMOS进行调度运行。
每个Task注册后分配一个ID;每个Task最多包含16个Event,其中包括1个消息事件(0x8000)和15个自定义事件,采用BitMap的方式定义事件标志,如:(0x0001、0x0002、0x0004……0x8000)。
Event事件标志位,为1则运行,为0则不运行。
bit0 | bit1 | bit2 | bit3 | bit4 | bit5 | bit6 | bit7 | bit8 | bit9 | bit10 | bit11 | bit12 | bit13 | bit14 | bit15 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 | 0/1 |
TMOS任务链表如下:
TMOS循环查询任务链表,根据任务ID确定优先级,越小越高。每个任务运行完一个事件后便通过异或的方式清除已运行的事件,同时return未运行的事件标志,接着运行下一个任务;当任务调度系统运行一遍后,再次回来运行任务链表头的一个事件,如此循环下去。
TMOS调度机制如下:
3、常用API分析
//注册任务,传入任务事件回调函数,返回任务ID。
tmosTaskID TMOS_ProcessEventRegister( pTaskEventHandlerFn eventCb );
//立即启动taskID任务中对应的event事件,事件只执行一次
bStatus_t tmos_set_event( tmosTaskID taskID, tmosEvents event );
//定时time*625us后启动taskID任务中对应的event事件,事件只执行一次
bStatus_t tmos_start_task( tmosTaskID taskID, tmosEvents event, tmosTimer time );
//停止一个定时事件
bStatus_t tmos_stop_task( tmosTaskID taskID, tmosEvents event );
//清理一个已经超时的event事件,不能在自己的event函数内执行
bStatus_t tmos_clear_event( tmosTaskID taskID, tmosEvents event );
//开始一个定时事件,不断的执行,除非运行tmos_stop_task关掉,
bStatus_t tmos_start_reload_task( tmosTaskID taskID, tmosEvents event, tmosTimer time );
//获取对应taskID 和event 的最后一个周期时长,返回0是没有找到
tmosTimer tmos_get_task_timer( tmosTaskID taskID, tmosEvents event );
//TMOS时钟初始化
bStatus_t TMOS_TimerInit( pfnGetSysClock fnGetClock );
//返回tmos系统运行时长,单位为625us,如1600=1s
uint32_t TMOS_GetSystemClock( void );
//tmos的系统处理函数,需要不断在主函数中运行
void TMOS_SystemProcess( void );
/**************消息相关*************/
//发送消息函数,参数为消息想要发送到哪一层的taskID以及消息指针。当调用此函数时,对应参数taskID层的消息事件将会立即置1生效
bStatus_t tmos_msg_send( tmosTaskID taskID, uint8_t *msg_ptr );
// 接收消息函数,参数为需要接收任务taskID的消息。
uint8_t *tmos_msg_receive( tmosTaskID taskID );
//申请内存函数,发送消息之前需要先给消息申请内存空间。如果返回为NULL,则申请失败
uint8_t *tmos_msg_allocate( uint16_t len );
//释放消息占用内存的函数,处理完消息后需要释放内存占用。
bStatus_t tmos_msg_deallocate( uint8_t *msg_ptr );
/******TMOS定义的函数,较C库函数节省内存,功能类似******/
uint32_t tmos_rand( void ); // pseudo-random number
bool tmos_memcmp( const void *src1, const void *src2, uint32_t len ); // TRUE - same, FALSE - different
bool tmos_isbufset( uint8_t *buf, uint8_t val, uint32_t len ); // TRUE if all "val",FALSE otherwise
uint32_t tmos_strlen( char *pString );
uint32_t tmos_memset( void * pDst, uint8_t Value, uint32_t len );
void tmos_memcpy( void *dst, const void *src, uint32_t len ); // Generic memory copy.
4、TMOS 使用Demo
4.1 任务管理
4.1.1 示例代码
tmos_demo_task.h
#ifndef _TMOS_DEMO_TASK_H_
#dedine _TMOS_DEMO_TASK_H_
#include "CH57x_common.h"
#include "CH57xBLE_LIB.H"
#include "stdint.h"
#define DEMO_TASK_TMOS_EVT_TEST_1 (0x0001<<0)
#define DEMO_TASK_TMOS_EVT_TEST_2 (0x0001<<1)
#define DEMO_TASK_TMOS_EVT_TEST_3 (0x0001<<2)
#define DEMO_TASK_TMOS_EVT_TEST_4 (0x0001<<3)
#define DEMO_TASK_TMOS_EVT_TEST_5 (0x0001<<4)
void demo_task_init(void);
#endif
tmos_demo_task.C
#include "tmos_demo_task.h"
//存储 当前task id 的全局变量
tmosTaskID demo_task_id = INVALID_TASK_ID;
//task的event处理回调函数,需要在注册task时候,传进去
static uint16_t demo_task_process_event( uint8_t task_id, uint16_t events )
{
//event 处理
if(events & DEMO_TASK_TMOS_EVT_TEST_1)
{
PRINT("DEMO_TASK_TMOS_EVT_TEST_1 evt test \r\n");
return (events ^ DEMO_TASK_TMOS_EVT_TEST_1); //异或的方式清除该事件运行标志,并返回未运行的事件标志
}
//event 处理
if(events & DEMO_TASK_TMOS_EVT_TEST_2)
{
tmos_start_task(demo_task_id,DEMO_TASK_TMOS_EVT_TEST_3,1600);
PRINT("DEMO_TASK_TMOS_EVT_TEST_2 evt test \r\n");
return (events ^ DEMO_TASK_TMOS_EVT_TEST_2);
}
//event 处理
if(events & DEMO_TASK_TMOS_EVT_TEST_3)
{
tmos_start_task(demo_task_id,DEMO_TASK_TMOS_EVT_TEST_3,1600);
PRINT("DEMO_TASK_TMOS_EVT_TEST_3 evt test \r\n");
return (events ^ DEMO_TASK_TMOS_EVT_TEST_3);
}
// Discard unknown events
return 0;
}
//初始化task
//包括注册函数,可以注册后去开启event
void demo_task_init( void )
{
//注册task id,同事把该task的event处理函数传进去
demo_task_id = TMOS_ProcessEventRegister( demo_task_process_event );
//立即开始一个event
tmos_set_event(demo_task_id,DEMO_TASK_TMOS_EVT_TEST_1);
//开始一个定时event,1s后产生,当前语句只会产生一次event
//可以在event产生后去开启event,可以是别的task的,也可以是当前task的event
tmos_start_task(demo_task_id,DEMO_TASK_TMOS_EVT_TEST_2,1600);
}
4.1.2 使用方法
-
把上面"tmos_demo_task.c" 和 “tmos_demo_task.h” 两个文件加到工程里面
-
在main函数里的while(1)之前上面调用函数 demo_task_init();
4.1.3 运行现象
- 芯片先运行
PRINT("DEMO_TASK_TMOS_EVT_TEST_1 evt test \r\n");
- 一秒后运行
PRINT("DEMO_TASK_TMOS_EVT_TEST_2 evt test \r\n");
- 然后按照1s一次不断运行
PRINT("DEMO_TASK_TMOS_EVT_TEST_3 evt test \r\n");
4.2 消息管理
4.2.1 示例代码
tmos_message_demo_message.h
#ifndef _TMOS_DEMO_MESSAGE_H_
#define _TMOS_DEMO_MESSAGE_H_
#include "CH57x_common.h"
#include "CH57xBLE_LIB.H"
#include "stdint.h"
// Test Task Events
#define TEST_EVENT_1 (0x0001<<0)
#define TEST_EVENT_2 (0x0001<<1)
void TMOS_init(void);
uint16_t test_process_event_1(uint8_t taskID,uint16_t event);
uint16_t test_process_event_2(uint8_t taskID,uint16_t event);
#endif
tmos_demo_message.c
#include "tmos_demo_message.h"
#define MSG_EVENT_TEST 0x10
uint8_t TestTaskID1 = INVALID_TASK_ID;
uint8_t TestTaskID2 = INVALID_TASK_ID;
void TMOS_init(void)
{
TestTaskID1 = TMOS_ProcessEventRegister(test_process_event_1);
TestTaskID2 = TMOS_ProcessEventRegister(test_process_event_2);
tmos_start_task( TestTaskID1, TEST_EVENT_1, 1600 );
tmos_start_task( TestTaskID2, TEST_EVENT_2, 1600 ); //延时启动 TEST_EVENT_2事件 延时时间:1600*625us
}
//消息处理的函数
static void demo_task_process_TMOSMsg( tmos_event_hdr_t *pMsg )
{
switch ( pMsg->event )
{
case MSG_EVENT_TEST:
PRINT("pMsg->event=%x,pMsg->status=%x\r\n",pMsg->event,pMsg->status);
break;
default:
PRINT("pMsg->event %04x\r\n",pMsg->event);
break;
}
}
uint16_t test_process_event_1(uint8_t taskID,uint16_t events)
{
tmos_event_hdr_t *test_message;
if ( events & TEST_EVENT_1 )
{
PRINT("Run TEST_EVENT_1 in task 1\r\n");
//申请消息内存空间
test_message =(tmos_event_hdr_t*) tmos_msg_allocate(sizeof(tmos_event_hdr_t));
if(test_message)
{
test_message->event = MSG_EVENT_TEST;
test_message->status = 0x55;
//发送消息至 TestTaskID2 任务
tmos_msg_send(TestTaskID2 ,(uint8_t *)test_message);
}
tmos_start_task( taskID, TEST_EVENT_1, 1600 );
return ( events ^ TEST_EVENT_1 );
}
return 0;
}
uint16_t test_process_event_2(uint8_t taskID,uint16_t events)
{
//消息处理
if ( events & SYS_EVENT_MSG )
{
uint8_t *pMsg;
if ( (pMsg = tmos_msg_receive( taskID )) != NULL )
{
//消息处理
demo_task_process_TMOSMsg( (tmos_event_hdr_t *)pMsg );
//释放消息空间
tmos_msg_deallocate( pMsg );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
//事件处理
if ( events & TEST_EVENT_2 )
{
PRINT("Run TEST_EVENT_2 in task 2\r\n");
tmos_start_task( taskID, TEST_EVENT_2, 1600 );
return ( events ^ TEST_EVENT_2 );
}
return 0;
}
4.2.2 使用方法
- 把上面"tmos_demo_message.c" 和 “tmos_demo_message.h” 两个文件加到工程里面
- 在main函数里的while(1)之前上面调用函数 demo_task_init();
4.2.3 运行现象
间隔1S,重复运行:
PRINT("Run TEST_EVENT_1 in task 1\r\n");
PRINT("Run TEST_EVENT_2 in task 2\r\n");
PRINT("pMsg->event=%x,pMsg->status=%x\r\n",pMsg->event,pMsg->status);
5、TMOS使用注意事项
- 禁止在中断中调用任务调度函数
- 如果使用了ble,建议不要在单个任务中执行超过连接间隔一半时长的任务,否则将影响蓝牙通讯
- 在事件生效执行的代码中调用
tmos_start_task
函数时,延时时间以当前事件生效时间点为基准偏移,所以对调用延时执行函数在生效执行的代码中摆放的位置没有要求。 - 任务存在优先级,根据在xxx_ProcessEvent函数中判断的先后顺序决定,同时生效的任务,先执行先判断,后执行后判断。注意,执行完先判断的事件任务后,要等到任务调度系统轮巡一遍后,才会执行后判断的事件任务。
- 事件名按位定义,每一层taskID最多包含1个消息事件和15个任务事件(共16位)