文章目录
1、CMSIS-RTOS2 API 之 事件标志
1.1 函数
osEventFlagsId_t osEventFlagsNew (const osEventFlagsAttr_t *attr)
创建并初始化事件标志对象。更多…
uint32_t osEventFlagsSet (osEventFlagsId_t ef_id, uint32_t flags)
设置指定的事件标志。更多…
uint32_t osEventFlagsClear (osEventFlagsId_t ef_id, uint32_t flags)
清除指定的事件标志。更多…
uint32_t osEventFlagsGet (osEventFlagsId_t ef_id)
获取当前的事件标志。更多…
uint32_t osEventFlagsWait (osEventFlagsId_t ef_id, uint32_t flags, uint32_t options, uint32_t timeout)
等待一个或多个事件标志发出信号。更多…
osStatus_t osEventFlagsDelete (osEventFlagsId_t ef_id)
删除事件标志对象。更多…
const char * osEventFlagsGetName (osEventFlagsId_t ef_id)
获取事件标志对象的名称。更多…
1.2 举例:
led_thread.h
#ifndef __LED_THREAD_H
#define __LED_THREAD_H
#include "stm32f4xx_hal.h"
#include "cmsis_os2.h"
#include "bsp_led.h"
#include "app_main.h"
void led_task (void * arg);
#define led1_event1 1<<0
#define led1_event2 1<<1
#define led1_event3 1<<2
#define led1_event4 1<<3
#define led2_event1 1<<0
#define led2_event2 1<<1
#define led2_event3 1<<2
#define led3_event1 1<<0
#define led3_event2 2<<1
#define led3_event3 3<<2
#endif
osEventFlagsId_t EventFlag_LED1;
osEventFlagsId_t EventFlag_LED2;
osEventFlagsId_t EventFlag_LED3;
static const osEventFlagsAttr_t EventFlagAttr_1 = {
.name = "1_Events",
};
static const osEventFlagsAttr_t EventFlagAttr_2 = {
.name = "2_Events",
};
static const osEventFlagsAttr_t EventFlagAttr_3 = {
.name = "3_Events",
};
EventFlag_LED1 = osEventFlagsNew(&EventFlagAttr_1);
EventFlag_LED2 = osEventFlagsNew(&EventFlagAttr_2);
EventFlag_LED3 = osEventFlagsNew(&EventFlagAttr_3);
void main_task (void * arg)
{
while(1)
{
osDelay(100);
//事件标志1
event_flag=osEventFlagsWait (EventFlag_LED1,led1_event1|led1_event2|led1_event3|led1_event4,osFlagsWaitAny,1);//osWaitForever
if( event_flag== led1_event1)
{
LED_ERR_ON();//获取事件led1_event1,并自动清除标记
}
else if(event_flag== led1_event2)
{
LED_ERR_OFF();//获取事件led1_event2,并自动清除标记
}
else if((event_flag == led1_event3) || (event_flag == led1_event4))
{
LED_ERR_Toggle();//获取事件led1_event1或者led1_event2,并自动清除标记
}
//事件标志2
event_flag=osEventFlagsWait (EventFlag_LED2,led2_event1|led2_event2,osFlagsWaitAll,1);//osWaitForever
if(event_flag == (led2_event2|led2_event1))//同时发生
{
LED_ERR_Toggle();
}
//事件标志3
event_flag=osEventFlagsGet (EventFlag_LED3);
if(event_flag==led3_event1)
{
osDelay(1);//事件led3_event1获取,不清除标记,下次循环依旧进来
}
if(event_flag==led3_event2)
{
osDelay(1);//事件led3_event2获取,不清除标记,下次循环依旧进来
}
if(event_flag==(led3_event2|led3_event1))
{
osEventFlagsClear(EventFlag_LED3,led3_event2|led3_event1);//事件led3_event1,led3_event2获取,并且清除事件标记
}
}
}
void led_task (void * arg)
{
osStatus_t status;
while(1)
{
switch(event_test){
case 0:
osDelay(1);
break;
case 1:
osEventFlagsSet (EventFlag_LED1,led1_event1);//标记1,事件1
event_test=0;
break;
case 2:
osEventFlagsSet (EventFlag_LED1,led1_event2);//标记1,事件2
event_test=0;
break;
case 3:
osEventFlagsSet (EventFlag_LED1,led1_event3);//标记1,事件3
event_test=0;
break;
case 4:
osEventFlagsSet (EventFlag_LED1,led1_event4);//标记1,事件4
event_test=0;
break;
case 5:
osEventFlagsSet (EventFlag_LED2,led2_event1);//标记2,事件1
event_test=0;
break;
case 6:
osEventFlagsSet (EventFlag_LED2,led2_event2);//标记2,事件2
event_test=0;
break;
case 7:
osEventFlagsSet (EventFlag_LED2,led2_event1);//标记3,事件1
event_test=0;
break;
case 8:
osEventFlagsSet (EventFlag_LED2,led2_event2);//标记3,事件2
event_test=0;
break;
}
}
}
2、CMSIS-RTOS2 API 之信号量
2.1 函数
osSemaphoreId_t osSemaphoreNew (uint32_t max_count, uint32_t initial_count, const osSemaphoreAttr_t *attr)
创建并初始化信号量对象。更多…
参数:
[in] max_count 可用令牌的最大数量。
[in] initial_count 可用令牌的初始数量。
[in] attr 信号量属性; NULL:默认值。
返回
信号标识以供其他功能参考,或者在发生错误时使用 NULL
const char * osSemaphoreGetName (osSemaphoreId_t semaphore_id)
获取信号量对象的名称。更多…
osStatus_t osSemaphoreAcquire (osSemaphoreId_t semaphore_id, uint32_t timeout)
如果没有令牌可用,则获取信号量标记或超时。更多…
osStatus_t osSemaphoreRelease (osSemaphoreId_t semaphore_id)
释放一个信号量令牌直到最初的最大数量。更多…
uint32_t osSemaphoreGetCount (osSemaphoreId_t semaphore_id)
获取当前的信号量令牌计数。更多…
osStatus_t osSemaphoreDelete (osSemaphoreId_t semaphore_id)
删除一个信号量对象。更多…
2.2 描述
信号量对象应初始化为可用令牌的最大数量。这个可用资源的数量被指定为 osSemaphoreNew 函数的参数。每次使用 osSemaphoreAcquire(处于可用状态)获得信号量标记时,信号量计数递减。当信号量计数为 0(即耗尽状态)时,不能获得更多的信号量标记。尝试获取信号量标记的线程/ISR 需要等待,直到下一个标记为空。信号量与 osSemaphoreRelease 一起发布,增加信号量计数。
2.3 简单举例
osSemaphoreId_t sem1;
static const osSemaphoreAttr_t semAttr_SEM1 = {
.name = "SEM1",
};
sem1 = osSemaphoreNew(5, 0, &semAttr_SEM1 );
/*----------------------------------------------------------------------------
Wait to acquire a semaphore token from sem1 then flash LED 1
*---------------------------------------------------------------------------*/
__NO_RETURN void led_Thread1 (void *argument) {
for (;;) {
osSemaphoreAcquire(sem1, osWaitForever);
LED_On(1);
osSemaphoreAcquire(sem1, osWaitForever);
LED_Off(1);
}
}
/*----------------------------------------------------------------------------
Flash LED 2 and 'release' a semaphore token to sem1
*---------------------------------------------------------------------------*/
__NO_RETURN void led_Thread2 (void *argument) {
for (;;) {
osSemaphoreRelease(sem1);
LED_On(2);
osDelay(500);
osSemaphoreRelease(sem1);
LED_Off(2);
osDelay(500);
}
}
2.4 运用举例
使用信号量组举例:
//初始化
osSemaphoreId_t RcvSem[8];
for (i=0;i<8;i++)
{
RcvSem[i] = osSemaphoreNew(10,0,NULL);
}
//
/*----------------------------------------------------------------------------
'release' a semaphore token to RcvSem
*---------------------------------------------------------------------------*/
typedef struct
{
uint8_t cmd
uint8_t data[530];
uint16_t length;
}rcvMessage_t;
__NO_RETURN void led_Thread2 (void *argument) {
rcvMessage_t RcvMsg;
for (;;) {
//do something 接收到数据...
switch(RcvMsg.cmd)
{
case 'l':
if (RcvMsg.length==2)
{
k = RcvMsg->data[1];
osSemaphoreRelease(RcvSem[k]);
}
}
}
}
__NO_RETURN void led_Thread1 (void *argument) {
uint8_t sendFailTime = 0;
uint32_t flags;
osStatus_t status;
for (;;) {
status= osMessageQueueGet(ReLoadMsg, &ReloadPoint, NULL, 0U);//等待是否发送数据
if (osOK == status)
{
sendFailTime =0;
while (sendFailTime <3)//没收到回复,重发三次
{
// while (osOK == osSemaphoreAcquire(RcvSem[channel],0x1u));
osEventFlagsSet(photoDataSendEvt_id,0x1);//给发送线程发送数据
while(1)
{
flags = osEventFlagsWait(SendFinishEvt_id,0x1,osFlagsWaitAny,0); //等待发送完成
if (flags == 1)
break;
osDelay(10);
}
sem_status=osSemaphoreAcquire(photoRcvSem[channel],1000);// 1S内,等待回复
if (sem_status == osOK)
{
sendFailTime = 5;
}
sendFailTime ++;
}
}
}
3、CMSIS-RTOS2 API 之 互斥锁管理
互斥量通常在各种各样的操作系统上用于资源管理。MCU上的很多资源能被复用,但是,在同一个时刻上仅仅只有一个线程能访问这些资源(例如通讯(串口,SPI),内存,文件等)。互斥量通常用于保护一些共享资源的访问。在线程上获取互斥量,也必须释放互斥量(释放了互斥量,其他线程才能访问共享资源)。
osMutexId_t sendPhotoMutex;
static const osMutexAttr_t MutexAttr_uartMutex = {
.name = "uartMutex",
};
sendPhotoMutex = osMutexNew(&MutexAttr_uartMutex);
void main_task (void * arg)
{
osStatus_t status;
while(1)
{
osDelay(100);
status=osMutexAcquire(sendPhotoMutex, osWaitForever);
if(status == osOK)
{
//do someting.. 同一个时刻,仅有一个线程能访问的资源
osDelay(1);
}
osMutexRelease(sendPhotoMutex);
}
}
创建互斥锁后,Lock counter :0
获取互斥锁后,Lock counter:1
释放互斥锁后,Lock counter:0
4、内核信息与控制
4.1 uint32_t osKernelGetTickCount ( void )
功能:
Returns:RTOS kernel current tick count.
The function osKernelGetTickCount returns the current RTOS kernel tick count
#include "cmsis_os2.h"
void Thread_1 (void *arg) { // Thread function
uint32_t tick;
tick = osKernelGetTickCount(); // retrieve the number of system ticks
for (;;) {
tick += 1000; // delay 1000 ticks periodically
osDelayUntil(tick);
// ...
}
}
//500ms或者200ms更新
//状态更新
void packPartStatus(unsigned char cmd)
{
if(msg_StautsAll.fb_epos.work_status==Running)
{
SendStatusPeriod = 500;
}
else
{
SendStatusPeriod = 2000;
}
if ((osKernelGetTickCount()-refreshTick)>SendStatusPeriod)
{
updata_status(cmd,temp_add);
refreshTick = osKernelGetTickCount();
sendStatusCount ++;
}
}
4.2 uint32_t osKernelGetTickFreq (void )
功能:返回系统内核频率,一般是1000HZ,1mS
4.3 uint32_t osKernelGetSysTimerFreq (void )
功能:返回RTOS内核系统计数器时钟频率(我使用的是SysTick时钟180M)
4.4 uint32_t osKernelGetSysTimerCount (void )
功能:返回系统内核时钟计数值
void Thread_1 (void *arg) { // Thread function
uint32_t TimerFreq=0;
uint32_t TimerCounter1_last=0;
uint32_t TimerCounter1_next=0;
uint32_t TimerCounter2_last=0;
uint32_t TimerCounter2_next=0;
TimerFreq= osKernelGetSysTimerFreq(); //返回值为:180000000(我设置系统时钟 SYSCLK 等于 180M)
for (;;) {
TimerFreq= osKernelGetSysTimerFreq(); //180M 内核时钟(systick 时钟)频率
TimerFreq= osKernelGetTickFreq(); //1000 内核频率
TimerCounter1_last= osKernelGetSysTimerCount();//内核时钟计数值
TimerCounter2_last= osKernelGetTickCount(); //内核计数值
osDelay(100);
TimerCounter1_next= osKernelGetSysTimerCount();//内核时钟计数值
TimerCounter2_next= osKernelGetTickCount(); //内核计数值
}
}
从下图可以看出:
1、TimerCounter1_next - TimerCounter1_last = 17,993,028,约等于 18 000 000,内核时钟频率为180M,所以约等于100mS。
2、TimerCounter2_next - TimerCounter2_last = 100,内核频率为1000,所以约等于100mS
5、内存池
osMemoryPoolAlloc : 从内存池中分配内存块。
osMemoryPoolDelete : 删除一个内存池对象。
osMemoryPoolFree : 将分配的内存块返还给内存池。
osMemoryPoolGetBlockSize : 获取内存池中的内存块大小。
osMemoryPoolGetCapacity : 获取内存池中的最大内存块数。
osMemoryPoolGetCount : 获取内存池中使用的内存块数量。
osMemoryPoolGetName : 获取内存池对象的名称。
osMemoryPoolGetSpace : 获取内存池中可用的内存块数量。
osMemoryPoolNew : 创建并初始化一个内存池对象。
数据结构文档:
/// Attributes structure for memory pool.
typedef struct {
const char *name; ///< name of the memory pool
uint32_t attr_bits; ///< attribute bits 保留位
void *cb_mem; ///< memory for control block 内存控制块
uint32_t cb_size; ///< size of provided memory for control block
void *mp_mem; ///< memory for data storage
uint32_t mp_size; ///< size of provided memory for data storage
} osMemoryPoolAttr_t;
6、内存池与消息队列的结合
使用消息队列时,使用内存池地址来传递数据。从而提高消息队列效率
举例:
初始化
//.h 定义
#define MEMPOOL_OBJECTS 15 // number of Memory Pool Objects
#define TCPSENDPOOL 0xc0700000 //外部SDRAM
#define BYTE11K 1024*11
#define MAX_RECEIVE_LENGTH 512
typedef struct
{
char data[MAX_RECEIVE_LENGTH];
} sendMsg_t;
//.c创建地址池,消息队列
/*queue message*/
osMessageQueueId_t TcpSendMessage;
/*memory pool*/
osMemoryPoolId_t ReplaySend_MemPool; // memory pool id
const osMemoryPoolAttr_t TcpSendMemoryPool_attr = {
.mp_mem = (void *)TCPSENDPOOL ,
.mp_size = BYTE11K
};
/*queue*/
TcpSendMessage = osMessageQueueNew(100, sizeof(uint32_t), NULL);//100*4Byte(地址)
if(TcpSendMessage==NULL)
return -1;
/*memory pool*/
// ReplaySend_MemPool = osMemoryPoolNew(MEMPOOL_OBJECTS, sizeof(sendMsg_t), &TcpSendMemoryPool_attr);//创建内存池
ReplaySend_MemPool = osMemoryPoolNew(MEMPOOL_OBJECTS, sizeof(sendMsg_t), NULL);//创建内存池
if(ReplaySend_MemPool==NULL)
{
return -1;//MemPool object not created, handle failure
}
task1
uint32_t GetPoolUsed=0,PoolRemain=0,MaxMemoryBlocks=0,GetBlockSizes=0;
sendMsg_t *pMem1,*pMem2,*pMem3,*pMemMsg;
void main_task (void * arg)
{
uint16_t i=0;
static uint8_t j=0;
osStatus_t status;
while(1)
{
//内存池获取、释放测试
#if 1
pMem1 = (sendMsg_t *)osMemoryPoolAlloc(ReplaySend_MemPool, 0U);//申请内存
GetPoolUsed = osMemoryPoolGetCount(ReplaySend_MemPool); //已用内存池个数
PoolRemain = osMemoryPoolGetSpace (ReplaySend_MemPool); //剩余内存池个数
MaxMemoryBlocks = osMemoryPoolGetCapacity(ReplaySend_MemPool); //内存块最大个数
GetBlockSizes=osMemoryPoolGetBlockSize(ReplaySend_MemPool); //单个内存块多少个字节
pMem2 = (sendMsg_t *)osMemoryPoolAlloc(ReplaySend_MemPool, 0U);
GetPoolUsed = osMemoryPoolGetCount(ReplaySend_MemPool);
PoolRemain = osMemoryPoolGetSpace (ReplaySend_MemPool);
pMem3 = (sendMsg_t *)osMemoryPoolAlloc(ReplaySend_MemPool, 0U);
GetPoolUsed = osMemoryPoolGetCount(ReplaySend_MemPool);
PoolRemain = osMemoryPoolGetSpace (ReplaySend_MemPool);
osMemoryPoolFree(ReplaySend_MemPool,pMem1);//释放内存池
GetPoolUsed = osMemoryPoolGetCount(ReplaySend_MemPool);
PoolRemain = osMemoryPoolGetSpace (ReplaySend_MemPool);
osMemoryPoolFree(ReplaySend_MemPool,pMem2);
GetPoolUsed = osMemoryPoolGetCount(ReplaySend_MemPool);
PoolRemain = osMemoryPoolGetSpace (ReplaySend_MemPool);
osMemoryPoolFree(ReplaySend_MemPool,pMem3);
GetPoolUsed = osMemoryPoolGetCount(ReplaySend_MemPool);
PoolRemain = osMemoryPoolGetSpace (ReplaySend_MemPool);
#endif
//消息队列数据传递-使用内存池地址
pMemMsg = (sendMsg_t *)osMemoryPoolAlloc(ReplaySend_MemPool, 0U);//申请内存
for(i=0;i<MAX_RECEIVE_LENGTH;i++)
{
pMemMsg->data[i]=j;
}
j++;
osMessageQueuePut(TcpSendMessage, &pMemMsg, 0U, 0U);
}
}
task2
void led_task (void * arg)
{
osStatus_t status;
uint32_t ptr;
sendMsg_t *prcv;
while(1)
{
status= osMessageQueueGet(TcpSendMessage, &ptr, NULL, 0U);
if (osOK == status)
{
prcv = (sendMsg_t *)ptr;
//do something...
/*
HAL_UART_Transmit_IT(&huart3,(uint8_t *)&prcv->data[0],strlen(prcv->data));
osDelay(strlen(prcv->data));
while(huart3.gState != HAL_UART_STATE_READY)
osDelay(1);
*/
osMemoryPoolFree(ReplaySend_MemPool,prcv);
}
}
}
注意事项:当属性设置为NULL,自动申请片内RAM时,配置全局内存池不能设置的过小,否则会创建失败。
RTX中每个线程可拥有高达32个线程标志,这些线程标志存放在线程控制块中,其余线程可以通过设置这些Thread Flags来中断该线程的执行。当某个执行线程调用osThreadFlagsWait()时,该线程会被阻塞,并进入wait_event状态。若其他线程将所选(specified)的标志中的某个或全部Thread Flags置位,则该线程将会变为就绪态。
7、CMSIS-RTOS2 API 之线程标志组
RTX中每个线程可拥有高达32个线程标志,这些线程标志存放在线程控制块中,其余线程可以通过设置这些Thread Flags来中断该线程的执行。当某个执行线程调用osThreadFlagsWait()时,该线程会被阻塞,并进入wait_event状态。若其他线程将所选(specified)的标志中的某个或全部Thread Flags置位,则该线程将会变为就绪态。
抓重点,线程标志和事件标志的区别:
1、线程标志只能发送给单个指定线程
2、事件标志可以发送给多个线程
uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags)
输入:
- thread_id : 线程ID
- flags: 设置多个线程标志
输出: - 返回设置后的线程标志,或者错误码
osFlagsErrorUnknown: unspecified error.
osFlagsErrorParameter: parameter thread_id is not a valid thread or flags has highest bit set.
osFlagsErrorResource: the thread is in invalid state.
uint32_t osThreadFlagsClear ( uint32_t flags )
参数:
- flags : 线程标志
返回: - 清除前的线程标志或者错误码(如果最高位被置位)
osFlagsErrorUnknown: unspecified error, i.e. not called from a running threads context.
osFlagsErrorParameter: parameter flags has highest bit set.
osFlagsErrorISR: the function osThreadFlagsClear cannot be called from interrupt service routines.
uint32_t osThreadFlagsGet ( void )
输入
- 无
输出 - 当前线程标志
uint32_t osThreadFlagsWait ( uint32_t flags,uint32_t options,uint32_t timeout )
// 输入
- flags : 等待的线程标志,选定等待的标志位集合,比如1U表示只有标志0,3U则表示选中标志0和标志1
- options : 标志选项
- timeout : 超时设定
/输出 - 清除之前的线程标志,注意该函数返回时会自动清除等待线程标志(除非选项设定为osFlagsNoClear)
options | – | – |
---|---|---|
osFlagsWaitAny | Wait for any flag (default). | |
osFlagsWaitAll | Wait for all flags. | |
osFlagsNoClear | Do not clear flags which have been specified to wait for |
任何线程都可以将其他线程的标志位置位,线程本身只能等待自己的标志位。
举例
/*
*********************************************************************************************************
* 函 数 名: AppTaskCreate
* 功能说明: 创建应用任务
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
/* 任务句柄 */
osThreadId_t main_ids = NULL;
osThreadId_t led_ids = NULL;
//Attributes structure for main_thread.
static const osThreadAttr_t ThreadAttr_MAIN = {
.name = "Main_Thread",
.priority=osPriorityNormal,
.stack_size =512,
};
static const osThreadAttr_t ThreadAttr_LED = {
.name = "LED_Thread",
.priority=osPriorityNormal,
.stack_size =512,
};
static void AppTaskCreate (void)
{
//main_task
main_ids=osThreadNew(main_task, NULL, &ThreadAttr_MAIN);
//led_task
led_ids=osThreadNew(led_task, NULL, &ThreadAttr_LED);
}
#define flag0 (1 << 0) //(32位 bit0)
#define flag1 (1 << 1) //(32位 bit1)
#define flag2 (1 << 2) //(32位 bit2)
#define flag3 (1 << 3) //(32位 bit3)
#define flag4 (1 << 4) //(32位 bit4)
uint8_t KEY_VALUE=0;
/*
** KEY_VALUE=1,线程组flag0置位
** KEY_VALUE=2,线程组flag1置位
** KEY_VALUE=3,线程组flag2置位
** KEY_VALUE=4,线程组flag1与flag2置位
** KEY_VALUE=5,线程组flag3与flag4置位
*/
void led_task(void *arg)
{
uint32_t flags;
while(1)
{
if(KEY_VALUE == 1)
{
flags=osThreadFlagsSet(main_ids,flag0); /* 置位main_ids线程的flag0 */
if(flags==flag0)
KEY_VALUE = 0;
}
if(KEY_VALUE == 2)
{
flags=osThreadFlagsSet(main_ids,flag1); /* 置位main_ids线程的flag1 */
if(flags==flag1)
KEY_VALUE = 0;
}
if(KEY_VALUE == 3)
{
flags=osThreadFlagsSet(main_ids,flag2); /* 置位main_ids线程的flag2 */
if(flags==flag2)
KEY_VALUE = 0;
}
if(KEY_VALUE == 4)
{
flags=osThreadFlagsSet(main_ids,flag1|flag2); /* 置位main_ids线程的flag1与flag2事件 */
if(flags == (flag1|flag2))
KEY_VALUE = 0;
}
if(KEY_VALUE == 5)
{
flags=osThreadFlagsSet(main_ids,flag3|flag4); /* 置位main_ids线程的flag3与flag4事件 */
if(flags == (flag3|flag4))
KEY_VALUE = 0;
}
osDelay(1);
}
}
void main_task (void * arg)
{
osStatus_t status;
uint32_t flags;
while(1)
{
flags = osThreadFlagsWait(flag0|flag1|flag2,osFlagsWaitAny,1); /* osWaitForever 一直等待自身线程事件组,直到flag0或flag1或 flag2被置1 */
/* 看看是不是flag0被置1 */
if(flags == flag0)
printf("get flag 0 !\r\n");
/* 看看是不是flag1被置1 */
if(flags == flag1)
printf("get flag 1 !\r\n");
/* 看看是不是flag2被置1 */
if(flags == flag2)
printf("get flag 2 !\r\n");
/* 看看是不是flag2 flag1都被置1 */
if(flags == (flag2|flag1))
printf("get flag 2 flag 1 !\r\n");
flags = osThreadFlagsWait(flag3|flag4,osFlagsWaitAll,1); /*等待flag3 flag4一起被置位*/
/* 看看是不是flag3 flag4 一起被置1 */
if(flags == (flag3|flag4))
printf("get flag 3 flog4 !\r\n");
osDelay(1);
}
}