【WCH蓝牙系列芯片】-基于CH32V208开发板—TMOS系统说明(一)
-------------------------------------------------------------------------------------------------------------------------------------
在CH32V208芯片中使用蓝牙过程中,针对蓝牙协议栈开发的“操作系统”——TMOS系统,这是一种简化版的OSAL嵌入式系统。由于蓝牙使用过程中,需要蓝牙与多个设备连接并实现多功能和多任务,这就会导致了调度问题。因此,TMOS被作为一个操作系统抽象层,使用TMOS处理多事件和多任务切换,将事件和任务对应起来。
TMOS系统任务管理——通过时间片轮询的方式进行多个任务的调度,但是实际上每次只有一个任务运行。但是可以使用任务调度的策略将多个任务进行调度,每个任务占用一定的时间,所有的任务通过时间分片的方式处理。
TMOS 系统时钟单位为 625us,来源于芯片RTC。
用户通过注册任务(Task)将自定义的事件(Event)添加到TMOS的任务链表中,由TMOS进行调度运行。在任务初始化完成后,tmos在循环中轮询任务事件,事件的标志存储在16位变量中,每一位在同一个任务中对应一个唯一的事件。标志位为1表示该位对应的事件运行,为0表示不运行。
每个Task用户最多可以定义15个事件,其中0x8000为系统预留的SYS_EVENT_MSG事件,是系统消息传递事件,不可被定义。根据任务ID确定优先级,优先级越低,任务越先运行。每个任务运行完一个事件后便通过异或的方式清除已运行的事件,同时return未运行的事件标志,接着运行下一个任务;当任务调度系统运行一遍后,再次回来运行任务链表头的一个事件,如此循环下去。
在CH32V208的从机例程中,创建一个1s点灯的TMOS任务,进行说明如何使用TMOS。
1、在peripheral.c中创建Peripheral_TaskID,并存储当前Peripheral_TaskID的全局变量
2、定义一个点灯事件标志
3、在Peripheral_Init从机初始化中,注册任务事件,将事件的回调函数注册到TMOS中。
4、在任务事件处理函数中,添加一个1S的LED闪烁的事件。
static void LED_init (void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA、GPIOC时钟 GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2; //(|按位或运算) GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度最大50MHZ GPIO_Init(GPIOA,&GPIO_InitStruct); //调用GPIOA端口 GPIO_ResetBits(GPIOA, GPIO_Pin_1); GPIO_ResetBits(GPIOA, GPIO_Pin_2); } static void LED_change(void) { static uint8_t LED_State; if(LED_State) { LED_State = 0; GPIO_ResetBits(GPIOA, GPIO_Pin_1); GPIO_ResetBits(GPIOA, GPIO_Pin_2); } else { LED_State = 1; GPIO_SetBits(GPIOA, GPIO_Pin_1); GPIO_SetBits(GPIOA, GPIO_Pin_2); } }
并声明这个两个函数
5、调用事件,在Peripheral_LinkEstablished用于处理连接建立事件的回调函数中,检查是否已经存在连接,
如果已经连接,将会终止当前连接,同时打印一条信息以示连接已达到最大限制。
如果还没有连接,就会执行tmos_start_task 函数设置定时器来触发一些事件,包括周期性事件、参数更新事件和读取 RSSI 事件,还有IS点灯的事件。从而开启点灯的这个事件。
static void Peripheral_LinkEstablished(gapRoleEvent_t *pEvent) { gapEstLinkReqEvent_t *event = (gapEstLinkReqEvent_t *)pEvent; // See if already connected if(peripheralConnList.connHandle != GAP_CONNHANDLE_INIT) { GAPRole_TerminateLink(pEvent->linkCmpl.connectionHandle); PRINT("Connection max...\n"); } else { peripheralConnList.connHandle = event->connectionHandle; peripheralConnList.connInterval = event->connInterval; peripheralConnList.connSlaveLatency = event->connLatency; peripheralConnList.connTimeout = event->connTimeout; peripheralMTU = ATT_MTU_SIZE; // Set timer for periodic event tmos_start_task(Peripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD); // Set timer for param update event tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY); // Start read rssi tmos_start_task(Peripheral_TaskID, SBP_READ_RSSI_EVT, SBP_READ_RSSI_EVT_PERIOD); // 添加一个点灯的TMOS事件 tmos_start_task(Peripheral_TaskID, SBP_LED_EVT, 1600); //1600*0.625uS = 1S PRINT("Conn %x - Int %x \n", event->connectionHandle, event->connInterval); } }
这样通过手机连接蓝牙后,可以看到LED1和LED2就会以1s的时间进行闪烁。
在TMOS使用的注意事项:
1. 禁止在中断中调用TMOS函数
2. 使用蓝牙过程中,建议不要在单个任务中执行超过连接间隔一半时长的任务,否则将影响蓝牙通讯
3. 在中断中建议不要执行超过连接间隔一半时长的任务,否则将影响蓝牙通讯
4. 在事件生效执行的代码中调用延时执行函数(tmos_start_task)时,延时时间以当前事件生效时间点为基准偏移,
所以对调用延时执行函数(tmos_start_task)在生效执行的代码中摆放的位置没有要求。
5. 任务存在优先级,根据在xxx_ProcessEvent函数中判断的先后顺序决定,同时生效的任务,先执行先判断,后执行后判断。
注意,执行完先判断的事件任务后,要等到任务调度系统轮巡一遍后,才会执行后判断的事件任务。
6. 事件名按位定义,每一层taskID最多包含1个消息事件和15个任务事件(共16位)