迅维网

查看: 1723|回复: 0
打印 上一主题 下一主题

【NUCLEO-F412ZG试用体验】FreeRTOS移植试验

[复制链接]
跳转到指定楼层
1#
发表于 2017-2-7 12:49:12 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式 来自: 山东青岛 来自 山东青岛

马上注册,获取阅读精华内容及下载权限

您需要 登录 才可以下载或查看,没有帐号?注册

x


在开发实际应用系统时,我们经常需要考虑数据的实时性和多任务,嵌入式实时操作系统的出现为实现这一目的提供了很好的助力。FreeRTOS是近年来比较流行的嵌入式实时操作系统,而且是开源免费的,STM32CubeMX对它也提供了支持。我们可以使用STM32CubeMX很方便的添加上FreeRTOS,只需要在配置界面找到“MiddleWares”并将其下的FreeRTOS选为“Enable”就可添加上FreeRTOS:

                               
登录/注册后看高清大图

                              

配置完成后,更新源码就会在软件中添加上FreeRTOS的相关文件,各文件的功能网上已经有很多介绍,就不啰嗦了,而且也不属于我们需要说明的内容。

                               
登录/注册后看高清大图


添加完了之后,源码中就会添加创建一个缺省任务的代码。因为最少要有一个任务才有意义。查看main()函数,就会出现如下这样一段代码:

  /* Createthe thread(s) */

  /*definition and creation of defaultTask */

osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

  defaultTaskHandle= osThreadCreate(osThread(defaultTask), NULL);


  /* USERCODE BEGIN RTOS_THREADS */

  /* addthreads, ... */

  /* USERCODE END RTOS_THREADS */


  /* USERCODE BEGIN RTOS_QUEUES */

  /* addqueues, ... */

  /* USERCODE END RTOS_QUEUES */



  /* Startscheduler */

osKernelStart();

其实这段代码就实现两个功能:创建任务和情动任务调度。不过ST在FreeRTOS的基础上做了进一步的封装。其中osKernelStart()函数用于启动任务调度,这个函数比较简单只不过是调用了一下FreeRTOS中的任务调度函数vTaskStartScheduler(),其实现原型如下:

osStatus osKernelStart (void)

{

vTaskStartScheduler();

  

  returnosOK;

}

而实现任务创建的就是osThreadDef(defaultTask,StartDefaultTask, osPriorityNormal, 0, 128);和defaultTaskHandle =osThreadCreate(osThread(defaultTask), NULL);这两行代码。这两行代码就是创建了一个任务显示名称为defaultTask的StartDefaultTask任务。带是代码看起来有点不容易理解,其实是ST进行了进一步封装的结果。

首先osThreadDef(defaultTask,StartDefaultTask, osPriorityNormal, 0, 128)其实是一个宏,其定义的原型如下:

#define osThreadDef(name, thread, priority,instances, stacksz)  \

const osThreadDef_t os_thread_def_##name = \

{ #name, (thread), (priority), (instances),(stacksz)  }

那么osThreadDef_t又是个什么呢?其实是ST定义的创建任务的参数结构体:

typedef struct os_thread_def  {

  char                   *name;        ///< Thread name

os_pthread            pthread;      ///< startaddress of thread function

osPriority            tpriority;    ///< initialthread priority

uint32_t              instances;    ///< maximumnumber of instances of that thread function

uint32_t              stacksize;    ///< stack sizerequirements in bytes; 0 is default stack size

} osThreadDef_t;

所以第一句的含义就很明确了,就是定义了一个osThreadDef_t类型的变量用于存储用于创建任务的参数。第二句很明显是一个函数调用,但参数与我们见到的FreeRTOS中的创建任务的函数有些不同。其实它的第一个参数osThread(defaultTask)也是一宏:

#define osThread(name)  \

&os_thread_def_##name

其实就是调用第一句中定义的osThreadDef_t类型的结构体变量。这样就明确了,第二句实际上就是调用FreeRTOS中的任务创建函数创建任务:

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)  {

    returnNULL;

  }

  

  returnhandle;

}

所以我们要创建其他的任务,只需照此设计即可。当然任务中的内容根据需要修改,STM32CubeMX添加的缺省任务,参数和实现内容也可以更改。在这里我们不对缺省任务做太多修改,在其任务函数中添加部分我们的内容。

考虑到我们所应用到的功能,我们需要6个任务,利用上缺省任务我们还需要添加5个任务。之所以要添加这么多的任务是因为我们不想让各种功能相互影响。在某一外设出现操作错误时不会影响其他部分的应用,这也是我们使用多任务实时操作系统的原因之一。

缺省任务我们让它来跑逻辑控制。再增加几个任务分别来跑各种功能,由于AD和DA都是通过SPI1通讯来完成的,所以采用同一个任务。

首先定义几个任务操作句柄:

/*定义任务句柄*/

osThreadId defaultTaskHandle;

osThreadId addaTaskHandle;

osThreadId ndirTaskHandle;

osThreadId lcdTaskHandle;

osThreadId ethernetTaskHandle;

osThreadId paraTaskHandle;

在声明对应的任务处理函数:

/*声明任务处理函数*/

void StartDefaultTask(void const * argument);

void ADDATask(void const * argument);

void NDIRTask(void const * argument);

void LCDTask(void const * argument);

void EthernetTask(void const * argument);

void ParameterTask(void const * argument);

编写各任务处理函数,由于各种功能在之前已经实现了所以任务函数比较简单:

/* 缺省任务,用来处理逻辑控制 */

void StartDefaultTask(void const * argument)

{

  for(;;)

  {

    /*逻辑处理*/

   LogicOperation();

   osDelay(1);

  }

}


/* AD/DC数据处理任务函数 */

void ADDATask(void const * argument)

{

  for(;;)

  {

    /*获取AD采集的测量值*/

   GetMeasuredValue();

    /*设置模拟量输出*/

   SetOutputValue();

   osDelay(1);

  }

}


/* 远红外炭氢检测数据处理任务函数 */

void NDIRTask(void const * argument)

{

  for(;;)

  {

    /*获取CH4测量值*/

   GetNDIRData();

   osDelay(1);

  }

}


/* LCD显示通讯处理任务函数 */

void LCDTask(void const * argument)

{

  for(;;)

  {

    /*显示屏数据通讯*/

   LCD_DataExchange();

   osDelay(1);

  }

}


/* 以太网通讯处理任务函数 */

void EthernetTask(void const * argument)

{

  for(;;)

  {

    /*以太网通讯处理*/

   EthernetProcess();

   osDelay(1);

  }

}


/* 参数存取处理任务函数 */

void ParameterTask(void const * argument)

{

  for(;;)

  {

    /*参数的存储、恢复等处理*/

   ParameterProcess();

   osDelay(1);

  }

}

再在系统中创建任务:

/* Create the thread(s) */

  /*definition and creation of defaultTask */

osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);

defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);


  /* USERCODE BEGIN RTOS_THREADS */

osThreadDef(addaTask, ADDATask, osPriorityNormal, 0, 128);

addaTaskHandle = osThreadCreate(osThread(addaTask), NULL);

  

osThreadDef(ndirTask, NDIRTask, osPriorityNormal, 0, 128);

ndirTaskHandle = osThreadCreate(osThread(ndirTask), NULL);

  

osThreadDef(lcdTask, LCDTask, osPriorityNormal, 0, 128);

lcdTaskHandle = osThreadCreate(osThread(lcdTask), NULL);

  

osThreadDef(ethernetTask, EthernetTask, osPriorityNormal, 0, 128);

ethernetTaskHandle = osThreadCreate(osThread(ethernetTask), NULL);

  

osThreadDef(paraTask, ParameterTask, osPriorityNormal, 0, 128);

paraTaskHandle = osThreadCreate(osThread(paraTask), NULL);

  /* USERCODE END RTOS_THREADS */

至此在系统中添加FreeRTOS的过程完成,编译调试看看结果:

                               
登录/注册后看高清大图


编译无错误,运行正常。通过IO中断看看运行结果与没有操作系统时有没有区别:

                               
登录/注册后看高清大图


在运行一段时间后再看看结果:

                               
登录/注册后看高清大图

系统运行无误,与我们所希望的一样。

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表
附近
店铺
微信扫码查看附近店铺
维修
报价
扫码查看手机版报价
信号元
件查询
点位图


芯片搜索

快速回复