iot-fan

联系: iotfan123#163.com
注意:
1,本博客之内容来源于网上收集以及相关技术人员提供,如果有侵犯到您的权益,请电邮我沟通;
2,本博客之内容乃分享,交流,学习,研究之目的,作者不对内容的真实性,有效性,及时性负责,也不对因本博客的任何内容导致的任何后果负责;
3,本博客之内容禁止转发到CSDN网站,转到别的网站请保留出处.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  39 随笔 :: 0 文章 :: 32 评论 :: 14万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

CH57x 的蓝牙中,为了方便其协议栈自身的管理,以及用户的使用,使用一个非常轻量级的操作系统"TMOS",

TMOS 特点:

  • TMOS 实际上就是OSAL的简化版本,这是一个轮询实现的系统,不支持抢占任务;
  • TMOS 系统时钟单位为 625us, 时钟来源为RTC
  • 多任务管理方式实际上只有一个任务在运行
  • 可以使用任务调度的策略将多个任务进行调度,每个任务占用一定的时间,所有的任务通过时间分片的方式处理。

[一些常用的API说明]

//tmosTaskID taskID
//以不同的taskid 来区分不同的任务,越小优先级越高
//tmosEvents event
//每个task 下拥有的event,16bit,每bit代表一个event,对于同一个task,一共16个event,其中0x8000为系统使用,剩下15个为用户使用
//注册task id,一般用于注册任务时候,首先执行的
tmosTaskID TMOS_ProcessEventRegister( pTaskEventHandlerFn eventCb );
//设置一个event,,根据taskid 和event 来决定具体的事件
bStatus_t tmos_set_event( tmosTaskID taskID, tmosEvents event );
//清理一个已经超时的event,不能在自己的event 函数内执行
bStatus_t tmos_clear_event( tmosTaskID taskID, tmosEvents event );
//开始一个定时事件,只执行一次,
//tmosTimer具体是 1600 = 1s
bStatus_t tmos_start_task( tmosTaskID taskID, tmosEvents event, tmosTimer time );
//开始一个定时事件,不断的执行,除非运行tmos_stop_task关掉,
//tmosTimer具体是 1600 = 1s
bStatus_t tmos_start_reload_task( tmosTaskID taskID, tmosEvents event, tmosTimer time );
//停止一个定时事件
bStatus_t tmos_stop_task( tmosTaskID taskID, tmosEvents event );
//获取对应taskid 和event 的最后一个周期时常,返回0是没有找到.
tmosTimer tmos_get_task_timer( tmosTaskID taskID, tmosEvents event );
//发送tmos消息
bStatus_t tmos_msg_send( tmosTaskID taskID, uint8_t *msg_ptr );
//接收tmos消息
uint8_t *tmos_msg_receive( tmosTaskID taskID );
//申请动态内存
uint8_t *tmos_msg_allocate( uint16_t len );
//释放动态内存
bStatus_t tmos_msg_deallocate( uint8_t *msg_ptr );
//获取tmos系统堆的剩余大小
uint32_t tmos_memory_getlen(void);
uint8_t tmos_snv_read( uint8_t id, uint8_t len, void *pBuf);
//tmos的系统处理函数,需要不断在主函数中运行
void TMOS_SystemProcess( void );
//返回tmos系统运行的clock,1600=1s
uint32_t TMOS_GetSystemClock( void );
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.

注册任务

开始定时事件

[示例代码]

源码:

"tmos_demo_task.h"

#include <stdint.h>
#include "CH57x_common.h"
#include "CH57xBLE_LIB.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);
uint8 tmos_demo_task_send_msg( uint8_t task_id,uint8_t *data ,uint16_t length ) ;

"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 ) {
if ( events & SYS_EVENT_MSG ) {
uint8_t *pMsg;
if ( (pMsg = tmos_msg_receive( demo_task_id )) != NULL ) {
//We can’t get the length of the received message here,
//Length information is required when using variable length messages
PRINT("revice data:");
for(uint8_t i=0;i<8;i++){
PRINT("%02x ",pMsg[i]);
}
PRINT("\r\n");
// Release the TMOS message
tmos_msg_deallocate( pMsg );
}
// return unprocessed events
return (events ^ SYS_EVENT_MSG);
}
//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");
uint8_t test_data[] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88};
PRINT("tmos send fixed length data test\r\n");
tmos_demo_task_send_msg(demo_task_id,test_data,8);
return (events ^ DEMO_TASK_TMOS_EVT_TEST_3);
}
// Discard unknown events
return 0;
}
uint8 tmos_demo_task_send_msg( uint8_t task_id,uint8_t *data ,uint16_t length ) {
uint8_t *p_data;
if ( task_id != TASK_NO_TASK ) {
// Send the address to the task
p_data = tmos_msg_allocate(length);
if ( p_data ) {
tmos_memcpy(p_data,data,length);
tmos_msg_send( task_id, p_data );
return ( SUCCESS );
}
}
return ( FAILURE );
}
//初始化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);
}

使用:

  1. 把上面"tmos_demo_task.c" 和 "tmos_demo_task.h" 两个文件加到工程里面
  2. 在main函数里的while(1)之前上面调用 demo_task_init();

现象:

  1. 芯片先运行 PRINT("DEMO_TASK_TMOS_EVT_TEST_1 evt test \r\n");
  2. 一秒后运行 PRINT("DEMO_TASK_TMOS_EVT_TEST_2 evt test \r\n");
  3. 然后按照1s一次不断运行 PRINT("DEMO_TASK_TMOS_EVT_TEST_3 evt test \r\n");
    并且对本task发送自定义固定长度消息,然后task里面获取到系统消息,并且打印出来

[注意事项]

任务调度函数使用注意事项:

  1. 禁止在中断中调用,包括操作任务和传递消息
  2. 如果使用了ble,建议不要在单个任务中执行超过连接间隔一半时长的任务,否则将影响蓝牙通讯
  3. CH579/573系列的芯片上,受限于RAM的的大小,TMOS的CNT更新是在TMOS_SystemProcess()里面完成的,这意味着:
    • 如果在事件生效执行的代码中调用延时执行函数时,延时时间以当前事件生效时间点为基准偏移,所以对调用延时执行函数在生效执行的代码中摆放的位置没有要求。
    • 如果启用睡眠,在睡眠醒来后立刻调用tmos_start_task之类的函数,会因tmos的cnt值还是睡眠之前的值并没有更新, 导致相对事件误差比较大(比如已经睡眠了1s,而tmos_start一个500ms的事件,这样会导致tmos认为已经过去了1s,而不是刚刚开始这个task,所以会立刻执行这个task).
  4. 任务存在优先级,根据在xxx_ProcessEvent函数中判断的先后顺序决定,同时生效的任务,先执行先判断,后执行后判断。注意,执行完先判断的事件任务后,要等到任务调度系统轮巡一遍后,才会执行后判断的事件任务。
  5. 事件名按位定义,每一层taskID最多包含1个消息事件和15个任务事件(共16位)

[问题排查]

1. tmos_start_task 事件却没有运行?

  • 如果tmos_start_task返回值是false,则代表调用失败
    • 调用失败可能是tmos的ram不够了比如真的比较少,或者申请的ram 没有释放;
    • 可能是task id 没有申请
  • 如果是调用成功,是不是其他地方给stop了
  • 或者对应的event之前,被别的event给处理了,比如return (events ^ xxxx_EVT);

2. 获取tmos系统堆的剩余内存大小

调用下面函数既可以,可以写个周期事件,定时监控

uint32_t tmos_memory_getlen(void);

[高级使用]

消息管理

消息是一个带有数据的事件,用于协议栈各层之间传递数据,支持同时添加多个消息。

//申请内存函数,发送消息之前需要先给消息申请内存空间。如果返回为NULL,则申请失败。
extern u8 *tmos_msg_allocate( u16 len );
//发送消息函数,参数为消息想要发送到哪一层的taskID以及消息指针。当调用此函数时,对应参数taskID层的消息事件将会立即置1生效
extern bStatus_t tmos_msg_send( tmosTaskID taskID, u8 *msg_ptr );
//接收消息函数,参数为想要接收哪一层的taskID。
//这里我们并得不到消息的长度,如果是变长信息,需要在信息里面有消息长度指示,比如第一个字节为长度
extern u8 *tmos_msg_receive( tmosTaskID taskID );
//释放消息占用内存的函数,处理完消息后需要释放内存占用。
extern bStatus_t tmos_msg_deallocate( u8 *msg_ptr );
//获取tmos系统堆的剩余大小
uint32_t tmos_memory_getlen(void);
posted on   iot-fan  阅读(9167)  评论(7编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示