一种简洁、可拓展的RTOS任务初始化设计

2023-05-16

已剪辑自: https://mp.weixin.qq.com/s/9IN3AZsqnvgvYLukqvlEPQ

随着写代码功力的提升,个人对于代码的整洁、优雅、可维护、易拓展等就有了一定的要求,虽然自己曾经就属于那种全局变量满天飞,想到哪里写到哪里的嵌入式软件工程师;但是这一切在现在来说必须要结束了!要想做一个好的项目,我们时刻都要去想它的框架如何设计,如何去兼容未来的拓展,以便我们构建一个优雅、整洁、易维护、易拓展的程序,少出问题,少加班,拿高薪;因此,我们必须在代码的设计上利用编程语言的特性来下一些功夫。

图片

在之前,我就经常发现很多工程师在写RTOS代码的时候存在如下问题:

  • 随意定义任务的位置,随意初始化任务代码。
  • 由于任务函数初始化参数过多,当同时创建多个任务时,任务初始化函数写得非常长,非常难看。

例如我之前写的这个RT-Thread的项目:

码云仓库:

git clone https://gitee.com/morixinguan/personal-open-source-project.git

部分代码如下:

/***************按键处理任务*************/
#define KEY_TASK_PRIORITY      3
#define KEY_TASK_SIZE         2000
static rt_thread_t key_task_thread = RT_NULL;
static void Start_Key_Task(void *parameter);
/***************按键处理任务*************/
/***************传感器任务处理*************/
#define SENSOR_PRIORITY           4
#define SENSOR_TASK_SIZE            2048
rt_sem_t sensor_data_sem = RT_NULL;
static rt_thread_t sensor_task_thread = RT_NULL;
/*状态栏更新线程入口函数 */
static void StartSensor_Task(void *parameter);
/***************传感器任务处理*************/
/***************控制任务处理*************/
#define CONTROL_PRIORITY           5
#define CONTROL_TASK_SIZE           2048
static rt_thread_t control_task_thread = RT_NULL;
/*控制任务更新线程入口函数 */
static void StartControl_Task(void *parameter);
/***************控制任务处理*************/

......省略.....
/*启动其它任务*/
void start_other_rt_thread(void)
{
    /*1、创建按键线程*/
    key_task_thread = rt_thread_create("key_th",
                                       Start_Key_Task, RT_NULL,
                                       KEY_TASK_SIZE,
                                       KEY_TASK_PRIORITY, TASK_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (key_task_thread != RT_NULL)
        rt_thread_startup(key_task_thread);

    /*2、创建控制线程*/
    control_task_thread = rt_thread_create("con_th",
                                           StartControl_Task, RT_NULL,
                                           CONTROL_TASK_SIZE,
                                           CONTROL_PRIORITY, TASK_TIMESLICE);

    /* 如果获得线程控制块,启动这个线程 */
    if (control_task_thread != RT_NULL)
        rt_thread_startup(control_task_thread);

    Menu_Init();
    //关指示灯
    HAL_GPIO_WritePin(BOARD_LED_GPIO_Port, BOARD_LED_Pin, GPIO_PIN_RESET);
}

其实这个看起来还算舒服一点,至少它的位置是比较统一的,而且任务并不算很多;但是如果任务更多,这个代码看起来就会很长,比如我找来的下面这个代码,具体就不说是哪位小伙伴写的了:

static  void  AppTaskStart (void *p_arg)
{
    OS_ERR       err;
 CPU_SR      cpu_sr = 0;
 uint8_t test[10];
   (void)p_arg;
    BSP_Init(); 
    CPU_Init();                  
 delay_init(168);
 uart_init(9600);   
 TxDMAConfig();
 RxDMAConfig((uint32_t)g_usart1RxBuf0,(uint32_t)g_usart1RxBuf1,USART1BUFSIZE);
 USART1_RxCallback = USART1_DMARxCallback;
 __HAL_DMA_ENABLE(&UART1RxDMA_Handler); 
 RTC_Init();
 PRINTER_Init();
 W25QXX_Init();
 LCD_BSP_Init();
 LcdInit();
 ADC_BSP_Init();
 NixieTube_BSPInit();
 MenuSystemInit();
 offplay();
 SRAM_Init();
 CH456IF_Init();
 ch456_test();
 my_mem_init(SRAMEX); /* 初始化外部SRAM */
 Data_Init();         /* 初始化数据存储模块 */
#if OS_CFG_STAT_TASK_EN > 0u
    OSStatTaskCPUUsageInit(&err);
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_IntDisMeasMaxCurReset();
#endif

#if OS_CFG_SCHED_ROUND_ROBIN_EN  //时间片轮度算法  
 OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);  
#endif 
 OS_CRITICAL_ENTER();
 
 /*mutex create zone:begin*/
 OSMutexCreate((OS_MUTEX* )&TEST_MUTEX,
      (CPU_CHAR* )"TEST_MUTEX",
                  (OS_ERR*  )&err);
 
 OSMutexCreate((OS_MUTEX* )&FLASH_MUTEX,
      (CPU_CHAR* )"FLASH READ MUTEX",
                  (OS_ERR*  )&err);
 /*mutex create zone:end*/
 
 /*USER TASK CREATE ZONE:BEGIN*/
 OSTaskCreate(&USBProcessTaskTCB,
     "USB Process Task",
     USBProcessTask,
     0u,
     USB_CFG_PROCESS_TASK_PRIO,
     USBProcessTaskStk,
     USBProcessTaskStk[USB_CFG_PROCESS_TASK_STK_SIZE / 10u],
     USB_CFG_PROCESS_TASK_STK_SIZE,
     0u,   //message amount
     0u,
     0u,
    (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
    &err);
 
 OSTaskCreate(&teskTaskTCB,                              /* Create the start task                                */
     "test Process Task",
     testProcessTask,
     0u,
     TEST_CFG_PROCESS_TASK_PRIO,
     TESTProcessTaskStk,
     TESTProcessTaskStk[TEST_CFG_PROCESS_TASK_STK_SIZE / 10u],
     TEST_CFG_PROCESS_TASK_STK_SIZE,
     0u,   //message amount
     0u,
     0u,
    (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
    &err);
 OS_CRITICAL_EXIT(); 
    while (DEF_TRUE) {   
        udp_flag |= LWIP_SEND_DATA;  
        OSTimeDlyHMSM(0u, 0u, 0u, 100u,
                      OS_OPT_TIME_HMSM_STRICT,
                      &err);
    }
}

难受吗?至少我是觉得很难受的!解决这个问题可以使用一种简单的、可扩展的RTOS初始化设计模式,这个设计模式的原则就是创建一个通用的初始化函数,然后这个函数可以遍历RTOS初始化配置表来初始化所有的任务,让我们来看看如何创建这样的设计模式。

1、创建任务初始化结构

第一步是检查 RTOS 的任务创建函数,并查看初始化任务所需的参数。任务初始化结构只是一个包含初始化任务所需的所有参数的结构。但是不同的RTOS之间可能不同,以freertos为例:

typedef struct
{
    TaskFunction_t const taskptr;           
    const char *   const taskname;                
    const configSTACK_DEPTH_TYPE stackdepth;    
    void * const   parametersptr;                 
    UBaseType_t    taskpriority;                   
    TaskHandle_t * const taskhandle;            
}FreertosTaskParams_t;
2、创建任务配置表

有了第1步所定义的结构体以后,我们就可以创建一个配置表了,这个配置表就包含了所有的任务以及初始化这些任务的所需的参数,例如:

FreertosTaskParams_t Task_Parameters_conf[] = 
{
    {(Function_t)Task_1, "Task_1",TASK_1_STACK_DEPTH, &Telemetry, TASK_1_PRIORITY, NULL}, 
    {(Function_t)Task_2, "Task_2",TASK_2_STACK_DEPTH, NULL      , TASK_2_PRIORITY, NULL}, 
    {(Function_t)Task_3, "Task_3",TASK_3_STACK_DEPTH, &Telemetry, TASK_3_PRIORITY, NULL}, 
    {(Function_t)Task_4, "Task_4",TASK_4_STACK_DEPTH, &Telemetry, TASK_4_PRIORITY, NULL}, 
    {(Function_t)Task_5, "Task_5",TASK_5_STACK_DEPTH, &Telemetry, TASK_5_PRIORITY, NULL}, 
    {(Function_t)Task_6, "Task_6",TASK_6_STACK_DEPTH, &Telemetry, TASK_6_PRIORITY, NULL}, 
};

这个表里有很多参数我们还没有进行宏定义。这些都是我们将在应用程序中定义的用于初始化任务的参数。例如,每个任务的优先级可能都不一样,这里用一个宏,例如TASK_1_PRIORITY来进行表示。

3、创建初始化循环

创建任务配置表以后,初始化任务只用一个for循环就好了,然后将结构体数组里的各个参数分别对应到RTOS创建任务的API里就可以了。例如,我们可以使用以下循环初始化任务:

#define NR(x) (sizeof(x)/sizeof(x[0]))
for(uint8_t count = 0; count < NR(Task_Parameters_conf); count++)
{
    (void)xTaskCreate(Task_Parameters_conf[TaskCount].taskptr,
                      Task_Parameters_conf[TaskCount].taskname,
                      Task_Parameters_conf[TaskCount].stackdepth,
                      Task_Parameters_conf[TaskCount].parametersptr,
                      Task_Parameters_conf[TaskCount].taskpriority, 
                      Task_Parameters_conf[TaskCount].taskhandle);
}

这里要注意的是,我们将(void)放在xTaskCreate前面,其实这样是表示我们在创建任务的时候忽略了xTaskCreate这个函数的的返回值。正常情况下,我们当前希望检查函数的返回值,这样可以增加整个程序的健壮性,但在这种情况下,我们将在初始化期间创建所有任务,并且不会出现任何内存问题。但是,我们可以依靠freerTOS malloc失败的钩子函数来捕获开发过程中的任何动态内存分配问题。或者,我们可以检查返回值,然后创建一个函数,这个函数在出现问题时进行检查和恢复。

4、结论

这种简单的RTOS初始化的设计模式是可扩展的,可重用的,并且能够很容易进行修改。这是嵌入式软件工程师如何利用设计模式的一个很好的例子。这种设计模式可以与任何RTOS一起使用。

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

一种简洁、可拓展的RTOS任务初始化设计 的相关文章

随机推荐

  • 如何判断一段程序是否是裸机程序?

    在嵌入式MCU领域 xff0c 一般将不移植操作系统直接烧录运行的程序称为裸机程序 一般来说 xff0c 非易失性存储 xff0c 时钟 xff0c 图形显示 xff0c 网络通讯 xff0c 用户I O设备 都需要硬件依赖 基于硬件基础
  • 单片机STM32有什么推荐的裸机编程架构

    作者 xff1a DBinary 链接 xff1a https www zhihu com question 438340661 answer 2735154401 来源 xff1a 知乎 著作权归作者所有 商业转载请联系作者获得授权 xf
  • 一文讲清微服务架构、分布式架构、微服务、SOA

    文章目录 四种软件架构 xff1a 单体架构 分布式架构 微服务架构 Serverless架构一 单体架构二 分布式应用三 微服务架构四 Serverless架构 微服务是什么 xff1f 一 单体软件二 面向服务架构三 微服务 SOA架构
  • 敏捷开发,持续集成/交付/部署, DevOps总结

    文章目录 敏捷开发入门教程一 迭代开发二 增量开发三 敏捷开发的好处3 1 早期交付3 2 降低风险 四 如何进行每一次迭代五 敏捷开发的价值观六 十二条原则七 参考链接 持续集成 交付 部署一 概念二 持续交付三 持续部署四 流程4 1
  • IC集成电路 测试与验证的区别?

    在数字IC中 xff0c 验证与测试完全是两个概念 验证是在pre silicon 阶段 xff0c 也就是流片之前 xff0c 随着设计一起进行的 验证的主要目的是保证芯片逻辑功能的正确性和功能的完备性 验证的一般流程如下 xff1a 测
  • EGL综述

    参考 xff1a https www khronos org registry EGL specs eglspec 1 5 pdf 什么是EGL EGL是支持多平台 多操作系统的 xff0c 比如安卓 Unix Windows等 为了扩展性
  • pcie的rc模式和ep模式有什么区别?

    pcie的rc模式和ep模式有什么区别 xff1f RC PCI Express root complex 在RC模式时 xff0c 使用PCIE类型1配置头 xff1b EP endpoint device 工作方式 在EP模式时 xff
  • Android程序员一年没上班该如何找工作

    前言 Android程序员老王在21年7月份向公司提出了离职 离职后老王觉得在上家工作那么久 xff0c 就想趁着这个机会好好放松一下 由于让自己休息了两个月在加上他自己存了一点积蓄 xff0c 导致后面半年时间都没有找工作面试 到了22年
  • 为什么C语言执行效率高,运行快?

    已剪辑自 https mp weixin qq com s JUucTzACS IFO3iTO77DhQ 简述 都说C语言编写的程序执行效率比较高 xff0c 那么到底高在哪里 xff0c 我们一块来学习学习 C语言由来 C语言源自于BCP
  • 嵌入式5个RTOS程序设计建议

    已剪辑自 https mp weixin qq com s cCgQ5nfGiQckyqkXKxWtLQ 今天聊一下RTOS应用程序设计的五个实践技巧 我在编写RTOS应用程序的过程中 xff0c 经常会遇到这些困难 xff0c 包括正确确
  • 详解C语言二级指针三种内存模型

    已剪辑自 https mp weixin qq com s EBoKOgoVFl751jPe QEAlg 整理 xff1a 李肖遥 二级指针相对于一级指针 xff0c 显得更难 xff0c 难在于指针和数组的混合 xff0c 定义不同类型的
  • 软件架构设计与需求分析方法论

    文章目录 1 软件架构体系1 1 系统与子系统1 2 模块 组件 服务1 3 软件架构体系 2 架构原则2 1 解耦2 2 分层2 3 封装 3 架构的方法3 1 业务架构3 2 功能架构3 3 系统架构3 4 技术架构3 5 数据架构3
  • 马斯洛人类需求五层次理论(Maslow‘s Hierarchy of Needs)

    已剪辑自 https wiki mbalib com wiki E9 A9 AC E6 96 AF E6 B4 9B E4 BA BA E7 B1 BB E9 9C 80 E6 B1 82 E4 BA 94 E5 B1 82 E6 AC A
  • 从需求收集到需求落地,需求分析如何才能更全面?

    从需求收集到需求落地 xff0c 需求分析如何才能更全面 xff1f 已剪辑自 http www moonpm com 503 html 一 什么是需求 心里学上定义 xff1a 需求是由个体在生理上或者心理上感到某种欠缺而力求获得满足的一
  • 什么是云原生?

    已剪辑自 https juejin cn post 6844904197859590151 伴随云计算的滚滚浪潮 xff0c 云原生 CloudNative 的概念应运而生 xff0c 云原生很火 xff0c 火得一塌糊涂 xff0c 都0
  • 三年!我完成了自己的一次蜕变

    已剪辑自 https mp weixin qq com s r9Qv4XkLQ 3QClOeb5f19g 大家好 xff0c 我是txp xff0c 今天分享一篇我个人的一个成长经历 xff01 希望对大家有帮助 xff0c 文字可能会稍微
  • 真正的模块化编程原来是这样的!

    已剪辑自 https mp weixin qq com s uo4tnsEnpULAruayZHcKAw 随着我们工程化经验的增加 xff0c 不知不觉的我们就会关心到这个问题 xff0c 模块化 xff0c 模块设计就显现出来 xff0c
  • 分享嵌入式软件调试方法和几个工具

    已剪辑自 https mp weixin qq com s dbYmBOISjd7tzniVT2l eg 我们常常说 xff0c 软件三分写七分调 实际开发中 xff0c 确实也是这样子的 我工作这几年了 xff0c 对这体会也越来越深 每
  • Ubuntu安装指定版本clang-format

    执行以下命令即可 xff1a wget O https apt llvm org llvm snapshot gpg key sudo apt key add sudo vim etc apt sources list 插入从https a
  • 一种简洁、可拓展的RTOS任务初始化设计

    已剪辑自 https mp weixin qq com s 9IN3AZsqnvgvYLukqvlEPQ 随着写代码功力的提升 xff0c 个人对于代码的整洁 优雅 可维护 易拓展等就有了一定的要求 xff0c 虽然自己曾经就属于那种全局变