开始使用STM32做项目的时候,发现当使用一些接口去创建task,或者使用定时器等等需要提前定义一些变量和宏,然后将他们作为参数传入到接口中。比如:
#define TSK1_TASK_PRIO 3
#define TSK1_STK_SIZE 128
OS_TCB Tsk1TaskTcb;
CPU_STK TSK1_TASK_STK[TSK1_STK_SIZE];
void task1_main(void *p_arg);
#define TSK2_TASK_PRIO 4
#define TSK2_STK_SIZE 256
OS_TCB Tsk2TaskTcb;
CPU_STK TSK2_TASK_STK[TSK2_STK_SIZE];
void task2_main(void *p_arg);
#define TSK3_TASK_PRIO 5
#define TSK3_STK_SIZE 512
OS_TCB Tsk3TaskTcb;
CPU_STK TSK3_TASK_STK[TSK3_STK_SIZE];
void task3_main(void *p_arg);
int main(void)
{
OS_CRITICAL_ENTER();
OSTaskCreate((OS_TCB *)&Tsk1TaskTcb,
(CPU_CHAR *)"task1 task",
(OS_TASK_PTR )task1_main,
(void *)0,
(OS_PRIO )TSK1_TASK_PRIO ,
(CPU_STK *)&TSK1_TASK_STK[0],
(CPU_STK_SIZE)TSK1_STK_SIZE/10,
(CPU_STK_SIZE)TSK1_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR *)&err);
OSTaskCreate((OS_TCB *)&Tsk2TaskTcb,
(CPU_CHAR *)"task2 task",
(OS_TASK_PTR )task2_main,
(void *)0,
(OS_PRIO )TSK2_TASK_PRIO ,
(CPU_STK *)&TSK2_TASK_STK[0],
(CPU_STK_SIZE)TSK2_STK_SIZE/10,
(CPU_STK_SIZE)TSK2_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR *)&err);
OSTaskCreate((OS_TCB *)&Tsk3TaskTcb,
(CPU_CHAR *)"task3 task",
(OS_TASK_PTR )task3_main,
(void *)0,
(OS_PRIO )TSK3_TASK_PRIO ,
(CPU_STK *)&TSK3_TASK_STK[0],
(CPU_STK_SIZE)TSK3_STK_SIZE/10,
(CPU_STK_SIZE)TSK3_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR *)&err);
OS_CRITICAL_EXIT();
}
当需要的task多起来的时候真的会发现这些代码很“啰嗦”,而且,这种方式定义出来的各个task太“散”了,不方便统一管理,最好的方式是存放到一个数组中,通过使用数组下标索引的方式访问不同的task。接下来讨论如何使用宏去定义变量,初始化结构体和数组元素,以此达到简化代码方便维护的目的。
我们先来整理一下task需要的公共信息抽象出一个结构体:
typedef void (*TASK_FUNC_F)(void *);
typedef struct _TASK_INFO_T
{
const UINT8 *pName;
UINT8 id;
UINT8 prio;
UINT16 stkSize;
UINT32 *pStkBase;
OS_TCB tcb;
VOID *pArg;
TASK_FUNC_F func;
} TASK_INFO_T;
下面我们可以定义一个数组包含所有task的信息来统一管理:
static TASK_INFO_T g_taskTable[TASK_ID_END] = {TASK_OBJS};
先来看一下数组的大小宏 TASK_ID_END 是如何定义的:
typedef enum _TASK_ID_E
{
TASK_ID_TSK1 = 0,
TASK_ID_TSK2,
TASK_ID_TSK3,
TASK_ID_END
} TASK_ID_E;
再看一下我们是如何用宏 TASK_OBJS 初始化了数组所有元素:
#define TASK_OBJS \
GEN_TASK_OBJ(TSK1,TASK_ID_TSK1,3,128),\
GEN_TASK_OBJ(TSK2,TASK_ID_TSK2,4,256),\
GEN_TASK_OBJ(TSK3,TASK_ID_TSK3,5,512)
数组的每个元素类型是 TASK_INFO_T ,每个数组元素该如何用宏初始化:
#define GEN_TASK_OBJ(name,_id,_prio,_stkSize) \
{.pName=#name,.id=_id,.prio=_prio,.stkSize=_stkSize,\
.pStkBase=TASK_##name##_STK}
其他参数比较好理解,对于栈的基地址 TASK_##name##_STK 还没有设置,这里我们仍然用一个宏去做初始化:
#define TASK_STK_CFG \
GEN_TASK_STK(TSK1,128);\
GEN_TASK_STK(TSK2,256);\
GEN_TASK_STK(TSK3,512);\
下面看一下宏 GEN_TASK_STK 如何定义了task基地址:
#define GEN_TASK_STK(name,_stkSize) \
CPU_STK TASK_##name##_STK[_stkSize]
最后我们把整个过程完整的记录下: task.h:
typedef void (*TASK_FUNC_F)(void *);
typedef enum _TASK_ID_E
{
TASK_ID_TSK1 = 0,
TASK_ID_TSK2,
TASK_ID_TSK3,
TASK_ID_END
} TASK_ID_E;
#define GEN_TASK_STK(name,_stkSize) \
CPU_STK TASK_##name##_STK[_stkSize]
#define GEN_TASK_OBJ(name,_id,_prio,_stkSize) \
{.pName=#name,.id=_id,.prio=_prio,.stkSize=_stkSize,\
.pStkBase=TASK_##name##_STK}
#define TASK_STK_CFG \
GEN_TASK_STK(TSK1,256);\
GEN_TASK_STK(TSK2,512);\
GEN_TASK_STK(TSK3,512);\
#define TASK_OBJS\
GEN_TASK_OBJ(TSK1,TASK_ID_TSK1,3,256),\
GEN_TASK_OBJ(TSK2,TASK_ID_TSK2,13,512),\
GEN_TASK_OBJ(TSK3,TASK_ID_TSK3,11,512)
INT8 TASK_Create(UINT8 id, TASK_FUNC_F func, VOID *pArg);
task.c:
typedef struct _TASK_INFO_T
{
const UINT8 *pName;
UINT8 id;
UINT8 prio;
UINT16 stkSize;
UINT32 *pStkBase;
OS_TCB tcb;
VOID *pArg;
TASK_FUNC_F func;
} TASK_INFO_T;
TASK_STK_CFG;
static TASK_INFO_T g_taskTable[TASK_ID_END] = {TASK_OBJS};
INT8 TASK_Create(UINT8 id, TASK_FUNC_F func, VOID *pArg)
{
TASK_INFO_T *pTask = NULL;
pTask = &g_taskTable[id];
pTask->func = func;
pTask->pArg = pArg;
OSTaskCreate((OS_TCB *)&pTask->tcb,
(CPU_CHAR *)pTask->pName,
(OS_TASK_PTR )pTask->func,
(void *)pTask->pArg,
(OS_PRIO )pTask->prio,
(CPU_STK *)pTask->pStkBase,
(CPU_STK_SIZE)pTask->stkSize/10,
(CPU_STK_SIZE)pTask->stkSize,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void *)0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR *)&err);
}
main.c:
int main(void)
{
OS_CRITICAL_ENTER();
TASK_Create(TASK_ID_TSK1, task1_main, NULL);
TASK_Create(TASK_ID_TSK2, task2_main, NULL);
TASK_Create(TASK_ID_TSK3, task3_main, NULL);
OS_CRITICAL_EXIT();
}
总结:
- 上面的代码并不完善,举出STM32只是作为一个例子,关键是要说明使用宏定义变量,初始化结构体,初始化数组,使代码更加简洁,便于维护。
- “#”:是将参数字符串化的预处理。
- “##”:是连接符,宏定义中定义变量的名字经常使用到。
|