FreeRTOS多任务管理

2023-05-16

文章目录

  • 1、任务
    • 1.1 任务简介
    • 1.2 任务调度
    • 1.3 任务的状态 (就绪态 / 运行态 / 阻塞态 / 挂起态)
    • 1.4 空闲任务
  • 2、动态创建两个任务
    • 2.1 定义动态内存空间的堆
    • 2.2 定义任务函数
    • 2.3 定义 任务控制块 指针
    • 2.4 动态创建任务 xTaskCreate()
    • 2.5 启动任务 vTaskStartScheduler()
  • 3、常用的任务函数
    • 3.1 任务挂起 vTaskSuspend()
    • 3.2 任务恢复 vTaskResume()
    • 3.3 任务删除 vTaskDelete()
    • 3.4 阻塞延时函数
      • 3.4.1 相对延时函数 vTaskDelay()
      • 3.4.2 绝对延时函数 vTaskDelayUntil()


1、任务

1.1 任务简介

(1) 在裸机系统中,系统的主体就是 main 函数里面顺序执行的无限循环,这个无限循环里面 CPU 按照顺序完成各种事情。
(2) 在FreeRTOS中,根据功能的不同,把整个系统分割成一个个独立无限循环无法返回的函数,这个函数就称为 任务

1.2 任务调度

任何时刻,只有一个任务得到运行,FreeRTOS调度器 决定运行哪个任务。调度器会不断的启动、停止每一个任务,宏观看上去所有的任务都在同时在执行。
FreeRTOS 中的任务是 抢占式 调度机制,高优先级的任务可打断 低优先级任务,低优先级任务必须在高优先级任务阻塞结束后 才能得到调度。

1.3 任务的状态 (就绪态 / 运行态 / 阻塞态 / 挂起态)

任务状态通常分为以下四种:
就绪态 (ready):该任务在就绪列表中,就绪的任务已经具备执行的能力,只等待调度器进行调度,新创建的任务会初始化为就绪态

运行态 (running):该状态表明任务正在执行,此时它占用处理器,FreeRTOS调度器选择运行的永远是处于最高优先级的 就绪态任务,当任务被运行的一刻,它的任务状态就变成了运行态

阻塞态 (blocked):正在运行的任务发生阻塞 (延时、读信号量等待、读写队列或者等待读写事件) 时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态。

挂起态 (suspended):处于挂起态的任务对调度器而言是不可见的,让一个任务进入挂起状态的唯一办法就是调用 vTaskSuspend() 函数;而把一个挂起状态的任务恢复 的唯一途径 就是调用 vTaskResume() 或 vTaskResumeFromISR() 函数。

挂起态阻塞态的区别:
当任务有较长的时间不允许运行的时候,我们可以挂起任务,这样 调度器就不会管这个任务的任何信息,直到我们调用恢复任务的 API 函数;
而任务处于阻塞态的时候,系统还需要判断 阻塞态的任务是否超时,是否可以解除阻塞。

他们之间的转换关系如下图:
在这里插入图片描述
(1):创建任务→就绪态 (ready):任务创建完成后进入就绪态,表明任务已准备就绪,随时可以运行,只等待调度器进行调度。
(2):就绪态→运行态 (running):发生任务切换时,就绪列表最高优先级的任务被执行,从而进入运行态。
(3):运行态→就绪态:有更高优先级任务创建或者恢复后,会发生 任务调度,此刻就绪列表中最高优先级任务变为运行态,那么原先运行的任务由运行态变为就绪态,依然在就绪列表中,等待最高优先级的任务运行完毕继续运行原来的任务 (此处可以看做是 CPU使用权被更高优先级的任务抢占了)。
(4):运行态→阻塞态 (blocked):正在运行的任务发生阻塞 (延时、读信号量等待) 时,该任务会从就绪列表中删除,任务状态由运行态变成阻塞态,然后发生任务切换,运行就绪列表中当前最高优先级任务。
(5):阻塞态→就绪态:阻塞的任务被恢复后 (任务恢复、延时时间超时、读信号量超时或读到信号量等),此时被恢复的任务会被加入就绪列表,从而由阻塞态变成就绪态;如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。
(6) (7) (8):就绪态、阻塞态、运行态→挂起态 (suspended):任务可以通过调用 vTaskSuspend() API 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到CPU的使用权,也不会参与调度,除非它从挂起态中解除。
(9):挂起态→就绪态:把一个挂起状态的任务 恢复的唯一途径就是调用 vTaskResume() 或 vTaskResumeFromISR() API 函数,如果此时被恢复任务的优先级高于正在运行任务的优先级,则会发生 任务切换,将该任务将再次转换任务状态,由就绪态变成运行态。

1.4 空闲任务

空闲任务是系统在 启动调度器的时候,创建的优先级最低的任务,空闲任务主体主要是做一些系统内存的清理工作。如果没有其它任务可以运行,这个时候CPU就运行空闲任务。

2、动态创建两个任务

2.1 定义动态内存空间的堆

使用动态内存,即 堆(heap),也属于 SRAM。
FreeRTOS 做法是在SRAM里面定义一个大数组,也就是堆内存,供 FreeRTOS的动态内存分配函数使用,在第一次使用的时候,系统会将定义的堆内存进行初始化。

//系统所有总的堆大小
#define configTOTAL_HEAP_SIZE    ((size_t)(36*1024))    (1)
static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; 	    (2)

(1) 堆内存的大小为 configTOTAL_HEAP_SIZE,在 FreeRTOSConfig.h 中,由用户自己定义,configSUPPORT_DYNAMIC_ALLOCATION 这个宏定义在使用FreeRTOS操作系统的时候必须开启。
(2) 从内部SRAM 里面定义一个静态数组 ucHeap,大小由configTOTAL_HEAP_SIZE这个宏决定,目前定义为 36KB。

2.2 定义任务函数

创建两个任务,分别 让 LED 灯以 500ms / 1000ms的频率闪烁,具体实现见代码清单:

 static void LED1_Task(void* parameter)
 {
   while (1) {							
     LED1_ON;
     vTaskDelay(500); /*  延时500个tick */

     LED1_OFF;
     vTaskDelay(500); /*  延时500个tick */
   }
 }
 
 static void LED2_Task(void* parameter)
 {
   while (1) {									(1)
     LED2_ON;
     vTaskDelay(1000); /*  延时1000个tick */  	(2)

     LED2_OFF;
     vTaskDelay(1000); /*  延时1000个tick */
   }
}

(1) :任务必须是一个死循环。
(2) :任务里面的延时函数必须使用 FreeRTOS 里面提供的延时函数 vTaskDelay()
调用 vTaskDelay() 函数的时候,当前任务进入阻塞状态,调度器会切换到其它就绪的任务,从而实现多任务。

2.3 定义 任务控制块 指针

任务控制块 (TCB) 是在任务创建的时候分配内存空间创建,任务创建函数会返回一个指针,用于指向 任务控制块,所以要预先为任务栈定义一个任务控制块指针,也就是 任务句柄

任务句柄的数据类型为 TaskHandle_t,在 task.h 中定义,实际上就是一个 空指针,具体实现见代码清单:

 /* 任务句柄 */
 typedef void * TaskHandle_t;

定义任务句柄:

/**************************** 任务句柄 ********************************/
 /*
 * 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
 * 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
 * 这个句柄可以为 NULL。
 */
 static  TaskHandle_t  AppTaskCreate_Handle  =  NULL;	/* 创建任务句柄 */
 static  TaskHandle_t  LED1_Task_Handle  	=  NULL;	/* LED1 任务句柄 */
 static  TaskHandle_t  LED2_Task_Handle  	=  NULL;	/* LED2 任务句柄 */

2.4 动态创建任务 xTaskCreate()

使用动态内存的时候,使用 xTaskCreate() 函数来创建一个任务。

 	/* 创建 AppTaskCreate 任务 */
 	xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */	   	(1)
 						(const char* )"AppTaskCreate", 	/* 任务名字 */	       	(2)
 						(uint16_t )512, 				/* 任务栈大小 */			(3)
 						(void* )NULL,					/* 任务入口函数参数 */	(4)
 						(UBaseType_t )1, 				/* 任务的优先级 */	 	(5)
 						(TaskHandle_t* )&AppTaskCreate_Handle); /* 任务控制块 指针 */ (6)
 	/* 启动任务调度 */
 	if (pdPASS == xReturn)
 		vTaskStartScheduler(); /* 启动任务,开启调度 */

(1) :任务入口函数,即任务函数的名称。(用户自定义,用于创建任务)
(2) :任务名字,字符串形式, 最大长度由 FreeRTOSConfig .h 中定义的configMAX_TASK_NAME_LEN 宏指定,多余部分会被自动截掉,这里任务名字最好要与 任务函数入口名字一致,方便进行调试。
(3) :任务堆栈大小,单位为字,在32位的处理器下,一个字等于4个字节,那么任务大小就为 512*4 = 2048 字节。
(4) :任务入口函数形参,不用的时候配置为 0 或者 NULL即可。
(5) :任务的优先级。 优先级范围根据 FreeRTOSConfig .h 中的宏 configMAX_PRIORITIES 决定:

  1. 如果使能 configUSE_PORT_OPTIMISED_TASK_SELECTION 这个宏定义,则最多支持 32 个优先级;
  2. 如果不用特殊方法查找下一个运行的任务,那么则不强制要求限制最大可用优先级数目。

在 FreeRTOS 中,数值越大 优先级越高,0代表最低优先级

(6) :任务控制块 指针。 在使用动态内存的时候,任务创建函数 xTaskCreate() 会返回一个指针 指向 任务控制块,该任务控制块是 xTaskCreate() 函数里面动态分配的一块内存。

任务创建好后,是处于 任务就绪 (ready),在就绪态的任务可以参与操作系统的调度。

说明
为了方便管理,任务的创建都通过 AppTaskCreate() 任务创建函数来执行:

 static void AppTaskCreate(void)
 {
 	BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
 
 	taskENTER_CRITICAL(); //进入临界区
 
 	/* 创建 LED1_Task 任务 */
 	xReturn = xTaskCreate((TaskFunction_t )LED1_Task, 	/* 任务入口函数 */
 				(const char* )"LED1_Task", 				/* 任务名字 */
				(uint16_t )512, 						/* 任务栈大小 */
 				(void* )NULL,  							/* 任务入口函数参数 */
 				(UBaseType_t )2, 						/* 任务的优先级 */
 				(TaskHandle_t* )&LED1_Task_Handle);		/* 任务控制块指针 */
   
   	if (pdPASS == xReturn)
		printf("创建 LED1_Task 任务成功!\r\n");
 
 	/* 创建 LED2_Task 任务 */
 	xReturn = xTaskCreate((TaskFunction_t )LED2_Task, 	/* 任务入口函数 */
 				(const char* )"LED2_Task",				/* 任务名字 */
				(uint16_t )512, 						/* 任务栈大小 */
 				(void* )NULL,  							/* 任务入口函数参数 */
 				(UBaseType_t )3, 						/* 任务的优先级 */
 				(TaskHandle_t* )&LED2_Task_Handle);		/* 任务控制块指针 */
   
   	if (pdPASS == xReturn)
		printf("创建 LED2_Task 任务成功!\r\n");
   
 	vTaskDelete(AppTaskCreate_Handle); //删除 AppTaskCreate 任务
 
 	taskEXIT_CRITICAL(); //退出临界区
 }

2.5 启动任务 vTaskStartScheduler()

在创建完任务的时候,我们需要 开启调度器,因为创建仅仅是把任务添加到系统中,还没真正调度,并且空闲任务也没实现,定时器任务也没实现,这些都是在开启调度函数 vTaskStartScheduler() 中实现的。

任务调度器只启动一次,之后就不会再次执行了。从此,任务管理都由 FreeRTOS 管理,此时是真正进入 实时操作系统

 /*****************************************************************
 * @brief 主函数
 * @param 无
 * @retval 无
 * @note 第一步:开发板硬件初始化
 第二步:创建 APP 应用任务
 第三步:启动 FreeRTOS,开始多任务调度
 ****************************************************************/
 int main(void)
 {
     BaseType_t xReturn = pdPASS;	/* 定义一个创建信息返回值,默认为 pdPASS */

     /* 第1步 开发板硬件初始化 */
     BSP_Init();

	 /* 第2步 创建 AppTaskCreate 任务 */
 	xReturn = xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */		(1)
 						(const char* )"AppTaskCreate", 	/* 任务名字 */			(2)
 						(uint16_t )512, 				/* 任务栈大小 */  		(3)
 						(void* )NULL,					/* 任务入口函数参数 */ 	(4)
 						(UBaseType_t )1, 				/* 任务的优先级 */  		(5)
 						(TaskHandle_t* )&AppTaskCreate_Handle); /* 任务控制块指针 */ (6)

     /* 第3步 启动任务调度 */
     if (pdPASS == xReturn)
   		 vTaskStartScheduler(); /* 启动任务,开启调度 */
     else
   		 return -1;

     while (1); /* 正常不会执行到这里 */
 }  

3、常用的任务函数

3.1 任务挂起 vTaskSuspend()

任务可以通过调用 vTaskSuspend() 函数都可以将处于任何状态的任务挂起,被挂起的任务得不到 CPU 的使用权,也不会参与调度,它相对于调度器而言是不可见的,除非它从挂起态中解除。

注:任务可以调用 vTaskSuspend() 这个函数来挂起任务自身,但是在挂起自身的时候会进行一次任务上下文切换,需要 挂起自身就将 xTaskToSuspend 设置为 NULL 传递进来即可。
无论任务是什么状态都可以被挂起,只要调用了 vTaskSuspend() 这个函数就会挂起成功,不论是挂起其他任务还是挂起任务自身。

代码示例:

 static TaskHandle_t LED_Task_Handle = NULL;	/*LED任务句柄*/
 
 static void KEY_Task(void* parameter)
 {
     while (1) {
         if ( Key_Scan(KEY1_GPIO_PORT, KEY1_GPIO_PIN) == KEY_ON ) {
             /*K1被按下*/
             printf("挂起 LED 任务!\n");
             vTaskSuspend(LED_Task_Handle);	/*挂起LED任务*/
         }
         vTaskDelay(20);			/*延时20个tick*/
     }
 }

3.2 任务恢复 vTaskResume()

任务恢复就是让挂起的任务 重新进入就绪状态,恢复的任务会保留挂起前的状态信息,在恢复的时候 根据挂起时的状态,继续运行。
如果被恢复任务在所有就绪态任务中,处于最高优先级列表的第一位,那么系统将进行任务上下文的切换。

代码示例:

static TaskHandle_t LED_Task_Handle = NULL;		/* LED  任务句柄 */
 
static void KEY_Task(void* parameter)
{
   while (1) {
       if ( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) {
           /* K2 被按下 */
           printf("恢复 LED 任务!\n");
           vTaskResume(LED_Task_Handle);	/* 恢复LED任务!*/
       }
       vTaskDelay(20);				/* 延时20个tick */
   }
}

3.3 任务删除 vTaskDelete()

vTaskDelete() 用于删除一个任务。
(1) 当一个任务 删除另外一个任务时,形参为 要删除任务 创建时返回的任务句柄;
(2) 如果是删除自身, 则形参为 NULL。

要想使用该函数必须在 FreeRTOSConfig.h 中把 INCLUDE_vTaskDelete 定义为 1,删除的任务将从所有 就绪,阻塞,挂起和事件列表中删除。

代码示例:


/*  删除任务本身 */
vTaskDelete( NULL );

/*  在其他任务删除 DeleteTask 任务 */
vTaskDelete( DeleteHandle );

3.4 阻塞延时函数

3.4.1 相对延时函数 vTaskDelay()

阻塞延时的阻塞,是指任务调用该延时函数后,任务会被剥离 CPU 使用权,然后进入阻塞状态,直到延时结束,任务重新获取 CPU 使用权才可以继续运行。在任务阻塞的这段时间,CPU可以去执行其它的任务,如果其它的任务也在延时状态,那么 CPU就将运行空闲任务。延时的时长由形参 xTicksToDelay 决定,单位为系统节拍周期, 比如系统的时钟节拍周期为 1ms,那么调用 vTaskDelay(1) 的延时时间则为 1ms。

vTaskDelay() 延时是相对性的延时,它指定的延时时间是从调用 vTaskDelay() 结束后开始计算的,经过指定的时间后延时结束。
比如 vTaskDelay(100), 从调用 vTaskDelay() 结束后,任务进入阻塞状态,经过 100 个系统时钟节拍周期后,任务解除阻塞。因此,vTaskDelay() 并不适用与周期性执行任务的场合。此外,其它任务和中断活动,也会影响到 vTaskDelay() 的调用(比如调用前,高优先级任务抢占了当前任务),进而影响到任务的下一次执行的时间。

任务的延时在实际中运用特别多,因为需要暂停一个任务,让任务放弃 CPU,延时结束后再继续运行该任务,如果任务中没有阻塞的话,比该任务优先级低的任务 则无法得到CPU的使用权,就无法运行。

代码示例:

void vTaskA( void * pvParameters )
 {
     while (1) {
       // ...
       // 这里为任务主体代码
       // ...

       /*  调用相对延时函数, 阻塞 1000 个 个 tick */
       vTaskDelay( 1000 );
     }
 }

3.4.2 绝对延时函数 vTaskDelayUntil()

在 FreeRTOS 中,除了相对延时函数,还有绝对延时函数 vTaskDelayUntil(),这个绝对延时常用于较精确周期运行任务,比如我有一个任务,希望它以 固定频率定期执行,而不受外部的影响,任务从上一次运行开始到下一次运行开始的时间间隔是绝对的,而不是相对的。

函数原型:

void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement );

代码实例:

void vTaskA( void * pvParameters )
 {
   /*  用于保存上次时间。调用后系统自动更新 */
   static portTickType PreviousWakeTime;
   /*  设置延时时间,将时间转为节拍数 */
   const portTickType TimeIncrement = pdMS_TO_TICKS(1000);

   /*  获取当前系统时间 */
   PreviousWakeTime = xTaskGetTickCount();

   while (1) {
     /*  调用绝对延时函数, 任务时间间隔为 1000 个 个 tick */
     vTaskDelayUntil( &PreviousWakeTime ,TimeIncrement );

     // ...
     // 这里为任务主体代码
     // ...
   }
}

注意:在使用的时候要将延时时间转化为系统节拍,在任务主体之前要调用延时函数。
任务会先调用 vTaskDelayUntil() 使任务进入 阻塞态,等到时间到了就从阻塞中解除,然后执行主体代码,任务主体代码执行完毕。会继续调用 vTaskDelayUntil() 使任务进入阻塞态,然后就是循环这样子执行。

即使任务在执行过程中发生中断,那么也不会影响这个任务的运行周期,仅仅是缩短了阻塞的时间而已,到了要唤醒的时间依旧会将任务唤醒。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

FreeRTOS多任务管理 的相关文章

  • Java枚举详解及常见用法

    1 枚举的使用场景 枚举是一个被命名的整型常数的集合 xff0c 用于声明一组带标识符的常数 枚举在曰常生活中很常见 xff0c 例如一个人的性别只能是 男 或者 女 xff0c 一周的星期只能是 7 天中的一个等 类似这种当一个变量有几种
  • ros编译时与opencv发生冲突的解决办法 opencv_imgcodecs3.so.3.3.1: undefined reference to `TIFFReadDirectory@LIBTIFF

    在编译ros项目的时候 xff0c 遇到下面的错误 xff1a 38 Building CXX object cube slam detect 3d cuboid CMakeFiles detect 3d cuboid dir src ma
  • ROS的一些常用的命令

    下面是ros下面的一些常用操作 xff0c 先做个总结 xff0c 方便后续使用 1 只编译工作空间下面的一个包 catkin make DCATKIN WHITELIST PACKAGES span class token operato
  • realsense相机两种获取相机内外参的方式

    realsense相机出厂的时候一般都标定好了 xff0c 直接读取他们的内参即可 终端输入 xff1a rs sensor control 然后按照提示操作即可 方法 xff1a https blog csdn net mystyle a
  • 【Python开发利器-TKinterDesigner 】 :第一讲:TkinterDesigner的基本功能介绍

    Python开发利器 TKinterDesigner xff1a 第一讲 xff1a TkinterDesigner的基本功能介绍
  • 基于Spine动画的AVATAR换装系统优化

    大家好 xff0c 我是红孩儿 xff0c 目前在玩吧技术专家组负责技术研发工作 xff0c 好久不见 xff0c 作为一名引擎开发者 xff0c 这次我给大家分享一下基于Spine动画的AVATAR换装优化 xff0c 希望能够对有类似需
  • TKinterDesigner v1.6.4 前瞻- 即将开启Python移动开发时代

    TKinterDesigner v1 6 4 前瞻 即将开启Python移动开发时代 我想 xff1a 这可能是一个新时代的开启吧
  • TKinterDesigner从入门到精通视频教程

    想使用TKinter开发桌面应用 xff1f 想使用TKinter开发移动应用 xff1f TKinterDesigner来帮助你吧 xff01 欢迎使用TKinterDesigner xff01 以下是 TKinterDesigner从入
  • 房卡麻将分析系列 "牌局回放" 之 播放处理

    持续放出房卡麻将设计与研发讲解 敬请关注微信共众号 xff1a 红孩儿的游戏开发之路 房卡麻将分析系列 34 牌局回放 34 之 播放处理 昨天红孩儿给大伙讲了讲 牌局回放 的数据记录处理 xff0c 有了数据的存储 xff0c 下面就是数
  • AutoSAR 标准--基础安全特性

    CSM 密码服务管理器 Crypto Service Manager xff1a 访问加密服务 配置用于执行服务的加密服务和算 同步或异步执行的配置 安全计数器的配置 对加密密钥的操作配置 配置证书操作 CRYIF 密码算法接口 Inter
  • 房卡麻将分析系列之"发牌器"算法设计

    房卡麻将分析系列之 34 发牌器 34 算法设计 大家好 xff0c 经过一段时间的努力 xff0c 在填平了大部分源码框架中的坑后 xff0c 我们的 大赢家 红中麻将总算完成了1 0版本 xff0c 虽然仍然有诸多问题 xff0c 但总
  • 房卡麻将分析系列之"架构选型"

    房卡 麻将研发技巧 xff0c 尽在 红孩儿的游戏开发之路 xff0c 欢迎关注公众号 xff01 房卡麻将分析系列之 34 架构选型 34 这段时间 xff0c 团队的棋牌订制咨询量激增 xff0c 每天都有人问如何进行订制合作 xff0
  • 房卡麻将分析系列之"断线重连"

    房卡 麻将研发技巧 xff0c 尽在 红孩儿的游戏开发之路 xff0c 欢迎关注公众号 xff01 房卡麻将分析系列之 34 断线重连 34 大家好 xff0c 我是红孩儿 xff0c 房卡 麻将分析系列继续进行中 在进行游戏的过程中 xf
  • 房卡麻将分析系列之"千里传音"

    房卡 麻将研发技巧 xff0c 尽在 红孩儿的游戏开发之路 xff0c 欢迎关注公众号 xff01 房卡麻将分析系列之 34 千里传音 34 在房卡棋牌游戏中 xff0c 因为要频繁的看牌 xff0c 出牌 为了实时沟通打字聊天往往比较麻烦
  • 房卡棋牌分析系列之"微信登录"

    房卡 麻将研发技巧 xff0c 尽在 红孩儿的游戏开发之路 xff0c 欢迎关注公众号 xff01 房卡棋牌分析系列之 34 微信登录 34 房卡棋牌之所以火热 xff0c 很大程度上取决于当下中国智能机的普及和微信作为基础通讯社交工具的广
  • 程序员三十岁之后要考虑什么?

    程序员三十岁之后要考虑什么 xff1f 周六受邀参加了触控科技CocoaChina开发者社区举办的 34 移动游戏人才培养和创业机会 的沙龙 其间被问到一个问题 xff1a 程序员三十岁之后该怎么办 xff1f 三十岁后该怎么办 xff1f
  • 房卡麻将分析之"一键入局"

    地方棋牌 xff0c VR AR技术 xff0c 请关注公众号 xff1a 34 红孩儿的游戏开发之路 房卡麻将分析之 34 一键入局 34 房卡麻将通过微信邀请好友加入房间进行游戏 xff0c 大大方便了玩家进行麻将游戏 这也凸显了微信对
  • 房卡麻将分析之"防作弊处理"

    房卡麻将分析之 34 防作弊处理 34 棋牌游戏最重要的一个特点就是人与人对局 xff0c 因为玩家各自的不可见 xff0c 就存在着一些作弊的可能性和漏洞 对于手机房卡麻将游戏 xff0c 大家最讨厌的问题就是作弊 如何防止玩家作弊 xf
  • 房卡麻将分析之“缺人玩法”

    一般玩麻将都是四个人 xff0c 遇到 三缺一 怎么办 xff1f 大多数情况下 xff0c 玩家只好等待或叫人 xff0c 这无疑增加了玩家的等待时间 xff0c 同时也减缓了房卡的消耗速度 其实四个人并不是玩麻将的硬性条件 xff0c
  • 房卡麻将分析之“代开房间”

    什么是代开房间 代开房间 xff0c 就是可以创建一个新的房间 xff0c 让其它玩家加入 为什么要做代开房间 xff08 1 xff09 可以帮助没有房卡的朋友开房 xff0c 提升未买卡玩家玩游戏的积级性 xff08 2 xff09 代

随机推荐

  • 信号量sem_wait()的使用

    闲来无事 xff0c 我给大家讲下UNIX Linux下信号量函数的使用 首先你得知道什么叫信号量 xff0c 什么时候要用信号量 这个嘛 xff0c 主要就是用来保护共享资源的 xff0c 也就是说如果你想限制某个 xff08 些 xff
  • 房卡麻将分析之“俱乐部功能”

    什么是俱乐部 群主 xff08 代理 xff09 创建俱乐部后 xff0c 可以邀请自已的群成员成为自已的俱乐部会员 xff0c 方便管理 俱乐部玩法由创始人设置 xff0c 只有俱乐部成员才能加入 xff0c 并且俱乐部房间全部消耗创始人
  • 红孩儿网狐Cocos经典棋牌开发教程-卞安-专题视频课程

    红孩儿网狐Cocos经典棋牌开发教程 4750人已学习 课程介绍 由知名Cocos技术讲师红孩儿讲解的基于网狐源码进行棋牌开发的系列教程 课程收益 1 快速理解掌握网狐棋牌的源码框架 2 能够学会使用网狐棋牌快速换皮产品 3 能够在网狐棋牌
  • STM32F407VET6 普通 IO 口模拟串口发送数据

    1 只实现了 xff0c 使用普通 IO 口模拟串口的发送 xff0c 没有实现接收 2 由于是模拟发送的是 TTL 电平 xff0c 所以在使用串口助手验证发送数据的准确性时 xff0c 应该使用 USB 转 TTL xff0c 而不能使
  • 虹科知识分享|关于阻塞函数和非阻塞函数

    在CAN和LIN相关的开发库里 xff0c 不可避免的会出现 收 和 发 的函数 如何快速有效的处理数据 xff0c 是开发中重要的事项 但实际上单路CAN由于波特率的限制 xff0c 数据量并不会很大 xff1a 以常用的500k为例 x
  • 车用技术总线 | 从应用角度了解下LIN总线

    注意 xff1a 本文约4850字 xff0c 完整阅读时间约13分钟 主要内容 LIN总线概述 LIN总线的应用 示例 xff1a LIN总线和CAN总线的窗户控制 LIN总线的信号报文 记录LIN总线数据 LIN总线数据记录应用案例 本
  • 工业通讯 | CAN基础内容详解(二)——物理层

    往期回顾 工业通讯 CAN基础内容详解 xff08 一 xff09 物理层主要完成设备间的信号传送 xff0c 把各种信号转换成物理信号 xff0c 并将这些信号传输到其他目标设备 在这一层中 xff0c CAN bus对信号电平 通信时使
  • 04ROS中的头文件与源文件

    ROS中的头文件与源文件 本节主要介绍ROS的C 43 43 实现中 xff0c 如何使用头文件与源文件的方式封装代码 xff0c 具体内容如下 设置头文件 xff0c 可执行文件作为源文件 xff1b 分别设置头文件 xff0c 源文件与
  • TX2系统安装

    手头新拿到块TX2 xff0c 从零开始搞 xff0c 特此记录以备后续查看 xff0d 2017 07 16 乐 参考官方网址 JetPack下载网址 准备刷机环境 下载JetPack L4T 3 1 linux x64 run pc环境
  • 单片机是嵌入式的子类

    1系统组成结构上的区别 xff08 1 xff09 单片机基本结构 单片机由运算器 控制器 存储器 输入输出设备构成 xff08 2 xff09 嵌入式系统成部分 嵌入式系统一般由嵌入式微处理器 外围硬件设备 嵌入式操作系统 特定的应用程序
  • STM32串口协议概念及结构体初始化详解+串口点灯+串口收发程序

    文章目录 一 串口通信协议简介1 RS232标准2 USB转串口3 原生的串口到串口 二 初始化结构体解1 USART初始化结构体2 同步时钟初始化结构体3 编程时需要用到的固件库函数 三 串口点灯代码实现1 USART C文件2 USAR
  • 无人机光流定位系列——(一)原理剖析

    这次参加深圳的高交会 xff0c 在qualcomm展区看到了多款mini无人机 xff0c 大家稍微细心一点就可以发现这些无人机都使用了一种叫光流定位的技术 xff0c 很多人可能都还不明白光流定位是个什么东西 xff0c 是如何进行定位
  • 现代控制理论2——状态空间分析法

    注 xff1a 本文是在MOOC平台上学习西北工业大学 现代控制理论基础 xff08 郭建国 赵斌 郭宗易 xff09 的课程进行随笔记录与整理 一 状态空间描述的相关概念 1 系统模型包括 xff1a 内部结构 xff0c 以及内部结构反
  • 输出调节——内模原理(1)

    浏览了百度和部分文献 xff08 主要为 xff1a 调节问题系统综述 xff0c 线性系统的内模原理 xff09 xff0c 在此尝试进行一些归纳 一 问题描述 输出调节问题 xff1a output regulation problem
  • RGB-D相机建图——2、openvins安装、测试与连接自己设备

    基础内容贴推荐 xff1a https zhuanlan zhihu com p 93814423 https blog csdn net weixin 39752599 article details 105906652 https zh
  • RGB-D相机建图——3、使用kalibr进行相机标定

    Kalibr 视觉惯性校准工具箱 官方网站 xff1a https github com ethz asl kalibr Kalibr是一个工具箱 xff0c 可以解决以下校准问题 xff1a 多摄像机校准 xff1a 具有非全局共享重叠视
  • 02.构建项目流程梳理及总结

    02 构建项目流程梳理及总结
  • Nuttx学习入门

    Nuttx学习 NuttX 是一个实时操作系统 RTOS xff0c 强调标准合规性和占用空间小 可从 8 位扩展到 64 位微控制器环境 xff0c NuttX 中的主要管理标准是 POSIX 和 ANSI 标准 NuttX 的主要环境依
  • 软件测试之如何介绍自己的项目

    测试人员在找工作的过程中 xff0c 通常有一个问题是很难绕开的 就是要如何向别人介绍自己之前做过的项目 要解决这个问题 xff0c 大致可以分为如下几个步骤 xff1a 1 对项目进行基本介绍 2 说明自己负责测试的模块 3 针对部分模块
  • FreeRTOS多任务管理

    文章目录 1 任务1 1 任务简介1 2 任务调度1 3 任务的状态 就绪态 运行态 阻塞态 挂起态 1 4 空闲任务 2 动态创建两个任务2 1 定义动态内存空间的堆2 2 定义任务函数2 3 定义 任务控制块 指针2 4 动态创建任务