IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> ??善用宏——定义及初始化变量结构体和数组?? -> 正文阅读

[嵌入式]??善用宏——定义及初始化变量结构体和数组??

开始使用STM32做项目的时候,发现当使用一些接口去创建task,或者使用定时器等等需要提前定义一些变量和宏,然后将他们作为参数传入到接口中。比如:

/* 这些定义每个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;		/* task名字 */
    UINT8           id;			/* 作为数组的索引 */
    UINT8           prio;		/* task优先级 */
    UINT16          stkSize;	/* task使用的栈大小 */
    UINT32         *pStkBase;	/* task栈的基地址 */
    OS_TCB          tcb;		/* task控制块 */
    VOID           *pArg;		/* task主函数的参数 */
    TASK_FUNC_F func;    		/* task主函数 */
} 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,

    /* Add new above this */
    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,

    /* Add new above this */
    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);\ 
    /* Add new above this */

#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) 
    /* Add new above this */

INT8 TASK_Create(UINT8 id, TASK_FUNC_F func, VOID *pArg);    

task.c:

typedef struct _TASK_INFO_T
{
    const UINT8    *pName;		/* task名字 */
    UINT8           id;			/* 作为数组的索引 */
    UINT8           prio;		/* task优先级 */
    UINT16          stkSize;	/* task使用的栈大小 */
    UINT32         *pStkBase;	/* task栈的基地址 */
    OS_TCB          tcb;		/* task控制块 */
    VOID           *pArg;		/* task主函数的参数 */
    TASK_FUNC_F func;    		/* task主函数 */
} TASK_INFO_T;

/* 定义了所有task的栈基地址 */
TASK_STK_CFG;

/* 将所有的task信息存放到数组中统一管理 */
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);
	/* Add new above this */
	
	OS_CRITICAL_EXIT();
}

总结:

  1. 上面的代码并不完善,举出STM32只是作为一个例子,关键是要说明使用宏定义变量,初始化结构体,初始化数组,使代码更加简洁,便于维护。
  2. “#”:是将参数字符串化的预处理。
  3. “##”:是连接符,宏定义中定义变量的名字经常使用到。
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-04 11:22:45  更:2021-08-04 11:24:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 21:18:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码