CH579/CH57x 的TMOS系統使用


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 函數內執行
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 );

bStatus_t tmos_msg_send( tmosTaskID taskID, uint8_t *msg_ptr );
uint8_t   *tmos_msg_receive( tmosTaskID taskID );

uint8_t   *tmos_msg_allocate( uint16_t len );
bStatus_t tmos_msg_deallocate( uint8_t *msg_ptr );

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);

[高級使用]

消息管理

消息是一個帶有數據的事件,用於協議棧各層之間傳遞數據,支持同時添加多個消息。

//申請內存函數,發送消息之前需要先給消息申請內存空間。如果返回為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 );


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM