一、osThreadDef是一个宏定义
#define osThreadDef(name, thread, priority, instances, stacksz) \
const osThreadDef_t os_thread_def_##name = \
{ #name, (thread), (priority), (instances), (stacksz), NULL, NULL }
所以
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
const osThreadDef_t os_thread_def_defaultTask = { "defaultTask", (StartDefaultTask), (osPriorityNormal), (0), (128) }
相当于定义了一个结构体类型为osThreadDef_t 的结构体常量os_thread_def_defaultTask 常量,并且赋值。
宏定义中,##的作用就是把2个宏参数连接为1个数,或实现字符串的连接。 #的作用就是将#后面的宏参数进行字符串的操作,也就是将#后面的参数两边加上一对双引号使其成为字符串。
osThreadDef_t 是一个结构体定义,记住在单片机的库函数中一般变量名后缀是_t 结尾的都是一个结构体。 在这个结构体中
typedef struct os_thread_def {
char *name;
os_pthread pthread;
osPriority tpriority;
uint32_t instances;
uint32_t stacksize;
#if( configSUPPORT_STATIC_ALLOCATION == 1 )
uint32_t *buffer;
osStaticThreadDef_t *controlblock;
#endif
} osThreadDef_t;
解释:使用typedef将结构体os_thread_def 取一个osThreadDef_t 的别名,以后就用osThreadDef_t 代替结构体os_thread_def 。当使用动态创建函数也就是采用动态内存分配时有5个成员变量,当使用静态创建函数也就是采用动态内存分配时有7个成员变量。一般在工程应用中采用动态内存分配,简单一些,不用自己设置堆栈大小和控制块。
-
*name:指定一个名字,比如指定为 defaultTask。在定义结构体变量时,会使用##(连接符) 自动在 defaultTask 前面加os_thread_def_前缀,最终的结构体变量名为 os_thread_def_defaultTask。 -
hread:指定线程函数,创建线程时该函数就会被注册为线程函数,运行线程时就是在执行线程函数的代码,不过如果以普通方式来调用线程函数的话,线程函数就是一个普通的函数。线程函数的格式是固定的,为void 函数名(void const * 参数名) ,函数名和参数名可以自己定,但是返回值和参数的类型必须是void 和void const * 。宏的第二个参数thread 就应该写为 StartDefaultTask。 疑问:谁会传递参数给线程函数? 答:后面讲 osThreadCreate 函数时再介绍。 -
priority:指定线程的优先级。 有关线程优先级,后面讲枚举类型osPiority 时会详细介绍,一般的话会指定为普通优 先级osPriorityNormal 即可。 -
instances:线程实例 0:表示 osThreadDef 宏所定义的结构体变量(数据结构)只能用来创建一个线程(线程实例) 其它值>0 的值:比如 3,表示使用该数据结构可以创建 3 个线程,这三个线程使用的都是相同的线程函数,后面会具体举例。 疑问:一个线程函数可以被多个线程使用吗? 答:当然可以,虽然几个线程所用的线程函数都是一样的,但是各自运行各自的,互不 干扰。 -
stacksz:指定线程栈的大小 一般指定为 128 字节就够了。由于线程栈比较小,因此当线程函数要使用很大的数组时,最好定义为全局变量或者动态开辟在堆中,否者可能会导致线程栈的空间不够用。
函数指针 void (*os_pthread)
typedef void (*os_pthread) (void const *argument);
结构体 osPriority
typedef enum {
osPriorityIdle = -3,
osPriorityLow = -2,
osPriorityBelowNormal = -1,
osPriorityNormal = 0,
osPriorityAboveNormal = +1,
osPriorityHigh = +2,
osPriorityRealtime = +3,
osPriorityError = 0x84
} osPriority;
二、osThreadCreate创建任务
在前面的案例中使用osThreadDef 宏定义了一个结构体变量,变量最终的名字为os_thread_def_defaultTask ,创建任务(线程)时会使用该结构体变量,但是我们在使用 osThreadDef 宏时所指定的名字叫defaultTask ,因此需要在前面加os_thread_def_ 前缀,以构建出完整的名字,然后才能访问该结构体变量。
osThread 的作用即是在前面加os_thread_def_ 前缀并加上取地址符& ,表示对结构体变进行取地址。
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
其中的参数osThread(defaultTask) 也是一个宏定义。 所以
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
相当于
defaultTaskHandle = osThreadCreate(os_thread_def_defaultTask, NULL);
其中osThreadCreate 函数定义如下
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
{
TaskHandle_t handle;
if (xTaskCreate((TaskFunction_t)thread_def->pthread,
(const portCHAR *)thread_def->name,
thread_def->stacksize, argument,
makeFreeRtosPriority(thread_def->tpriority),&handle)
!= pdPASS)
{
return NULL;
}
return handle;
}
函数原型
osThreadId osThreadCreate (const osThreadDef_t *thread_def, void *argument)
功能:使用 osThreadDef 宏所定义的结构体变量来创建一个线程。创建好线程后,然后进入 READY 状态,等待任务管理来调度运行。
参数
-
参数 1:指定osThreadDef 所定义结构体变量的指针,通过该指针即可访问结构体变量,然后使用里面的信息来创建线程。由于结构体变量名字的前面有一个os_thread_def_ 前缀,所以需要使用 osThread 宏来添加前缀,如果指定的名字为task1 的话,第一个参数应该写为osThread(task1) ,进行宏替换后的最终效果为&os_thread_def_task1 。实际上我们完全可以将第一个参数直接写为&os_thread_def_task1 ,不过使用osThread 宏显然会更方便一些。 -
参数 2:传递给线程函数的参数。线程函数的参数 argument 的值就来自于这里,如果没有什么参数要传递的,就设置为NULL。
返回值:
- 函数调用成功就返回唯一标识线程的线程 ID(句柄),如果失败就返回 NULL。
其中优先级还要经过计算才得到
如果想直接用数字定义优先级,可以通过修改以下2处实现
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
相当于
const osThreadDef_t os_thread_def_defaultTask = { "defaultTask", (StartDefaultTask), (osPriorityNormal), (0), (128) }
defaultTaskHandle = osThreadCreate(os_thread_def_defaultTask, NULL);
|